Browse Source

Add human readable representations of txs and PSBTs

Human readable representation for CTransaction
objects in jmbitcoin.secp256k1_transaction.py and for
PartiallySignedTransaction objects in jmclient.wallet.
PSBTWalletMixin, use of these in maker, taker, direct
send and in tests. Users should note that PSBT human
readable representations can in some cases be really
huge.
master
Adam Gibson 6 years ago
parent
commit
ad459d2fdb
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 3
      jmbitcoin/jmbitcoin/__init__.py
  2. 73
      jmbitcoin/jmbitcoin/secp256k1_transaction.py
  3. 6
      jmbitcoin/test/test_tx_signing.py
  4. 4
      jmclient/jmclient/maker.py
  5. 4
      jmclient/jmclient/taker.py
  6. 11
      jmclient/jmclient/taker_utils.py
  7. 114
      jmclient/jmclient/wallet.py
  8. 61
      jmclient/test/test_psbt_wallet.py
  9. 2
      jmclient/test/test_snicker.py

3
jmbitcoin/jmbitcoin/__init__.py

@ -13,5 +13,6 @@ from bitcointx.core import (x, b2x, b2lx, lx, COutPoint, CTxOut, CTxIn,
from bitcointx.core.key import KeyStore
from bitcointx.core.script import (CScript, OP_0, SignatureHash, SIGHASH_ALL,
SIGVERSION_WITNESS_V0, CScriptWitness)
from bitcointx.core.psbt import PartiallySignedTransaction
from bitcointx.core.psbt import (PartiallySignedTransaction, PSBT_Input,
PSBT_Output)

73
jmbitcoin/jmbitcoin/secp256k1_transaction.py

@ -2,16 +2,81 @@
# note, only used for non-cryptographic randomness:
import random
import json
from jmbitcoin.secp256k1_main import *
from jmbase import bintohex, utxo_to_utxostr
from bitcointx.core import (CMutableTransaction, Hash160, CTxInWitness,
CMutableOutPoint, CMutableTxIn,
CMutableTxOut, ValidationError)
CMutableOutPoint, CMutableTxIn, CTransaction,
CMutableTxOut, CTxIn, CTxOut, ValidationError)
from bitcointx.core.script import *
from bitcointx.wallet import P2WPKHCoinAddress, CCoinAddress, P2PKHCoinAddress
from bitcointx.wallet import (P2WPKHCoinAddress, CCoinAddress, P2PKHCoinAddress,
CCoinAddressError)
from bitcointx.core.scripteval import (VerifyScript, SCRIPT_VERIFY_WITNESS,
SCRIPT_VERIFY_P2SH, SIGVERSION_WITNESS_V0)
def hrt(tx, jsonified=True):
""" Given a CTransaction object, output a human
readable json-formatted string (suitable for terminal
output or large GUI textbox display) containing
all details of that transaction.
If `jsonified` is False, the dict is returned, instead
of the json string.
"""
assert isinstance(tx, CTransaction)
outdict = {}
outdict["hex"] = bintohex(tx.serialize())
outdict["inputs"]=[]
outdict["outputs"]=[]
outdict["txid"]= bintohex(tx.GetTxid()[::-1])
outdict["nLockTime"] = tx.nLockTime
outdict["nVersion"] = tx.nVersion
for i, inp in enumerate(tx.vin):
if not tx.wit.vtxinwit:
# witness section is not initialized/empty
witarg = None
else:
witarg = tx.wit.vtxinwit[i]
outdict["inputs"].append(hrinp(inp, witarg))
for i, out in enumerate(tx.vout):
outdict["outputs"].append(hrout(out))
if not jsonified:
return outdict
return json.dumps(outdict, indent=4)
def hrinp(txinput, txinput_witness):
""" Pass objects of type CTxIn and CTxInWitness (or None)
and a dict of human-readable entries for this input
is returned.
"""
assert isinstance(txinput, CTxIn)
outdict = {}
success, u = utxo_to_utxostr((txinput.prevout.hash[::-1],
txinput.prevout.n))
assert success
outdict["outpoint"] = u
outdict["scriptSig"] = bintohex(txinput.scriptSig)
outdict["nSequence"] = txinput.nSequence
if txinput_witness:
outdict["witness"] = bintohex(
txinput_witness.scriptWitness.serialize())
return outdict
def hrout(txoutput):
""" Returns a dict of human-readable entries
for this output.
"""
assert isinstance(txoutput, CTxOut)
outdict = {}
outdict["value_sats"] = txoutput.nValue
outdict["scriptPubKey"] = bintohex(txoutput.scriptPubKey)
try:
addr = CCoinAddress.from_scriptPubKey(txoutput.scriptPubKey)
outdict["address"] = str(addr)
except CCoinAddressError:
pass # non standard script
return outdict
def estimate_tx_size(ins, outs, txtype='p2pkh'):
'''Estimate transaction size.
The txtype field as detailed below is used to distinguish

6
jmbitcoin/test/test_tx_signing.py

@ -4,6 +4,7 @@ import pytest
import binascii
import hashlib
from jmbase import bintohex
import jmbitcoin as btc
@pytest.mark.parametrize(
@ -58,8 +59,9 @@ def test_sign_standard_txs(addrtype):
if not sig:
print(msg)
raise
print("created signature: ", binascii.hexlify(sig))
print("serialized transaction: {}".format(btc.b2x(tx.serialize())))
print("created signature: ", bintohex(sig))
print("serialized transaction: {}".format(bintohex(tx.serialize())))
print("deserialized transaction: {}\n".format(btc.hrt(tx)))
def test_mk_shuffled_tx():
# prepare two addresses for the outputs

4
jmclient/jmclient/maker.py

@ -130,7 +130,9 @@ class Maker(object):
tx = btc.CMutableTransaction.deserialize(tx_from_taker)
except Exception as e:
return (False, 'malformed txhex. ' + repr(e))
jlog.info('obtained tx\n' + bintohex(tx.serialize()))
# if the above deserialization was successful, the human readable
# parsing will be also:
jlog.info('obtained tx\n' + btc.hrt(tx))
goodtx, errmsg = self.verify_unsigned_tx(tx, offerinfo)
if not goodtx:
jlog.info('not a good tx, reason=' + errmsg)

4
jmclient/jmclient/taker.py

@ -499,7 +499,7 @@ class Taker(object):
self.outputs.append({'address': self.coinjoin_address(),
'value': self.cjamount})
self.latest_tx = btc.make_shuffled_tx(self.utxo_tx, self.outputs)
jlog.info('obtained tx\n' + bintohex(self.latest_tx.serialize()))
jlog.info('obtained tx\n' + btc.hrt(self.latest_tx))
for index, ins in enumerate(self.latest_tx.vin):
utxo = (ins.prevout.hash[::-1], ins.prevout.n)
@ -1008,7 +1008,7 @@ class P2EPTaker(Taker):
# contains only those.
tx = btc.make_shuffled_tx(self.input_utxos, self.outputs,
version=2, locktime=compute_tx_locktime())
jlog.info('Created proposed fallback tx\n' + pprint.pformat(str(tx)))
jlog.info('Created proposed fallback tx:\n' + btc.hrt(tx))
# We now sign as a courtesy, because if we disappear the recipient
# can still claim his coins with this.
# sign our inputs before transfer

11
jmclient/jmclient/taker_utils.py

@ -11,7 +11,7 @@ from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\
from .wallet import BaseWallet, estimate_tx_fee, compute_tx_locktime, \
FidelityBondMixin
from jmbitcoin import make_shuffled_tx, amount_to_str, mk_burn_script,\
PartiallySignedTransaction, CMutableTxOut
PartiallySignedTransaction, CMutableTxOut, hrt
from jmbase.support import EXIT_SUCCESS
log = get_log()
@ -159,8 +159,7 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
return False
new_psbt_signed = PartiallySignedTransaction.deserialize(serialized_psbt)
print("Completed PSBT created: ")
print(pformat(new_psbt_signed))
# TODO add more readable info here as for case below.
print(wallet_service.hr_psbt(new_psbt_signed))
return new_psbt_signed
else:
success, msg = wallet_service.sign_tx(tx, inscripts)
@ -168,9 +167,7 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
log.error("Failed to sign transaction, quitting. Error msg: " + msg)
return
log.info("Got signed transaction:\n")
log.info(pformat(str(tx)))
log.info("In serialized form (for copy-paste):")
log.info(bintohex(tx.serialize()))
log.info(hrt(tx))
actual_amount = amount if amount != 0 else total_inputs_val - fee_est
log.info("Sends: " + amount_to_str(actual_amount) + " to destination: " + destination)
if not answeryes:
@ -179,7 +176,7 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
log.info("You chose not to broadcast the transaction, quitting.")
return False
else:
accepted = accept_callback(pformat(str(tx)), destination, actual_amount,
accepted = accept_callback(hrt(tx), destination, actual_amount,
fee_est)
if not accepted:
return False

114
jmclient/jmclient/wallet.py

@ -6,6 +6,7 @@ import collections
import numbers
import random
import base64
import json
from binascii import hexlify, unhexlify
from datetime import datetime
from calendar import timegm
@ -28,7 +29,7 @@ from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH,\
from .support import get_random_bytes
from . import mn_encode, mn_decode
import jmbitcoin as btc
from jmbase import JM_WALLET_NAME_PREFIX
from jmbase import JM_WALLET_NAME_PREFIX, bintohex
"""
@ -1006,6 +1007,117 @@ class PSBTWalletMixin(object):
def __init__(self, storage, **kwargs):
super(PSBTWalletMixin, self).__init__(storage, **kwargs)
@staticmethod
def hr_psbt(in_psbt):
""" Returns a jsonified indented string with all relevant
information, in human readable form, contained in a PSBT.
Warning: the output can be very verbose in certain cases.
"""
assert isinstance(in_psbt, btc.PartiallySignedTransaction)
outdict = {}
outdict["psbt-version"] = in_psbt.version
# human readable serialization of these three global fields is for
# now on a "best-effort" basis, i.e. just takes the representation
# provided by the underlying classes in bitcointx, though this may
# not be very readable.
# TODO: Improve proprietary/unknown as needed.
if in_psbt.xpubs:
outdict["xpubs"] = {str(k): bintohex(
v.serialize()) for k, v in in_psbt.xpubs.items()}
if in_psbt.proprietary_fields:
outdict["proprietary-fields"] = str(in_psbt.proprietary_fields)
if in_psbt.unknown_fields:
outdict["unknown-fields"] = str(in_psbt.unknown_fields)
outdict["unsigned-tx"] = btc.hrt(in_psbt.unsigned_tx, jsonified=False)
outdict["psbt-inputs"] = []
for inp in in_psbt.inputs:
outdict["psbt-inputs"].append(PSBTWalletMixin.hr_psbt_in(inp))
outdict["psbt-outputs"] = []
for out in in_psbt.outputs:
outdict["psbt-outputs"].append(PSBTWalletMixin.hr_psbt_out(out))
return json.dumps(outdict, indent=4)
@staticmethod
def hr_psbt_in(psbt_input):
""" Returns a dict containing human readable information
about a bitcointx.core.psbt.PSBT_Input object.
"""
assert isinstance(psbt_input, btc.PSBT_Input)
outdict = {}
if psbt_input.index is not None:
outdict["input-index"] = psbt_input.index
if psbt_input.utxo:
if isinstance(psbt_input.utxo, btc.CTxOut):
outdict["utxo"] = btc.hrout(psbt_input.utxo)
elif isinstance(psbt_input.utxo, btc.CTransaction):
# human readable full transaction is *too* verbose:
outdict["utxo"] = bintohex(psbt_input.utxo.serialize())
else:
assert False, "invalid PSBT Input utxo field."
if psbt_input.sighash_type:
outdict["sighash-type"] = psbt_input.sighash_type
if psbt_input.redeem_script:
outdict["redeem-script"] = bintohex(psbt_input.redeem_script)
if psbt_input.witness_script:
outdict["witness-script"] = bintohex(psbt_input.witness_script)
if psbt_input.partial_sigs:
# convert the dict entries to hex:
outdict["partial-sigs"] = {bintohex(k): bintohex(v) for k,v in \
psbt_input.partial_sigs.items()}
# Note we do not currently add derivation info to our own inputs,
# but probably will in future ( TODO ), still this is shown for
# externally generated PSBTs:
if psbt_input.derivation_map:
# TODO it would be more useful to print the indexes of the
# derivation path as integers, than 4 byte hex:
outdict["derivation-map"] = {bintohex(k): bintohex(v.serialize(
)) for k, v in psbt_input.derivation_map.items()}
# we show these fields on a best-effort basis; same comment as for
# globals section as mentioned in hr_psbt()
if psbt_input.proprietary_fields:
outdict["proprietary-fields"] = str(psbt_input.proprietary_fields)
if psbt_input.unknown_fields:
outdict["unknown-fields"] = str(psbt_input.unknown_fields)
if psbt_input.proof_of_reserves_commitment:
outdict["proof-of-reserves-commitment"] = \
str(psbt_input.proof_of_reserves_commitment)
outdict["final-scriptSig"] = bintohex(psbt_input.final_script_sig)
outdict["final-scriptWitness"] = bintohex(
psbt_input.final_script_witness.serialize())
return outdict
@staticmethod
def hr_psbt_out(psbt_output):
""" Returns a dict containing human readable information
about a PSBT_Output object.
"""
assert isinstance(psbt_output, btc.PSBT_Output)
outdict = {}
if psbt_output.index is not None:
outdict["output-index"] = psbt_output.index
if psbt_output.derivation_map:
# See note to derivation map in hr_psbt_in()
outdict["derivation-map"] = {bintohex(k): bintohex(v.serialize(
)) for k, v in psbt_output.derivation_map.items()}
if psbt_output.redeem_script:
outdict["redeem-script"] = bintohex(psbt_output.redeem_script)
if psbt_output.witness_script:
outdict["witness-script"] = bintohex(psbt_output.witness_script)
if psbt_output.proprietary_fields:
outdict["proprietary-fields"] = str(psbt_output.proprietary_fields)
if psbt_output.unknown_fields:
outdict["unknown-fields"] = str(psbt_output.unknown_fields)
return outdict
@staticmethod
def witness_utxos_to_psbt_utxos(utxos):
""" Given a dict of utxos as returned from select_utxos,

61
jmclient/test/test_psbt_wallet.py

@ -11,10 +11,10 @@ from commontest import make_wallets, dummy_accept_callback, dummy_info_callback
import jmbitcoin as bitcoin
import pytest
from jmbase import get_log, bintohex
from jmbase import get_log, bintohex, hextobin
from jmclient import (load_test_config, jm_single, direct_send,
SegwitLegacyWallet, SegwitWallet, LegacyWallet)
from jmclient.wallet import PSBTWalletMixin
log = get_log()
def test_create_and_sign_psbt_with_legacy(setup_psbt_wallet):
@ -130,6 +130,8 @@ def test_create_psbt_and_sign(setup_psbt_wallet, unowned_utxo, wallet_cls):
if unowned_utxo:
newpsbt.inputs[-1].redeem_script = redeem_script
print(bintohex(newpsbt.serialize()))
print("human readable: ")
print(wallet_service.hr_psbt(newpsbt))
# we cannot compare with a fixed expected result due to wallet randomization, but we can
# check psbt structure:
expected_inputs_length = 3 if unowned_utxo else 2
@ -164,9 +166,9 @@ def test_create_psbt_and_sign(setup_psbt_wallet, unowned_utxo, wallet_cls):
@pytest.mark.parametrize('payment_amt, wallet_cls_sender, wallet_cls_receiver', [
(0.05, SegwitLegacyWallet, SegwitLegacyWallet),
(0.95, SegwitLegacyWallet, SegwitWallet),
(0.05, SegwitWallet, SegwitLegacyWallet),
(0.95, SegwitWallet, SegwitWallet),
#(0.95, SegwitLegacyWallet, SegwitWallet),
#(0.05, SegwitWallet, SegwitLegacyWallet),
#(0.95, SegwitWallet, SegwitWallet),
])
def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
wallet_cls_receiver):
@ -211,6 +213,8 @@ def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
info_callback=dummy_info_callback,
with_final_psbt=True)
print("Initial payment PSBT created:\n{}".format(wallet_s.hr_psbt(
payment_psbt)))
# ensure that the payemnt amount is what was intended:
out_amts = [x.nValue for x in payment_psbt.unsigned_tx.vout]
# NOTE this would have to change for more than 2 outputs:
@ -274,7 +278,7 @@ def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
version=payment_psbt.unsigned_tx.nVersion,
locktime=payment_psbt.unsigned_tx.nLockTime)
print("we created this unsigned tx: ")
print(unsigned_payjoin_tx)
print(bitcoin.hrt(unsigned_payjoin_tx))
# to create the PSBT we need the spent_outs for each input,
# in the right order:
spent_outs = []
@ -301,6 +305,9 @@ def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
r_payjoin_psbt = wallet_r.create_psbt_from_tx(unsigned_payjoin_tx,
spent_outs=spent_outs)
print("Receiver created payjoin PSBT:\n{}".format(
wallet_r.hr_psbt(r_payjoin_psbt)))
signresultandpsbt, err = wallet_r.sign_psbt(r_payjoin_psbt.serialize(),
with_sign_result=True)
assert not err, err
@ -308,6 +315,9 @@ def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
assert signresult.num_inputs_final == len(receiver_utxos)
assert not signresult.is_final
print("Receiver signing successful. Payjoin PSBT is now:\n{}".format(
wallet_r.hr_psbt(receiver_signed_psbt)))
# *** STEP 3 ***
# **************
@ -317,11 +327,50 @@ def test_payjoin_workflow(setup_psbt_wallet, payment_amt, wallet_cls_sender,
receiver_signed_psbt.serialize(), with_sign_result=True)
assert not err, err
signresult, sender_signed_psbt = signresultandpsbt
print("Sender's final signed PSBT is:\n{}".format(
wallet_s.hr_psbt(sender_signed_psbt)))
assert signresult.is_final
# broadcast the tx
extracted_tx = sender_signed_psbt.extract_transaction().serialize()
assert jm_single().bc_interface.pushtx(extracted_tx)
""" test vector data for human readable parsing only,
they are taken from bitcointx/tests/test_psbt.py and in turn
taken from BIP174 test vectors.
TODO add more, but note we are not testing functionality here.
"""
hr_test_vectors = {
# PSBT with one P2PKH input. Outputs are empty
"one-p2pkh": '70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000',
# PSBT with one P2PKH input and one P2SH-P2WPKH input.
# First input is signed and finalized. Outputs are empty
"first-input-signed": '70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000',
# PSBT with one P2PKH input which has a non-final scriptSig
# and has a sighash type specified. Outputs are empty
"nonfinal-scriptsig": '70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000',
# PSBT with one P2PKH input and one P2SH-P2WPKH input both with
# non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available.
# Outputs filled.
"mixed-inputs-nonfinal": '70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000',
# PSBT with one P2SH-P2WSH input of a 2-of-2 multisig, redeemScript,
# witnessScript, and keypaths are available. Contains one signature.
"2-2-multisig-p2wsh": '70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000',
# PSBT with unknown types in the inputs
"unknown-input-types": '70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000',
# PSBT with `PSBT_GLOBAL_XPUB`
"global-xpub": '70736274ff01009d0100000002710ea76ab45c5cb6438e607e59cc037626981805ae9e0dfd9089012abb0be5350100000000ffffffff190994d6a8b3c8c82ccbcfb2fba4106aa06639b872a8d447465c0d42588d6d670000000000ffffffff0200e1f505000000001976a914b6bc2c0ee5655a843d79afedd0ccc3f7dd64340988ac605af405000000001600141188ef8e4ce0449eaac8fb141cbf5a1176e6a088000000004f010488b21e039e530cac800000003dbc8a5c9769f031b17e77fea1518603221a18fd18f2b9a54c6c8c1ac75cbc3502f230584b155d1c7f1cd45120a653c48d650b431b67c5b2c13f27d7142037c1691027569c503100008000000080000000800001011f00e1f5050000000016001433b982f91b28f160c920b4ab95e58ce50dda3a4a220203309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c47304402202d704ced830c56a909344bd742b6852dccd103e963bae92d38e75254d2bb424502202d86c437195df46c0ceda084f2a291c3da2d64070f76bf9b90b195e7ef28f77201220603309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c1827569c5031000080000000800000008000000000010000000001011f00e1f50500000000160014388fb944307eb77ef45197d0b0b245e079f011de220202c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b11047304402204cb1fb5f869c942e0e26100576125439179ae88dca8a9dc3ba08f7953988faa60220521f49ca791c27d70e273c9b14616985909361e25be274ea200d7e08827e514d01220602c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b1101827569c5031000080000000800000008000000000000000000000220202d20ca502ee289686d21815bd43a80637b0698e1fbcdbe4caed445f6c1a0a90ef1827569c50310000800000008000000080000000000400000000',
# PSBT with proprietary values
"proprietary-values": '70736274ff0100550200000001ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff018e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac0000000015fc0a676c6f62616c5f706678016d756c7469706c790563686965660001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb823080ffc06696e5f706678fde80377686174056672616d650afc00fe40420f0061736b077361746f7368690012fc076f75745f706678feffffff01636f726e05746967657217fc076f75745f706678ffffffffffffffffff707570707905647269766500'
}
def test_hr_psbt(setup_psbt_wallet):
bitcoin.select_chain_params("bitcoin")
for k, v in hr_test_vectors.items():
print(PSBTWalletMixin.hr_psbt(
bitcoin.PartiallySignedTransaction.from_binary(hextobin(v))))
bitcoin.select_chain_params("bitcoin/regtest")
@pytest.fixture(scope="module")
def setup_psbt_wallet():
load_test_config()

2
jmclient/test/test_snicker.py

@ -45,7 +45,7 @@ def test_snicker_e2e(setup_snicker, nw, wallet_structures,
assert tx, "Failed to spend from receiver wallet"
print("Parent transaction OK. It was: ")
print(tx)
print(btc.hrt(tx))
wallet_r.process_new_tx(tx)
# we must identify the receiver's output we're going to use;
# it can be destination or change, that's up to the proposer

Loading…
Cancel
Save