Page MenuHomePhabricator

No OneTemporary

diff --git a/test/functional/abc_feature_minerfund.py b/test/functional/abc_feature_minerfund.py
index 788c94a36..aed98c634 100644
--- a/test/functional/abc_feature_minerfund.py
+++ b/test/functional/abc_feature_minerfund.py
@@ -1,182 +1,182 @@
# Copyright (c) 2020 The Bitcoin developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from decimal import Decimal
from test_framework.blocktools import create_block, create_coinbase
from test_framework.cashaddr import decode
from test_framework.messages import XEC, CTxOut, ToHex
from test_framework.script import OP_EQUAL, OP_HASH160, CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.txtools import pad_tx
from test_framework.util import assert_equal, assert_greater_than_or_equal
MINER_FUND_RATIO = 32
MINER_FUND_ADDR = "ecregtest:prfhcnyqnl5cgrnmlfmms675w93ld7mvvq9jcw0zsn"
class MinerFundTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
- self.noban_tx_relay
+ self.noban_tx_relay = True
self.extra_args = [
[
"-enableminerfund",
],
[],
]
@staticmethod
def get_best_coinbase(n):
return n.getblock(n.getbestblockhash(), 2)["tx"][0]
def run_for_ratio(self, ratio):
node = self.nodes[0]
self.log.info("Create some history")
self.generate(node, 10)
coinbase = self.get_best_coinbase(node)
assert_greater_than_or_equal(len(coinbase["vout"]), 2)
block_reward = sum([vout["value"] for vout in coinbase["vout"]])
def check_miner_fund_output():
coinbase = self.get_best_coinbase(node)
assert_equal(len(coinbase["vout"]), 2)
assert_equal(
coinbase["vout"][1]["scriptPubKey"]["addresses"][0], MINER_FUND_ADDR
)
total = Decimal()
for o in coinbase["vout"]:
total += o["value"]
assert_equal(total, block_reward)
assert_greater_than_or_equal(
coinbase["vout"][1]["value"], (ratio * total) / 100
)
# The coinbase has an output to the miner fund address.
# Now we send part of the coinbase to the fund.
check_miner_fund_output()
def create_cb_pay_to_address():
_, _, script_hash = decode(MINER_FUND_ADDR)
miner_fund_amount = int(block_reward * XEC * ratio / 100)
# Build a coinbase with no miner fund
cb = create_coinbase(node.getblockcount() + 1)
# Keep only the block reward output
cb.vout = cb.vout[:1]
# Change the block reward to account for the miner fund
cb.vout[0].nValue = int(block_reward * XEC - miner_fund_amount)
# Add the miner fund output
cb.vout.append(
CTxOut(
nValue=miner_fund_amount,
scriptPubKey=CScript([OP_HASH160, script_hash, OP_EQUAL]),
)
)
pad_tx(cb)
cb.calc_sha256()
return cb
tip = node.getbestblockhash()
# Build a custom coinbase that spend to the new miner fund address
# and check it is accepted.
good_block = create_block(
int(tip, 16),
create_cb_pay_to_address(),
node.getblock(tip)["time"] + 1,
version=4,
)
good_block.solve()
node.submitblock(ToHex(good_block))
assert_equal(node.getbestblockhash(), good_block.hash)
# node0 mines a block with a coinbase output to the miner fund.
address = node.get_deterministic_priv_key().address
first_block_has_miner_fund = self.generatetoaddress(
node, nblocks=1, address=address
)[0]
check_miner_fund_output()
# Invalidate it
for n in self.nodes:
n.invalidateblock(first_block_has_miner_fund)
# node1 mines a block without a coinbase output to the miner fund.
with node.assert_debug_log(expected_msgs=["policy-bad-miner-fund"], timeout=10):
first_block_no_miner_fund = self.generatetoaddress(
self.nodes[1], nblocks=1, address=address, sync_fun=self.no_op
)[0]
coinbase = self.get_best_coinbase(self.nodes[1])
assert_equal(len(coinbase["vout"]), 1)
# node0 parks the block since the miner fund is enforced by policy.
def parked_block(blockhash):
for tip in node.getchaintips():
if tip["hash"] == blockhash:
assert tip["status"] != "active"
return tip["status"] == "parked"
return False
self.wait_until(lambda: parked_block(first_block_no_miner_fund))
# Unpark the block
node.unparkblock(first_block_no_miner_fund)
# Invalidate it
for n in self.nodes:
n.invalidateblock(first_block_no_miner_fund)
# Connecting the block again does not park because block policies are
# only checked the first time a block is connected.
for n in self.nodes:
n.reconsiderblock(first_block_no_miner_fund)
assert_equal(n.getbestblockhash(), first_block_no_miner_fund)
def test_without_avalanche(self):
self.log.info("Test the behavior when avalanche is completely disabled")
self.restart_node(0, extra_args=["-enableminerfund"])
self.restart_node(1, extra_args=["-avalanche=0"])
self.connect_nodes(0, 1)
avalanche_node = self.nodes[0]
other_node = self.nodes[1]
# First mine a block on an avalanche enabled node, check that the node with
# avalanche uninitialized accepts it
avalanche_tip = self.generate(avalanche_node, 1, sync_fun=self.sync_blocks)[0]
coinbase = self.get_best_coinbase(other_node)
assert_greater_than_or_equal(len(coinbase["vout"]), 2)
# Mine a block on a non-avalanche node, check that the avalanche node parks it
with avalanche_node.assert_debug_log(
["Park block because it violated a block policy: policy-bad-miner-fund"]
):
other_tip = self.generate(other_node, 1, sync_fun=self.no_op)[0]
coinbase = self.get_best_coinbase(other_node)
assert_equal(len(coinbase["vout"]), 1)
assert_equal(other_node.getblock(other_tip)["confirmations"], 1)
assert_equal(other_node.getbestblockhash(), other_tip)
assert_equal(avalanche_node.getblock(other_tip)["confirmations"], -1)
assert_equal(avalanche_node.getbestblockhash(), avalanche_tip)
def run_test(self):
self.run_for_ratio(MINER_FUND_RATIO)
self.test_without_avalanche()
if __name__ == "__main__":
MinerFundTest().main()
diff --git a/test/functional/abc_p2p_avalanche_contender_voting.py b/test/functional/abc_p2p_avalanche_contender_voting.py
index bf132ea8a..aa0d01968 100644
--- a/test/functional/abc_p2p_avalanche_contender_voting.py
+++ b/test/functional/abc_p2p_avalanche_contender_voting.py
@@ -1,235 +1,235 @@
# Copyright (c) 2024 The Bitcoin developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the resolution of stake contender preconsensus via avalanche."""
import time
from test_framework.avatools import can_find_inv_in_poll, get_ava_p2p_interface
from test_framework.key import ECPubKey
from test_framework.messages import (
MSG_AVA_STAKE_CONTENDER,
AvalancheContenderVoteError,
AvalancheVote,
hash256,
ser_uint256,
)
from test_framework.p2p import p2p_lock
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, uint256_hex
QUORUM_NODE_COUNT = 16
class AvalancheContenderVotingTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.noban_tx_relay = True
self.extra_args = [
[
"-avalanchestakingpreconsensus=1",
"-avalanchestakingrewards=1",
"-avaproofstakeutxodustthreshold=1000000",
"-avaproofstakeutxoconfirmations=1",
"-avacooldown=0",
"-avaminquorumstake=0",
"-avaminavaproofsnodecount=0",
- "-whitelist=noban@127.0.0.1",
"-persistavapeers=0",
],
]
self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
node = self.nodes[0]
# Set mock time so we can control when proofs will be considered for staking rewards
now = int(time.time())
node.setmocktime(now)
# Build a fake quorum of nodes.
def get_quorum():
def new_ava_interface(node):
# Generate a unique payout script for each proof so we can accurately test the stake winners
payoutAddress = node.getnewaddress()
peer = get_ava_p2p_interface(self, node, payoutAddress=payoutAddress)
# This test depends on each proof being added to the contender cache before
# the next block arrives, so we wait until that happens.
blockhash = node.getbestblockhash()
self.wait_until(
lambda: node.getstakecontendervote(
blockhash, uint256_hex(peer.proof.proofid)
)
== AvalancheContenderVoteError.PENDING
)
return peer
return [new_ava_interface(node) for _ in range(0, QUORUM_NODE_COUNT)]
# Pick one node from the quorum for polling.
quorum = get_quorum()
poll_node = quorum[0]
assert node.getavalancheinfo()["ready_to_poll"] is True
def has_finalized_proof(proofid):
can_find_inv_in_poll(quorum, proofid)
return node.getrawavalancheproof(uint256_hex(proofid))["finalized"]
for peer in quorum:
self.wait_until(lambda: has_finalized_proof(peer.proof.proofid))
# Get the key so we can verify signatures.
avakey = ECPubKey()
avakey.set(bytes.fromhex(node.getavalanchekey()))
def assert_response(expected):
response = poll_node.wait_for_avaresponse()
r = response.response
# Verify signature
assert avakey.verify_schnorr(response.sig, r.get_hash())
votes = r.votes
assert_equal(len(votes), len(expected))
for i in range(0, len(votes)):
assert_equal(repr(votes[i]), repr(expected[i]))
def make_contender_id(prevblockhash, proofid):
return int.from_bytes(
hash256(ser_uint256(int(prevblockhash, 16)) + ser_uint256(proofid)),
"little",
)
# Before finalizing any blocks, no contender promotion occurs in the cache,
# so the only way to test if the node knows about a particular contender is
# to check it at the block height that the proof was first seen at.
for i in range(0, QUORUM_NODE_COUNT):
# We started with a clean chain and a new block is mined when creating
# each quorum proof to make it valid, so the first block the proof was
# seen at is the quorum proof's index + 1.
blockhash = node.getblockhash(i + 1)
poll_ids = []
expected = []
for p in range(0, QUORUM_NODE_COUNT):
contender_proof = quorum[p].proof
contender_id = make_contender_id(blockhash, contender_proof.proofid)
poll_ids.append(contender_id)
# If the node knows about the contender, it will respond as INVALID
expected_vote = (
AvalancheContenderVoteError.PENDING
if p == i
else AvalancheContenderVoteError.UNKNOWN
)
expected.append(AvalancheVote(expected_vote, contender_id))
poll_node.send_poll(poll_ids, inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(expected)
# Unknown contender
unknown_contender_id = 0x123
poll_node.send_poll([unknown_contender_id], inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(
[AvalancheVote(AvalancheContenderVoteError.UNKNOWN, unknown_contender_id)]
)
def has_finalized_tip(tip_expected):
hash_tip_final = int(tip_expected, 16)
can_find_inv_in_poll(quorum, hash_tip_final)
return node.isfinalblock(tip_expected)
# Finalize a block so we promote the contender cache with every block
tip = node.getbestblockhash()
self.wait_until(lambda: has_finalized_tip(tip))
assert_equal(node.getbestblockhash(), tip)
# Now trigger building the whole cache for a block
tip = self.generate(node, 1)[0]
# Unknown contender is still unknown
poll_node.send_poll([unknown_contender_id], inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(
[AvalancheVote(AvalancheContenderVoteError.UNKNOWN, unknown_contender_id)]
)
def get_all_contender_ids(tip):
return [make_contender_id(tip, peer.proof.proofid) for peer in quorum]
# All contenders are pending. They cannot be winners yet since mock time
# has not advanced past the staking rewards minimum registration delay.
for contender_id in get_all_contender_ids(tip):
poll_node.send_poll([contender_id], inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(
[AvalancheVote(AvalancheContenderVoteError.PENDING, contender_id)]
)
# Advance time past the staking rewards minimum registration delay and
# mine a block.
now += 90 * 60 + 1
node.setmocktime(now)
tip = self.generate(node, 1)[0]
# Staking rewards has been computed. Check vote for all contenders.
contenders = get_all_contender_ids(tip)
local_winner_proofid = int(node.getstakingreward(tip)[0]["proofid"], 16)
local_winner_cid = make_contender_id(tip, local_winner_proofid)
poll_node.send_poll(contenders, inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(
[
AvalancheVote(
(
AvalancheContenderVoteError.ACCEPTED
if cid == local_winner_cid
else AvalancheContenderVoteError.INVALID
),
cid,
)
for cid in contenders
]
)
# Answer polls until contenders start polling
self.wait_until(lambda: can_find_inv_in_poll(quorum, local_winner_cid))
def get_polled_contenders():
# Pop a poll from any peer
def wait_for_poll():
self.wait_until(lambda: any(len(peer.avapolls) > 0 for peer in quorum))
with p2p_lock:
for peer in quorum:
if len(peer.avapolls) > 0:
return peer.avapolls.pop(0)
return None
poll = wait_for_poll()
assert poll is not None
return [
inv.hash for inv in poll.invs if inv.type == MSG_AVA_STAKE_CONTENDER
]
# Check that the local winner was polled
polled_contenders = get_polled_contenders()
assert local_winner_cid in polled_contenders
# Check that the max number of contenders were polled
assert_equal(len(polled_contenders), 12)
# Manually set a winner that isn't the local winner
manual_winner = (
quorum[0].proof
if local_winner_proofid != quorum[0].proof.proofid
else quorum[1].proof
)
manual_winner_cid = make_contender_id(tip, manual_winner.proofid)
node.setstakingreward(tip, manual_winner.payout_script.hex())
poll_node.send_poll([manual_winner_cid], inv_type=MSG_AVA_STAKE_CONTENDER)
assert_response(
[AvalancheVote(AvalancheContenderVoteError.ACCEPTED, manual_winner_cid)]
)
if __name__ == "__main__":
AvalancheContenderVotingTest().main()

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 20:39 (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865904
Default Alt Text
(16 KB)

Event Timeline