Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add burnchain op type to the new_block event #168

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/chainstate/burn/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ pub struct UserBurnSupportOp {
pub burn_header_hash: BurnchainHeaderHash, // hash of burnchain block with this tx
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum BlockstackOperationType {
LeaderBlockCommit(LeaderBlockCommitOp),
DepositStx(DepositStxOp),
Expand Down
89 changes: 52 additions & 37 deletions src/chainstate/stacks/db/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4513,6 +4513,10 @@ impl StacksChainState {
Ok((applied, receipts))
}

/// NOTE: stacking ops are currently not processed by subnets nodes; thus, no
/// StackStx op receipts are added to the return. If anyone wants to again
/// process stacking ops, the receipt generation logic must be uncommented.
///
/// Process any Stacking-related bitcoin operations
/// that haven't been processed in this Stacks fork yet.
pub fn process_stacking_ops(
Expand Down Expand Up @@ -4548,7 +4552,7 @@ impl StacksChainState {
)
});
match result {
Ok((value, _, events)) => {
Ok((value, _, _events)) => {
if let Value::Response(ref resp) = value {
if !resp.committed {
debug!("StackStx burn op rejected by PoX contract.";
Expand All @@ -4562,19 +4566,19 @@ impl StacksChainState {
.expect("BUG: cost declined between executions");
cost_so_far = clarity_tx.cost_so_far();

let receipt = StacksTransactionReceipt {
transaction: TransactionOrigin::Burn(txid),
events,
result: value,
post_condition_aborted: false,
stx_burned: 0,
contract_analysis: None,
execution_cost,
microblock_header: None,
tx_index: 0,
};

all_receipts.push(receipt);
// let receipt = StacksTransactionReceipt {
// transaction: TransactionOrigin::Burn(BlockstackOperationType::StackStx(stack_stx_op)),
// events,
// result: value,
// post_condition_aborted: false,
// stx_burned: 0,
// contract_analysis: None,
// execution_cost,
// microblock_header: None,
// tx_index: 0,
// };

// all_receipts.push(receipt);
} else {
unreachable!(
"BUG: Non-response value returned by Stacking STX burnchain op"
Expand All @@ -4593,6 +4597,10 @@ impl StacksChainState {
all_receipts
}

/// NOTE: transfer stx ops are currently not processed by subnets nodes; thus, no
/// TransferStx op receipts are added to the return. If anyone wants to again
/// process transfer stx ops, the receipt generation logic must be uncommented.
///
/// Process any STX transfer bitcoin operations
/// that haven't been processed in this Stacks fork yet.
pub fn process_transfer_ops(
Expand All @@ -4617,17 +4625,18 @@ impl StacksChainState {
tx.run_stx_transfer(&sender.into(), &recipient.into(), transfered_ustx)
});
match result {
Ok((value, _, events)) => Some(StacksTransactionReceipt {
transaction: TransactionOrigin::Burn(txid),
events,
result: value,
post_condition_aborted: false,
stx_burned: 0,
contract_analysis: None,
execution_cost: ExecutionCost::zero(),
microblock_header: None,
tx_index: 0,
}),
Ok((_value, _, _events)) => None,
// Some(StacksTransactionReceipt {
// transaction: TransactionOrigin::Burn(BlockstackOperationType::TransferStx(transfer_stx_op)),
// events,
// result: value,
// post_condition_aborted: false,
// stx_burned: 0,
// contract_analysis: None,
// execution_cost: ExecutionCost::zero(),
// microblock_header: None,
// tx_index: 0,
// }),
Err(e) => {
info!("TransferStx burn op processing error.";
"error" => ?e,
Expand Down Expand Up @@ -4659,22 +4668,24 @@ impl StacksChainState {
amount,
sender,
..
} = deposit_stx_op;
} = &deposit_stx_op;
// call the corresponding deposit function in the hyperchains contract
let result = clarity_tx.connection().as_transaction(|tx| {
StacksChainState::account_credit(tx, &sender, amount as u64);
StacksChainState::account_credit(tx, &sender, *amount as u64);
StacksTransactionEvent::STXEvent(STXEventType::STXMintEvent(
STXMintEventData {
recipient: sender,
amount,
recipient: sender.clone(),
amount: *amount,
},
))
});
// deposits increment the STX liquidity in the layer 2
clarity_tx.increment_ustx_liquid_supply(amount);
clarity_tx.increment_ustx_liquid_supply(*amount);

Some(StacksTransactionReceipt {
transaction: TransactionOrigin::Burn(txid),
transaction: TransactionOrigin::Burn(
BlockstackOperationType::DepositStx(deposit_stx_op),
),
events: vec![result],
result: Value::okay_true(),
post_condition_aborted: false,
Expand Down Expand Up @@ -4710,14 +4721,14 @@ impl StacksChainState {
amount,
sender,
..
} = deposit_ft_op;
} = &deposit_ft_op;
// call the corresponding deposit function in the hyperchains contract
let result = clarity_tx.connection().as_transaction(|tx| {
tx.run_contract_call(
&sender.clone(),
&hc_contract_id,
&*hc_function_name,
&[Value::UInt(amount), Value::Principal(sender)],
&[Value::UInt(*amount), Value::Principal(sender.clone())],
|_, _| false,
)
});
Expand All @@ -4728,7 +4739,9 @@ impl StacksChainState {

match result {
Ok((value, _, events)) => Some(StacksTransactionReceipt {
transaction: TransactionOrigin::Burn(txid),
transaction: TransactionOrigin::Burn(BlockstackOperationType::DepositFt(
deposit_ft_op,
)),
events,
result: value,
post_condition_aborted: false,
Expand Down Expand Up @@ -4769,13 +4782,13 @@ impl StacksChainState {
id,
sender,
..
} = deposit_nft_op;
} = &deposit_nft_op;
let result = clarity_tx.connection().as_transaction(|tx| {
tx.run_contract_call(
&sender.clone(),
&hc_contract_id,
&*hc_function_name,
&[Value::UInt(id), Value::Principal(sender)],
&[Value::UInt(*id), Value::Principal(sender.clone())],
|_, _| false,
)
});
Expand All @@ -4786,7 +4799,9 @@ impl StacksChainState {

match result {
Ok((value, _, events)) => Some(StacksTransactionReceipt {
transaction: TransactionOrigin::Burn(txid),
transaction: TransactionOrigin::Burn(BlockstackOperationType::DepositNft(
deposit_nft_op,
)),
events,
result: value,
post_condition_aborted: false,
Expand Down
7 changes: 4 additions & 3 deletions src/chainstate/stacks/events.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::burnchains::Txid;
use crate::chainstate::burn::operations::BlockstackOperationType;
use crate::chainstate::stacks::StacksMicroblockHeader;
use crate::chainstate::stacks::StacksTransaction;
use crate::codec::StacksMessageCodec;
Expand All @@ -14,7 +15,7 @@ pub use clarity::vm::events::StacksTransactionEvent;
#[derive(Debug, Clone, PartialEq)]
pub enum TransactionOrigin {
Stacks(StacksTransaction),
Burn(Txid),
Burn(BlockstackOperationType),
}

impl From<StacksTransaction> for TransactionOrigin {
Expand All @@ -26,13 +27,13 @@ impl From<StacksTransaction> for TransactionOrigin {
impl TransactionOrigin {
pub fn txid(&self) -> Txid {
match self {
TransactionOrigin::Burn(txid) => txid.clone(),
TransactionOrigin::Burn(op) => op.txid(),
TransactionOrigin::Stacks(tx) => tx.txid(),
}
}
pub fn serialize_to_vec(&self) -> Vec<u8> {
match self {
TransactionOrigin::Burn(txid) => txid.as_bytes().to_vec(),
TransactionOrigin::Burn(op) => op.txid().as_bytes().to_vec(),
TransactionOrigin::Stacks(tx) => tx.txid().as_bytes().to_vec(),
}
}
Expand Down
12 changes: 9 additions & 3 deletions testnet/stacks-node/src/event_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use http_types::{Method, Request, Url};
use serde_json::json;

use stacks::burnchains::Txid;
use stacks::chainstate::burn::operations::BlockstackOperationType;
use stacks::chainstate::coordinator::BlockEventDispatcher;
use stacks::chainstate::stacks::db::StacksHeaderInfo;
use stacks::chainstate::stacks::events::{
Expand Down Expand Up @@ -49,6 +50,7 @@ struct ReceiptPayloadInfo<'a> {
raw_result: String,
raw_tx: String,
contract_interface_json: serde_json::Value,
burnchain_op: serde_json::Value,
}

const STATUS_RESP_TRUE: &str = "success";
Expand Down Expand Up @@ -195,12 +197,14 @@ impl EventObserver {
_ => unreachable!(), // Transaction results should always be a Value::Response type
};

let (txid, raw_tx) = match tx {
TransactionOrigin::Burn(txid) => (txid.to_string(), "00".to_string()),
let (txid, raw_tx, burnchain_op) = match tx {
TransactionOrigin::Burn(op) => {
(op.txid().to_string(), "00".to_string(), json!(op.clone()))
}
TransactionOrigin::Stacks(ref tx) => {
let txid = tx.txid().to_string();
let bytes = tx.serialize_to_vec();
(txid, bytes_to_hex(&bytes))
(txid, bytes_to_hex(&bytes), json!(null))
}
};

Expand All @@ -220,6 +224,7 @@ impl EventObserver {
raw_result,
raw_tx,
contract_interface_json,
burnchain_op,
}
}

Expand All @@ -237,6 +242,7 @@ impl EventObserver {
"raw_result": format!("0x{}", &receipt_payload_info.raw_result),
"raw_tx": format!("0x{}", &receipt_payload_info.raw_tx),
"contract_abi": receipt_payload_info.contract_interface_json,
"burnchain_op": receipt_payload_info.burnchain_op,
"execution_cost": receipt.execution_cost,
"microblock_sequence": receipt.microblock_header.as_ref().map(|x| x.sequence),
"microblock_hash": receipt.microblock_header.as_ref().map(|x| format!("0x{}", x.block_hash())),
Expand Down
76 changes: 76 additions & 0 deletions testnet/stacks-node/src/tests/l1_observer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use clarity::vm::Value;

use stacks::burnchains::Burnchain;
use stacks::chainstate::burn::db::sortdb::SortitionDB;
use stacks::chainstate::burn::operations::BlockstackOperationType;
use stacks::chainstate::stacks::events::{StacksTransactionReceipt, TransactionOrigin};
use stacks::chainstate::stacks::{
CoinbasePayload, StacksPrivateKey, StacksTransaction, TransactionAuth, TransactionPayload,
Expand Down Expand Up @@ -259,6 +260,36 @@ fn select_transactions_where(
return result;
}

/// Deserializes the `BlockstackOperationType` objects (non-null objects are produced by burn
/// transactions) from `blocks` and returns all those that match `test_fn`.
fn select_burn_transactions_where(
blocks: &Vec<serde_json::Value>,
test_fn: fn(&BlockstackOperationType) -> bool,
) -> Vec<BlockstackOperationType> {
let mut result = vec![];
for (block_idx, block) in blocks.iter().enumerate() {
let transactions = block.get("transactions").unwrap().as_array().unwrap();
for (tx_idx, tx) in transactions.iter().enumerate() {
let burnchain_op = tx.get("burnchain_op").unwrap();
let new_op: Option<BlockstackOperationType> =
serde_json::from_value(burnchain_op.clone()).unwrap();
if let Some(op) = new_op {
println!(
"select_burn_transactions_where considers: block_idx: {}, tx_idx: {}, tx: {:?}, parsed: {:?}",
block_idx, tx_idx, &tx, &op
);

let test_value = test_fn(&op);
if test_value {
result.push(op);
}
}
}
}

return result;
}

/// Uses MOCKNET_PRIVATE_KEY_1 to publish the hyperchains contract and supporting
/// trait contracts
pub fn publish_hc_contracts_to_l1(mut l1_nonce: u64, config: &Config, miner: PrincipalData) -> u64 {
Expand Down Expand Up @@ -762,6 +793,18 @@ fn l1_deposit_and_withdraw_asset_integration_test() {
.to_string();
let amount = Value::deserialize(&result, &TypeSignature::UIntType);
assert_eq!(amount, Value::UInt(1));

// Check the burnchain ops
let block_data = test_observer::get_blocks();
let burn_deposit_ft_tx = select_burn_transactions_where(&block_data, |op| {
if let BlockstackOperationType::DepositFt(_) = op {
true
} else {
false
}
});
assert_eq!(burn_deposit_ft_tx.len(), 1);

// Check that the user owns the NFT on the hyperchain now
let res = call_read_only(
&l2_rpc_origin,
Expand All @@ -787,6 +830,17 @@ fn l1_deposit_and_withdraw_asset_integration_test() {
Value::some(Value::Principal(user_addr.into())).unwrap()
);

// Check the burnchain ops
let block_data = test_observer::get_blocks();
let burn_deposit_nft_tx = select_burn_transactions_where(&block_data, |op| {
if let BlockstackOperationType::DepositNft(_) = op {
true
} else {
false
}
});
assert_eq!(burn_deposit_nft_tx.len(), 1);

// Check that the user does not own the FT on the L1
let res = call_read_only(
&l1_rpc_origin,
Expand Down Expand Up @@ -1405,6 +1459,17 @@ fn l1_deposit_and_withdraw_stx_integration_test() {
(l1_starting_account_balance - default_fee * l1_nonce - 1) as u128
);

// Check the burnchain ops
let block_data = test_observer::get_blocks();
let burn_deposit_stx_tx = select_burn_transactions_where(&block_data, |op| {
if let BlockstackOperationType::DepositStx(_) = op {
true
} else {
false
}
});
assert_eq!(burn_deposit_stx_tx.len(), 1);

// Call the withdraw stx function on the L2 from unauthorized user
let l2_withdraw_stx_tx_unauth = make_contract_call(
&MOCKNET_PRIVATE_KEY_3,
Expand Down Expand Up @@ -2131,6 +2196,17 @@ fn nft_deposit_and_withdraw_integration_test() {
);
assert_eq!(owner, Value::okay(Value::none()).unwrap());

// Check the burnchain ops
let block_data = test_observer::get_blocks();
let burn_deposit_tx = select_burn_transactions_where(&block_data, |op| {
if let BlockstackOperationType::DepositNft(_) = op {
true
} else {
false
}
});
assert_eq!(burn_deposit_tx.len(), 1);

// Withdraw the L1 native NFT from the L2 (with `nft-withdraw?`)
let l2_withdraw_nft_tx = make_contract_call(
&MOCKNET_PRIVATE_KEY_1,
Expand Down