Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

EIP-98: Optional transaction state root #4296

Merged
merged 3 commits into from
Jan 25, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethcore/light/src/on_demand/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/classic.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"networkID" : "0x1",
"chainID": "0x3d",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/expanse.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID": "0x1",
"subprotocolName": "exp"
"subprotocolName": "exp",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/frontier.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@
"minGasLimit": "0x1388",
"networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/frontier_like_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/frontier_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/homestead_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/morden.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"networkID" : "0x2",
"chainID": "0x3e",
"forkBlock": "0x1b34d8",
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/olympic.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x0400",
"minGasLimit": "125000",
"networkID" : "0x0"
"networkID" : "0x0",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
3 changes: 2 additions & 1 deletion ethcore/res/ethereum/ropsten.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"minGasLimit": "0x1388",
"networkID" : "0x3",
"forkBlock": 333922,
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f"
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {
Expand Down
7 changes: 7 additions & 0 deletions ethcore/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that error be propagated upstream?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how commit error are handles in the rest of the codebase.

}
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());
Expand All @@ -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())));
}
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,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![
Expand All @@ -1911,7 +1911,7 @@ mod tests {
],
},
Receipt {
state_root: H256::default(),
state_root: Some(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
logs: vec![
Expand All @@ -1920,7 +1920,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![
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1698,7 +1698,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(),
Expand Down
2 changes: 1 addition & 1 deletion ethcore/src/client/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions ethcore/src/spec/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ethjson::spec::Params> for CommonParams {
Expand All @@ -65,6 +67,7 @@ impl From<ethjson::spec::Params> 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),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that enable eip98 automatically for spec files where that prop is not defined (private chains?)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the intention

}
}
}
Expand Down
10 changes: 8 additions & 2 deletions ethcore/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,14 @@ 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 = match env_info.number < engine.params().eip98_transition {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using if-else here would a bit more idiomatic and suprisingly shorter then match.

true => {
self.commit()?;
Some(self.root().clone())
},
false => 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})
}
Expand Down
57 changes: 42 additions & 15 deletions ethcore/src/types/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<H256>,
/// 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.
Expand All @@ -40,7 +40,7 @@ pub struct Receipt {

impl Receipt {
/// Create a new receipt.
pub fn new(state_root: H256, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
pub fn new(state_root: Option<H256>, gas_used: U256, logs: Vec<LogEntry>) -> Receipt {
Receipt {
state_root: state_root,
gas_used: gas_used,
Expand All @@ -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);
Expand All @@ -63,13 +67,21 @@ impl Encodable for Receipt {
impl Decodable for Receipt {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> 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)?,
})
}
}
}

Expand Down Expand Up @@ -98,7 +110,7 @@ pub struct RichReceipt {
/// Logs bloom
pub log_bloom: LogBloom,
/// State root
pub state_root: H256,
pub state_root: Option<H256>,
}

/// Receipt with additional info.
Expand All @@ -124,14 +136,29 @@ pub struct LocalizedReceipt {
/// Logs bloom
pub log_bloom: LogBloom,
/// State root
pub state_root: H256,
pub state_root: Option<H256>,
}

#[test]
fn test_no_state_root() {
let expected = ::rustc_serialize::hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new(
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(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
Some("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()),
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
Expand Down
4 changes: 4 additions & 0 deletions json/src/spec/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub struct Params {
/// Expected fork block hash.
#[serde(rename="forkCanonHash")]
pub fork_hash: Option<H256>,

/// See `CommonParams` docs.
#[serde(rename="eip98Transition")]
pub eip98_transition: Option<Uint>,
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion rpc/src/v1/tests/mocked/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 5 additions & 5 deletions rpc/src/v1/types/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct Receipt {
pub logs: Vec<Log>,
/// State Root
#[serde(rename="root")]
pub state_root: H256,
pub state_root: Option<H256>,
/// Logs bloom
#[serde(rename="logsBloom")]
pub logs_bloom: H2048,
Expand All @@ -62,7 +62,7 @@ impl From<LocalizedReceipt> 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(),
}
}
Expand All @@ -79,7 +79,7 @@ impl From<RichReceipt> 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(),
}
}
Expand All @@ -96,7 +96,7 @@ impl From<EthReceipt> 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(),
}
}
Expand Down Expand Up @@ -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();
Expand Down