mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-21 11:25:46 +02:00
Add a "tx output spender" index
Adds an outpoint -> txid index, which can be used to find which transactions spent a given output. We use a composite key with 2 parts (suggested by @romanz): hash(spent outpoint) and tx position, with an empty value. To find the spending tx for a given outpoint, we do a prefix search (prefix being the hash of the provided outpoint), and for all keys that match this prefix we load the tx at the position specified in the key and return it, along with the block hash, if does spend the provided outpoint. To handle reorgs we just erase the keys computed from the removed block. This index is extremely useful for Lightning and more generally for layer-2 protocols that rely on chains of unpublished transactions. If enabled, this index will be used by `gettxspendingprevout` when it does not find a spending transaction in the mempool.
This commit is contained in:
@@ -79,7 +79,7 @@ class InitTest(BitcoinTestFramework):
|
||||
if self.is_wallet_compiled():
|
||||
lines_to_terminate_after.append(b'Verifying wallet')
|
||||
|
||||
args = ['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1']
|
||||
args = ['-txindex=1', '-blockfilterindex=1', '-coinstatsindex=1', '-txospenderindex=1']
|
||||
for terminate_line in lines_to_terminate_after:
|
||||
self.log.info(f"Starting node and will terminate after line {terminate_line}")
|
||||
with node.busy_wait_for_debug_log([terminate_line]):
|
||||
@@ -133,6 +133,11 @@ class InitTest(BitcoinTestFramework):
|
||||
'error_message': 'LevelDB error: Corruption: CURRENT points to a non-existent file',
|
||||
'startup_args': ['-txindex=1'],
|
||||
},
|
||||
{
|
||||
'filepath_glob': 'indexes/txospenderindex/db/MANIFEST*',
|
||||
'error_message': 'LevelDB error: Corruption: CURRENT points to a non-existent file',
|
||||
'startup_args': ['-txospenderindex=1'],
|
||||
},
|
||||
# Removing these files does not result in a startup error:
|
||||
# 'indexes/blockfilter/basic/*.dat', 'indexes/blockfilter/basic/db/*.*', 'indexes/coinstatsindex/db/*.*',
|
||||
# 'indexes/txindex/*.log', 'indexes/txindex/CURRENT', 'indexes/txindex/LOCK'
|
||||
@@ -174,6 +179,11 @@ class InitTest(BitcoinTestFramework):
|
||||
'error_message': 'LevelDB error: Corruption',
|
||||
'startup_args': ['-txindex=1'],
|
||||
},
|
||||
{
|
||||
'filepath_glob': 'indexes/txospenderindex/db/*',
|
||||
'error_message': 'LevelDB error: Corruption',
|
||||
'startup_args': ['-txospenderindex=1'],
|
||||
},
|
||||
# Perturbing these files does not result in a startup error:
|
||||
# 'indexes/blockfilter/basic/*.dat', 'indexes/txindex/MANIFEST*', 'indexes/txindex/LOCK'
|
||||
]
|
||||
@@ -183,6 +193,7 @@ class InitTest(BitcoinTestFramework):
|
||||
err_fragment = round_info['error_message']
|
||||
startup_args = round_info['startup_args']
|
||||
target_files = list(node.chain_path.glob(file_patt))
|
||||
assert target_files, f"Failed to find expected files: {file_patt}"
|
||||
|
||||
for target_file in target_files:
|
||||
self.log.info(f"Deleting file to ensure failure {target_file}")
|
||||
@@ -209,6 +220,7 @@ class InitTest(BitcoinTestFramework):
|
||||
for dir in dirs:
|
||||
shutil.copytree(node.chain_path / dir, node.chain_path / f"{dir}_bak")
|
||||
target_files = list(node.chain_path.glob(file_patt))
|
||||
assert target_files, f"Failed to find expected files: {file_patt}"
|
||||
|
||||
for target_file in target_files:
|
||||
self.log.info(f"Perturbing file to ensure failure {target_file}")
|
||||
|
||||
182
test/functional/rpc_gettxspendingprevout.py
Executable file
182
test/functional/rpc_gettxspendingprevout.py
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test gettxspendingprevout RPC."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
|
||||
class GetTxSpendingPrevoutTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 3
|
||||
self.noban_tx_relay = True
|
||||
self.extra_args = [
|
||||
["-txospenderindex"],
|
||||
["-txospenderindex"],
|
||||
[],
|
||||
]
|
||||
|
||||
def run_test(self):
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
confirmed_utxo = self.wallet.get_utxo()
|
||||
|
||||
# Create a tree of unconfirmed transactions in the mempool:
|
||||
# txA
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# txB txC
|
||||
# / \ / \
|
||||
# / \ / \
|
||||
# txD txE txF txG
|
||||
# \ /
|
||||
# \ /
|
||||
# txH
|
||||
|
||||
def create_tx(**kwargs):
|
||||
return self.wallet.send_self_transfer_multi(
|
||||
from_node=self.nodes[0],
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
txA = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
|
||||
txB = create_tx(utxos_to_spend=[txA["new_utxos"][0]], num_outputs=2)
|
||||
txC = create_tx(utxos_to_spend=[txA["new_utxos"][1]], num_outputs=2)
|
||||
txD = create_tx(utxos_to_spend=[txB["new_utxos"][0]], num_outputs=1)
|
||||
txE = create_tx(utxos_to_spend=[txB["new_utxos"][1]], num_outputs=1)
|
||||
txF = create_tx(utxos_to_spend=[txC["new_utxos"][0]], num_outputs=2)
|
||||
txG = create_tx(utxos_to_spend=[txC["new_utxos"][1]], num_outputs=1)
|
||||
txH = create_tx(utxos_to_spend=[txE["new_utxos"][0],txF["new_utxos"][0]], num_outputs=1)
|
||||
txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH = [
|
||||
tx["txid"] for tx in [txA, txB, txC, txD, txE, txF, txG, txH]
|
||||
]
|
||||
|
||||
mempool = self.nodes[0].getrawmempool()
|
||||
assert_equal(len(mempool), 8)
|
||||
for txid in [txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH]:
|
||||
assert_equal(txid in mempool, True)
|
||||
|
||||
self.log.info("Find transactions spending outputs")
|
||||
# wait until spending transactions are found in the mempool of node 0, 1 and 2
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ])
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : txidA}, {'txid' : txidA, 'vout' : 1, 'spendingtxid' : txidC} ])
|
||||
self.wait_until(lambda: self.nodes[1].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ]) == result)
|
||||
self.wait_until(lambda: self.nodes[2].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ], mempool_only=True) == result)
|
||||
|
||||
self.log.info("Find transaction spending multiple outputs")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidE, 'vout' : 0}, {'txid' : txidF, 'vout' : 0} ])
|
||||
assert_equal(result, [ {'txid' : txidE, 'vout' : 0, 'spendingtxid' : txidH}, {'txid' : txidF, 'vout' : 0, 'spendingtxid' : txidH} ])
|
||||
|
||||
self.log.info("Find no transaction when output is unspent")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidH, 'vout' : 0} ])
|
||||
assert_equal(result, [ {'txid' : txidH, 'vout' : 0} ])
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidA, 'vout' : 5} ])
|
||||
assert_equal(result, [ {'txid' : txidA, 'vout' : 5} ])
|
||||
result = self.nodes[1].gettxspendingprevout([ {'txid' : txidA, 'vout' : 5} ])
|
||||
assert_equal(result, [ {'txid' : txidA, 'vout' : 5} ])
|
||||
result = self.nodes[2].gettxspendingprevout([ {'txid' : txidA, 'vout' : 5} ])
|
||||
assert_equal(result, [ {'txid' : txidA, 'vout' : 5} ])
|
||||
|
||||
self.log.info("Mixed spent and unspent outputs")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidB, 'vout' : 0}, {'txid' : txidG, 'vout' : 3} ])
|
||||
assert_equal(result, [ {'txid' : txidB, 'vout' : 0, 'spendingtxid' : txidD}, {'txid' : txidG, 'vout' : 3} ])
|
||||
|
||||
self.log.info("Unknown input fields")
|
||||
assert_raises_rpc_error(-3, "Unexpected key unknown", self.nodes[0].gettxspendingprevout, [{'txid' : txidC, 'vout' : 1, 'unknown' : 42}])
|
||||
|
||||
self.log.info("Invalid vout provided")
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].gettxspendingprevout, [{'txid' : txidA, 'vout' : -1}])
|
||||
|
||||
self.log.info("Invalid txid provided")
|
||||
assert_raises_rpc_error(-3, "JSON value of type number for field txid is not of expected type string", self.nodes[0].gettxspendingprevout, [{'txid' : 42, 'vout' : 0}])
|
||||
|
||||
self.log.info("Missing outputs")
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, outputs are missing", self.nodes[0].gettxspendingprevout, [])
|
||||
|
||||
self.log.info("Missing vout")
|
||||
assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].gettxspendingprevout, [{'txid' : txidA}])
|
||||
|
||||
self.log.info("Missing txid")
|
||||
assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].gettxspendingprevout, [{'vout' : 3}])
|
||||
|
||||
blockhash = self.generate(self.wallet, 1)[0]
|
||||
# spending transactions are found in the index of nodes 0 and 1 but not node 2
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : txidA, 'blockhash' : blockhash, 'spendingtx' : txA['hex']}, {'txid' : txidA, 'vout' : 1, 'spendingtxid' : txidC, 'blockhash' : blockhash, 'spendingtx' : txC['hex']} ])
|
||||
result = self.nodes[1].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : txidA, 'blockhash' : blockhash, 'spendingtx' : txA['hex']}, {'txid' : txidA, 'vout' : 1, 'spendingtxid' : txidC, 'blockhash' : blockhash, 'spendingtx' : txC['hex']} ])
|
||||
result = self.nodes[2].gettxspendingprevout([{ 'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1}])
|
||||
|
||||
self.log.info("Check that our txospenderindex is updated when a reorg replaces a spending transaction")
|
||||
confirmed_utxo = self.wallet.get_utxo(mark_as_spent = False)
|
||||
tx1 = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=1)
|
||||
blockhash = self.generate(self.wallet, 1)[0]
|
||||
# tx1 is confirmed, and indexed in txospenderindex as spending our utxo
|
||||
assert tx1["txid"] not in self.nodes[0].getrawmempool()
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : tx1["txid"], 'blockhash' : blockhash, 'spendingtx' : tx1['hex']} ])
|
||||
# replace tx1 with tx2
|
||||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
|
||||
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
|
||||
self.nodes[2].invalidateblock(self.nodes[2].getbestblockhash())
|
||||
assert tx1["txid"] in self.nodes[0].getrawmempool()
|
||||
assert tx1["txid"] in self.nodes[1].getrawmempool()
|
||||
tx2 = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
|
||||
assert tx2["txid"] in self.nodes[0].getrawmempool()
|
||||
|
||||
# check that when we find tx2 when we look in the mempool for a tx spending our output
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : tx2["txid"], 'spendingtx' : tx2['hex']} ])
|
||||
|
||||
# check that our txospenderindex has been updated
|
||||
blockhash = self.generate(self.wallet, 1)[0]
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : tx2["txid"], 'blockhash' : blockhash, 'spendingtx' : tx2['hex']} ])
|
||||
|
||||
self.log.info("Check that our txospenderindex is updated when a reorg cancels a spending transaction")
|
||||
confirmed_utxo = self.wallet.get_utxo(mark_as_spent = False)
|
||||
tx1 = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=1)
|
||||
tx2 = create_tx(utxos_to_spend=[tx1["new_utxos"][0]], num_outputs=1)
|
||||
# tx1 spends our utxo, tx2 spends tx1
|
||||
blockhash = self.generate(self.wallet, 1)[0]
|
||||
# tx1 and tx2 are confirmed, and indexed in txospenderindex
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : tx1["txid"], 'blockhash' : blockhash, 'spendingtx' : tx1['hex']} ])
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : tx1['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : tx1['txid'], 'vout' : 0, 'spendingtxid' : tx2["txid"], 'blockhash' : blockhash, 'spendingtx' : tx2['hex']} ])
|
||||
# replace tx1 with tx3
|
||||
blockhash= self.nodes[0].getbestblockhash()
|
||||
self.nodes[0].invalidateblock(blockhash)
|
||||
self.nodes[1].invalidateblock(blockhash)
|
||||
self.nodes[2].invalidateblock(blockhash)
|
||||
tx3 = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2, fee_per_output=2000)
|
||||
assert tx3["txid"] in self.nodes[0].getrawmempool()
|
||||
assert tx1["txid"] not in self.nodes[0].getrawmempool()
|
||||
assert tx2["txid"] not in self.nodes[0].getrawmempool()
|
||||
# tx2 is not in the mempool anymore, but still in txospender index which has not been rewound yet
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : tx1['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : tx1['txid'], 'vout' : 0, 'spendingtxid' : tx2["txid"], 'blockhash' : blockhash, 'spendingtx' : tx2['hex']} ])
|
||||
txinfo = self.nodes[0].getrawtransaction(tx2["txid"], verbose = True, blockhash = blockhash)
|
||||
assert_equal(txinfo["confirmations"], 0)
|
||||
assert_equal(txinfo["in_active_chain"], False)
|
||||
|
||||
blockhash = self.generate(self.wallet, 1)[0]
|
||||
# we check that the spending tx for tx1 is now tx3
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : tx3["txid"], 'blockhash' : blockhash, 'spendingtx' : tx3['hex']} ])
|
||||
# we check that there is no more spending tx for tx1
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : tx1['txid'], 'vout' : 0} ], return_spending_tx=True)
|
||||
assert_equal(result, [ {'txid' : tx1['txid'], 'vout' : 0} ])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
GetTxSpendingPrevoutTest(__file__).main()
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-present The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test RPCs that retrieve information from the mempool."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
|
||||
class RPCMempoolInfoTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
|
||||
def run_test(self):
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
confirmed_utxo = self.wallet.get_utxo()
|
||||
|
||||
# Create a tree of unconfirmed transactions in the mempool:
|
||||
# txA
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# / \
|
||||
# txB txC
|
||||
# / \ / \
|
||||
# / \ / \
|
||||
# txD txE txF txG
|
||||
# \ /
|
||||
# \ /
|
||||
# txH
|
||||
|
||||
def create_tx(**kwargs):
|
||||
return self.wallet.send_self_transfer_multi(
|
||||
from_node=self.nodes[0],
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
txA = create_tx(utxos_to_spend=[confirmed_utxo], num_outputs=2)
|
||||
txB = create_tx(utxos_to_spend=[txA["new_utxos"][0]], num_outputs=2)
|
||||
txC = create_tx(utxos_to_spend=[txA["new_utxos"][1]], num_outputs=2)
|
||||
txD = create_tx(utxos_to_spend=[txB["new_utxos"][0]], num_outputs=1)
|
||||
txE = create_tx(utxos_to_spend=[txB["new_utxos"][1]], num_outputs=1)
|
||||
txF = create_tx(utxos_to_spend=[txC["new_utxos"][0]], num_outputs=2)
|
||||
txG = create_tx(utxos_to_spend=[txC["new_utxos"][1]], num_outputs=1)
|
||||
txH = create_tx(utxos_to_spend=[txE["new_utxos"][0],txF["new_utxos"][0]], num_outputs=1)
|
||||
txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH = [
|
||||
tx["txid"] for tx in [txA, txB, txC, txD, txE, txF, txG, txH]
|
||||
]
|
||||
|
||||
mempool = self.nodes[0].getrawmempool()
|
||||
assert_equal(len(mempool), 8)
|
||||
for txid in [txidA, txidB, txidC, txidD, txidE, txidF, txidG, txidH]:
|
||||
assert_equal(txid in mempool, True)
|
||||
|
||||
self.log.info("Find transactions spending outputs")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : confirmed_utxo['txid'], 'vout' : 0}, {'txid' : txidA, 'vout' : 1} ])
|
||||
assert_equal(result, [ {'txid' : confirmed_utxo['txid'], 'vout' : 0, 'spendingtxid' : txidA}, {'txid' : txidA, 'vout' : 1, 'spendingtxid' : txidC} ])
|
||||
|
||||
self.log.info("Find transaction spending multiple outputs")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidE, 'vout' : 0}, {'txid' : txidF, 'vout' : 0} ])
|
||||
assert_equal(result, [ {'txid' : txidE, 'vout' : 0, 'spendingtxid' : txidH}, {'txid' : txidF, 'vout' : 0, 'spendingtxid' : txidH} ])
|
||||
|
||||
self.log.info("Find no transaction when output is unspent")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidH, 'vout' : 0} ])
|
||||
assert_equal(result, [ {'txid' : txidH, 'vout' : 0} ])
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidA, 'vout' : 5} ])
|
||||
assert_equal(result, [ {'txid' : txidA, 'vout' : 5} ])
|
||||
|
||||
self.log.info("Mixed spent and unspent outputs")
|
||||
result = self.nodes[0].gettxspendingprevout([ {'txid' : txidB, 'vout' : 0}, {'txid' : txidG, 'vout' : 3} ])
|
||||
assert_equal(result, [ {'txid' : txidB, 'vout' : 0, 'spendingtxid' : txidD}, {'txid' : txidG, 'vout' : 3} ])
|
||||
|
||||
self.log.info("Unknown input fields")
|
||||
assert_raises_rpc_error(-3, "Unexpected key unknown", self.nodes[0].gettxspendingprevout, [{'txid' : txidC, 'vout' : 1, 'unknown' : 42}])
|
||||
|
||||
self.log.info("Invalid vout provided")
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].gettxspendingprevout, [{'txid' : txidA, 'vout' : -1}])
|
||||
|
||||
self.log.info("Invalid txid provided")
|
||||
assert_raises_rpc_error(-3, "JSON value of type number for field txid is not of expected type string", self.nodes[0].gettxspendingprevout, [{'txid' : 42, 'vout' : 0}])
|
||||
|
||||
self.log.info("Missing outputs")
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, outputs are missing", self.nodes[0].gettxspendingprevout, [])
|
||||
|
||||
self.log.info("Missing vout")
|
||||
assert_raises_rpc_error(-3, "Missing vout", self.nodes[0].gettxspendingprevout, [{'txid' : txidA}])
|
||||
|
||||
self.log.info("Missing txid")
|
||||
assert_raises_rpc_error(-3, "Missing txid", self.nodes[0].gettxspendingprevout, [{'vout' : 3}])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RPCMempoolInfoTest(__file__).main()
|
||||
@@ -100,7 +100,7 @@ class RpcMiscTest(BitcoinTestFramework):
|
||||
assert_equal(node.getindexinfo(), {})
|
||||
|
||||
# Restart the node with indices and wait for them to sync
|
||||
self.restart_node(0, ["-txindex", "-blockfilterindex", "-coinstatsindex"])
|
||||
self.restart_node(0, ["-txindex", "-blockfilterindex", "-coinstatsindex", "-txospenderindex"])
|
||||
self.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values()))
|
||||
|
||||
# Returns a list of all running indices by default
|
||||
@@ -111,10 +111,11 @@ class RpcMiscTest(BitcoinTestFramework):
|
||||
"txindex": values,
|
||||
"basic block filter index": values,
|
||||
"coinstatsindex": values,
|
||||
"txospenderindex": values
|
||||
}
|
||||
)
|
||||
# Specifying an index by name returns only the status of that index
|
||||
for i in {"txindex", "basic block filter index", "coinstatsindex"}:
|
||||
for i in {"txindex", "basic block filter index", "coinstatsindex", "txospenderindex"}:
|
||||
assert_equal(node.getindexinfo(i), {i: values})
|
||||
|
||||
# Specifying an unknown index name returns an empty result
|
||||
|
||||
@@ -373,7 +373,7 @@ BASE_SCRIPTS = [
|
||||
'feature_presegwit_node_upgrade.py',
|
||||
'feature_settings.py',
|
||||
'rpc_getdescriptorinfo.py',
|
||||
'rpc_mempool_info.py',
|
||||
'rpc_gettxspendingprevout.py',
|
||||
'rpc_help.py',
|
||||
'feature_framework_testshell.py',
|
||||
'tool_rpcauth.py',
|
||||
|
||||
Reference in New Issue
Block a user