Merge pull request #62 from gavofyork/ethash
Block complete. Needs tests.
This commit is contained in:
commit
1bcb6f8ee3
123
src/block.rs
123
src/block.rs
@ -17,6 +17,8 @@ pub struct Block {
|
||||
|
||||
archive: Vec<Entry>,
|
||||
archive_set: HashSet<H256>,
|
||||
|
||||
uncles: Vec<Header>,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
@ -26,6 +28,7 @@ impl Block {
|
||||
state: state,
|
||||
archive: Vec::new(),
|
||||
archive_set: HashSet::new(),
|
||||
uncles: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,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 {
|
||||
@ -67,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: Bytes,
|
||||
uncle_bytes: Bytes,
|
||||
}
|
||||
|
||||
/// A block that has a valid seal.
|
||||
@ -75,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.
|
||||
@ -106,12 +141,14 @@ impl<'engine> OpenBlock<'engine> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
@ -120,17 +157,14 @@ impl<'engine> OpenBlock<'engine> {
|
||||
}
|
||||
|
||||
/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure out the uncles.
|
||||
pub fn close(self, uncles: Vec<Header>, author: Address, extra_data: Bytes) -> ClosedBlock<'engine> {
|
||||
pub fn close(self) -> ClosedBlock<'engine> {
|
||||
let mut s = self;
|
||||
// populate rest of header.
|
||||
s.engine.on_close_block(&mut s.block);
|
||||
s.block.header.author = author;
|
||||
// s.header.transactions_root = ...;
|
||||
let uncle_bytes = uncles.iter().fold(RlpStream::new_list(uncles.len()), |mut s, u| {s.append(&u.rlp(Seal::With)); s} ).out();
|
||||
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.extra_data = extra_data;
|
||||
s.block.header.state_root = s.block.state.root().clone();
|
||||
// s.header.receipts_root = ...;
|
||||
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();
|
||||
@ -143,29 +177,47 @@ impl<'engine> IsBlock for OpenBlock<'engine> {
|
||||
fn block(&self) -> &Block { &self.block }
|
||||
}
|
||||
|
||||
impl<'engine> ClosedBlock<'engine> {
|
||||
fn new<'a>(open_block: OpenBlock<'a>, uncles: Bytes) -> ClosedBlock<'a> {
|
||||
ClosedBlock {
|
||||
open_block: open_block,
|
||||
uncles: uncles,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@ -175,12 +227,11 @@ impl IsBlock for SealedBlock {
|
||||
#[test]
|
||||
fn open_block() {
|
||||
use spec::*;
|
||||
use ethereum::*;
|
||||
let engine = new_morden().to_engine().unwrap();
|
||||
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 = b.close(vec![], Address::zero(), vec![]);
|
||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap());
|
||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]);
|
||||
let b = b.close();
|
||||
let _ = b.seal(vec![]);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub use util::*;
|
||||
pub use basic_types::*;
|
||||
pub use error::*;
|
||||
pub use env_info::*;
|
||||
pub use evm_schedule::*;
|
||||
pub use views::*;
|
||||
@ -7,4 +8,4 @@ pub use builtin::*;
|
||||
pub use header::*;
|
||||
pub use account::*;
|
||||
pub use transaction::*;
|
||||
pub use receipt::*;
|
||||
pub use receipt::*;
|
@ -11,7 +11,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![] }
|
||||
|
||||
@ -25,7 +25,8 @@ pub trait Engine {
|
||||
fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule;
|
||||
|
||||
/// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`.
|
||||
fn maximum_extra_data_size(&self, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) }
|
||||
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.
|
||||
@ -36,12 +37,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, _header: &Header, _parent: Option<&Header>, _block: Option<&[u8]>) -> Result<(), EthcoreError> { Ok(()) }
|
||||
fn verify_block(&self, _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<(), EthcoreError> { 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
52
src/error.rs
Normal 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);*/
|
@ -34,7 +34,7 @@ fn on_close_block() {
|
||||
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 = b.close(vec![], Address::zero(), vec![]);
|
||||
let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()], Address::zero(), vec![]);
|
||||
let b = b.close();
|
||||
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244F40000").unwrap());
|
||||
}
|
@ -9,6 +9,7 @@ use basic_types::*;
|
||||
/// 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,
|
||||
@ -27,7 +28,7 @@ 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 {
|
||||
@ -60,6 +61,16 @@ 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();
|
||||
@ -163,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 {
|
||||
|
@ -86,6 +86,7 @@ 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;
|
||||
|
@ -1,10 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user