qa: functional test for transaction listing commands

This commit is contained in:
Antoine Poinsot 2022-11-22 18:31:29 +01:00 committed by edouard
parent 1f06c4d4dc
commit c39cb07360

View File

@ -1,7 +1,5 @@
import os
import pytest
import random
import shutil
import time
from fixtures import *
@ -47,9 +45,7 @@ def test_listcoins(lianad, bitcoind):
# If the coin gets confirmed, it'll be marked as such.
bitcoind.generate_block(1, wait_for_mempool=txid)
block_height = bitcoind.rpc.getblockcount()
wait_for(
lambda: lianad.rpc.listcoins()["coins"][0]["block_height"] == block_height
)
wait_for(lambda: lianad.rpc.listcoins()["coins"][0]["block_height"] == block_height)
# Same if the coin gets spent.
spend_tx = spend_coins(lianad, bitcoind, (res[0],))
@ -356,3 +352,173 @@ def test_start_rescan(lianad, bitcoind):
# Now that it caught up it noticed which one were used onchain, so it won't reuse
# this derivation indexes anymore.
assert lianad.rpc.getnewaddress() not in (first_address, second_address)
def test_listtransactions(lianad, bitcoind):
"""Test listing of transactions by txid and timespan"""
def sign_and_broadcast(psbt):
txid = psbt.tx.txid().hex()
psbt = lianad.sign_psbt(psbt)
lianad.rpc.updatespend(psbt.to_base64())
lianad.rpc.broadcastspend(txid)
return txid
def wait_synced():
wait_for(
lambda: lianad.rpc.getinfo()["blockheight"] == bitcoind.rpc.getblockcount()
)
best_block = bitcoind.rpc.getbestblockhash()
initial_timestamp = bitcoind.rpc.getblockheader(best_block)["time"]
wait_synced()
# Deposit multiple coins in a single transaction
destinations = {
lianad.rpc.getnewaddress()["address"]: 0.0123456,
lianad.rpc.getnewaddress()["address"]: 0.0123457,
lianad.rpc.getnewaddress()["address"]: 0.0123458,
}
txid = bitcoind.rpc.sendmany("", destinations)
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 3)
bitcoind.generate_block(1, wait_for_mempool=txid)
# Mine 12 blocks to force the blocktime to increase
bitcoind.generate_block(12)
wait_synced()
best_block = bitcoind.rpc.getbestblockhash()
second_timestamp = bitcoind.rpc.getblockheader(best_block)["time"]
assert second_timestamp > initial_timestamp
# Deposit a coin that will be unspent
addr = lianad.rpc.getnewaddress()["address"]
txid = bitcoind.rpc.sendtoaddress(addr, 0.123456)
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 4)
bitcoind.generate_block(1, wait_for_mempool=txid)
# Deposit a coin that will be spent with a change output
addr = lianad.rpc.getnewaddress()["address"]
txid = bitcoind.rpc.sendtoaddress(addr, 0.23456)
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 5)
bitcoind.generate_block(1, wait_for_mempool=txid)
outpoint = next(
c["outpoint"] for c in lianad.rpc.listcoins()["coins"] if txid in c["outpoint"]
)
destinations = {
bitcoind.rpc.getnewaddress(): 100_000,
}
res = lianad.rpc.createspend(destinations, [outpoint], 6)
psbt = PSBT.from_base64(res["psbt"])
txid = sign_and_broadcast(psbt)
bitcoind.generate_block(1, wait_for_mempool=txid)
# Mine 12 blocks to force the blocktime to increase
bitcoind.generate_block(12)
wait_synced()
best_block = bitcoind.rpc.getbestblockhash()
third_timestamp = bitcoind.rpc.getblockheader(best_block)["time"]
assert third_timestamp > second_timestamp
bitcoind.generate_block(12)
wait_synced()
# Deposit a coin that will be spent with a change output and also two new deposits
addr = lianad.rpc.getnewaddress()["address"]
txid = bitcoind.rpc.sendtoaddress(addr, 0.3456)
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 7)
bitcoind.generate_block(1, wait_for_mempool=txid)
outpoint = next(
c["outpoint"] for c in lianad.rpc.listcoins()["coins"] if txid in c["outpoint"]
)
destinations = {
bitcoind.rpc.getnewaddress(): 11_000,
addr: 12_000, # Even with address reuse! Booooh
lianad.rpc.getnewaddress()["address"]: 13_000,
}
res = lianad.rpc.createspend(destinations, [outpoint], 6)
psbt = PSBT.from_base64(res["psbt"])
txid = sign_and_broadcast(psbt)
bitcoind.generate_block(1, wait_for_mempool=txid)
# Deposit a coin that will be spending (unconfirmed spend transaction)
addr = lianad.rpc.getnewaddress()["address"]
txid = bitcoind.rpc.sendtoaddress(addr, 0.456)
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 11)
bitcoind.generate_block(1, wait_for_mempool=txid)
outpoint = next(
c["outpoint"] for c in lianad.rpc.listcoins()["coins"] if txid in c["outpoint"]
)
destinations = {
bitcoind.rpc.getnewaddress(): 11_000,
}
res = lianad.rpc.createspend(destinations, [outpoint], 6)
psbt = PSBT.from_base64(res["psbt"])
txid = sign_and_broadcast(psbt)
# At this point we have 12 spent and unspent coins, one of them is unconfirmed.
wait_for(lambda: len(lianad.rpc.listcoins()["coins"]) == 12)
# However some of them share the same txid! This is the case of the 3 first coins
# for instance, or the Spend transactions with multiple outputs at one of our addresses.
# In total, that's 8 transactions.
txids = set(c["outpoint"][:-2] for c in lianad.rpc.listcoins()["coins"])
assert len(txids) == 8
# We can query all of them at once using listtransactions. The result contains all
# the correct transactions as hex, with no duplicate.
all_txs = lianad.rpc.listtransactions(list(txids))["transactions"]
assert len(all_txs) == 8
for tx in all_txs:
txid = bitcoind.rpc.decoderawtransaction(tx["tx"])["txid"]
txids.remove(txid) # This will raise an error if it isn't there
# We can also query them one by one.
txids = set(c["outpoint"][:-2] for c in lianad.rpc.listcoins()["coins"])
for txid in txids:
txs = lianad.rpc.listtransactions([txid])["transactions"]
bit_txid = bitcoind.rpc.decoderawtransaction(txs[0]["tx"])["txid"]
assert bit_txid == txid
# We can query all confirmed transactions
best_block = bitcoind.rpc.getbestblockhash()
final_timestamp = bitcoind.rpc.getblockheader(best_block)["time"]
txs = lianad.rpc.listconfirmed(initial_timestamp, final_timestamp, 10)[
"transactions"
]
assert len(txs) == 7, "The last spend tx is unconfirmed"
for tx in txs:
txid = bitcoind.rpc.decoderawtransaction(tx["tx"])["txid"]
txids.remove(txid) # This will raise an error if it isn't there
# We can limit the size of the result
txs = lianad.rpc.listconfirmed(initial_timestamp, final_timestamp, 5)[
"transactions"
]
assert len(txs) == 5
# We can restrict the query to a certain time window.
# First get the txid of all the transactions that happened during this timespan.
txids = set()
for coin in lianad.rpc.listcoins()["coins"]:
if coin["block_height"] is None:
continue
block_hash = bitcoind.rpc.getblockhash(coin["block_height"])
block_time = bitcoind.rpc.getblockheader(block_hash)["time"]
spend_time = None
if coin["spend_info"] is not None and coin["spend_info"]["height"] is not None:
spend_bhash = bitcoind.rpc.getblockhash(coin["spend_info"]["height"])
spend_time = bitcoind.rpc.getblockheader(spend_bhash)["time"]
if (block_time >= second_timestamp and block_time <= third_timestamp) or (
spend_time is not None
and spend_time >= second_timestamp
and spend_time <= third_timestamp
):
txids.add(coin["outpoint"][:-2])
# It's all 7 minus the first deposit and the last confirmed spend. So that's 5 of them.
assert len(txids) == 3
# Now let's compare with what lianad is giving us.
txs = lianad.rpc.listconfirmed(second_timestamp, third_timestamp, 10)[
"transactions"
]
assert len(txs) == 3
bit_txids = set(bitcoind.rpc.decoderawtransaction(tx["tx"])["txid"] for tx in txs)
assert bit_txids == txids