You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
.github/workflows/bitbox-simulator.yml runs the BitBox02 firmware-simulator with bitbox-testkit@v0.5.0 against the official bitbox02-firmware/simulator Docker image. Today the workflow paths: filter (lines 37-45) triggers only on:
lib/packages/hardware_wallet/**
lib/packages/wallet/**
lib/screens/hardware_connect_bitbox/**
their test mirrors
pubspec.yaml
the workflow itself
That filter is path-triggered, not opt-in (the slash-command variant bitbox-simulator-slash.yml is the opt-in path). Broadening paths: makes the workflow trigger on more PRs, not fewer.
What testkit v0.5.0 already covers
Verified from DFXswiss/bitbox-testkit:go/bitbox/simulator/scenarios.goBaselineScenarios():
So "realunit-app's derivation path is uncovered" would be wrong. What EthAddressMainnet does NOT do is assert a specific address value — it only checks the returned string has the expected length/format. That's the gap.
Similarly: EthSignTypedDataKycMultiPage already feeds the realunit-app EIP-712 payload (realUnitUserKycPayload constant in scenarios.go mirrors the KYC registration shape). And EthSignMessageAscii/EthSignMessageBoundary already cover signPersonalMessage (the DFX auth signature path).
So the KYC sign ceremony and DFX auth signature are at Tier 2 today. They are NOT the gap.
Realunit-app surface
Verified from lib/packages/utils/default_assets.dart:
The Tier-2 matrix is exactly 2 assets, 1 derivation path, no BTC.
Scope — gaps actually worth a new testkit scenario
Each item is a candidate for a PR in DFXswiss/bitbox-testkit, followed by an action-pin bump on .github/workflows/bitbox-simulator.yml:72. This is a cross-repo workflow.
SellBitboxEIP1559RealUnitTx — EIP-1559 sell-BitBox transaction sign with realunit-app's exact to (the sell router/relay address), value: 0, data (ERC-20 transfer calldata produced by _sellService.createUnsignedTransactions server-side), gasLimit, maxFeePerGas, maxPriorityFeePerGas shape. EthSignEIP1559Mainnet exists but uses a generic payload — confirm whether realunit's tx bytes match or need their own baseline.
SellBitboxFailureBranches — assert the simulator returns the expected error class for each FakeBitboxBehavior equivalent on the firmware side (user-cancel, malformed payload, mid-stream disconnect). Today these branches are only covered at Tier 0 with the Dart fake.
EthAddressRealUnitMainnetExact — EthAddressMainnet already uses the right derivation path but asserts only the address-string shape. The realunit-specific scenario should assert the exact address derived from the simulator's fixture mnemonic, so the testkit catches any byte-level drift in the simulator's BIP-32 implementation.
EthAddressRealUnitSepoliaExact — same shape for Sepolia (chainId: 11155111).
Out of scope — Tier 2 already covers these
Buy/Sell-Sign personal-message ceremony — BuyPaymentInfoCubit / SellPaymentInfoCubit use DfxAuthService.signMessage (auth-token JWT, personal-message sign). Already covered by EthSignMessageAscii + EthSignMessageBoundary. Only SellBitboxCubit adds a per-transaction EIP-1559 sign — that's the only real new scenario.
"Mnemonic-based restore" — The BitBox02 firmware simulator restores from a hard-coded fixture mnemonic ("boring mistake dish oyster …" in testkit RestoreSimulatorMnemonic); it does NOT accept user-entered mnemonic. The realunit-app RestoreWalletCubit does software-wallet BIP-39 restore (bip39.validateMnemonic in WalletService.restoreWallet) — not BitBox. The two restore paths are unrelated; nothing to add at Tier 2 for the app-side restore.
Workflow changes
Broaden bitbox-simulator.ymlpaths: (currently lines 37-45) to also include lib/screens/sell_bitbox/** + its test mirror, so PRs to the actual sell-BitBox surface trigger the simulator. Do not broaden to lib/screens/buy/** or lib/screens/sell/** — those don't call BitBox per-tx (only signMessage via DfxAuthService, which is already covered).
The bitbox-simulator-slash.yml workflow is issue_comment-triggered with no paths: filter — there is nothing to "expand" there. If broader opt-in coverage is wanted, update the slash-command workflow's comment block to document when maintainers should fire it.
Document the bitbox_flutter v0.0.7 pin (resolved-ref ebe0fb04e0fb1d56ae6fa815277598c980ac1940 in pubspec.lock:73) inside the workflow's comment header so simulator-vs-Dart-plugin compatibility is reviewable in git blame.
Cross-repo dependency
This issue cannot land here alone. The actual scenario code lives in DFXswiss/bitbox-testkit:
PR in DFXswiss/bitbox-testkit adding the scenario(s) under go/bitbox/simulator/scenarios.go.
Tag/release new testkit version (v0.5.1 or v0.6.0).
Bump action ref pin on .github/workflows/bitbox-simulator.yml:72 (currently @45a1253d23b545d801cf5a1f42c040b85e389c7d # v0.5.0).
A tracking issue in DFXswiss/bitbox-testkit is owned by a separate decision-issue (see Related).
Acceptance criteria
Every new scenario runs against the upstream bitbox02-firmware/simulator Docker image via testkit
Failures are diff-able: scenario captures the sign payload the simulator received vs. expected baseline
CI cost: bitbox-simulator runs on ubuntu-latest (timeout-minutes: 15); broadening paths to lib/screens/sell_bitbox/** adds ≤ N runs per week where N = PR volume on that surface — bounded
Context
.github/workflows/bitbox-simulator.ymlruns the BitBox02 firmware-simulator withbitbox-testkit@v0.5.0against the officialbitbox02-firmware/simulatorDocker image. Today the workflowpaths:filter (lines 37-45) triggers only on:lib/packages/hardware_wallet/**lib/packages/wallet/**lib/screens/hardware_connect_bitbox/**pubspec.yamlThat filter is path-triggered, not opt-in (the slash-command variant
bitbox-simulator-slash.ymlis the opt-in path). Broadeningpaths:makes the workflow trigger on more PRs, not fewer.What testkit v0.5.0 already covers
Verified from
DFXswiss/bitbox-testkit:go/bitbox/simulator/scenarios.goBaselineScenarios():PairAndDeviceInfo,RestoreSimulatorMnemonic,RootFingerprintDeterministicEthAddressMainnet,EthAddressPolygonMultiByteV,EthSignMessageAscii,EthSignMessageBoundary,EthSignEIP1559Mainnet,EthSignTypedDataKycMultiPage,EthSignTypedDataNonAsciiRejected,EthSignLegacyPolygonMultiByteVBtcXpubZpubMainnet,BtcAddressP2WPKHMainnet,BtcAddressP2TRTaproot,BtcSignMessageMainnetImportant:
EthAddressMainnetalready uses the pathm/44'/60'/0'/0/0— exactly realunit-app's derivation. The testkit code:So "realunit-app's derivation path is uncovered" would be wrong. What
EthAddressMainnetdoes NOT do is assert a specific address value — it only checks the returned string has the expected length/format. That's the gap.Similarly:
EthSignTypedDataKycMultiPagealready feeds the realunit-app EIP-712 payload (realUnitUserKycPayloadconstant inscenarios.gomirrors the KYC registration shape). AndEthSignMessageAscii/EthSignMessageBoundaryalready coversignPersonalMessage(the DFX auth signature path).So the KYC sign ceremony and DFX auth signature are at Tier 2 today. They are NOT the gap.
Realunit-app surface
Verified from
lib/packages/utils/default_assets.dart:realUnitAsset— Ethereum mainnet,chainId: 1, contract0x553C7f9C780316FC1D34b8e14ac2465Ab22a090B, ERC-20realUnitTestAsset— Sepolia,chainId: 11155111, same contract symbolm/44'/60'/0'/0/0(lib/packages/hardware_wallet/bitbox_credentials.dart:17)The Tier-2 matrix is exactly 2 assets, 1 derivation path, no BTC.
Scope — gaps actually worth a new testkit scenario
Each item is a candidate for a PR in
DFXswiss/bitbox-testkit, followed by an action-pin bump on.github/workflows/bitbox-simulator.yml:72. This is a cross-repo workflow.SellBitboxEIP1559RealUnitTx— EIP-1559 sell-BitBox transaction sign with realunit-app's exactto(the sell router/relay address),value: 0,data(ERC-20 transfer calldata produced by_sellService.createUnsignedTransactionsserver-side),gasLimit,maxFeePerGas,maxPriorityFeePerGasshape.EthSignEIP1559Mainnetexists but uses a generic payload — confirm whether realunit's tx bytes match or need their own baseline.SellBitboxFailureBranches— assert the simulator returns the expected error class for eachFakeBitboxBehaviorequivalent on the firmware side (user-cancel, malformed payload, mid-stream disconnect). Today these branches are only covered at Tier 0 with the Dart fake.EthAddressRealUnitMainnetExact—EthAddressMainnetalready uses the right derivation path but asserts only the address-string shape. The realunit-specific scenario should assert the exact address derived from the simulator's fixture mnemonic, so the testkit catches any byte-level drift in the simulator's BIP-32 implementation.EthAddressRealUnitSepoliaExact— same shape for Sepolia (chainId: 11155111).Out of scope — Tier 2 already covers these
Buy/Sell-Sign personal-message ceremony—BuyPaymentInfoCubit/SellPaymentInfoCubituseDfxAuthService.signMessage(auth-token JWT, personal-message sign). Already covered byEthSignMessageAscii+EthSignMessageBoundary. OnlySellBitboxCubitadds a per-transaction EIP-1559 sign — that's the only real new scenario."Mnemonic-based restore"— The BitBox02 firmware simulator restores from a hard-coded fixture mnemonic ("boring mistake dish oyster …"in testkitRestoreSimulatorMnemonic); it does NOT accept user-entered mnemonic. The realunit-appRestoreWalletCubitdoes software-wallet BIP-39 restore (bip39.validateMnemonicinWalletService.restoreWallet) — not BitBox. The two restore paths are unrelated; nothing to add at Tier 2 for the app-side restore.Workflow changes
bitbox-simulator.ymlpaths:(currently lines 37-45) to also includelib/screens/sell_bitbox/**+ its test mirror, so PRs to the actual sell-BitBox surface trigger the simulator. Do not broaden tolib/screens/buy/**orlib/screens/sell/**— those don't call BitBox per-tx (onlysignMessageviaDfxAuthService, which is already covered).bitbox-simulator-slash.ymlworkflow isissue_comment-triggered with nopaths:filter — there is nothing to "expand" there. If broader opt-in coverage is wanted, update the slash-command workflow's comment block to document when maintainers should fire it.bitbox_flutter v0.0.7pin (resolved-refebe0fb04e0fb1d56ae6fa815277598c980ac1940inpubspec.lock:73) inside the workflow's comment header so simulator-vs-Dart-plugin compatibility is reviewable ingit blame.Cross-repo dependency
This issue cannot land here alone. The actual scenario code lives in
DFXswiss/bitbox-testkit:DFXswiss/bitbox-testkitadding the scenario(s) undergo/bitbox/simulator/scenarios.go.v0.5.1orv0.6.0)..github/workflows/bitbox-simulator.yml:72(currently@45a1253d23b545d801cf5a1f42c040b85e389c7d # v0.5.0).A tracking issue in
DFXswiss/bitbox-testkitis owned by a separate decision-issue (see Related).Acceptance criteria
bitbox02-firmware/simulatorDocker image via testkitubuntu-latest(timeout-minutes: 15); broadening paths tolib/screens/sell_bitbox/**adds ≤ N runs per week where N = PR volume on that surface — boundedEstimated effort
SellBitboxEIP1559RealUnitTxscenario in bitbox-testkit (Go + payload bytes)SellBitboxFailureBranchesscenarioEthAddressRealUnitMainnetExact+EthAddressRealUnitSepoliaExactpaths:broadening + workflow comment-block updateMost of the effort is in bitbox-testkit (Go scenarios), not in this repo.
Related
.github/workflows/bitbox-simulator.yml,.github/workflows/bitbox-simulator-slash.ymlDFXswiss/bitbox-testkit— scenario code lives therebitbox_flutterfork (DFXswiss/bitbox_flutter, refv0.0.7)auth_sign_flow_test.dartoverlaps intentionally withEthSignMessage*at this tier)bitbox-testkitscenarios #557 — bitbox-testkit cross-repo tracking