From c012dfc3ef1ce4c95bbaf2c3fcaf665c9a319858 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 25 Jan 2017 20:22:48 +0100 Subject: [PATCH] EIP-98: Optional transaction state root (#4296) * EIP98: Optional receipt state root * Use if-else * Fixing tests --- ethcore/light/src/on_demand/request.rs | 2 +- ethcore/res/ethereum/classic.json | 3 +- ethcore/res/ethereum/eip150_test.json | 3 +- ethcore/res/ethereum/eip161_test.json | 3 +- ethcore/res/ethereum/expanse.json | 3 +- ethcore/res/ethereum/frontier.json | 3 +- ethcore/res/ethereum/frontier_like_test.json | 3 +- ethcore/res/ethereum/frontier_test.json | 3 +- ethcore/res/ethereum/homestead_test.json | 3 +- ethcore/res/ethereum/morden.json | 3 +- ethcore/res/ethereum/olympic.json | 3 +- ethcore/res/ethereum/ropsten.json | 3 +- ethcore/src/block.rs | 7 +++ ethcore/src/blockchain/blockchain.rs | 6 +- ethcore/src/client/client.rs | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/spec/spec.rs | 3 + ethcore/src/state/mod.rs | 9 ++- ethcore/src/types/receipt.rs | 61 ++++++++++++++------ json/src/spec/params.rs | 4 ++ rpc/src/v1/tests/mocked/eth.rs | 2 +- rpc/src/v1/types/receipt.rs | 10 ++-- 22 files changed, 99 insertions(+), 42 deletions(-) diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 62374d61c..025a92af6 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -268,7 +268,7 @@ mod tests { #[test] fn check_receipts() { let receipts = (0..5).map(|_| Receipt { - state_root: H256::random(), + state_root: Some(H256::random()), gas_used: 21_000u64.into(), log_bloom: Default::default(), logs: Vec::new(), diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index c1879d459..b165fe169 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -29,7 +29,8 @@ "networkID" : "0x1", "chainID": "0x3d", "forkBlock": "0x1d4c00", - "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 34ef478dc..f43abea4b 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -22,7 +22,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json index 884053d2a..b51d052c9 100644 --- a/ethcore/res/ethereum/eip161_test.json +++ b/ethcore/res/ethereum/eip161_test.json @@ -22,7 +22,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index d8cbd5b0c..17e25c22a 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -28,7 +28,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID": "0x1", - "subprotocolName": "exp" + "subprotocolName": "exp", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index 91a8ae9e6..2ab8bbe5b 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -146,7 +146,8 @@ "minGasLimit": "0x1388", "networkID" : "0x1", "forkBlock": "0x1d4c00", - "forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb" + "forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 8f41c61c8..7b73faa30 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -142,7 +142,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index 0fad8f37e..bf79729ba 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -22,7 +22,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index a757a7bc6..95bd85a94 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -22,7 +22,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 2adea0f8c..5b64b63da 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -29,7 +29,8 @@ "networkID" : "0x2", "chainID": "0x3e", "forkBlock": "0x1b34d8", - "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145" + "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index 655410ee1..baf1c7d05 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -22,7 +22,8 @@ "accountStartNonce": "0x00", "maximumExtraDataSize": "0x0400", "minGasLimit": "125000", - "networkID" : "0x0" + "networkID" : "0x0", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index 04b3bdee0..231073e4d 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -26,7 +26,8 @@ "minGasLimit": "0x1388", "networkID" : "0x3", "forkBlock": 333922, - "forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f" + "forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f", + "eip98Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 90d4a1f3f..9e3e86f62 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -375,6 +375,9 @@ impl<'x> OpenBlock<'x> { let unclosed_state = s.block.state.clone(); s.engine.on_close_block(&mut s.block); + if let Err(e) = s.block.state.commit() { + warn!("Encountered error on state commit: {}", e); + } s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().to_vec()))); let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out(); s.block.header.set_uncles_hash(uncle_bytes.sha3()); @@ -396,6 +399,10 @@ impl<'x> OpenBlock<'x> { let mut s = self; s.engine.on_close_block(&mut s.block); + + if let Err(e) = s.block.state.commit() { + warn!("Encountered error on state commit: {}", e); + } if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &SHA3_NULL_RLP { s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes().to_vec()))); } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index deae5ef30..3876a8c72 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -1896,7 +1896,7 @@ mod tests { let db = new_db(temp.as_str()); let bc = new_chain(&genesis, db.clone()); insert_block(&db, &bc, &b1, vec![Receipt { - state_root: H256::default(), + state_root: Some(H256::default()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -1905,7 +1905,7 @@ mod tests { ], }, Receipt { - state_root: H256::default(), + state_root: Some(H256::default()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -1914,7 +1914,7 @@ mod tests { }]); insert_block(&db, &bc, &b2, vec![ Receipt { - state_root: H256::default(), + state_root: Some(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 d1ed8454f..f2ad08819 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1713,7 +1713,7 @@ mod tests { let block_number = 1; let block_hash = 5.into(); - let state_root = 99.into(); + let state_root = Some(99.into()); let gas_used = 10.into(); let raw_tx = Transaction { nonce: 0.into(), diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index cfece95d2..af2f97cee 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -591,7 +591,7 @@ impl BlockChainClient for TestBlockChainClient { // starts with 'f' ? if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { let receipt = BlockReceipts::new(vec![Receipt::new( - H256::zero(), + Some(H256::zero()), U256::zero(), vec![])]); let mut rlp = RlpStream::new(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 4628fd42d..0cc32106a 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -53,6 +53,8 @@ pub struct CommonParams { pub min_gas_limit: U256, /// Fork block to check. pub fork_block: Option<(BlockNumber, H256)>, + /// Number of first block where EIP-98 rules begin. + pub eip98_transition: BlockNumber, } impl From for CommonParams { @@ -65,6 +67,7 @@ impl From for CommonParams { subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), min_gas_limit: p.min_gas_limit.into(), fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None }, + eip98_transition: p.eip98_transition.map_or(0, Into::into), } } } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 1b124bc01..1b899d935 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -519,8 +519,13 @@ impl State { // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); - self.commit()?; - let receipt = Receipt::new(self.root().clone(), e.cumulative_gas_used, e.logs); + let state_root = if env_info.number < engine.params().eip98_transition { + self.commit()?; + Some(self.root().clone()) + } else { + None + }; + let receipt = Receipt::new(state_root, e.cumulative_gas_used, e.logs); trace!(target: "state", "Transaction receipt: {:?}", receipt); Ok(ApplyOutcome{receipt: receipt, trace: e.trace}) } diff --git a/ethcore/src/types/receipt.rs b/ethcore/src/types/receipt.rs index d5c5e1c8c..f3906dc19 100644 --- a/ethcore/src/types/receipt.rs +++ b/ethcore/src/types/receipt.rs @@ -28,8 +28,8 @@ use log_entry::{LogEntry, LocalizedLogEntry}; #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "ipc", binary)] pub struct Receipt { - /// The state root after executing the transaction. - pub state_root: H256, + /// 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. @@ -40,7 +40,7 @@ pub struct Receipt { impl Receipt { /// Create a new receipt. - pub fn new(state_root: H256, gas_used: U256, logs: Vec) -> Receipt { + pub fn new(state_root: Option, gas_used: U256, logs: Vec) -> Receipt { Receipt { state_root: state_root, gas_used: gas_used, @@ -52,8 +52,12 @@ impl Receipt { impl Encodable for Receipt { fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(4); - s.append(&self.state_root); + if let Some(ref root) = self.state_root { + s.begin_list(4); + s.append(root); + } else { + s.begin_list(3); + } s.append(&self.gas_used); s.append(&self.log_bloom); s.append(&self.logs); @@ -63,13 +67,21 @@ impl Encodable for Receipt { impl Decodable for Receipt { fn decode(decoder: &D) -> Result where D: Decoder { let d = decoder.as_rlp(); - let receipt = Receipt { - state_root: d.val_at(0)?, - gas_used: d.val_at(1)?, - log_bloom: d.val_at(2)?, - logs: d.val_at(3)?, - }; - Ok(receipt) + if d.item_count() == 3 { + Ok(Receipt { + state_root: None, + gas_used: d.val_at(0)?, + log_bloom: d.val_at(1)?, + logs: d.val_at(2)?, + }) + } else { + Ok(Receipt { + state_root: d.val_at(0)?, + gas_used: d.val_at(1)?, + log_bloom: d.val_at(2)?, + logs: d.val_at(3)?, + }) + } } } @@ -98,7 +110,7 @@ pub struct RichReceipt { /// Logs bloom pub log_bloom: LogBloom, /// State root - pub state_root: H256, + pub state_root: Option, } /// Receipt with additional info. @@ -124,14 +136,29 @@ pub struct LocalizedReceipt { /// Logs bloom pub log_bloom: LogBloom, /// State root - pub state_root: H256, + pub state_root: Option, } #[test] -fn test_basic() { - let expected = ::rustc_serialize::hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); +fn test_no_state_root() { + let expected = ::rustc_serialize::hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( - "2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(), + None, + 0x40cae.into(), + vec![LogEntry { + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + topics: vec![], + data: vec![0u8; 32] + }] + ); + assert_eq!(&encode(&r)[..], &expected[..]); +} + +#[test] +fn test_basic() { + let expected = ::rustc_serialize::hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let r = Receipt::new( + Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), 0x40cae.into(), vec![LogEntry { address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 45adef4fc..26cedcf4a 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -49,6 +49,10 @@ pub struct Params { /// Expected fork block hash. #[serde(rename="forkCanonHash")] pub fork_hash: Option, + + /// See `CommonParams` docs. + #[serde(rename="eip98Transition")] + pub eip98_transition: Option, } #[cfg(test)] diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 66013f73c..1e9352f62 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -969,7 +969,7 @@ fn rpc_eth_transaction_receipt() { log_index: 1, }], log_bloom: 0.into(), - state_root: 0.into(), + state_root: Some(0.into()), }; let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index 55a514ddf..29e0f277c 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -45,7 +45,7 @@ pub struct Receipt { pub logs: Vec, /// State Root #[serde(rename="root")] - pub state_root: H256, + pub state_root: Option, /// Logs bloom #[serde(rename="logsBloom")] pub logs_bloom: H2048, @@ -62,7 +62,7 @@ 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.into(), + state_root: r.state_root.map(Into::into), logs_bloom: r.log_bloom.into(), } } @@ -79,7 +79,7 @@ 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.into(), + state_root: r.state_root.map(Into::into), logs_bloom: r.log_bloom.into(), } } @@ -96,7 +96,7 @@ impl From for Receipt { gas_used: None, contract_address: None, logs: r.logs.into_iter().map(Into::into).collect(), - state_root: r.state_root.into(), + state_root: r.state_root.map(Into::into), logs_bloom: r.log_bloom.into(), } } @@ -135,7 +135,7 @@ mod tests { log_type: "mined".into(), }], logs_bloom: 15.into(), - state_root: 10.into(), + state_root: Some(10.into()), }; let serialized = serde_json::to_string(&receipt).unwrap();