Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/actions/build-sim/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ runs:
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y gcc-arm-linux-gnueabihf libsdl2-image-dev libslirp-dev libpcsclite-dev ninja-build
pip install poetry
sudo apt-get install -y gcc-arm-linux-gnueabihf libsdl2-image-dev libslirp-dev libpcsclite-dev ninja-build libltdl-dev
pip install poetry uv
wget https://github.com/protocolbuffers/protobuf/releases/download/v22.0/protoc-22.0-linux-x86_64.zip
sudo unzip protoc-22.0-linux-x86_64.zip -d /usr/local
protoc --version
Expand All @@ -30,6 +30,9 @@ runs:
git config --global user.email '[email protected]'
git config --global user.name 'ci'
git config --global --add safe.directory "$GITHUB_WORKSPACE"
# Rewrite lwip URLs to github mirror to avoid stalling issue
git config --global --add url."https://github.com/lwip-tcpip/lwip.git".insteadOf "https://git.savannah.gnu.org/r/lwip.git"
git config --global --add url."https://github.com/lwip-tcpip/lwip.git".insteadOf "https://git.savannah.nongnu.org/git/lwip.git"
cd test
./setup_environment.sh --"${{ inputs.name }}"
cd ..
Expand Down
13 changes: 9 additions & 4 deletions .github/actions/install-sim/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,25 @@ runs:
apt-get update
apt-get install -y libusb-1.0-0 qemu-user-static
tar -xvf speculos.tar.gz
poetry run pip install construct flask-cors flask-restful jsonschema ledgered mnemonic pyelftools pillow requests pytesseract
pip install construct flask-cors flask-restful jsonschema ledgered mnemonic pyelftools pillow requests pytesseract
poetry run pip install -e test/work/speculos
pip install -e test/work/speculos

- if: inputs.device == 'ledger'
- if: startsWith(inputs.device, 'ledger')
uses: actions/download-artifact@v4
with:
name: ${{ inputs.device == 'ledger-legacy' && 'ledger_app_nano_s' || 'ledger_app_nano_x' }}
name: ${{ inputs.device == 'ledger-legacy' && 'ledger_app_legacy' || 'ledger_app' }}


- if: inputs.device == 'ledger'
shell: bash
run: |
mv app.elf test/work/speculos/apps/btc-test.elf

- if: inputs.device == 'ledger-legacy'
shell: bash
run: |
mv app.elf test/work/speculos/apps/btc-test-legacy.elf

- if: inputs.device == 'keepkey'
shell: bash
run: |
Expand Down
6 changes: 3 additions & 3 deletions .github/actions/test-dist/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@ runs:
if: matrix.test.script == 'Wheel'
shell: bash
run: |
cd test; ./run_tests.py $DEVICE --interface=cli --device-only; cd ..
cd test; ./run_tests.py --${{ matrix.device }} --interface=cli --device-only; cd ..

- name: Run tests (Sdist)
if: matrix.test.script == 'Sdist'
shell: bash
run: |
cd test; ./run_tests.py $DEVICE --interface=cli --device-only; cd ..
cd test; ./run_tests.py --${{ matrix.device }} --interface=cli --device-only; cd ..

- name: Run tests (Bindist)
if: matrix.test.script == 'Bindist'
shell: bash
run: |
cd test; poetry run ./run_tests.py $DEVICE --interface=bindist --device-only; cd ..
cd test; poetry run ./run_tests.py --${{ matrix.device }} --interface=bindist --device-only; cd ..

- if: failure()
shell: bash
Expand Down
35 changes: 14 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,7 @@ jobs:
with:
sim: trezor
include: ${{ needs.prepare-sim-matrices.outputs.trezor }}
# Ubuntu 22.04 ships with glibc 2.35, which is needed to keep Trezor 1
# binaries compatible with Debian Bookworm (glibc 2.36) Python containers.
# Trezor T binaries don't need this.
runs-on: ubuntu-22.04
runs-on: ubuntu-latest

sim-builder-coldcard:
name: Coldcard sim builder
Expand All @@ -126,7 +123,7 @@ jobs:
with:
sim: coldcard
include: ${{ needs.prepare-sim-matrices.outputs.coldcard }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest

sim-builder-bitbox:
name: Bitbox sim builder
Expand Down Expand Up @@ -162,27 +159,23 @@ jobs:
with:
sim: keepkey
include: ${{ needs.prepare-sim-matrices.outputs.keepkey }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest

ledger-s-app-builder:
name: Ledger Nano S Bitcoin App builder
uses: ./.github/workflows/ledger-app-builder.yml
ledger-legacy-app-builder:
name: Ledger Bitcoin Legacy App builder
uses: ./.github/workflows/ledger-legacy-app-builder.yml
with:
app: nano_s
runs-on: ubuntu-latest

ledger-x-app-builder:
name: Ledger Nano X Bitcoin App builder
ledger-app-builder:
name: Ledger Bitcoin App builder
uses: ./.github/workflows/ledger-app-builder.yml
with:
app: nano_x
runs-on: ubuntu-latest

bitcoind-builder:
name: bitcoind builder
# Ubuntu 22.04 ships with glibc 2.35, which is needed to keep binaries
# compatible with Debian Bookworm (glibc 2.36) Python containers.
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/build-bitcoind
Expand All @@ -201,16 +194,16 @@ jobs:
device: trezor-t
runs-on: ubuntu-latest

test-ledger-s:
test-ledger-legacy:
uses: ./.github/workflows/device-test.yml
needs: [sim-builder-ledger, ledger-s-app-builder, bitcoind-builder, dist-builder]
needs: [sim-builder-ledger, ledger-legacy-app-builder, bitcoind-builder, dist-builder]
with:
device: ledger-legacy
runs-on: ubuntu-latest

test-ledger-x:
test-ledger:
uses: ./.github/workflows/device-test.yml
needs: [sim-builder-ledger, ledger-x-app-builder, bitcoind-builder, dist-builder]
needs: [sim-builder-ledger, ledger-app-builder, bitcoind-builder, dist-builder]
with:
device: ledger
runs-on: ubuntu-latest
Expand All @@ -220,7 +213,7 @@ jobs:
needs: [sim-builder-coldcard, bitcoind-builder, dist-builder]
with:
device: coldcard
runs-on: ubuntu-22.04
runs-on: ubuntu-latest

test-bitbox01:
uses: ./.github/workflows/device-test.yml
Expand Down
11 changes: 4 additions & 7 deletions .github/workflows/ledger-app-builder.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
name: Ledger App Builder
name: Ledger Nano X App Builder
on:
workflow_call:
inputs:
app:
required: true # 'nano_s' or 'nano_x'
type: string
runs-on:
required: false
type: string
default: ubuntu-latest

jobs:
build:
name: Build ${{ inputs.app }}
name: Build Bitcoin App
runs-on: ${{ inputs.runs-on }}
container: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- run: |
git clone https://github.com/LedgerHQ/app-bitcoin-new.git
cd app-bitcoin-new
make DEBUG=1 ${{ inputs.app == 'nano_x' && 'BOLOS_SDK=$NANOX_SDK' || '' }}
make DEBUG=1 BOLOS_SDK=$NANOX_SDK
- uses: actions/upload-artifact@v4
with:
name: ${{ inputs.app == 'nano_x' && 'ledger_app_nano_x' || 'ledger_app_nano_s' }}
name: ledger_app
path: app-bitcoin-new/bin/app.elf
23 changes: 23 additions & 0 deletions .github/workflows/ledger-legacy-app-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Ledger App Builder
on:
workflow_call:
inputs:
runs-on:
required: false
type: string
default: ubuntu-latest

jobs:
build:
name: Build Bitcoin Legacy App
runs-on: ${{ inputs.runs-on }}
container: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest
steps:
- run: |
git clone https://github.com/LedgerHQ/app-bitcoin.git -b legacy-1.6.6
cd app-bitcoin
make DEBUG=1 BOLOS_SDK=$NANOSP_SDK
- uses: actions/upload-artifact@v4
with:
name: ledger_app_legacy
path: app-bitcoin/bin/app.elf
57 changes: 29 additions & 28 deletions test/data/coldcard-multisig.patch
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
From fd51e85693e0d66129133b1f195134aead1cf7d0 Mon Sep 17 00:00:00 2001
From: Andrew Chow <achow101-[email protected]>
Date: Tue, 17 Dec 2019 17:56:05 -0500
Subject: [PATCH 2/3] Change default simulator multisig
From 038e4b4f5e8128f3910745324332770b0973408c Mon Sep 17 00:00:00 2001
From: Ava Chow <[email protected]>
Date: Wed, 1 Oct 2025 13:30:01 -0700
Subject: [PATCH 1/2] Change default simulator multisig

---
unix/variant/sim_settings.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/unix/variant/sim_settings.py b/unix/variant/sim_settings.py
index 2706fb4..f9b533d 100644
index 01392d8f..527d0d9b 100644
--- a/unix/variant/sim_settings.py
+++ b/unix/variant/sim_settings.py
@@ -71,7 +71,11 @@ if '--ms' in sys.argv:
Expand All @@ -18,27 +18,28 @@ index 2706fb4..f9b533d 100644
- sim_defaults['multisig'] = [['MeMyself', [2, 4], [[3503269483, 'tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9'], [2389277556, 'tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc'], [3190206587, 'tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa'], [1130956047, 'tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n']], {'ch': 'XTN', 'pp': "45'"}]]
+ sim_defaults['multisig'] = [
+ ['mstest', [2, 3], [[1130956047, 0, 'tpubDF2rnouQaaYrR9x68P5Jm8WjhCE4atyGiPviFA9ve5iMnYbkTjof2HjzejcQcD7getPusDLPsWJLN2UttzK3pyVgBkRs52MiRZM7ZJ8TrEq'], [1130956047, 1, 'tpubDETRnZNJAqXiVeiL8UMDzCTBAoh3JvZkgXLdb1K2xzpJLepuJ6ka8jnVyRSkVh8Nbbo8u8dobZCsNENmRKipLzHNsS5mccjKSpXgSgavTQe'], [1130956047, 2, 'tpubDF3hdPQ5oDhtYjjaC596pboPii7UZmjqZcBPBRAbb6Bgn9hKoFxb8zWsBfdiCnTq3htUs2Yi2reeG3kMqHzZGZykJQAB5aKrJ8UfiXjmaLD']], {'ft': 8, 'ch': 'XTN', "d": ["48'/1'/0'/0'", "48'/1'/1'/0'", "48'/1'/2'/0'"]}],
+ ['mstest1', [2, 3], [[1130956047, 0, 'tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP'], [1130956047, 1, 'tpubDETRnZNJAqXiVeiL8UMDzCTBAoh3JvZkgXLdb1K2xzpJLepuJ6ka8jnVyRSkVh8Nbbo8u8dobZCsNENmRKipLzHNsS5mccjKSpXgSgavTQe'], [1130956047, 2, 'tpubDF3hdPQ5oDhtYjjaC596pboPii7UZmjqZcBPBRAbb6Bgn9hKoFxb8zWsBfdiCnTq3htUs2Yi2reeG3kMqHzZGZykJQAB5aKrJ8UfiXjmaLD']], {'ft': 14, 'ch': 'XTN', "d": ["48'/1'/0'/0'", "48'/1'/1'/0'", "48'/1'/2'/0'"]}],
+ ['mstest2', [2, 3], [[1130956047, 0, 'tpubDF2rnouQaaYrXF4noGTv6rQYmx87cQ4GrUdhpvXkhtChwQPbdGTi8GA88NUaSrwZBwNsTkC9bFkkC8vDyGBVVAQTZ2AS6gs68RQXtXcCvkP'], [1130956047, 1, 'tpubDETRnZNJAqXiVeiL8UMDzCTBAoh3JvZkgXLdb1K2xzpJLepuJ6ka8jnVyRSkVh8Nbbo8u8dobZCsNENmRKipLzHNsS5mccjKSpXgSgavTQe'], [1130956047, 2, 'tpubDF3hdPQ5oDhtYjjaC596pboPii7UZmjqZcBPBRAbb6Bgn9hKoFxb8zWsBfdiCnTq3htUs2Yi2reeG3kMqHzZGZykJQAB5aKrJ8UfiXjmaLD']], {'ft': 26, 'ch': 'XTN', "d": ["48'/1'/0'/0'", "48'/1'/1'/0'", "48'/1'/2'/0'"]}],
+ ['mstest1', [2, 3], [[1130956047, 0, 'tpubDF2rnouQaaYrR9x68P5Jm8WjhCE4atyGiPviFA9ve5iMnYbkTjof2HjzejcQcD7getPusDLPsWJLN2UttzK3pyVgBkRs52MiRZM7ZJ8TrEq'], [1130956047, 1, 'tpubDETRnZNJAqXiVeiL8UMDzCTBAoh3JvZkgXLdb1K2xzpJLepuJ6ka8jnVyRSkVh8Nbbo8u8dobZCsNENmRKipLzHNsS5mccjKSpXgSgavTQe'], [1130956047, 2, 'tpubDF3hdPQ5oDhtYjjaC596pboPii7UZmjqZcBPBRAbb6Bgn9hKoFxb8zWsBfdiCnTq3htUs2Yi2reeG3kMqHzZGZykJQAB5aKrJ8UfiXjmaLD']], {'ft': 14, 'ch': 'XTN', "d": ["48'/1'/0'/0'", "48'/1'/1'/0'", "48'/1'/2'/0'"]}],
+ ['mstest2', [2, 3], [[1130956047, 0, 'tpubDF2rnouQaaYrR9x68P5Jm8WjhCE4atyGiPviFA9ve5iMnYbkTjof2HjzejcQcD7getPusDLPsWJLN2UttzK3pyVgBkRs52MiRZM7ZJ8TrEq'], [1130956047, 1, 'tpubDETRnZNJAqXiVeiL8UMDzCTBAoh3JvZkgXLdb1K2xzpJLepuJ6ka8jnVyRSkVh8Nbbo8u8dobZCsNENmRKipLzHNsS5mccjKSpXgSgavTQe'], [1130956047, 2, 'tpubDF3hdPQ5oDhtYjjaC596pboPii7UZmjqZcBPBRAbb6Bgn9hKoFxb8zWsBfdiCnTq3htUs2Yi2reeG3kMqHzZGZykJQAB5aKrJ8UfiXjmaLD']], {'ft': 26, 'ch': 'XTN', "d": ["48'/1'/0'/0'", "48'/1'/1'/0'", "48'/1'/2'/0'"]}],
+ ]
sim_defaults['fee_limit'] = -1

if '--xfp' in sys.argv:
--
2.38.1
2.51.0

From 8b4323c1e393d79d46248dd822ca9aaaeb2b2bc3 Mon Sep 17 00:00:00 2001

From 3f7b6dfcf72788a4a0784eb871462c289e8a747b Mon Sep 17 00:00:00 2001
From: Sjors Provoost <[email protected]>
Date: Wed, 23 Jul 2025 10:16:22 +0200
Subject: [PATCH] Allow multisigs to share master fingerprint
Subject: [PATCH 2/2] Allow multisigs to share master fingerprint

Co-Authored-By: Ava Chow <[email protected]>
---
shared/multisig.py | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
shared/multisig.py | 39 ++++++++++++++++++++++++---------------
1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/shared/multisig.py b/shared/multisig.py
index 446998a2..cabb2003 100644
index 1e692d41..6294e9a3 100644
--- a/shared/multisig.py
+++ b/shared/multisig.py
@@ -144,9 +144,9 @@ class MultisigWallet(WalletABC):
Expand All @@ -53,7 +54,7 @@ index 446998a2..cabb2003 100644

@classmethod
def render_addr_fmt(cls, addr_fmt):
@@ -270,7 +270,11 @@ class MultisigWallet(WalletABC):
@@ -275,7 +275,11 @@ class MultisigWallet(WalletABC):

def get_xfp_paths(self):
# return list of lists [xfp, *deriv]
Expand All @@ -65,8 +66,8 @@ index 446998a2..cabb2003 100644
+ return ret

@classmethod
def find_match(cls, M, N, xfp_paths, addr_fmt=None):
@@ -305,24 +309,31 @@ class MultisigWallet(WalletABC):
def find_match(cls, M, N, xfp_paths, addr_fmts=None):
@@ -305,24 +309,29 @@ class MultisigWallet(WalletABC):
# the same prefix path per-each xfp, as indicated
# xfp_paths (unordered)?
# - could also check non-prefix part is all non-hardened
Expand All @@ -79,35 +80,35 @@ index 446998a2..cabb2003 100644
if x[0] not in self.xfp_paths:
return False
- prefix = self.xfp_paths[x[0]]
-
- if len(x) < len(prefix):
- # PSBT specs a path shorter than wallet's xpub
- #print('path len: %d vs %d' % (len(prefix), len(x)))
- return False
-
- comm = len(prefix)
- if tuple(prefix[:comm]) != tuple(x[:comm]):
- # xfp => maps to wrong path
- #print('path mismatch:\n%r\n%r\ncomm=%d' % (prefix[:comm], x[:comm], comm))
+ for prefix in self.xfp_paths[x[0]]:
+ if len(x) < len(prefix):
+ # PSBT specs a path shorter than wallet's xpub
+ #print('path len: %d vs %d' % (len(prefix), len(x)))
+ return False

- if len(x) < len(prefix):
- # PSBT specs a path shorter than wallet's xpub
- #print('path len: %d vs %d' % (len(prefix), len(x)))
- return False
+
+ comm = len(prefix)
+ if tuple(prefix[:comm]) != tuple(x[:comm]):
+ # xfp => maps to wrong path
+ # But maybe there is another path that does match, so keep going
+ #print('path mismatch:\n%r\n%r\ncomm=%d' % (prefix[:comm], x[:comm], comm))
+ continue
+ else:
+ # Found a match, cleanly exit
+ break
+ else:
+ # No match was found

- comm = len(prefix)
- if tuple(prefix[:comm]) != tuple(x[:comm]):
- # xfp => maps to wrong path
- #print('path mismatch:\n%r\n%r\ncomm=%d' % (prefix[:comm], x[:comm], comm))
return False

return True
--
2.39.5 (Apple Git-154)
2.51.0

25 changes: 25 additions & 0 deletions test/data/nanopb-deprecated-mode.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
From 4f69dc003f6f1092cbcdb0152fdee0e303583c8b Mon Sep 17 00:00:00 2001
From: Ava Chow <[email protected]>
Date: Thu, 2 Oct 2025 12:42:50 -0700
Subject: [PATCH] Remove deprecated file mode

---
generator/nanopb_generator.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/generator/nanopb_generator.py b/generator/nanopb_generator.py
index 12af67e..7107198 100755
--- a/generator/nanopb_generator.py
+++ b/generator/nanopb_generator.py
@@ -1625,7 +1625,7 @@ def parse_file(filename, fdesc, options):
optfilename = os.path.join(p, optfilename)
if options.verbose:
sys.stderr.write('Reading options from ' + optfilename + '\n')
- Globals.separate_options = read_options_file(open(optfilename, "rU"))
+ Globals.separate_options = read_options_file(open(optfilename, "r"))
break
else:
# If we are given a full filename and it does not exist, give an error.
--
2.51.0

Loading
Loading