From 41c274dfee170cce57072bb5deceafcee4b4dcdf Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Thu, 2 Jan 2025 11:55:42 -0500 Subject: [PATCH 1/4] Allow token transfers in tenure change blocks due to phantom unlock txs Signed-off-by: Jacinta Ferrant --- testnet/stacks-node/src/tests/nakamoto_integrations.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 13923a847a..25d7c5606c 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -241,7 +241,7 @@ impl TestSigningChannel { /// Assert that the block events captured by the test observer /// all match the miner heuristic of *exclusively* including the -/// tenure change transaction in tenure changing blocks. +/// tenure change transaction and token transfers in tenure changing blocks. pub fn check_nakamoto_empty_block_heuristics() { let blocks = test_observer::get_blocks(); for block in blocks.iter() { @@ -257,10 +257,12 @@ pub fn check_nakamoto_empty_block_heuristics() { let only_coinbase_and_tenure_change = txs.iter().all(|tx| { matches!( tx.payload, - TransactionPayload::TenureChange(_) | TransactionPayload::Coinbase(..) + TransactionPayload::TenureChange(_) + | TransactionPayload::Coinbase(..) + | TransactionPayload::TokenTransfer(..) ) }); - assert!(only_coinbase_and_tenure_change, "Nakamoto blocks with a tenure change in them should only have coinbase or tenure changes"); + assert!(only_coinbase_and_tenure_change, "Nakamoto blocks with a tenure change in them should only have coinbase, tenure changes, or token transfers."); } } } From b84e753ab44c752c544695475e5115b882a0c095 Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Thu, 2 Jan 2025 14:34:31 -0500 Subject: [PATCH 2/4] Do not allow token transfers and filter out phantom txs from parse_transactions fn Signed-off-by: Jacinta Ferrant --- .../stacks-node/src/tests/nakamoto_integrations.rs | 8 +++----- testnet/stacks-node/src/tests/neon_integrations.rs | 13 +++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index 25d7c5606c..13923a847a 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -241,7 +241,7 @@ impl TestSigningChannel { /// Assert that the block events captured by the test observer /// all match the miner heuristic of *exclusively* including the -/// tenure change transaction and token transfers in tenure changing blocks. +/// tenure change transaction in tenure changing blocks. pub fn check_nakamoto_empty_block_heuristics() { let blocks = test_observer::get_blocks(); for block in blocks.iter() { @@ -257,12 +257,10 @@ pub fn check_nakamoto_empty_block_heuristics() { let only_coinbase_and_tenure_change = txs.iter().all(|tx| { matches!( tx.payload, - TransactionPayload::TenureChange(_) - | TransactionPayload::Coinbase(..) - | TransactionPayload::TokenTransfer(..) + TransactionPayload::TenureChange(_) | TransactionPayload::Coinbase(..) ) }); - assert!(only_coinbase_and_tenure_change, "Nakamoto blocks with a tenure change in them should only have coinbase, tenure changes, or token transfers."); + assert!(only_coinbase_and_tenure_change, "Nakamoto blocks with a tenure change in them should only have coinbase or tenure changes"); } } } diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index fc363d3db8..2d82b69214 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -194,9 +194,10 @@ pub mod test_observer { use std::sync::Mutex; use std::thread; + use clarity::boot_util::boot_code_addr; use stacks::chainstate::stacks::boot::RewardSet; use stacks::chainstate::stacks::events::StackerDBChunksEvent; - use stacks::chainstate::stacks::StacksTransaction; + use stacks::chainstate::stacks::{StacksTransaction, TransactionPayload}; use stacks::codec::StacksMessageCodec; use stacks::config::{EventKeyType, EventObserverConfig}; use stacks::net::api::postblock_proposal::BlockValidateResponse; @@ -578,7 +579,7 @@ pub mod test_observer { PROPOSAL_RESPONSES.lock().unwrap().clear(); } - /// Parse the StacksTransactions from a block (does not include burn ops) + /// Parse the StacksTransactions from a block (does not include burn ops or phantom txs) /// panics on any failures to parse pub fn parse_transactions(block: &serde_json::Value) -> Vec { block @@ -597,6 +598,14 @@ pub mod test_observer { let tx_bytes = hex_bytes(&tx_hex[2..]).unwrap(); let tx = StacksTransaction::consensus_deserialize(&mut tx_bytes.as_slice()).unwrap(); + + let mainnet_address = boot_code_addr(true).into(); + let testnet_address = boot_code_addr(false).into(); + if let TransactionPayload::TokenTransfer(address, amount, _) = &tx.payload { + if *address == mainnet_address || *address == testnet_address && *amount == 0 { + return None; + } + } Some(tx) }) .collect() From 33b4ee30051a226f3128148f9db9c4890d0dab7c Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Thu, 2 Jan 2025 14:45:09 -0500 Subject: [PATCH 3/4] Fix boot_code_addr call Signed-off-by: Jacinta Ferrant --- testnet/stacks-node/src/tests/neon_integrations.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 2d82b69214..17c36929d9 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -200,6 +200,7 @@ pub mod test_observer { use stacks::chainstate::stacks::{StacksTransaction, TransactionPayload}; use stacks::codec::StacksMessageCodec; use stacks::config::{EventKeyType, EventObserverConfig}; + use stacks::core::CHAIN_ID_MAINNET; use stacks::net::api::postblock_proposal::BlockValidateResponse; use stacks::util::hash::hex_bytes; use stacks_common::types::chainstate::StacksBlockId; @@ -599,10 +600,9 @@ pub mod test_observer { let tx = StacksTransaction::consensus_deserialize(&mut tx_bytes.as_slice()).unwrap(); - let mainnet_address = boot_code_addr(true).into(); - let testnet_address = boot_code_addr(false).into(); + let boot_address = boot_code_addr(tx.chain_id == CHAIN_ID_MAINNET).into(); if let TransactionPayload::TokenTransfer(address, amount, _) = &tx.payload { - if *address == mainnet_address || *address == testnet_address && *amount == 0 { + if *address == boot_address && *amount == 0 { return None; } } From 5e3134316cda4827003c0762ac7c702bb2fae8f7 Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Fri, 3 Jan 2025 12:02:28 -0500 Subject: [PATCH 4/4] CRC: add comment and add helper function is_phantom to StacksTransaction Signed-off-by: Jacinta Ferrant --- stackslib/src/chainstate/stacks/transaction.rs | 11 +++++++++++ testnet/stacks-node/src/tests/neon_integrations.rs | 14 +++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/stackslib/src/chainstate/stacks/transaction.rs b/stackslib/src/chainstate/stacks/transaction.rs index c45b212b68..b9eb3b5ac3 100644 --- a/stackslib/src/chainstate/stacks/transaction.rs +++ b/stackslib/src/chainstate/stacks/transaction.rs @@ -34,6 +34,7 @@ use crate::chainstate::stacks::{TransactionPayloadID, *}; use crate::codec::Error as CodecError; use crate::core::*; use crate::net::Error as net_error; +use crate::util_lib::boot::boot_code_addr; impl StacksMessageCodec for TransactionContractCall { fn consensus_serialize(&self, fd: &mut W) -> Result<(), codec_error> { @@ -1031,6 +1032,16 @@ impl StacksTransaction { _ => false, } } + + /// Is this a phantom transaction? + pub fn is_phantom(&self) -> bool { + let boot_address = boot_code_addr(self.is_mainnet()).into(); + if let TransactionPayload::TokenTransfer(address, amount, _) = &self.payload { + *address == boot_address && *amount == 0 + } else { + false + } + } } impl StacksTransactionSigner { diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 17c36929d9..fbe7421ab2 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -194,13 +194,11 @@ pub mod test_observer { use std::sync::Mutex; use std::thread; - use clarity::boot_util::boot_code_addr; use stacks::chainstate::stacks::boot::RewardSet; use stacks::chainstate::stacks::events::StackerDBChunksEvent; - use stacks::chainstate::stacks::{StacksTransaction, TransactionPayload}; + use stacks::chainstate::stacks::StacksTransaction; use stacks::codec::StacksMessageCodec; use stacks::config::{EventKeyType, EventObserverConfig}; - use stacks::core::CHAIN_ID_MAINNET; use stacks::net::api::postblock_proposal::BlockValidateResponse; use stacks::util::hash::hex_bytes; use stacks_common::types::chainstate::StacksBlockId; @@ -590,21 +588,19 @@ pub mod test_observer { .unwrap() .iter() .filter_map(|tx_json| { + // Filter out burn ops if let Some(burnchain_op_val) = tx_json.get("burnchain_op") { if !burnchain_op_val.is_null() { return None; } } + // Filter out phantom txs let tx_hex = tx_json.get("raw_tx").unwrap().as_str().unwrap(); let tx_bytes = hex_bytes(&tx_hex[2..]).unwrap(); let tx = StacksTransaction::consensus_deserialize(&mut tx_bytes.as_slice()).unwrap(); - - let boot_address = boot_code_addr(tx.chain_id == CHAIN_ID_MAINNET).into(); - if let TransactionPayload::TokenTransfer(address, amount, _) = &tx.payload { - if *address == boot_address && *amount == 0 { - return None; - } + if tx.is_phantom() { + return None; } Some(tx) })