From 4dc7d3dc451dccb545bd221633de7a5eeb8041bc Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 21 Sep 2017 10:11:53 +0200 Subject: [PATCH] Fixed receipt serialization and RPC (#6555) --- ethcore/light/src/on_demand/request.rs | 5 +- ethcore/light/src/types/request/mod.rs | 7 +- ethcore/src/block.rs | 4 +- ethcore/src/blockchain/blockchain.rs | 11 +-- ethcore/src/client/client.rs | 14 ++- ethcore/src/client/test_client.rs | 5 +- ethcore/src/miner/miner.rs | 2 +- ethcore/src/state/mod.rs | 20 ++--- ethcore/types/src/receipt.rs | 103 ++++++++++++++-------- rpc/src/v1/tests/helpers/miner_service.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 6 +- rpc/src/v1/types/receipt.rs | 33 +++++-- 12 files changed, 127 insertions(+), 85 deletions(-) diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 495d56bd9..ead2e13d7 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use ethcore::basic_account::BasicAccount; use ethcore::encoded; use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::receipt::Receipt; +use ethcore::receipt::{Receipt, TransactionOutcome}; use ethcore::state::{self, ProvedExecution}; use ethcore::transaction::SignedTransaction; use vm::EnvInfo; @@ -973,8 +973,7 @@ mod tests { #[test] fn check_receipts() { let receipts = (0..5).map(|_| Receipt { - state_root: Some(H256::random()), - status_code: None, + outcome: TransactionOutcome::StateRoot(H256::random()), gas_used: 21_000u64.into(), log_bloom: Default::default(), logs: Vec::new(), diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index d96278c23..c762f5d04 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -1739,13 +1739,15 @@ mod tests { #[test] fn receipts_roundtrip() { + use ethcore::receipt::{Receipt, TransactionOutcome}; let req = IncompleteReceiptsRequest { hash: Field::Scalar(Default::default()), }; let full_req = Request::Receipts(req.clone()); + let receipt = Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new()); let res = ReceiptsResponse { - receipts: vec![Default::default(), Default::default()], + receipts: vec![receipt.clone(), receipt], }; let full_res = Response::Receipts(res.clone()); @@ -1900,6 +1902,7 @@ mod tests { #[test] fn responses_vec() { + use ethcore::receipt::{Receipt, TransactionOutcome}; let mut stream = RlpStream::new_list(2); stream.begin_list(0).begin_list(0); @@ -1907,7 +1910,7 @@ mod tests { let reqs = vec![ Response::Headers(HeadersResponse { headers: vec![] }), Response::HeaderProof(HeaderProofResponse { proof: vec![], hash: Default::default(), td: 100.into()}), - Response::Receipts(ReceiptsResponse { receipts: vec![Default::default()] }), + Response::Receipts(ReceiptsResponse { receipts: vec![Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new())] }), Response::Body(BodyResponse { body: body }), Response::Account(AccountResponse { proof: vec![], diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 63f79335d..f16e20eac 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -35,7 +35,7 @@ use engines::Engine; use error::{Error, BlockError, TransactionError}; use factory::Factories; use header::Header; -use receipt::Receipt; +use receipt::{Receipt, TransactionOutcome}; use state::State; use state_db::StateDB; use trace::FlatTrace; @@ -533,7 +533,7 @@ impl LockedBlock { pub fn strip_receipts(self) -> LockedBlock { let mut block = self; for receipt in &mut block.block.receipts { - receipt.state_root = None; + receipt.outcome = TransactionOutcome::Unknown; } block.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().into_vec()))); block diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 7eb7a9f2d..63df5d47b 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1481,7 +1481,7 @@ mod tests { use hash::keccak; use util::kvdb::KeyValueDB; use bigint::hash::*; - use receipt::Receipt; + use receipt::{Receipt, TransactionOutcome}; use blockchain::{BlockProvider, BlockChain, Config, ImportRoute}; use tests::helpers::*; use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer}; @@ -2048,8 +2048,7 @@ mod tests { let db = new_db(); let bc = new_chain(&genesis, db.clone()); insert_block(&db, &bc, &b1, vec![Receipt { - state_root: Some(H256::default()), - status_code: None, + outcome: TransactionOutcome::StateRoot(H256::default()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2058,8 +2057,7 @@ mod tests { ], }, Receipt { - state_root: Some(H256::default()), - status_code: None, + outcome: TransactionOutcome::StateRoot(H256::default()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2068,8 +2066,7 @@ mod tests { }]); insert_block(&db, &bc, &b2, vec![ Receipt { - state_root: Some(H256::default()), - status_code: None, + outcome: TransactionOutcome::StateRoot(H256::default()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 8659ade6f..c1a74c8dd 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -2054,7 +2054,7 @@ fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receip log_index: no_of_logs + i, }).collect(), log_bloom: receipt.log_bloom, - state_root: receipt.state_root, + outcome: receipt.outcome, } } @@ -2100,7 +2100,7 @@ mod tests { use super::transaction_receipt; use ethkey::KeyPair; use log_entry::{LogEntry, LocalizedLogEntry}; - use receipt::{Receipt, LocalizedReceipt}; + use receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; use transaction::{Transaction, LocalizedTransaction, Action}; use tests::helpers::TestEngine; @@ -2111,7 +2111,7 @@ mod tests { let block_number = 1; let block_hash = 5.into(); - let state_root = Some(99.into()); + let state_root = 99.into(); let gas_used = 10.into(); let raw_tx = Transaction { nonce: 0.into(), @@ -2139,14 +2139,12 @@ mod tests { data: vec![], }]; let receipts = vec![Receipt { - state_root: state_root, - status_code: None, + outcome: TransactionOutcome::StateRoot(state_root), gas_used: 5.into(), log_bloom: Default::default(), logs: vec![logs[0].clone()], }, Receipt { - state_root: state_root, - status_code: None, + outcome: TransactionOutcome::StateRoot(state_root), gas_used: gas_used, log_bloom: Default::default(), logs: logs.clone(), @@ -2182,7 +2180,7 @@ mod tests { log_index: 2, }], log_bloom: Default::default(), - state_root: state_root, + outcome: TransactionOutcome::StateRoot(state_root), }); } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 5052b67d6..87c4c6001 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -42,7 +42,7 @@ use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; -use receipt::{Receipt, LocalizedReceipt}; +use receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; use blockchain::extras::BlockReceipts; use error::{ImportResult, Error as EthcoreError}; use evm::{Factory as EvmFactory, VMType}; @@ -618,8 +618,7 @@ impl BlockChainClient for TestBlockChainClient { // starts with 'f' ? if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { let receipt = BlockReceipts::new(vec![Receipt::new( - Some(H256::zero()), - None, + TransactionOutcome::StateRoot(H256::zero()), U256::zero(), vec![])]); let mut rlp = RlpStream::new(); diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index c088a18be..c52459fdf 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1022,7 +1022,7 @@ impl MinerService for Miner { }, logs: receipt.logs.clone(), log_bloom: receipt.log_bloom, - state_root: receipt.state_root, + outcome: receipt.outcome.clone(), } }) } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 2472557a4..ba4c3f7ae 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -26,7 +26,7 @@ use std::fmt; use std::sync::Arc; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; -use receipt::Receipt; +use receipt::{Receipt, TransactionOutcome}; use engines::Engine; use vm::EnvInfo; use error::Error; @@ -699,21 +699,19 @@ impl State { eip658 || (env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition); - let state_root = if no_intermediate_commits { - None + let outcome = if no_intermediate_commits { + if eip658 { + TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) + } else { + TransactionOutcome::Unknown + } } else { self.commit()?; - Some(self.root().clone()) - }; - - let status_byte = if eip658 { - Some(if e.exception.is_some() { 0 } else { 1 }) - } else { - None + TransactionOutcome::StateRoot(self.root().clone()) }; let output = e.output; - let receipt = Receipt::new(state_root, status_byte, e.cumulative_gas_used, e.logs); + let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); trace!(target: "state", "Transaction receipt: {:?}", receipt); Ok(ApplyOutcome { diff --git a/ethcore/types/src/receipt.rs b/ethcore/types/src/receipt.rs index 380a9fa8d..76c5fca23 100644 --- a/ethcore/types/src/receipt.rs +++ b/ethcore/types/src/receipt.rs @@ -25,44 +25,56 @@ use rlp::*; use {BlockNumber}; use log_entry::{LogBloom, LogEntry, LocalizedLogEntry}; +/// Transaction outcome store in the receipt. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TransactionOutcome { + /// Status and state root are unknown under EIP-98 rules. + Unknown, + /// State root is known. Pre EIP-98 and EIP-658 rules. + StateRoot(H256), + /// Status code is known. EIP-658 rules. + StatusCode(u8), +} + /// Information describing execution of a transaction. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Receipt { - /// The state root after executing the transaction. Optional since EIP98 - pub state_root: Option, /// The total gas used in the block following execution of the transaction. pub gas_used: U256, /// The OR-wide combination of all logs' blooms for this transaction. pub log_bloom: LogBloom, /// The logs stemming from this transaction. pub logs: Vec, - /// Status byte. Optional before EIP-658. - pub status_code: Option, + /// Transaction outcome. + pub outcome: TransactionOutcome, } impl Receipt { /// Create a new receipt. - pub fn new(state_root: Option, status_code: Option, gas_used: U256, logs: Vec) -> Receipt { + pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec) -> Receipt { Receipt { - state_root: state_root, gas_used: gas_used, log_bloom: logs.iter().fold(LogBloom::default(), |mut b, l| { b = &b | &l.bloom(); b }), //TODO: use |= operator logs: logs, - status_code: status_code, + outcome: outcome, } } } impl Encodable for Receipt { fn rlp_append(&self, s: &mut RlpStream) { - if let Some(ref status) = self.status_code { - s.begin_list(4); - s.append(status); - } else if let Some(ref root) = self.state_root { - s.begin_list(4); - s.append(root); - } else { - s.begin_list(3); + match self.outcome { + TransactionOutcome::Unknown => { + s.begin_list(3); + }, + TransactionOutcome::StateRoot(ref root) => { + s.begin_list(4); + s.append(root); + }, + TransactionOutcome::StatusCode(ref status_code) => { + s.begin_list(4); + s.append(status_code); + }, } s.append(&self.gas_used); s.append(&self.log_bloom); @@ -74,28 +86,25 @@ impl Decodable for Receipt { fn decode(rlp: &UntrustedRlp) -> Result { if rlp.item_count()? == 3 { Ok(Receipt { - state_root: None, - status_code: None, + outcome: TransactionOutcome::Unknown, gas_used: rlp.val_at(0)?, log_bloom: rlp.val_at(1)?, logs: rlp.list_at(2)?, }) } else { - let mut receipt = Receipt { + Ok(Receipt { gas_used: rlp.val_at(1)?, log_bloom: rlp.val_at(2)?, logs: rlp.list_at(3)?, - state_root: None, - status_code: None, - }; - - let first = rlp.at(0)?; - if first.is_data() && first.data()?.len() == 1 { - receipt.status_code = Some(first.as_val()?); - } else { - receipt.state_root = Some(first.as_val()?); - } - Ok(receipt) + outcome: { + let first = rlp.at(0)?; + if first.is_data() && first.data()?.len() <= 1 { + TransactionOutcome::StatusCode(first.as_val()?) + } else { + TransactionOutcome::StateRoot(first.as_val()?) + } + } + }) } } } @@ -123,8 +132,8 @@ pub struct RichReceipt { pub logs: Vec, /// Logs bloom pub log_bloom: LogBloom, - /// State root - pub state_root: Option, + /// Transaction outcome. + pub outcome: TransactionOutcome, } /// Receipt with additional info. @@ -148,21 +157,20 @@ pub struct LocalizedReceipt { pub logs: Vec, /// Logs bloom pub log_bloom: LogBloom, - /// State root - pub state_root: Option, + /// Transaction outcome. + pub outcome: TransactionOutcome, } #[cfg(test)] mod tests { - use super::Receipt; + use super::{Receipt, TransactionOutcome}; use log_entry::LogEntry; #[test] fn test_no_state_root() { let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( - None, - None, + TransactionOutcome::Unknown, 0x40cae.into(), vec![LogEntry { address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), @@ -177,8 +185,25 @@ mod tests { fn test_basic() { let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( - Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), - None, + TransactionOutcome::StateRoot("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), + 0x40cae.into(), + vec![LogEntry { + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + topics: vec![], + data: vec![0u8; 32] + }] + ); + let encoded = ::rlp::encode(&r); + assert_eq!(&encoded[..], &expected[..]); + let decoded: Receipt = ::rlp::decode(&encoded); + assert_eq!(decoded, r); + } + + #[test] + fn test_status_code() { + let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let r = Receipt::new( + TransactionOutcome::StatusCode(0), 0x40cae.into(), vec![LogEntry { address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 92d36cf15..5314ba64f 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -261,7 +261,7 @@ impl MinerService for TestMinerService { contract_address: None, logs: r.logs.clone(), log_bloom: r.log_bloom, - state_root: r.state_root, + outcome: r.outcome.clone(), } ) } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 69804235b..537586f7b 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -30,7 +30,7 @@ use ethkey::Secret; use ethcore::account_provider::AccountProvider; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId}; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; -use ethcore::receipt::LocalizedReceipt; +use ethcore::receipt::{LocalizedReceipt, TransactionOutcome}; use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; @@ -1007,7 +1007,7 @@ fn rpc_eth_transaction_receipt() { log_index: 1, }], log_bloom: 0.into(), - state_root: Some(0.into()), + outcome: TransactionOutcome::StateRoot(0.into()), }; let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); @@ -1020,7 +1020,7 @@ fn rpc_eth_transaction_receipt() { "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","contractAddress":null,"cumulativeGasUsed":"0x20","gasUsed":"0x10","logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","data":"0x","logIndex":"0x1","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"}],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":"0x0000000000000000000000000000000000000000000000000000000000000000","statusCode":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index 29e0f277c..ca313a440 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use v1::types::{Log, H160, H256, H2048, U256}; -use ethcore::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt}; +use ethcore::receipt::{Receipt as EthReceipt, RichReceipt, LocalizedReceipt, TransactionOutcome}; /// Receipt #[derive(Debug, Serialize)] @@ -49,6 +49,25 @@ pub struct Receipt { /// Logs bloom #[serde(rename="logsBloom")] pub logs_bloom: H2048, + /// Status code + #[serde(rename="statusCode")] + pub status_code: Option, +} + +impl Receipt { + fn outcome_to_state_root(outcome: TransactionOutcome) -> Option { + match outcome { + TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) => None, + TransactionOutcome::StateRoot(root) => Some(root.into()), + } + } + + fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option { + match *outcome { + TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => None, + TransactionOutcome::StatusCode(ref code) => Some(*code), + } + } } impl From for Receipt { @@ -62,7 +81,8 @@ impl From for Receipt { gas_used: Some(r.gas_used.into()), contract_address: r.contract_address.map(Into::into), logs: r.logs.into_iter().map(Into::into).collect(), - state_root: r.state_root.map(Into::into), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), logs_bloom: r.log_bloom.into(), } } @@ -79,7 +99,8 @@ impl From for Receipt { gas_used: Some(r.gas_used.into()), contract_address: r.contract_address.map(Into::into), logs: r.logs.into_iter().map(Into::into).collect(), - state_root: r.state_root.map(Into::into), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), logs_bloom: r.log_bloom.into(), } } @@ -96,7 +117,8 @@ impl From for Receipt { gas_used: None, contract_address: None, logs: r.logs.into_iter().map(Into::into).collect(), - state_root: r.state_root.map(Into::into), + status_code: Self::outcome_to_status_code(&r.outcome), + state_root: Self::outcome_to_state_root(r.outcome), logs_bloom: r.log_bloom.into(), } } @@ -109,7 +131,7 @@ mod tests { #[test] fn receipt_serialization() { - let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f"}"#; + let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x4510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":"mined"}],"root":"0x000000000000000000000000000000000000000000000000000000000000000a","logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f","statusCode":null}"#; let receipt = Receipt { transaction_hash: Some(0.into()), @@ -136,6 +158,7 @@ mod tests { }], logs_bloom: 15.into(), state_root: Some(10.into()), + status_code: None, }; let serialized = serde_json::to_string(&receipt).unwrap();