mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-09 10:43:19 -05:00
Merge bitcoin/bitcoin#26657: test: Run feature_bip68_sequence.py with MiniWallet
4159ccd031
test: Run feature_bip68_sequence.py with MiniWallet (Miles Liu)fc0caaf4aa
test: Add "include mempool" flag to MiniWallet rescan_utxos (Miles Liu)d0a909ae54
test: Add "include immature coinbase" flag to MiniWallet get_utxos (Miles Liu)e5b9127d9e
test: Add signs P2TR and RAWSCRIPT to MiniWallet sign_tx (Miles Liu) Pull request description: This PR enables one more of the non-wallet functional tests (feature_bip68_sequence.py) to be run even when no wallet is compiled in by using the MiniWallet instead, as proposed in #20078. ACKs for top commit: achow101: ACK4159ccd031
MarcoFalke: review ACK4159ccd031
🤸 Tree-SHA512: e891b189381e961c840b45fc30d058363707fd54c1c4bdc3d29623b03309981f1d3ebfac27e6aecf621bdbcd7e31754a3ef7c53f86346f7dd241c137e64c92bd
This commit is contained in:
commit
599e941c19
2 changed files with 93 additions and 92 deletions
|
@ -32,6 +32,7 @@ from test_framework.util import (
|
||||||
assert_raises_rpc_error,
|
assert_raises_rpc_error,
|
||||||
softfork_active,
|
softfork_active,
|
||||||
)
|
)
|
||||||
|
from test_framework.wallet import MiniWallet
|
||||||
|
|
||||||
SCRIPT_W0_SH_OP_TRUE = script_to_p2wsh_script(CScript([OP_TRUE]))
|
SCRIPT_W0_SH_OP_TRUE = script_to_p2wsh_script(CScript([OP_TRUE]))
|
||||||
|
|
||||||
|
@ -58,14 +59,12 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
|
||||||
self.skip_if_no_wallet()
|
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
|
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
|
||||||
|
self.wallet = MiniWallet(self.nodes[0])
|
||||||
|
|
||||||
# Generate some coins
|
# Generate some coins
|
||||||
self.generate(self.nodes[0], 110)
|
self.generate(self.wallet, 110)
|
||||||
|
|
||||||
self.log.info("Running test disable flag")
|
self.log.info("Running test disable flag")
|
||||||
self.test_disable_flag()
|
self.test_disable_flag()
|
||||||
|
@ -92,16 +91,10 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
# the first sequence bit is set.
|
# the first sequence bit is set.
|
||||||
def test_disable_flag(self):
|
def test_disable_flag(self):
|
||||||
# Create some unconfirmed inputs
|
# Create some unconfirmed inputs
|
||||||
new_addr = self.nodes[0].getnewaddress()
|
utxo = self.wallet.send_self_transfer(from_node=self.nodes[0])["new_utxo"]
|
||||||
self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC
|
|
||||||
|
|
||||||
utxos = self.nodes[0].listunspent(0, 0)
|
|
||||||
assert len(utxos) > 0
|
|
||||||
|
|
||||||
utxo = utxos[0]
|
|
||||||
|
|
||||||
tx1 = CTransaction()
|
tx1 = CTransaction()
|
||||||
value = int((utxo["amount"] - self.relayfee) * COIN)
|
value = int((utxo["value"] - self.relayfee) * COIN)
|
||||||
|
|
||||||
# Check that the disable flag disables relative locktime.
|
# Check that the disable flag disables relative locktime.
|
||||||
# If sequence locks were used, this would require 1 block for the
|
# If sequence locks were used, this would require 1 block for the
|
||||||
|
@ -110,8 +103,8 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
|
tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
|
||||||
tx1.vout = [CTxOut(value, SCRIPT_W0_SH_OP_TRUE)]
|
tx1.vout = [CTxOut(value, SCRIPT_W0_SH_OP_TRUE)]
|
||||||
|
|
||||||
tx1_signed = self.nodes[0].signrawtransactionwithwallet(tx1.serialize().hex())["hex"]
|
self.wallet.sign_tx(tx=tx1)
|
||||||
tx1_id = self.nodes[0].sendrawtransaction(tx1_signed)
|
tx1_id = self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx1.serialize().hex())
|
||||||
tx1_id = int(tx1_id, 16)
|
tx1_id = int(tx1_id, 16)
|
||||||
|
|
||||||
# This transaction will enable sequence-locks, so this transaction should
|
# This transaction will enable sequence-locks, so this transaction should
|
||||||
|
@ -125,13 +118,13 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||||
tx2.rehash()
|
tx2.rehash()
|
||||||
|
|
||||||
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx2.serialize().hex())
|
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
|
||||||
|
|
||||||
# Setting the version back down to 1 should disable the sequence lock,
|
# Setting the version back down to 1 should disable the sequence lock,
|
||||||
# so this should be accepted.
|
# so this should be accepted.
|
||||||
tx2.nVersion = 1
|
tx2.nVersion = 1
|
||||||
|
|
||||||
self.nodes[0].sendrawtransaction(tx2.serialize().hex())
|
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
|
||||||
|
|
||||||
# Calculate the median time past of a prior block ("confirmations" before
|
# Calculate the median time past of a prior block ("confirmations" before
|
||||||
# the current tip).
|
# the current tip).
|
||||||
|
@ -144,20 +137,13 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
# Create lots of confirmed utxos, and use them to generate lots of random
|
# Create lots of confirmed utxos, and use them to generate lots of random
|
||||||
# transactions.
|
# transactions.
|
||||||
max_outputs = 50
|
max_outputs = 50
|
||||||
addresses = []
|
while len(self.wallet.get_utxos(include_immature_coinbase=False, mark_as_spent=False)) < 200:
|
||||||
while len(addresses) < max_outputs:
|
|
||||||
addresses.append(self.nodes[0].getnewaddress())
|
|
||||||
while len(self.nodes[0].listunspent()) < 200:
|
|
||||||
import random
|
import random
|
||||||
random.shuffle(addresses)
|
|
||||||
num_outputs = random.randint(1, max_outputs)
|
num_outputs = random.randint(1, max_outputs)
|
||||||
outputs = {}
|
self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=num_outputs)
|
||||||
for i in range(num_outputs):
|
self.generate(self.wallet, 1)
|
||||||
outputs[addresses[i]] = random.randint(1, 20)*0.01
|
|
||||||
self.nodes[0].sendmany("", outputs)
|
|
||||||
self.generate(self.nodes[0], 1)
|
|
||||||
|
|
||||||
utxos = self.nodes[0].listunspent()
|
utxos = self.wallet.get_utxos(include_immature_coinbase=False)
|
||||||
|
|
||||||
# Try creating a lot of random transactions.
|
# Try creating a lot of random transactions.
|
||||||
# Each time, choose a random number of inputs, and randomly set
|
# Each time, choose a random number of inputs, and randomly set
|
||||||
|
@ -214,19 +200,20 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1
|
sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1
|
||||||
sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
|
sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
|
||||||
tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
|
tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
|
||||||
value += utxos[j]["amount"]*COIN
|
value += utxos[j]["value"]*COIN
|
||||||
# Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
|
# Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
|
||||||
tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50
|
tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50
|
||||||
tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE))
|
tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE))
|
||||||
rawtx = self.nodes[0].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
|
self.wallet.sign_tx(tx=tx)
|
||||||
|
|
||||||
if (using_sequence_locks and not should_pass):
|
if (using_sequence_locks and not should_pass):
|
||||||
# This transaction should be rejected
|
# This transaction should be rejected
|
||||||
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
|
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx.serialize().hex())
|
||||||
else:
|
else:
|
||||||
# This raw transaction should be accepted
|
# This raw transaction should be accepted
|
||||||
self.nodes[0].sendrawtransaction(rawtx)
|
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx.serialize().hex())
|
||||||
utxos = self.nodes[0].listunspent()
|
self.wallet.rescan_utxos()
|
||||||
|
utxos = self.wallet.get_utxos(include_immature_coinbase=False)
|
||||||
|
|
||||||
# Test that sequence locks on unconfirmed inputs must have nSequence
|
# Test that sequence locks on unconfirmed inputs must have nSequence
|
||||||
# height or time of 0 to be accepted.
|
# height or time of 0 to be accepted.
|
||||||
|
@ -237,8 +224,8 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
cur_height = self.nodes[0].getblockcount()
|
cur_height = self.nodes[0].getblockcount()
|
||||||
|
|
||||||
# Create a mempool tx.
|
# Create a mempool tx.
|
||||||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
|
self.wallet.rescan_utxos()
|
||||||
tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
|
tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
|
||||||
tx1.rehash()
|
tx1.rehash()
|
||||||
|
|
||||||
# Anyone-can-spend mempool tx.
|
# Anyone-can-spend mempool tx.
|
||||||
|
@ -247,11 +234,11 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx2.nVersion = 2
|
tx2.nVersion = 2
|
||||||
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
|
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
|
||||||
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||||
tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
|
self.wallet.sign_tx(tx=tx2)
|
||||||
tx2 = tx_from_hex(tx2_raw)
|
tx2_raw = tx2.serialize().hex()
|
||||||
tx2.rehash()
|
tx2.rehash()
|
||||||
|
|
||||||
self.nodes[0].sendrawtransaction(tx2_raw)
|
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
|
||||||
|
|
||||||
# Create a spend of the 0th output of orig_tx with a sequence lock
|
# Create a spend of the 0th output of orig_tx with a sequence lock
|
||||||
# of 1, and test what happens when submitting.
|
# of 1, and test what happens when submitting.
|
||||||
|
@ -271,10 +258,10 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
|
|
||||||
if (orig_tx.hash in node.getrawmempool()):
|
if (orig_tx.hash in node.getrawmempool()):
|
||||||
# sendrawtransaction should fail if the tx is in the mempool
|
# sendrawtransaction should fail if the tx is in the mempool
|
||||||
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, tx.serialize().hex())
|
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=node, tx_hex=tx.serialize().hex())
|
||||||
else:
|
else:
|
||||||
# sendrawtransaction should succeed if the tx is not in the mempool
|
# sendrawtransaction should succeed if the tx is not in the mempool
|
||||||
node.sendrawtransaction(tx.serialize().hex())
|
self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
|
||||||
|
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
|
@ -287,7 +274,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
cur_time = int(time.time())
|
cur_time = int(time.time())
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
self.nodes[0].setmocktime(cur_time + 600)
|
self.nodes[0].setmocktime(cur_time + 600)
|
||||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
|
self.generate(self.wallet, 1, sync_fun=self.no_op)
|
||||||
cur_time += 600
|
cur_time += 600
|
||||||
|
|
||||||
assert tx2.hash in self.nodes[0].getrawmempool()
|
assert tx2.hash in self.nodes[0].getrawmempool()
|
||||||
|
@ -321,12 +308,12 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
|
tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
|
||||||
assert tx5.hash not in self.nodes[0].getrawmempool()
|
assert tx5.hash not in self.nodes[0].getrawmempool()
|
||||||
|
|
||||||
utxos = self.nodes[0].listunspent()
|
utxo = self.wallet.get_utxo()
|
||||||
tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1))
|
tx5.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=1))
|
||||||
tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
|
tx5.vout[0].nValue += int(utxo["value"]*COIN)
|
||||||
raw_tx5 = self.nodes[0].signrawtransactionwithwallet(tx5.serialize().hex())["hex"]
|
self.wallet.sign_tx(tx=tx5)
|
||||||
|
|
||||||
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
|
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx5.serialize().hex())
|
||||||
|
|
||||||
# Test mempool-BIP68 consistency after reorg
|
# Test mempool-BIP68 consistency after reorg
|
||||||
#
|
#
|
||||||
|
@ -362,7 +349,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
# Reset the chain and get rid of the mocktimed-blocks
|
# Reset the chain and get rid of the mocktimed-blocks
|
||||||
self.nodes[0].setmocktime(0)
|
self.nodes[0].setmocktime(0)
|
||||||
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
|
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
|
||||||
self.generate(self.nodes[0], 10, sync_fun=self.no_op)
|
self.generate(self.wallet, 10, sync_fun=self.no_op)
|
||||||
|
|
||||||
# Make sure that BIP68 isn't being used to validate blocks prior to
|
# Make sure that BIP68 isn't being used to validate blocks prior to
|
||||||
# activation height. If more blocks are mined prior to this test
|
# activation height. If more blocks are mined prior to this test
|
||||||
|
@ -370,9 +357,8 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
# this test should be moved to run earlier, or deleted.
|
# this test should be moved to run earlier, or deleted.
|
||||||
def test_bip68_not_consensus(self):
|
def test_bip68_not_consensus(self):
|
||||||
assert not softfork_active(self.nodes[0], 'csv')
|
assert not softfork_active(self.nodes[0], 'csv')
|
||||||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
|
|
||||||
|
|
||||||
tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
|
tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
|
||||||
tx1.rehash()
|
tx1.rehash()
|
||||||
|
|
||||||
# Make an anyone-can-spend transaction
|
# Make an anyone-can-spend transaction
|
||||||
|
@ -382,11 +368,12 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||||
|
|
||||||
# sign tx2
|
# sign tx2
|
||||||
tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
|
self.wallet.sign_tx(tx=tx2)
|
||||||
|
tx2_raw = tx2.serialize().hex()
|
||||||
tx2 = tx_from_hex(tx2_raw)
|
tx2 = tx_from_hex(tx2_raw)
|
||||||
tx2.rehash()
|
tx2.rehash()
|
||||||
|
|
||||||
self.nodes[0].sendrawtransaction(tx2.serialize().hex())
|
self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
|
||||||
|
|
||||||
# Now make an invalid spend of tx2 according to BIP68
|
# Now make an invalid spend of tx2 according to BIP68
|
||||||
sequence_value = 100 # 100 block relative locktime
|
sequence_value = 100 # 100 block relative locktime
|
||||||
|
@ -399,7 +386,7 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
|
||||||
tx3.rehash()
|
tx3.rehash()
|
||||||
|
|
||||||
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx3.serialize().hex())
|
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx3.serialize().hex())
|
||||||
|
|
||||||
# make a block that violates bip68; ensure that the tip updates
|
# make a block that violates bip68; ensure that the tip updates
|
||||||
block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[tx1, tx2, tx3])
|
block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[tx1, tx2, tx3])
|
||||||
|
@ -415,22 +402,19 @@ class BIP68Test(BitcoinTestFramework):
|
||||||
min_activation_height = 432
|
min_activation_height = 432
|
||||||
height = self.nodes[0].getblockcount()
|
height = self.nodes[0].getblockcount()
|
||||||
assert_greater_than(min_activation_height - height, 2)
|
assert_greater_than(min_activation_height - height, 2)
|
||||||
self.generate(self.nodes[0], min_activation_height - height - 2, sync_fun=self.no_op)
|
self.generate(self.wallet, min_activation_height - height - 2, sync_fun=self.no_op)
|
||||||
assert not softfork_active(self.nodes[0], 'csv')
|
assert not softfork_active(self.nodes[0], 'csv')
|
||||||
self.generate(self.nodes[0], 1, sync_fun=self.no_op)
|
self.generate(self.wallet, 1, sync_fun=self.no_op)
|
||||||
assert softfork_active(self.nodes[0], 'csv')
|
assert softfork_active(self.nodes[0], 'csv')
|
||||||
self.sync_blocks()
|
self.sync_blocks()
|
||||||
|
|
||||||
# Use self.nodes[1] to test that version 2 transactions are standard.
|
# Use self.nodes[1] to test that version 2 transactions are standard.
|
||||||
def test_version2_relay(self):
|
def test_version2_relay(self):
|
||||||
inputs = [ ]
|
mini_wallet = MiniWallet(self.nodes[1])
|
||||||
outputs = { self.nodes[1].getnewaddress() : 1.0 }
|
mini_wallet.rescan_utxos()
|
||||||
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
|
tx = mini_wallet.create_self_transfer()["tx"]
|
||||||
rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex']
|
|
||||||
tx = tx_from_hex(rawtxfund)
|
|
||||||
tx.nVersion = 2
|
tx.nVersion = 2
|
||||||
tx_signed = self.nodes[1].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
|
mini_wallet.sendrawtransaction(from_node=self.nodes[1], tx_hex=tx.serialize().hex())
|
||||||
self.nodes[1].sendrawtransaction(tx_signed)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
BIP68Test().main()
|
BIP68Test().main()
|
||||||
|
|
|
@ -55,6 +55,7 @@ from test_framework.util import (
|
||||||
assert_equal,
|
assert_equal,
|
||||||
assert_greater_than_or_equal,
|
assert_greater_than_or_equal,
|
||||||
)
|
)
|
||||||
|
from test_framework.blocktools import COINBASE_MATURITY
|
||||||
|
|
||||||
DEFAULT_FEE = Decimal("0.0001")
|
DEFAULT_FEE = Decimal("0.0001")
|
||||||
|
|
||||||
|
@ -100,8 +101,8 @@ class MiniWallet:
|
||||||
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
|
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
|
||||||
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
|
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
|
||||||
|
|
||||||
def _create_utxo(self, *, txid, vout, value, height):
|
def _create_utxo(self, *, txid, vout, value, height, coinbase, confirmations):
|
||||||
return {"txid": txid, "vout": vout, "value": value, "height": height}
|
return {"txid": txid, "vout": vout, "value": value, "height": height, "coinbase": coinbase, "confirmations": confirmations}
|
||||||
|
|
||||||
def _bulk_tx(self, tx, target_weight):
|
def _bulk_tx(self, tx, target_weight):
|
||||||
"""Pad a transaction with extra outputs until it reaches a target weight (or higher).
|
"""Pad a transaction with extra outputs until it reaches a target weight (or higher).
|
||||||
|
@ -118,13 +119,25 @@ class MiniWallet:
|
||||||
def get_balance(self):
|
def get_balance(self):
|
||||||
return sum(u['value'] for u in self._utxos)
|
return sum(u['value'] for u in self._utxos)
|
||||||
|
|
||||||
def rescan_utxos(self):
|
def rescan_utxos(self, *, include_mempool=True):
|
||||||
"""Drop all utxos and rescan the utxo set"""
|
"""Drop all utxos and rescan the utxo set"""
|
||||||
self._utxos = []
|
self._utxos = []
|
||||||
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
|
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
|
||||||
assert_equal(True, res['success'])
|
assert_equal(True, res['success'])
|
||||||
for utxo in res['unspents']:
|
for utxo in res['unspents']:
|
||||||
self._utxos.append(self._create_utxo(txid=utxo["txid"], vout=utxo["vout"], value=utxo["amount"], height=utxo["height"]))
|
self._utxos.append(
|
||||||
|
self._create_utxo(txid=utxo["txid"],
|
||||||
|
vout=utxo["vout"],
|
||||||
|
value=utxo["amount"],
|
||||||
|
height=utxo["height"],
|
||||||
|
coinbase=utxo["coinbase"],
|
||||||
|
confirmations=res["height"] - utxo["height"] + 1))
|
||||||
|
if include_mempool:
|
||||||
|
mempool = self._test_node.getrawmempool(verbose=True)
|
||||||
|
# Sort tx by ancestor count. See BlockAssembler::SortForBlock in src/node/miner.cpp
|
||||||
|
sorted_mempool = sorted(mempool.items(), key=lambda item: (item[1]["ancestorcount"], int(item[0], 16)))
|
||||||
|
for txid, _ in sorted_mempool:
|
||||||
|
self.scan_tx(self._test_node.getrawtransaction(txid=txid, verbose=True))
|
||||||
|
|
||||||
def scan_tx(self, tx):
|
def scan_tx(self, tx):
|
||||||
"""Scan the tx and adjust the internal list of owned utxos"""
|
"""Scan the tx and adjust the internal list of owned utxos"""
|
||||||
|
@ -139,15 +152,14 @@ class MiniWallet:
|
||||||
pass
|
pass
|
||||||
for out in tx['vout']:
|
for out in tx['vout']:
|
||||||
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
|
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
|
||||||
self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0))
|
self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0, coinbase=False, confirmations=0))
|
||||||
|
|
||||||
def scan_txs(self, txs):
|
def scan_txs(self, txs):
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
self.scan_tx(tx)
|
self.scan_tx(tx)
|
||||||
|
|
||||||
def sign_tx(self, tx, fixed_length=True):
|
def sign_tx(self, tx, fixed_length=True):
|
||||||
"""Sign tx that has been created by MiniWallet in P2PK mode"""
|
if self._mode == MiniWalletMode.RAW_P2PK:
|
||||||
assert_equal(self._mode, MiniWalletMode.RAW_P2PK)
|
|
||||||
(sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
|
(sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
|
||||||
assert err is None
|
assert err is None
|
||||||
# for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
|
# for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
|
||||||
|
@ -160,6 +172,15 @@ class MiniWallet:
|
||||||
break
|
break
|
||||||
tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
|
tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
|
elif self._mode == MiniWalletMode.RAW_OP_TRUE:
|
||||||
|
for i in range(len(tx.vin)):
|
||||||
|
tx.vin[i].scriptSig = CScript([OP_NOP] * 43) # pad to identical size
|
||||||
|
elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
||||||
|
tx.wit.vtxinwit = [CTxInWitness()] * len(tx.vin)
|
||||||
|
for i in range(len(tx.vin)):
|
||||||
|
tx.wit.vtxinwit[i].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
def generate(self, num_blocks, **kwargs):
|
def generate(self, num_blocks, **kwargs):
|
||||||
"""Generate blocks with coinbase outputs to the internal address, and call rescan_utxos"""
|
"""Generate blocks with coinbase outputs to the internal address, and call rescan_utxos"""
|
||||||
|
@ -204,9 +225,13 @@ class MiniWallet:
|
||||||
else:
|
else:
|
||||||
return self._utxos[index]
|
return self._utxos[index]
|
||||||
|
|
||||||
def get_utxos(self, *, mark_as_spent=True):
|
def get_utxos(self, *, include_immature_coinbase=False, mark_as_spent=True):
|
||||||
"""Returns the list of all utxos and optionally mark them as spent"""
|
"""Returns the list of all utxos and optionally mark them as spent"""
|
||||||
utxos = deepcopy(self._utxos)
|
if not include_immature_coinbase:
|
||||||
|
utxo_filter = filter(lambda utxo: not utxo['coinbase'] or COINBASE_MATURITY <= utxo['confirmations'], self._utxos)
|
||||||
|
else:
|
||||||
|
utxo_filter = self._utxos
|
||||||
|
utxos = deepcopy(list(utxo_filter))
|
||||||
if mark_as_spent:
|
if mark_as_spent:
|
||||||
self._utxos = []
|
self._utxos = []
|
||||||
return utxos
|
return utxos
|
||||||
|
@ -273,17 +298,7 @@ class MiniWallet:
|
||||||
tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)]
|
tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)]
|
||||||
tx.nLockTime = locktime
|
tx.nLockTime = locktime
|
||||||
|
|
||||||
if self._mode == MiniWalletMode.RAW_P2PK:
|
|
||||||
self.sign_tx(tx)
|
self.sign_tx(tx)
|
||||||
elif self._mode == MiniWalletMode.RAW_OP_TRUE:
|
|
||||||
for i in range(len(utxos_to_spend)):
|
|
||||||
tx.vin[i].scriptSig = CScript([OP_NOP] * 43) # pad to identical size
|
|
||||||
elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
|
||||||
tx.wit.vtxinwit = [CTxInWitness()] * len(utxos_to_spend)
|
|
||||||
for i in range(len(utxos_to_spend)):
|
|
||||||
tx.wit.vtxinwit[i].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if target_weight:
|
if target_weight:
|
||||||
self._bulk_tx(tx, target_weight)
|
self._bulk_tx(tx, target_weight)
|
||||||
|
@ -295,6 +310,8 @@ class MiniWallet:
|
||||||
vout=i,
|
vout=i,
|
||||||
value=Decimal(tx.vout[i].nValue) / COIN,
|
value=Decimal(tx.vout[i].nValue) / COIN,
|
||||||
height=0,
|
height=0,
|
||||||
|
coinbase=False,
|
||||||
|
confirmations=0,
|
||||||
) for i in range(len(tx.vout))],
|
) for i in range(len(tx.vout))],
|
||||||
"txid": txid,
|
"txid": txid,
|
||||||
"hex": tx.serialize().hex(),
|
"hex": tx.serialize().hex(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue