Skip to content

Commit

Permalink
test: add test for block-reward property
Browse files Browse the repository at this point in the history
This is separated out as a "slow" test because it requires mining 100
blocks to wait for the rewards to mature.
  • Loading branch information
obycode committed Jun 17, 2024
1 parent b4d88b7 commit 1fbbdf1
Show file tree
Hide file tree
Showing 2 changed files with 345 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/slow-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
test-name:
- tests::epoch_21::test_pox_reorg_flap_duel
- tests::epoch_21::test_pox_reorg_flap_reward_cycles
- tests::nakamoto_integrations::check_block_info_rewards
steps:
## Setup test environment
- name: Setup Test Environment
Expand Down
344 changes: 344 additions & 0 deletions testnet/stacks-node/src/tests/nakamoto_integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5956,3 +5956,347 @@ fn check_block_info() {

run_loop_thread.join().unwrap();
}

#[test]
#[ignore]
/// Verify `block-reward` property in `get-block-info?` and `get-tenure-info?`.
/// This test is separated from `check_block_info` above because it needs to
/// mine 100+ blocks to mature the block reward, so it is slow.
fn check_block_info_rewards() {
if env::var("BITCOIND_TEST") != Ok("1".into()) {
return;
}

let mut signers = TestSigners::default();
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
let http_origin = format!("http://{}", &naka_conf.node.rpc_bind);
naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1);
let sender_sk = Secp256k1PrivateKey::new();
let sender_signer_sk = Secp256k1PrivateKey::new();
let sender_signer_addr = tests::to_addr(&sender_signer_sk);

// setup sender + recipient for some test stx transfers
// these are necessary for the interim blocks to get mined at all
let sender_addr = tests::to_addr(&sender_sk);
let send_amt = 100;
let send_fee = 180;
let deploy_fee = 3000;
naka_conf.add_initial_balance(
PrincipalData::from(sender_addr.clone()).to_string(),
3 * deploy_fee + (send_amt + send_fee) * 2,
);
naka_conf.add_initial_balance(
PrincipalData::from(sender_signer_addr.clone()).to_string(),
100000,
);
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
let stacker_sk = setup_stacker(&mut naka_conf);

test_observer::spawn();
let observer_port = test_observer::EVENT_OBSERVER_PORT;
naka_conf.events_observers.insert(EventObserverConfig {
endpoint: format!("localhost:{observer_port}"),
events_keys: vec![EventKeyType::AnyEvent],
});

let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
btcd_controller
.start_bitcoind()
.expect("Failed starting bitcoind");
let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None);
btc_regtest_controller.bootstrap_chain(201);

let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap();
let run_loop_stopper = run_loop.get_termination_switch();
let Counters {
blocks_processed,
naka_submitted_vrfs: vrfs_submitted,
naka_submitted_commits: commits_submitted,
naka_proposed_blocks: proposals_submitted,
..
} = run_loop.counters();

let coord_channel = run_loop.coordinator_channels();

let run_loop_thread = thread::Builder::new()
.name("run_loop".into())
.spawn(move || run_loop.start(None, 0))
.unwrap();
wait_for_runloop(&blocks_processed);

let mut sender_nonce = 0;

// Deploy this version with the Clarity 1 / 2 before epoch 3
let contract0_name = "test-contract-0";
let contract_clarity1 = "(define-read-only (get-info (height uint))
{
burnchain-header-hash: (get-block-info? burnchain-header-hash height),
id-header-hash: (get-block-info? id-header-hash height),
header-hash: (get-block-info? header-hash height),
miner-address: (get-block-info? miner-address height),
time: (get-block-info? time height),
vrf-seed: (get-block-info? vrf-seed height),
block-reward: (get-block-info? block-reward height),
miner-spend-total: (get-block-info? miner-spend-total height),
miner-spend-winner: (get-block-info? miner-spend-winner height),
}
)";

let contract_tx0 = make_contract_publish(
&sender_sk,
sender_nonce,
deploy_fee,
contract0_name,
contract_clarity1,
);
sender_nonce += 1;
submit_tx(&http_origin, &contract_tx0);

boot_to_epoch_3(
&naka_conf,
&blocks_processed,
&[stacker_sk],
&[sender_signer_sk],
&mut Some(&mut signers),
&mut btc_regtest_controller,
);

info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");

info!("Nakamoto miner started...");
blind_signer(&naka_conf, &signers, proposals_submitted);

let result0 = call_read_only(
&naka_conf,
&sender_addr,
contract0_name,
"get-info",
vec![&clarity::vm::Value::UInt(1)],
);
let tuple0 = result0.expect_tuple().unwrap().data_map;
info!("Info from pre-epoch 3.0: {:?}", tuple0);

// first block wakes up the run loop, wait until a key registration has been submitted.
next_block_and(&mut btc_regtest_controller, 60, || {
let vrf_count = vrfs_submitted.load(Ordering::SeqCst);
Ok(vrf_count >= 1)
})
.unwrap();

// second block should confirm the VRF register, wait until a block commit is submitted
next_block_and(&mut btc_regtest_controller, 60, || {
let commits_count = commits_submitted.load(Ordering::SeqCst);
Ok(commits_count >= 1)
})
.unwrap();

// This version uses the Clarity 1 / 2 function
let contract1_name = "test-contract-1";
let contract_tx1 = make_contract_publish_versioned(
&sender_sk,
sender_nonce,
deploy_fee,
contract1_name,
contract_clarity1,
Some(ClarityVersion::Clarity2),
);
sender_nonce += 1;
submit_tx(&http_origin, &contract_tx1);

// This version uses the Clarity 3 functions
let contract3_name = "test-contract-3";
let contract_clarity3 = "(define-read-only (get-tenure-info (height uint))
{
burnchain-header-hash: (get-tenure-info? burnchain-header-hash height),
miner-address: (get-tenure-info? miner-address height),
time: (get-tenure-info? time height),
vrf-seed: (get-tenure-info? vrf-seed height),
block-reward: (get-tenure-info? block-reward height),
miner-spend-total: (get-tenure-info? miner-spend-total height),
miner-spend-winner: (get-tenure-info? miner-spend-winner height),
}
)";

let contract_tx3 = make_contract_publish(
&sender_sk,
sender_nonce,
deploy_fee,
contract3_name,
contract_clarity3,
);
sender_nonce += 1;
submit_tx(&http_origin, &contract_tx3);

next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
.unwrap();

// Sleep to ensure the seconds have changed
thread::sleep(Duration::from_secs(1));

// Mine a Nakamoto block
info!("Mining Nakamoto block");
let blocks_processed_before = coord_channel
.lock()
.expect("Mutex poisoned")
.get_stacks_blocks_processed();

// submit a tx so that the miner will mine an extra block
let transfer_tx =
make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt);
sender_nonce += 1;
submit_tx(&http_origin, &transfer_tx);

loop {
let blocks_processed = coord_channel
.lock()
.expect("Mutex poisoned")
.get_stacks_blocks_processed();
if blocks_processed > blocks_processed_before {
break;
}
thread::sleep(Duration::from_millis(100));
}

// Sleep to ensure the seconds have changed
thread::sleep(Duration::from_secs(1));

// Mine a Nakamoto block
info!("Mining Nakamoto block");
let blocks_processed_before = coord_channel
.lock()
.expect("Mutex poisoned")
.get_stacks_blocks_processed();

// submit a tx so that the miner will mine an extra block
let transfer_tx =
make_stacks_transfer(&sender_sk, sender_nonce, send_fee, &recipient, send_amt);
submit_tx(&http_origin, &transfer_tx);

loop {
let blocks_processed = coord_channel
.lock()
.expect("Mutex poisoned")
.get_stacks_blocks_processed();
if blocks_processed > blocks_processed_before {
break;
}
thread::sleep(Duration::from_millis(100));
}

let info = get_chain_info_result(&naka_conf).unwrap();
info!("Chain info: {:?}", info);
let last_stacks_block_height = info.stacks_tip_height as u128;

// Mining 100 blocks takes a while, so only run this test if the env var is set
let last_nakamoto_block = last_stacks_block_height;

// Mine 100+ burn blocks to get the block reward matured
info!("Mining 102 tenures to mature the block reward");
for i in 0..102 {
let commits_before = commits_submitted.load(Ordering::SeqCst);
next_block_and(&mut btc_regtest_controller, 60, || {
let commits_count = commits_submitted.load(Ordering::SeqCst);
Ok(commits_count >= commits_before + 1)
})
.unwrap();
info!("Mined a block ({i})");
}

let info = get_chain_info_result(&naka_conf).unwrap();
info!("Chain info: {:?}", info);
let last_stacks_block_height = info.stacks_tip_height as u128;

// Check the block reward is now matured in one of the tenure-change blocks
let result0 = call_read_only(
&naka_conf,
&sender_addr,
contract0_name,
"get-info",
vec![&clarity::vm::Value::UInt(last_stacks_block_height - 100)],
);
let tuple0 = result0.expect_tuple().unwrap().data_map;
assert_eq!(
tuple0
.get("block-reward")
.unwrap()
.clone()
.expect_optional()
.unwrap()
.unwrap(),
Value::UInt(2040806360)
);

let result1 = call_read_only(
&naka_conf,
&sender_addr,
contract1_name,
"get-info",
vec![&clarity::vm::Value::UInt(last_stacks_block_height - 100)],
);
let tuple1 = result1.expect_tuple().unwrap().data_map;
assert_eq!(tuple0, tuple1);

let result3_tenure = call_read_only(
&naka_conf,
&sender_addr,
contract3_name,
"get-tenure-info",
vec![&clarity::vm::Value::UInt(last_stacks_block_height - 100)],
);
let tuple3_tenure = result3_tenure.expect_tuple().unwrap().data_map;
assert_eq!(
tuple3_tenure.get("block-reward"),
tuple0.get("block-reward")
);

// Check the block reward is now matured in one of the Nakamoto blocks
let result0 = call_read_only(
&naka_conf,
&sender_addr,
contract0_name,
"get-info",
vec![&clarity::vm::Value::UInt(last_nakamoto_block)],
);
let tuple0 = result0.expect_tuple().unwrap().data_map;
assert_eq!(
tuple0
.get("block-reward")
.unwrap()
.clone()
.expect_optional()
.unwrap()
.unwrap(),
Value::UInt(3061200000)
);

let result1 = call_read_only(
&naka_conf,
&sender_addr,
contract1_name,
"get-info",
vec![&clarity::vm::Value::UInt(last_nakamoto_block)],
);
let tuple1 = result1.expect_tuple().unwrap().data_map;
assert_eq!(tuple0, tuple1);

let result3_tenure = call_read_only(
&naka_conf,
&sender_addr,
contract3_name,
"get-tenure-info",
vec![&clarity::vm::Value::UInt(last_nakamoto_block)],
);
let tuple3_tenure = result3_tenure.expect_tuple().unwrap().data_map;
assert_eq!(
tuple3_tenure.get("block-reward"),
tuple0.get("block-reward")
);

coord_channel
.lock()
.expect("Mutex poisoned")
.stop_chains_coordinator();
run_loop_stopper.store(false, Ordering::SeqCst);

run_loop_thread.join().unwrap();
}

0 comments on commit 1fbbdf1

Please sign in to comment.