diff --git a/.github/actions/commit-changes/action.yml b/.github/actions/commit-changes/action.yml new file mode 100644 index 0000000..0d8ac4f --- /dev/null +++ b/.github/actions/commit-changes/action.yml @@ -0,0 +1,67 @@ +name: 'Commit and push if the version file has changed' + +inputs: + name: + description: 'The name of the commiter' + required: true + default: 'github-actions[bot]' + email: + description: 'The email of the commiter' + required: true + default: 'github-actions[bot]@users.noreply.github.com' + message: + description: 'The commit message' + required: true + default: 'New release version(s)' + files: + description: 'The file(s) to add in the commit' + required: true + default: '*' + directory: + description: 'The directory in which the action will be performed' + required: true + default: '.' + branch: + description: 'Checkout (or create) on a specific branch before commit/push' + required: true + default: 'master' + secret: + description: 'A token allowing to push the commit on the repository' + required: true + default: '.' + repository: + description: 'The repository where to push' + required: true + default: '' + +runs: + using: 'composite' + steps: + - name: Commit the changes + run: | + git config --global user.name ${{ inputs.name }} + ORIGIN="$(pwd)" + cd ${{ inputs.directory }} + git switch ${{ inputs.branch }} 2>/dev/null || git switch -c ${{ inputs.branch }} + git status + CHANGES="$(git status --porcelain ${{ inputs.files }})" + if [ -z "${CHANGES}" ]; \ + then \ + echo "No changes, stopping now"; \ + cd ${origin} \ + exit 0; \ + fi + echo -e "Changes:\n${CHANGES}" + git add ${{ inputs.files }} + git commit -am "${{ inputs.message }}" + git log -n 2 + cd ${ORIGIN} + shell: bash + + - name: Push commit + uses: ad-m/github-push-action@master + with: + github_token: ${{ inputs.secret }} + branch: ${{ inputs.branch }} + directory: ${{ inputs.directory }} + repository: ${{ inputs.repository }} diff --git a/.github/workflows/sdk-generation.yml b/.github/workflows/sdk-generation.yml new file mode 100644 index 0000000..162371e --- /dev/null +++ b/.github/workflows/sdk-generation.yml @@ -0,0 +1,43 @@ +name: Updating the SDK + +on: + workflow_dispatch: + push: + branches: + - master + - develop + +jobs: + updating_SDK: + name: Updating the SDK + runs-on: ubuntu-latest + + steps: + - name: Clone + uses: actions/checkout@v2 + with: + # by default the action uses fetch-depth = 1, which creates + # shallow repositories from which we can't push + fetch-depth: 0 + submodules: recursive + # needed, else the push inside the action will use default credentials + # instead of provided ones + persist-credentials: false + + - name: Build new SDK + run: python tools/build_sdk.py + + - name: Extract branch name + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + id: extract_branch + + - name: Commit & push changes in the SDK if any + uses: ./.github/actions/commit-changes + with: + name: 'ldg-github-ci' + directory: ethereum-plugin-sdk + branch: ${{ steps.extract_branch.outputs.branch }} + message: "[update] Branch ${{ steps.extract_branch.outputs.branch }} | Commit ${GITHUB_SHA}" + secret: ${{ secrets.CI_BOT_TOKEN }} + repository: LedgerHQ/ethereum-plugin-sdk diff --git a/Makefile b/Makefile index 0a1a209..0ddf3e8 100644 --- a/Makefile +++ b/Makefile @@ -389,7 +389,7 @@ $(shell git submodule update --init) endif # rebuild -$(shell python3 ethereum-plugin-sdk/build_sdk.py) +$(shell python3 tools/build_sdk.py) $(shell find ./ethereum-plugin-sdk -iname '*.h' -o -iname '*.c' | xargs clang-format-10 -i) # check if a difference is noticed (fail if it happens in CI build) diff --git a/tools/build_sdk.py b/tools/build_sdk.py new file mode 100755 index 0000000..aab3a6e --- /dev/null +++ b/tools/build_sdk.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +''' +This script extract a few specific definitions from app-ethereum that are +required to exchange information with ethereum external plugins. +It should always be launched from app-ethereum: + +python3 ethereum-plugin-sdk/build_sdk.py + +''' + +import os + + +def extract_from_headers(sources, nodes_to_extract): + cat_sources = [] + for source in sources: + with open(source, 'r') as f: + cat_sources += f.readlines() + + sdk_body = [] + for key, values in nodes_to_extract.items(): + for value in values: + node = [] + unclosed_curvy_brackets = False + unclosed_parantheses = False + for line in cat_sources: + if key in line and value in line: + node += [line] + unclosed_curvy_brackets = line.count('{') - line.count('}') + if unclosed_curvy_brackets == False: + break + elif (key == "fn" and value in line) or unclosed_parantheses: + node += [line] + unclosed_parantheses = line.find(")") == -1 + if unclosed_parantheses == False: + break + elif unclosed_curvy_brackets: + node += [line] + unclosed_curvy_brackets += line.count( + '{') - line.count('}') + if unclosed_curvy_brackets: + continue + else: + break + + sdk_body += [''.join(node)] + + return '\n'.join(sdk_body) + + +def extract_from_c_files(sources, nodes_to_extract): + cat_sources = [] + for source in sources: + with open(source, 'r') as f: + cat_sources += f.readlines() + + sdk_body = [] + for node_name in nodes_to_extract: + node = [] + copying = False + wait_curvy_bracket = True + for line in cat_sources: + if node_name in line: + copying = True + node += [line] + unclosed_curvy_brackets = line.count('{') - line.count('}') + elif copying: + node += [line] + unclosed_curvy_brackets += line.count('{') - line.count('}') + if wait_curvy_bracket: + wait_curvy_bracket = line.count('}') == 0 + if unclosed_curvy_brackets != 0 or wait_curvy_bracket: + continue + else: + break + + sdk_body += [''.join(node)] + + return '\n'.join(sdk_body) + + +def merge_headers(sources, nodes_to_extract): + includes = [ + '#include "os.h"', + '#include "cx.h"', + '#include ', + '#include ' + ] + + body = extract_from_headers(sources, nodes_to_extract) + + eth_internals_h = '\n\n'.join([ + "/* This file is auto-generated, don't edit it */", + "#pragma once", + '\n'.join(includes), + body + ]) + + with open("ethereum-plugin-sdk/include/eth_internals.h", 'w') as f: + f.write(eth_internals_h) + + +def copy_header(header_to_copy, merged_headers): + + merged_headers = [os.path.basename(path) for path in merged_headers] + + with open(header_to_copy, 'r') as f: + source = f.readlines() + + eth_plugin_interface_h = [ + "/* This file is auto-generated, don't edit it */\n"] + for line in source: + eth_plugin_interface_h += [line] + for header in merged_headers: + if header in line: + del eth_plugin_interface_h[-1] + break + + # add '#include "eth_internals.h"' + include_index = eth_plugin_interface_h.index('#include "cx.h"\n') + eth_plugin_interface_h.insert( + include_index+1, '#include "eth_internals.h"\n') + + # dump to file + with open("ethereum-plugin-sdk/include/eth_plugin_interface.h", 'w') as f: + f.writelines(eth_plugin_interface_h) + + +def merge_c_files(sources, nodes_to_extract): + includes = [ + '#include "eth_internals.h"' + ] + + body = extract_from_c_files(sources, nodes_to_extract) + + eth_internals_h = '\n\n'.join([ + "/* This file is auto-generated, don't edit it */", + '\n'.join(includes), + body + ]) + + with open("ethereum-plugin-sdk/include/eth_internals.c", 'w') as f: + f.write(eth_internals_h) + + +if __name__ == "__main__": + + # some nodes will be extracted from these headers and merged into a new one, copied to sdk + headers_to_merge = [ + "src/tokens.h", + "src/chainConfig.h", + "src/utils.h", + "src_common/ethUstream.h", + "src_common/ethUtils.h", + "src/shared_context.h", + "src/eth_plugin_internal.h", + "src/nft.h", + ] + nodes_to_extract = { + "#define": ["MAX_TICKER_LEN", "ADDRESS_LENGTH", "INT256_LENGTH", "WEI_TO_ETHER", "SELECTOR_SIZE", "PARAMETER_LENGTH", "RUN_APPLICATION", "COLLECTION_NAME_MAX_LEN"], + "typedef enum": [], + "typedef struct": ["tokenDefinition_t", "txInt256_t", "txContent_t", "nftInfo_t"], + "typedef union": ["extraInfo_t"], + "__attribute__((no_instrument_function)) inline": ["int allzeroes"], + "const": ["HEXDIGITS"], + "fn": ["void getEthAddressStringFromBinary", "void getEthAddressFromKey", "void getEthDisplayableAddress", "bool adjustDecimals", "bool uint256_to_decimal", "void amountToString", "void u64_to_string", "void copy_address", "void copy_parameter"] + } + merge_headers(headers_to_merge, nodes_to_extract) + + # this header will be stripped from all #include related to previously merged headers, then copied to sdk + copy_header("src/eth_plugin_interface.h", headers_to_merge) + + # extract and merge function bodies + c_files_to_merge = [ + "src/utils.c", + "src_common/ethUtils.c", + "src/eth_plugin_internal.c" + ] + merge_c_files(c_files_to_merge, nodes_to_extract["fn"])