Пример #1
0
def test_change_p2sh_p2wpkh(start_sign, end_sign, check_against_bitcoind,
                            cap_story, case):
    # not fraud: output address encoded in various equiv forms
    from pycoin.tx.Tx import Tx
    from pycoin.tx.TxOut import TxOut

    # NOTE: out#1 is change:
    #chg_addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo'

    psbt = open('data/example-change.psbt', 'rb').read()
    b4 = BasicPSBT().parse(psbt)

    t = Tx.parse(BytesIO(b4.txn))

    pkh = t.txs_out[1].hash160()

    if case == 'p2wpkh':
        t.txs_out[1].script = bytes([0, 20]) + bytes(pkh)

        from bech32 import encode
        expect_addr = encode('tb', 0, pkh)

    elif case == 'p2sh':

        spk = bytes([0xa9, 0x14]) + pkh + bytes([0x87])

        b4.outputs[1].redeem_script = bytes([0, 20]) + bytes(pkh)
        t.txs_out[1].script = spk

        expect_addr = t.txs_out[1].address('XTN')

    b4.txn = t.as_bin()

    with BytesIO() as fd:
        b4.serialize(fd)
        mod_psbt = fd.getvalue()

    open('debug/mod-%s.psbt' % case, 'wb').write(mod_psbt)

    start_sign(mod_psbt)

    time.sleep(.1)
    _, story = cap_story()

    check_against_bitcoind(B2A(b4.txn),
                           Decimal('0.00000294'),
                           change_outs=[
                               1,
                           ],
                           dests=[(1, expect_addr)])

    #print(story)
    assert expect_addr in story
    assert parse_change_back(story) == (Decimal('1.09997082'), [expect_addr])

    signed = end_sign(True)
Пример #2
0
def test_change_outs(fake_txn,
                     start_sign,
                     end_sign,
                     cap_story,
                     dev,
                     num_outs,
                     act_outs,
                     segwit,
                     out_style,
                     num_ins=3):
    # create a TXN which has change outputs, which shouldn't be shown to user, and also not fail.
    xp = dev.master_xpub

    couts = num_outs if act_outs == -1 else num_ins - act_outs
    psbt = fake_txn(num_ins,
                    num_outs,
                    xp,
                    segwit_in=segwit,
                    outstyles=[out_style],
                    change_outputs=range(couts))

    open('debug/change.psbt', 'wb').write(psbt)

    # should be able to sign, but get warning
    start_sign(psbt, False)

    time.sleep(.1)
    title, story = cap_story()
    print(repr(story))

    assert title == "OK TO SEND?"
    assert 'Network fee' in story

    if couts < num_outs:
        assert '- to address -' in story
    else:
        assert 'Consolidating' in story

    if couts == 1:
        assert "- to address -" in story
    else:
        assert "- to addresses -" in story

    val, addrs = parse_change_back(story)
    assert val > 0  # hard to calc here
    assert len(addrs) == couts
    if out_style == 'p2pkh':
        assert all((i[0] in 'mn') for i in addrs)
    elif out_style == 'p2wpkh':
        assert set(i[0:4] for i in addrs) == {'tb1q'}
    elif out_style == 'p2wpkh-p2sh':
        assert set(i[0] for i in addrs) == {'2'}
Пример #3
0
def test_change_troublesome(start_sign, cap_story, try_path, expect):
    from struct import pack

    # NOTE: out#1 is change:
    # addr = 'mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo'
    # path = (m=4369050F)/44'/1'/0'/1/5
    # pubkey = 03c80814536f8e801859fc7c2e5129895b261153f519d4f3418ffb322884a7d7e1

    psbt = open('data/example-change.psbt', 'rb').read()
    b4 = BasicPSBT().parse(psbt)

    if 0:
        #from pycoin.tx.Tx import Tx
        #from pycoin.tx.TxOut import TxOut
        # tweak output addr to garbage
        t = Tx.parse(BytesIO(b4.txn))
        chg = t.txs_out[1]  # pycoin.tx.TxOut.TxOut
        b = bytearray(chg.script)
        b[-5] ^= 0x55
        chg.script = bytes(b)

        b4.txn = t.as_bin()

    pubkey = a2b_hex(
        '03c80814536f8e801859fc7c2e5129895b261153f519d4f3418ffb322884a7d7e1')
    path = [
        int(p) if ("'" not in p) else 0x80000000 + int(p[:-1])
        for p in try_path.split('/')
    ]
    bin_path = b4.outputs[1].bip32_paths[pubkey][0:4] \
                + b''.join(pack('<I', i) for i in path)
    b4.outputs[1].bip32_paths[pubkey] = bin_path

    with BytesIO() as fd:
        b4.serialize(fd)
        mod_psbt = fd.getvalue()

    open('debug/troublesome.psbt', 'wb').write(mod_psbt)

    start_sign(mod_psbt)
    time.sleep(0.1)
    title, story = cap_story()
    assert 'OK TO SEND' in title
    assert '(1 warning below)' in story, "no warning shown"

    assert expect in story, story

    assert parse_change_back(story) == (Decimal('1.09997082'),
                                        ['mvBGHpVtTyjmcfSsy6f715nbTGvwgbgbwo'])
Пример #4
0
def test_render_outs(out_style, segwit, outval, fake_txn, start_sign, end_sign,
                     dev):
    # check how we render the value of outputs
    # - works on simulator and connected USB real-device
    xp = dev.master_xpub
    oi = int(Decimal(outval) * int(1E8))

    psbt = fake_txn(1,
                    2,
                    dev.master_xpub,
                    segwit_in=segwit,
                    outvals=[oi, 1E8 - oi],
                    outstyles=[out_style],
                    change_outputs=[1])

    open('debug/render.psbt', 'wb').write(psbt)

    # should be able to sign, but get warning

    # use new feature to have Coldcard return the 'visualization' of transaction
    start_sign(psbt, False, stxn_flags=STXN_VISUALIZE)
    story = end_sign(accept=None, expect_txn=False)

    story = story.decode('ascii')

    assert 'Network fee' in story
    assert '- to address -' in story

    # check rendered right
    lines = story.split('\n')
    amt = Decimal(lines[lines.index(' - to address -') - 1].split(' ')[0])
    assert amt == Decimal(outval)

    val, addrs = parse_change_back(story)
    assert val == (1 - Decimal(outval))
    assert len(addrs) == 1

    if out_style == 'p2pkh':
        assert all((i[0] in 'mn1') for i in addrs)
    elif out_style == 'p2wpkh':
        pr = set(i[0:4] for i in addrs)
        assert len(pr) == 1
        assert pr.pop() in {'tb1q', 'bc1q'}
    elif out_style == 'p2wpkh-p2sh':
        assert len(set(i[0] for i in addrs)) == 1
        assert addrs[0][0] in {'2', '3'}
Пример #5
0
def test_change_outs(fake_txn, start_sign, end_sign, cap_story, dev, num_outs, master_xpub,
                        act_outs, segwit, out_style, visualized, add_xpub, num_ins=3):
    # create a TXN which has change outputs, which shouldn't be shown to user, and also not fail.
    xp = dev.master_xpub

    couts = num_outs if act_outs == -1 else num_ins-act_outs
    psbt = fake_txn(num_ins, num_outs, xp, segwit_in=segwit,
                        outstyles=[out_style], change_outputs=range(couts), add_xpub=add_xpub)

    open('debug/change.psbt', 'wb').write(psbt)

    # should be able to sign, but get warning
    if not visualized:
        start_sign(psbt, False)

        time.sleep(.1)
        title, story = cap_story()
        print(repr(story))

        assert title == "OK TO SEND?"
    else:
        # use new feature to have Coldcard return the 'visualization' of transaction
        start_sign(psbt, False, stxn_flags=visualized)
        story = end_sign(accept=None, expect_txn=False)

        story = story.decode('ascii')

        if (visualized & STXN_SIGNED):
            # last line should be signature, using 'm' over the rest
            from pycoin.contrib.msg_signing import verify_message
            from pycoin.key.BIP32Node import BIP32Node

            #def verify_message(key_or_address, signature, message=None, msg_hash=None, netcode=None):

            assert story[-1] == '\n'
            last_nl = story[:-1].rindex('\n')
            msg, sig = story[0:last_nl+1], story[last_nl:]
            wallet = BIP32Node.from_wallet_key(master_xpub)
            assert verify_message(wallet, sig, message=msg) == True
            story = msg

    assert 'Network fee' in story

    if couts < num_outs:
        assert '- to address -' in story
    else:
        assert 'Consolidating' in story

    if couts == 1:
        assert "- to address -" in story
    else:
        assert "- to addresses -" in story

    val, addrs = parse_change_back(story)
    assert val > 0          # hard to calc here
    assert len(addrs) == couts
    if out_style == 'p2pkh':
        assert all((i[0] in 'mn') for i in addrs)
    elif out_style == 'p2wpkh':
        assert set(i[0:4] for i in addrs) == {'tb1q'}
    elif out_style == 'p2wpkh-p2sh':
        assert set(i[0] for i in addrs) == {'2'}