From 197e8a061f1d37df79b43fd145778ad357860b11 Mon Sep 17 00:00:00 2001 From: Hank Stoever Date: Tue, 24 Sep 2024 13:38:17 -0700 Subject: [PATCH] feat: add integration test for stx-transfer and delegate burn ops in Nakamoto --- .github/workflows/bitcoin-tests.yml | 2 +- .../src/tests/nakamoto_integrations.rs | 170 +++++++++++++++++- 2 files changed, 167 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index a6d4dff460..839ada1ef9 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -114,7 +114,7 @@ jobs: - tests::signer::v0::partial_tenure_fork - tests::signer::v0::mine_2_nakamoto_reward_cycles - tests::signer::v0::signer_set_rollover - - tests::nakamoto_integrations::stack_stx_burn_op_integration_test + - tests::nakamoto_integrations::burn_ops_integration_test - tests::nakamoto_integrations::check_block_heights - tests::nakamoto_integrations::clarity_burn_state - tests::nakamoto_integrations::check_block_times diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index b95eed7fa5..bd669c7180 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -34,7 +34,8 @@ use rand::RngCore; use stacks::burnchains::{MagicBytes, Txid}; use stacks::chainstate::burn::db::sortdb::SortitionDB; use stacks::chainstate::burn::operations::{ - BlockstackOperationType, PreStxOp, StackStxOp, VoteForAggregateKeyOp, + BlockstackOperationType, DelegateStxOp, PreStxOp, StackStxOp, TransferStxOp, + VoteForAggregateKeyOp, }; use stacks::chainstate::coordinator::comm::CoordinatorChannels; use stacks::chainstate::coordinator::OnChainRewardSetProvider; @@ -4012,7 +4013,17 @@ fn follower_bootup_across_multiple_cycles() { #[test] #[ignore] -fn stack_stx_burn_op_integration_test() { +/// Test out various burn operations being processed in Nakamoto. +/// +/// There are 4 burn ops submitted: +/// +/// - stx-transfer +/// - delegate-stx +/// - stack-stx +/// +/// Additionally, a stack-stx without a signer key is submitted, which should +/// not be processed in Nakamoto. +fn burn_ops_integration_test() { if env::var("BITCOIND_TEST") != Ok("1".into()) { return; } @@ -4027,10 +4038,26 @@ fn stack_stx_burn_op_integration_test() { let signer_sk_2 = Secp256k1PrivateKey::new(); let signer_addr_2 = tests::to_addr(&signer_sk_2); + let stacker_sk_1 = Secp256k1PrivateKey::new(); + let stacker_addr_1 = tests::to_addr(&stacker_sk_1); + + let stacker_sk_2 = Secp256k1PrivateKey::new(); + let stacker_addr_2 = tests::to_addr(&stacker_sk_2); + let mut signers = TestSigners::new(vec![signer_sk_1.clone()]); let stacker_sk = setup_stacker(&mut naka_conf); + // Add the initial balances to the other accounts + naka_conf.add_initial_balance( + PrincipalData::from(stacker_addr_1.clone()).to_string(), + 1000000, + ); + naka_conf.add_initial_balance( + PrincipalData::from(stacker_addr_2.clone()).to_string(), + 1000000, + ); + test_observer::spawn(); let observer_port = test_observer::EVENT_OBSERVER_PORT; naka_conf.events_observers.insert(EventObserverConfig { @@ -4135,7 +4162,49 @@ fn stack_stx_burn_op_integration_test() { .is_ok(), "Pre-stx operation should submit successfully" ); - info!("Submitted 2 pre-stx ops at block {block_height}, mining a few blocks..."); + + let mut miner_signer_3 = Keychain::default(naka_conf.node.seed.clone()).generate_op_signer(); + info!("Submitting third pre-stx op"); + let pre_stx_op_3 = PreStxOp { + output: stacker_addr_1.clone(), + txid: Txid([0u8; 32]), + vtxindex: 0, + block_height: 0, + burn_header_hash: BurnchainHeaderHash([0u8; 32]), + }; + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch30, + BlockstackOperationType::PreStx(pre_stx_op_3), + &mut miner_signer_3, + 1 + ) + .is_ok(), + "Pre-stx operation should submit successfully" + ); + + info!("Submitting fourth pre-stx op"); + let mut miner_signer_4 = Keychain::default(naka_conf.node.seed.clone()).generate_op_signer(); + let pre_stx_op_4 = PreStxOp { + output: stacker_addr_2.clone(), + txid: Txid([0u8; 32]), + vtxindex: 0, + block_height: 0, + burn_header_hash: BurnchainHeaderHash([0u8; 32]), + }; + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch30, + BlockstackOperationType::PreStx(pre_stx_op_4), + &mut miner_signer_4, + 1 + ) + .is_ok(), + "Pre-stx operation should submit successfully" + ); + info!("Submitted 4 pre-stx ops at block {block_height}, mining a few blocks..."); // Mine until the next prepare phase let block_height = btc_regtest_controller.get_headers_height(); @@ -4216,6 +4285,8 @@ fn stack_stx_burn_op_integration_test() { let mut signer_burnop_signer_1 = BurnchainOpSigner::new(signer_sk_1.clone(), false); let mut signer_burnop_signer_2 = BurnchainOpSigner::new(signer_sk_2.clone(), false); + let mut stacker_burnop_signer_1 = BurnchainOpSigner::new(stacker_sk_1.clone(), false); + let mut stacker_burnop_signer_2 = BurnchainOpSigner::new(stacker_sk_2.clone(), false); info!( "Before stack-stx op, signer 1 total: {}", @@ -4247,6 +4318,55 @@ fn stack_stx_burn_op_integration_test() { info!("Signer 1 addr: {}", signer_addr_1.to_b58()); info!("Signer 2 addr: {}", signer_addr_2.to_b58()); + info!("Submitting transfer STX op"); + let transfer_stx_op = TransferStxOp { + sender: stacker_addr_1.clone(), + recipient: stacker_addr_2.clone(), + transfered_ustx: 10000, + memo: vec![], + txid: Txid([0u8; 32]), + vtxindex: 0, + block_height: 0, + burn_header_hash: BurnchainHeaderHash([0u8; 32]), + }; + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch30, + BlockstackOperationType::TransferStx(transfer_stx_op), + &mut stacker_burnop_signer_1, + 1 + ) + .is_ok(), + "Transfer STX operation should submit successfully" + ); + + info!("Submitting delegate STX op"); + let del_stx_op = DelegateStxOp { + sender: stacker_addr_2.clone(), + delegate_to: stacker_addr_1.clone(), + reward_addr: None, + delegated_ustx: 100_000, + // to be filled in + txid: Txid([0u8; 32]), + vtxindex: 0, + block_height: 0, + burn_header_hash: BurnchainHeaderHash([0u8; 32]), + until_burn_height: None, + }; + + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch30, + BlockstackOperationType::DelegateStx(del_stx_op), + &mut stacker_burnop_signer_2, + 1 + ) + .is_ok(), + "Delegate STX operation should submit successfully" + ); + let pox_info = get_pox_info(&http_origin).unwrap(); let min_stx = pox_info.next_cycle.min_threshold_ustx; @@ -4318,6 +4438,8 @@ fn stack_stx_burn_op_integration_test() { } let mut stack_stx_found = false; + let mut transfer_stx_found = false; + let mut delegate_stx_found = false; let mut stack_stx_burn_op_tx_count = 0; let blocks = test_observer::get_blocks(); info!("stack event observer num blocks: {:?}", blocks.len()); @@ -4332,6 +4454,45 @@ fn stack_stx_burn_op_integration_test() { if raw_tx == "0x00" { info!("Found a burn op: {:?}", tx); let burnchain_op = tx.get("burnchain_op").unwrap().as_object().unwrap(); + if burnchain_op.contains_key("transfer_stx") { + let transfer_stx_obj = burnchain_op.get("transfer_stx").unwrap(); + let sender_obj = transfer_stx_obj.get("sender").unwrap(); + let sender = sender_obj.get("address").unwrap().as_str().unwrap(); + let recipient_obj = transfer_stx_obj.get("recipient").unwrap(); + let recipient = recipient_obj.get("address").unwrap().as_str().unwrap(); + let transfered_ustx = transfer_stx_obj + .get("transfered_ustx") + .unwrap() + .as_u64() + .unwrap(); + assert_eq!(sender, stacker_addr_1.to_string()); + assert_eq!(recipient, stacker_addr_2.to_string()); + assert_eq!(transfered_ustx, 10000); + info!( + "Transfer STX op: sender: {}, recipient: {}, transfered_ustx: {}", + sender, recipient, transfered_ustx + ); + transfer_stx_found = true; + continue; + } + if burnchain_op.contains_key("delegate_stx") { + info!("Got delegate STX op: {:?}", burnchain_op); + let delegate_stx_obj = burnchain_op.get("delegate_stx").unwrap(); + let sender_obj = delegate_stx_obj.get("sender").unwrap(); + let sender = sender_obj.get("address").unwrap().as_str().unwrap(); + let delegate_to_obj = delegate_stx_obj.get("delegate_to").unwrap(); + let delegate_to = delegate_to_obj.get("address").unwrap().as_str().unwrap(); + let delegated_ustx = delegate_stx_obj + .get("delegated_ustx") + .unwrap() + .as_u64() + .unwrap(); + assert_eq!(sender, stacker_addr_2.to_string()); + assert_eq!(delegate_to, stacker_addr_1.to_string()); + assert_eq!(delegated_ustx, 100_000); + delegate_stx_found = true; + continue; + } if !burnchain_op.contains_key("stack_stx") { warn!("Got unexpected burnchain op: {:?}", burnchain_op); panic!("unexpected btc transaction type"); @@ -4378,7 +4539,8 @@ fn stack_stx_burn_op_integration_test() { stack_stx_burn_op_tx_count, 1, "Stack-stx tx without a signer_key shouldn't have been submitted" ); - + assert!(transfer_stx_found, "Expected transfer STX op"); + assert!(delegate_stx_found, "Expected delegate STX op"); let sortdb = btc_regtest_controller.sortdb_mut(); let sortdb_conn = sortdb.conn(); let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb_conn).unwrap();