mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-14 15:25:51 +02:00
test: add PSBT proprietary merge regression coverage
Add unit and functional regression tests asserting that combine/merge preserves proprietary fields at the global, input, and output scopes.
This commit is contained in:
@@ -9,6 +9,16 @@
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(psbt_tests, BasicTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(psbt_tests, BasicTestingSetup)
|
||||||
|
|
||||||
|
static PSBTProprietary MakeProprietary(uint64_t subtype, uint8_t key_data, uint8_t value)
|
||||||
|
{
|
||||||
|
return PSBTProprietary{
|
||||||
|
.subtype = subtype,
|
||||||
|
.identifier = {'p', 's', 'b', 't'},
|
||||||
|
.key = {key_data},
|
||||||
|
.value = {value},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void CheckTimeLock(const std::string& base64_psbt, std::optional<uint32_t> timelock)
|
void CheckTimeLock(const std::string& base64_psbt, std::optional<uint32_t> timelock)
|
||||||
{
|
{
|
||||||
util::Result<PartiallySignedTransaction> psbt = DecodeBase64PSBT(base64_psbt);
|
util::Result<PartiallySignedTransaction> psbt = DecodeBase64PSBT(base64_psbt);
|
||||||
@@ -167,4 +177,43 @@ BOOST_AUTO_TEST_CASE(psbt2_addoutput)
|
|||||||
BOOST_CHECK_EQUAL(psbt.outputs.size(), 2);
|
BOOST_CHECK_EQUAL(psbt.outputs.size(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(merge_proprietary_fields)
|
||||||
|
{
|
||||||
|
CMutableTransaction tx;
|
||||||
|
tx.vin.emplace_back(COutPoint{});
|
||||||
|
tx.vout.emplace_back(0, CScript{});
|
||||||
|
|
||||||
|
PartiallySignedTransaction left(tx);
|
||||||
|
PartiallySignedTransaction right(tx);
|
||||||
|
|
||||||
|
const auto left_prop = MakeProprietary(/*subtype=*/1, /*key_data=*/0x01, /*value=*/0xaa);
|
||||||
|
const auto right_prop = MakeProprietary(/*subtype=*/2, /*key_data=*/0x02, /*value=*/0xbb);
|
||||||
|
|
||||||
|
left.m_proprietary.insert(left_prop);
|
||||||
|
left.inputs[0].m_proprietary.insert(left_prop);
|
||||||
|
left.outputs[0].m_proprietary.insert(left_prop);
|
||||||
|
|
||||||
|
right.m_proprietary.insert(right_prop);
|
||||||
|
right.inputs[0].m_proprietary.insert(right_prop);
|
||||||
|
right.outputs[0].m_proprietary.insert(right_prop);
|
||||||
|
|
||||||
|
BOOST_REQUIRE(left.Merge(right));
|
||||||
|
|
||||||
|
BOOST_REQUIRE_EQUAL(left.m_proprietary.size(), 2U);
|
||||||
|
BOOST_REQUIRE_EQUAL(left.inputs[0].m_proprietary.size(), 2U);
|
||||||
|
BOOST_REQUIRE_EQUAL(left.outputs[0].m_proprietary.size(), 2U);
|
||||||
|
|
||||||
|
const auto global_it = left.m_proprietary.find(right_prop);
|
||||||
|
BOOST_REQUIRE(global_it != left.m_proprietary.end());
|
||||||
|
BOOST_CHECK(global_it->value == right_prop.value);
|
||||||
|
|
||||||
|
const auto input_it = left.inputs[0].m_proprietary.find(right_prop);
|
||||||
|
BOOST_REQUIRE(input_it != left.inputs[0].m_proprietary.end());
|
||||||
|
BOOST_CHECK(input_it->value == right_prop.value);
|
||||||
|
|
||||||
|
const auto output_it = left.outputs[0].m_proprietary.find(right_prop);
|
||||||
|
BOOST_REQUIRE(output_it != left.outputs[0].m_proprietary.end());
|
||||||
|
BOOST_CHECK(output_it->value == right_prop.value);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ from test_framework.messages import (
|
|||||||
CTxOut,
|
CTxOut,
|
||||||
MAX_BIP125_RBF_SEQUENCE,
|
MAX_BIP125_RBF_SEQUENCE,
|
||||||
WITNESS_SCALE_FACTOR,
|
WITNESS_SCALE_FACTOR,
|
||||||
|
ser_compact_size,
|
||||||
)
|
)
|
||||||
from test_framework.psbt import (
|
from test_framework.psbt import (
|
||||||
PSBT,
|
PSBT,
|
||||||
PSBTMap,
|
PSBTMap,
|
||||||
|
PSBT_GLOBAL_PROPRIETARY,
|
||||||
PSBT_GLOBAL_UNSIGNED_TX,
|
PSBT_GLOBAL_UNSIGNED_TX,
|
||||||
PSBT_GLOBAL_VERSION,
|
PSBT_GLOBAL_VERSION,
|
||||||
PSBT_IN_RIPEMD160,
|
PSBT_IN_RIPEMD160,
|
||||||
@@ -35,8 +37,10 @@ from test_framework.psbt import (
|
|||||||
PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS,
|
PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS,
|
||||||
PSBT_IN_MUSIG2_PUB_NONCE,
|
PSBT_IN_MUSIG2_PUB_NONCE,
|
||||||
PSBT_IN_NON_WITNESS_UTXO,
|
PSBT_IN_NON_WITNESS_UTXO,
|
||||||
|
PSBT_IN_PROPRIETARY,
|
||||||
PSBT_IN_WITNESS_UTXO,
|
PSBT_IN_WITNESS_UTXO,
|
||||||
PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS,
|
PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS,
|
||||||
|
PSBT_OUT_PROPRIETARY,
|
||||||
PSBT_OUT_TAP_TREE,
|
PSBT_OUT_TAP_TREE,
|
||||||
PSBT_OUT_SCRIPT,
|
PSBT_OUT_SCRIPT,
|
||||||
)
|
)
|
||||||
@@ -294,6 +298,65 @@ class PSBTTest(BitcoinTestFramework):
|
|||||||
assert "participant_pubkeys" in out_participant_pks
|
assert "participant_pubkeys" in out_participant_pks
|
||||||
assert_equal(out_participant_pks["participant_pubkeys"], [out_pubkey1.hex(), out_pubkey2.hex()])
|
assert_equal(out_participant_pks["participant_pubkeys"], [out_pubkey1.hex(), out_pubkey2.hex()])
|
||||||
|
|
||||||
|
def test_combinepsbt_preserves_proprietary_fields(self):
|
||||||
|
self.log.info("Test that combining PSBTs preserves proprietary fields")
|
||||||
|
|
||||||
|
def proprietary_key(type_byte, identifier, subtype, key_data=b""):
|
||||||
|
return bytes([type_byte]) + ser_compact_size(len(identifier)) + identifier + ser_compact_size(subtype) + key_data
|
||||||
|
|
||||||
|
def proprietary_entry(key, value, identifier, subtype):
|
||||||
|
return {"identifier": identifier.hex(), "subtype": subtype, "key": key.hex(), "value": value.hex()}
|
||||||
|
|
||||||
|
tx = CTransaction()
|
||||||
|
tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b"")]
|
||||||
|
tx.vout = [CTxOut(nValue=0, scriptPubKey=b"")]
|
||||||
|
|
||||||
|
global_key_a = proprietary_key(type_byte=PSBT_GLOBAL_PROPRIETARY, identifier=b"gc", subtype=1, key_data=b"\x01")
|
||||||
|
global_key_b = proprietary_key(type_byte=PSBT_GLOBAL_PROPRIETARY, identifier=b"gc", subtype=2, key_data=b"\x02")
|
||||||
|
input_key_a = proprietary_key(type_byte=PSBT_IN_PROPRIETARY, identifier=b"in", subtype=3, key_data=b"\x03")
|
||||||
|
input_key_b = proprietary_key(type_byte=PSBT_IN_PROPRIETARY, identifier=b"in", subtype=4, key_data=b"\x04")
|
||||||
|
output_key_a = proprietary_key(type_byte=PSBT_OUT_PROPRIETARY, identifier=b"out", subtype=5, key_data=b"\x05")
|
||||||
|
output_key_b = proprietary_key(type_byte=PSBT_OUT_PROPRIETARY, identifier=b"out", subtype=6, key_data=b"\x06")
|
||||||
|
|
||||||
|
psbt1 = PSBT(
|
||||||
|
g=PSBTMap({
|
||||||
|
PSBT_GLOBAL_UNSIGNED_TX: tx.serialize(),
|
||||||
|
global_key_a: b"\xaa",
|
||||||
|
}),
|
||||||
|
i=[PSBTMap({
|
||||||
|
input_key_a: b"\xbb",
|
||||||
|
})],
|
||||||
|
o=[PSBTMap({
|
||||||
|
output_key_a: b"\xcc",
|
||||||
|
})],
|
||||||
|
).to_base64()
|
||||||
|
psbt2 = PSBT(
|
||||||
|
g=PSBTMap({
|
||||||
|
PSBT_GLOBAL_UNSIGNED_TX: tx.serialize(),
|
||||||
|
global_key_b: b"\xdd",
|
||||||
|
}),
|
||||||
|
i=[PSBTMap({
|
||||||
|
input_key_b: b"\xee",
|
||||||
|
})],
|
||||||
|
o=[PSBTMap({
|
||||||
|
output_key_b: b"\xff",
|
||||||
|
})],
|
||||||
|
).to_base64()
|
||||||
|
|
||||||
|
decoded = self.nodes[0].decodepsbt(self.nodes[0].combinepsbt([psbt1, psbt2]))
|
||||||
|
assert_equal(decoded["proprietary"], [
|
||||||
|
proprietary_entry(key=global_key_a, value=b"\xaa", identifier=b"gc", subtype=1),
|
||||||
|
proprietary_entry(key=global_key_b, value=b"\xdd", identifier=b"gc", subtype=2),
|
||||||
|
])
|
||||||
|
assert_equal(decoded["inputs"][0]["proprietary"], [
|
||||||
|
proprietary_entry(key=input_key_a, value=b"\xbb", identifier=b"in", subtype=3),
|
||||||
|
proprietary_entry(key=input_key_b, value=b"\xee", identifier=b"in", subtype=4),
|
||||||
|
])
|
||||||
|
assert_equal(decoded["outputs"][0]["proprietary"], [
|
||||||
|
proprietary_entry(key=output_key_a, value=b"\xcc", identifier=b"out", subtype=5),
|
||||||
|
proprietary_entry(key=output_key_b, value=b"\xff", identifier=b"out", subtype=6),
|
||||||
|
])
|
||||||
|
|
||||||
def test_sighash_mismatch(self):
|
def test_sighash_mismatch(self):
|
||||||
self.log.info("Test sighash type mismatches")
|
self.log.info("Test sighash type mismatches")
|
||||||
self.nodes[0].createwallet("sighash_mismatch")
|
self.nodes[0].createwallet("sighash_mismatch")
|
||||||
@@ -1281,6 +1344,8 @@ class PSBTTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
self.test_decodepsbt_musig2_input_output_types()
|
self.test_decodepsbt_musig2_input_output_types()
|
||||||
|
|
||||||
|
self.test_combinepsbt_preserves_proprietary_fields()
|
||||||
|
|
||||||
self.log.info("Test that combining PSBTs with different transactions fails")
|
self.log.info("Test that combining PSBTs with different transactions fails")
|
||||||
tx = CTransaction()
|
tx = CTransaction()
|
||||||
tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b"")]
|
tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b"")]
|
||||||
|
|||||||
Reference in New Issue
Block a user