Skip to content

Commit

Permalink
tests: Add CPFP tests
Browse files Browse the repository at this point in the history
  • Loading branch information
danielabrozzoni committed Nov 3, 2021
1 parent b0f1f79 commit 0a38f9a
Showing 1 changed file with 151 additions and 0 deletions.
151 changes: 151 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from bitcoin.core import COIN
from fixtures import *
from test_framework import serializations
from test_framework.utils import (
TailableProc,
POSTGRES_IS_SETUP,
Expand Down Expand Up @@ -225,3 +226,153 @@ def test_no_cosig_server(revault_network):
rn.spend_vaults_anyhow(vaults[:2])
rn.unvault_vaults_anyhow([vaults[-1]])
rn.cancel_vault(vaults[-1])


@pytest.mark.skipif(not POSTGRES_IS_SETUP, reason="Needs Postgres for servers db")
def test_cpfp_transaction(revault_network, bitcoind):
CSV = 12
revault_network.deploy(2, 1, n_stkmanagers=1, csv=CSV)
man = revault_network.mans()[1]
stks = revault_network.stks()
amount = 0.24
vault = revault_network.fund(amount)
deposit = f"{vault['txid']}:{vault['vout']}"

revault_network.secure_vault(vault)
revault_network.activate_vault(vault)
_, spend_psbt = revault_network.unvault_vaults_anyhow_unconfirmed(
[vault], priority=True
)

unvault_psbt = serializations.PSBT()
unvault_b64 = stks[0].rpc.getunvaulttx(deposit)["unvault_tx"]
unvault_psbt.deserialize(unvault_b64)
unvault_psbt.tx.calc_sha256()
unvault_txid = unvault_psbt.tx.hash
spend_txid = spend_psbt.tx.hash

# Let's test that the revaultd poller behaves correctly
# Case 1: the unvault is broadcasted and inserted in the CPFP table
for w in revault_network.participants():
wait_for(
lambda: len(w.rpc.listvaults(["unvaulting"], [deposit])["vaults"]) == 1,
)
man.wait_for_log(
f"Inserted freshly-broadcasted Unvault transaction {unvault_txid} in CPFPable table",
)

# Uh oh! The feerate is too low, miners aren't including our transaction...
# 1 block is fine, maybe miners didn't see the tx...
bitcoind.generate_blocks_censor(1, [unvault_txid])
man.wait_for_log("Checking if transactions need CPFP...")
assert bitcoind.rpc.getmempoolentry(unvault_txid)["descendantcount"] == 1

# 2 blocks and yet not included? We start being aggressive.
bitcoind.generate_blocks_censor(1, [unvault_txid])
man.wait_for_log(
f"CPFPed transaction with id '{unvault_txid}'",
)
assert bitcoind.rpc.getmempoolentry(unvault_txid)["descendantcount"] == 2

# Case 2: the unvault is confirmed and removed from the CPFP table
bitcoind.generate_block(1)
for w in revault_network.participants():
wait_for(
lambda: len(w.rpc.listvaults(["unvaulted"], [deposit])["vaults"]) == 1,
)
man.wait_for_log(
f"Removed Unvault transaction {unvault_txid} from CPFPable table as it's confirmed",
)

# Case 3: the unvault got unconfirmed, insert it again in the CPFP table
bitcoind.simple_reorg(bitcoind.rpc.getblockcount() - 1, -1)
for w in revault_network.participants():
w.wait_for_log("Detected reorg")
wait_for(
lambda: len(w.rpc.listvaults(["unvaulting"], [deposit])["vaults"]) == 1,
)
man.wait_for_log(
f"Inserted Unvault transaction {unvault_txid} in CPFPable table as it got unconfirmed",
)

# TODO: Case 4: the unvault got evicted from the mempool, remove it from the CPFP table
# This is not implemented yet

# Alright, now let's do everything again for the spend :tada:

# Confirming the unvault
bitcoind.generate_block(1)
for w in revault_network.participants():
wait_for(
lambda: len(w.rpc.listvaults(["unvaulted"], [deposit])["vaults"]) == 1,
)

bitcoind.generate_block(CSV - 1)
man.wait_for_logs(
[
f"Succesfully broadcasted Spend tx '{spend_txid}'",
f"Inserted freshly-broadcasted Spend transaction {spend_txid} in CPFPable table",
]
)

# Uh oh! The feerate is too low, miners aren't including our transaction...
# 1 block is fine, maybe miners didn't see the tx...
bitcoind.generate_blocks_censor(1, [spend_txid])
assert bitcoind.rpc.getmempoolentry(spend_txid)["descendantcount"] == 1

# 2 blocks and yet not included? We start being aggressive.
bitcoind.generate_blocks_censor(1, [spend_txid])
man.wait_for_log(
f"CPFPed transaction with id '{spend_txid}'",
)
assert bitcoind.rpc.getmempoolentry(spend_txid)["descendantcount"] == 2

# Case 2: the spend is confirmed and removed from the CPFP table
bitcoind.generate_block(1)
for w in revault_network.participants():
wait_for(
lambda: len(w.rpc.listvaults(["spent"], [deposit])["vaults"]) == 1,
)
man.wait_for_log(
f"Removed Spend transaction {spend_txid} from CPFPable table as it's confirmed",
)

# Case 3: the spend got unconfirmed, insert it again in the CPFP table
bitcoind.simple_reorg(bitcoind.rpc.getblockcount() - 1, -1)
for w in revault_network.participants():
w.wait_for_log("Detected reorg")
wait_for(
lambda: len(w.rpc.listvaults(["spending"], [deposit])["vaults"]) == 1,
)
man.wait_for_log(
f"Inserted Spend transaction {spend_txid} in CPFPable table as it got unconfirmed",
)

# TODO: Case 4: the spend got evicted from the mempool, remove it from the CPFP table
# This case is (hopefully <3) handled in the code, it just needs to be tested

# Let's test that non priority txs don't get cpfped
amount = 0.24
vault = revault_network.fund(amount)
deposit = f"{vault['txid']}:{vault['vout']}"

revault_network.secure_vault(vault)
revault_network.activate_vault(vault)
spend_psbt = revault_network.unvault_vaults_anyhow([vault], priority=False)
spend_txid = spend_psbt.tx.hash

bitcoind.generate_block(CSV - 1)
man.wait_for_log(
f"Succesfully broadcasted Spend tx '{spend_txid}'",
)

# Uh oh! The feerate is too low, miners aren't including our transaction...
# 1 block is fine, maybe miners didn't see the tx...
bitcoind.generate_blocks_censor(1, [spend_txid])
assert bitcoind.rpc.getmempoolentry(spend_txid)["descendantcount"] == 1

# 2 blocks and yet not included? We are still calm, this tx has no priority...
bitcoind.generate_blocks_censor(1, [spend_txid])
man.wait_for_log("Checking if transactions need CPFP...")
# Nah, they don't
assert bitcoind.rpc.getmempoolentry(spend_txid)["descendantcount"] == 1

0 comments on commit 0a38f9a

Please sign in to comment.