mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-06-18 11:59:24 +08:00
chore: expand test coverage for inventory generation using the new declarative nakamoto chain history test framework
This commit is contained in:
@@ -20,8 +20,9 @@ use std::sync::mpsc::sync_channel;
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use stacks_common::address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG};
|
||||
use stacks_common::codec::{read_next, StacksMessageCodec};
|
||||
use stacks_common::types::chainstate::StacksPrivateKey;
|
||||
use stacks_common::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPublicKey};
|
||||
use stacks_common::types::StacksEpoch;
|
||||
|
||||
use crate::chainstate::burn::db::sortdb::SortitionDB;
|
||||
@@ -33,20 +34,27 @@ use crate::chainstate::nakamoto::coordinator::tests::{
|
||||
};
|
||||
use crate::chainstate::nakamoto::NakamotoChainState;
|
||||
use crate::chainstate::stacks::db::StacksChainState;
|
||||
use crate::chainstate::stacks::{
|
||||
StacksTransaction, StacksTransactionSigner, TokenTransferMemo, TransactionAnchorMode,
|
||||
TransactionAuth, TransactionPayload, TransactionVersion,
|
||||
};
|
||||
use crate::clarity::vm::types::StacksAddressExtensions;
|
||||
use crate::core::StacksEpochExtension;
|
||||
use crate::net::inv::nakamoto::InvGenerator;
|
||||
use crate::net::test::TestPeer;
|
||||
use crate::net::test::{TestEventObserver, TestPeer};
|
||||
use crate::net::tests::{NakamotoBootPlan, NakamotoBootStep, NakamotoBootTenure};
|
||||
use crate::net::{
|
||||
Error as NetError, GetNakamotoInvData, HandshakeData, NakamotoInvData, StacksMessage,
|
||||
StacksMessageType,
|
||||
};
|
||||
use crate::stacks_common::types::Address;
|
||||
use crate::util_lib::db::Error as DBError;
|
||||
|
||||
/// Handshake with and get the reward cycle inventories for a range of reward cycles
|
||||
fn peer_get_nakamoto_invs(
|
||||
mut peer: TestPeer<'static>,
|
||||
fn peer_get_nakamoto_invs<'a>(
|
||||
mut peer: TestPeer<'a>,
|
||||
reward_cycles: &[u64],
|
||||
) -> (TestPeer<'static>, Vec<StacksMessageType>) {
|
||||
) -> (TestPeer<'a>, Vec<StacksMessageType>) {
|
||||
let privk = StacksPrivateKey::new();
|
||||
let mut convo = peer.make_client_convo();
|
||||
let client_peer = peer.make_client_local_peer(privk.clone());
|
||||
@@ -80,63 +88,65 @@ fn peer_get_nakamoto_invs(
|
||||
|
||||
let (shutdown_send, shutdown_recv) = sync_channel(1);
|
||||
let join_handle = thread::spawn(move || {
|
||||
loop {
|
||||
peer.step_with_ibd(false).unwrap();
|
||||
if let Ok(..) = shutdown_recv.try_recv() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
peer
|
||||
});
|
||||
let mut tcp_socket = TcpStream::connect(peer_addr).unwrap();
|
||||
|
||||
let mut tcp_socket = TcpStream::connect(peer_addr).unwrap();
|
||||
|
||||
// first, handshake
|
||||
let handshake_data = StacksMessageType::Handshake(HandshakeData::from_local_peer(&client_peer));
|
||||
let signed_handshake_data = convo
|
||||
.sign_message(&chain_view, &privk, handshake_data)
|
||||
.unwrap();
|
||||
signed_handshake_data
|
||||
.consensus_serialize(&mut tcp_socket)
|
||||
.unwrap();
|
||||
|
||||
// read back handshake-accept
|
||||
let msg: StacksMessage = read_next(&mut tcp_socket).unwrap();
|
||||
match msg.payload {
|
||||
StacksMessageType::HandshakeAccept(..)
|
||||
| StacksMessageType::StackerDBHandshakeAccept(..) => {}
|
||||
x => {
|
||||
error!("Peer returned {:?}", &x);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
let mut replies = vec![];
|
||||
for get_nakamoto_inv in get_nakamoto_invs {
|
||||
// send getnakamotoinv
|
||||
get_nakamoto_inv
|
||||
// first, handshake
|
||||
let handshake_data =
|
||||
StacksMessageType::Handshake(HandshakeData::from_local_peer(&client_peer));
|
||||
let signed_handshake_data = convo
|
||||
.sign_message(&chain_view, &privk, handshake_data)
|
||||
.unwrap();
|
||||
signed_handshake_data
|
||||
.consensus_serialize(&mut tcp_socket)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
// read back the message
|
||||
let msg: StacksMessage = read_next(&mut tcp_socket).unwrap();
|
||||
let is_inv_reply = if let StacksMessageType::NakamotoInv(..) = &msg.payload {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_inv_reply {
|
||||
replies.push(msg.payload);
|
||||
break;
|
||||
} else {
|
||||
debug!("Got spurious meessage {:?}", &msg);
|
||||
// read back handshake-accept
|
||||
let msg: StacksMessage = read_next(&mut tcp_socket).unwrap();
|
||||
match msg.payload {
|
||||
StacksMessageType::HandshakeAccept(..)
|
||||
| StacksMessageType::StackerDBHandshakeAccept(..) => {}
|
||||
x => {
|
||||
error!("Peer returned {:?}", &x);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
let mut replies = vec![];
|
||||
for get_nakamoto_inv in get_nakamoto_invs {
|
||||
// send getnakamotoinv
|
||||
get_nakamoto_inv
|
||||
.consensus_serialize(&mut tcp_socket)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
// read back the message
|
||||
let msg: StacksMessage = read_next(&mut tcp_socket).unwrap();
|
||||
let is_inv_reply = if let StacksMessageType::NakamotoInv(..) = &msg.payload {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_inv_reply {
|
||||
replies.push(msg.payload);
|
||||
break;
|
||||
} else {
|
||||
debug!("Got spurious meessage {:?}", &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shutdown_send.send(true).unwrap();
|
||||
replies
|
||||
});
|
||||
|
||||
loop {
|
||||
peer.step_with_ibd(false).unwrap();
|
||||
if let Ok(..) = shutdown_recv.try_recv() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown_send.send(true).unwrap();
|
||||
let peer = join_handle.join().unwrap();
|
||||
let replies = join_handle.join().unwrap();
|
||||
|
||||
(peer, replies)
|
||||
}
|
||||
@@ -300,3 +310,256 @@ fn test_nakamoto_inv_10_extended_tenures_10_sortitions() {
|
||||
assert_eq!(bitvec.len() as u16, inv.bitlen);
|
||||
}
|
||||
}
|
||||
|
||||
fn make_nakamoto_peer_from_invs<'a>(
|
||||
test_name: &str,
|
||||
observer: &'a TestEventObserver,
|
||||
rc_len: u32,
|
||||
prepare_len: u32,
|
||||
bitvecs: Vec<Vec<bool>>,
|
||||
) -> TestPeer<'a> {
|
||||
for bitvec in bitvecs.iter() {
|
||||
assert_eq!(bitvec.len() as u32, rc_len);
|
||||
}
|
||||
|
||||
let private_key = StacksPrivateKey::from_seed(&[2]);
|
||||
let addr = StacksAddress::from_public_keys(
|
||||
C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
|
||||
&AddressHashMode::SerializeP2PKH,
|
||||
1,
|
||||
&vec![StacksPublicKey::from_private(&private_key)],
|
||||
)
|
||||
.unwrap();
|
||||
let recipient_addr =
|
||||
StacksAddress::from_string("ST2YM3J4KQK09V670TD6ZZ1XYNYCNGCWCVTASN5VM").unwrap();
|
||||
|
||||
let mut sender_nonce = 0;
|
||||
|
||||
let mut next_stx_transfer = || {
|
||||
let mut stx_transfer = StacksTransaction::new(
|
||||
TransactionVersion::Testnet,
|
||||
TransactionAuth::from_p2pkh(&private_key).unwrap(),
|
||||
TransactionPayload::TokenTransfer(
|
||||
recipient_addr.clone().to_account_principal(),
|
||||
1,
|
||||
TokenTransferMemo([0x00; 34]),
|
||||
),
|
||||
);
|
||||
stx_transfer.chain_id = 0x80000000;
|
||||
stx_transfer.anchor_mode = TransactionAnchorMode::OnChainOnly;
|
||||
stx_transfer.set_tx_fee(1);
|
||||
stx_transfer.auth.set_origin_nonce(sender_nonce);
|
||||
sender_nonce += 1;
|
||||
|
||||
let mut tx_signer = StacksTransactionSigner::new(&stx_transfer);
|
||||
tx_signer.sign_origin(&private_key).unwrap();
|
||||
let stx_transfer_signed = tx_signer.get_tx().unwrap();
|
||||
|
||||
stx_transfer_signed
|
||||
};
|
||||
|
||||
let mut boot_tenures = vec![];
|
||||
for bitvec in bitvecs.iter() {
|
||||
for has_tenure in bitvec {
|
||||
if *has_tenure {
|
||||
boot_tenures.push(NakamotoBootTenure::Sortition(vec![
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
]));
|
||||
} else {
|
||||
boot_tenures.push(NakamotoBootTenure::NoSortition(vec![
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::TenureExtend(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::TenureExtend(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::TenureExtend(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::Block(vec![next_stx_transfer()]),
|
||||
NakamotoBootStep::TenureExtend(vec![next_stx_transfer()]),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let plan = NakamotoBootPlan::new(test_name)
|
||||
.with_private_key(private_key)
|
||||
.with_pox_constants(rc_len, prepare_len)
|
||||
.with_initial_balances(vec![(addr.into(), 1_000_000)]);
|
||||
|
||||
let peer = plan.boot_into_nakamoto_peer(boot_tenures, Some(observer));
|
||||
peer
|
||||
}
|
||||
|
||||
fn check_inv_messages(
|
||||
bitvecs: Vec<Vec<bool>>,
|
||||
rc_len: u32,
|
||||
nakamoto_start_burn_height: u64,
|
||||
messages: Vec<StacksMessageType>,
|
||||
) {
|
||||
for (msg_idx, msg) in messages.into_iter().enumerate() {
|
||||
let StacksMessageType::NakamotoInv(inv) = msg else {
|
||||
panic!("Did not receive an inv for reward cycle {}", msg_idx);
|
||||
};
|
||||
for bit in 0..(inv.bitlen as usize) {
|
||||
let burn_block_height = (msg_idx as u64) * u64::from(rc_len) + (bit as u64);
|
||||
let msg_bit = inv.tenures[bit / 8] & (1 << (bit % 8)) != 0;
|
||||
if burn_block_height < nakamoto_start_burn_height {
|
||||
// inv doesn't cover epoch 2
|
||||
assert!(
|
||||
!msg_bit,
|
||||
"Bit {} in message {} is set but is before nakamoto-start height {} ({})",
|
||||
bit, msg_idx, nakamoto_start_burn_height, burn_block_height
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let inv_offset: u64 = burn_block_height - nakamoto_start_burn_height;
|
||||
let bitvec_idx = (inv_offset / u64::from(rc_len)) as usize;
|
||||
let expected_bit = if bitvec_idx >= bitvecs.len() {
|
||||
false
|
||||
} else {
|
||||
bitvecs[bitvec_idx][(inv_offset % u64::from(rc_len)) as usize]
|
||||
};
|
||||
assert_eq!(msg_bit, expected_bit, "Bit {} in message {} is {}, but expected {}. burn_block_height = {}, inv_offset = {}, bitvec_idx = {}, nakamoto_start_burn_height = {}",
|
||||
bit, msg_idx, msg_bit, expected_bit, burn_block_height, inv_offset, bitvec_idx, nakamoto_start_burn_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nakamoto_invs_full() {
|
||||
let observer = TestEventObserver::new();
|
||||
let bitvecs = vec![
|
||||
vec![true, true, true, true, true, true, true, true, true, true],
|
||||
vec![true, true, true, true, true, true, true, true, true, true],
|
||||
vec![true, true, true, true, true, true, true, true, true, true],
|
||||
vec![true, true, true, true, true, true, true, true, true, true],
|
||||
vec![true, true, true, true, true, true, true, true, true, true],
|
||||
];
|
||||
|
||||
let peer = make_nakamoto_peer_from_invs(function_name!(), &observer, 10, 3, bitvecs.clone());
|
||||
let (peer, reward_cycle_invs) =
|
||||
peer_get_nakamoto_invs(peer, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
|
||||
let nakamoto_start =
|
||||
NakamotoBootPlan::nakamoto_first_tenure_height(&peer.config.burnchain.pox_constants);
|
||||
|
||||
eprintln!("{:#?}", &reward_cycle_invs);
|
||||
assert_eq!(reward_cycle_invs.len(), 10);
|
||||
check_inv_messages(bitvecs, 10, nakamoto_start, reward_cycle_invs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nakamoto_invs_alternating() {
|
||||
let observer = TestEventObserver::new();
|
||||
let bitvecs = vec![
|
||||
vec![
|
||||
true, false, true, false, true, false, true, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, true, false, true, false, true, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
true, false, true, false, true, false, true, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, true, false, true, false, true, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
true, false, true, false, true, false, true, true, true, true,
|
||||
],
|
||||
];
|
||||
|
||||
let peer = make_nakamoto_peer_from_invs(function_name!(), &observer, 10, 3, bitvecs.clone());
|
||||
let (peer, reward_cycle_invs) =
|
||||
peer_get_nakamoto_invs(peer, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
|
||||
let nakamoto_start =
|
||||
NakamotoBootPlan::nakamoto_first_tenure_height(&peer.config.burnchain.pox_constants);
|
||||
|
||||
eprintln!("{:#?}", &reward_cycle_invs);
|
||||
assert_eq!(reward_cycle_invs.len(), 10);
|
||||
check_inv_messages(bitvecs, 10, nakamoto_start, reward_cycle_invs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nakamoto_invs_sparse() {
|
||||
let observer = TestEventObserver::new();
|
||||
let bitvecs = vec![
|
||||
vec![
|
||||
true, false, false, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, true, false, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, true, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, false, true, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, false, false, true, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, false, false, false, true, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, false, false, false, false, true, true, true, true,
|
||||
],
|
||||
];
|
||||
|
||||
let peer = make_nakamoto_peer_from_invs(function_name!(), &observer, 10, 3, bitvecs.clone());
|
||||
let (peer, reward_cycle_invs) =
|
||||
peer_get_nakamoto_invs(peer, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
|
||||
let nakamoto_start =
|
||||
NakamotoBootPlan::nakamoto_first_tenure_height(&peer.config.burnchain.pox_constants);
|
||||
|
||||
eprintln!("{:#?}", &reward_cycle_invs);
|
||||
assert_eq!(reward_cycle_invs.len(), 12);
|
||||
check_inv_messages(bitvecs, 10, nakamoto_start, reward_cycle_invs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nakamoto_invs_different_anchor_blocks() {
|
||||
let observer = TestEventObserver::new();
|
||||
let bitvecs = vec![
|
||||
vec![true, true, true, true, true, true, false, true, true, true],
|
||||
vec![true, true, true, true, true, false, false, true, true, true],
|
||||
vec![
|
||||
true, true, true, true, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
true, true, true, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
true, true, false, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
true, false, false, false, false, false, false, true, true, true,
|
||||
],
|
||||
vec![
|
||||
false, false, false, false, false, false, false, true, true, true,
|
||||
],
|
||||
];
|
||||
|
||||
let peer = make_nakamoto_peer_from_invs(function_name!(), &observer, 10, 3, bitvecs.clone());
|
||||
let (peer, reward_cycle_invs) =
|
||||
peer_get_nakamoto_invs(peer, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
|
||||
let nakamoto_start =
|
||||
NakamotoBootPlan::nakamoto_first_tenure_height(&peer.config.burnchain.pox_constants);
|
||||
|
||||
eprintln!("{:#?}", &reward_cycle_invs);
|
||||
assert_eq!(reward_cycle_invs.len(), 12);
|
||||
check_inv_messages(bitvecs, 10, nakamoto_start, reward_cycle_invs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user