Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864582
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment