chore: expand test coverage for inventory generation using the new declarative nakamoto chain history test framework

This commit is contained in:
Jude Nelson
2024-01-31 00:12:10 -05:00
parent 0412525fc5
commit 300aeda09f

View File

@@ -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);
}