From 17f194a19e0b89c9fdbf9f44f56c4489b62cd5a6 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 18 Jan 2023 15:20:05 -0500 Subject: [PATCH 1/7] wip, test works except for pox --- src/chainstate/coordinator/tests.rs | 6 + src/chainstate/stacks/boot/pox_2_tests.rs | 28 ++ src/chainstate/stacks/db/blocks.rs | 6 +- src/clarity_vm/special.rs | 69 ++++- .../burnchains/bitcoin_regtest_controller.rs | 92 +++++- .../src/tests/neon_integrations.rs | 274 +++++++++++++++++- 6 files changed, 461 insertions(+), 14 deletions(-) diff --git a/src/chainstate/coordinator/tests.rs b/src/chainstate/coordinator/tests.rs index 89608a877..022dce5e6 100644 --- a/src/chainstate/coordinator/tests.rs +++ b/src/chainstate/coordinator/tests.rs @@ -3548,6 +3548,10 @@ fn test_delegate_stx_btc_ops() { &second_del, ); + if ix == 10 { + + } + // Check that the effects of the delegate stx op sent when ix==10 // are materialized for ix=11... (we check that the // changes endure for the following blocks) @@ -3582,6 +3586,8 @@ fn test_delegate_stx_btc_ops() { "The second delegation should not be active" ); } + + } } diff --git a/src/chainstate/stacks/boot/pox_2_tests.rs b/src/chainstate/stacks/boot/pox_2_tests.rs index 959db16ce..3ccf25676 100644 --- a/src/chainstate/stacks/boot/pox_2_tests.rs +++ b/src/chainstate/stacks/boot/pox_2_tests.rs @@ -1339,6 +1339,7 @@ fn delegate_stack_increase() { let tip = get_tip(peer.sortdb.as_ref()); // submit delegation tx + let success_alice_delegation = alice_nonce; let alice_delegation_1 = make_pox_2_contract_call( &alice, alice_nonce, @@ -1554,6 +1555,33 @@ fn delegate_stack_increase() { "(err 18)" ); + let delegate_stx_tx = &alice_txs.get(&success_alice_delegation).unwrap().clone().events[0]; + let delegate_stx_op_data = HashMap::from([ + ("pox-addr", Value::none()), + ("amount-ustx", Value::UInt(10230000000000)), + ("unlock-burn-height", Value::none()), + ( + "delegate-to", + Value::Principal( + StacksAddress::from_string("ST1GCB6NH3XR67VT4R5PKVJ2PYXNVQ4AYQATXNP4P") + .unwrap() + .to_account_principal(), + ), + ), + ]); + let common_data = PoxPrintFields { + op_name: "delegate-stx".to_string(), + stacker: Value::Principal( + StacksAddress::from_string("ST2Q1B4S2DY2Y96KYNZTVCCZZD1V9AGWCS5MFXM4C") + .unwrap() + .to_account_principal(), + ), + balance: Value::UInt(10240000000000), + locked: Value::UInt(0), + burnchain_unlock_height: Value::UInt(0), + }; + check_pox_print_event(delegate_stx_tx, common_data, delegate_stx_op_data); + // Check that the call to `delegate-stack-increase` has a well-formed print event. let delegate_stack_increase_tx = &bob_txs.get(&4).unwrap().clone().events[0]; let pox_addr_val = generate_pox_clarity_value("60c59ab11f7063ef44c16d3dc856f76bbb915eba"); diff --git a/src/chainstate/stacks/db/blocks.rs b/src/chainstate/stacks/db/blocks.rs index 71d2c3694..ee939de09 100644 --- a/src/chainstate/stacks/db/blocks.rs +++ b/src/chainstate/stacks/db/blocks.rs @@ -5866,9 +5866,9 @@ impl StacksChainState { /// in the block, and a `PreCommitClarityBlock` struct. /// /// The `StacksEpochReceipts` contains the list of transaction - /// receipts for both the preceeding microblock stream that the - /// block confirms, as well as the transaction receipts for the - /// anchored block's transactions. Finally, it returns the + /// receipts for the preceeding microblock stream that the + /// block confirms, the anchored block's transactions, and the + /// btc wire transactions. Finally, it returns the /// execution costs for the microblock stream and for the anchored /// block (separately). /// diff --git a/src/clarity_vm/special.rs b/src/clarity_vm/special.rs index 17a99626d..34547717b 100644 --- a/src/clarity_vm/special.rs +++ b/src/clarity_vm/special.rs @@ -253,11 +253,11 @@ fn handle_pox_v1_api_contract_call( } /// Determine who the stacker is for a given function. -/// - for non-delegate functions, it's tx-sender -/// - for delegate functions, it's the first argument +/// - for non-delegate stacking functions, it's tx-sender +/// - for delegate stacking functions, it's the first argument fn get_stacker(sender: &PrincipalData, function_name: &str, args: &[Value]) -> Value { match function_name { - "stack-stx" | "stack-increase" | "stack-extend" => Value::Principal(sender.clone()), + "stack-stx" | "stack-increase" | "stack-extend" | "delegate-stx" => Value::Principal(sender.clone()), _ => args[0].clone(), } } @@ -322,6 +322,39 @@ fn create_event_info_aggregation_code(function_name: &str) -> String { ) } +/// Craft the code snippet to evaluate an event-info for the delegate-stx function +fn create_event_info_delegation_code( + sender: &PrincipalData, + function_name: &str, + args: &[Value], +) -> String { + format!( + r#" + (let ( + (stacker '{stacker}) + (func-name "{func_name}") + (stacker-info (stx-account stacker)) + (total-balance (stx-get-balance stacker)) + ) + {{ + ;; Function name + name: func-name, + ;; The principal of the stacker + stacker: stacker, + ;; The current available balance + balance: total-balance, + ;; The amount of locked STX + locked: (get locked stacker-info), + ;; The burnchain block height of when the tokens unlock. Zero if no tokens are locked. + burnchain-unlock-height: (get unlock-height stacker-info), + }} + ) + "#, + stacker = get_stacker(sender, function_name, args), + func_name = function_name + ) +} + /// Craft the code snippet to generate the method-specific `data` payload fn create_event_info_data_code(function_name: &str, args: &[Value]) -> String { match function_name { @@ -532,6 +565,32 @@ fn create_event_info_data_code(function_name: &str, args: &[Value]) -> String { reward_cycle = &args[1] ) } + "delegate-stx" => { + format!( + r#" + {{ + data: {{ + ;; amount of ustx to delegate. + ;; equal to args[0] + amount-ustx: {amount_ustx}, + ;; address of delegatee. + ;; equal to args[1] + delegate-to: '{delegate_to}, + ;; optional burnchain height when the delegation finishes. + ;; derived from args[2] + unlock-burn-height: {until_burn_height}, + ;; optional PoX address tuple. + ;; equal to args[3]. + pox-addr: {pox_addr} + }} + }} + "#, + amount_ustx = &args[0], + delegate_to = &args[1], + until_burn_height = &args[2], + pox_addr = &args[3], + ) + }, _ => format!("{{ data: {{ unimplemented: true }} }}"), } } @@ -566,6 +625,8 @@ fn synthesize_pox_2_event_info( "stack-aggregation-commit" | "stack-aggregation-commit-indexed" | "stack-aggregation-increase" => Some(create_event_info_aggregation_code(function_name)), + "delegate-stx" => Some(create_event_info_delegation_code(sender, function_name, args)), + // use create_event_info_stack_or_delegate_code _ => None, }; @@ -631,7 +692,7 @@ fn synthesize_pox_2_event_info( e })?; - test_debug!( + info!( "Synthesized PoX-2 event info for '{}''s call to '{}': {:?}", sender, function_name, diff --git a/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs b/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs index 294d7bb2b..cbd958c52 100644 --- a/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs +++ b/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs @@ -38,10 +38,7 @@ use stacks::burnchains::{ }; use stacks::burnchains::{Burnchain, BurnchainParameters}; use stacks::chainstate::burn::db::sortdb::SortitionDB; -use stacks::chainstate::burn::operations::{ - BlockstackOperationType, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, TransferStxOp, - UserBurnSupportOp, -}; +use stacks::chainstate::burn::operations::{BlockstackOperationType, DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, TransferStxOp, UserBurnSupportOp}; use stacks::chainstate::coordinator::comm::CoordinatorChannels; #[cfg(test)] use stacks::chainstate::stacks::address::PoxAddress; @@ -950,6 +947,89 @@ impl BitcoinRegtestController { Some(tx) } + #[cfg(test)] + /// Build a delegate stacks tx. + /// this *only* works if the only existant UTXO is from a PreStx Op + /// this is okay for testing, but obviously not okay for actual use. + /// The reason for this constraint is that the bitcoin_regtest_controller's UTXO + /// and signing logic are fairly intertwined, and untangling the two seems excessive + /// for a functionality that won't be implemented for production via this controller. + fn build_delegate_stacks_tx( + &mut self, + epoch_id: StacksEpochId, + payload: DelegateStxOp, + signer: &mut BurnchainOpSigner, + utxo_to_use: Option, + ) -> Option { + let public_key = signer.get_public_key(); + let max_tx_size = 230; + + let (mut tx, mut utxos) = if let Some(utxo) = utxo_to_use { + ( + Transaction { + input: vec![], + output: vec![], + version: 1, + lock_time: 0, + }, + UTXOSet { + bhh: BurnchainHeaderHash::zero(), + utxos: vec![utxo], + }, + ) + } else { + self.prepare_tx( + epoch_id, + &public_key, + DUST_UTXO_LIMIT + max_tx_size * self.config.burnchain.satoshis_per_byte, + None, + None, + 0, + )? + }; + + // Serialize the payload + let op_bytes = { + let mut bytes = self.config.burnchain.magic_bytes.as_bytes().to_vec(); + payload.consensus_serialize(&mut bytes).ok()?; + bytes + }; + + let consensus_output = TxOut { + value: 0, + script_pubkey: Builder::new() + .push_opcode(opcodes::All::OP_RETURN) + .push_slice(&op_bytes) + .into_script(), + }; + + tx.output = vec![consensus_output]; + tx.output.push( + PoxAddress::Standard(payload.delegate_to.clone(), None) + .to_bitcoin_tx_out(DUST_UTXO_LIMIT), + ); + + self.finalize_tx( + epoch_id, + &mut tx, + DUST_UTXO_LIMIT, + 0, + max_tx_size, + self.config.burnchain.satoshis_per_byte, + &mut utxos, + signer, + )?; + + increment_btc_ops_sent_counter(); + + info!( + "Miner node: submitting stacks delegate op - {}", + public_key.to_hex() + ); + + Some(tx) + } + #[cfg(not(test))] fn build_pre_stacks_tx( &mut self, @@ -1687,7 +1767,9 @@ impl BitcoinRegtestController { self.build_transfer_stacks_tx(epoch_id, payload, op_signer, None) } BlockstackOperationType::StackStx(_payload) => unimplemented!(), - BlockstackOperationType::DelegateStx(_payload) => unimplemented!(), + BlockstackOperationType::DelegateStx(payload) => { + self.build_delegate_stacks_tx(epoch_id, payload, op_signer, None) + } }; transaction.map(|tx| SerializedTx::new(tx)) diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 2398439ee..ba05cacb8 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -16,7 +16,7 @@ use rusqlite::types::ToSql; use stacks::burnchains::bitcoin::address::{BitcoinAddress, LegacyBitcoinAddressType}; use stacks::burnchains::bitcoin::BitcoinNetworkType; use stacks::burnchains::Txid; -use stacks::chainstate::burn::operations::{BlockstackOperationType, PreStxOp, TransferStxOp}; +use stacks::chainstate::burn::operations::{BlockstackOperationType, DelegateStxOp, PreStxOp, TransferStxOp}; use stacks::chainstate::coordinator::comm::CoordinatorChannels; use stacks::clarity_cli::vm_execute as execute; use stacks::codec::StacksMessageCodec; @@ -956,6 +956,7 @@ pub fn get_balance(http_origin: &str, account: &F) -> u128 #[derive(Debug)] pub struct Account { pub balance: u128, + pub locked: u128, pub nonce: u64, } @@ -971,6 +972,7 @@ pub fn get_account(http_origin: &str, account: &F) -> Acco info!("Account response: {:#?}", res); Account { balance: u128::from_str_radix(&res.balance[2..], 16).unwrap(), + locked: u128::from_str_radix(&res.locked[2..], 16).unwrap(), nonce: res.nonce, } } @@ -1685,6 +1687,274 @@ fn stx_transfer_btc_integration_test() { channel.stop_chains_coordinator(); } +#[test] +#[ignore] +fn stx_delegate_btc_integration_test() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + + let spender_sk = StacksPrivateKey::from_hex(SK_1).unwrap(); + let spender_stx_addr: StacksAddress = to_addr(&spender_sk); + let spender_addr: PrincipalData = spender_stx_addr.clone().into(); + let _spender_btc_addr = BitcoinAddress::from_bytes_legacy( + BitcoinNetworkType::Regtest, + LegacyBitcoinAddressType::PublicKeyHash, + &spender_stx_addr.bytes.0, + ) + .unwrap(); + + let spender_2_sk = StacksPrivateKey::from_hex(SK_2).unwrap(); + let spender_2_stx_addr: StacksAddress = to_addr(&spender_2_sk); + let spender_2_addr: PrincipalData = spender_2_stx_addr.clone().into(); + let pox_pubkey = Secp256k1PublicKey::from_hex( + "02f006a09b59979e2cb8449f58076152af6b124aa29b948a3714b8d5f15aa94ede", + ) + .unwrap(); + let pox_pubkey_hash = bytes_to_hex( + &Hash160::from_node_public_key(&pox_pubkey) + .to_bytes() + .to_vec(), + ); + + let (mut conf, _miner_account) = neon_integration_test_conf(); + + conf.initial_balances.push(InitialBalance { + address: spender_addr.clone(), + amount: 100300, + }); + + conf.initial_balances.push(InitialBalance { + address: spender_2_addr.clone(), + amount: 100300, + }); + + // force mainnet limits in 2.05 for this test + conf.burnchain.epochs = Some(vec![ + StacksEpoch { + epoch_id: StacksEpochId::Epoch20, + start_height: 0, + end_height: 1, + block_limit: BLOCK_LIMIT_MAINNET_20.clone(), + network_epoch: PEER_VERSION_EPOCH_2_0, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch2_05, + start_height: 1, + end_height: 2, + block_limit: BLOCK_LIMIT_MAINNET_205.clone(), + network_epoch: PEER_VERSION_EPOCH_2_05, + }, + StacksEpoch { + epoch_id: StacksEpochId::Epoch21, + start_height: 2, + end_height: 9223372036854775807, + block_limit: BLOCK_LIMIT_MAINNET_21.clone(), + network_epoch: PEER_VERSION_EPOCH_2_1, + }, + ]); + conf.burnchain.pox_2_activation = Some(3); + + test_observer::spawn(); + conf.events_observers.push(EventObserverConfig { + endpoint: format!("localhost:{}", test_observer::EVENT_OBSERVER_PORT), + events_keys: vec![EventKeyType::AnyEvent], + }); + + let mut btcd_controller = BitcoinCoreController::new(conf.clone()); + btcd_controller + .start_bitcoind() + .map_err(|_e| ()) + .expect("Failed starting bitcoind"); + + let mut burnchain_config = Burnchain::regtest(&conf.get_burn_db_path()); + + // reward cycle length = 5, so 3 reward cycle slots + 2 prepare-phase burns + let reward_cycle_len = 5; + let prepare_phase_len = 2; + let pox_constants = PoxConstants::new( + reward_cycle_len, + prepare_phase_len, + 2, + 5, + 15, + (16 * reward_cycle_len - 1).into(), + (17 * reward_cycle_len).into(), + u32::MAX, + ); + burnchain_config.pox_constants = pox_constants.clone(); + + let mut btc_regtest_controller = BitcoinRegtestController::with_burnchain( + conf.clone(), None, Some(burnchain_config.clone()), None + ); + let http_origin = format!("http://{}", &conf.node.rpc_bind); + + btc_regtest_controller.bootstrap_chain(201); + + eprintln!("Chain bootstrapped..."); + + let mut run_loop = neon::RunLoop::new(conf.clone()); + let blocks_processed = run_loop.get_blocks_processed_arc(); + + let channel = run_loop.get_coordinator_channel().unwrap(); + + thread::spawn(move || run_loop.start(None, 0)); + + // give the run loop some time to start up! + wait_for_runloop(&blocks_processed); + + // first block wakes up the run loop + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + // first block will hold our VRF registration + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + // second block will be the first mined Stacks block + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + test_observer::clear(); + + // Mine a few more blocks so that Epoch 2.1 (and thus pox-2) can take effect. + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + let pox_info = get_pox_info(&http_origin); + info!("pox info after startup: {:?}", pox_info); + assert_eq!(pox_info.contract_id, format!("{}", boot_code_id("pox-2", false))); + + // let's query the spender's account: + assert_eq!(get_balance(&http_origin, &spender_addr), 100300); + + // okay, let's send a pre-stx op. + let pre_stx_op = PreStxOp { + output: spender_stx_addr.clone(), + // to be filled in + txid: Txid([0u8; 32]), + vtxindex: 0, + block_height: 0, + burn_header_hash: BurnchainHeaderHash([0u8; 32]), + }; + + let mut miner_signer = Keychain::default(conf.node.seed.clone()).generate_op_signer(); + + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch21, + BlockstackOperationType::PreStx(pre_stx_op), + &mut miner_signer, + 1 + ) + .is_some(), + "Pre-stx operation should submit successfully" + ); + + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + // let's fire off our delegate op. + let recipient_sk = StacksPrivateKey::new(); + let recipient_addr = to_addr(&recipient_sk); + let del_stx_op = DelegateStxOp { + sender: spender_stx_addr.clone(), + delegate_to: recipient_addr.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, + }; + + let mut spender_signer = BurnchainOpSigner::new(spender_sk.clone(), false); + + assert!( + btc_regtest_controller + .submit_operation( + StacksEpochId::Epoch21, + BlockstackOperationType::DelegateStx(del_stx_op), + &mut spender_signer, + 1 + ) + .is_some(), + "Delegate operation should submit successfully" + ); + // should be elected in the same block as the transfer, so balances should be unchanged. + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + assert_eq!(get_balance(&http_origin, &spender_addr), 100300); + assert_eq!(get_balance(&http_origin, &recipient_addr), 0); + + // this block should process the delegation, after which the balaces should be unchanged + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + assert_eq!(get_balance(&http_origin, &spender_addr), 100300); + assert_eq!(get_balance(&http_origin, &recipient_addr), 0); + + let tx = make_contract_call( + &spender_2_sk, + 0, + 262, + &StacksAddress::from_string("ST000000000000000000002AMW42H").unwrap(), + "pox-2", + "stack-stx", + &[ + Value::UInt(500), + execute( + &format!("{{ hashbytes: 0x{}, version: 0x00 }}", pox_pubkey_hash), + ClarityVersion::Clarity2, + ) + .unwrap() + .unwrap(), + Value::UInt(10), + Value::UInt(6), + ], + ); + + // okay, let's push that stacking transaction! + submit_tx(&http_origin, &tx); + + let pox_info = get_pox_info(&http_origin); + // now let's mine until the next reward cycle starts ... + // while sort_height < ((pox_info.current_cycle.id as u32 * pox_constants.reward_cycle_length) + 1).into() { + // next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + // sort_height = channel.get_sortitions_processed(); + // eprintln!("Sort height: {}", sort_height); + // } + + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + let account = get_account(&http_origin, &spender_stx_addr); + assert_eq!(account.locked, 500); + + + let blocks = test_observer::get_blocks(); + for block in blocks.iter() { + let events = block.get("events").unwrap().as_array().unwrap(); + for event in events.iter() { + let event_type = event.get("type").unwrap().as_str().unwrap(); + if event_type == "contract_event" { + let contract_event = event.get("contract_event").unwrap().as_object().unwrap(); + let sub_type = contract_event.get("topic").unwrap().as_str().unwrap(); + assert_eq!(sub_type, "print"); + + let name_field = &contract_event["value"]["Response"]["data"]["Tuple"]["data_map"]["name"]; + let name_data = name_field["Sequence"]["String"]["ASCII"]["data"].as_array().unwrap(); + let ascii_vec = name_data.iter().map(|num| num.as_u64().unwrap() as u8).collect(); + let name = String::from_utf8(ascii_vec).unwrap(); + assert_eq!(name, "delegate-stx"); + } + } + } + + test_observer::clear(); + channel.stop_chains_coordinator(); +} + #[test] #[ignore] fn bitcoind_resubmission_test() { @@ -5536,7 +5806,7 @@ fn pox_integration_test() { 15, (16 * reward_cycle_len - 1).into(), (17 * reward_cycle_len).into(), - u32::max_value(), + u32::MAX, ); burnchain_config.pox_constants = pox_constants.clone(); From aa102f22bed0df1aa496ed7f0d494b008ad17b31 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 18 Jan 2023 16:52:14 -0500 Subject: [PATCH 2/7] clean up test --- src/clarity_vm/special.rs | 2 +- .../src/tests/neon_integrations.rs | 65 ++++++------------- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/src/clarity_vm/special.rs b/src/clarity_vm/special.rs index 34547717b..4afcd965f 100644 --- a/src/clarity_vm/special.rs +++ b/src/clarity_vm/special.rs @@ -692,7 +692,7 @@ fn synthesize_pox_2_event_info( e })?; - info!( + test_debug!( "Synthesized PoX-2 event info for '{}''s call to '{}': {:?}", sender, function_name, diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index ba05cacb8..524b5b22d 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1697,16 +1697,9 @@ fn stx_delegate_btc_integration_test() { let spender_sk = StacksPrivateKey::from_hex(SK_1).unwrap(); let spender_stx_addr: StacksAddress = to_addr(&spender_sk); let spender_addr: PrincipalData = spender_stx_addr.clone().into(); - let _spender_btc_addr = BitcoinAddress::from_bytes_legacy( - BitcoinNetworkType::Regtest, - LegacyBitcoinAddressType::PublicKeyHash, - &spender_stx_addr.bytes.0, - ) - .unwrap(); - let spender_2_sk = StacksPrivateKey::from_hex(SK_2).unwrap(); - let spender_2_stx_addr: StacksAddress = to_addr(&spender_2_sk); - let spender_2_addr: PrincipalData = spender_2_stx_addr.clone().into(); + let recipient_sk = StacksPrivateKey::new(); + let recipient_addr = to_addr(&recipient_sk); let pox_pubkey = Secp256k1PublicKey::from_hex( "02f006a09b59979e2cb8449f58076152af6b124aa29b948a3714b8d5f15aa94ede", ) @@ -1723,13 +1716,12 @@ fn stx_delegate_btc_integration_test() { address: spender_addr.clone(), amount: 100300, }); - conf.initial_balances.push(InitialBalance { - address: spender_2_addr.clone(), - amount: 100300, + address: recipient_addr.clone().into(), + amount: 300, }); - // force mainnet limits in 2.05 for this test + // update epoch info so that Epoch 2.1 takes effect conf.burnchain.epochs = Some(vec![ StacksEpoch { epoch_id: StacksEpochId::Epoch20, @@ -1819,13 +1811,6 @@ fn stx_delegate_btc_integration_test() { next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); - let pox_info = get_pox_info(&http_origin); - info!("pox info after startup: {:?}", pox_info); - assert_eq!(pox_info.contract_id, format!("{}", boot_code_id("pox-2", false))); - - // let's query the spender's account: - assert_eq!(get_balance(&http_origin, &spender_addr), 100300); - // okay, let's send a pre-stx op. let pre_stx_op = PreStxOp { output: spender_stx_addr.clone(), @@ -1852,8 +1837,6 @@ fn stx_delegate_btc_integration_test() { next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); // let's fire off our delegate op. - let recipient_sk = StacksPrivateKey::new(); - let recipient_addr = to_addr(&recipient_sk); let del_stx_op = DelegateStxOp { sender: spender_stx_addr.clone(), delegate_to: recipient_addr.clone(), @@ -1868,7 +1851,6 @@ fn stx_delegate_btc_integration_test() { }; let mut spender_signer = BurnchainOpSigner::new(spender_sk.clone(), false); - assert!( btc_regtest_controller .submit_operation( @@ -1880,48 +1862,40 @@ fn stx_delegate_btc_integration_test() { .is_some(), "Delegate operation should submit successfully" ); - // should be elected in the same block as the transfer, so balances should be unchanged. - next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); - assert_eq!(get_balance(&http_origin, &spender_addr), 100300); - assert_eq!(get_balance(&http_origin, &recipient_addr), 0); - // this block should process the delegation, after which the balaces should be unchanged + // the second block should process the delegation, after which the balaces should be unchanged + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); assert_eq!(get_balance(&http_origin, &spender_addr), 100300); - assert_eq!(get_balance(&http_origin, &recipient_addr), 0); + assert_eq!(get_balance(&http_origin, &recipient_addr), 300); + let sort_height = channel.get_sortitions_processed(); let tx = make_contract_call( - &spender_2_sk, + &recipient_sk, 0, - 262, + 293, &StacksAddress::from_string("ST000000000000000000002AMW42H").unwrap(), "pox-2", - "stack-stx", + "delegate-stack-stx", &[ - Value::UInt(500), + Value::Principal(spender_addr.clone()), + Value::UInt(100_000), execute( &format!("{{ hashbytes: 0x{}, version: 0x00 }}", pox_pubkey_hash), ClarityVersion::Clarity2, ) .unwrap() .unwrap(), - Value::UInt(10), + Value::UInt(sort_height as u128), Value::UInt(6), ], ); - // okay, let's push that stacking transaction! + // push the stacking transaction submit_tx(&http_origin, &tx); - let pox_info = get_pox_info(&http_origin); - // now let's mine until the next reward cycle starts ... - // while sort_height < ((pox_info.current_cycle.id as u32 * pox_constants.reward_cycle_length) + 1).into() { - // next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); - // sort_height = channel.get_sortitions_processed(); - // eprintln!("Sort height: {}", sort_height); - // } - + // let's mine until the next reward cycle starts ... next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); @@ -1929,8 +1903,7 @@ fn stx_delegate_btc_integration_test() { next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); let account = get_account(&http_origin, &spender_stx_addr); - assert_eq!(account.locked, 500); - + assert_eq!(account.locked, 100_000); let blocks = test_observer::get_blocks(); for block in blocks.iter() { @@ -1946,7 +1919,7 @@ fn stx_delegate_btc_integration_test() { let name_data = name_field["Sequence"]["String"]["ASCII"]["data"].as_array().unwrap(); let ascii_vec = name_data.iter().map(|num| num.as_u64().unwrap() as u8).collect(); let name = String::from_utf8(ascii_vec).unwrap(); - assert_eq!(name, "delegate-stx"); + assert!(name == "delegate-stack-stx" || name == "delegate-stx"); } } } From 40ce97bc6b83338bd5fc49ab8247a5a50b7f22e1 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 18 Jan 2023 17:07:10 -0500 Subject: [PATCH 3/7] cleaned up tests --- src/chainstate/coordinator/tests.rs | 6 --- src/clarity_vm/special.rs | 39 ++----------------- .../src/tests/neon_integrations.rs | 22 ++++++++--- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/src/chainstate/coordinator/tests.rs b/src/chainstate/coordinator/tests.rs index 022dce5e6..89608a877 100644 --- a/src/chainstate/coordinator/tests.rs +++ b/src/chainstate/coordinator/tests.rs @@ -3548,10 +3548,6 @@ fn test_delegate_stx_btc_ops() { &second_del, ); - if ix == 10 { - - } - // Check that the effects of the delegate stx op sent when ix==10 // are materialized for ix=11... (we check that the // changes endure for the following blocks) @@ -3586,8 +3582,6 @@ fn test_delegate_stx_btc_ops() { "The second delegation should not be active" ); } - - } } diff --git a/src/clarity_vm/special.rs b/src/clarity_vm/special.rs index 4afcd965f..6bb58ed49 100644 --- a/src/clarity_vm/special.rs +++ b/src/clarity_vm/special.rs @@ -262,7 +262,8 @@ fn get_stacker(sender: &PrincipalData, function_name: &str, args: &[Value]) -> V } } -/// Craft the code snippet to evaluate an event-info for a stack-* or a delegate-stack-* function +/// Craft the code snippet to evaluate an event-info for a stack-* function, +/// a delegate-stack-* function, or for delegate-stx fn create_event_info_stack_or_delegate_code( sender: &PrincipalData, function_name: &str, @@ -322,39 +323,6 @@ fn create_event_info_aggregation_code(function_name: &str) -> String { ) } -/// Craft the code snippet to evaluate an event-info for the delegate-stx function -fn create_event_info_delegation_code( - sender: &PrincipalData, - function_name: &str, - args: &[Value], -) -> String { - format!( - r#" - (let ( - (stacker '{stacker}) - (func-name "{func_name}") - (stacker-info (stx-account stacker)) - (total-balance (stx-get-balance stacker)) - ) - {{ - ;; Function name - name: func-name, - ;; The principal of the stacker - stacker: stacker, - ;; The current available balance - balance: total-balance, - ;; The amount of locked STX - locked: (get locked stacker-info), - ;; The burnchain block height of when the tokens unlock. Zero if no tokens are locked. - burnchain-unlock-height: (get unlock-height stacker-info), - }} - ) - "#, - stacker = get_stacker(sender, function_name, args), - func_name = function_name - ) -} - /// Craft the code snippet to generate the method-specific `data` payload fn create_event_info_data_code(function_name: &str, args: &[Value]) -> String { match function_name { @@ -625,8 +593,7 @@ fn synthesize_pox_2_event_info( "stack-aggregation-commit" | "stack-aggregation-commit-indexed" | "stack-aggregation-increase" => Some(create_event_info_aggregation_code(function_name)), - "delegate-stx" => Some(create_event_info_delegation_code(sender, function_name, args)), - // use create_event_info_stack_or_delegate_code + "delegate-stx" => Some(create_event_info_stack_or_delegate_code(sender, function_name, args)), _ => None, }; diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 524b5b22d..f7b756362 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1795,13 +1795,8 @@ fn stx_delegate_btc_integration_test() { // give the run loop some time to start up! wait_for_runloop(&blocks_processed); - // first block wakes up the run loop next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); - - // first block will hold our VRF registration next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); - - // second block will be the first mined Stacks block next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); test_observer::clear(); @@ -1836,6 +1831,7 @@ fn stx_delegate_btc_integration_test() { ); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + // let's fire off our delegate op. let del_stx_op = DelegateStxOp { sender: spender_stx_addr.clone(), @@ -1870,6 +1866,7 @@ fn stx_delegate_btc_integration_test() { assert_eq!(get_balance(&http_origin, &spender_addr), 100300); assert_eq!(get_balance(&http_origin, &recipient_addr), 300); + // send a delegate-stack-stx transaction let sort_height = channel.get_sortitions_processed(); let tx = make_contract_call( &recipient_sk, @@ -1902,9 +1899,12 @@ fn stx_delegate_btc_integration_test() { next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + // check the locked amount for the spender account let account = get_account(&http_origin, &spender_stx_addr); assert_eq!(account.locked, 100_000); + let mut delegate_stack_stx_found = false; + let mut delegate_stx_found = false; let blocks = test_observer::get_blocks(); for block in blocks.iter() { let events = block.get("events").unwrap().as_array().unwrap(); @@ -1912,17 +1912,27 @@ fn stx_delegate_btc_integration_test() { let event_type = event.get("type").unwrap().as_str().unwrap(); if event_type == "contract_event" { let contract_event = event.get("contract_event").unwrap().as_object().unwrap(); + + // Check that it is a print event let sub_type = contract_event.get("topic").unwrap().as_str().unwrap(); assert_eq!(sub_type, "print"); + // Ensure that the function name is as expected + // This verifies that there were print events for delegate-stack-stx and delegate-stx let name_field = &contract_event["value"]["Response"]["data"]["Tuple"]["data_map"]["name"]; let name_data = name_field["Sequence"]["String"]["ASCII"]["data"].as_array().unwrap(); let ascii_vec = name_data.iter().map(|num| num.as_u64().unwrap() as u8).collect(); let name = String::from_utf8(ascii_vec).unwrap(); - assert!(name == "delegate-stack-stx" || name == "delegate-stx"); + if name == "delegate-stack-stx" { + delegate_stack_stx_found = true; + } else if name == "delegate-stx" { + delegate_stx_found = true; + } } } } + assert!(delegate_stx_found); + assert!(delegate_stack_stx_found); test_observer::clear(); channel.stop_chains_coordinator(); From 3d741db7fb4787093d9d883ada7e365e47a9e33a Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 18 Jan 2023 17:32:06 -0500 Subject: [PATCH 4/7] added integration test to yml file --- .github/workflows/bitcoin-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index 6879c3fee..c42ece0f1 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -47,6 +47,7 @@ jobs: - tests::neon_integrations::bitcoind_integration_test - tests::neon_integrations::liquid_ustx_integration - tests::neon_integrations::stx_transfer_btc_integration_test + - tests::neon_integrations::stx_delegate_btc_integration_test - tests::neon_integrations::bitcoind_forking_test - tests::neon_integrations::should_fix_2771 - tests::neon_integrations::pox_integration_test From 105700543f1d037e91f1c423f01a071a1e886256 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Thu, 19 Jan 2023 14:52:30 -0500 Subject: [PATCH 5/7] ran fmt + fized non-test build --- src/chainstate/stacks/boot/pox_2_tests.rs | 6 ++++- src/clarity_vm/special.rs | 10 ++++--- .../burnchains/bitcoin_regtest_controller.rs | 16 ++++++++++- .../src/tests/neon_integrations.rs | 27 +++++++++++++------ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/chainstate/stacks/boot/pox_2_tests.rs b/src/chainstate/stacks/boot/pox_2_tests.rs index 3ccf25676..ab9cf9ad4 100644 --- a/src/chainstate/stacks/boot/pox_2_tests.rs +++ b/src/chainstate/stacks/boot/pox_2_tests.rs @@ -1555,7 +1555,11 @@ fn delegate_stack_increase() { "(err 18)" ); - let delegate_stx_tx = &alice_txs.get(&success_alice_delegation).unwrap().clone().events[0]; + let delegate_stx_tx = &alice_txs + .get(&success_alice_delegation) + .unwrap() + .clone() + .events[0]; let delegate_stx_op_data = HashMap::from([ ("pox-addr", Value::none()), ("amount-ustx", Value::UInt(10230000000000)), diff --git a/src/clarity_vm/special.rs b/src/clarity_vm/special.rs index 6bb58ed49..15eb77842 100644 --- a/src/clarity_vm/special.rs +++ b/src/clarity_vm/special.rs @@ -257,7 +257,9 @@ fn handle_pox_v1_api_contract_call( /// - for delegate stacking functions, it's the first argument fn get_stacker(sender: &PrincipalData, function_name: &str, args: &[Value]) -> Value { match function_name { - "stack-stx" | "stack-increase" | "stack-extend" | "delegate-stx" => Value::Principal(sender.clone()), + "stack-stx" | "stack-increase" | "stack-extend" | "delegate-stx" => { + Value::Principal(sender.clone()) + } _ => args[0].clone(), } } @@ -558,7 +560,7 @@ fn create_event_info_data_code(function_name: &str, args: &[Value]) -> String { until_burn_height = &args[2], pox_addr = &args[3], ) - }, + } _ => format!("{{ data: {{ unimplemented: true }} }}"), } } @@ -585,7 +587,8 @@ fn synthesize_pox_2_event_info( | "stack-extend" | "delegate-stack-extend" | "stack-increase" - | "delegate-stack-increase" => Some(create_event_info_stack_or_delegate_code( + | "delegate-stack-increase" + | "delegate-stx" => Some(create_event_info_stack_or_delegate_code( sender, function_name, args, @@ -593,7 +596,6 @@ fn synthesize_pox_2_event_info( "stack-aggregation-commit" | "stack-aggregation-commit-indexed" | "stack-aggregation-increase" => Some(create_event_info_aggregation_code(function_name)), - "delegate-stx" => Some(create_event_info_stack_or_delegate_code(sender, function_name, args)), _ => None, }; diff --git a/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs b/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs index cbd958c52..06fb637a5 100644 --- a/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs +++ b/testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs @@ -38,7 +38,10 @@ use stacks::burnchains::{ }; use stacks::burnchains::{Burnchain, BurnchainParameters}; use stacks::chainstate::burn::db::sortdb::SortitionDB; -use stacks::chainstate::burn::operations::{BlockstackOperationType, DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, TransferStxOp, UserBurnSupportOp}; +use stacks::chainstate::burn::operations::{ + BlockstackOperationType, DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, + TransferStxOp, UserBurnSupportOp, +}; use stacks::chainstate::coordinator::comm::CoordinatorChannels; #[cfg(test)] use stacks::chainstate::stacks::address::PoxAddress; @@ -831,6 +834,17 @@ impl BitcoinRegtestController { unimplemented!() } + #[cfg(not(test))] + fn build_delegate_stacks_tx( + &mut self, + _epoch_id: StacksEpochId, + _payload: DelegateStxOp, + _signer: &mut BurnchainOpSigner, + _utxo: Option, + ) -> Option { + unimplemented!() + } + #[cfg(test)] pub fn submit_manual( &mut self, diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index f7b756362..02a313378 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -16,7 +16,9 @@ use rusqlite::types::ToSql; use stacks::burnchains::bitcoin::address::{BitcoinAddress, LegacyBitcoinAddressType}; use stacks::burnchains::bitcoin::BitcoinNetworkType; use stacks::burnchains::Txid; -use stacks::chainstate::burn::operations::{BlockstackOperationType, DelegateStxOp, PreStxOp, TransferStxOp}; +use stacks::chainstate::burn::operations::{ + BlockstackOperationType, DelegateStxOp, PreStxOp, TransferStxOp, +}; use stacks::chainstate::coordinator::comm::CoordinatorChannels; use stacks::clarity_cli::vm_execute as execute; use stacks::codec::StacksMessageCodec; @@ -1703,7 +1705,7 @@ fn stx_delegate_btc_integration_test() { let pox_pubkey = Secp256k1PublicKey::from_hex( "02f006a09b59979e2cb8449f58076152af6b124aa29b948a3714b8d5f15aa94ede", ) - .unwrap(); + .unwrap(); let pox_pubkey_hash = bytes_to_hex( &Hash160::from_node_public_key(&pox_pubkey) .to_bytes() @@ -1777,7 +1779,10 @@ fn stx_delegate_btc_integration_test() { burnchain_config.pox_constants = pox_constants.clone(); let mut btc_regtest_controller = BitcoinRegtestController::with_burnchain( - conf.clone(), None, Some(burnchain_config.clone()), None + conf.clone(), + None, + Some(burnchain_config.clone()), + None, ); let http_origin = format!("http://{}", &conf.node.rpc_bind); @@ -1882,8 +1887,8 @@ fn stx_delegate_btc_integration_test() { &format!("{{ hashbytes: 0x{}, version: 0x00 }}", pox_pubkey_hash), ClarityVersion::Clarity2, ) - .unwrap() - .unwrap(), + .unwrap() + .unwrap(), Value::UInt(sort_height as u128), Value::UInt(6), ], @@ -1919,9 +1924,15 @@ fn stx_delegate_btc_integration_test() { // Ensure that the function name is as expected // This verifies that there were print events for delegate-stack-stx and delegate-stx - let name_field = &contract_event["value"]["Response"]["data"]["Tuple"]["data_map"]["name"]; - let name_data = name_field["Sequence"]["String"]["ASCII"]["data"].as_array().unwrap(); - let ascii_vec = name_data.iter().map(|num| num.as_u64().unwrap() as u8).collect(); + let name_field = + &contract_event["value"]["Response"]["data"]["Tuple"]["data_map"]["name"]; + let name_data = name_field["Sequence"]["String"]["ASCII"]["data"] + .as_array() + .unwrap(); + let ascii_vec = name_data + .iter() + .map(|num| num.as_u64().unwrap() as u8) + .collect(); let name = String::from_utf8(ascii_vec).unwrap(); if name == "delegate-stack-stx" { delegate_stack_stx_found = true; From 349f3f641d41695d7cddc8b02144bdb22ae98496 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 20 Jan 2023 13:22:55 -0500 Subject: [PATCH 6/7] fixed failing unit test --- src/chainstate/stacks/db/blocks.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/chainstate/stacks/db/blocks.rs b/src/chainstate/stacks/db/blocks.rs index ee939de09..808776c21 100644 --- a/src/chainstate/stacks/db/blocks.rs +++ b/src/chainstate/stacks/db/blocks.rs @@ -12131,7 +12131,10 @@ pub mod test { .collect(); init_balances.push((addr.to_account_principal(), initial_balance)); peer_config.initial_balances = init_balances; - peer_config.epochs = Some(StacksEpoch::unit_test_2_1(0)); + let mut epochs = StacksEpoch::unit_test_2_1(0); + let num_epochs = epochs.len(); + epochs[num_epochs - 1].block_limit.runtime = 10_000_000; + peer_config.epochs = Some(epochs); peer_config.burnchain.pox_constants.v1_unlock_height = 26; let mut peer = TestPeer::new(peer_config); From 5c9c72a9db732758dd3d4600c6c8392909599e4a Mon Sep 17 00:00:00 2001 From: Greg Date: Tue, 24 Jan 2023 09:19:11 -0500 Subject: [PATCH 7/7] 1) remove .idea directory, added by mistake, 2) add .idea to .gitignore --- .gitignore | 2 ++ .idea/.gitignore | 3 --- .idea/modules.xml | 8 -------- .idea/stacks-blockchain.iml | 18 ------------------ .idea/vcs.xml | 6 ------ 5 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/modules.xml delete mode 100644 .idea/stacks-blockchain.iml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 3ed73b181..5069c4712 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,5 @@ lcov.info # codecov coverage.lcov + +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521a..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 717be4427..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/stacks-blockchain.iml b/.idea/stacks-blockchain.iml deleted file mode 100644 index 37f024274..000000000 --- a/.idea/stacks-blockchain.iml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file