diff --git a/test/functional/feature_fedpeg.py b/test/functional/feature_fedpeg.py index 5e8d1d59a9..0914edf65b 100755 --- a/test/functional/feature_fedpeg.py +++ b/test/functional/feature_fedpeg.py @@ -13,6 +13,11 @@ from test_framework.util import ( assert_raises_rpc_error, assert_equal, ) +from test_framework.messages import ( + CTransaction, + CTxInWitness, + FromHex, +) from decimal import Decimal def get_new_unconfidential_address(node, addr_type="p2sh-segwit"): @@ -212,6 +217,12 @@ class FedPegTest(BitcoinTestFramework): raw_pegin = sidechain.createrawpegin(raw, proof)['hex'] signed_pegin = sidechain.signrawtransactionwithwallet(raw_pegin) + sample_pegin_struct = FromHex(CTransaction(), signed_pegin["hex"]) + # Round-trip peg-in transaction using python serialization + assert_equal(signed_pegin["hex"], sample_pegin_struct.serialize().hex()) + # Store this for later (evil laugh) + sample_pegin_witness = sample_pegin_struct.wit.vtxinwit[0].peginWitness + pegtxid1 = sidechain.claimpegin(raw, proof) # Will invalidate the block that confirms this transaction later @@ -388,6 +399,21 @@ class FedPegTest(BitcoinTestFramework): assert(sidechain.getwalletinfo()["balance"]["bitcoin"] == bal_1) + # Test superfluous peg-in witness data on regular spend before we have no funds + raw_spend = sidechain.createrawtransaction([], {sidechain.getnewaddress():1}) + fund_spend = sidechain.fundrawtransaction(raw_spend) + sign_spend = sidechain.signrawtransactionwithwallet(fund_spend["hex"]) + signed_struct = FromHex(CTransaction(), sign_spend["hex"]) + # Non-witness tx has no witness serialized yet + if len(signed_struct.wit.vtxinwit) == 0: + signed_struct.wit.vtxinwit = [CTxInWitness()] + signed_struct.wit.vtxinwit[0].peginWitness.stack = sample_pegin_witness.stack + assert_equal(sidechain.testmempoolaccept([signed_struct.serialize().hex()])[0]["allowed"], False) + assert_equal(sidechain.testmempoolaccept([signed_struct.serialize().hex()])[0]["reject-reason"], "68: extra-pegin-witness") + signed_struct.wit.vtxinwit[0].peginWitness.stack = [b'\x00'*100000] # lol + assert_equal(sidechain.testmempoolaccept([signed_struct.serialize().hex()])[0]["allowed"], False) + assert_equal(sidechain.testmempoolaccept([signed_struct.serialize().hex()])[0]["reject-reason"], "68: extra-pegin-witness") + peg_out_txid = sidechain.sendtomainchain(some_btc_addr, 1) peg_out_details = sidechain.decoderawtransaction(sidechain.getrawtransaction(peg_out_txid)) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index e87d5f19e6..fa4ca43020 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -285,6 +285,38 @@ class COutPoint(): def __repr__(self): return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n) +OUTPOINT_ISSUANCE_FLAG = (1 << 31) +OUTPOINT_PEGIN_FLAG = (1 << 30) +OUTPOINT_INDEX_MASK = 0x3fffffff + +class CAssetIssuance(): + def __init__(self): + self.assetBlindingNonce = 0 + self.assetEntropy = 0 + self.nAmount = CTxOutValue() + self.nInflationKeys = CTxOutValue() + + def isNull(self): + return self.nAmount.isNull() and self.nInflationKeys.isNull() + + def deserialize(self, f): + self.assetBlindingNonce = deser_uint256(f) + self.assetEntropy = deser_uint256(f) + self.nAmount = CTxOutValue() + self.nAmount.deserialize(f) + self.nInflationKeys = CTxOutValue() + self.nInflatoinKeys.deserialize(f) + + def serialize(self): + r = b"" + r += ser_uint256(self.assetBlindingNonce) + r += ser_uint256(self.assetEntropy) + r += self.nAmount.serialize() + r += self.nInflationKeys.serialize() + return r + + def __repr__(self): + return "CAssetIssuance(assetBlindingNonce=%064x assetEntropy=%064x nAmount=%s nInflationKeys=%s)" % (self.assetBlindingNonce, self.assetEntropy, self.nAmount.vchCommitment, self.nInflationKeys.vchCommitment) class CTxIn(): def __init__(self, outpoint=None, scriptSig=b"", nSequence=0): @@ -294,24 +326,52 @@ class CTxIn(): self.prevout = outpoint self.scriptSig = scriptSig self.nSequence = nSequence + self.m_is_pegin = False + self.assetIssuance = CAssetIssuance() def deserialize(self, f): self.prevout = COutPoint() self.prevout.deserialize(f) + + has_asset_issuance = False + # Do masking, extract presence of assetIssuance + if not self.prevout.isNull(): # ignore coinbase for issuance/pegin + if self.prevout.n & OUTPOINT_ISSUANCE_FLAG > 0: + has_asset_issuance = True + if self.prevout.n & OUTPOINT_PEGIN_FLAG > 0: + self.m_is_pegin = True + self.prevout.n = self.prevout.n & OUTPOINT_INDEX_MASK + self.scriptSig = deser_string(f) self.nSequence = struct.unpack("