示例#1
0
    def doit(accept=True, in_psbt=None, finalize=False):

        if accept != None:
            need_keypress('y' if accept else 'x')

        if accept == False:
            with pytest.raises(CCUserRefused):
                done = None
                while done == None:
                    time.sleep(0.050)
                    done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None)
            return
        else:
            done = None
            while done == None:
                time.sleep(0.050)
                done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None)

        assert len(done) == 2

        resp_len, chk = done
        psbt_out = dev.download_file(resp_len, chk)

        if not finalize:
            if in_psbt:
                assert BasicPSBT().parse(in_psbt) == BasicPSBT().parse(psbt_out)
        else:
            from pycoin.tx.Tx import Tx
            # parse it
            res = psbt_out
            assert res[0:4] != b'psbt'
            t = Tx.from_bin(res)
            assert t.version in [1, 2]

        return psbt_out
示例#2
0
def test_txid_calc(num_ins, fake_txn, try_sign, dev, segwit, decode_with_bitcoind, cap_story):
    # verify correct txid for transactions is being calculated
    xp = dev.master_xpub

    psbt = fake_txn(num_ins, 1, xp, segwit_in=segwit)

    _, txn = try_sign(psbt, accept=True, finalize=True)

    #print('Signed; ' + B2A(txn))

    time.sleep(.1)
    title, story = cap_story()
    assert '0' in story
    assert 'TXID' in title, story
    txid = story.strip()

    if 1:
        # compare to PyCoin
        from pycoin.tx.Tx import Tx
        t = Tx.from_bin(txn)
        assert t.id() == txid

    if 1:
        # compare to bitcoin core
        decoded = decode_with_bitcoind(txn)
        pprint(decoded)

        assert len(decoded['vin']) == num_ins
        if segwit:
            assert all(x['txinwitness'] for x in decoded['vin'])

        assert decoded['txid'] == txid
示例#3
0
def test_bip143_attack_data_capture(num_utxo, segwit_in, try_sign, fake_txn, settings_set,
                                    settings_get, cap_story, sim_exec, hist_count):

    # cleanup prev runs, if very first time thru
    sim_exec('import history; history.OutptValueCache.clear()')
    hist_b4 = hist_count()
    assert hist_b4 == 0

    # make a txn, capture the outputs of that as inputs for another txn
    psbt = fake_txn(1, num_utxo+3, segwit_in=segwit_in, change_outputs=range(num_utxo+2),
                        outstyles=(['p2wpkh']*num_utxo) + ['p2wpkh-p2sh', 'p2pkh'])
    _, txn = try_sign(psbt, accept=True, finalize=True)

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

    num_inp_utxo = (1 if segwit_in else 0)

    time.sleep(.1)
    title, story = cap_story()
    assert 'TXID' in title, story
    txid = story.strip()

    assert hist_count() in {128, hist_b4+num_utxo+num_inp_utxo}

    # compare to PyCoin
    from pycoin.tx.Tx import Tx
    t = Tx.from_bin(txn)
    assert t.id() == txid

    # expect all of new "change outputs" to be recorded (none of the non-segwit change tho)
    # plus the one input we "revealed"
    after1 = settings_get('ovc')
    assert len(after1) == min(30, num_utxo + num_inp_utxo)

    all_utxo = hist_count()
    assert all_utxo == hist_b4+num_utxo+num_inp_utxo

    # build a new PSBT based on those change outputs
    psbt2, raw = spend_outputs(psbt, txn)

    # try to sign that ... should work fine
    try_sign(raw, accept=True, finalize=True)
    time.sleep(.1)

    # should not affect stored data, because those values already cached
    assert settings_get('ovc') == after1

    # any tweaks to input side's values should fail.
    for amt in [int(1E6), 1]:
        def value_tweak(spendables):
            assert len(spendables) > 2
            spendables[0][1].coin_value += amt

        psbt3, raw = spend_outputs(psbt, txn, tweaker=value_tweak)
        with pytest.raises(CCProtoError) as ee:
            orig, result = try_sign(raw, accept=True, finalize=True)

        assert 'but PSBT claims' in str(ee), ee
示例#4
0
def test_sdcard_signing(encoding, num_outs, try_sign_microsd, fake_txn, try_sign, dev):
    # exercise the txn encode/decode from sdcard
    xp = dev.master_xpub

    psbt = fake_txn(2, num_outs, xp, segwit_in=True)

    _, txn, txid = try_sign_microsd(psbt, finalize=True, encoding=encoding)

    from pycoin.tx.Tx import Tx
    t = Tx.from_bin(txn)
    assert t.id() == txid
示例#5
0
 async def getblock(self, blockhash: str, mode: int = 1):
     start = time.time()
     if mode == 2:
         raise NotImplementedError
     block_header = self.repository.headers.get_block_header(blockhash)
     if not block_header:
         return
     if mode == 1:
         txids, size = self.repository.blockchain.get_txids_by_block_hash(
             block_header['block_hash'])
         if txids:
             block = self._serialize_header(block_header)
             block.update({'tx': txids, 'size': size})
             best_header = self.repository.headers.get_best_header()
             block['confirmations'] = best_header[
                 'block_height'] - block_header['block_height'] + 1
             Logger.p2p.info(
                 'Verbose block %s (%s) provided from local storage in %ss)',
                 block_header['block_height'], blockhash,
                 '{:.4f}'.format(time.time() - start))
             return block
         p2p_block = await self._get_block(block_header, verbose=True)
         Logger.p2p.info('Verbose block %s (%s) provided from P2P in %ss)',
                         block_header['block_height'], blockhash,
                         '{:.4f}'.format(time.time() - start))
         return p2p_block['verbose']
     else:
         transactions, size = self.repository.blockchain.get_transactions_by_block_hash(
             blockhash)
         if transactions:
             Logger.p2p.info(
                 'Raw block %s (%s) provided from local storage in %ss)',
                 block_header['block_height'], blockhash,
                 '{:.4f}'.format(time.time() - start))
             try:
                 block = Block.parse(io.BytesIO(
                     block_header['header_bytes']),
                                     include_transactions=False)
                 block.set_txs([
                     Tx.from_bin(t['transaction_bytes'])
                     for t in transactions
                 ])
                 return block.as_hex()
             except:
                 Logger.repository.error(
                     'Error loading block %s from repository, falling back to P2P'
                     % blockhash)
         p2p_block = await self._get_block(block_header)
         Logger.p2p.info('Raw block %s (%s) provided from P2P in %ss)',
                         block_header['block_height'], blockhash,
                         '{:.4f}'.format(time.time() - start))
         return binascii.hexlify(p2p_block['block_bytes']).decode()
示例#6
0
    def doit(accept=True,
             in_psbt=None,
             finalize=False,
             accept_ms_import=False,
             expect_txn=True):

        if accept_ms_import:
            # XXX would be better to do cap_story here, but that would limit test to simulator
            need_keypress('y')
            time.sleep(0.050)

        if accept != None:
            need_keypress('y' if accept else 'x', timeout=None)

        if accept == False:
            with pytest.raises(CCUserRefused):
                done = None
                while done == None:
                    time.sleep(0.050)
                    done = dev.send_recv(CCProtocolPacker.get_signed_txn(),
                                         timeout=None)
            return
        else:
            done = None
            while done == None:
                time.sleep(0.00)
                done = dev.send_recv(CCProtocolPacker.get_signed_txn(),
                                     timeout=None)

        assert len(done) == 2

        resp_len, chk = done
        psbt_out = dev.download_file(resp_len, chk)

        if not expect_txn:
            # skip checks; it's text
            return psbt_out

        if not finalize:
            if in_psbt:
                from psbt import BasicPSBT
                assert BasicPSBT().parse(in_psbt) != None
        else:
            from pycoin.tx.Tx import Tx
            # parse it
            res = psbt_out
            assert res[0:4] != b'psbt', 'still a PSBT, but asked for finalize'
            t = Tx.from_bin(res)
            assert t.version in [1, 2]

        return psbt_out
示例#7
0
def spend_outputs(funding_psbt, finalized_txn, tweaker=None):
    # take details from PSBT that created a finalized txn (also provided)
    # and build a new PSBT that spends those change outputs.
    from pycoin.tx.Tx import Tx
    from pycoin.tx.TxOut import TxOut
    from pycoin.tx.TxIn import TxIn
    funding = Tx.from_bin(finalized_txn)
    b4 = BasicPSBT().parse(funding_psbt)

    # segwit change outputs only
    spendables = [(n, i) for n, i in enumerate(funding.tx_outs_as_spendable())
                  if i.script[0:2] == b'\x00\x14' and b4.outputs[n].bip32_paths
                  ]

    #spendables = list(reversed(spendables))
    random.shuffle(spendables)

    if tweaker:
        tweaker(spendables)

    nn = BasicPSBT()
    nn.inputs = [BasicPSBTInput(idx=i) for i in range(len(spendables))]
    nn.outputs = [BasicPSBTOutput(idx=0)]

    # copy input values from funding PSBT's output side
    for p_in, (f_out, sp) in zip(nn.inputs,
                                 [(b4.outputs[x], s) for x, s in spendables]):
        p_in.bip32_paths = f_out.bip32_paths
        p_in.witness_script = f_out.redeem_script
        with BytesIO() as fd:
            sp.stream(fd)
            p_in.witness_utxo = fd.getvalue()

    # build new txn: single output, no change, no miner fee
    act_scr = fake_dest_addr('p2wpkh')
    dest_out = TxOut(sum(s.coin_value for n, s in spendables), act_scr)

    txn = Tx(2, [s.tx_in() for _, s in spendables], [dest_out])

    # put unsigned TXN into PSBT
    with BytesIO() as b:
        txn.stream(b)
        nn.txn = b.getvalue()

    with BytesIO() as rv:
        nn.serialize(rv)
        raw = rv.getvalue()

    open('debug/spend_outs.psbt', 'wb').write(raw)

    return nn, raw
示例#8
0
 def _get_transaction(self, txid: (str, bytes)):
     key = self.get_key(txid, prefix=TRANSACTION_PREFIX)
     data = self.session.get(self.storage_name + b'.' + key)
     if not data:
         return
     blockhash = data[-32:]
     if not int.from_bytes(blockhash[:8], 'little'):
         data = data[:-32]
     return {
         'transaction_bytes': data,
         'block_hash': blockhash,
         'txid': txid,
         'transaction_object': Tx.from_bin(data)
     }
示例#9
0
    def rawtx(self, body):
        tx = Tx.from_bin(body)

        orders = ctrl.api.get_orders_of_last_fifteen_minutes_ctl()
        print(orders)
        addr_order_dict = {i['bitaddr']: i for i in orders}
        addrs = list(addr_order_dict.keys())

        for i in tx.txs_out:
            addr = i.address()
            if addr not in addrs:
                continue

            order = addr_order_dict[addr]
            if order['satoshi'] != i.coin_value:
                continue

            ctrl.api.update_order_ctl(order['order_id'], {'state': 1})
示例#10
0
    def doit(f_or_data,
             accept=True,
             finalize=False,
             accept_ms_import=False,
             complete=False,
             encoding='binary',
             del_after=0):

        if f_or_data[0:5] == b'psbt\xff':
            ip = f_or_data
            filename = 'memory'
        else:
            filename = f_or_data
            ip = open(f_or_data, 'rb').read()
            if ip[0:10] == b'70736274ff':
                ip = a2b_hex(ip.strip())
            assert ip[0:5] == b'psbt\xff'

        psbtname = 'ftrysign'

        # population control
        from glob import glob
        import os
        pat = microsd_path(psbtname + '*.psbt')
        for f in glob(pat):
            assert 'psbt' in f
            os.unlink(f)

        if encoding == 'hex':
            ip = b2a_hex(ip)
        elif encoding == 'base64':
            from base64 import b64encode, b64decode
            ip = b64encode(ip)
        else:
            assert encoding == 'binary'

        with open_microsd(psbtname + '.psbt', 'wb') as sd:
            sd.write(ip)

        goto_home()
        pick_menu_item('Ready To Sign')

        time.sleep(.1)
        _, story = cap_story()
        if 'Choose PSBT file' in story:
            need_keypress('y')
            time.sleep(.1)

        pick_menu_item(psbtname + '.psbt')

        time.sleep(.1)

        if accept_ms_import:
            # XXX would be better to do cap_story here, but that would limit test to simulator
            need_keypress('y')
            time.sleep(0.050)

        title, story = cap_story()
        assert title == 'OK TO SEND?'

        if accept != None:
            need_keypress('y' if accept else 'x')

        if accept == False:
            time.sleep(0.050)

            # look for "Aborting..." ??
            return ip, None, None

        # wait for it to finish
        for r in range(10):
            time.sleep(0.1)
            title, story = cap_story()
            if title == 'PSBT Signed': break
        else:
            assert False, 'timed out'

        txid = None
        lines = story.split('\n')
        if 'Final TXID:' in lines:
            txid = lines[-1]
            result_fname = lines[-4]
        else:
            result_fname = lines[-1]

        result = open_microsd(result_fname, 'rb').read()

        if encoding == 'hex' or finalize:
            result = a2b_hex(result.strip())
        elif encoding == 'base64':
            result = b64decode(result)
        else:
            assert encoding == 'binary'

        in_file = microsd_path(psbtname + '.psbt')

        # read back final product
        if finalize:

            if del_after:
                if not txid:
                    txid = re.findall('[0-9a-f]{64}', result_fname)[0]
                assert result_fname == txid + '.txn'
                assert not os.path.exists(in_file)
            else:
                assert 'final' in result_fname
                assert os.path.exists(in_file)

            from pycoin.tx.Tx import Tx
            # parse it a little
            assert result[
                0:4] != b'psbt', 'still a PSBT, but asked for finalize'
            t = Tx.from_bin(result)
            assert t.version in [1, 2]
            assert t.id() == txid

        else:
            assert result[0:5] == b'psbt\xff'

            if complete:
                assert '-signed' in result_fname
            else:
                assert '-part' in result_fname

            if del_after:
                assert not os.path.exists(in_file)

            from psbt import BasicPSBT
            was = BasicPSBT().parse(ip)
            now = BasicPSBT().parse(result)
            assert was.txn == now.txn
            assert was != now

        return ip, result, txid
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG
# operation happens EvalScript() will be evaluating the redeemScript, so the
# corresponding SignatureHash() function will use that same script when it
# replaces the scriptSig in the transaction being hashed with the script being
# executed.

## override module method
SignatureHash = MySignatureHash

sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)

# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])

# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([sig, txin_redeemScript])

# Verify the signature worked. This calls EvalScript() and actually executes
# the opcodes in the scripts to see if everything worked out. If it doesn't an
# exception will be raised.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))

# Done! Print the transaction to standard output with the bytes-to-hex
# function.
print("Transaction:\n", b2x(tx.serialize()))

pycoin_tx = Tx.from_bin(tx.serialize())
print("Pycoin tx:\n", pycoin_tx.__repr__())

示例#12
0
    def doit(f_or_data,
             accept=True,
             finalize=False,
             accept_ms_import=False,
             complete=False):

        if f_or_data[0:5] == b'psbt\xff':
            ip = f_or_data
            filename = 'memory'
        else:
            filename = f_or_data
            ip = open(f_or_data, 'rb').read()
            if ip[0:10] == b'70736274ff':
                ip = a2b_hex(ip.strip())
            assert ip[0:5] == b'psbt\xff'

        psbtname = 'ftrysign'

        # population control
        from glob import glob
        import os
        pat = microsd_path(psbtname + '*.psbt')
        for f in glob(pat):
            assert 'psbt' in f
            os.unlink(f)

        with open_microsd(psbtname + '.psbt', 'wb') as sd:
            sd.write(ip)

        goto_home()
        pick_menu_item('Ready To Sign')

        time.sleep(.1)
        _, story = cap_story()
        if 'Choose PSBT file' in story:
            need_keypress('y')
            time.sleep(.1)

        pick_menu_item(psbtname + '.psbt')

        time.sleep(.1)

        if accept_ms_import:
            # XXX would be better to do cap_story here, but that would limit test to simulator
            need_keypress('y')
            time.sleep(0.050)

        title, story = cap_story()
        assert title == 'OK TO SEND?'

        if accept != None:
            need_keypress('y' if accept else 'x')

        if accept == False:
            time.sleep(0.050)

            # look for "Aborting..." ??
            return ip, None

        # wait for it to finish
        for r in range(10):
            time.sleep(0.1)
            title, story = cap_story()
            if title == 'PSBT Signed': break
        else:
            assert False, 'timed out'

        result_fname = story.split('\n')[-1]

        result = open_microsd(result_fname, 'rb').read()

        # read back final product
        if finalize:
            assert 'final' in result_fname

            from pycoin.tx.Tx import Tx
            # parse it a little
            assert result[
                0:4] != b'psbt', 'still a PSBT, but asked for finalize'
            t = Tx.from_bin(result)
            assert t.version in [1, 2]

        else:
            if complete:
                assert '-signed' in result_fname
            else:
                assert '-part' in result_fname

            from psbt import BasicPSBT
            was = BasicPSBT().parse(ip)
            now = BasicPSBT().parse(result)
            assert was.txn == now.txn
            assert was != now

        return ip, result
示例#13
0
 def tx_from_b64(h):
     d = binascii.a2b_base64(h.encode("utf8"))
     return Tx.from_bin(d)