mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-03 12:30:07 +00:00
psbt: fix bug re witness_utxo serialization
This commit is contained in:
parent
c8c1ea9c86
commit
90b190bbcd
3 changed files with 28 additions and 21 deletions
|
@ -795,7 +795,7 @@ class TestLegacyPartialTxFormat(TestCaseForTestnet):
|
|||
|
||||
wallet = WalletIntegrityHelper.create_multisig_wallet([ks1, ks2, ks3], '2of3', config=self.config)
|
||||
|
||||
tx = tx_from_any('cHNidP8BAJ8CAAAAAYfEZGymkLOX41eyOyE3AwaRqQoGimaQg000C0voSs1qAQAAAAD9////A6CGAQAAAAAAFgAUi8nZR+TQrdwvTDS4NxA060ez0wUUDAMAAAAAACIAIKYIfE+EpV3DkBRymhjgiVUTnUOEVZ0f0qSKHZXXRqQlQA0DAAAAAAAZdqkUwT/WKU0b57lBClU49LTvEPxZTueIrBMrGABPAQJXVIMAAAAAAAAAAADWRNzdekrLQyNV4BCsSl+VWUDIKpdncxt9idxC6zzaxAJy+qL5i3bMnWVe8oHAes2nXDCpkNw6Unts+SqPWmuKgARL8hIJTwECV1SDAdJM1ZGAAAABmcTWyJP6Gt3sawEhGBE34lw4GUMzuMVyFbPPHm1+evECoj3a5pi7YJW4uANb3R6UR59mwpZ52Bkx4P4HqkNhSe4I0kzVkQEAAIBPAQJXVIMB0kzVkYAAAAC2CoUImtCFCm/+hZCj4VBk5j7v0CCBPfLkprMgnOPfXwLIsU8pF9VXzUgpcMEUw+NFfgnSVjl4Aid6Lk0VGaufjAjSTNWRAAAAgAABASogoQcAAAAAAAAgqUjX+mq7uX4xd5rlQ4MBK0E9U4Icf9OUkA9rRDxh3u4iAgMHo8QdB+2XbWXiE+gj0ChAk3R15wm0ElPoXpcOPLFmdEcwRAIgL3vl+tOY8aNXYpMznyJ00ievF3mGkKoa0A/4PZZyXMUCIADLLp//W+I6hicYxmWlA18XJ4PpsxWOroNnP2xZrcPAAQEFaVIhAqnctXDoKAx0HwkDLBWAlbeqOwzkAa2gMPLUe5mfAgYGIQMHo8QdB+2XbWXiE+gj0ChAk3R15wm0ElPoXpcOPLFmdCEDUhsKReBC8IzNA69H/Yi7IHtUFODjC7h5n8oxGgYyOhlTriIGAwejxB0H7ZdtZeIT6CPQKECTdHXnCbQSU+helw48sWZ0ENJM1ZEAAACAAAAAAAEAAAAiBgNSGwpF4ELwjM0Dr0f9iLsge1QU4OMLuHmfyjEaBjI6GRDSTNWRAQAAgAAAAAABAAAAIgYCqdy1cOgoDHQfCQMsFYCVt6o7DOQBraAw8tR7mZ8CBgYMS/ISCQAAAAABAAAAAAABAWlSIQIFgp+VIldxIsqcqb62f5TPL+CtDRfgUqEQcBz0Eo0znCECLMooLP3ZzB84fTCYZh1ovJyKOaxr1yww21eaeFfQhZshAtkOPIlzhEucwLOElMSFFaMZ7sOsJIn5b29V5qppEqYIU64iAgLZDjyJc4RLnMCzhJTEhRWjGe7DrCSJ+W9vVeaqaRKmCBDSTNWRAAAAgAEAAAAAAAAAIgICLMooLP3ZzB84fTCYZh1ovJyKOaxr1yww21eaeFfQhZsQ0kzVkQEAAIABAAAAAAAAACICAgWCn5UiV3EiypypvrZ/lM8v4K0NF+BSoRBwHPQSjTOcDEvyEgkBAAAAAAAAAAAA')
|
||||
tx = tx_from_any('70736274ff01009f020000000187c4646ca690b397e357b23b2137030691a90a068a6690834d340b4be84acd6a0100000000fdffffff03a0860100000000001600148bc9d947e4d0addc2f4c34b8371034eb47b3d305140c030000000000220020a6087c4f84a55dc39014729a18e08955139d4384559d1fd2a48a1d95d746a425400d0300000000001976a914c13fd6294d1be7b9410a5538f4b4ef10fc594ee788ac132b18004f0102575483000000000000000000d644dcdd7a4acb432355e010ac4a5f955940c82a9767731b7d89dc42eb3cdac40272faa2f98b76cc9d655ef281c07acda75c30a990dc3a527b6cf92a8f5a6b8a80044bf212094f010257548301d24cd5918000000199c4d6c893fa1addec6b0121181137e25c38194333b8c57215b3cf1e6d7e7af102a23ddae698bb6095b8b8035bdd1e94479f66c29679d81931e0fe07aa436149ee08d24cd591010000804f010257548301d24cd59180000000b60a85089ad0850a6ffe8590a3e15064e63eefd020813df2e4a6b3209ce3df5f02c8b14f2917d557cd482970c114c3e3457e09d256397802277a2e4d1519ab9f8c08d24cd591000000800001012b20a1070000000000220020a948d7fa6abbb97e31779ae54383012b413d53821c7fd394900f6b443c61deee22020307a3c41d07ed976d65e213e823d02840937475e709b41253e85e970e3cb1667447304402202f7be5fad398f1a3576293339f2274d227af17798690aa1ad00ff83d96725cc5022000cb2e9fff5be23a862718c665a5035f172783e9b3158eae83673f6c59adc3c001010569522102a9dcb570e8280c741f09032c158095b7aa3b0ce401ada030f2d47b999f020606210307a3c41d07ed976d65e213e823d02840937475e709b41253e85e970e3cb166742103521b0a45e042f08ccd03af47fd88bb207b5414e0e30bb8799fca311a06323a1953ae22060307a3c41d07ed976d65e213e823d02840937475e709b41253e85e970e3cb1667410d24cd591000000800000000001000000220603521b0a45e042f08ccd03af47fd88bb207b5414e0e30bb8799fca311a06323a1910d24cd591010000800000000001000000220602a9dcb570e8280c741f09032c158095b7aa3b0ce401ada030f2d47b999f0206060c4bf212090000000001000000000001016952210205829f9522577122ca9ca9beb67f94cf2fe0ad0d17e052a110701cf4128d339c21022cca282cfdd9cc1f387d3098661d68bc9c8a39ac6bd72c30db579a7857d0859b2102d90e3c8973844b9cc0b38494c48515a319eec3ac2489f96f6f55e6aa6912a60853ae220202d90e3c8973844b9cc0b38494c48515a319eec3ac2489f96f6f55e6aa6912a60810d24cd5910000008001000000000000002202022cca282cfdd9cc1f387d3098661d68bc9c8a39ac6bd72c30db579a7857d0859b10d24cd59101000080010000000000000022020205829f9522577122ca9ca9beb67f94cf2fe0ad0d17e052a110701cf4128d339c0c4bf2120901000000000000000000')
|
||||
tx.add_info_from_wallet(wallet)
|
||||
raw_tx = serialize_tx_in_legacy_format(tx, wallet=wallet)
|
||||
self.assertEqual('45505446ff000200000000010187c4646ca690b397e357b23b2137030691a90a068a6690834d340b4be84acd6a0100000000fdffffff03a0860100000000001600148bc9d947e4d0addc2f4c34b8371034eb47b3d305140c030000000000220020a6087c4f84a55dc39014729a18e08955139d4384559d1fd2a48a1d95d746a425400d0300000000001976a914c13fd6294d1be7b9410a5538f4b4ef10fc594ee788acfeffffffff20a10700000000000000050001ff47304402202f7be5fad398f1a3576293339f2274d227af17798690aa1ad00ff83d96725cc5022000cb2e9fff5be23a862718c665a5035f172783e9b3158eae83673f6c59adc3c00101fffd0201524c53ff02575483000000000000000000d644dcdd7a4acb432355e010ac4a5f955940c82a9767731b7d89dc42eb3cdac40272faa2f98b76cc9d655ef281c07acda75c30a990dc3a527b6cf92a8f5a6b8a80000001004c53ff0257548301d24cd59180000000b60a85089ad0850a6ffe8590a3e15064e63eefd020813df2e4a6b3209ce3df5f02c8b14f2917d557cd482970c114c3e3457e09d256397802277a2e4d1519ab9f8c000001004c53ff0257548301d24cd5918000000199c4d6c893fa1addec6b0121181137e25c38194333b8c57215b3cf1e6d7e7af102a23ddae698bb6095b8b8035bdd1e94479f66c29679d81931e0fe07aa436149ee0000010053ae132b1800',
|
||||
|
|
|
@ -96,6 +96,22 @@ class TxOutput:
|
|||
return cls(scriptpubkey=bfh(bitcoin.address_to_script(address)),
|
||||
value=value)
|
||||
|
||||
def serialize_to_network(self) -> bytes:
|
||||
buf = int.to_bytes(self.value, 8, byteorder="little", signed=False)
|
||||
script = self.scriptpubkey
|
||||
buf += bfh(var_int(len(script.hex()) // 2))
|
||||
buf += script
|
||||
return buf
|
||||
|
||||
@classmethod
|
||||
def from_network_bytes(cls, raw: bytes) -> 'TxOutput':
|
||||
vds = BCDataStream()
|
||||
vds.write(raw)
|
||||
txout = parse_output(vds)
|
||||
if vds.can_read_more():
|
||||
raise SerializationError('extra junk at the end of TxOutput bytes')
|
||||
return txout
|
||||
|
||||
def to_legacy_tuple(self) -> Tuple[int, str, Union[int, str]]:
|
||||
if self.address:
|
||||
return TYPE_ADDRESS, self.address, self.value
|
||||
|
@ -686,20 +702,12 @@ class Transaction:
|
|||
s += int_to_hex(txin.nsequence, 4)
|
||||
return s
|
||||
|
||||
@classmethod
|
||||
def serialize_output(cls, output: TxOutput) -> str:
|
||||
s = int_to_hex(output.value, 8)
|
||||
script = output.scriptpubkey.hex()
|
||||
s += var_int(len(script)//2)
|
||||
s += script
|
||||
return s
|
||||
|
||||
def _calc_bip143_shared_txdigest_fields(self) -> BIP143SharedTxDigestFields:
|
||||
inputs = self.inputs()
|
||||
outputs = self.outputs()
|
||||
hashPrevouts = bh2u(sha256d(b''.join(txin.prevout.serialize_to_network() for txin in inputs)))
|
||||
hashSequence = bh2u(sha256d(bfh(''.join(int_to_hex(txin.nsequence, 4) for txin in inputs))))
|
||||
hashOutputs = bh2u(sha256d(bfh(''.join(self.serialize_output(o) for o in outputs))))
|
||||
hashOutputs = bh2u(sha256d(bfh(''.join(o.serialize_to_network().hex() for o in outputs))))
|
||||
return BIP143SharedTxDigestFields(hashPrevouts=hashPrevouts,
|
||||
hashSequence=hashSequence,
|
||||
hashOutputs=hashOutputs)
|
||||
|
@ -738,7 +746,7 @@ class Transaction:
|
|||
return ''
|
||||
txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, create_script_sig(txin))
|
||||
for txin in inputs)
|
||||
txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs)
|
||||
txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs)
|
||||
|
||||
use_segwit_ser_for_estimate_size = estimate_size and self.is_segwit(guess_for_address=True)
|
||||
use_segwit_ser_for_actual_use = not estimate_size and self.is_segwit()
|
||||
|
@ -996,7 +1004,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
def __init__(self, *args, **kwargs):
|
||||
TxInput.__init__(self, *args, **kwargs)
|
||||
self.utxo = None # type: Optional[Transaction]
|
||||
self.witness_utxo = None # type: Optional[bytes]
|
||||
self.witness_utxo = None # type: Optional[TxOutput]
|
||||
self.part_sigs = {} # type: Dict[bytes, bytes] # pubkey -> sig
|
||||
self.sighash = None # type: Optional[int]
|
||||
self.bip32_paths = {} # type: Dict[bytes, Tuple[bytes, Sequence[int]]] # pubkey -> (xpub_fingerprint, path)
|
||||
|
@ -1020,7 +1028,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
'value_sats': self.value_sats(),
|
||||
'address': self.address,
|
||||
'utxo': str(self.utxo) if self.utxo else None,
|
||||
'witness_utxo': self.witness_utxo.hex() if self.witness_utxo else None,
|
||||
'witness_utxo': self.witness_utxo.serialize_to_network().hex() if self.witness_utxo else None,
|
||||
'sighash': self.sighash,
|
||||
'redeem_script': self.redeem_script.hex() if self.redeem_script else None,
|
||||
'witness_script': self.witness_script.hex() if self.witness_script else None,
|
||||
|
@ -1081,7 +1089,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
raise SerializationError(f"duplicate key: {repr(kt)}")
|
||||
if self.utxo is not None:
|
||||
raise SerializationError(f"PSBT input cannot have both PSBT_IN_NON_WITNESS_UTXO and PSBT_IN_WITNESS_UTXO")
|
||||
self.witness_utxo = val
|
||||
self.witness_utxo = TxOutput.from_network_bytes(val)
|
||||
if key: raise SerializationError(f"key for {repr(kt)} must be empty")
|
||||
elif kt == PSBTInputType.PARTIAL_SIG:
|
||||
if key in self.part_sigs:
|
||||
|
@ -1131,7 +1139,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
|
||||
def serialize_psbt_section_kvs(self, wr):
|
||||
if self.witness_utxo:
|
||||
wr(PSBTInputType.WITNESS_UTXO, self.witness_utxo)
|
||||
wr(PSBTInputType.WITNESS_UTXO, self.witness_utxo.serialize_to_network())
|
||||
elif self.utxo:
|
||||
wr(PSBTInputType.NON_WITNESS_UTXO, bfh(self.utxo.serialize_to_network(include_sigs=True)))
|
||||
for pk, val in sorted(self.part_sigs.items()):
|
||||
|
@ -1159,7 +1167,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
out_idx = self.prevout.out_idx
|
||||
return self.utxo.outputs()[out_idx].value
|
||||
if self.witness_utxo:
|
||||
return int.from_bytes(self.witness_utxo[:8], byteorder="little", signed=True)
|
||||
return self.witness_utxo.value
|
||||
return None
|
||||
|
||||
@property
|
||||
|
@ -1179,7 +1187,7 @@ class PartialTxInput(TxInput, PSBTSection):
|
|||
out_idx = self.prevout.out_idx
|
||||
return self.utxo.outputs()[out_idx].scriptpubkey
|
||||
if self.witness_utxo:
|
||||
return self.witness_utxo[8:]
|
||||
return self.witness_utxo.scriptpubkey
|
||||
return None
|
||||
|
||||
def is_complete(self) -> bool:
|
||||
|
@ -1625,7 +1633,7 @@ class PartialTransaction(Transaction):
|
|||
else:
|
||||
txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, preimage_script if txin_index==k else '')
|
||||
for k, txin in enumerate(inputs))
|
||||
txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs)
|
||||
txouts = var_int(len(outputs)) + ''.join(o.serialize_to_network().hex() for o in outputs)
|
||||
preimage = nVersion + txins + txouts + nLocktime + nHashType
|
||||
return preimage
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ from .keystore import load_keystore, Hardware_KeyStore, KeyStore
|
|||
from .util import multisig_type
|
||||
from .storage import StorageEncryptionVersion, WalletStorage
|
||||
from . import transaction, bitcoin, coinchooser, paymentrequest, ecc, bip32
|
||||
from .transaction import (Transaction, TxInput, UnknownTxinType,
|
||||
from .transaction import (Transaction, TxInput, UnknownTxinType, TxOutput,
|
||||
PartialTransaction, PartialTxInput, PartialTxOutput, TxOutpoint)
|
||||
from .plugin import run_hook
|
||||
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
|
||||
|
@ -1240,8 +1240,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
|||
item = received.get(txin.prevout.to_str())
|
||||
if item:
|
||||
txin_value = item[1]
|
||||
txin_value_bytes = txin_value.to_bytes(8, byteorder="little", signed=True)
|
||||
txin.witness_utxo = txin_value_bytes + bfh(bitcoin.address_to_script(address))
|
||||
txin.witness_utxo = TxOutput.from_address_and_value(address, txin_value)
|
||||
else: # legacy input
|
||||
if txin.utxo is None:
|
||||
# note: for hw wallets, for legacy inputs, ignore_network_issues used to be False
|
||||
|
|
Loading…
Add table
Reference in a new issue