diff --git a/core/src/apps/bitcoin/keychain.py b/core/src/apps/bitcoin/keychain.py index c8878b840..eeb34e0d2 100644 --- a/core/src/apps/bitcoin/keychain.py +++ b/core/src/apps/bitcoin/keychain.py @@ -29,12 +29,19 @@ if False: MsgIn = TypeVar("MsgIn", bound=MsgWithCoinName) HandlerWithCoinInfo = Callable[..., Awaitable[MsgOut]] -# common patterns + +# BIP-45 for multisig: https://github.com/bitcoin/bips/blob/master/bip-0045.mediawiki PATTERN_BIP45 = "m/45'/[0-100]/change/address_index" -PATTERN_PURPOSE48_LEGACY = "m/48'/coin_type'/account'/0'/change/address_index" + +# Electrum Purpose48. See docs/misc/purpose48.md +# Electrum does not seem to use the raw script type, it is included here for completeness. +PATTERN_PURPOSE48_RAW = "m/48'/coin_type'/account'/0'/change/address_index" PATTERN_PURPOSE48_P2SHSEGWIT = "m/48'/coin_type'/account'/1'/change/address_index" PATTERN_PURPOSE48_SEGWIT = "m/48'/coin_type'/account'/2'/change/address_index" + +# BIP-49 for segwit-in-P2SH: https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki PATTERN_BIP49 = "m/49'/coin_type'/account'/change/address_index" +# BIP-84 for segwit: https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki PATTERN_BIP84 = "m/84'/coin_type'/account'/change/address_index" # compatibility patterns, will be removed in the future @@ -72,7 +79,7 @@ def validate_path_against_script_type( elif script_type in (I.SPENDADDRESS, I.SPENDMULTISIG) and multisig: patterns.append(PATTERN_BIP45) - patterns.append(PATTERN_PURPOSE48_LEGACY) + patterns.append(PATTERN_PURPOSE48_RAW) if coin.coin_name in BITCOIN_NAMES: patterns.append(PATTERN_GREENADDRESS_A) patterns.append(PATTERN_GREENADDRESS_B) @@ -104,7 +111,7 @@ def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]: patterns = [ PATTERN_BIP44, PATTERN_BIP45, - PATTERN_PURPOSE48_LEGACY, + PATTERN_PURPOSE48_RAW, ] # compatibility patterns diff --git a/core/src/apps/common/paths.py b/core/src/apps/common/paths.py index 59ffe57e3..e40858014 100644 --- a/core/src/apps/common/paths.py +++ b/core/src/apps/common/paths.py @@ -238,8 +238,13 @@ class _NeverMatchingSchema: AlwaysMatchingSchema: PathSchemaType = _AlwaysMatchingSchema # type: ignore NeverMatchingSchema: PathSchemaType = _NeverMatchingSchema # type: ignore +# BIP-44 for basic (legacy) Bitcoin accounts, and widely used for other currencies: +# https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki PATTERN_BIP44 = "m/44'/coin_type'/account'/change/address_index" +# BIP-44 public key export, starting at end of the hardened part PATTERN_BIP44_PUBKEY = "m/44'/coin_type'/account'/*" +# SEP-0005 for non-UTXO-based currencies, defined by Stellar: +# https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md PATTERN_SEP5 = "m/44'/coin_type'/account'" diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index b3bbbd86c..c1b05a062 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -42,5 +42,6 @@ - [Generated Files](misc/generated-files.md) - [Git Hooks](misc/git-hooks.md) - [Monorepo Notes](misc/monorepo.md) + - [Purpose48 derivation scheme](misc/purpose48.md) - [Review Process](misc/review.md) - [TOIF Image Format](misc/toif.md) diff --git a/docs/misc/purpose48.md b/docs/misc/purpose48.md new file mode 100644 index 000000000..f484a8839 --- /dev/null +++ b/docs/misc/purpose48.md @@ -0,0 +1,52 @@ +# Purpose48 derivation scheme + +Per [BIP-43], the first level of the derivation path is used as a "purpose". The purpose +number is usually selected to match the BIP number: e.g., BIP-49 uses purpose `49'`. + +There is no officially proposed **BIP-48** standard. Despite that, a de-facto standard +for the purpose `48'` exists in the wild and is implemented by several HD wallets, most +notably [Electrum]. This standard was never before formally +specified, and this document aims to rectify the situation. + +## Motivation + +Purpose48 is intended for multisig scenarios. It allows using multiple script types from +a single logical account or root key, while keeping multisig keys separate from +single-sig keys. + +## Specification + +The following BIP-32 path levels are defined: + +``` +m / 48' / coin_type' / account' / script_type' / change / address_index +``` + +Meaning of all fields except `script_type` is defined in [BIP-44] + +`script_type` can have the following values: + +* `0`: raw [BIP-11] (p2ms) multisig +* `1`: p2sh-wrapped segwit multisig (p2wsh-p2sh) +* `2`: native segwit multisig (p2wsh) + +The path derivation is hardened up to and including the `script_type` field. + +## Trezor implementation + +`script_type` value `0` corresponds to `SPENDMULTISIG`/`PAYTOMULTISIG`. + +Value `1` corresponds to `SPENDP2SHWITNESS`/`PAYTOP2SHWITNESS`. + +Value `2` corresponds to `SPENDWITNESS`/`PAYTOWITNESS`. + +## References + +Electrum implementation: https://github.com/spesmilo/electrum/blob/9931df9f254e49eb929723be62af61971b3032c8/electrum/keystore.py#L862-L889 + +Trezor implementation: TBD + +[Electrum]: https://electrum.org/ +[BIP-11]: https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki +[BIP-43]: https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki +[BIP-44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki