From b5710b820a6c90357f3b3325f29e3e849035068c Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Fri, 30 Jul 2021 17:39:14 +0200 Subject: [PATCH] docs(core): replace Purpose48 with BIP-48 https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki --- core/.changelog.d/1744.changed | 1 + core/src/apps/bitcoin/keychain.py | 23 ++++---- docs/SUMMARY.md | 1 - docs/misc/purpose48.md | 52 ------------------- .../device_tests/test_msg_getaddress_show.py | 10 ++-- 5 files changed, 18 insertions(+), 69 deletions(-) create mode 100644 core/.changelog.d/1744.changed delete mode 100644 docs/misc/purpose48.md diff --git a/core/.changelog.d/1744.changed b/core/.changelog.d/1744.changed new file mode 100644 index 000000000..748780be5 --- /dev/null +++ b/core/.changelog.d/1744.changed @@ -0,0 +1 @@ +Refer to `m/48'/...` multisig derivation paths as BIP-48 instead of Purpose48. diff --git a/core/src/apps/bitcoin/keychain.py b/core/src/apps/bitcoin/keychain.py index 13948e651..a00d9d534 100644 --- a/core/src/apps/bitcoin/keychain.py +++ b/core/src/apps/bitcoin/keychain.py @@ -35,11 +35,12 @@ if False: # BIP-45 for multisig: https://github.com/bitcoin/bips/blob/master/bip-0045.mediawiki PATTERN_BIP45 = "m/45'/[0-100]/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-48 for multisig: https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki +# The raw script type is not part of the BIP (and Electrum, as a notable implementation, +# does not use it), it is included here for completeness. +PATTERN_BIP48_RAW = "m/48'/coin_type'/account'/0'/change/address_index" +PATTERN_BIP48_P2SHSEGWIT = "m/48'/coin_type'/account'/1'/change/address_index" +PATTERN_BIP48_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" @@ -97,7 +98,7 @@ def validate_path_against_script_type( script_type in (InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG) and multisig ): - patterns.append(PATTERN_PURPOSE48_RAW) + patterns.append(PATTERN_BIP48_RAW) if coin.slip44 == SLIP44_BITCOIN or ( coin.fork_id is not None and coin.slip44 != SLIP44_TESTNET ): @@ -113,7 +114,7 @@ def validate_path_against_script_type( elif coin.segwit and script_type == InputScriptType.SPENDP2SHWITNESS: patterns.append(PATTERN_BIP49) if multisig: - patterns.append(PATTERN_PURPOSE48_P2SHSEGWIT) + patterns.append(PATTERN_BIP48_P2SHSEGWIT) if coin.slip44 == SLIP44_BITCOIN: patterns.append(PATTERN_GREENADDRESS_A) patterns.append(PATTERN_GREENADDRESS_B) @@ -123,7 +124,7 @@ def validate_path_against_script_type( elif coin.segwit and script_type == InputScriptType.SPENDWITNESS: patterns.append(PATTERN_BIP84) if multisig: - patterns.append(PATTERN_PURPOSE48_SEGWIT) + patterns.append(PATTERN_BIP48_SEGWIT) if coin.slip44 == SLIP44_BITCOIN: patterns.append(PATTERN_GREENADDRESS_A) patterns.append(PATTERN_GREENADDRESS_B) @@ -137,7 +138,7 @@ def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]: # basic patterns patterns = [ PATTERN_BIP44, - PATTERN_PURPOSE48_RAW, + PATTERN_BIP48_RAW, ] # patterns without coin_type field must be treated as if coin_type == 0 @@ -173,8 +174,8 @@ def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]: ( PATTERN_BIP49, PATTERN_BIP84, - PATTERN_PURPOSE48_P2SHSEGWIT, - PATTERN_PURPOSE48_SEGWIT, + PATTERN_BIP48_P2SHSEGWIT, + PATTERN_BIP48_SEGWIT, ) ) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index bb6441daf..5882198b1 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -43,7 +43,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) - [Changelog](misc/changelog.md) - [TOIF Image Format](misc/toif.md) diff --git a/docs/misc/purpose48.md b/docs/misc/purpose48.md deleted file mode 100644 index f484a8839..000000000 --- a/docs/misc/purpose48.md +++ /dev/null @@ -1,52 +0,0 @@ -# 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 diff --git a/tests/device_tests/test_msg_getaddress_show.py b/tests/device_tests/test_msg_getaddress_show.py index 447130189..dc84f19ad 100644 --- a/tests/device_tests/test_msg_getaddress_show.py +++ b/tests/device_tests/test_msg_getaddress_show.py @@ -92,7 +92,7 @@ def test_show_multisig_3(client): ) -VECTORS_MULTISIG = ( # script_type, purpose48_type, address, xpubs, ignore_xpub_magic +VECTORS_MULTISIG = ( # script_type, bip48_type, address, xpubs, ignore_xpub_magic ( messages.InputScriptType.SPENDMULTISIG, 0, @@ -165,15 +165,15 @@ VECTORS_MULTISIG = ( # script_type, purpose48_type, address, xpubs, ignore_xpub @pytest.mark.skip_t1 @pytest.mark.multisig @pytest.mark.parametrize( - "script_type, purpose48_type, address, xpubs, ignore_xpub_magic", VECTORS_MULTISIG + "script_type, bip48_type, address, xpubs, ignore_xpub_magic", VECTORS_MULTISIG ) def test_show_multisig_xpubs( - client, script_type, purpose48_type, address, xpubs, ignore_xpub_magic + client, script_type, bip48_type, address, xpubs, ignore_xpub_magic ): nodes = [ btc.get_public_node( client, - tools.parse_path(f"48h/0h/{i}h/{purpose48_type}h"), + tools.parse_path(f"48h/0h/{i}h/{bip48_type}h"), coin_name="Bitcoin", ) for i in range(3) @@ -235,7 +235,7 @@ def test_show_multisig_xpubs( btc.get_address( client, "Bitcoin", - tools.parse_path(f"48h/0h/{i}h/{purpose48_type}h/0/0"), + tools.parse_path(f"48h/0h/{i}h/{bip48_type}h/0/0"), show_display=True, multisig=multisig, script_type=script_type,