Merge branch 'master' of github.com:gavofyork/ethcore into verification

This commit is contained in:
arkpar 2016-01-10 15:21:11 +01:00
commit daab45f599
13 changed files with 353 additions and 96 deletions

7
src/basic_types.rs Normal file
View File

@ -0,0 +1,7 @@
use util::*;
/// Type for a 2048-bit log-bloom, as used by our blocks.
pub type LogBloom = H2048;
/// Constant 2048-bit datum for 0. Often used as a default.
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);

View File

@ -1,9 +1,5 @@
use util::*;
use transaction::*;
use receipt::*;
use common::*;
use engine::*;
use header::*;
use env_info::*;
use state::*;
/// A transaction/receipt execution entry.
@ -21,6 +17,8 @@ pub struct Block {
archive: Vec<Entry>,
archive_set: HashSet<H256>,
uncles: Vec<Header>,
}
impl Block {
@ -30,6 +28,7 @@ impl Block {
state: state,
archive: Vec::new(),
archive_set: HashSet::new(),
uncles: Vec::new(),
}
}
@ -49,6 +48,9 @@ pub trait IsBlock {
/// Get all information on transactions in this block.
fn archive(&self) -> &Vec<Entry> { &self.block().archive }
/// Get all uncles in this block.
fn uncles(&self) -> &Vec<Header> { &self.block().uncles }
}
impl IsBlock for Block {
@ -71,7 +73,7 @@ pub struct OpenBlock<'engine> {
/// There is no function available to push a transaction. If you want that you'll need to `reopen()` it.
pub struct ClosedBlock<'engine> {
open_block: OpenBlock<'engine>,
_uncles: Vec<Header>,
uncle_bytes: Bytes,
}
/// A block that has a valid seal.
@ -79,23 +81,52 @@ pub struct ClosedBlock<'engine> {
/// The block's header has valid seal arguments. The block cannot be reversed into a ClosedBlock or OpenBlock.
pub struct SealedBlock {
block: Block,
_bytes: Bytes,
uncle_bytes: Bytes,
}
impl<'engine> OpenBlock<'engine> {
/// Create a new OpenBlock ready for transaction pushing.
pub fn new<'a>(engine: &'a Engine, db: OverlayDB, parent: &Header, last_hashes: LastHashes) -> OpenBlock<'a> {
pub fn new<'a>(engine: &'a Engine, db: OverlayDB, parent: &Header, last_hashes: LastHashes, author: Address, extra_data: Bytes) -> OpenBlock<'a> {
let mut r = OpenBlock {
block: Block::new(State::from_existing(db, parent.state_root.clone(), engine.account_start_nonce())),
engine: engine,
last_hashes: last_hashes,
};
r.block.header.set_author(author);
r.block.header.set_extra_data(extra_data);
engine.populate_from_parent(&mut r.block.header, parent);
engine.on_new_block(&mut r.block);
r
}
/// Alter the author for the block.
pub fn set_author(&mut self, author: Address) { self.block.header.set_author(author); }
/// Alter the extra_data for the block.
pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> {
if extra_data.len() > self.engine.maximum_extra_data_size() {
Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: 0, max: self.engine.maximum_extra_data_size(), found: extra_data.len()}))
} else {
self.block.header.set_extra_data(extra_data);
Ok(())
}
}
/// Add an uncle to the block, if possible.
///
/// NOTE Will check chain constraints and the uncle number but will NOT check
/// that the header itself is actually valid.
pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> {
if self.block.uncles.len() >= self.engine.maximum_uncle_count() {
return Err(BlockError::TooManyUncles);
}
// TODO: check number
// TODO: check not a direct ancestor (use last_hashes for that)
self.block.uncles.push(valid_uncle_header);
Ok(())
}
/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
@ -105,17 +136,19 @@ impl<'engine> OpenBlock<'engine> {
timestamp: self.block.header.timestamp.clone(),
difficulty: self.block.header.difficulty.clone(),
last_hashes: self.last_hashes.clone(),
gas_used: if let Some(ref t) = self.block.archive.last() {t.receipt.gas_used} else {U256::from(0)},
gas_used: self.block.archive.last().map(|t| t.receipt.gas_used).unwrap_or(U256::from(0)),
gas_limit: self.block.header.gas_limit.clone(),
}
}
/// Push a transaction into the block. It will be executed, and archived together with the receipt.
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, EthcoreError> {
/// Push a transaction into the block.
///
/// If valid, it will be executed, and archived together with the receipt.
pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
let env_info = self.env_info();
match self.block.state.apply(&env_info, self.engine, &t, true) {
Ok(x) => {
self.block.archive_set.insert(h.unwrap_or_else(||t.sha3()));
self.block.archive_set.insert(h.unwrap_or_else(||t.hash()));
self.block.archive.push(Entry { transaction: t, receipt: x.receipt });
Ok(&self.block.archive.last().unwrap().receipt)
}
@ -123,30 +156,68 @@ impl<'engine> OpenBlock<'engine> {
}
}
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles.
pub fn close(self, _uncles: Vec<Header>) -> ClosedBlock<'engine> { unimplemented!(); }
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles.
pub fn close(self) -> ClosedBlock<'engine> {
let mut s = self;
s.engine.on_close_block(&mut s.block);
s.block.header.transactions_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.transaction.rlp_bytes()).collect());
let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out();
s.block.header.uncles_hash = uncle_bytes.sha3();
s.block.header.state_root = s.block.state.root().clone();
s.block.header.receipts_root = ordered_trie_root(s.block.archive.iter().map(|ref e| e.receipt.rlp_bytes()).collect());
s.block.header.log_bloom = s.block.archive.iter().fold(LogBloom::zero(), |mut b, e| {b |= &e.receipt.log_bloom; b});
s.block.header.gas_used = s.block.archive.last().map(|t| t.receipt.gas_used).unwrap_or(U256::from(0));
s.block.header.note_dirty();
ClosedBlock::new(s, uncle_bytes)
}
}
impl<'engine> IsBlock for OpenBlock<'engine> {
fn block(&self) -> &Block { &self.block }
}
impl<'engine> ClosedBlock<'engine> {
/// Get the hash of the header without seal arguments.
pub fn preseal_hash(&self) -> H256 { unimplemented!(); }
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles.
pub fn seal(self, _seal_fields: Vec<Bytes>) -> SealedBlock { unimplemented!(); }
/// Turn this back into an `OpenBlock`.
pub fn reopen(self) -> OpenBlock<'engine> { unimplemented!(); }
}
impl<'engine> IsBlock for ClosedBlock<'engine> {
fn block(&self) -> &Block { &self.open_block.block }
}
impl<'engine> ClosedBlock<'engine> {
fn new<'a>(open_block: OpenBlock<'a>, uncle_bytes: Bytes) -> ClosedBlock<'a> {
ClosedBlock {
open_block: open_block,
uncle_bytes: uncle_bytes,
}
}
/// Get the hash of the header without seal arguments.
pub fn hash(&self) -> H256 { self.header().rlp_sha3(Seal::Without) }
/// Provide a valid seal in order to turn this into a `SealedBlock`.
///
/// NOTE: This does not check the validity of `seal` with the engine.
pub fn seal(self, seal: Vec<Bytes>) -> Result<SealedBlock, BlockError> {
let mut s = self;
if seal.len() != s.open_block.engine.seal_fields() {
return Err(BlockError::InvalidSealArity(Mismatch{expected: s.open_block.engine.seal_fields(), found: seal.len()}));
}
s.open_block.block.header.set_seal(seal);
Ok(SealedBlock { block: s.open_block.block, uncle_bytes: s.uncle_bytes })
}
/// Turn this back into an `OpenBlock`.
pub fn reopen(self) -> OpenBlock<'engine> { self.open_block }
}
impl SealedBlock {
/// Get the RLP-encoding of the block.
pub fn rlp_bytes(&self) -> Bytes {
let mut block_rlp = RlpStream::new_list(3);
self.block.header.stream_rlp(&mut block_rlp, Seal::With);
block_rlp.append_list(self.block.archive.len());
for e in self.block.archive.iter() { e.transaction.rlp_append(&mut block_rlp); }
block_rlp.append_raw(&self.uncle_bytes, 1);
block_rlp.out()
}
}
impl IsBlock for SealedBlock {
@ -155,11 +226,12 @@ impl IsBlock for SealedBlock {
#[test]
fn open_block() {
use super::*;
use spec::*;
let engine = Spec::new_test().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db = OverlayDB::new_temp();
engine.spec().ensure_db_good(&mut db);
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]);
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]);
let b = b.close();
let _ = b.seal(vec![]);
}

View File

@ -1,4 +1,6 @@
pub use util::*;
pub use basic_types::*;
pub use error::*;
pub use env_info::*;
pub use evm_schedule::*;
pub use views::*;
@ -6,3 +8,4 @@ pub use builtin::*;
pub use header::*;
pub use account::*;
pub use transaction::*;
pub use receipt::*;

View File

@ -12,7 +12,7 @@ pub trait Engine {
fn version(&self) -> SemanticVersion { SemanticVersion::new(0, 0, 0) }
/// The number of additional header fields required for this engine.
fn seal_fields(&self) -> u32 { 0 }
fn seal_fields(&self) -> usize { 0 }
/// Default values of the additional fields RLP-encoded in a raw (non-list) harness.
fn seal_rlp(&self) -> Bytes { vec![] }
@ -27,6 +27,7 @@ pub trait Engine {
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
fn maximum_extra_data_size(&self) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
fn maximum_uncle_count(&self) -> usize { 2 }
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) }
/// Block transformation functions, before and after the transactions.
@ -37,12 +38,12 @@ pub trait Engine {
/// `parent` (the parent header) and `block` (the header's full block) may be provided for additional
/// checks. Returns either a null `Ok` or a general error detailing the problem with import.
// TODO: consider including State in the params.
fn verify_block(&self, _mode: VerificationMode, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), VerificationError> { Ok(()) }
fn verify_block(&self, _mode: VerificationMode, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) }
/// Additional verification for transactions in blocks.
// TODO: Add flags for which bits of the transaction to check.
// TODO: consider including State in the params.
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), VerificationError> { Ok(()) }
fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
/// Don't forget to call Super::populateFromParent when subclassing & overriding.
// TODO: consider including State in the params.

52
src/error.rs Normal file
View File

@ -0,0 +1,52 @@
//! General error types for use in ethcore.
use util::*;
#[derive(Debug)]
pub struct Mismatch<T: fmt::Debug> {
pub expected: T,
pub found: T,
}
#[derive(Debug)]
pub struct OutOfBounds<T: fmt::Debug> {
pub min: T,
pub max: T,
pub found: T,
}
#[derive(Debug)]
pub enum BlockError {
TooManyUncles,
UncleWrongGeneration,
ExtraDataOutOfBounds(OutOfBounds<usize>),
InvalidSealArity(Mismatch<usize>),
}
#[derive(Debug)]
/// General error type which should be capable of representing all errors in ethcore.
pub enum Error {
Util(UtilError),
Block(BlockError),
UnknownEngineName(String),
}
impl From<BlockError> for Error {
fn from(err: BlockError) -> Error {
Error::Block(err)
}
}
// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted.
/*#![feature(concat_idents)]
macro_rules! assimilate {
($name:ident) => (
impl From<concat_idents!($name, Error)> for Error {
fn from(err: concat_idents!($name, Error)) -> Error {
Error:: $name (err)
}
}
)
}
assimilate!(FromHex);
assimilate!(BaseData);*/

View File

@ -24,7 +24,7 @@ impl Engine for Ethash {
/// Apply the block reward on finalisation of the block.
fn on_close_block(&self, block: &mut Block) {
let a = block.header().author.clone();
block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("block_reward").unwrap()));
block.state_mut().add_balance(&a, &decode(&self.spec().engine_params.get("blockReward").unwrap()));
}
fn verify_block(&self, mode: VerificationMode, header: &Header, parent: Option<&Header>, block: Option<&[u8]>) -> Result<(), VerificationError> {
@ -113,11 +113,9 @@ impl Ethash {
}
}
// TODO: test for on_close_block.
#[test]
fn playpen() {
fn on_close_block() {
use super::*;
use state::*;
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db = OverlayDB::new_temp();

View File

@ -56,7 +56,7 @@ mod tests {
let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
morden.to_engine();
let _ = morden.to_engine();
}
#[test]
@ -67,6 +67,6 @@ mod tests {
let genesis = frontier.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());
frontier.to_engine();
let _ = frontier.to_engine();
}
}

View File

@ -1,14 +1,5 @@
use util::*;
/// Type for a 2048-bit log-bloom, as used by our blocks.
pub type LogBloom = H2048;
/// Constant address for point 0. Often used as a default.
pub static ZERO_ADDRESS: Address = Address([0x00; 20]);
/// Constant 256-bit datum for 0. Often used as a default.
pub static ZERO_H256: H256 = H256([0x00; 32]);
/// Constant 2048-bit datum for 0. Often used as a default.
pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
use basic_types::*;
/// A block header.
///
@ -18,6 +9,7 @@ pub static ZERO_LOGBLOOM: LogBloom = H2048([0x00; 256]);
/// Doesn't do all that much on its own.
#[derive(Debug)]
pub struct Header {
// TODO: make all private.
pub parent_hash: H256,
pub timestamp: U256,
pub number: U256,
@ -36,7 +28,12 @@ pub struct Header {
pub difficulty: U256,
pub seal: Vec<Bytes>,
pub hash: RefCell<Option<H256>>, //TODO: make this private
pub hash: RefCell<Option<H256>>,
}
pub enum Seal {
With,
Without,
}
impl Header {
@ -64,19 +61,64 @@ impl Header {
}
}
pub fn author(&self) -> &Address { &self.author }
pub fn extra_data(&self) -> &Bytes { &self.extra_data }
pub fn seal(&self) -> &Vec<Bytes> { &self.seal }
// TODO: seal_at, set_seal_at &c.
pub fn set_author(&mut self, a: Address) { if a != self.author { self.author = a; self.note_dirty(); } }
pub fn set_extra_data(&mut self, a: Bytes) { if a != self.extra_data { self.extra_data = a; self.note_dirty(); } }
pub fn set_seal(&mut self, a: Vec<Bytes>) { self.seal = a; self.note_dirty(); }
/// Get the hash of this header (sha3 of the RLP).
pub fn hash(&self) -> H256 {
let mut hash = self.hash.borrow_mut();
match &mut *hash {
&mut Some(ref h) => h.clone(),
hash @ &mut None => {
let mut stream = RlpStream::new();
stream.append(self);
let h = stream.as_raw().sha3();
*hash = Some(h.clone());
h.clone()
*hash = Some(self.rlp_sha3(Seal::With));
hash.as_ref().unwrap().clone()
}
}
}
/// Note that some fields have changed. Resets the memoised hash.
pub fn note_dirty(&self) {
*self.hash.borrow_mut() = None;
}
// TODO: get hash without seal.
// TODO: make these functions traity
pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) {
s.append_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 });
s.append(&self.parent_hash);
s.append(&self.uncles_hash);
s.append(&self.author);
s.append(&self.state_root);
s.append(&self.transactions_root);
s.append(&self.receipts_root);
s.append(&self.log_bloom);
s.append(&self.difficulty);
s.append(&self.number);
s.append(&self.gas_limit);
s.append(&self.gas_used);
s.append(&self.timestamp);
s.append(&self.extra_data);
match with_seal {
Seal::With => for b in self.seal.iter() { s.append_raw(&b, 1); },
_ => {}
}
}
pub fn rlp(&self, with_seal: Seal) -> Bytes {
let mut s = RlpStream::new();
self.stream_rlp(&mut s, with_seal);
s.out()
}
pub fn rlp_sha3(&self, with_seal: Seal) -> H256 { self.rlp(with_seal).sha3() }
}
impl Decodable for Header {
@ -132,28 +174,6 @@ impl Encodable for Header {
})
}
}
/*
trait RlpStandard {
fn append(&self, s: &mut RlpStream);
}
impl RlpStandard for Header {
fn append(&self, s: &mut RlpStream) {
s.append_list(13);
s.append(self.parent_hash);
s.append_raw(self.seal[0]);
s.append_standard(self.x);
}
fn populate(&mut self, s: &Rlp) {
}
}
impl RlpStream {
fn append_standard<O>(&mut self, o: &O) where O: RlpStandard {
o.append(self);
}
}
*/
#[cfg(test)]
mod tests {

View File

@ -1,5 +1,5 @@
#![feature(cell_extras)]
#![feature(augmented_assignments)]
//! Ethcore's ethereum implementation
//!
//! ### Rust version
@ -85,6 +85,8 @@ extern crate evmjit;
extern crate ethcore_util as util;
pub mod common;
pub mod basic_types;
pub mod error;
pub mod env_info;
pub mod engine;
pub mod state;

View File

@ -1,8 +1,47 @@
use util::*;
use basic_types::LogBloom;
/// A single log's entry.
pub struct LogEntry {
pub address: Address,
pub topics: Vec<H256>,
pub data: Bytes,
}
impl RlpStandard for LogEntry {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(3);
s.append(&self.address);
s.append(&self.topics);
s.append(&self.data);
}
}
impl LogEntry {
pub fn bloom(&self) -> LogBloom {
self.topics.iter().fold(LogBloom::from_bloomed(&self.address.sha3()), |b, t| b.with_bloomed(&t.sha3()))
}
}
/// Information describing execution of a transaction.
pub struct Receipt {
// TODO
pub state_root: H256,
pub gas_used: U256,
pub log_bloom: LogBloom,
pub logs: Vec<LogEntry>,
}
impl RlpStandard for Receipt {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(4);
s.append(&self.state_root);
s.append(&self.gas_used);
s.append(&self.log_bloom);
// TODO: make work:
//s.append(&self.logs);
s.append_list(self.logs.len());
for l in self.logs.iter() {
l.rlp_append(s);
}
}
}

View File

@ -73,11 +73,11 @@ pub struct Spec {
impl Spec {
/// Convert this object into a boxed Engine of the right underlying type.
// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead.
pub fn to_engine(self) -> Result<Box<Engine>, EthcoreError> {
pub fn to_engine(self) -> Result<Box<Engine>, Error> {
match self.engine_name.as_ref() {
"NullEngine" => Ok(NullEngine::new_boxed(self)),
"Ethash" => Ok(super::ethereum::Ethash::new_boxed(self)),
_ => Err(EthcoreError::UnknownName)
_ => Err(Error::UnknownEngineName(self.engine_name.clone()))
}
}

View File

@ -1,8 +1,4 @@
use util::*;
use account::Account;
use transaction::Transaction;
use receipt::Receipt;
use env_info::EnvInfo;
use common::*;
use engine::Engine;
/// Information concerning the result of the `State::apply` operation.
@ -10,7 +6,7 @@ pub struct ApplyInfo {
pub receipt: Receipt,
}
pub type ApplyResult = Result<ApplyInfo, EthcoreError>;
pub type ApplyResult = Result<ApplyInfo, Error>;
/// Representation of the entire state of all accounts in the system.
pub struct State {
@ -79,6 +75,11 @@ impl State {
self.require_or_from(contract, false, || Account::new_contract(U256::from(0u8)), |r| r.reset_code());
}
/// Remove an existing account.
pub fn kill_account(&mut self, account: &Address) {
self.cache.borrow_mut().insert(account.clone(), None);
}
/// Get the balance of account `a`.
pub fn balance(&self, a: &Address) -> U256 {
self.get(a, false).as_ref().map(|account| account.balance().clone()).unwrap_or(U256::from(0u8))
@ -228,12 +229,11 @@ use util::hash::*;
use util::trie::*;
use util::rlp::*;
use util::uint::*;
use std::str::FromStr;
use account::*;
#[test]
fn code_from_database() {
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
let (r, db) = {
let mut s = State::new_temp();
s.require_or_from(&a, false, ||Account::new_contract(U256::from(42u32)), |_|{});
@ -250,7 +250,7 @@ fn code_from_database() {
#[test]
fn storage_at_from_database() {
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
let (r, db) = {
let mut s = State::new_temp();
s.set_storage(&a, H256::from(&U256::from(01u64)), H256::from(&U256::from(69u64)));
@ -264,7 +264,7 @@ fn storage_at_from_database() {
#[test]
fn get_from_database() {
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
let (r, db) = {
let mut s = State::new_temp();
s.inc_nonce(&a);
@ -279,11 +279,45 @@ fn get_from_database() {
assert_eq!(s.nonce(&a), U256::from(1u64));
}
#[test]
fn remove() {
let a = Address::zero();
let mut s = State::new_temp();
s.inc_nonce(&a);
assert_eq!(s.nonce(&a), U256::from(1u64));
s.kill_account(&a);
assert_eq!(s.nonce(&a), U256::from(0u64));
}
#[test]
fn remove_from_database() {
let a = Address::zero();
let (r, db) = {
let mut s = State::new_temp();
s.inc_nonce(&a);
s.commit();
assert_eq!(s.nonce(&a), U256::from(1u64));
s.drop()
};
let (r, db) = {
let mut s = State::from_existing(db, r, U256::from(0u8));
assert_eq!(s.nonce(&a), U256::from(1u64));
s.kill_account(&a);
s.commit();
assert_eq!(s.nonce(&a), U256::from(0u64));
s.drop()
};
let s = State::from_existing(db, r, U256::from(0u8));
assert_eq!(s.nonce(&a), U256::from(0u64));
}
#[test]
fn alter_balance() {
let mut s = State::new_temp();
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let b = Address::from_str("0000000000000000000000000000000000000001").unwrap();
let a = Address::zero();
let b = address_from_u64(1u64);
s.add_balance(&a, &U256::from(69u64));
assert_eq!(s.balance(&a), U256::from(69u64));
s.commit();
@ -303,7 +337,7 @@ fn alter_balance() {
#[test]
fn alter_nonce() {
let mut s = State::new_temp();
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
s.inc_nonce(&a);
assert_eq!(s.nonce(&a), U256::from(1u64));
s.inc_nonce(&a);
@ -319,7 +353,7 @@ fn alter_nonce() {
#[test]
fn balance_nonce() {
let mut s = State::new_temp();
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
assert_eq!(s.balance(&a), U256::from(0u64));
assert_eq!(s.nonce(&a), U256::from(0u64));
s.commit();
@ -330,7 +364,7 @@ fn balance_nonce() {
#[test]
fn ensure_cached() {
let mut s = State::new_temp();
let a = Address::from_str("0000000000000000000000000000000000000000").unwrap();
let a = Address::zero();
s.require(&a, false);
s.commit();
assert_eq!(s.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785");

View File

@ -9,6 +9,8 @@ pub struct Transaction {
to: Option<Address>,
value: U256,
data: Bytes,
hash: RefCell<Option<H256>>, //TODO: make this private
}
impl Transaction {
@ -21,10 +23,36 @@ impl Transaction {
pub fn is_message_call(&self) -> bool {
!self.is_contract_creation()
}
}
/// Get the hash of this transaction.
pub fn sha3(&self) -> H256 {
unimplemented!();
impl RlpStandard for Transaction {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_list(6);
s.append(&self.nonce);
s.append(&self.gas_price);
s.append(&self.gas);
s.append(&self.to);
s.append(&self.value);
s.append(&self.data);
}
}
impl Transaction {
/// Get the hash of this header (sha3 of the RLP).
pub fn hash(&self) -> H256 {
let mut hash = self.hash.borrow_mut();
match &mut *hash {
&mut Some(ref h) => h.clone(),
hash @ &mut None => {
*hash = Some(self.rlp_sha3());
hash.as_ref().unwrap().clone()
}
}
}
/// Note that some fields have changed. Resets the memoised hash.
pub fn note_dirty(&self) {
*self.hash.borrow_mut() = None;
}
}
@ -52,6 +80,7 @@ impl Decodable for Transaction {
to: try!(Decodable::decode(&d[3])),
value: try!(Decodable::decode(&d[4])),
data: try!(Decodable::decode(&d[5])),
hash: RefCell::new(None)
};
Ok(transaction)