From 57436f0dcb11762471218d0eb5b0ecbd125e70eb Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 13 Jan 2023 14:45:29 -0500 Subject: [PATCH 01/11] added burnchain_op field to transaction event payload + test --- src/chainstate/stacks/db/blocks.rs | 36 +++++++++---------- src/chainstate/stacks/events.rs | 7 ++-- testnet/stacks-node/src/event_dispatcher.rs | 11 ++++-- .../src/tests/neon_integrations.rs | 25 +++++++++++++ 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/chainstate/stacks/db/blocks.rs b/src/chainstate/stacks/db/blocks.rs index a8bb8f2289..6807f3ef5b 100644 --- a/src/chainstate/stacks/db/blocks.rs +++ b/src/chainstate/stacks/db/blocks.rs @@ -4920,23 +4920,23 @@ impl StacksChainState { txid, burn_header_hash, .. - } = stack_stx_op; + } = &stack_stx_op; let result = clarity_tx.connection().as_transaction(|tx| { tx.run_contract_call( - &sender.into(), + &sender.clone().into(), None, &boot_code_id(active_pox_contract, mainnet), "stack-stx", &[ - Value::UInt(stacked_ustx), + Value::UInt(*stacked_ustx), // this .expect() should be unreachable since we coerce the hash mode when // we parse the StackStxOp from a burnchain transaction reward_addr .as_clarity_tuple() .expect("FATAL: stack-stx operation has no hash mode") .into(), - Value::UInt(u128::from(block_height)), - Value::UInt(u128::from(num_cycles)), + Value::UInt(u128::from(*block_height)), + Value::UInt(u128::from(*num_cycles)), ], |_, _| false, ) @@ -4958,7 +4958,7 @@ impl StacksChainState { .expect("BUG: cost declined between executions"); let receipt = StacksTransactionReceipt { - transaction: TransactionOrigin::Burn(txid), + transaction: TransactionOrigin::Burn(BlockstackOperationType::StackStx(stack_stx_op)), events, result: value, post_condition_aborted: false, @@ -5009,20 +5009,20 @@ impl StacksChainState { burn_header_hash, memo, .. - } = transfer_stx_op; + } = &transfer_stx_op; let result = clarity_tx.connection().as_transaction(|tx| { tx.run_stx_transfer( - &sender.into(), - &recipient.into(), - transfered_ustx, - &BuffData { data: memo }, + &sender.clone().into(), + &recipient.clone().into(), + *transfered_ustx, + &BuffData { data: memo.clone() }, ) }); match result { Ok((value, _, events)) => { debug!("Processed TransferStx burnchain op"; "transfered_ustx" => transfered_ustx, "sender" => %sender, "recipient" => %recipient, "txid" => %txid); Some(StacksTransactionReceipt { - transaction: TransactionOrigin::Burn(txid), + transaction: TransactionOrigin::Burn(BlockstackOperationType::TransferStx(transfer_stx_op)), events, result: value, post_condition_aborted: false, @@ -5071,7 +5071,7 @@ impl StacksChainState { txid, burn_header_hash, .. - } = delegate_stx_op; + } = &delegate_stx_op; let reward_addr_val = if let Some((_, addr)) = &reward_addr { // this .expect() should be unreachable since we coerce the hash mode when // we parse the DelegateStxOp from a burnchain transaction @@ -5087,20 +5087,20 @@ impl StacksChainState { }; let until_burn_height_val = if let Some(height) = until_burn_height { - Value::some(Value::UInt(u128::from(height))) + Value::some(Value::UInt(u128::from(*height))) .expect("FATAL: construction of an optional uint Clarity value should succeed.") } else { Value::none() }; let result = clarity_tx.connection().as_transaction(|tx| { tx.run_contract_call( - &sender.into(), + &sender.clone().into(), None, &boot_code_id(active_pox_contract, mainnet), "delegate-stx", &[ - Value::UInt(delegated_ustx), - Value::Principal(delegate_to.into()), + Value::UInt(*delegated_ustx), + Value::Principal(delegate_to.clone().into()), until_burn_height_val, reward_addr_val, ], @@ -5126,7 +5126,7 @@ impl StacksChainState { .expect("BUG: cost declined between executions"); let receipt = StacksTransactionReceipt { - transaction: TransactionOrigin::Burn(txid), + transaction: TransactionOrigin::Burn(BlockstackOperationType::DelegateStx(delegate_stx_op)), events, result: value, post_condition_aborted: false, diff --git a/src/chainstate/stacks/events.rs b/src/chainstate/stacks/events.rs index 987d88bdd2..062d284986 100644 --- a/src/chainstate/stacks/events.rs +++ b/src/chainstate/stacks/events.rs @@ -11,11 +11,12 @@ use clarity::vm::types::{ }; pub use clarity::vm::events::StacksTransactionEvent; +use crate::chainstate::burn::operations::BlockstackOperationType; #[derive(Debug, Clone, PartialEq)] pub enum TransactionOrigin { Stacks(StacksTransaction), - Burn(Txid), + Burn(BlockstackOperationType), } impl From for TransactionOrigin { @@ -27,7 +28,7 @@ impl From 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(), } } @@ -35,7 +36,7 @@ impl TransactionOrigin { /// a database pub fn serialize_to_dbstring(&self) -> String { match self { - TransactionOrigin::Burn(txid) => format!("BTC({})", txid), + TransactionOrigin::Burn(op) => format!("BTC({})", op.txid()), TransactionOrigin::Stacks(tx) => to_hex(&tx.serialize_to_vec()), } } diff --git a/testnet/stacks-node/src/event_dispatcher.rs b/testnet/stacks-node/src/event_dispatcher.rs index b820591ba3..d32132c486 100644 --- a/testnet/stacks-node/src/event_dispatcher.rs +++ b/testnet/stacks-node/src/event_dispatcher.rs @@ -46,6 +46,7 @@ struct ReceiptPayloadInfo<'a> { raw_result: String, raw_tx: String, contract_interface_json: serde_json::Value, + burnchain_op_json: serde_json::Value, } const STATUS_RESP_TRUE: &str = "success"; @@ -214,12 +215,14 @@ impl EventObserver { } }; - 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)) } }; @@ -239,6 +242,7 @@ impl EventObserver { raw_result, raw_tx, contract_interface_json, + burnchain_op_json: burnchain_op, } } @@ -256,6 +260,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_json, "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())), diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index cd8161e5cd..859f210140 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1505,6 +1505,12 @@ fn stx_transfer_btc_integration_test() { let (mut conf, _miner_account) = neon_integration_test_conf(); + test_observer::spawn(); + conf.events_observers.push(EventObserverConfig { + endpoint: format!("localhost:{}", test_observer::EVENT_OBSERVER_PORT), + events_keys: vec![EventKeyType::AnyEvent], + }); + conf.initial_balances.push(InitialBalance { address: spender_addr.clone(), amount: 100300, @@ -1547,6 +1553,8 @@ fn stx_transfer_btc_integration_test() { // second block will be the first mined Stacks block next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + test_observer::clear(); + // let's query the spender's account: assert_eq!(get_balance(&http_origin, &spender_addr), 100300); @@ -1686,6 +1694,23 @@ fn stx_transfer_btc_integration_test() { assert_eq!(get_balance(&http_origin, &recipient_addr), 200_000); assert_eq!(get_balance(&http_origin, &spender_2_addr), 300); + let mut found_btc_tx = false; + let blocks = test_observer::get_blocks(); + for block in blocks { + let transactions = block.get("transactions").unwrap().as_array().unwrap(); + for tx in transactions.iter() { + let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap(); + if raw_tx == "0x00" { + let burnchain_op = tx.get("burnchain_op").unwrap().as_object().unwrap(); + if !burnchain_op.contains_key("TransferStx") { + panic!("unexpected btc transaction type"); + } + found_btc_tx = true; + } + } + } + assert!(found_btc_tx); + channel.stop_chains_coordinator(); } From 06cad4adbba9a3992baf688952d4375820c6a698 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 20 Jan 2023 15:50:29 -0500 Subject: [PATCH 02/11] changed parameter name --- src/chainstate/stacks/db/blocks.rs | 18 +++++++++++------- src/chainstate/stacks/events.rs | 2 +- testnet/stacks-node/src/event_dispatcher.rs | 10 +++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/chainstate/stacks/db/blocks.rs b/src/chainstate/stacks/db/blocks.rs index 6807f3ef5b..f800fff983 100644 --- a/src/chainstate/stacks/db/blocks.rs +++ b/src/chainstate/stacks/db/blocks.rs @@ -4958,7 +4958,9 @@ impl StacksChainState { .expect("BUG: cost declined between executions"); let receipt = StacksTransactionReceipt { - transaction: TransactionOrigin::Burn(BlockstackOperationType::StackStx(stack_stx_op)), + transaction: TransactionOrigin::Burn( + BlockstackOperationType::StackStx(stack_stx_op), + ), events, result: value, post_condition_aborted: false, @@ -5009,13 +5011,13 @@ impl StacksChainState { burn_header_hash, memo, .. - } = &transfer_stx_op; + } = transfer_stx_op.clone(); let result = clarity_tx.connection().as_transaction(|tx| { tx.run_stx_transfer( - &sender.clone().into(), - &recipient.clone().into(), - *transfered_ustx, - &BuffData { data: memo.clone() }, + &sender.into(), + &recipient.into(), + transfered_ustx, + &BuffData { data: memo }, ) }); match result { @@ -5126,7 +5128,9 @@ impl StacksChainState { .expect("BUG: cost declined between executions"); let receipt = StacksTransactionReceipt { - transaction: TransactionOrigin::Burn(BlockstackOperationType::DelegateStx(delegate_stx_op)), + transaction: TransactionOrigin::Burn( + BlockstackOperationType::DelegateStx(delegate_stx_op), + ), events, result: value, post_condition_aborted: false, diff --git a/src/chainstate/stacks/events.rs b/src/chainstate/stacks/events.rs index 062d284986..15f79ddd03 100644 --- a/src/chainstate/stacks/events.rs +++ b/src/chainstate/stacks/events.rs @@ -10,8 +10,8 @@ use clarity::vm::types::{ AssetIdentifier, PrincipalData, QualifiedContractIdentifier, StandardPrincipalData, Value, }; -pub use clarity::vm::events::StacksTransactionEvent; use crate::chainstate::burn::operations::BlockstackOperationType; +pub use clarity::vm::events::StacksTransactionEvent; #[derive(Debug, Clone, PartialEq)] pub enum TransactionOrigin { diff --git a/testnet/stacks-node/src/event_dispatcher.rs b/testnet/stacks-node/src/event_dispatcher.rs index d32132c486..9550e3df41 100644 --- a/testnet/stacks-node/src/event_dispatcher.rs +++ b/testnet/stacks-node/src/event_dispatcher.rs @@ -215,10 +215,10 @@ impl EventObserver { } }; - let (txid, raw_tx, burnchain_op) = match tx { - TransactionOrigin::Burn(op) => ( - op.txid().to_string(), "00".to_string(), json!(op.clone()) - ), + let (txid, raw_tx, burnchain_op_json) = 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(); @@ -242,7 +242,7 @@ impl EventObserver { raw_result, raw_tx, contract_interface_json, - burnchain_op_json: burnchain_op, + burnchain_op_json, } } From 441ad8fbc4e15337db038b944517506e9775b852 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 20 Jan 2023 16:08:29 -0500 Subject: [PATCH 03/11] update event dispatcher md file --- docs/event-dispatcher.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index a6a7678f97..843d4f34d9 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -57,6 +57,7 @@ Example: "transactions": [ { "contract_abi": null, + "burnchain_op": null, "raw_result": "0x03", "raw_tx": "0x808000000004008bc5147525b8f477f0bc4522a88c8339b2494db50000000000000002000000000000000001015814daf929d8700af344987681f44e913890a12e38550abe8e40f149ef5269f40f4008083a0f2e0ddf65dcd05ecfc151c7ff8a5308ad04c77c0e87b5aeadad31010200000000040000000000000000000000000000000000000000000000000000000000000000", "status": "success", @@ -68,6 +69,7 @@ Example: }, { "contract_abi": null, + "burnchain_op": null, "raw_result": "0x03", "raw_tx": "0x80800000000400f942874ce525e87f21bbe8c121b12fac831d02f4000000000000000000000000000003e800006ae29867aec4b0e4f776bebdcea7f6d9a24eeff370c8c739defadfcbb52659b30736ad4af021e8fb741520a6c65da419fdec01989fdf0032fc1838f427a9a36102010000000000051ac2d519faccba2e435f3272ff042b89435fd160ff00000000000003e800000000000000000000000000000000000000000000000000000000000000000000", "status": "success", @@ -165,6 +167,7 @@ Example: "transactions": [ { "contract_abi": null, + "burnchain_op": null, "raw_result": "0x03", "raw_tx": "0x808000000004008bc5147525b8f477f0bc4522a88c8339b2494db50000000000000002000000000000000001015814daf929d8700af344987681f44e913890a12e38550abe8e40f149ef5269f40f4008083a0f2e0ddf65dcd05ecfc151c7ff8a5308ad04c77c0e87b5aeadad31010200000000040000000000000000000000000000000000000000000000000000000000000000", "status": "success", @@ -176,6 +179,7 @@ Example: }, { "contract_abi": null, + "burnchain_op": null, "raw_result": "0x03", "raw_tx": "0x80800000000400f942874ce525e87f21bbe8c121b12fac831d02f4000000000000000000000000000003e800006ae29867aec4b0e4f776bebdcea7f6d9a24eeff370c8c739defadfcbb52659b30736ad4af021e8fb741520a6c65da419fdec01989fdf0032fc1838f427a9a36102010000000000051ac2d519faccba2e435f3272ff042b89435fd160ff00000000000003e800000000000000000000000000000000000000000000000000000000000000000000", "status": "success", From 3d2b3e56b2c99507702b0041e0acb7e762ed5ccf Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 25 Jan 2023 16:19:05 -0500 Subject: [PATCH 04/11] wip - add example of stx op to md file --- docs/event-dispatcher.md | 46 ++++++++++++++++++- .../src/tests/neon_integrations.rs | 3 ++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index 843d4f34d9..b9846727c5 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -30,6 +30,12 @@ and any events emitted from Stacks transactions during the block. If the transaction originally comes from the parent microblock stream preceding this block, the microblock related fields will be filled in. +If the `raw_tx` field for a particular transaction is "0x00", that indicates +that it is a burnchain operation. A burnchain operation is a transaction that +is executed on the Stacks network, but was sent through the Bitcoin network. +The Stacks network supports a few specific burnchain operations. You can read +more about them [here](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md#stx-operations-on-bitcoin). + Example: ```json @@ -78,8 +84,46 @@ Example: "microblock_sequence": "3", "microblock_hash": "0x9304fcbcc6daf5ac3f264522e0df50eddb5be85df6ee8a9fc2384c54274daaac", "microblock_parent_hash": "0x4893ab44636023efa08374033428e44eca490582bd39a6e61f3b6cf749b474bd" + }, + { + "burnchain_op": { + "TransferStx": { + "block_height": 208, + "burn_header_hash": [ + 55, 174,104,155,73,251,142,121,133,74,19,169,209,44,210,112,95,13,83,91,39,16,212,142,211,91, + 234,92,56,7,85,250], + "memo": [], + "recipient": { + "bytes": "455e5d8c309ff012c55882cf7f50a4b542739b9c", + "version": 26 + }, + "sender": { + "bytes": "9ffdc100acf4d475b71a042b76fb4447966102ab", + "version": 26 + }, + "transfered_ustx": 100000, + "txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", + "vtxindex": 2 + } + }, + "contract_abi": null, + "execution_cost": { + "read_count": 0, + "read_length": 0, + "runtime": 0, + "write_count": 0, + "write_length": 0 + }, + "microblock_hash": null, + "microblock_parent_hash": null, + "microblock_sequence": null, + "raw_result": "0x0703", + "raw_tx": "0x00", + "status": "success", + "tx_index": 0, + "txid": "0x85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6" } - ], + ], "matured_miner_rewards": [ { "recipient": "ST31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZZ239N96", diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 859f210140..8648b716a2 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1701,6 +1701,8 @@ fn stx_transfer_btc_integration_test() { for tx in transactions.iter() { let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap(); if raw_tx == "0x00" { + let dbg = format!("{:#}", tx); + info!("TX-BTC {}", dbg); let burnchain_op = tx.get("burnchain_op").unwrap().as_object().unwrap(); if !burnchain_op.contains_key("TransferStx") { panic!("unexpected btc transaction type"); @@ -1710,6 +1712,7 @@ fn stx_transfer_btc_integration_test() { } } assert!(found_btc_tx); + assert!(false); channel.stop_chains_coordinator(); } From f36473a444029d2bcdfa57ba36010dcc0a8d5123 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 1 Feb 2023 16:58:50 -0500 Subject: [PATCH 05/11] added docs, serialization functions, tests, and json formatting for btc ops is readable --- docs/event-dispatcher.md | 87 +++- src/chainstate/burn/operations/mod.rs | 422 +++++++++++++++++- src/chainstate/stacks/address.rs | 50 +++ .../src/tests/neon_integrations.rs | 3 - 4 files changed, 546 insertions(+), 16 deletions(-) diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index b9846727c5..171f9351c2 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -35,6 +35,7 @@ that it is a burnchain operation. A burnchain operation is a transaction that is executed on the Stacks network, but was sent through the Bitcoin network. The Stacks network supports a few specific burnchain operations. You can read more about them [here](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md#stx-operations-on-bitcoin). +The section below has example json encodings for each of the burnchain operations. Example: @@ -88,22 +89,22 @@ Example: { "burnchain_op": { "TransferStx": { - "block_height": 208, - "burn_header_hash": [ - 55, 174,104,155,73,251,142,121,133,74,19,169,209,44,210,112,95,13,83,91,39,16,212,142,211,91, - 234,92,56,7,85,250], - "memo": [], + "block_height": 10, + "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", + "memo": "0x000102030405", "recipient": { - "bytes": "455e5d8c309ff012c55882cf7f50a4b542739b9c", - "version": 26 + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22 }, "sender": { - "bytes": "9ffdc100acf4d475b71a042b76fb4447966102ab", - "version": 26 + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26 }, - "transfered_ustx": 100000, + "transfered_ustx": 10, "txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", - "vtxindex": 2 + "vtxindex": 10 } }, "contract_abi": null, @@ -120,7 +121,7 @@ Example: "raw_result": "0x0703", "raw_tx": "0x00", "status": "success", - "tx_index": 0, + "tx_index": 2, "txid": "0x85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6" } ], @@ -151,6 +152,68 @@ Example: } ``` +#### Example json values for burnchain operations +- TransferStx +```json +{ + "TransferStx": { + "block_height": 10, + "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", + "memo": "0x000102030405", + "recipient": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22 + }, + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26 + }, + "transfered_ustx": 10, + "txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", + "vtxindex": 10 + } +} +``` + +- StackStx +```json +{ + "StackStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "num_cycles": 10, + "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26 + }, + "stacked_ustx": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10 + } +} +``` + +- DelegateStx +```json +{ + "DelegateStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "output": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26 + }, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10 + } +} +``` + ### `POST /new_burn_block` This payload includes information about burn blocks as their sortitions are processed. diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index e7b9b0fbed..56912e5dac 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -20,6 +20,8 @@ use std::error; use std::fmt; use std::fs; use std::io; +use serde::{Deserialize, Serialize}; +use clarity::vm::types::PrincipalData; use crate::burnchains::Burnchain; use crate::burnchains::BurnchainBlockHeader; @@ -42,7 +44,7 @@ use crate::chainstate::stacks::address::PoxAddress; use crate::util_lib::db::DBConn; use crate::util_lib::db::DBTx; use crate::util_lib::db::Error as db_error; -use stacks_common::util::hash::Hash160; +use stacks_common::util::hash::{Hash160, hex_bytes, to_hex}; use stacks_common::util::hash::Sha512Trunc256Sum; use stacks_common::util::secp256k1::MessageSignature; use stacks_common::util::vrf::VRFPublicKey; @@ -180,24 +182,31 @@ impl From for Error { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct TransferStxOp { + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub sender: StacksAddress, + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub recipient: StacksAddress, pub transfered_ustx: u128, + #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] pub memo: Vec, // common to all transactions pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct StackStxOp { + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub sender: StacksAddress, /// the PoX reward address. /// NOTE: the address in .pox will be tagged as either p2pkh or p2sh; it's impossible to tell /// if it's a segwit-p2sh since that looks identical to a p2sh address. + #[serde(serialize_with = "crate::chainstate::stacks::address::pox_addr_b58_serialize")] + #[serde(deserialize_with = "crate::chainstate::stacks::address::pox_addr_b58_deser")] pub reward_addr: PoxAddress, /// how many ustx this transaction locks pub stacked_ustx: u128, @@ -207,6 +216,7 @@ pub struct StackStxOp { pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -214,24 +224,29 @@ pub struct StackStxOp { pub struct PreStxOp { /// the output address /// (must be a legacy Bitcoin address) + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub output: StacksAddress, // common to all transactions pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderBlockCommitOp { + #[serde(deserialize_with = "block_hh_deserialize", serialize_with = "block_hh_serialize")] pub block_header_hash: BlockHeaderHash, // hash of Stacks block header (sha512/256) + #[serde(deserialize_with = "vrf_seed_deserialize", serialize_with = "vrf_seed_serialize")] pub new_seed: VRFSeed, // new seed for this block pub parent_block_ptr: u32, // block height of the block that contains the parent block hash pub parent_vtxindex: u16, // offset in the parent block where the parent block hash can be found pub key_block_ptr: u32, // pointer to the block that contains the leader key registration pub key_vtxindex: u16, // offset in the block where the leader key can be found + #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] pub memo: Vec, // extra unused byte /// how many burn tokens (e.g. satoshis) were committed to produce this block @@ -247,6 +262,8 @@ pub struct LeaderBlockCommitOp { pub apparent_sender: BurnchainSigner, /// PoX/Burn outputs + #[serde(serialize_with = "crate::chainstate::stacks::address::pox_addr_vec_b58_serialize")] + #[serde(deserialize_with = "crate::chainstate::stacks::address::pox_addr_vec_b58_deser")] pub commit_outs: Vec, // PoX sunset burn pub sunset_burn: u64, @@ -255,19 +272,23 @@ pub struct LeaderBlockCommitOp { pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderKeyRegisterOp { + #[serde(deserialize_with = "consensus_hash_deserialize", serialize_with = "consensus_hash_serialize")] pub consensus_hash: ConsensusHash, // consensus hash at time of issuance pub public_key: VRFPublicKey, // EdDSA public key + #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] pub memo: Vec, // extra bytes in the op-return // common to all transactions pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of burn chain block } @@ -291,12 +312,15 @@ pub struct UserBurnSupportOp { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct DelegateStxOp { + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub sender: StacksAddress, + #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] pub delegate_to: StacksAddress, /// a tuple representing the output index of the reward address in the BTC transaction, // and the actual PoX reward address. /// NOTE: the address in .pox-2 will be tagged as either p2pkh or p2sh; it's impossible to tell /// if it's a segwit-p2sh since that looks identical to a p2sh address. + #[serde(deserialize_with = "reward_addr_deserialize", serialize_with = "reward_addr_serialize")] pub reward_addr: Option<(u32, PoxAddress)>, pub delegated_ustx: u128, pub until_burn_height: Option, @@ -305,6 +329,7 @@ pub struct DelegateStxOp { pub txid: Txid, // transaction ID pub vtxindex: u32, // index in the block where this tx occurs pub block_height: u64, // block height at which this tx occurs + #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -447,3 +472,398 @@ pub fn parse_u32_from_be(bytes: &[u8]) -> Option { pub fn parse_u16_from_be(bytes: &[u8]) -> Option { bytes.try_into().ok().map(u16::from_be_bytes) } + +// serialization functions +fn hex_serialize(bhh: &BurnchainHeaderHash, s: S) -> Result { + let inst = bhh.to_hex(); + s.serialize_str(inst.as_str()) +} + +fn hex_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + BurnchainHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +fn block_hh_serialize(bhh: &BlockHeaderHash, s: S) -> Result { + let inst = bhh.to_hex(); + s.serialize_str(inst.as_str()) +} + +fn block_hh_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + BlockHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +fn vrf_seed_serialize(seed: &VRFSeed, s: S) -> Result { + let inst = seed.to_hex(); + s.serialize_str(inst.as_str()) +} + +fn vrf_seed_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + VRFSeed::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +fn consensus_hash_serialize(ch: &ConsensusHash, s: S) -> Result { + let inst = ch.to_hex(); + s.serialize_str(inst.as_str()) +} + +fn consensus_hash_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + ConsensusHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +fn memo_serialize(memo: &Vec, s: S) -> Result { + let hex_inst = to_hex(memo); + let byte_str = format!("0x{}", hex_inst); + s.serialize_str(byte_str.as_str()) +} + +fn memo_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result, D::Error> { + let bytes_str = String::deserialize(d)?; + let hex_inst = &bytes_str[2..]; + + hex_bytes(&hex_inst).map_err(serde::de::Error::custom) +} + +#[derive(Serialize, Deserialize)] +struct StacksAddrJsonDisplay { + address: String, + #[serde(deserialize_with = "hash_160_deserialize", serialize_with = "hash_160_serialize")] + address_hash_bytes: Hash160, + address_version: u8, +} + +fn hash_160_serialize(hash: &Hash160, s: S) -> Result { + let hex_inst = to_hex(&hash.0); + let byte_str = format!("0x{}", hex_inst); + s.serialize_str(byte_str.as_str()) +} + +fn hash_160_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let bytes_str = String::deserialize(d)?; + let hex_inst = &bytes_str[2..]; + Hash160::from_hex(&hex_inst).map_err(serde::de::Error::custom) +} + +fn stacks_addr_serialize(addr: &StacksAddress, s: S) -> Result { + let addr_str = addr.to_string(); + let addr_display = StacksAddrJsonDisplay { + address: addr_str, + address_hash_bytes: addr.bytes, + address_version: addr.version, + }; + addr_display.serialize(s) +} + +fn stacks_addr_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let addr_display = StacksAddrJsonDisplay::deserialize(d)?; + Ok(StacksAddress { + version: addr_display.address_version, + bytes: addr_display.address_hash_bytes, + }) +} + +fn reward_addr_serialize(addr: &Option<(u32, PoxAddress)>, s: S) -> Result { + match addr { + None => s.serialize_none(), + Some((index, pox_addr)) => { + let str_addr = pox_addr.clone().to_b58(); + s.serialize_some(&(index, str_addr)) + } + } +} + +fn reward_addr_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result, D::Error> { + let reward_addr: Option<(u32, String)> = Option::deserialize(d)?; + match reward_addr { + None => Ok(None), + Some((input, pox_add_str)) => { + let pox_addr = PoxAddress::from_b58(&pox_add_str).ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string"))?; + Ok(Some((input, pox_addr))) + } + } +} + +mod test { + use serde::{Deserialize, Serialize}; + use serde_json::Serializer; + use stacks_common::address::{AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG}; + use stacks_common::types::Address; + use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed}; + use stacks_common::util::hash::Hash160; + use stacks_common::util::vrf::{VRFPrivateKey, VRFPublicKey}; + use crate::burnchains::{BurnchainSigner, Txid}; + use crate::chainstate::burn::operations::{DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, StackStxOp, TransferStxOp, UserBurnSupportOp}; + use crate::chainstate::stacks::address::PoxAddress; + + #[test] + fn test_serialization_transfer_stx_op() { + let sender_addr = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2"; + let sender = StacksAddress::from_string(sender_addr).unwrap(); + let recipient_addr = "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K"; + let recipient = StacksAddress::from_string(recipient_addr).unwrap(); + let op = TransferStxOp { + sender, + recipient, + transfered_ustx: 10, + memo: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05], + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "memo": "0x000102030405", + "recipient": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22, + }, + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "transfered_ustx": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = TransferStxOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } + + #[test] + fn test_serialization_stack_stx_op() { + let sender_addr = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2"; + let sender = StacksAddress::from_string(sender_addr).unwrap(); + let reward_addr = PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + bytes: Hash160([0x01; 20]) + }, + Some(AddressHashMode::SerializeP2PKH) + ); + + let op = StackStxOp { + sender, + reward_addr, + stacked_ustx: 10, + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + num_cycles: 10, + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "num_cycles": 10, + "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "stacked_ustx": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = StackStxOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } + + #[test] + fn test_serialization_pre_stx_op() { + let output_addr = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2"; + let output = StacksAddress::from_string(output_addr).unwrap(); + + let op = PreStxOp { + output, + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "output": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = PreStxOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } + + #[test] + fn test_serialization_leader_block_commit_op() { + let pox_addr = PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + bytes: Hash160([0x01; 20]) + }, + Some(AddressHashMode::SerializeP2PKH) + ); + + let op = LeaderBlockCommitOp { + block_header_hash: BlockHeaderHash([0x10; 32]), + new_seed: VRFSeed([0x10; 32]), + parent_block_ptr: 10, + parent_vtxindex: 10, + key_block_ptr: 10, + key_vtxindex: 10, + memo: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05], + input: (Txid([10u8; 32]), 10), + burn_parent_modulus: 10, + apparent_sender: BurnchainSigner("signer".to_string()), + commit_outs: vec![pox_addr.clone(), pox_addr], + sunset_burn: 10, + burn_fee: 10, + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "apparent_sender": "signer", + "block_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "block_height": 10, + "burn_fee": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "burn_parent_modulus": 10, + "commit_outs": ["16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], + "input": ("0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", 10), + "key_block_ptr": 10, + "key_vtxindex": 10, + "memo": "0x000102030405", + "new_seed": "1010101010101010101010101010101010101010101010101010101010101010", + "parent_block_ptr": 10, + "parent_vtxindex": 10, + "sunset_burn": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = LeaderBlockCommitOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } + + #[test] + fn test_serialization_leader_key_register_op() { + let public_key = VRFPublicKey::from_bytes(&[0x10; 32]).unwrap(); + let op = LeaderKeyRegisterOp { + consensus_hash: ConsensusHash([0x10; 20]), + public_key, + memo: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05], + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "consensus_hash": "1010101010101010101010101010101010101010", + "memo": "0x000102030405", + "public_key": "1010101010101010101010101010101010101010101010101010101010101010", + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = LeaderKeyRegisterOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } + + #[test] + fn test_serialization_delegate_stx_op() { + let sender_addr = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2"; + let sender = StacksAddress::from_string(sender_addr).unwrap(); + let delegate_to_addr = "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K"; + let delegate_to = StacksAddress::from_string(delegate_to_addr).unwrap(); + let pox_addr = PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + bytes: Hash160([0x01; 20]) + }, + Some(AddressHashMode::SerializeP2PKH), + ); + let op = DelegateStxOp { + sender, + delegate_to, + reward_addr: Some((10, pox_addr)), + delegated_ustx: 10, + until_burn_height: None, + txid: Txid([10u8; 32]), + vtxindex: 10, + block_height: 10, + burn_header_hash: BurnchainHeaderHash([0x10; 32]), + }; + let serialized_json = serde_json::json!(&op); + let constructed_json = json!({ + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "delegate_to": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22, + }, + "delegated_ustx": 10, + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "until_burn_height": null, + "vtxindex": 10, + }); + + assert_json_eq!(serialized_json.clone(), constructed_json); + + let deserialized = DelegateStxOp::deserialize(serialized_json).unwrap(); + assert_eq!(op, deserialized); + } +} \ No newline at end of file diff --git a/src/chainstate/stacks/address.rs b/src/chainstate/stacks/address.rs index 9f06783cb2..7d2d431d04 100644 --- a/src/chainstate/stacks/address.rs +++ b/src/chainstate/stacks/address.rs @@ -18,6 +18,8 @@ use std::cmp::{Ord, Ordering}; use std::io::prelude::*; use std::io::{Read, Write}; use std::{fmt, io}; +use serde::{Deserialize, Deserializer, Serializer}; +use serde::ser::SerializeSeq; use crate::burnchains::bitcoin::address::{ legacy_address_type_to_version_byte, legacy_version_byte_to_address_type, to_b58_version_byte, @@ -92,6 +94,44 @@ pub enum PoxAddress { Addr32(bool, PoxAddressType32, [u8; 32]), } +/// Serializes a PoxAddress as a B58 check encoded address or a bech32 address +pub fn pox_addr_b58_serialize( + input: &PoxAddress, + ser: S, +) -> Result { + ser.serialize_str(&input.clone().to_b58()) +} + +/// Deserializes a PoxAddress from a B58 check encoded address or a bech32 address +pub fn pox_addr_b58_deser<'de, D: Deserializer<'de>>(deser: D) -> Result { + let string_repr = String::deserialize(deser)?; + PoxAddress::from_b58(&string_repr) + .ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string")) +} + +/// Serializes each PoxAddress in a vector as a B58 check encoded address or a bech32 address +pub fn pox_addr_vec_b58_serialize( + input: &Vec, + ser: S, +) -> Result { + let mut seq = ser.serialize_seq(Some(input.len()))?; + for element in input { + seq.serialize_element(&element.clone().to_b58())?; + } + seq.end() +} + +/// Deserializes each PoxAddress in a vector from a B58 check encoded address or a bech32 address +pub fn pox_addr_vec_b58_deser<'de, D: Deserializer<'de>>(deser: D) -> Result, D::Error> { + let mut pox_vec = vec![]; + let pox_vec_ser: Vec = Vec::deserialize(deser)?; + for elem in pox_vec_ser { + pox_vec.push(PoxAddress::from_b58(&elem) + .ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string"))?); + } + Ok(pox_vec) +} + impl std::fmt::Display for PoxAddress { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_db_string()) @@ -407,6 +447,16 @@ impl PoxAddress { } } + // Convert from a B58 encoded bitcoin address + pub fn from_b58(input: &str) -> Option { + let btc_addr = BitcoinAddress::from_string(input)?; + PoxAddress::try_from_bitcoin_output(&BitcoinTxOutput { + address: btc_addr, + units: 0, + }) + } + + /// Convert this PoxAddress into a Bitcoin tx output pub fn to_bitcoin_tx_out(&self, value: u64) -> TxOut { match *self { diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 8648b716a2..859f210140 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1701,8 +1701,6 @@ fn stx_transfer_btc_integration_test() { for tx in transactions.iter() { let raw_tx = tx.get("raw_tx").unwrap().as_str().unwrap(); if raw_tx == "0x00" { - let dbg = format!("{:#}", tx); - info!("TX-BTC {}", dbg); let burnchain_op = tx.get("burnchain_op").unwrap().as_object().unwrap(); if !burnchain_op.contains_key("TransferStx") { panic!("unexpected btc transaction type"); @@ -1712,7 +1710,6 @@ fn stx_transfer_btc_integration_test() { } } assert!(found_btc_tx); - assert!(false); channel.stop_chains_coordinator(); } From ebeeede1602969f60b88c7a2c192c37864d3f209 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Wed, 1 Feb 2023 17:10:30 -0500 Subject: [PATCH 06/11] updated address hash mode --- src/chainstate/burn/operations/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 56912e5dac..2e8ff72be4 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -665,7 +665,7 @@ mod test { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, bytes: Hash160([0x01; 20]) }, - Some(AddressHashMode::SerializeP2PKH) + None ); let op = StackStxOp { @@ -738,7 +738,7 @@ mod test { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, bytes: Hash160([0x01; 20]) }, - Some(AddressHashMode::SerializeP2PKH) + None ); let op = LeaderBlockCommitOp { @@ -827,7 +827,7 @@ mod test { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, bytes: Hash160([0x01; 20]) }, - Some(AddressHashMode::SerializeP2PKH), + None ); let op = DelegateStxOp { sender, From b3fe8d40a7bc1a8fdf9aff386c4aa2f68b098d4b Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Thu, 2 Feb 2023 14:56:20 -0500 Subject: [PATCH 07/11] addressed feedback --- docs/event-dispatcher.md | 10 +- src/chainstate/burn/operations/mod.rs | 289 +++++++++++--------------- src/chainstate/stacks/address.rs | 16 +- stacks-common/src/codec/mod.rs | 1 + stacks-common/src/codec/serde.rs | 121 +++++++++++ 5 files changed, 267 insertions(+), 170 deletions(-) create mode 100644 stacks-common/src/codec/serde.rs diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index 171f9351c2..c4c953ce18 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -203,12 +203,20 @@ Example: "DelegateStx": { "block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "output": { + "delegate_to": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22 + }, + "delegated_ustx": 10, + "sender": { "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", "address_version": 26 }, + "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "until_burn_height": null, "vtxindex": 10 } } diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 2e8ff72be4..76dca01995 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use clarity::vm::types::PrincipalData; +use serde::{Deserialize, Serialize}; use std::convert::From; use std::convert::TryInto; use std::error; use std::fmt; use std::fs; use std::io; -use serde::{Deserialize, Serialize}; -use clarity::vm::types::PrincipalData; use crate::burnchains::Burnchain; use crate::burnchains::BurnchainBlockHeader; @@ -44,8 +44,8 @@ use crate::chainstate::stacks::address::PoxAddress; use crate::util_lib::db::DBConn; use crate::util_lib::db::DBTx; use crate::util_lib::db::Error as db_error; -use stacks_common::util::hash::{Hash160, hex_bytes, to_hex}; use stacks_common::util::hash::Sha512Trunc256Sum; +use stacks_common::util::hash::{hex_bytes, to_hex, Hash160}; use stacks_common::util::secp256k1::MessageSignature; use stacks_common::util::vrf::VRFPublicKey; @@ -182,25 +182,40 @@ impl From for Error { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct TransferStxOp { - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub sender: StacksAddress, - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub recipient: StacksAddress, pub transfered_ustx: u128, - #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::memo_deserialize", + serialize_with = "stacks_common::codec::serde::memo_serialize" + )] pub memo: Vec, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct StackStxOp { - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub sender: StacksAddress, /// the PoX reward address. /// NOTE: the address in .pox will be tagged as either p2pkh or p2sh; it's impossible to tell @@ -213,10 +228,13 @@ pub struct StackStxOp { pub num_cycles: u8, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -224,30 +242,45 @@ pub struct StackStxOp { pub struct PreStxOp { /// the output address /// (must be a legacy Bitcoin address) - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub output: StacksAddress, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderBlockCommitOp { - #[serde(deserialize_with = "block_hh_deserialize", serialize_with = "block_hh_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::block_hh_deserialize", + serialize_with = "stacks_common::codec::serde::block_hh_serialize" + )] pub block_header_hash: BlockHeaderHash, // hash of Stacks block header (sha512/256) - #[serde(deserialize_with = "vrf_seed_deserialize", serialize_with = "vrf_seed_serialize")] - pub new_seed: VRFSeed, // new seed for this block + #[serde( + deserialize_with = "stacks_common::codec::serde::vrf_seed_deserialize", + serialize_with = "stacks_common::codec::serde::vrf_seed_serialize" + )] + pub new_seed: VRFSeed, // new seed for this block pub parent_block_ptr: u32, // block height of the block that contains the parent block hash pub parent_vtxindex: u16, // offset in the parent block where the parent block hash can be found pub key_block_ptr: u32, // pointer to the block that contains the leader key registration pub key_vtxindex: u16, // offset in the block where the leader key can be found - #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] - pub memo: Vec, // extra unused byte + #[serde( + deserialize_with = "stacks_common::codec::serde::memo_deserialize", + serialize_with = "stacks_common::codec::serde::memo_serialize" + )] + pub memo: Vec, // extra unused byte /// how many burn tokens (e.g. satoshis) were committed to produce this block pub burn_fee: u64, @@ -269,26 +302,38 @@ pub struct LeaderBlockCommitOp { pub sunset_burn: u64, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderKeyRegisterOp { - #[serde(deserialize_with = "consensus_hash_deserialize", serialize_with = "consensus_hash_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::consensus_hash_deserialize", + serialize_with = "stacks_common::codec::serde::consensus_hash_serialize" + )] pub consensus_hash: ConsensusHash, // consensus hash at time of issuance - pub public_key: VRFPublicKey, // EdDSA public key - #[serde(deserialize_with = "memo_deserialize", serialize_with = "memo_serialize")] - pub memo: Vec, // extra bytes in the op-return + pub public_key: VRFPublicKey, // EdDSA public key + #[serde( + deserialize_with = "stacks_common::codec::serde::memo_deserialize", + serialize_with = "stacks_common::codec::serde::memo_serialize" + )] + pub memo: Vec, // extra bytes in the op-return // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of burn chain block } @@ -312,24 +357,36 @@ pub struct UserBurnSupportOp { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct DelegateStxOp { - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub sender: StacksAddress, - #[serde(deserialize_with = "stacks_addr_deserialize", serialize_with = "stacks_addr_serialize")] + #[serde( + deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", + serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" + )] pub delegate_to: StacksAddress, /// a tuple representing the output index of the reward address in the BTC transaction, // and the actual PoX reward address. /// NOTE: the address in .pox-2 will be tagged as either p2pkh or p2sh; it's impossible to tell /// if it's a segwit-p2sh since that looks identical to a p2sh address. - #[serde(deserialize_with = "reward_addr_deserialize", serialize_with = "reward_addr_serialize")] + #[serde( + deserialize_with = "reward_addr_deserialize", + serialize_with = "reward_addr_serialize" + )] pub reward_addr: Option<(u32, PoxAddress)>, pub delegated_ustx: u128, pub until_burn_height: Option, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde(deserialize_with = "hex_deserialize", serialize_with = "hex_serialize")] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs + #[serde( + deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", + serialize_with = "stacks_common::codec::serde::burn_hh_serialize" + )] pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -473,113 +530,12 @@ pub fn parse_u16_from_be(bytes: &[u8]) -> Option { bytes.try_into().ok().map(u16::from_be_bytes) } -// serialization functions -fn hex_serialize(bhh: &BurnchainHeaderHash, s: S) -> Result { - let inst = bhh.to_hex(); - s.serialize_str(inst.as_str()) -} - -fn hex_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - BurnchainHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -fn block_hh_serialize(bhh: &BlockHeaderHash, s: S) -> Result { - let inst = bhh.to_hex(); - s.serialize_str(inst.as_str()) -} - -fn block_hh_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - BlockHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -fn vrf_seed_serialize(seed: &VRFSeed, s: S) -> Result { - let inst = seed.to_hex(); - s.serialize_str(inst.as_str()) -} - -fn vrf_seed_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - VRFSeed::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -fn consensus_hash_serialize(ch: &ConsensusHash, s: S) -> Result { - let inst = ch.to_hex(); - s.serialize_str(inst.as_str()) -} - -fn consensus_hash_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - ConsensusHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -fn memo_serialize(memo: &Vec, s: S) -> Result { - let hex_inst = to_hex(memo); - let byte_str = format!("0x{}", hex_inst); - s.serialize_str(byte_str.as_str()) -} - -fn memo_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result, D::Error> { - let bytes_str = String::deserialize(d)?; - let hex_inst = &bytes_str[2..]; - - hex_bytes(&hex_inst).map_err(serde::de::Error::custom) -} - -#[derive(Serialize, Deserialize)] -struct StacksAddrJsonDisplay { - address: String, - #[serde(deserialize_with = "hash_160_deserialize", serialize_with = "hash_160_serialize")] - address_hash_bytes: Hash160, - address_version: u8, -} - -fn hash_160_serialize(hash: &Hash160, s: S) -> Result { - let hex_inst = to_hex(&hash.0); - let byte_str = format!("0x{}", hex_inst); - s.serialize_str(byte_str.as_str()) -} - -fn hash_160_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let bytes_str = String::deserialize(d)?; - let hex_inst = &bytes_str[2..]; - Hash160::from_hex(&hex_inst).map_err(serde::de::Error::custom) -} - -fn stacks_addr_serialize(addr: &StacksAddress, s: S) -> Result { - let addr_str = addr.to_string(); - let addr_display = StacksAddrJsonDisplay { - address: addr_str, - address_hash_bytes: addr.bytes, - address_version: addr.version, - }; - addr_display.serialize(s) -} - -fn stacks_addr_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let addr_display = StacksAddrJsonDisplay::deserialize(d)?; - Ok(StacksAddress { - version: addr_display.address_version, - bytes: addr_display.address_hash_bytes, - }) -} - -fn reward_addr_serialize(addr: &Option<(u32, PoxAddress)>, s: S) -> Result { +// these serialization functions should be moved to stacks_common/src/codec/serde.rs +// once PoxAddress is moved to stacks_common. +fn reward_addr_serialize( + addr: &Option<(u32, PoxAddress)>, + s: S, +) -> Result { match addr { None => s.serialize_none(), Some((index, pox_addr)) => { @@ -596,23 +552,30 @@ fn reward_addr_deserialize<'de, D: serde::Deserializer<'de>>( match reward_addr { None => Ok(None), Some((input, pox_add_str)) => { - let pox_addr = PoxAddress::from_b58(&pox_add_str).ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string"))?; + let pox_addr = PoxAddress::from_b58(&pox_add_str).ok_or_else(|| { + serde::de::Error::custom("Failed to decode PoxAddress from string") + })?; Ok(Some((input, pox_addr))) } } } mod test { + use crate::burnchains::{BurnchainSigner, Txid}; + use crate::chainstate::burn::operations::{ + DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, StackStxOp, + TransferStxOp, UserBurnSupportOp, + }; + use crate::chainstate::stacks::address::PoxAddress; use serde::{Deserialize, Serialize}; use serde_json::Serializer; use stacks_common::address::{AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG}; + use stacks_common::types::chainstate::{ + BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed, + }; use stacks_common::types::Address; - use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed}; use stacks_common::util::hash::Hash160; use stacks_common::util::vrf::{VRFPrivateKey, VRFPublicKey}; - use crate::burnchains::{BurnchainSigner, Txid}; - use crate::chainstate::burn::operations::{DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, StackStxOp, TransferStxOp, UserBurnSupportOp}; - use crate::chainstate::stacks::address::PoxAddress; #[test] fn test_serialization_transfer_stx_op() { @@ -663,9 +626,9 @@ mod test { let reward_addr = PoxAddress::Standard( StacksAddress { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, - bytes: Hash160([0x01; 20]) + bytes: Hash160([0x01; 20]), }, - None + None, ); let op = StackStxOp { @@ -736,9 +699,9 @@ mod test { let pox_addr = PoxAddress::Standard( StacksAddress { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, - bytes: Hash160([0x01; 20]) + bytes: Hash160([0x01; 20]), }, - None + None, ); let op = LeaderBlockCommitOp { @@ -825,9 +788,9 @@ mod test { let pox_addr = PoxAddress::Standard( StacksAddress { version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, - bytes: Hash160([0x01; 20]) + bytes: Hash160([0x01; 20]), }, - None + None, ); let op = DelegateStxOp { sender, @@ -866,4 +829,4 @@ mod test { let deserialized = DelegateStxOp::deserialize(serialized_json).unwrap(); assert_eq!(op, deserialized); } -} \ No newline at end of file +} diff --git a/src/chainstate/stacks/address.rs b/src/chainstate/stacks/address.rs index 7d2d431d04..466de966f0 100644 --- a/src/chainstate/stacks/address.rs +++ b/src/chainstate/stacks/address.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use serde::ser::SerializeSeq; +use serde::{Deserialize, Deserializer, Serializer}; use std::cmp::{Ord, Ordering}; use std::io::prelude::*; use std::io::{Read, Write}; use std::{fmt, io}; -use serde::{Deserialize, Deserializer, Serializer}; -use serde::ser::SerializeSeq; use crate::burnchains::bitcoin::address::{ legacy_address_type_to_version_byte, legacy_version_byte_to_address_type, to_b58_version_byte, @@ -122,12 +122,17 @@ pub fn pox_addr_vec_b58_serialize( } /// Deserializes each PoxAddress in a vector from a B58 check encoded address or a bech32 address -pub fn pox_addr_vec_b58_deser<'de, D: Deserializer<'de>>(deser: D) -> Result, D::Error> { +pub fn pox_addr_vec_b58_deser<'de, D: Deserializer<'de>>( + deser: D, +) -> Result, D::Error> { let mut pox_vec = vec![]; let pox_vec_ser: Vec = Vec::deserialize(deser)?; for elem in pox_vec_ser { - pox_vec.push(PoxAddress::from_b58(&elem) - .ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string"))?); + pox_vec.push( + PoxAddress::from_b58(&elem).ok_or_else(|| { + serde::de::Error::custom("Failed to decode PoxAddress from string") + })?, + ); } Ok(pox_vec) } @@ -456,7 +461,6 @@ impl PoxAddress { }) } - /// Convert this PoxAddress into a Bitcoin tx output pub fn to_bitcoin_tx_out(&self, value: u64) -> TxOut { match *self { diff --git a/stacks-common/src/codec/mod.rs b/stacks-common/src/codec/mod.rs index b0eca1e255..d1c2f2feb2 100644 --- a/stacks-common/src/codec/mod.rs +++ b/stacks-common/src/codec/mod.rs @@ -8,6 +8,7 @@ use crate::util::secp256k1::MESSAGE_SIGNATURE_ENCODED_SIZE; #[macro_use] pub mod macros; +pub mod serde; #[derive(Debug)] pub enum Error { diff --git a/stacks-common/src/codec/serde.rs b/stacks-common/src/codec/serde.rs new file mode 100644 index 0000000000..237a84394d --- /dev/null +++ b/stacks-common/src/codec/serde.rs @@ -0,0 +1,121 @@ +use crate::types::chainstate::{ + BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed, +}; +use crate::util::hash::{hex_bytes, to_hex, Hash160}; +use serde::{Deserialize, Serialize}; + +/// This file contains functions used for custom serde serializations and deserializations. + +pub fn burn_hh_serialize( + bhh: &BurnchainHeaderHash, + s: S, +) -> Result { + let inst = bhh.to_hex(); + s.serialize_str(inst.as_str()) +} + +pub fn burn_hh_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + BurnchainHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +pub fn block_hh_serialize( + bhh: &BlockHeaderHash, + s: S, +) -> Result { + let inst = bhh.to_hex(); + s.serialize_str(inst.as_str()) +} + +pub fn block_hh_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + BlockHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +pub fn vrf_seed_serialize(seed: &VRFSeed, s: S) -> Result { + let inst = seed.to_hex(); + s.serialize_str(inst.as_str()) +} + +pub fn vrf_seed_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result { + let inst_str = String::deserialize(d)?; + VRFSeed::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +pub fn consensus_hash_serialize( + ch: &ConsensusHash, + s: S, +) -> Result { + let inst = ch.to_hex(); + s.serialize_str(inst.as_str()) +} + +pub fn consensus_hash_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let inst_str = String::deserialize(d)?; + ConsensusHash::from_hex(&inst_str).map_err(serde::de::Error::custom) +} + +pub fn memo_serialize(memo: &Vec, s: S) -> Result { + let hex_inst = to_hex(memo); + let byte_str = format!("0x{}", hex_inst); + s.serialize_str(byte_str.as_str()) +} + +pub fn memo_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result, D::Error> { + let bytes_str = String::deserialize(d)?; + let hex_inst = &bytes_str[2..]; + + hex_bytes(&hex_inst).map_err(serde::de::Error::custom) +} + +#[derive(Serialize, Deserialize)] +struct StacksAddrJsonDisplay { + address: String, + #[serde( + deserialize_with = "hash_160_deserialize", + serialize_with = "hash_160_serialize" + )] + address_hash_bytes: Hash160, + address_version: u8, +} + +fn hash_160_serialize(hash: &Hash160, s: S) -> Result { + let hex_inst = to_hex(&hash.0); + let byte_str = format!("0x{}", hex_inst); + s.serialize_str(byte_str.as_str()) +} + +fn hash_160_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result { + let bytes_str = String::deserialize(d)?; + let hex_inst = &bytes_str[2..]; + Hash160::from_hex(&hex_inst).map_err(serde::de::Error::custom) +} + +pub fn stacks_addr_serialize( + addr: &StacksAddress, + s: S, +) -> Result { + let addr_str = addr.to_string(); + let addr_display = StacksAddrJsonDisplay { + address: addr_str, + address_hash_bytes: addr.bytes, + address_version: addr.version, + }; + addr_display.serialize(s) +} + +pub fn stacks_addr_deserialize<'de, D: serde::Deserializer<'de>>( + d: D, +) -> Result { + let addr_display = StacksAddrJsonDisplay::deserialize(d)?; + Ok(StacksAddress { + version: addr_display.address_version, + bytes: addr_display.address_hash_bytes, + }) +} From 487747abb21e392d4da4cff875c71a5133ea9dc1 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Thu, 2 Feb 2023 17:16:18 -0500 Subject: [PATCH 08/11] added specific json serialization function --- src/chainstate/burn/db/sortdb.rs | 5 +- src/chainstate/burn/operations/mod.rs | 480 ++++++++------------ stacks-common/src/codec/mod.rs | 1 - stacks-common/src/codec/serde.rs | 121 ----- testnet/stacks-node/src/event_dispatcher.rs | 12 +- 5 files changed, 194 insertions(+), 425 deletions(-) delete mode 100644 stacks-common/src/codec/serde.rs diff --git a/src/chainstate/burn/db/sortdb.rs b/src/chainstate/burn/db/sortdb.rs index 10a36d67fb..c6ae61ddb8 100644 --- a/src/chainstate/burn/db/sortdb.rs +++ b/src/chainstate/burn/db/sortdb.rs @@ -9602,7 +9602,10 @@ pub mod tests { delegate_to: StacksAddress::new(7, Hash160([7u8; 20])), reward_addr: Some(( 123, - PoxAddress::Standard(StacksAddress::new(8, Hash160([8u8; 20])), None), + PoxAddress::Standard( + StacksAddress::new(8, Hash160([8u8; 20])), + Some(AddressHashMode::SerializeP2PKH), + ), )), delegated_ustx: 789, until_burn_height: Some(1000), diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 76dca01995..430b254a0b 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -182,59 +182,33 @@ impl From for Error { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct TransferStxOp { - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub sender: StacksAddress, - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub recipient: StacksAddress, pub transfered_ustx: u128, - #[serde( - deserialize_with = "stacks_common::codec::serde::memo_deserialize", - serialize_with = "stacks_common::codec::serde::memo_serialize" - )] pub memo: Vec, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct StackStxOp { - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub sender: StacksAddress, /// the PoX reward address. /// NOTE: the address in .pox will be tagged as either p2pkh or p2sh; it's impossible to tell /// if it's a segwit-p2sh since that looks identical to a p2sh address. - #[serde(serialize_with = "crate::chainstate::stacks::address::pox_addr_b58_serialize")] - #[serde(deserialize_with = "crate::chainstate::stacks::address::pox_addr_b58_deser")] pub reward_addr: PoxAddress, /// how many ustx this transaction locks pub stacked_ustx: u128, pub num_cycles: u8, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -242,45 +216,25 @@ pub struct StackStxOp { pub struct PreStxOp { /// the output address /// (must be a legacy Bitcoin address) - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub output: StacksAddress, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderBlockCommitOp { - #[serde( - deserialize_with = "stacks_common::codec::serde::block_hh_deserialize", - serialize_with = "stacks_common::codec::serde::block_hh_serialize" - )] pub block_header_hash: BlockHeaderHash, // hash of Stacks block header (sha512/256) - #[serde( - deserialize_with = "stacks_common::codec::serde::vrf_seed_deserialize", - serialize_with = "stacks_common::codec::serde::vrf_seed_serialize" - )] - pub new_seed: VRFSeed, // new seed for this block + pub new_seed: VRFSeed, // new seed for this block pub parent_block_ptr: u32, // block height of the block that contains the parent block hash pub parent_vtxindex: u16, // offset in the parent block where the parent block hash can be found pub key_block_ptr: u32, // pointer to the block that contains the leader key registration pub key_vtxindex: u16, // offset in the block where the leader key can be found - #[serde( - deserialize_with = "stacks_common::codec::serde::memo_deserialize", - serialize_with = "stacks_common::codec::serde::memo_serialize" - )] - pub memo: Vec, // extra unused byte + pub memo: Vec, // extra unused byte /// how many burn tokens (e.g. satoshis) were committed to produce this block pub burn_fee: u64, @@ -295,45 +249,27 @@ pub struct LeaderBlockCommitOp { pub apparent_sender: BurnchainSigner, /// PoX/Burn outputs - #[serde(serialize_with = "crate::chainstate::stacks::address::pox_addr_vec_b58_serialize")] - #[serde(deserialize_with = "crate::chainstate::stacks::address::pox_addr_vec_b58_deser")] pub commit_outs: Vec, // PoX sunset burn pub sunset_burn: u64, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct LeaderKeyRegisterOp { - #[serde( - deserialize_with = "stacks_common::codec::serde::consensus_hash_deserialize", - serialize_with = "stacks_common::codec::serde::consensus_hash_serialize" - )] pub consensus_hash: ConsensusHash, // consensus hash at time of issuance - pub public_key: VRFPublicKey, // EdDSA public key - #[serde( - deserialize_with = "stacks_common::codec::serde::memo_deserialize", - serialize_with = "stacks_common::codec::serde::memo_serialize" - )] - pub memo: Vec, // extra bytes in the op-return + pub public_key: VRFPublicKey, // EdDSA public key + pub memo: Vec, // extra bytes in the op-return // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of burn chain block } @@ -357,36 +293,20 @@ pub struct UserBurnSupportOp { #[derive(Debug, PartialEq, Clone, Eq, Serialize, Deserialize)] pub struct DelegateStxOp { - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub sender: StacksAddress, - #[serde( - deserialize_with = "stacks_common::codec::serde::stacks_addr_deserialize", - serialize_with = "stacks_common::codec::serde::stacks_addr_serialize" - )] pub delegate_to: StacksAddress, /// a tuple representing the output index of the reward address in the BTC transaction, // and the actual PoX reward address. /// NOTE: the address in .pox-2 will be tagged as either p2pkh or p2sh; it's impossible to tell /// if it's a segwit-p2sh since that looks identical to a p2sh address. - #[serde( - deserialize_with = "reward_addr_deserialize", - serialize_with = "reward_addr_serialize" - )] pub reward_addr: Option<(u32, PoxAddress)>, pub delegated_ustx: u128, pub until_burn_height: Option, // common to all transactions - pub txid: Txid, // transaction ID - pub vtxindex: u32, // index in the block where this tx occurs - pub block_height: u64, // block height at which this tx occurs - #[serde( - deserialize_with = "stacks_common::codec::serde::burn_hh_deserialize", - serialize_with = "stacks_common::codec::serde::burn_hh_serialize" - )] + pub txid: Txid, // transaction ID + pub vtxindex: u32, // index in the block where this tx occurs + pub block_height: u64, // block height at which this tx occurs pub burn_header_hash: BurnchainHeaderHash, // hash of the burn chain block header } @@ -401,6 +321,21 @@ pub enum BlockstackOperationType { DelegateStx(DelegateStxOp), } +// serialization helpers for blockstack_op_to_json function +pub fn memo_serialize(memo: &Vec) -> String { + let hex_inst = to_hex(memo); + format!("0x{}", hex_inst) +} + +pub fn stacks_addr_serialize(addr: &StacksAddress) -> serde_json::Value { + let addr_str = addr.to_string(); + json!({ + "address": addr_str, + "address_hash_bytes": format!("0x{}", addr.bytes), + "address_version": addr.version + }) +} + impl BlockstackOperationType { pub fn opcode(&self) -> Opcodes { match *self { @@ -497,6 +432,78 @@ impl BlockstackOperationType { BlockstackOperationType::DelegateStx(ref mut data) => data.burn_header_hash = hash, }; } + + pub fn pre_stx_to_json(op: &PreStxOp) -> serde_json::Value { + json!({ + "PreStx": { + "block_height": op.block_height, + "burn_header_hash": &op.burn_header_hash.to_hex(), + "output": stacks_addr_serialize(&op.output), + "txid": op.txid, + "vtxindex": op.vtxindex, + } + }) + } + + pub fn stack_stx_to_json(op: &StackStxOp) -> serde_json::Value { + json!({ + "StackStx": { + "block_height": op.block_height, + "burn_header_hash": &op.burn_header_hash.to_hex(), + "num_cycles": op.num_cycles, + "reward_addr": op.reward_addr.clone().to_b58(), + "sender": stacks_addr_serialize(&op.sender), + "stacked_ustx": op.stacked_ustx, + "txid": op.txid, + "vtxindex": op.vtxindex, + } + }) + } + + pub fn transfer_stx_to_json(op: &TransferStxOp) -> serde_json::Value { + json!({ + "TransferStx": { + "block_height": op.block_height, + "burn_header_hash": &op.burn_header_hash.to_hex(), + "memo": memo_serialize(&op.memo), + "recipient": stacks_addr_serialize(&op.recipient), + "sender": stacks_addr_serialize(&op.sender), + "transfered_ustx": op.transfered_ustx, + "txid": op.txid, + "vtxindex": op.vtxindex, + } + }) + } + + pub fn delegate_stx_to_json(op: &DelegateStxOp) -> serde_json::Value { + json!({ + "DelegateStx": { + "block_height": op.block_height, + "burn_header_hash": &op.burn_header_hash.to_hex(), + "delegate_to": stacks_addr_serialize(&op.delegate_to), + "delegated_ustx": op.delegated_ustx, + "sender": stacks_addr_serialize(&op.sender), + "reward_addr": &op.reward_addr.as_ref().map(|(index, addr)| (index, addr.clone().to_b58())), + "txid": op.txid, + "until_burn_height": op.until_burn_height, + "vtxindex": op.vtxindex, + } + + }) + } + + pub fn blockstack_op_to_json(&self) -> serde_json::Value { + match self { + BlockstackOperationType::PreStx(op) => Self::pre_stx_to_json(op), + BlockstackOperationType::StackStx(op) => Self::stack_stx_to_json(op), + BlockstackOperationType::TransferStx(op) => Self::transfer_stx_to_json(op), + BlockstackOperationType::DelegateStx(op) => Self::delegate_stx_to_json(op), + // json serialization for the remaining op types is not implemented for now. This function + // is currently only used to json-ify burnchain ops executed as Stacks transactions (so, + // stack_stx, transfer_stx, and delegate_stx). + _ => json!(null), + } + } } impl fmt::Display for BlockstackOperationType { @@ -530,52 +537,18 @@ pub fn parse_u16_from_be(bytes: &[u8]) -> Option { bytes.try_into().ok().map(u16::from_be_bytes) } -// these serialization functions should be moved to stacks_common/src/codec/serde.rs -// once PoxAddress is moved to stacks_common. -fn reward_addr_serialize( - addr: &Option<(u32, PoxAddress)>, - s: S, -) -> Result { - match addr { - None => s.serialize_none(), - Some((index, pox_addr)) => { - let str_addr = pox_addr.clone().to_b58(); - s.serialize_some(&(index, str_addr)) - } - } -} - -fn reward_addr_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result, D::Error> { - let reward_addr: Option<(u32, String)> = Option::deserialize(d)?; - match reward_addr { - None => Ok(None), - Some((input, pox_add_str)) => { - let pox_addr = PoxAddress::from_b58(&pox_add_str).ok_or_else(|| { - serde::de::Error::custom("Failed to decode PoxAddress from string") - })?; - Ok(Some((input, pox_addr))) - } - } -} - mod test { - use crate::burnchains::{BurnchainSigner, Txid}; + use crate::burnchains::Txid; use crate::chainstate::burn::operations::{ - DelegateStxOp, LeaderBlockCommitOp, LeaderKeyRegisterOp, PreStxOp, StackStxOp, - TransferStxOp, UserBurnSupportOp, + BlockstackOperationType, DelegateStxOp, PreStxOp, StackStxOp, TransferStxOp, }; use crate::chainstate::stacks::address::PoxAddress; - use serde::{Deserialize, Serialize}; - use serde_json::Serializer; - use stacks_common::address::{AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG}; + use stacks_common::address::C32_ADDRESS_VERSION_MAINNET_SINGLESIG; use stacks_common::types::chainstate::{ BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed, }; use stacks_common::types::Address; use stacks_common::util::hash::Hash160; - use stacks_common::util::vrf::{VRFPrivateKey, VRFPublicKey}; #[test] fn test_serialization_transfer_stx_op() { @@ -593,30 +566,29 @@ mod test { block_height: 10, burn_header_hash: BurnchainHeaderHash([0x10; 32]), }; - let serialized_json = serde_json::json!(&op); + let serialized_json = BlockstackOperationType::transfer_stx_to_json(&op); let constructed_json = json!({ - "block_height": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "memo": "0x000102030405", - "recipient": { - "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", - "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", - "address_version": 22, - }, - "sender": { - "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", - "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", - "address_version": 26, - }, - "transfered_ustx": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "vtxindex": 10, + "TransferStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "memo": "0x000102030405", + "recipient": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22, + }, + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "transfered_ustx": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + } }); - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = TransferStxOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); + assert_json_eq!(serialized_json, constructed_json); } #[test] @@ -641,26 +613,25 @@ mod test { burn_header_hash: BurnchainHeaderHash([0x10; 32]), num_cycles: 10, }; - let serialized_json = serde_json::json!(&op); + let serialized_json = BlockstackOperationType::stack_stx_to_json(&op); let constructed_json = json!({ - "block_height": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "num_cycles": 10, - "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", - "sender": { - "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", - "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", - "address_version": 26, - }, - "stacked_ustx": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "vtxindex": 10, + "StackStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "num_cycles": 10, + "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "stacked_ustx": 10, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + } }); - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = StackStxOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); + assert_json_eq!(serialized_json, constructed_json); } #[test] @@ -675,108 +646,22 @@ mod test { block_height: 10, burn_header_hash: BurnchainHeaderHash([0x10; 32]), }; - let serialized_json = serde_json::json!(&op); + let serialized_json = BlockstackOperationType::pre_stx_to_json(&op); let constructed_json = json!({ - "block_height": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "output": { - "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", - "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", - "address_version": 26, - }, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "vtxindex": 10, - }); - - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = PreStxOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); - } - - #[test] - fn test_serialization_leader_block_commit_op() { - let pox_addr = PoxAddress::Standard( - StacksAddress { - version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, - bytes: Hash160([0x01; 20]), - }, - None, - ); - - let op = LeaderBlockCommitOp { - block_header_hash: BlockHeaderHash([0x10; 32]), - new_seed: VRFSeed([0x10; 32]), - parent_block_ptr: 10, - parent_vtxindex: 10, - key_block_ptr: 10, - key_vtxindex: 10, - memo: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05], - input: (Txid([10u8; 32]), 10), - burn_parent_modulus: 10, - apparent_sender: BurnchainSigner("signer".to_string()), - commit_outs: vec![pox_addr.clone(), pox_addr], - sunset_burn: 10, - burn_fee: 10, - txid: Txid([10u8; 32]), - vtxindex: 10, - block_height: 10, - burn_header_hash: BurnchainHeaderHash([0x10; 32]), - }; - let serialized_json = serde_json::json!(&op); - let constructed_json = json!({ - "apparent_sender": "signer", - "block_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "block_height": 10, - "burn_fee": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "burn_parent_modulus": 10, - "commit_outs": ["16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], - "input": ("0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", 10), - "key_block_ptr": 10, - "key_vtxindex": 10, - "memo": "0x000102030405", - "new_seed": "1010101010101010101010101010101010101010101010101010101010101010", - "parent_block_ptr": 10, - "parent_vtxindex": 10, - "sunset_burn": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "vtxindex": 10, - }); - - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = LeaderBlockCommitOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); - } - - #[test] - fn test_serialization_leader_key_register_op() { - let public_key = VRFPublicKey::from_bytes(&[0x10; 32]).unwrap(); - let op = LeaderKeyRegisterOp { - consensus_hash: ConsensusHash([0x10; 20]), - public_key, - memo: vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05], - txid: Txid([10u8; 32]), - vtxindex: 10, - block_height: 10, - burn_header_hash: BurnchainHeaderHash([0x10; 32]), - }; - let serialized_json = serde_json::json!(&op); - let constructed_json = json!({ - "block_height": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "consensus_hash": "1010101010101010101010101010101010101010", - "memo": "0x000102030405", - "public_key": "1010101010101010101010101010101010101010101010101010101010101010", - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "vtxindex": 10, + "PreStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "output": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "vtxindex": 10, + } }); - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = LeaderKeyRegisterOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); + assert_json_eq!(serialized_json, constructed_json); } #[test] @@ -803,30 +688,29 @@ mod test { block_height: 10, burn_header_hash: BurnchainHeaderHash([0x10; 32]), }; - let serialized_json = serde_json::json!(&op); + let serialized_json = BlockstackOperationType::delegate_stx_to_json(&op); let constructed_json = json!({ - "block_height": 10, - "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", - "delegate_to": { - "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", - "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", - "address_version": 22, - }, - "delegated_ustx": 10, - "sender": { - "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", - "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", - "address_version": 26, - }, - "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", - "until_burn_height": null, - "vtxindex": 10, + "DelegateStx": { + "block_height": 10, + "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", + "delegate_to": { + "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", + "address_hash_bytes": "0x89f5fd1f719e4449c980de38e3504be6770a2698", + "address_version": 22, + }, + "delegated_ustx": 10, + "sender": { + "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", + "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", + "address_version": 26, + }, + "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], + "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "until_burn_height": null, + "vtxindex": 10, + } }); - assert_json_eq!(serialized_json.clone(), constructed_json); - - let deserialized = DelegateStxOp::deserialize(serialized_json).unwrap(); - assert_eq!(op, deserialized); + assert_json_eq!(serialized_json, constructed_json); } } diff --git a/stacks-common/src/codec/mod.rs b/stacks-common/src/codec/mod.rs index d1c2f2feb2..b0eca1e255 100644 --- a/stacks-common/src/codec/mod.rs +++ b/stacks-common/src/codec/mod.rs @@ -8,7 +8,6 @@ use crate::util::secp256k1::MESSAGE_SIGNATURE_ENCODED_SIZE; #[macro_use] pub mod macros; -pub mod serde; #[derive(Debug)] pub enum Error { diff --git a/stacks-common/src/codec/serde.rs b/stacks-common/src/codec/serde.rs deleted file mode 100644 index 237a84394d..0000000000 --- a/stacks-common/src/codec/serde.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::types::chainstate::{ - BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, VRFSeed, -}; -use crate::util::hash::{hex_bytes, to_hex, Hash160}; -use serde::{Deserialize, Serialize}; - -/// This file contains functions used for custom serde serializations and deserializations. - -pub fn burn_hh_serialize( - bhh: &BurnchainHeaderHash, - s: S, -) -> Result { - let inst = bhh.to_hex(); - s.serialize_str(inst.as_str()) -} - -pub fn burn_hh_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - BurnchainHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -pub fn block_hh_serialize( - bhh: &BlockHeaderHash, - s: S, -) -> Result { - let inst = bhh.to_hex(); - s.serialize_str(inst.as_str()) -} - -pub fn block_hh_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - BlockHeaderHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -pub fn vrf_seed_serialize(seed: &VRFSeed, s: S) -> Result { - let inst = seed.to_hex(); - s.serialize_str(inst.as_str()) -} - -pub fn vrf_seed_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result { - let inst_str = String::deserialize(d)?; - VRFSeed::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -pub fn consensus_hash_serialize( - ch: &ConsensusHash, - s: S, -) -> Result { - let inst = ch.to_hex(); - s.serialize_str(inst.as_str()) -} - -pub fn consensus_hash_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let inst_str = String::deserialize(d)?; - ConsensusHash::from_hex(&inst_str).map_err(serde::de::Error::custom) -} - -pub fn memo_serialize(memo: &Vec, s: S) -> Result { - let hex_inst = to_hex(memo); - let byte_str = format!("0x{}", hex_inst); - s.serialize_str(byte_str.as_str()) -} - -pub fn memo_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result, D::Error> { - let bytes_str = String::deserialize(d)?; - let hex_inst = &bytes_str[2..]; - - hex_bytes(&hex_inst).map_err(serde::de::Error::custom) -} - -#[derive(Serialize, Deserialize)] -struct StacksAddrJsonDisplay { - address: String, - #[serde( - deserialize_with = "hash_160_deserialize", - serialize_with = "hash_160_serialize" - )] - address_hash_bytes: Hash160, - address_version: u8, -} - -fn hash_160_serialize(hash: &Hash160, s: S) -> Result { - let hex_inst = to_hex(&hash.0); - let byte_str = format!("0x{}", hex_inst); - s.serialize_str(byte_str.as_str()) -} - -fn hash_160_deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result { - let bytes_str = String::deserialize(d)?; - let hex_inst = &bytes_str[2..]; - Hash160::from_hex(&hex_inst).map_err(serde::de::Error::custom) -} - -pub fn stacks_addr_serialize( - addr: &StacksAddress, - s: S, -) -> Result { - let addr_str = addr.to_string(); - let addr_display = StacksAddrJsonDisplay { - address: addr_str, - address_hash_bytes: addr.bytes, - address_version: addr.version, - }; - addr_display.serialize(s) -} - -pub fn stacks_addr_deserialize<'de, D: serde::Deserializer<'de>>( - d: D, -) -> Result { - let addr_display = StacksAddrJsonDisplay::deserialize(d)?; - Ok(StacksAddress { - version: addr_display.address_version, - bytes: addr_display.address_hash_bytes, - }) -} diff --git a/testnet/stacks-node/src/event_dispatcher.rs b/testnet/stacks-node/src/event_dispatcher.rs index 9550e3df41..42220b043d 100644 --- a/testnet/stacks-node/src/event_dispatcher.rs +++ b/testnet/stacks-node/src/event_dispatcher.rs @@ -30,6 +30,7 @@ use stacks::vm::events::{FTEventType, NFTEventType, STXEventType}; use stacks::vm::types::{AssetIdentifier, QualifiedContractIdentifier, Value}; use super::config::{EventKeyType, EventObserverConfig}; +use stacks::chainstate::burn::operations::BlockstackOperationType; use stacks::chainstate::burn::ConsensusHash; use stacks::chainstate::stacks::db::unconfirmed::ProcessedUnconfirmedState; use stacks::chainstate::stacks::miner::TransactionEvent; @@ -216,9 +217,11 @@ impl EventObserver { }; let (txid, raw_tx, burnchain_op_json) = match tx { - TransactionOrigin::Burn(op) => { - (op.txid().to_string(), "00".to_string(), json!(op.clone())) - } + TransactionOrigin::Burn(op) => ( + op.txid().to_string(), + "00".to_string(), + BlockstackOperationType::blockstack_op_to_json(&op), + ), TransactionOrigin::Stacks(ref tx) => { let txid = tx.txid().to_string(); let bytes = tx.serialize_to_vec(); @@ -251,7 +254,8 @@ impl EventObserver { receipt: &StacksTransactionReceipt, tx_index: u32, ) -> serde_json::Value { - let receipt_payload_info = EventObserver::generate_payload_info_for_receipt(receipt); + let receipt_payloa + d_info = EventObserver::generate_payload_info_for_receipt(receipt); json!({ "txid": format!("0x{}", &receipt_payload_info.txid), From 2b9565fef6c15ecb45dfd58d2970081a56a42276 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Thu, 2 Feb 2023 17:25:44 -0500 Subject: [PATCH 09/11] cleanup --- src/chainstate/burn/operations/mod.rs | 2 - src/chainstate/stacks/address.rs | 54 --------------------- testnet/stacks-node/src/event_dispatcher.rs | 3 +- 3 files changed, 1 insertion(+), 58 deletions(-) diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 430b254a0b..9e81cf0f39 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clarity::vm::types::PrincipalData; -use serde::{Deserialize, Serialize}; use std::convert::From; use std::convert::TryInto; use std::error; diff --git a/src/chainstate/stacks/address.rs b/src/chainstate/stacks/address.rs index 466de966f0..9f06783cb2 100644 --- a/src/chainstate/stacks/address.rs +++ b/src/chainstate/stacks/address.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use serde::ser::SerializeSeq; -use serde::{Deserialize, Deserializer, Serializer}; use std::cmp::{Ord, Ordering}; use std::io::prelude::*; use std::io::{Read, Write}; @@ -94,49 +92,6 @@ pub enum PoxAddress { Addr32(bool, PoxAddressType32, [u8; 32]), } -/// Serializes a PoxAddress as a B58 check encoded address or a bech32 address -pub fn pox_addr_b58_serialize( - input: &PoxAddress, - ser: S, -) -> Result { - ser.serialize_str(&input.clone().to_b58()) -} - -/// Deserializes a PoxAddress from a B58 check encoded address or a bech32 address -pub fn pox_addr_b58_deser<'de, D: Deserializer<'de>>(deser: D) -> Result { - let string_repr = String::deserialize(deser)?; - PoxAddress::from_b58(&string_repr) - .ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string")) -} - -/// Serializes each PoxAddress in a vector as a B58 check encoded address or a bech32 address -pub fn pox_addr_vec_b58_serialize( - input: &Vec, - ser: S, -) -> Result { - let mut seq = ser.serialize_seq(Some(input.len()))?; - for element in input { - seq.serialize_element(&element.clone().to_b58())?; - } - seq.end() -} - -/// Deserializes each PoxAddress in a vector from a B58 check encoded address or a bech32 address -pub fn pox_addr_vec_b58_deser<'de, D: Deserializer<'de>>( - deser: D, -) -> Result, D::Error> { - let mut pox_vec = vec![]; - let pox_vec_ser: Vec = Vec::deserialize(deser)?; - for elem in pox_vec_ser { - pox_vec.push( - PoxAddress::from_b58(&elem).ok_or_else(|| { - serde::de::Error::custom("Failed to decode PoxAddress from string") - })?, - ); - } - Ok(pox_vec) -} - impl std::fmt::Display for PoxAddress { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_db_string()) @@ -452,15 +407,6 @@ impl PoxAddress { } } - // Convert from a B58 encoded bitcoin address - pub fn from_b58(input: &str) -> Option { - let btc_addr = BitcoinAddress::from_string(input)?; - PoxAddress::try_from_bitcoin_output(&BitcoinTxOutput { - address: btc_addr, - units: 0, - }) - } - /// Convert this PoxAddress into a Bitcoin tx output pub fn to_bitcoin_tx_out(&self, value: u64) -> TxOut { match *self { diff --git a/testnet/stacks-node/src/event_dispatcher.rs b/testnet/stacks-node/src/event_dispatcher.rs index 42220b043d..a14d8d8399 100644 --- a/testnet/stacks-node/src/event_dispatcher.rs +++ b/testnet/stacks-node/src/event_dispatcher.rs @@ -254,8 +254,7 @@ impl EventObserver { receipt: &StacksTransactionReceipt, tx_index: u32, ) -> serde_json::Value { - let receipt_payloa - d_info = EventObserver::generate_payload_info_for_receipt(receipt); + let receipt_payload_info = EventObserver::generate_payload_info_for_receipt(receipt); json!({ "txid": format!("0x{}", &receipt_payload_info.txid), From 9911a8d062f1848e23dade69bc64311bdf7495c1 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 3 Feb 2023 11:29:59 -0500 Subject: [PATCH 10/11] added docs, updated field names in json object --- docs/event-dispatcher.md | 16 ++++++------ src/chainstate/burn/operations/mod.rs | 37 +++++++++++++++------------ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index c4c953ce18..8a7194815e 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -89,7 +89,7 @@ Example: { "burnchain_op": { "TransferStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", "memo": "0x000102030405", "recipient": { @@ -103,7 +103,7 @@ Example: "address_version": 26 }, "transfered_ustx": 10, - "txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", + "burn_txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", "vtxindex": 10 } }, @@ -157,7 +157,7 @@ Example: ```json { "TransferStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", "memo": "0x000102030405", "recipient": { @@ -171,7 +171,7 @@ Example: "address_version": 26 }, "transfered_ustx": 10, - "txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", + "burn_txid": "85aa2106186723f3c4f1d8bb58e3a02746ca9be1be9f4be0c6557079e1f660e6", "vtxindex": 10 } } @@ -181,7 +181,7 @@ Example: ```json { "StackStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "num_cycles": 10, "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", @@ -191,7 +191,7 @@ Example: "address_version": 26 }, "stacked_ustx": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "vtxindex": 10 } } @@ -201,7 +201,7 @@ Example: ```json { "DelegateStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "delegate_to": { "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", @@ -215,7 +215,7 @@ Example: "address_version": 26 }, "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "until_burn_height": null, "vtxindex": 10 } diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 9e81cf0f39..b33d65f304 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -434,10 +434,10 @@ impl BlockstackOperationType { pub fn pre_stx_to_json(op: &PreStxOp) -> serde_json::Value { json!({ "PreStx": { - "block_height": op.block_height, + "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "output": stacks_addr_serialize(&op.output), - "txid": op.txid, + "burn_txid": op.txid, "vtxindex": op.vtxindex, } }) @@ -446,13 +446,13 @@ impl BlockstackOperationType { pub fn stack_stx_to_json(op: &StackStxOp) -> serde_json::Value { json!({ "StackStx": { - "block_height": op.block_height, + "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "num_cycles": op.num_cycles, "reward_addr": op.reward_addr.clone().to_b58(), "sender": stacks_addr_serialize(&op.sender), "stacked_ustx": op.stacked_ustx, - "txid": op.txid, + "burn_txid": op.txid, "vtxindex": op.vtxindex, } }) @@ -461,13 +461,13 @@ impl BlockstackOperationType { pub fn transfer_stx_to_json(op: &TransferStxOp) -> serde_json::Value { json!({ "TransferStx": { - "block_height": op.block_height, + "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "memo": memo_serialize(&op.memo), "recipient": stacks_addr_serialize(&op.recipient), "sender": stacks_addr_serialize(&op.sender), "transfered_ustx": op.transfered_ustx, - "txid": op.txid, + "burn_txid": op.txid, "vtxindex": op.vtxindex, } }) @@ -476,13 +476,13 @@ impl BlockstackOperationType { pub fn delegate_stx_to_json(op: &DelegateStxOp) -> serde_json::Value { json!({ "DelegateStx": { - "block_height": op.block_height, + "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "delegate_to": stacks_addr_serialize(&op.delegate_to), "delegated_ustx": op.delegated_ustx, "sender": stacks_addr_serialize(&op.sender), "reward_addr": &op.reward_addr.as_ref().map(|(index, addr)| (index, addr.clone().to_b58())), - "txid": op.txid, + "burn_txid": op.txid, "until_burn_height": op.until_burn_height, "vtxindex": op.vtxindex, } @@ -490,6 +490,11 @@ impl BlockstackOperationType { }) } + // An explicit JSON serialization function is used (instead of using the default serialization + // function) for the Blockstack ops. This is because (a) we wanted the serialization to be + // more readable, and (b) the serialization used to display PoxAddress as a string is lossy, + // so we wouldn't want to use this serialization by default (because there will be issues with + // deserialization). pub fn blockstack_op_to_json(&self) -> serde_json::Value { match self { BlockstackOperationType::PreStx(op) => Self::pre_stx_to_json(op), @@ -567,7 +572,7 @@ mod test { let serialized_json = BlockstackOperationType::transfer_stx_to_json(&op); let constructed_json = json!({ "TransferStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "memo": "0x000102030405", "recipient": { @@ -581,7 +586,7 @@ mod test { "address_version": 26, }, "transfered_ustx": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "vtxindex": 10, } }); @@ -614,7 +619,7 @@ mod test { let serialized_json = BlockstackOperationType::stack_stx_to_json(&op); let constructed_json = json!({ "StackStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "num_cycles": 10, "reward_addr": "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf", @@ -624,7 +629,7 @@ mod test { "address_version": 26, }, "stacked_ustx": 10, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "vtxindex": 10, } }); @@ -647,14 +652,14 @@ mod test { let serialized_json = BlockstackOperationType::pre_stx_to_json(&op); let constructed_json = json!({ "PreStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "output": { "address": "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2", "address_hash_bytes": "0xaf3f91f38aa21ade7e9f95efdbc4201eeb4cf0f8", "address_version": 26, }, - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "vtxindex": 10, } }); @@ -689,7 +694,7 @@ mod test { let serialized_json = BlockstackOperationType::delegate_stx_to_json(&op); let constructed_json = json!({ "DelegateStx": { - "block_height": 10, + "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "delegate_to": { "address": "SP24ZBZ8ZE6F48JE9G3F3HRTG9FK7E2H6K2QZ3Q1K", @@ -703,7 +708,7 @@ mod test { "address_version": 26, }, "reward_addr": [10, "16Jswqk47s9PUcyCc88MMVwzgvHPvtEpf"], - "txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "burn_txid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", "until_burn_height": null, "vtxindex": 10, } From dab5d0bd8e91a7dc74a5188bc14c1743742a6ef8 Mon Sep 17 00:00:00 2001 From: Pavitthra Pandurangan Date: Fri, 3 Feb 2023 14:09:23 -0500 Subject: [PATCH 11/11] switched from pascal case to camel case --- docs/event-dispatcher.md | 8 ++++---- src/chainstate/burn/operations/mod.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/event-dispatcher.md b/docs/event-dispatcher.md index 8a7194815e..17ca78c63e 100644 --- a/docs/event-dispatcher.md +++ b/docs/event-dispatcher.md @@ -88,7 +88,7 @@ Example: }, { "burnchain_op": { - "TransferStx": { + "transfer_stx": { "burn_block_height": 10, "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", "memo": "0x000102030405", @@ -156,7 +156,7 @@ Example: - TransferStx ```json { - "TransferStx": { + "transfer_stx": { "burn_block_height": 10, "burn_header_hash": "1410131010105914101010101013704010101010221010101010101010101010", "memo": "0x000102030405", @@ -180,7 +180,7 @@ Example: - StackStx ```json { - "StackStx": { + "stack_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "num_cycles": 10, @@ -200,7 +200,7 @@ Example: - DelegateStx ```json { - "DelegateStx": { + "delegate_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "delegate_to": { diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index b33d65f304..331f4815f0 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -433,7 +433,7 @@ impl BlockstackOperationType { pub fn pre_stx_to_json(op: &PreStxOp) -> serde_json::Value { json!({ - "PreStx": { + "pre_stx": { "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "output": stacks_addr_serialize(&op.output), @@ -445,7 +445,7 @@ impl BlockstackOperationType { pub fn stack_stx_to_json(op: &StackStxOp) -> serde_json::Value { json!({ - "StackStx": { + "stack_stx": { "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "num_cycles": op.num_cycles, @@ -460,7 +460,7 @@ impl BlockstackOperationType { pub fn transfer_stx_to_json(op: &TransferStxOp) -> serde_json::Value { json!({ - "TransferStx": { + "transfer_stx": { "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "memo": memo_serialize(&op.memo), @@ -475,7 +475,7 @@ impl BlockstackOperationType { pub fn delegate_stx_to_json(op: &DelegateStxOp) -> serde_json::Value { json!({ - "DelegateStx": { + "delegate_stx": { "burn_block_height": op.block_height, "burn_header_hash": &op.burn_header_hash.to_hex(), "delegate_to": stacks_addr_serialize(&op.delegate_to), @@ -571,7 +571,7 @@ mod test { }; let serialized_json = BlockstackOperationType::transfer_stx_to_json(&op); let constructed_json = json!({ - "TransferStx": { + "transfer_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "memo": "0x000102030405", @@ -618,7 +618,7 @@ mod test { }; let serialized_json = BlockstackOperationType::stack_stx_to_json(&op); let constructed_json = json!({ - "StackStx": { + "stack_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "num_cycles": 10, @@ -651,7 +651,7 @@ mod test { }; let serialized_json = BlockstackOperationType::pre_stx_to_json(&op); let constructed_json = json!({ - "PreStx": { + "pre_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "output": { @@ -693,7 +693,7 @@ mod test { }; let serialized_json = BlockstackOperationType::delegate_stx_to_json(&op); let constructed_json = json!({ - "DelegateStx": { + "delegate_stx": { "burn_block_height": 10, "burn_header_hash": "1010101010101010101010101010101010101010101010101010101010101010", "delegate_to": {