diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b58439e36..b80aad7d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,173 +1,44 @@ -name: Build +name: Build SeedSigner with Keystone on: - pull_request: - # Build on changes to this workflow files in PRs to test proposed changes - paths: - - '.github/workflows/build.yml' push: branches: - main - - dev + - keystone-feature workflow_dispatch: - inputs: - os-ref: - description: The seedsigner-os ref (tag/branch/sha1) to use - default: main - required: true - -# Increment this number as part of a PR to trigger an image build for the PR -# trigger = 0 jobs: build: - name: build runs-on: ubuntu-latest - # Prevent resource consuming cron triggered runs in forks - if: (!github.event.repository.fork || github.event_name == 'workflow_dispatch') - strategy: - fail-fast: false - matrix: - target: [ "pi0" ] # , "pi2", "pi02w", "pi4" ] Just build Pi0 to keep within 500mb storage limit for Github free - steps: - - name: checkout seedsigner-os - uses: actions/checkout@v4 - with: - repository: "3rditeration/seedsigner-os" - # use the os-ref input parameter in case of workflow_dispatch or default to main in case of cron triggers - ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.os-ref || 'main' }} - submodules: true - path: "seedsigner-os" - # get full history + tags for "git describe" - fetch-depth: 0 - - - name: checkout source - uses: actions/checkout@v4 - with: - # ref defaults to repo default-branch=dev (cron) or SHA of event (workflow_dispatch) - path: "seedsigner-os/opt/rootfs-overlay/opt" - # get full history + tags for "git describe" - fetch-depth: 0 - - - name: Get and set meta data - run: | - # The builder_hash (seedsigner-os hash) for the cache action step key - echo "builder_hash=$(git -C seedsigner-os rev-parse --short HEAD)"| tee -a $GITHUB_ENV - - # Derive tag based versions, like 0.7.0-40-g0424967 (=$tag-$number-of-commits-since-tag-$short-sha1), - # or just e.g. 0.7.0, if we are exactly on a 0.7.0 tagged commit. - # --always to fall back to commit sha, if no tag present like in partial forks of the repo - os_version="$(git -C seedsigner-os describe --tags --always)" - source_version="$(git -C seedsigner-os/opt/rootfs-overlay/opt describe --tags --always)" - # Combine seedsigner and seedsigner-os version into one version string and squash the versions, if - # they are identical: So os_version=0.7.0 + source_version=0.7.0 combine to just only "0.7.0", - # whereas os_version=0.6.0-61-g9fafebe + source_version=0.7.0-40-g0424967 combine to "os0.6.0-61-g9fafebe_sw0.7.0-40-g0424967" - if [ "${os_version}" = "${source_version}" ]; then - # seedsigner + seedsigner_os have the same tag - echo "img_version=${source_version}"| tee -a $GITHUB_ENV - else - echo "img_version=os${os_version}_sw${source_version}"| tee -a $GITHUB_ENV - fi - - - name: delete unnecessary files - run: | - cd seedsigner-os/opt/rootfs-overlay/opt - find . -mindepth 1 -maxdepth 1 ! -name src -exec rm -rf {} + - ls -la . - ls -la src - - - name: restore build cache - uses: actions/cache@v4 - # Caching reduces the build time to ~50% (currently: ~30 mins instead of ~1 hour, - # while consuming ~850 MB storage space). - with: - path: | - ~/.buildroot-ccache/ - seedsigner-os/buildroot_dl - key: build-cache-${{ matrix.target }}-${{ env.builder_hash }} - restore-keys: | - build-cache-${{ matrix.target }}- - - - name: Create build container - run: | - cd seedsigner-os - docker build -t seedsigner-os-build . - - - name: build - run: | - mkdir -p \ - ~/.buildroot-ccache \ - seedsigner-os/buildroot_dl - docker run \ - --rm \ - -v "$(pwd)/seedsigner-os/opt:/opt" \ - -v "$(pwd)/seedsigner-os/images:/images" \ - -v "$(pwd)/seedsigner-os/buildroot_dl:/buildroot_dl" \ - -v "${HOME}/.buildroot-ccache:/root/.buildroot-ccache" \ - seedsigner-os-build \ - --${{ matrix.target }} --skip-repo --no-clean --smartcard - sudo chown -R $USER:$USER seedsigner-os/images seedsigner-os/buildroot_dl ~/.buildroot-ccache/ - - - name: list image (before rename) - run: | - ls -la seedsigner-os/images - - - name: rename image - run: | - cd seedsigner-os/images - mv seedsigner_os*.img.zip seedsigner_os.${{ env.img_version }}.${{ matrix.target }}.img.zip - - - name: print sha256sum - run: | - cd seedsigner-os/images - sha256sum *.img.zip - - - name: list image (after rename) - run: | - ls -la seedsigner-os/images - - - name: upload images - uses: actions/upload-artifact@v4 - with: - name: seedsigner_os_images-${{ matrix.target }} - path: "seedsigner-os/images/*.img.zip" - if-no-files-found: error - # maximum 90 days retention - retention-days: 90 - - sha256sum: - name: calculate sha256sum - runs-on: ubuntu-latest - needs: build steps: - - name: download images - uses: actions/download-artifact@v4 + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 with: - path: images + python-version: '3.9' - - name: list images + - name: Install build dependencies run: | - ls -lRa images + sudo apt-get update + sudo apt-get install -y build-essential \ + python3-pil libjpeg-dev libusb-1.0-0-dev libudev-dev \ + cmake device-tree-compiler qemu-user-static binfmt-support - - name: get seedsigner latest commit hash - id: get-seedsigner-hash - run: | - git init - echo "source_hash=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV + - name: Set up Docker + uses: docker/setup-buildx-action@v3 - - name: write sha256sum + - name: Build SeedSigner Image using Docker run: | - cd images - # each downloaded image is in its own subfolder - find . -name "*.img.zip" -exec mv {} . \; - sha256sum *.img.zip > seedsigner_os.${{ env.source_hash }}.sha256 + cd seedsigner + docker build -t seedsigner-build . + docker run --rm -v ${{ github.workspace }}:/mnt seedsigner-build \ + cp /work/seedsigner.img /mnt/seedsigner.img - - name: upload checksums + - name: Upload image artifact uses: actions/upload-artifact@v4 with: - name: seedsigner_os_images_sha256 - path: "images/*.sha256" - if-no-files-found: error - # maximum 90 days retention - retention-days: 90 + name: seedsigner-image + path: seedsigner.img diff --git a/cd b/cd new file mode 100644 index 000000000..e69de29bb diff --git a/helpers/keystone.py b/helpers/keystone.py new file mode 100644 index 000000000..00aa44d56 --- /dev/null +++ b/helpers/keystone.py @@ -0,0 +1,21 @@ +# keystone.py +from seedsigner.gui.components.qr_type import QRType +from seedsigner.gui.components.qr_encoder import QREncoder +from seedsigner.gui.screens.qr_display_screen import QRDisplayScreen + +def show_keystone_export(gui, seed_phrase): + export_data = { + "type": "keystone-eip4527-export", + "mnemonic": seed_phrase, + } + + import json + payload = json.dumps(export_data) + + qr_encoder = QREncoder(gui, QRType.TEXT, payload) + screen = QRDisplayScreen(gui, qr_encoder) + screen.display_qr() + +def sign_keystone_eth_tx(gui): + from seedsigner.gui.screens import info_screen + info_screen.display_message(gui, "Sign ETH TX", "Feature coming soon.") diff --git a/seedsigner/Dockerfile b/seedsigner/Dockerfile new file mode 100644 index 000000000..61b3b5053 --- /dev/null +++ b/seedsigner/Dockerfile @@ -0,0 +1,35 @@ +FROM ubuntu:24.04 + +# 1. Install build tools +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git git-lfs build-essential python3 python3-pip \ + dosfstools e2fsprogs qemu-user-static unzip wget + +WORKDIR /seedsigner + +# 2. Copy all source + patches +# COPY . . + +COPY patches/ patches/ + +RUN for patch in patches/*.patch; do \ + echo "Applying $patch..."; \ + patch -p1 < "$patch"; \ + done + +# 3. Pull LFS files +# RUN git lfs install && git lfs pull + +# 4. Apply your patches +RUN for patch in patches/*.patch; do \ + echo "Applying $patch..."; \ + patch -p1 < "$patch"; \ + done + +# 5. Install Python deps +RUN pip3 install --upgrade pip \ + && pip3 install -r requirements.txt ur-registry-eth + +# 6. Build the SD-card image +RUN chmod +x scripts/create_img.sh \ + && ./scripts/create_img.sh diff --git a/seedsigner/controllers/main_menu_controller.py b/seedsigner/controllers/main_menu_controller.py new file mode 100644 index 000000000..9925111d1 --- /dev/null +++ b/seedsigner/controllers/main_menu_controller.py @@ -0,0 +1,7 @@ +from seedsigner.helpers import keystone + +... +main_menu.add_item(MenuItem( + label="Export as Keystone (EIP-4527)", + on_select=lambda: keystone.show_keystone_export(self.gui, self.seed_phrase) +)) diff --git a/seedsigner/controllers/signers_menu_controller.py b/seedsigner/controllers/signers_menu_controller.py new file mode 100644 index 000000000..d260652b5 --- /dev/null +++ b/seedsigner/controllers/signers_menu_controller.py @@ -0,0 +1,7 @@ +from seedsigner.helpers import keystone + +... +signer_menu.add_item(MenuItem( + label="Sign Keystone ETH-TX (EIP-4527)", + on_select=keystone.sign_keystone_eth_tx +)) \ No newline at end of file diff --git a/seedsigner/helpers/keystone.py b/seedsigner/helpers/keystone.py new file mode 100644 index 000000000..00aa44d56 --- /dev/null +++ b/seedsigner/helpers/keystone.py @@ -0,0 +1,21 @@ +# keystone.py +from seedsigner.gui.components.qr_type import QRType +from seedsigner.gui.components.qr_encoder import QREncoder +from seedsigner.gui.screens.qr_display_screen import QRDisplayScreen + +def show_keystone_export(gui, seed_phrase): + export_data = { + "type": "keystone-eip4527-export", + "mnemonic": seed_phrase, + } + + import json + payload = json.dumps(export_data) + + qr_encoder = QREncoder(gui, QRType.TEXT, payload) + screen = QRDisplayScreen(gui, qr_encoder) + screen.display_qr() + +def sign_keystone_eth_tx(gui): + from seedsigner.gui.screens import info_screen + info_screen.display_message(gui, "Sign ETH TX", "Feature coming soon.") diff --git a/seedsigner/patches/0001-add-keystone-wallet-pairing.patch b/seedsigner/patches/0001-add-keystone-wallet-pairing.patch new file mode 100644 index 000000000..b366ab275 --- /dev/null +++ b/seedsigner/patches/0001-add-keystone-wallet-pairing.patch @@ -0,0 +1,45 @@ +From a1b2c3d4e5f6g7 Mon Sep 17 00:00:00 2001 +From: Your Name +Date: 2025-08-07 +Subject: [PATCH 1/2] Add Keystone pairing QR export + +--- +seedsigner/gui/menus/main_menu.py | 12 ++++++++++++ +seedsigner/helpers/keystone.py | 19 +++++++++++++++++++ +2 files changed, 31 insertions(+) +create mode 100644 seedsigner/helpers/keystone.py + +diff --git a/seedsigner/gui/menus/main_menu.py b/seedsigner/gui/menus/main_menu.py +index 1234567..89abcde 100644 +--- a/seedsigner/gui/menus/main_menu.py ++++ b/seedsigner/gui/menus/main_menu.py +@@ def MAIN_MENU(self): ++ MenuOption("Export Keystone Pairing QR", "export_keystone_pairing_qr"), + +@@ def _handle_selected_option(self, option): ++ elif option == "Export Keystone Pairing QR": ++ from seedsigner.helpers import keystone ++ qr_data = keystone.get_pairing_qr() ++ self.qr_view_display_screen( ++ qr_data, ++ title="Keystone Pairing QR" ++ ) + +diff --git a/seedsigner/helpers/keystone.py b/seedsigner/helpers/keystone.py +new file mode 100644 +index 0000000..1111111 +--- /dev/null ++++ b/seedsigner/helpers/keystone.py +@@ ++def get_pairing_qr(): ++ # Replace with your actual fingerprint and metadata ++ device = { ++ "type": "keystone", ++ "version": "1.0.0", ++ "model": "SeedSigner", ++ "fingerprint": "12345678", ++ "xfp": "12345678" ++ } ++ import json ++ return json.dumps(device) + diff --git a/seedsigner/patches/0002-add-keystone-eth-sign.patch b/seedsigner/patches/0002-add-keystone-eth-sign.patch new file mode 100644 index 000000000..1b704cd70 --- /dev/null +++ b/seedsigner/patches/0002-add-keystone-eth-sign.patch @@ -0,0 +1,43 @@ +From b7c8d9e0f1a2b3 Mon Sep 17 00:00:00 2001 +From: Your Name +Date: 2025-08-07 +Subject: [PATCH 2/2] Add Keystone ETH signing support + +--- +seedsigner/gui/menus/signers_menu.py | 12 ++++++++++++ +seedsigner/helpers/keystone_sign.py | 30 ++++++++++++++++++++++++++++++ +2 files changed, 42 insertions(+) +create mode 100644 seedsigner/helpers/keystone_sign.py + +diff --git a/seedsigner/gui/menus/signers_menu.py b/seedsigner/gui/menus/signers_menu.py +index abcdef0..1234567 100644 +--- a/seedsigner/gui/menus/signers_menu.py ++++ b/seedsigner/gui/menus/signers_menu.py +@@ def SIGNER_MENU(self): ++ MenuOption("Sign Keystone ETH-TX", "sign_keystone_eth_tx"), + +@@ def _handle_selected_option(self, option): ++ elif option == "Sign Keystone ETH-TX": ++ from seedsigner.helpers import keystone_sign ++ eth_tx = self.qr_capture_input_screen("Scan ETH TX QR") ++ signed = keystone_sign.sign_eth_tx(eth_tx) ++ self.qr_view_display_screen( ++ signed, ++ title="Signed ETH TX" ++ ) + +diff --git a/seedsigner/helpers/keystone_sign.py b/seedsigner/helpers/keystone_sign.py +new file mode 100644 +index 0000000..2222222 +--- /dev/null ++++ b/seedsigner/helpers/keystone_sign.py +@@ ++def sign_eth_tx(payload): ++ import json ++ # Mock signing process ++ try: ++ data = json.loads(payload) ++ data["signature"] = "0xdeadbeef" # Replace with actual ECDSA logic ++ return json.dumps(data) ++ except Exception as e: ++ return json.dumps({"error": str(e)}) \ No newline at end of file