Fixed receipt serialization and RPC (#6555)

This commit is contained in:
Arkadiy Paronyan 2017-09-21 10:11:53 +02:00 committed by Gav Wood
parent 2b39c43e81
commit 4dc7d3dc45
12 changed files with 127 additions and 85 deletions

View File

@ -21,7 +21,7 @@ use std::sync::Arc;
use ethcore::basic_account::BasicAccount; use ethcore::basic_account::BasicAccount;
use ethcore::encoded; use ethcore::encoded;
use ethcore::engines::{Engine, StateDependentProof}; use ethcore::engines::{Engine, StateDependentProof};
use ethcore::receipt::Receipt; use ethcore::receipt::{Receipt, TransactionOutcome};
use ethcore::state::{self, ProvedExecution}; use ethcore::state::{self, ProvedExecution};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
use vm::EnvInfo; use vm::EnvInfo;
@ -973,8 +973,7 @@ mod tests {
#[test] #[test]
fn check_receipts() { fn check_receipts() {
let receipts = (0..5).map(|_| Receipt { let receipts = (0..5).map(|_| Receipt {
state_root: Some(H256::random()), outcome: TransactionOutcome::StateRoot(H256::random()),
status_code: None,
gas_used: 21_000u64.into(), gas_used: 21_000u64.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
logs: Vec::new(), logs: Vec::new(),

View File

@ -1739,13 +1739,15 @@ mod tests {
#[test] #[test]
fn receipts_roundtrip() { fn receipts_roundtrip() {
use ethcore::receipt::{Receipt, TransactionOutcome};
let req = IncompleteReceiptsRequest { let req = IncompleteReceiptsRequest {
hash: Field::Scalar(Default::default()), hash: Field::Scalar(Default::default()),
}; };
let full_req = Request::Receipts(req.clone()); let full_req = Request::Receipts(req.clone());
let receipt = Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new());
let res = ReceiptsResponse { let res = ReceiptsResponse {
receipts: vec![Default::default(), Default::default()], receipts: vec![receipt.clone(), receipt],
}; };
let full_res = Response::Receipts(res.clone()); let full_res = Response::Receipts(res.clone());
@ -1900,6 +1902,7 @@ mod tests {
#[test] #[test]
fn responses_vec() { fn responses_vec() {
use ethcore::receipt::{Receipt, TransactionOutcome};
let mut stream = RlpStream::new_list(2); let mut stream = RlpStream::new_list(2);
stream.begin_list(0).begin_list(0); stream.begin_list(0).begin_list(0);
@ -1907,7 +1910,7 @@ mod tests {
let reqs = vec![ let reqs = vec![
Response::Headers(HeadersResponse { headers: vec![] }), Response::Headers(HeadersResponse { headers: vec![] }),
Response::HeaderProof(HeaderProofResponse { proof: vec![], hash: Default::default(), td: 100.into()}), 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::Body(BodyResponse { body: body }),
Response::Account(AccountResponse { Response::Account(AccountResponse {
proof: vec![], proof: vec![],

View File

@ -35,7 +35,7 @@ use engines::Engine;
use error::{Error, BlockError, TransactionError}; use error::{Error, BlockError, TransactionError};
use factory::Factories; use factory::Factories;
use header::Header; use header::Header;
use receipt::Receipt; use receipt::{Receipt, TransactionOutcome};
use state::State; use state::State;
use state_db::StateDB; use state_db::StateDB;
use trace::FlatTrace; use trace::FlatTrace;
@ -533,7 +533,7 @@ impl LockedBlock {
pub fn strip_receipts(self) -> LockedBlock { pub fn strip_receipts(self) -> LockedBlock {
let mut block = self; let mut block = self;
for receipt in &mut block.block.receipts { 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.block.header.set_receipts_root(ordered_trie_root(block.block.receipts.iter().map(|r| r.rlp_bytes().into_vec())));
block block

View File

@ -1481,7 +1481,7 @@ mod tests {
use hash::keccak; use hash::keccak;
use util::kvdb::KeyValueDB; use util::kvdb::KeyValueDB;
use bigint::hash::*; use bigint::hash::*;
use receipt::Receipt; use receipt::{Receipt, TransactionOutcome};
use blockchain::{BlockProvider, BlockChain, Config, ImportRoute}; use blockchain::{BlockProvider, BlockChain, Config, ImportRoute};
use tests::helpers::*; use tests::helpers::*;
use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer}; use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer};
@ -2048,8 +2048,7 @@ mod tests {
let db = new_db(); let db = new_db();
let bc = new_chain(&genesis, db.clone()); let bc = new_chain(&genesis, db.clone());
insert_block(&db, &bc, &b1, vec![Receipt { insert_block(&db, &bc, &b1, vec![Receipt {
state_root: Some(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
status_code: None,
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
logs: vec![ logs: vec![
@ -2058,8 +2057,7 @@ mod tests {
], ],
}, },
Receipt { Receipt {
state_root: Some(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
status_code: None,
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
logs: vec![ logs: vec![
@ -2068,8 +2066,7 @@ mod tests {
}]); }]);
insert_block(&db, &bc, &b2, vec![ insert_block(&db, &bc, &b2, vec![
Receipt { Receipt {
state_root: Some(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
status_code: None,
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
logs: vec![ logs: vec![

View File

@ -2054,7 +2054,7 @@ fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receip
log_index: no_of_logs + i, log_index: no_of_logs + i,
}).collect(), }).collect(),
log_bloom: receipt.log_bloom, log_bloom: receipt.log_bloom,
state_root: receipt.state_root, outcome: receipt.outcome,
} }
} }
@ -2100,7 +2100,7 @@ mod tests {
use super::transaction_receipt; use super::transaction_receipt;
use ethkey::KeyPair; use ethkey::KeyPair;
use log_entry::{LogEntry, LocalizedLogEntry}; use log_entry::{LogEntry, LocalizedLogEntry};
use receipt::{Receipt, LocalizedReceipt}; use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
use transaction::{Transaction, LocalizedTransaction, Action}; use transaction::{Transaction, LocalizedTransaction, Action};
use tests::helpers::TestEngine; use tests::helpers::TestEngine;
@ -2111,7 +2111,7 @@ mod tests {
let block_number = 1; let block_number = 1;
let block_hash = 5.into(); let block_hash = 5.into();
let state_root = Some(99.into()); let state_root = 99.into();
let gas_used = 10.into(); let gas_used = 10.into();
let raw_tx = Transaction { let raw_tx = Transaction {
nonce: 0.into(), nonce: 0.into(),
@ -2139,14 +2139,12 @@ mod tests {
data: vec![], data: vec![],
}]; }];
let receipts = vec![Receipt { let receipts = vec![Receipt {
state_root: state_root, outcome: TransactionOutcome::StateRoot(state_root),
status_code: None,
gas_used: 5.into(), gas_used: 5.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
logs: vec![logs[0].clone()], logs: vec![logs[0].clone()],
}, Receipt { }, Receipt {
state_root: state_root, outcome: TransactionOutcome::StateRoot(state_root),
status_code: None,
gas_used: gas_used, gas_used: gas_used,
log_bloom: Default::default(), log_bloom: Default::default(),
logs: logs.clone(), logs: logs.clone(),
@ -2182,7 +2180,7 @@ mod tests {
log_index: 2, log_index: 2,
}], }],
log_bloom: Default::default(), log_bloom: Default::default(),
state_root: state_root, outcome: TransactionOutcome::StateRoot(state_root),
}); });
} }
} }

View File

@ -42,7 +42,7 @@ use db::{NUM_COLUMNS, COL_STATE};
use header::{Header as BlockHeader, BlockNumber}; use header::{Header as BlockHeader, BlockNumber};
use filter::Filter; use filter::Filter;
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use receipt::{Receipt, LocalizedReceipt}; use receipt::{Receipt, LocalizedReceipt, TransactionOutcome};
use blockchain::extras::BlockReceipts; use blockchain::extras::BlockReceipts;
use error::{ImportResult, Error as EthcoreError}; use error::{ImportResult, Error as EthcoreError};
use evm::{Factory as EvmFactory, VMType}; use evm::{Factory as EvmFactory, VMType};
@ -618,8 +618,7 @@ impl BlockChainClient for TestBlockChainClient {
// starts with 'f' ? // starts with 'f' ?
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
let receipt = BlockReceipts::new(vec![Receipt::new( let receipt = BlockReceipts::new(vec![Receipt::new(
Some(H256::zero()), TransactionOutcome::StateRoot(H256::zero()),
None,
U256::zero(), U256::zero(),
vec![])]); vec![])]);
let mut rlp = RlpStream::new(); let mut rlp = RlpStream::new();

View File

@ -1022,7 +1022,7 @@ impl MinerService for Miner {
}, },
logs: receipt.logs.clone(), logs: receipt.logs.clone(),
log_bloom: receipt.log_bloom, log_bloom: receipt.log_bloom,
state_root: receipt.state_root, outcome: receipt.outcome.clone(),
} }
}) })
} }

View File

@ -26,7 +26,7 @@ use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY};
use receipt::Receipt; use receipt::{Receipt, TransactionOutcome};
use engines::Engine; use engines::Engine;
use vm::EnvInfo; use vm::EnvInfo;
use error::Error; use error::Error;
@ -699,21 +699,19 @@ impl<B: Backend> State<B> {
eip658 || eip658 ||
(env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition); (env_info.number >= engine.params().eip98_transition && env_info.number >= engine.params().validate_receipts_transition);
let state_root = if no_intermediate_commits { let outcome = if no_intermediate_commits {
None if eip658 {
TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 })
} else {
TransactionOutcome::Unknown
}
} else { } else {
self.commit()?; self.commit()?;
Some(self.root().clone()) TransactionOutcome::StateRoot(self.root().clone())
};
let status_byte = if eip658 {
Some(if e.exception.is_some() { 0 } else { 1 })
} else {
None
}; };
let output = e.output; 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); trace!(target: "state", "Transaction receipt: {:?}", receipt);
Ok(ApplyOutcome { Ok(ApplyOutcome {

View File

@ -25,44 +25,56 @@ use rlp::*;
use {BlockNumber}; use {BlockNumber};
use log_entry::{LogBloom, LogEntry, LocalizedLogEntry}; 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. /// Information describing execution of a transaction.
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Receipt { pub struct Receipt {
/// The state root after executing the transaction. Optional since EIP98
pub state_root: Option<H256>,
/// The total gas used in the block following execution of the transaction. /// The total gas used in the block following execution of the transaction.
pub gas_used: U256, pub gas_used: U256,
/// The OR-wide combination of all logs' blooms for this transaction. /// The OR-wide combination of all logs' blooms for this transaction.
pub log_bloom: LogBloom, pub log_bloom: LogBloom,
/// The logs stemming from this transaction. /// The logs stemming from this transaction.
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
/// Status byte. Optional before EIP-658. /// Transaction outcome.
pub status_code: Option<u8>, pub outcome: TransactionOutcome,
} }
impl Receipt { impl Receipt {
/// Create a new receipt. /// Create a new receipt.
pub fn new(state_root: Option<H256>, status_code: Option<u8>, gas_used: U256, logs: Vec<LogEntry>) -> Receipt { pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
Receipt { Receipt {
state_root: state_root,
gas_used: gas_used, gas_used: gas_used,
log_bloom: logs.iter().fold(LogBloom::default(), |mut b, l| { b = &b | &l.bloom(); b }), //TODO: use |= operator log_bloom: logs.iter().fold(LogBloom::default(), |mut b, l| { b = &b | &l.bloom(); b }), //TODO: use |= operator
logs: logs, logs: logs,
status_code: status_code, outcome: outcome,
} }
} }
} }
impl Encodable for Receipt { impl Encodable for Receipt {
fn rlp_append(&self, s: &mut RlpStream) { fn rlp_append(&self, s: &mut RlpStream) {
if let Some(ref status) = self.status_code { match self.outcome {
s.begin_list(4); TransactionOutcome::Unknown => {
s.append(status); s.begin_list(3);
} else if let Some(ref root) = self.state_root { },
TransactionOutcome::StateRoot(ref root) => {
s.begin_list(4); s.begin_list(4);
s.append(root); s.append(root);
} else { },
s.begin_list(3); TransactionOutcome::StatusCode(ref status_code) => {
s.begin_list(4);
s.append(status_code);
},
} }
s.append(&self.gas_used); s.append(&self.gas_used);
s.append(&self.log_bloom); s.append(&self.log_bloom);
@ -74,28 +86,25 @@ impl Decodable for Receipt {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> { fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
if rlp.item_count()? == 3 { if rlp.item_count()? == 3 {
Ok(Receipt { Ok(Receipt {
state_root: None, outcome: TransactionOutcome::Unknown,
status_code: None,
gas_used: rlp.val_at(0)?, gas_used: rlp.val_at(0)?,
log_bloom: rlp.val_at(1)?, log_bloom: rlp.val_at(1)?,
logs: rlp.list_at(2)?, logs: rlp.list_at(2)?,
}) })
} else { } else {
let mut receipt = Receipt { Ok(Receipt {
gas_used: rlp.val_at(1)?, gas_used: rlp.val_at(1)?,
log_bloom: rlp.val_at(2)?, log_bloom: rlp.val_at(2)?,
logs: rlp.list_at(3)?, logs: rlp.list_at(3)?,
state_root: None, outcome: {
status_code: None,
};
let first = rlp.at(0)?; let first = rlp.at(0)?;
if first.is_data() && first.data()?.len() == 1 { if first.is_data() && first.data()?.len() <= 1 {
receipt.status_code = Some(first.as_val()?); TransactionOutcome::StatusCode(first.as_val()?)
} else { } else {
receipt.state_root = Some(first.as_val()?); TransactionOutcome::StateRoot(first.as_val()?)
} }
Ok(receipt) }
})
} }
} }
} }
@ -123,8 +132,8 @@ pub struct RichReceipt {
pub logs: Vec<LogEntry>, pub logs: Vec<LogEntry>,
/// Logs bloom /// Logs bloom
pub log_bloom: LogBloom, pub log_bloom: LogBloom,
/// State root /// Transaction outcome.
pub state_root: Option<H256>, pub outcome: TransactionOutcome,
} }
/// Receipt with additional info. /// Receipt with additional info.
@ -148,21 +157,20 @@ pub struct LocalizedReceipt {
pub logs: Vec<LocalizedLogEntry>, pub logs: Vec<LocalizedLogEntry>,
/// Logs bloom /// Logs bloom
pub log_bloom: LogBloom, pub log_bloom: LogBloom,
/// State root /// Transaction outcome.
pub state_root: Option<H256>, pub outcome: TransactionOutcome,
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Receipt; use super::{Receipt, TransactionOutcome};
use log_entry::LogEntry; use log_entry::LogEntry;
#[test] #[test]
fn test_no_state_root() { fn test_no_state_root() {
let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new( let r = Receipt::new(
None, TransactionOutcome::Unknown,
None,
0x40cae.into(), 0x40cae.into(),
vec![LogEntry { vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
@ -177,8 +185,25 @@ mod tests {
fn test_basic() { fn test_basic() {
let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new( let r = Receipt::new(
Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), TransactionOutcome::StateRoot("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()),
None, 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(), 0x40cae.into(),
vec![LogEntry { vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),

View File

@ -261,7 +261,7 @@ impl MinerService for TestMinerService {
contract_address: None, contract_address: None,
logs: r.logs.clone(), logs: r.logs.clone(),
log_bloom: r.log_bloom, log_bloom: r.log_bloom,
state_root: r.state_root, outcome: r.outcome.clone(),
} }
) )
} }

View File

@ -30,7 +30,7 @@ use ethkey::Secret;
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId}; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId};
use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt; use ethcore::receipt::{LocalizedReceipt, TransactionOutcome};
use ethcore::transaction::{Transaction, Action}; use ethcore::transaction::{Transaction, Action};
use ethcore::miner::{ExternalMiner, MinerService}; use ethcore::miner::{ExternalMiner, MinerService};
use ethsync::SyncState; use ethsync::SyncState;
@ -1007,7 +1007,7 @@ fn rpc_eth_transaction_receipt() {
log_index: 1, log_index: 1,
}], }],
log_bloom: 0.into(), log_bloom: 0.into(),
state_root: Some(0.into()), outcome: TransactionOutcome::StateRoot(0.into()),
}; };
let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap();
@ -1020,7 +1020,7 @@ fn rpc_eth_transaction_receipt() {
"params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"],
"id": 1 "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())); assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
} }

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use v1::types::{Log, H160, H256, H2048, U256}; 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 /// Receipt
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -49,6 +49,25 @@ pub struct Receipt {
/// Logs bloom /// Logs bloom
#[serde(rename="logsBloom")] #[serde(rename="logsBloom")]
pub logs_bloom: H2048, pub logs_bloom: H2048,
/// Status code
#[serde(rename="statusCode")]
pub status_code: Option<u8>,
}
impl Receipt {
fn outcome_to_state_root(outcome: TransactionOutcome) -> Option<H256> {
match outcome {
TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) => None,
TransactionOutcome::StateRoot(root) => Some(root.into()),
}
}
fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option<u8> {
match *outcome {
TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => None,
TransactionOutcome::StatusCode(ref code) => Some(*code),
}
}
} }
impl From<LocalizedReceipt> for Receipt { impl From<LocalizedReceipt> for Receipt {
@ -62,7 +81,8 @@ impl From<LocalizedReceipt> for Receipt {
gas_used: Some(r.gas_used.into()), gas_used: Some(r.gas_used.into()),
contract_address: r.contract_address.map(Into::into), contract_address: r.contract_address.map(Into::into),
logs: r.logs.into_iter().map(Into::into).collect(), 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(), logs_bloom: r.log_bloom.into(),
} }
} }
@ -79,7 +99,8 @@ impl From<RichReceipt> for Receipt {
gas_used: Some(r.gas_used.into()), gas_used: Some(r.gas_used.into()),
contract_address: r.contract_address.map(Into::into), contract_address: r.contract_address.map(Into::into),
logs: r.logs.into_iter().map(Into::into).collect(), 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(), logs_bloom: r.log_bloom.into(),
} }
} }
@ -96,7 +117,8 @@ impl From<EthReceipt> for Receipt {
gas_used: None, gas_used: None,
contract_address: None, contract_address: None,
logs: r.logs.into_iter().map(Into::into).collect(), 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(), logs_bloom: r.log_bloom.into(),
} }
} }
@ -109,7 +131,7 @@ mod tests {
#[test] #[test]
fn receipt_serialization() { 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 { let receipt = Receipt {
transaction_hash: Some(0.into()), transaction_hash: Some(0.into()),
@ -136,6 +158,7 @@ mod tests {
}], }],
logs_bloom: 15.into(), logs_bloom: 15.into(),
state_root: Some(10.into()), state_root: Some(10.into()),
status_code: None,
}; };
let serialized = serde_json::to_string(&receipt).unwrap(); let serialized = serde_json::to_string(&receipt).unwrap();