Merge remote-tracking branch 'origin' into mk4
This commit is contained in:
commit
689b63ea24
@ -146,6 +146,9 @@ Type: `urn:nfc:ext:bitcoin.org:sha256`
|
||||
Body: Exactly 32 bytes of binary. It's the SHA256 over the main
|
||||
payload (PSBT file, for example).
|
||||
|
||||
If present, this value will always directly preceed the object (txn
|
||||
or PSBT) that it covers. NFC-V has CRC16 over each low-level message,
|
||||
but that's all.
|
||||
|
||||
## TXID Value
|
||||
|
||||
@ -184,4 +187,10 @@ When the Coldcard has signed and finalized a transaction, it can
|
||||
share it in this format. Typically the user will want to broadcast
|
||||
this new transaction on the Bitcoin P2P network.
|
||||
|
||||
# Examples
|
||||
|
||||
This section will include a number of examples, with analysis of the content.
|
||||
|
||||
- __comming soon__
|
||||
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
our current XFP value and try to sign the input (same for change outputs and change-fraud
|
||||
checks). This makes building a workable PSBT file easier and could be used to preserve
|
||||
privacy of XFP value itself. A warning is shown when this happens.
|
||||
- Enhancement: "Advanced > Export XPUB" provides direct way to show XPUB (or ZPUB/YPUB) for
|
||||
BIP-84 / BIP-44 / BIP-49 standard derivations, as a QR. Also can show XFP and master XPUB.
|
||||
|
||||
## 4.1.3 - Sep 2, 2021
|
||||
|
||||
|
||||
@ -949,6 +949,68 @@ that you will need to import other wallet software to track balance.''' + SENSIT
|
||||
import export
|
||||
await export.make_summary_file()
|
||||
|
||||
async def export_xpub(label, _2, item):
|
||||
# provide bare xpub in a QR/NFC for import into simple wallets.
|
||||
import chains, glob, stash
|
||||
from public_constants import AF_CLASSIC
|
||||
from ux import show_qr_code
|
||||
|
||||
chain = chains.current_chain()
|
||||
acct = 0
|
||||
|
||||
# decode menu code => standard derivation
|
||||
mode = item.arg
|
||||
if mode == -1:
|
||||
# XFP shortcut
|
||||
xfp = xfp2str(settings.get('xfp', 0))
|
||||
await show_qr_code(xfp, True)
|
||||
return
|
||||
|
||||
elif mode == 0:
|
||||
path = "m"
|
||||
addr_fmt = AF_CLASSIC
|
||||
else:
|
||||
remap = {44:0, 49:1, 84:2}[mode]
|
||||
_, path, addr_fmt = chains.CommonDerivations[remap]
|
||||
path = path.format(account='{acct}', coin_type=chain.b44_cointype, change=0, idx=0)[:-4]
|
||||
|
||||
# always show SLIP-132 style, because defacto
|
||||
show_slip132 = (addr_fmt != AF_CLASSIC)
|
||||
|
||||
while 1:
|
||||
msg = '''Show QR of the XPUB for path:\n\n%s\n\n''' % path
|
||||
|
||||
if '{acct}' in path:
|
||||
msg += "Press 1 to select account other than zero. "
|
||||
#if glob.NFC:
|
||||
# msg = "Press 3 to share over NFC. "
|
||||
|
||||
ch = await ux_show_story(msg, escape='13')
|
||||
if ch == 'x': return
|
||||
if ch == '1':
|
||||
acct = await ux_enter_number('Account Number:', 9999) or 0
|
||||
path = path.format(acct=acct)
|
||||
continue
|
||||
|
||||
# assume zero account if not picked
|
||||
path = path.format(acct=acct)
|
||||
|
||||
# render xpub/ypub/zpub
|
||||
with stash.SensitiveValues() as sv:
|
||||
print(path)
|
||||
node = sv.derive_path(path) if path != 'm' else sv.node
|
||||
xpub = chain.serialize_public(node, addr_fmt)
|
||||
|
||||
#if ch == '3' and glob.NFC:
|
||||
# await glob.NFC.share_text(xpub)
|
||||
#else:
|
||||
if 1:
|
||||
from ux import show_qr_code
|
||||
await show_qr_code(xpub, False)
|
||||
|
||||
break
|
||||
|
||||
|
||||
def electrum_export_story(background=False):
|
||||
# saves memory being in a function
|
||||
return ('''\
|
||||
@ -1514,6 +1576,7 @@ to consume all but the final PIN attempt.\
|
||||
MenuItem('Brick Mode', chooser=set_countdown_pin_mode),
|
||||
]
|
||||
|
||||
|
||||
async def pin_changer(_1, _2, item):
|
||||
# Help them to change pins with appropriate warnings.
|
||||
# - forcing them to drill-down to get warning about secondary is on purpose
|
||||
|
||||
@ -299,7 +299,6 @@ def slip32_deserialize(xp):
|
||||
# - single signer only
|
||||
CommonDerivations = [
|
||||
# name, path.format(), addr format
|
||||
#Obsolete, removed: ( 'Electrum (not BIP-44)', "m/{change}/{idx}", AF_CLASSIC ),
|
||||
( 'BIP-44 / Electrum', "m/44'/{coin_type}'/{account}'/{change}/{idx}", AF_CLASSIC ),
|
||||
( 'BIP-49 (P2WPKH-nested-in-P2SH)', "m/49'/{coin_type}'/{account}'/{change}/{idx}",
|
||||
AF_P2WPKH_P2SH ), # generates 3xxx/2xxx p2sh-looking addresses
|
||||
|
||||
@ -91,6 +91,15 @@ SettingsMenu = [
|
||||
MenuItem('Display Units', chooser=value_resolution_chooser),
|
||||
]
|
||||
|
||||
XpubExportMenu = [
|
||||
# xxxxxxxxxxxxxxxx
|
||||
MenuItem("Segwit (BIP-84)", f=export_xpub, arg=84),
|
||||
MenuItem("Classic (BIP-44)", f=export_xpub, arg=44),
|
||||
MenuItem("P2WPKH/P2SH (49)", f=export_xpub, arg=49),
|
||||
MenuItem("Master XPUB", f=export_xpub, arg=0),
|
||||
MenuItem("Current XFP", f=export_xpub, arg=-1),
|
||||
]
|
||||
|
||||
WalletExportMenu = [
|
||||
# xxxxxxxxxxxxxxxx (alphabetical ordering)
|
||||
MenuItem("Bitcoin Core", f=bitcoin_core_skeleton),
|
||||
@ -98,6 +107,7 @@ WalletExportMenu = [
|
||||
MenuItem("Wasabi Wallet", f=wasabi_skeleton),
|
||||
MenuItem("Unchained Capital", f=unchained_capital_export),
|
||||
MenuItem("Generic JSON", f=generic_skeleton),
|
||||
MenuItem("Export XPUB", menu=XpubExportMenu),
|
||||
]
|
||||
|
||||
SDCardMenu = [
|
||||
@ -204,6 +214,7 @@ AdvancedNormalMenu = [
|
||||
MenuItem('Paper Wallets', f=make_paper_wallet, predicate=lambda: make_paper_wallet),
|
||||
MenuItem('User Management', menu=make_users_menu, predicate=lambda: version.has_fatram),
|
||||
MenuItem('Derive Entropy', f=drv_entro_start),
|
||||
MenuItem("Export XPUB", menu=XpubExportMenu),
|
||||
MenuItem("Danger Zone", menu=DangerZoneMenu),
|
||||
]
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ if ! touch repro-build.sh ; then
|
||||
cd firmware/external
|
||||
git submodule update --init
|
||||
cd ../stm32
|
||||
rsync -av /work/src/releases/*.dfu ../releases
|
||||
rsync --ignore-missing-args -av /work/src/releases/*.dfu ../releases
|
||||
fi
|
||||
|
||||
# need signit.py in path
|
||||
@ -34,7 +34,6 @@ python -m pip install --editable .
|
||||
cd ../stm32
|
||||
|
||||
cd ../releases
|
||||
ls *.dfu
|
||||
if [ -f *-v$VERSION_STRING-coldcard.dfu ]; then
|
||||
echo "Using existing binary in ../releases, not downloading."
|
||||
else
|
||||
|
||||
@ -150,6 +150,18 @@ def need_keypress(dev, request):
|
||||
raise pytest.fail('need to provide keypresses')
|
||||
|
||||
return doit
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def enter_number(need_keypress):
|
||||
def doit(number):
|
||||
number = str(number) if not isinstance(number, str) else number
|
||||
for d in number:
|
||||
need_keypress(d)
|
||||
need_keypress('y')
|
||||
|
||||
return doit
|
||||
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def master_xpub(dev):
|
||||
|
||||
@ -15,7 +15,7 @@ from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH, AF_P2WSH_P2SH
|
||||
from pprint import pprint
|
||||
|
||||
@pytest.mark.parametrize('acct_num', [ None, '0', '99', '123'])
|
||||
def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, bitcoind_wallet, bitcoind_d_wallet):
|
||||
def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, bitcoind_wallet, bitcoind_d_wallet, enter_number):
|
||||
# test UX and operation of the 'bitcoin core' wallet export
|
||||
from pycoin.contrib.segwit_addr import encode as sw_encode
|
||||
|
||||
@ -35,12 +35,10 @@ def test_export_core(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_sto
|
||||
if acct_num is not None:
|
||||
need_keypress('1')
|
||||
time.sleep(0.1)
|
||||
for n in acct_num:
|
||||
need_keypress(n)
|
||||
enter_number(acct_num)
|
||||
else:
|
||||
acct_num = '0'
|
||||
|
||||
need_keypress('y')
|
||||
need_keypress('y')
|
||||
|
||||
time.sleep(0.1)
|
||||
title, story = cap_story()
|
||||
@ -198,7 +196,7 @@ def test_export_wasabi(dev, cap_menu, pick_menu_item, goto_home, cap_story, need
|
||||
|
||||
@pytest.mark.parametrize('mode', [ "Legacy (P2PKH)", "P2SH-Segwit", "Native Segwit"])
|
||||
@pytest.mark.parametrize('acct_num', [ None, '0', '99', '123'])
|
||||
def test_export_electrum(mode, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path):
|
||||
def test_export_electrum(mode, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path, use_mainnet):
|
||||
# lightly test electrum wallet export
|
||||
|
||||
goto_home()
|
||||
@ -447,4 +445,70 @@ def test_export_public_txt(dev, cap_menu, pick_menu_item, goto_home, cap_story,
|
||||
|
||||
addr_vs_path(rhs, path=lhs, addr_fmt=f)
|
||||
|
||||
|
||||
@pytest.mark.qrcode
|
||||
@pytest.mark.parametrize('acct_num', [ None, 0, 99, 8989])
|
||||
def test_export_xpub(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number, cap_screen_qr, use_mainnet):
|
||||
# XPUB's via QR
|
||||
|
||||
use_mainnet()
|
||||
|
||||
goto_home()
|
||||
pick_menu_item('Advanced')
|
||||
pick_menu_item('Export XPUB')
|
||||
|
||||
top_items = cap_menu()
|
||||
for m in top_items:
|
||||
is_xfp = False
|
||||
if '-84' in m:
|
||||
expect = "m/84'/0'/{acct}'"
|
||||
elif '-44' in m:
|
||||
expect = "m/44'/0'/{acct}'"
|
||||
elif '49' in m:
|
||||
expect = "m/49'/0'/{acct}'"
|
||||
elif 'Master' in m:
|
||||
expect = "m"
|
||||
elif 'XFP' in m:
|
||||
is_xfp = True
|
||||
|
||||
pick_menu_item(m)
|
||||
time.sleep(0.1)
|
||||
if is_xfp:
|
||||
got = cap_screen_qr().decode('ascii')
|
||||
assert got == xfp2str(simulator_fixed_xfp).upper()
|
||||
need_keypress('x')
|
||||
continue
|
||||
|
||||
title, story = cap_story()
|
||||
assert expect in story
|
||||
|
||||
if 'acct' in expect:
|
||||
assert "Press 1 to select account" in story
|
||||
if acct_num is not None:
|
||||
need_keypress('1')
|
||||
enter_number(acct_num)
|
||||
|
||||
time.sleep(0.1)
|
||||
expect = expect.format(acct=acct_num)
|
||||
title, story = cap_story()
|
||||
assert expect in story
|
||||
assert "Press 1 to select account" not in story
|
||||
|
||||
expect = expect.format(acct=0)
|
||||
|
||||
need_keypress('y')
|
||||
got_pub = cap_screen_qr().decode('ascii')
|
||||
if got_pub[0] not in 'xt':
|
||||
got_pub,*_ = slip132undo(got_pub)
|
||||
|
||||
got = BIP32Node.from_wallet_key(got_pub)
|
||||
|
||||
wallet = BIP32Node.from_wallet_key(simulator_fixed_xprv)
|
||||
if expect != 'm':
|
||||
wallet = wallet.subkey_for_path(expect[2:])
|
||||
assert got.sec() == wallet.sec()
|
||||
|
||||
need_keypress('x')
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
Loading…
Reference in New Issue
Block a user