diff --git a/Cargo.lock b/Cargo.lock index e7dae286f5..d5c99cee86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1510,6 +1510,7 @@ dependencies = [ "rstest", "schnorrkel", "secp256k1", + "serde", "serialization", "sha-1 0.10.1", "sha2 0.10.7", @@ -5624,6 +5625,7 @@ version = "0.1.1" dependencies = [ "hex", "serde", + "serde_json", "serialization-core", "serialization-tagged", "thiserror", diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs index fc14515814..6000de4b72 100644 --- a/chainstate/src/detail/chainstateref/mod.rs +++ b/chainstate/src/detail/chainstateref/mod.rs @@ -34,7 +34,7 @@ use common::{ tokens::TokenAuxiliaryData, tokens::{get_tokens_issuance_count, TokenId}, AccountNonce, AccountType, Block, ChainConfig, GenBlock, GenBlockId, OutPointSourceId, - Transaction, TxOutput, UtxoOutPoint, + SignedTransaction, Transaction, TxMainChainIndex, TxOutput, UtxoOutPoint, }, primitives::{id::WithId, BlockDistance, BlockHeight, Id, Idable}, time_getter::TimeGetter, @@ -162,6 +162,14 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat self.time_getter.get_time() } + pub fn get_is_transaction_index_enabled(&self) -> Result { + Ok(self + .db_tx + .get_is_mainchain_tx_index_enabled() + .map_err(PropertyQueryError::from)? + .expect("Must be set on node initialization")) + } + pub fn get_best_block_id(&self) -> Result, PropertyQueryError> { self.db_tx .get_best_block_id() @@ -188,11 +196,36 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat pub fn get_mainchain_tx_index( &self, tx_id: &OutPointSourceId, - ) -> Result, PropertyQueryError> { + ) -> Result, PropertyQueryError> { log::trace!("Loading transaction index of id: {:?}", tx_id); self.db_tx.get_mainchain_tx_index(tx_id).map_err(PropertyQueryError::from) } + pub fn get_transaction_in_block( + &self, + id: Id, + ) -> Result, PropertyQueryError> { + log::trace!("Loading whether tx index is enabled: {}", id); + let is_tx_index_enabled = self.get_is_transaction_index_enabled()?; + if !is_tx_index_enabled { + return Err(PropertyQueryError::TransactionIndexDisabled); + } + log::trace!("Loading transaction index with id: {}", id); + let tx_index = self.db_tx.get_mainchain_tx_index(&OutPointSourceId::Transaction(id))?; + let tx_index = match tx_index { + Some(tx_index) => tx_index, + None => return Ok(None), + }; + log::trace!("Loading transaction with id: {}", id); + let position = match tx_index.position() { + common::chain::SpendablePosition::Transaction(pos) => pos, + common::chain::SpendablePosition::BlockReward(_) => { + panic!("In get_transaction(), a tx id led to a block reward") + } + }; + Ok(self.db_tx.get_mainchain_tx_by_position(position)?) + } + pub fn get_block_id_by_height( &self, height: &BlockHeight, diff --git a/chainstate/src/detail/query.rs b/chainstate/src/detail/query.rs index b8a276c083..f0137528be 100644 --- a/chainstate/src/detail/query.rs +++ b/chainstate/src/detail/query.rs @@ -22,7 +22,8 @@ use common::{ RPCFungibleTokenInfo, RPCNonFungibleTokenInfo, RPCTokenInfo, TokenAuxiliaryData, TokenData, TokenId, }, - Block, GenBlock, OutPointSourceId, Transaction, TxMainChainIndex, TxOutput, + Block, GenBlock, OutPointSourceId, SignedTransaction, Transaction, TxMainChainIndex, + TxOutput, }, primitives::{BlockDistance, BlockHeight, Id, Idable}, }; @@ -145,6 +146,17 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat } } + pub fn is_transaction_index_enabled(&self) -> Result { + self.chainstate_ref.get_is_transaction_index_enabled() + } + + pub fn get_transaction_in_block( + &self, + id: Id, + ) -> Result, PropertyQueryError> { + self.chainstate_ref.get_transaction_in_block(id) + } + pub fn get_locator(&self) -> Result { let best_block_index = self .chainstate_ref diff --git a/chainstate/src/interface/chainstate_interface.rs b/chainstate/src/interface/chainstate_interface.rs index 44c80ea8e4..fe73d67403 100644 --- a/chainstate/src/interface/chainstate_interface.rs +++ b/chainstate/src/interface/chainstate_interface.rs @@ -22,7 +22,7 @@ use crate::{ChainInfo, ChainstateConfig, ChainstateError, ChainstateEvent}; use chainstate_types::{BlockIndex, EpochData, GenBlockIndex, Locator}; use common::chain::block::signed_block_header::SignedBlockHeader; -use common::chain::{AccountNonce, AccountType}; +use common::chain::{AccountNonce, AccountType, SignedTransaction}; use common::{ chain::{ block::{timestamp::BlockTimestamp, Block, BlockReward, GenBlock}, @@ -115,6 +115,11 @@ pub trait ChainstateInterface: Send { &self, starting_block: &Id, ) -> Result; + fn is_transaction_index_enabled(&self) -> Result; + fn get_transaction( + &self, + tx_id: &Id, + ) -> Result, ChainstateError>; fn is_already_an_orphan(&self, block_id: &Id) -> bool; fn orphans_count(&self) -> usize; fn get_ancestor( diff --git a/chainstate/src/interface/chainstate_interface_impl.rs b/chainstate/src/interface/chainstate_interface_impl.rs index 1058f0f5c9..e7c384e1b9 100644 --- a/chainstate/src/interface/chainstate_interface_impl.rs +++ b/chainstate/src/interface/chainstate_interface_impl.rs @@ -32,8 +32,8 @@ use common::{ block::{signed_block_header::SignedBlockHeader, Block, BlockReward, GenBlock}, config::ChainConfig, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, DelegationId, OutPointSourceId, PoolId, Transaction, TxInput, - TxMainChainIndex, TxOutput, UtxoOutPoint, + AccountNonce, AccountType, DelegationId, OutPointSourceId, PoolId, SignedTransaction, + Transaction, TxInput, TxMainChainIndex, TxOutput, UtxoOutPoint, }, primitives::{id::WithId, Amount, BlockHeight, Id}, }; @@ -579,6 +579,25 @@ impl ChainstateInterfa .get_account_nonce_count(account) .map_err(ChainstateError::FailedToReadProperty) } + + fn is_transaction_index_enabled(&self) -> Result { + self.chainstate + .query() + .map_err(ChainstateError::from)? + .is_transaction_index_enabled() + .map_err(ChainstateError::FailedToReadProperty) + } + + fn get_transaction( + &self, + tx_id: &Id, + ) -> Result, ChainstateError> { + self.chainstate + .query() + .map_err(ChainstateError::from)? + .get_transaction_in_block(*tx_id) + .map_err(ChainstateError::from) + } } // TODO: remove this function. The value of an output cannot be generalized and exposed from ChainstateInterface in such way diff --git a/chainstate/src/interface/chainstate_interface_impl_delegation.rs b/chainstate/src/interface/chainstate_interface_impl_delegation.rs index a98244fd31..b3d0dae517 100644 --- a/chainstate/src/interface/chainstate_interface_impl_delegation.rs +++ b/chainstate/src/interface/chainstate_interface_impl_delegation.rs @@ -25,7 +25,7 @@ use common::chain::{ block::{signed_block_header::SignedBlockHeader, timestamp::BlockTimestamp, BlockReward}, config::ChainConfig, tokens::TokenAuxiliaryData, - AccountNonce, AccountType, OutPointSourceId, TxMainChainIndex, + AccountNonce, AccountType, OutPointSourceId, SignedTransaction, TxMainChainIndex, }; use common::chain::{Transaction, UtxoOutPoint}; use common::{ @@ -342,6 +342,17 @@ where ) -> Result, ChainstateError> { self.deref().get_account_nonce_count(account) } + + fn is_transaction_index_enabled(&self) -> Result { + self.deref().is_transaction_index_enabled() + } + + fn get_transaction( + &self, + tx_id: &Id, + ) -> Result, ChainstateError> { + self.deref().get_transaction(tx_id) + } } #[cfg(test)] diff --git a/chainstate/src/rpc.rs b/chainstate/src/rpc.rs index d43f3801db..e718465835 100644 --- a/chainstate/src/rpc.rs +++ b/chainstate/src/rpc.rs @@ -21,12 +21,12 @@ use crate::{Block, BlockSource, ChainInfo, GenBlock}; use common::{ chain::{ tokens::{RPCTokenInfo, TokenId}, - PoolId, + PoolId, SignedTransaction, Transaction, }, primitives::{Amount, BlockHeight, Id}, }; use rpc::Result as RpcResult; -use serialization::hex_encoded::HexEncoded; +use serialization::{hex_encoded::HexEncoded, json_encoded::JsonEncoded}; #[rpc::rpc(server, client, namespace = "chainstate")] trait ChainstateRpc { @@ -42,6 +42,23 @@ trait ChainstateRpc { #[method(name = "get_block")] async fn get_block(&self, id: Id) -> RpcResult>>; + /// Returns a json-encoded serialized block with the given id. + #[method(name = "get_block_json")] + async fn get_block_json(&self, id: Id) -> RpcResult>; + + /// returns a hex-encoded transaction, assuming it's in the mainchain. + /// Note: The transaction index must be enabled in the node. + #[method(name = "get_transaction")] + async fn get_transaction( + &self, + id: Id, + ) -> RpcResult>>; + + /// returns a json-encoded transaction, assuming it's in the mainchain. + /// Note: The transaction index must be enabled in the node. + #[method(name = "get_transaction_json")] + async fn get_transaction_json(&self, id: Id) -> RpcResult>; + /// Returns a hex-encoded serialized blocks from the mainchain starting from a given block height. #[method(name = "get_mainchain_blocks")] async fn get_mainchain_blocks( @@ -117,6 +134,27 @@ impl ChainstateRpcServer for super::ChainstateHandle { Ok(block.map(HexEncoded::new)) } + async fn get_block_json(&self, id: Id) -> RpcResult> { + let block: Option = + rpc::handle_result(self.call(move |this| this.get_block(id)).await)?; + Ok(block.map(JsonEncoded::new).map(|blk| blk.to_string())) + } + + async fn get_transaction( + &self, + id: Id, + ) -> RpcResult>> { + let tx: Option = + rpc::handle_result(self.call(move |this| this.get_transaction(&id)).await)?; + Ok(tx.map(HexEncoded::new)) + } + + async fn get_transaction_json(&self, id: Id) -> RpcResult> { + let tx: Option = + rpc::handle_result(self.call(move |this| this.get_transaction(&id)).await)?; + Ok(tx.map(JsonEncoded::new).map(|tx| tx.to_string())) + } + async fn get_mainchain_blocks( &self, from: BlockHeight, diff --git a/chainstate/storage/src/internal/mod.rs b/chainstate/storage/src/internal/mod.rs index b28f7ccb9a..c261fcdbf0 100644 --- a/chainstate/storage/src/internal/mod.rs +++ b/chainstate/storage/src/internal/mod.rs @@ -23,7 +23,7 @@ use common::{ tokens::{TokenAuxiliaryData, TokenId}, transaction::{Transaction, TxMainChainIndex, TxMainChainPosition}, AccountNonce, AccountType, Block, ChainConfig, DelegationId, GenBlock, OutPointSourceId, - PoolId, UtxoOutPoint, + PoolId, SignedTransaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -245,7 +245,7 @@ impl BlockchainStorageRead for Store { fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result>; + ) -> crate::Result>; fn get_block_id_by_height( &self, diff --git a/chainstate/storage/src/internal/store_tx.rs b/chainstate/storage/src/internal/store_tx.rs index 6d46d15e3c..284f1dba03 100644 --- a/chainstate/storage/src/internal/store_tx.rs +++ b/chainstate/storage/src/internal/store_tx.rs @@ -22,7 +22,7 @@ use common::{ tokens::{TokenAuxiliaryData, TokenId}, transaction::{Transaction, TxMainChainIndex, TxMainChainPosition}, AccountNonce, AccountType, Block, DelegationId, GenBlock, OutPointSourceId, PoolId, - UtxoOutPoint, + SignedTransaction, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id, Idable, H256}, }; @@ -143,7 +143,7 @@ macro_rules! impl_read_ops { fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result> { + ) -> crate::Result> { let block_id = tx_index.block_id(); match self.0.get::().get(block_id) { Err(e) => Err(e.into()), @@ -153,7 +153,7 @@ macro_rules! impl_read_ops { let begin = tx_index.byte_offset_in_block() as usize; let encoded_tx = block.get(begin..).expect("Transaction outside of block range"); - let tx = Transaction::decode(&mut &*encoded_tx) + let tx = SignedTransaction::decode(&mut &*encoded_tx) .expect("Invalid tx encoding in DB"); Ok(Some(tx)) } diff --git a/chainstate/storage/src/internal/test.rs b/chainstate/storage/src/internal/test.rs index 87c283d87e..d043e7e054 100644 --- a/chainstate/storage/src/internal/test.rs +++ b/chainstate/storage/src/internal/test.rs @@ -52,8 +52,10 @@ fn test_storage_manipulation() { // Prepare some test data let tx0 = Transaction::new(0xaabbccdd, vec![], vec![]).unwrap(); let tx1 = Transaction::new(0xbbccddee, vec![], vec![]).unwrap(); + let signed_tx0 = SignedTransaction::new(tx0.clone(), vec![]).expect("invalid witness count"); + let signed_tx1 = SignedTransaction::new(tx1.clone(), vec![]).expect("invalid witness count"); let block0 = Block::new( - vec![SignedTransaction::new(tx0.clone(), vec![]).expect("invalid witness count")], + vec![signed_tx0.clone()], Id::new(H256::default()), BlockTimestamp::from_int_seconds(12), ConsensusData::None, @@ -61,7 +63,7 @@ fn test_storage_manipulation() { ) .unwrap(); let block1 = Block::new( - vec![SignedTransaction::new(tx1.clone(), vec![]).expect("invalid witness count")], + vec![signed_tx1], Id::new(block0.get_id().get()), BlockTimestamp::from_int_seconds(34), ConsensusData::None, @@ -114,7 +116,7 @@ fn test_storage_manipulation() { let pos_tx0 = TxMainChainPosition::new(block0.get_id(), offset_tx0 as u32); assert_eq!( &store.get_mainchain_tx_by_position(&pos_tx0).unwrap().unwrap(), - &tx0 + &signed_tx0 ); // Test setting and retrieving best chain id @@ -144,7 +146,7 @@ fn test_storage_manipulation() { ); if let Ok(Some(index)) = store.get_mainchain_tx_index(&out_id_tx0) { if let SpendablePosition::Transaction(ref p) = index.position() { - assert_eq!(store.get_mainchain_tx_by_position(p), Ok(Some(tx0))); + assert_eq!(store.get_mainchain_tx_by_position(p), Ok(Some(signed_tx0))); } else { unreachable!(); }; diff --git a/chainstate/storage/src/lib.rs b/chainstate/storage/src/lib.rs index 1ffa025794..f4eaae4ef7 100644 --- a/chainstate/storage/src/lib.rs +++ b/chainstate/storage/src/lib.rs @@ -31,7 +31,9 @@ use common::chain::block::BlockReward; use common::chain::config::EpochIndex; use common::chain::tokens::{TokenAuxiliaryData, TokenId}; use common::chain::transaction::{Transaction, TxMainChainIndex, TxMainChainPosition}; -use common::chain::{AccountNonce, AccountType, Block, GenBlock, OutPointSourceId}; +use common::chain::{ + AccountNonce, AccountType, Block, GenBlock, OutPointSourceId, SignedTransaction, +}; use common::primitives::{BlockHeight, Id}; use pos_accounting::{ AccountingBlockUndo, DeltaMergeUndo, PoSAccountingDeltaData, PoSAccountingStorageRead, @@ -93,7 +95,7 @@ pub trait BlockchainStorageRead: fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result>; + ) -> crate::Result>; /// Get mainchain block by its height fn get_block_id_by_height(&self, height: &BlockHeight) -> crate::Result>>; diff --git a/chainstate/storage/src/mock/mock_impl.rs b/chainstate/storage/src/mock/mock_impl.rs index 48a73f222b..735e31ebc9 100644 --- a/chainstate/storage/src/mock/mock_impl.rs +++ b/chainstate/storage/src/mock/mock_impl.rs @@ -25,7 +25,8 @@ use common::{ block::BlockReward, config::EpochIndex, transaction::{OutPointSourceId, Transaction, TxMainChainIndex, TxMainChainPosition}, - AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, UtxoOutPoint, + AccountNonce, AccountType, Block, DelegationId, GenBlock, PoolId, SignedTransaction, + UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -64,7 +65,7 @@ mockall::mock! { fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result>; + ) -> crate::Result>; fn get_block_id_by_height( &self, @@ -320,7 +321,7 @@ mockall::mock! { fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result>; + ) -> crate::Result>; fn get_block_id_by_height( &self, @@ -435,7 +436,7 @@ mockall::mock! { fn get_mainchain_tx_by_position( &self, tx_index: &TxMainChainPosition, - ) -> crate::Result>; + ) -> crate::Result>; fn get_block_id_by_height( &self, diff --git a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs index 723b09b04c..2a0ba4d397 100644 --- a/chainstate/test-suite/src/tests/chainstate_storage_tests.rs +++ b/chainstate/test-suite/src/tests/chainstate_storage_tests.rs @@ -87,7 +87,12 @@ fn store_coin(#[case] seed: Seed) { SpendablePosition::BlockReward(_) => unreachable!(), }; assert_eq!( - db_tx.get_mainchain_tx_by_position(tx_pos).expect("ok").expect("some").get_id(), + db_tx + .get_mainchain_tx_by_position(tx_pos) + .expect("ok") + .expect("some") + .transaction() + .get_id(), tx_id ); } @@ -178,7 +183,12 @@ fn store_token(#[case] seed: Seed) { SpendablePosition::BlockReward(_) => unreachable!(), }; assert_eq!( - db_tx.get_mainchain_tx_by_position(tx_pos).expect("ok").expect("some").get_id(), + db_tx + .get_mainchain_tx_by_position(tx_pos) + .expect("ok") + .expect("some") + .transaction() + .get_id(), tx_id ); } @@ -306,6 +316,7 @@ fn reorg_store_coin(#[case] seed: Seed) { .get_mainchain_tx_by_position(tx_2_pos) .expect("ok") .expect("some") + .transaction() .get_id(), tx_2_id ); @@ -323,6 +334,7 @@ fn reorg_store_coin(#[case] seed: Seed) { .get_mainchain_tx_by_position(tx_3_pos) .expect("ok") .expect("some") + .transaction() .get_id(), tx_3_id ); @@ -503,6 +515,7 @@ fn reorg_store_token(#[case] seed: Seed) { .get_mainchain_tx_by_position(tx_2_pos) .expect("ok") .expect("some") + .transaction() .get_id(), tx_2_id ); @@ -520,6 +533,7 @@ fn reorg_store_token(#[case] seed: Seed) { .get_mainchain_tx_by_position(tx_3_pos) .expect("ok") .expect("some") + .transaction() .get_id(), tx_3_id ); diff --git a/chainstate/types/src/error.rs b/chainstate/types/src/error.rs index c420aedf51..e0a73175c4 100644 --- a/chainstate/types/src/error.rs +++ b/chainstate/types/src/error.rs @@ -60,6 +60,8 @@ pub enum PropertyQueryError { PoolBalanceReadError(PoolId), #[error("Invalid starting block height: {0}")] InvalidStartingBlockHeightForMainchainBlocks(BlockHeight), + #[error("Transaction index must be enabled to retrieve transactions by id")] + TransactionIndexDisabled, } #[derive(Error, Debug, PartialEq, Eq, Clone)] diff --git a/common/src/chain/block/block_body/mod.rs b/common/src/chain/block/block_body/mod.rs index 9a21c59225..4ad98b5ebc 100644 --- a/common/src/chain/block/block_body/mod.rs +++ b/common/src/chain/block/block_body/mod.rs @@ -36,7 +36,7 @@ pub enum BlockMerkleTreeError { } #[must_use] -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub struct BlockBody { pub(super) reward: BlockReward, pub(super) transactions: Vec, diff --git a/common/src/chain/block/block_header.rs b/common/src/chain/block/block_header.rs index f0fc32b2b9..6423efa874 100644 --- a/common/src/chain/block/block_header.rs +++ b/common/src/chain/block/block_header.rs @@ -22,7 +22,7 @@ use crate::primitives::id::{Id, Idable, H256}; use crate::primitives::{id, VersionTag}; #[must_use] -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serialization::Tagged)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serialization::Tagged, serde::Serialize)] pub struct BlockHeader { pub(super) version: VersionTag<1>, pub(super) prev_block_id: Id, diff --git a/common/src/chain/block/block_reward.rs b/common/src/chain/block/block_reward.rs index 0dbc0d7e04..ee1939bd67 100644 --- a/common/src/chain/block/block_reward.rs +++ b/common/src/chain/block/block_reward.rs @@ -24,7 +24,7 @@ use crate::{ }; /// Represents a block reward. -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub struct BlockReward { reward_outputs: Vec, } diff --git a/common/src/chain/block/block_v1.rs b/common/src/chain/block/block_v1.rs index 18b0535644..a1ccee2aa4 100644 --- a/common/src/chain/block/block_v1.rs +++ b/common/src/chain/block/block_v1.rs @@ -28,7 +28,7 @@ use super::{ }; #[must_use] -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serialization::Tagged)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serialization::Tagged, serde::Serialize)] pub struct BlockV1 { pub(super) header: SignedBlockHeader, pub(super) body: BlockBody, diff --git a/common/src/chain/block/consensus_data.rs b/common/src/chain/block/consensus_data.rs index 40b496233f..b12e47526f 100644 --- a/common/src/chain/block/consensus_data.rs +++ b/common/src/chain/block/consensus_data.rs @@ -24,7 +24,7 @@ use serialization::{Decode, Encode}; use super::timestamp::BlockTimestamp; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub enum ConsensusData { #[codec(index = 0)] None, @@ -59,7 +59,7 @@ impl ConsensusData { } /// Data required to validate a block according to the PoS consensus rules. -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub struct PoSData { /// Inputs for block reward kernel_inputs: Vec, @@ -117,7 +117,7 @@ impl PoSData { } } -#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Encode, Decode, serde::Serialize)] pub struct PoWData { bits: Compact, nonce: u128, diff --git a/common/src/chain/block/mod.rs b/common/src/chain/block/mod.rs index 6b427abefd..539c76a694 100644 --- a/common/src/chain/block/mod.rs +++ b/common/src/chain/block/mod.rs @@ -60,7 +60,7 @@ pub enum BlockCreationError { WitnessMerkleTreeMismatch(H256, H256), } -#[derive(Debug, Clone, PartialEq, Eq, DirectEncode, DirectDecode, TypeName)] +#[derive(Debug, Clone, PartialEq, Eq, DirectEncode, DirectDecode, TypeName, serde::Serialize)] #[must_use] pub enum Block { V1(BlockV1), diff --git a/common/src/chain/block/signed_block_header.rs b/common/src/chain/block/signed_block_header.rs index ced98b1e82..c48e1da730 100644 --- a/common/src/chain/block/signed_block_header.rs +++ b/common/src/chain/block/signed_block_header.rs @@ -27,7 +27,7 @@ pub enum SignedHeaderError { AttemptedMutatingSignedHeader, } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName, serde::Serialize)] pub struct BlockHeaderSignatureData { signature: Signature, } @@ -43,7 +43,7 @@ impl BlockHeaderSignatureData { } #[must_use] -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName, serde::Serialize)] pub enum BlockHeaderSignature { #[codec(index = 0)] None, @@ -51,7 +51,9 @@ pub enum BlockHeaderSignature { HeaderSignature(BlockHeaderSignatureData), } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName, serialization::Tagged)] +#[derive( + Debug, Clone, PartialEq, Eq, Encode, Decode, TypeName, serialization::Tagged, serde::Serialize, +)] pub struct SignedBlockHeader { block_header: BlockHeader, signature_data: BlockHeaderSignature, diff --git a/common/src/chain/tokens/mod.rs b/common/src/chain/tokens/mod.rs index a457dd337d..47aaa49e19 100644 --- a/common/src/chain/tokens/mod.rs +++ b/common/src/chain/tokens/mod.rs @@ -91,13 +91,13 @@ impl TokenAuxiliaryData { } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, serde::Serialize)] pub struct TokenTransfer { pub token_id: TokenId, pub amount: Amount, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, serde::Serialize)] pub struct TokenIssuance { pub token_ticker: Vec, pub amount_to_issue: Amount, @@ -105,7 +105,7 @@ pub struct TokenIssuance { pub metadata_uri: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub enum TokenData { /// TokenTransfer data to another user. If it is a token, then the token data must also be transferred to the recipient. #[codec(index = 1)] diff --git a/common/src/chain/tokens/nft.rs b/common/src/chain/tokens/nft.rs index cf016b3fe0..6ab348f6c9 100644 --- a/common/src/chain/tokens/nft.rs +++ b/common/src/chain/tokens/nft.rs @@ -16,14 +16,14 @@ use crypto::key::PublicKey; use serialization::{extras::non_empty_vec::DataOrNoVec, Decode, Encode}; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub struct NftIssuance { pub metadata: Metadata, // TODO: Implement after additional research payout, royalty and refund. // Payout might be Multisig contract with amount enforcement. } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, serde::Serialize)] pub struct TokenCreator { pub public_key: PublicKey, } @@ -34,7 +34,7 @@ impl From for TokenCreator { } } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub struct Metadata { pub creator: Option, pub name: Vec, diff --git a/common/src/chain/transaction/account_nonce.rs b/common/src/chain/transaction/account_nonce.rs index e1da687f4c..54bf682c36 100644 --- a/common/src/chain/transaction/account_nonce.rs +++ b/common/src/chain/transaction/account_nonce.rs @@ -18,7 +18,7 @@ use serialization::{Decode, Encode}; /// An incremental value that represents sequential number of spending from an account. /// It's equivalent to the nonce in Ethereum and helps preserving order of transactions and /// avoid transaction replay. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Encode, Decode, serde::Serialize)] pub struct AccountNonce(#[codec(compact)] u64); impl AccountNonce { diff --git a/common/src/chain/transaction/account_outpoint.rs b/common/src/chain/transaction/account_outpoint.rs index 17b19fa5f7..a88652863f 100644 --- a/common/src/chain/transaction/account_outpoint.rs +++ b/common/src/chain/transaction/account_outpoint.rs @@ -37,14 +37,14 @@ impl From for AccountType { /// The type represents the amount to withdraw from a particular account. /// Otherwise it's unclear how much should be deducted from an account balance. /// It also helps solving 2 additional problems: calculating fees and providing ability to sign input balance with the witness. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Encode, Decode, serde::Serialize)] pub enum AccountSpending { #[codec(index = 0)] Delegation(DelegationId, Amount), } /// Type of OutPoint that represents spending from an account -#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Encode, Decode, serde::Serialize)] pub struct AccountOutPoint { nonce: AccountNonce, account: AccountSpending, diff --git a/common/src/chain/transaction/input.rs b/common/src/chain/transaction/input.rs index 0c794fa724..2c04d07243 100644 --- a/common/src/chain/transaction/input.rs +++ b/common/src/chain/transaction/input.rs @@ -19,7 +19,7 @@ use crate::chain::AccountNonce; use super::{AccountOutPoint, AccountSpending, OutPointSourceId, UtxoOutPoint}; -#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Encode, Decode, serde::Serialize)] pub enum TxInput { Utxo(UtxoOutPoint), Account(AccountOutPoint), diff --git a/common/src/chain/transaction/mod.rs b/common/src/chain/transaction/mod.rs index de473ba670..47c85d1320 100644 --- a/common/src/chain/transaction/mod.rs +++ b/common/src/chain/transaction/mod.rs @@ -54,7 +54,7 @@ pub enum TransactionSize { SmartContractTransaction(usize), } -#[derive(Debug, Clone, PartialEq, Eq, DirectEncode, DirectDecode, TypeName)] +#[derive(Debug, Clone, PartialEq, Eq, DirectEncode, DirectDecode, TypeName, serde::Serialize)] pub enum Transaction { V1(TransactionV1), } diff --git a/common/src/chain/transaction/output/mod.rs b/common/src/chain/transaction/output/mod.rs index 2740c7c548..075c31dcd3 100644 --- a/common/src/chain/transaction/output/mod.rs +++ b/common/src/chain/transaction/output/mod.rs @@ -19,7 +19,7 @@ use crate::{ primitives::{Amount, Id}, }; use script::Script; -use serialization::{Decode, DecodeAll, Encode}; +use serialization::{hex::HexEncode, Decode, DecodeAll, Encode}; use self::{stakelock::StakePoolData, timelock::OutputTimeLock}; @@ -42,6 +42,12 @@ pub enum Destination { ClassicMultisig(PublicKeyHash), } +impl serde::Serialize for Destination { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&format!("0x{}", self.hex_encode())) + } +} + impl Addressable for Destination { type Error = AddressError; @@ -62,7 +68,7 @@ impl Addressable for Destination { } } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub enum TxOutput { #[codec(index = 0)] Transfer(OutputValue, Destination), diff --git a/common/src/chain/transaction/output/output_value.rs b/common/src/chain/transaction/output/output_value.rs index 314925bc88..5169009fce 100644 --- a/common/src/chain/transaction/output/output_value.rs +++ b/common/src/chain/transaction/output/output_value.rs @@ -20,7 +20,7 @@ use crate::{ primitives::Amount, }; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize)] pub enum OutputValue { Coin(Amount), Token(Box), diff --git a/common/src/chain/transaction/output/stakelock.rs b/common/src/chain/transaction/output/stakelock.rs index 55bda2b2df..1ef46c2bd5 100644 --- a/common/src/chain/transaction/output/stakelock.rs +++ b/common/src/chain/transaction/output/stakelock.rs @@ -20,7 +20,7 @@ use crate::primitives::{per_thousand::PerThousand, Amount}; use super::Destination; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, serde::Serialize)] pub struct StakePoolData { value: Amount, staker: Destination, diff --git a/common/src/chain/transaction/output/timelock.rs b/common/src/chain/transaction/output/timelock.rs index 69a2760891..c55c76730a 100644 --- a/common/src/chain/transaction/output/timelock.rs +++ b/common/src/chain/transaction/output/timelock.rs @@ -17,7 +17,7 @@ use serialization::{Decode, Encode}; use crate::{chain::block::timestamp::BlockTimestamp, primitives::BlockHeight}; -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Encode, Decode)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Encode, Decode, serde::Serialize)] pub enum OutputTimeLock { #[codec(index = 0)] UntilHeight(BlockHeight), diff --git a/common/src/chain/transaction/signature/inputsig/mod.rs b/common/src/chain/transaction/signature/inputsig/mod.rs index 43e8e344b0..86343043ac 100644 --- a/common/src/chain/transaction/signature/inputsig/mod.rs +++ b/common/src/chain/transaction/signature/inputsig/mod.rs @@ -18,7 +18,7 @@ pub mod authorize_pubkeyhash_spend; pub mod classical_multisig; pub mod standard_signature; -use serialization::{Decode, Encode}; +use serialization::{hex_encoded::HexEncoded, Decode, Encode}; use standard_signature::StandardInputSignature; @@ -29,3 +29,9 @@ pub enum InputWitness { #[codec(index = 1)] Standard(StandardInputSignature), } + +impl serde::Serialize for InputWitness { + fn serialize(&self, serializer: S) -> Result { + HexEncoded::new(self).serialize(serializer) + } +} diff --git a/common/src/chain/transaction/signature/sighash/sighashtype.rs b/common/src/chain/transaction/signature/sighash/sighashtype.rs index a36f0bdae0..d78fd277f3 100644 --- a/common/src/chain/transaction/signature/sighash/sighashtype.rs +++ b/common/src/chain/transaction/signature/sighash/sighashtype.rs @@ -20,7 +20,7 @@ use super::TransactionSigError; /// Specifies which parts of the transaction a signature commits to. /// /// The values of the flags are the same as in Bitcoin. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Debug, Ord, PartialOrd)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Debug, Ord, PartialOrd, serde::Serialize)] pub struct SigHashType(u8); impl SigHashType { diff --git a/common/src/chain/transaction/signed_transaction.rs b/common/src/chain/transaction/signed_transaction.rs index a85f17e254..6c9f03746d 100644 --- a/common/src/chain/transaction/signed_transaction.rs +++ b/common/src/chain/transaction/signed_transaction.rs @@ -21,7 +21,7 @@ use crate::{ use serialization::{Decode, Encode}; use utils::ensure; -#[derive(Debug, Clone, PartialEq, Eq, Encode)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, serde::Serialize)] pub struct SignedTransaction { transaction: Transaction, signatures: Vec, diff --git a/common/src/chain/transaction/transaction_v1.rs b/common/src/chain/transaction/transaction_v1.rs index ffc2a7a298..b68055d6e6 100644 --- a/common/src/chain/transaction/transaction_v1.rs +++ b/common/src/chain/transaction/transaction_v1.rs @@ -22,7 +22,7 @@ use serialization::{Decode, Encode, Tagged}; use super::Transaction; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Tagged)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Tagged, serde::Serialize)] pub struct TransactionV1 { version: VersionTag<1>, #[codec(compact)] diff --git a/common/src/chain/transaction/utxo_outpoint.rs b/common/src/chain/transaction/utxo_outpoint.rs index dba5a17bae..46a4cc374d 100644 --- a/common/src/chain/transaction/utxo_outpoint.rs +++ b/common/src/chain/transaction/utxo_outpoint.rs @@ -17,7 +17,7 @@ use crate::chain::{transaction::Transaction, Block, GenBlock, Genesis}; use crate::primitives::Id; use serialization::{Decode, Encode}; -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Ord, PartialOrd, serde::Serialize)] pub enum OutPointSourceId { #[codec(index = 0)] Transaction(Id), @@ -58,7 +58,7 @@ impl OutPointSourceId { } } -#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Encode, Decode, Ord, PartialOrd, serde::Serialize)] pub struct UtxoOutPoint { id: OutPointSourceId, index: u32, diff --git a/common/src/primitives/compact.rs b/common/src/primitives/compact.rs index 84d02f1cb2..fa70431839 100644 --- a/common/src/primitives/compact.rs +++ b/common/src/primitives/compact.rs @@ -17,7 +17,7 @@ use crate::uint::Uint256; use serialization::{Decode, Encode}; use std::ops::Shl; -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Encode, Decode)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Encode, Decode, serde::Serialize)] pub struct Compact(pub u32); impl std::fmt::Debug for Compact { diff --git a/common/src/primitives/per_thousand.rs b/common/src/primitives/per_thousand.rs index 3ef74e2d17..37e9b06383 100644 --- a/common/src/primitives/per_thousand.rs +++ b/common/src/primitives/per_thousand.rs @@ -18,7 +18,7 @@ use serialization::{Decode, Encode, Error, Input}; use super::Amount; -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Encode, Debug)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Encode, Debug, serde::Serialize)] pub struct PerThousand(u16); impl PerThousand { diff --git a/common/src/primitives/version_tag.rs b/common/src/primitives/version_tag.rs index fe8b9b5b2a..0d532df502 100644 --- a/common/src/primitives/version_tag.rs +++ b/common/src/primitives/version_tag.rs @@ -25,6 +25,12 @@ impl Default for VersionTag { } } +impl serde::Serialize for VersionTag { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_u8(V) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index c68bf9c00b..e05144e05e 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -28,6 +28,7 @@ parity-scale-codec.workspace = true rand.workspace = true ripemd.workspace = true schnorrkel.workspace = true +serde = { workspace = true, features = ["derive"] } sha-1.workspace = true sha2.workspace = true sha3.workspace = true diff --git a/crypto/src/key/mod.rs b/crypto/src/key/mod.rs index 04e5863365..506a7a3c42 100644 --- a/crypto/src/key/mod.rs +++ b/crypto/src/key/mod.rs @@ -19,6 +19,7 @@ mod key_holder; pub mod secp256k1; pub mod signature; +use serialization::hex_encoded::HexEncoded; use serialization::{Decode, Encode}; use crate::key::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey}; @@ -56,6 +57,12 @@ pub struct PublicKey { pub_key: PublicKeyHolder, } +impl serde::Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result { + HexEncoded::new(self).serialize(serializer) + } +} + impl PrivateKey { pub fn new_from_entropy(key_kind: KeyKind) -> (PrivateKey, PublicKey) { Self::new_from_rng(&mut make_true_rng(), key_kind) diff --git a/crypto/src/key/signature/mod.rs b/crypto/src/key/signature/mod.rs index eaa4e715c7..7157acfa0b 100644 --- a/crypto/src/key/signature/mod.rs +++ b/crypto/src/key/signature/mod.rs @@ -17,19 +17,24 @@ use secp256k1; use std::io::BufWriter; use num_derive::FromPrimitive; -use serialization::{Decode, DecodeAll, Encode}; +use serialization::{hex_encoded::HexEncoded, Decode, DecodeAll, Encode}; #[derive(FromPrimitive)] pub enum SignatureKind { Secp256k1Schnorr = 0, } -// #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum Signature { Secp256k1Schnorr(secp256k1::schnorr::Signature), } +impl serde::Serialize for Signature { + fn serialize(&self, serializer: S) -> Result { + HexEncoded::new(self).serialize(serializer) + } +} + impl Encode for Signature { fn encode(&self) -> Vec { let mut buf = BufWriter::new(Vec::new()); diff --git a/crypto/src/vrf/mod.rs b/crypto/src/vrf/mod.rs index ba57d20290..dcb39a11da 100644 --- a/crypto/src/vrf/mod.rs +++ b/crypto/src/vrf/mod.rs @@ -15,7 +15,7 @@ use hmac::{Hmac, Mac}; use merlin::Transcript; -use serialization::{Decode, Encode}; +use serialization::{hex_encoded::HexEncoded, Decode, Encode}; use sha2::Sha512; use crate::{ @@ -70,6 +70,12 @@ pub struct VRFPublicKey { pub_key: VRFPublicKeyHolder, } +impl serde::Serialize for VRFPublicKey { + fn serialize(&self, serializer: S) -> Result { + HexEncoded::new(self).serialize(serializer) + } +} + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Decode, Encode)] pub(crate) enum VRFPublicKeyHolder { #[codec(index = 0)] diff --git a/crypto/src/vrf/primitives.rs b/crypto/src/vrf/primitives.rs index 124ee68d1a..1dcf59304e 100644 --- a/crypto/src/vrf/primitives.rs +++ b/crypto/src/vrf/primitives.rs @@ -18,7 +18,7 @@ use serialization::{Decode, Encode}; use super::schnorrkel::data::SchnorrkelVRFReturn; #[must_use] -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode)] +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, serde::Serialize)] pub enum VRFReturn { Schnorrkel(SchnorrkelVRFReturn), } diff --git a/crypto/src/vrf/schnorrkel/data.rs b/crypto/src/vrf/schnorrkel/data.rs index 77b47affcd..e9ea28868b 100644 --- a/crypto/src/vrf/schnorrkel/data.rs +++ b/crypto/src/vrf/schnorrkel/data.rs @@ -19,7 +19,7 @@ use schnorrkel::{ vrf::{VRFInOut, VRFPreOut, VRFProof}, PublicKey, }; -use serialization::{Decode, Encode}; +use serialization::{hex_encoded::HexEncoded, Decode, Encode}; use crate::vrf::{VRFError, VRFPublicKey}; @@ -39,6 +39,12 @@ pub struct SchnorrkelVRFReturn { proof: VRFProof, } +impl serde::Serialize for SchnorrkelVRFReturn { + fn serialize(&self, serializer: S) -> Result { + HexEncoded::new(self).serialize(serializer) + } +} + impl Encode for SchnorrkelVRFReturn { fn size_hint(&self) -> usize { SCHNORKEL_RETURN_SIZE diff --git a/mocks/src/chainstate.rs b/mocks/src/chainstate.rs index b74a83f753..1d557e840a 100644 --- a/mocks/src/chainstate.rs +++ b/mocks/src/chainstate.rs @@ -26,8 +26,8 @@ use common::{ GenBlock, }, tokens::{RPCTokenInfo, TokenAuxiliaryData, TokenId}, - AccountNonce, AccountType, ChainConfig, DelegationId, OutPointSourceId, PoolId, TxInput, - TxMainChainIndex, UtxoOutPoint, + AccountNonce, AccountType, ChainConfig, DelegationId, OutPointSourceId, PoolId, + SignedTransaction, Transaction, TxInput, TxMainChainIndex, UtxoOutPoint, }, primitives::{Amount, BlockHeight, Id}, }; @@ -166,6 +166,11 @@ mockall::mock! { &self, account: AccountType, ) -> Result, ChainstateError>; + fn is_transaction_index_enabled(&self) -> Result; + fn get_transaction( + &self, + tx_id: &Id, + ) -> Result, ChainstateError>; } } diff --git a/serialization/Cargo.toml b/serialization/Cargo.toml index 8bd9417180..780837e71d 100644 --- a/serialization/Cargo.toml +++ b/serialization/Cargo.toml @@ -11,4 +11,5 @@ serialization-tagged = { path = 'tagged' } hex.workspace = true serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true thiserror.workspace = true diff --git a/serialization/src/extras/non_empty_vec.rs b/serialization/src/extras/non_empty_vec.rs index 4620fa1c13..7ecf83a650 100644 --- a/serialization/src/extras/non_empty_vec.rs +++ b/serialization/src/extras/non_empty_vec.rs @@ -21,7 +21,7 @@ use serialization_core::{Decode, Encode}; /// - If the Vec has data, it encodes to just the Vec, the Option is omitted /// - If the Vec has no data, it encodes to None /// - Some(vec![]) and None are equivalent when encoded, but when decoded result in None -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize)] pub struct DataOrNoVec(Option>); impl AsRef>> for DataOrNoVec { diff --git a/serialization/src/hex_encoded.rs b/serialization/src/hex_encoded.rs index ec19bfd0cd..ef3b389dff 100644 --- a/serialization/src/hex_encoded.rs +++ b/serialization/src/hex_encoded.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::str::FromStr; +use std::{fmt::Display, str::FromStr}; use crate::hex::{HexDecode, HexEncode, HexError}; @@ -65,3 +65,9 @@ impl FromStr for HexEncoded { ::hex_decode_all(s).map(Self) } } + +impl Display for HexEncoded { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0.hex_encode()) + } +} diff --git a/serialization/src/json_encoded.rs b/serialization/src/json_encoded.rs new file mode 100644 index 0000000000..02b2434eb8 --- /dev/null +++ b/serialization/src/json_encoded.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2023 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{fmt::Display, str::FromStr}; + +/// Wrapper that serializes objects as json encoded string for `serde` +#[derive(Debug, Clone)] +pub struct JsonEncoded(T); + +impl JsonEncoded { + pub fn new(value: T) -> Self { + Self(value) + } + + pub fn take(self) -> T { + self.0 + } +} + +impl<'de, T: serde::Deserialize<'de>> JsonEncoded { + // We cannot use FromStr because of the lifetime limitation + pub fn from_string_slice(s: &'de str) -> Result { + serde_json::from_str(s) + } +} + +impl AsRef for JsonEncoded { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl From for JsonEncoded { + fn from(value: T) -> Self { + Self(value) + } +} + +impl serde::Serialize for JsonEncoded { + fn serialize(&self, serializer: S) -> Result { + self.0.serialize(serializer) + } +} + +impl serde::Deserialize<'de>> FromStr for JsonEncoded { + type Err = serde_json::Error; + + fn from_str(s: &str) -> Result { + serde_json::from_str(s).map(Self) + } +} + +impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for JsonEncoded { + fn deserialize>(deserializer: D) -> Result { + T::deserialize(deserializer).map(Self) + } +} + +impl Display for JsonEncoded { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str( + &serde_json::to_string(&self.0).unwrap_or("".to_string()), + ) + } +} diff --git a/serialization/src/lib.rs b/serialization/src/lib.rs index 9d37024f29..8956440e8f 100644 --- a/serialization/src/lib.rs +++ b/serialization/src/lib.rs @@ -19,6 +19,7 @@ pub mod encoded; pub mod extras; pub mod hex; pub mod hex_encoded; +pub mod json_encoded; // Re-export all the constituent parts pub use serialization_core::*;