docs(core): replace Purpose48 with BIP-48

https://github.com/bitcoin/bips/blob/master/bip-0048.mediawiki
pull/1754/head
Martin Milata 3 years ago
parent e4be53459d
commit b5710b820a

@ -0,0 +1 @@
Refer to `m/48'/...` multisig derivation paths as BIP-48 instead of Purpose48.

@ -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,
)
)

@ -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)

@ -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

@ -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,

Loading…
Cancel
Save