Skip to content

Commit 7568bf5

Browse files
committed
chore: bugfixes and SIP-029 integration test
1 parent 63617e9 commit 7568bf5

File tree

6 files changed

+249
-14
lines changed

6 files changed

+249
-14
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ jobs:
140140
- tests::nakamoto_integrations::utxo_check_on_startup_recover
141141
- tests::nakamoto_integrations::v3_signer_api_endpoint
142142
- tests::nakamoto_integrations::signer_chainstate
143+
- tests::nakamoto_integrations::sip029_coinbase_change
143144
# TODO: enable these once v1 signer is supported by a new nakamoto epoch
144145
# - tests::signer::v1::dkg
145146
# - tests::signer::v1::sign_request_rejected

stacks-common/src/types/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ impl StacksEpochId {
459459
match COINBASE_INTERVALS_TEST.lock() {
460460
Ok(schedule_opt) => {
461461
if let Some(schedule) = (*schedule_opt).as_ref() {
462+
info!("Use overridden coinbase schedule {:?}", &schedule);
462463
return schedule.clone();
463464
}
464465
}
@@ -520,11 +521,18 @@ impl StacksEpochId {
520521
| StacksEpochId::Epoch30 => {
521522
self.coinbase_reward_pre_sip029(first_burnchain_height, current_burnchain_height)
522523
}
523-
StacksEpochId::Epoch31 => self.coinbase_reward_sip029(
524-
mainnet,
525-
first_burnchain_height,
526-
current_burnchain_height,
527-
),
524+
StacksEpochId::Epoch31 => {
525+
let cb = self.coinbase_reward_sip029(
526+
mainnet,
527+
first_burnchain_height,
528+
current_burnchain_height,
529+
);
530+
info!(
531+
"Epoch31 coinbase at ({},{}) is {}",
532+
first_burnchain_height, current_burnchain_height, cb
533+
);
534+
cb
535+
}
528536
}
529537
}
530538
}

stackslib/src/chainstate/stacks/db/blocks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3618,7 +3618,7 @@ impl StacksChainState {
36183618
burn_block_height: u64,
36193619
first_burn_block_height: u64,
36203620
) -> u128 {
3621-
epoch.coinbase_reward(mainnet, burn_block_height, first_burn_block_height)
3621+
epoch.coinbase_reward(mainnet, first_burn_block_height, burn_block_height)
36223622
}
36233623

36243624
/// Create the block reward.

testnet/stacks-node/src/nakamoto_node/relayer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use stacks::chainstate::stacks::miner::{
3838
get_mining_spend_amount, signal_mining_blocked, signal_mining_ready,
3939
};
4040
use stacks::core::mempool::MemPoolDB;
41-
use stacks::core::STACKS_EPOCH_3_0_MARKER;
41+
use stacks::core::STACKS_EPOCH_3_1_MARKER;
4242
use stacks::monitoring::increment_stx_blocks_mined_counter;
4343
use stacks::net::db::LocalPeer;
4444
use stacks::net::p2p::NetworkHandle;
@@ -689,7 +689,7 @@ impl RelayerThread {
689689
key_block_ptr: u32::try_from(key.block_height)
690690
.expect("FATAL: burn block height exceeded u32"),
691691
key_vtxindex: u16::try_from(key.op_vtxindex).expect("FATAL: vtxindex exceeded u16"),
692-
memo: vec![STACKS_EPOCH_3_0_MARKER],
692+
memo: vec![STACKS_EPOCH_3_1_MARKER],
693693
new_seed: VRFSeed::from_proof(&tip_vrf_proof),
694694
parent_block_ptr: u32::try_from(commit_parent_block_burn_height)
695695
.expect("FATAL: burn block height exceeded u32"),

testnet/stacks-node/src/tests/epoch_25.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use stacks_common::types::chainstate::StacksPrivateKey;
2323

2424
use crate::config::InitialBalance;
2525
use crate::tests::bitcoin_regtest::BitcoinCoreController;
26-
use crate::tests::nakamoto_integrations::{next_block_and, wait_for};
26+
use crate::tests::nakamoto_integrations::wait_for;
2727
use crate::tests::neon_integrations::{
2828
get_account, get_chain_info, neon_integration_test_conf, next_block_and_wait, submit_tx,
2929
test_observer, wait_for_runloop,

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 231 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use http_types::headers::AUTHORIZATION;
2929
use lazy_static::lazy_static;
3030
use libsigner::v0::messages::SignerMessage as SignerMessageV0;
3131
use libsigner::{SignerSession, StackerDBSession};
32+
use rusqlite::OptionalExtension;
3233
use stacks::burnchains::{MagicBytes, Txid};
3334
use stacks::chainstate::burn::db::sortdb::SortitionDB;
3435
use stacks::chainstate::burn::operations::{
@@ -60,7 +61,7 @@ use stacks::core::{
6061
StacksEpoch, StacksEpochId, BLOCK_LIMIT_MAINNET_10, HELIUM_BLOCK_LIMIT_20,
6162
PEER_VERSION_EPOCH_1_0, PEER_VERSION_EPOCH_2_0, PEER_VERSION_EPOCH_2_05,
6263
PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4,
63-
PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, PEER_VERSION_TESTNET,
64+
PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0, PEER_VERSION_EPOCH_3_1, PEER_VERSION_TESTNET,
6465
};
6566
use stacks::libstackerdb::SlotMetadata;
6667
use stacks::net::api::callreadonly::CallReadOnlyRequestBody;
@@ -70,7 +71,7 @@ use stacks::net::api::getstackers::GetStackersResponse;
7071
use stacks::net::api::postblock_proposal::{
7172
BlockValidateReject, BlockValidateResponse, NakamotoBlockProposal, ValidateRejectCode,
7273
};
73-
use stacks::types::chainstate::StacksBlockId;
74+
use stacks::types::chainstate::{ConsensusHash, StacksBlockId};
7475
use stacks::util::hash::hex_bytes;
7576
use stacks::util_lib::boot::boot_code_id;
7677
use stacks::util_lib::signed_structured_data::pox4::{
@@ -84,7 +85,7 @@ use stacks_common::types::chainstate::{
8485
BlockHeaderHash, BurnchainHeaderHash, StacksAddress, StacksPrivateKey, StacksPublicKey,
8586
TrieHash,
8687
};
87-
use stacks_common::types::StacksPublicKeyBuffer;
88+
use stacks_common::types::{set_test_coinbase_schedule, CoinbaseInterval, StacksPublicKeyBuffer};
8889
use stacks_common::util::hash::{to_hex, Hash160, Sha512Trunc256Sum};
8990
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PrivateKey, Secp256k1PublicKey};
9091
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
@@ -114,7 +115,7 @@ pub static POX_4_DEFAULT_STACKER_BALANCE: u64 = 100_000_000_000_000;
114115
pub static POX_4_DEFAULT_STACKER_STX_AMT: u128 = 99_000_000_000_000;
115116

116117
lazy_static! {
117-
pub static ref NAKAMOTO_INTEGRATION_EPOCHS: [StacksEpoch; 9] = [
118+
pub static ref NAKAMOTO_INTEGRATION_EPOCHS: [StacksEpoch; 10] = [
118119
StacksEpoch {
119120
epoch_id: StacksEpochId::Epoch10,
120121
start_height: 0,
@@ -174,10 +175,17 @@ lazy_static! {
174175
StacksEpoch {
175176
epoch_id: StacksEpochId::Epoch30,
176177
start_height: 231,
177-
end_height: STACKS_EPOCH_MAX,
178+
end_height: 241,
178179
block_limit: HELIUM_BLOCK_LIMIT_20.clone(),
179180
network_epoch: PEER_VERSION_EPOCH_3_0
180181
},
182+
StacksEpoch {
183+
epoch_id: StacksEpochId::Epoch31,
184+
start_height: 241,
185+
end_height: STACKS_EPOCH_MAX,
186+
block_limit: HELIUM_BLOCK_LIMIT_20.clone(),
187+
network_epoch: PEER_VERSION_EPOCH_3_1
188+
},
181189
];
182190
}
183191

@@ -9500,3 +9508,221 @@ fn skip_mining_long_tx() {
95009508

95019509
run_loop_thread.join().unwrap();
95029510
}
9511+
9512+
#[test]
9513+
#[ignore]
9514+
/// Integration test for SIP-029
9515+
fn sip029_coinbase_change() {
9516+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
9517+
return;
9518+
}
9519+
9520+
let new_sched = vec![
9521+
CoinbaseInterval {
9522+
coinbase: 1_000_000_000,
9523+
effective_start_height: 0,
9524+
},
9525+
// NOTE: epoch 3.1 goes into effect at 241
9526+
CoinbaseInterval {
9527+
coinbase: 500_000_000,
9528+
effective_start_height: 245,
9529+
},
9530+
CoinbaseInterval {
9531+
coinbase: 125_000_000,
9532+
effective_start_height: 255,
9533+
},
9534+
CoinbaseInterval {
9535+
coinbase: 62_500_000,
9536+
effective_start_height: 265,
9537+
},
9538+
];
9539+
9540+
set_test_coinbase_schedule(Some(new_sched.clone()));
9541+
9542+
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
9543+
naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1);
9544+
naka_conf.node.pox_sync_sample_secs = 180;
9545+
naka_conf.burnchain.max_rbf = 10_000_000;
9546+
9547+
let sender_sk = Secp256k1PrivateKey::new();
9548+
let sender_signer_sk = Secp256k1PrivateKey::new();
9549+
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
9550+
let mut signers = TestSigners::new(vec![sender_signer_sk]);
9551+
let tenure_count = 5;
9552+
let inter_blocks_per_tenure = 9;
9553+
// setup sender + recipient for some test stx transfers
9554+
// these are necessary for the interim blocks to get mined at all
9555+
let sender_addr = tests::to_addr(&sender_sk);
9556+
let send_amt = 100;
9557+
let send_fee = 180;
9558+
naka_conf.add_initial_balance(
9559+
PrincipalData::from(sender_addr).to_string(),
9560+
(send_amt + send_fee) * tenure_count * inter_blocks_per_tenure,
9561+
);
9562+
naka_conf.add_initial_balance(PrincipalData::from(sender_signer_addr).to_string(), 100000);
9563+
let stacker_sk = setup_stacker(&mut naka_conf);
9564+
9565+
test_observer::spawn();
9566+
test_observer::register_any(&mut naka_conf);
9567+
9568+
let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
9569+
btcd_controller
9570+
.start_bitcoind()
9571+
.expect("Failed starting bitcoind");
9572+
let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None);
9573+
btc_regtest_controller.bootstrap_chain(201);
9574+
9575+
let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap();
9576+
let run_loop_stopper = run_loop.get_termination_switch();
9577+
let Counters {
9578+
blocks_processed,
9579+
naka_submitted_commits: commits_submitted,
9580+
naka_proposed_blocks: proposals_submitted,
9581+
..
9582+
} = run_loop.counters();
9583+
9584+
let coord_channel = run_loop.coordinator_channels();
9585+
9586+
let run_loop_thread = thread::Builder::new()
9587+
.name("run_loop".into())
9588+
.spawn(move || run_loop.start(None, 0))
9589+
.unwrap();
9590+
wait_for_runloop(&blocks_processed);
9591+
boot_to_epoch_3(
9592+
&naka_conf,
9593+
&blocks_processed,
9594+
&[stacker_sk],
9595+
&[sender_signer_sk],
9596+
&mut Some(&mut signers),
9597+
&mut btc_regtest_controller,
9598+
);
9599+
9600+
info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");
9601+
9602+
let burnchain = naka_conf.get_burnchain();
9603+
let sortdb = burnchain.open_sortition_db(true).unwrap();
9604+
let (chainstate, _) = StacksChainState::open(
9605+
naka_conf.is_mainnet(),
9606+
naka_conf.burnchain.chain_id,
9607+
&naka_conf.get_chainstate_path_str(),
9608+
None,
9609+
)
9610+
.unwrap();
9611+
9612+
info!("Nakamoto miner started...");
9613+
blind_signer(&naka_conf, &signers, proposals_submitted);
9614+
9615+
wait_for_first_naka_block_commit(60, &commits_submitted);
9616+
9617+
// mine until burnchain height 270
9618+
loop {
9619+
let commits_before = commits_submitted.load(Ordering::SeqCst);
9620+
next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
9621+
.unwrap();
9622+
wait_for(20, || {
9623+
Ok(commits_submitted.load(Ordering::SeqCst) > commits_before)
9624+
})
9625+
.unwrap();
9626+
9627+
let node_info = get_chain_info_opt(&naka_conf).unwrap();
9628+
if node_info.burn_block_height >= 270 {
9629+
break;
9630+
}
9631+
}
9632+
9633+
info!("Nakamoto miner has advanced to burn height 270");
9634+
9635+
// inspect `payments` table to see that coinbase was applied
9636+
let all_snapshots = sortdb.get_all_snapshots().unwrap();
9637+
9638+
// whether or not the last snapshot had a sortition
9639+
let mut prev_sortition = false;
9640+
9641+
// whether or not we witnessed the requisite coinbases
9642+
let mut witnessed_1000 = false;
9643+
let mut witnessed_500 = false;
9644+
let mut witnessed_125 = false;
9645+
let mut witnessed_62_5 = false;
9646+
9647+
// initial mining bonus
9648+
let initial_mining_bonus = 20400000;
9649+
9650+
for sn in all_snapshots {
9651+
if !sn.sortition {
9652+
prev_sortition = false;
9653+
continue;
9654+
}
9655+
if sn.consensus_hash == ConsensusHash([0x00; 20]) {
9656+
continue;
9657+
}
9658+
let coinbase = {
9659+
let sql = "SELECT coinbase FROM payments WHERE consensus_hash = ?1";
9660+
let args = rusqlite::params![&sn.consensus_hash];
9661+
let Some(coinbase) = chainstate
9662+
.db()
9663+
.query_row(sql, args, |r| {
9664+
let coinbase_txt: String = r.get_unwrap(0);
9665+
let coinbase: u64 = coinbase_txt.parse().unwrap();
9666+
Ok(coinbase)
9667+
})
9668+
.optional()
9669+
.unwrap()
9670+
else {
9671+
info!("No coinbase for {} {}", sn.block_height, &sn.consensus_hash);
9672+
continue;
9673+
};
9674+
9675+
coinbase
9676+
};
9677+
9678+
info!(
9679+
"Coinbase at {} {}: {}",
9680+
sn.block_height, &sn.consensus_hash, coinbase
9681+
);
9682+
// use >= for coinbases since a missed sortition can lead to coinbase accumulation
9683+
if sn.block_height < 245 {
9684+
if prev_sortition {
9685+
assert_eq!(coinbase, 1_000_000_000 + initial_mining_bonus);
9686+
witnessed_1000 = true;
9687+
} else {
9688+
assert!(coinbase >= 1_000_000_000 + initial_mining_bonus);
9689+
}
9690+
} else if sn.block_height < 255 {
9691+
if prev_sortition {
9692+
assert_eq!(coinbase, 500_000_000 + initial_mining_bonus);
9693+
witnessed_500 = true;
9694+
} else {
9695+
assert!(coinbase >= 500_000_000 + initial_mining_bonus);
9696+
}
9697+
} else if sn.block_height < 265 {
9698+
if prev_sortition {
9699+
assert_eq!(coinbase, 125_000_000 + initial_mining_bonus);
9700+
witnessed_125 = true;
9701+
} else {
9702+
assert!(coinbase >= 125_000_000 + initial_mining_bonus);
9703+
}
9704+
} else {
9705+
if prev_sortition {
9706+
assert_eq!(coinbase, 62_500_000 + initial_mining_bonus);
9707+
witnessed_62_5 = true;
9708+
} else {
9709+
assert!(coinbase >= 62_500_000 + initial_mining_bonus);
9710+
}
9711+
}
9712+
9713+
prev_sortition = true;
9714+
}
9715+
9716+
assert!(witnessed_1000);
9717+
assert!(witnessed_500);
9718+
assert!(witnessed_125);
9719+
assert!(witnessed_62_5);
9720+
9721+
coord_channel
9722+
.lock()
9723+
.expect("Mutex poisoned")
9724+
.stop_chains_coordinator();
9725+
run_loop_stopper.store(false, Ordering::SeqCst);
9726+
9727+
run_loop_thread.join().unwrap();
9728+
}

0 commit comments

Comments
 (0)