EIP-98: Optional transaction state root (#4296)

* EIP98: Optional receipt state root

* Use if-else

* Fixing tests
This commit is contained in:
Arkadiy Paronyan 2017-01-25 20:22:48 +01:00 committed by Gav Wood
parent f5a4b55dae
commit c012dfc3ef
22 changed files with 99 additions and 42 deletions

View File

@ -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(),

View File

@ -29,7 +29,8 @@
"networkID" : "0x1",
"chainID": "0x3d",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f"
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffffff"
},
"genesis": {
"seal": {

View File

@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffffff"
},
"genesis": {
"seal": {

View File

@ -28,7 +28,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID": "0x1",
"subprotocolName": "exp"
"subprotocolName": "exp",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -146,7 +146,8 @@
"minGasLimit": "0x1388",
"networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb"
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -142,7 +142,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -29,7 +29,8 @@
"networkID" : "0x2",
"chainID": "0x3e",
"forkBlock": "0x1b34d8",
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145"
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -22,7 +22,8 @@
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x0400",
"minGasLimit": "125000",
"networkID" : "0x0"
"networkID" : "0x0",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -26,7 +26,8 @@
"minGasLimit": "0x1388",
"networkID" : "0x3",
"forkBlock": 333922,
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f"
"forkCanonHash": "0x8737eb141d4f05db57af63fc8d3b4d4d8f9cddb0c4e1ab855de8c288fdc1924f",
"eip98Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -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())));
}

View File

@ -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![

View File

@ -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(),

View File

@ -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();

View File

@ -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 {
@ -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),
}
}
}

View File

@ -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})
}

View File

@ -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.
@ -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,
@ -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<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)?,
})
}
}
}
@ -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.
@ -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_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(),

View File

@ -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)]

View File

@ -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();

View File

@ -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,
@ -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(),
}
}
@ -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(),
}
}
@ -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(),
}
}
@ -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();