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

Initial Ethash/Block skeleton implementations. #57

Merged
merged 6 commits into from
Jan 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
26,713 changes: 26,713 additions & 0 deletions res/frontier.json

Large diffs are not rendered by default.

160 changes: 160 additions & 0 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use std::collections::hash_set::*;
use util::hash::*;
use util::bytes::*;
use util::uint::*;
use util::error::*;
use util::overlaydb::*;
use transaction::*;
use receipt::*;
use blockchain::*;
use engine::*;
use header::*;
use env_info::*;
use state::*;

/// A transaction/receipt execution entry.
pub struct Entry {
transaction: Transaction,
receipt: Receipt,
}

/// Internal type for a block's common elements.
pub struct Block {
header: Header,

/// State is the most final state in the block.
state: State,

archive: Vec<Entry>,
archive_set: HashSet<H256>,
}

impl Block {
fn new(state: State) -> Block {
Block {
header: Header::new(),
state: state,
archive: Vec::new(),
archive_set: HashSet::new(),
}
}

pub fn state_mut(&mut self) -> &mut State { &mut self.state }
}

/// Trait for a object that is_a `Block`.
pub trait IsBlock {
/// Get the block associated with this object.
fn block(&self) -> &Block;

/// Get the header associated with this object's block.
fn header(&self) -> &Header { &self.block().header }

/// Get the final state associated with this object's block.
fn state(&self) -> &State { &self.block().state }

/// Get all information on transactions in this block.
fn archive(&self) -> &Vec<Entry> { &self.block().archive }
}

impl IsBlock for Block {
fn block(&self) -> &Block { self }
}

/// Block that is ready for transactions to be added.
///
/// It's a bit like a Vec<Transaction>, eccept that whenever a transaction is pushed, we execute it and
/// maintain the system `state()`. We also archive execution receipts in preparation for later block creation.
pub struct OpenBlock<'engine> {
block: Block,
engine: &'engine Engine,
last_hashes: LastHashes,
}

/// Just like OpenBlock, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields,
/// and collected the uncles.
///
/// 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>,
}

/// A block that has a valid seal.
///
/// 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,
}

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> {
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,
};

engine.populate_from_parent(&mut r.block.header, parent);
engine.on_new_block(&mut r.block);
r
}

/// Get the environment info concerning this block.
pub fn env_info(&self) -> EnvInfo {
// TODO: memoise.
EnvInfo {
number: self.block.header.number.clone(),
author: self.block.header.author.clone(),
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_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> {
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.push(Entry { transaction: t, receipt: x.receipt });
Ok(&self.block.archive.last().unwrap().receipt)
}
Err(x) => Err(x)
}
}

/// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles.
pub fn close(self, _bc: &BlockChain) -> ClosedBlock { unimplemented!(); }
}

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 SealedBlock {
}

impl IsBlock for SealedBlock {
fn block(&self) -> &Block { &self.block }
}
6 changes: 6 additions & 0 deletions src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ impl Builtin {
}
None
}

/// Simple forwarder for cost.
pub fn cost(&self, s: usize) -> U256 { (*self.cost)(s) }

/// Simple forwarder for execute.
pub fn execute(&self, input: &[u8], output: &mut[u8]) { (*self.execute)(input, output); }
}

pub fn copy_to(src: &[u8], dest: &mut[u8]) {
Expand Down
21 changes: 10 additions & 11 deletions src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::collections::hash_map::*;
use util::bytes::*;
use util::hash::*;
use util::uint::*;
use util::rlp::*;
use util::semantic_version::*;
use util::error::*;
use header::Header;
use transaction::Transaction;
use block::Block;
use spec::Spec;
use evm_schedule::EvmSchedule;
use env_info::EnvInfo;
Expand Down Expand Up @@ -34,19 +36,17 @@ 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, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximum_extra_data_size").unwrap()) }
fn account_start_nonce(&self, _env_info: &EnvInfo) -> U256 { decode(&self.spec().engine_params.get("account_start_nonce").unwrap()) }
// TODO: refactor in terms of `on_preseal_block`
fn block_reward(&self, _env_info: &EnvInfo) -> U256 { decode(&self.spec().engine_params.get("block_reward").unwrap()) }
fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("account_start_nonce").unwrap()) }

/// Block transformation functions, before and after the transactions.
// fn on_new_block(&self, _env_info: &EnvInfo, _block: &mut Block) -> Result<(), EthcoreError> {}
// fn on_preseal_block(&self, _env_info: &EnvInfo, _block: &mut Block) -> Result<(), EthcoreError> {}
fn on_new_block(&self, _block: &mut Block) {}
fn on_close_block(&self, _block: &mut Block) {}

/// Verify that `header` is valid.
/// `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(&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<(), EthcoreError> { Ok(()) }

/// Additional verification for transactions in blocks.
// TODO: Add flags for which bits of the transaction to check.
Expand All @@ -55,14 +55,13 @@ pub trait Engine {

/// Don't forget to call Super::populateFromParent when subclassing & overriding.
// TODO: consider including State in the params.
fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) -> Result<(), EthcoreError> { Ok(()) }
fn populate_from_parent(&self, _header: &mut Header, _parent: &Header) {}

// TODO: builtin contract routing - to do this properly, it will require removing the built-in configuration-reading logic
// from Spec into here and removing the Spec::builtins field.
/* fn is_builtin(&self, a: Address) -> bool;
fn cost_of_builtin(&self, a: Address, in: &[u8]) -> bignum;
fn execute_builtin(&self, a: Address, in: &[u8], out: &mut [u8]);
*/
fn is_builtin(&self, a: &Address) -> bool { self.spec().builtins.contains_key(a) }
fn cost_of_builtin(&self, a: &Address, input: &[u8]) -> U256 { self.spec().builtins.get(a).unwrap().cost(input.len()) }
fn execute_builtin(&self, a: &Address, input: &[u8], output: &mut [u8]) { self.spec().builtins.get(a).unwrap().execute(input, output); }

// TODO: sealing stuff - though might want to leave this for later.
}
11 changes: 11 additions & 0 deletions src/ethash.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//use util::error::*;
use util::rlp::decode;
use engine::Engine;
use spec::Spec;
use block::*;
use evm_schedule::EvmSchedule;
use env_info::EnvInfo;

Expand All @@ -19,4 +22,12 @@ impl Engine for Ethash {
fn name(&self) -> &str { "Ethash" }
fn spec(&self) -> &Spec { &self.spec }
fn evm_schedule(&self, _env_info: &EnvInfo) -> EvmSchedule { EvmSchedule::new_frontier() }

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

// TODO: test for on_close_block.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,4 @@ pub mod views;
pub mod blockchain;
pub mod extras;
pub mod ethash;
pub mod block;
2 changes: 2 additions & 0 deletions src/receipt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use util::hash::*;
use util::uint::*;

/// Information describing execution of a transaction.
pub struct Receipt {
// TODO
pub state_root: H256,
pub gas_used: U256,
}
52 changes: 40 additions & 12 deletions src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,14 @@ impl Spec {
builtins.insert(addr.clone(), builtin);
}
}
let balance = if let Some(&Json::String(ref b)) = acc.find("balance") {U256::from_dec_str(b).unwrap_or(U256::from(0))} else {U256::from(0)};
let nonce = if let Some(&Json::String(ref n)) = acc.find("nonce") {U256::from_dec_str(n).unwrap_or(U256::from(0))} else {U256::from(0)};
let balance = acc.find("balance").and_then(|x| match x { &Json::String(ref b) => U256::from_dec_str(b).ok(), _ => None });
let nonce = acc.find("nonce").and_then(|x| match x { &Json::String(ref b) => U256::from_dec_str(b).ok(), _ => None });
// let balance = if let Some(&Json::String(ref b)) = acc.find("balance") {U256::from_dec_str(b).unwrap_or(U256::from(0))} else {U256::from(0)};
// let nonce = if let Some(&Json::String(ref n)) = acc.find("nonce") {U256::from_dec_str(n).unwrap_or(U256::from(0))} else {U256::from(0)};
// TODO: handle code & data if they exist.
state.insert(addr, Account::new_basic(balance, nonce));
if balance.is_some() || nonce.is_some() {
state.insert(addr, Account::new_basic(balance.unwrap_or(U256::from(0)), nonce.unwrap_or(U256::from(0))));
}
}
}

Expand Down Expand Up @@ -290,7 +294,7 @@ impl Spec {
}

/// Creates the actual Morden network chain spec.
pub fn new_morden() -> Spec {
pub fn new_morden_manual() -> Spec {
Spec {
engine_name: "Ethash".to_string(),
engine_params: vec![
Expand Down Expand Up @@ -337,6 +341,23 @@ impl Spec {
state_root_memo: RefCell::new(None),
}
}

/// Create a new Spec from a JSON UTF-8 data resource `data`.
pub fn from_json_utf8(data: &[u8]) -> Spec {
Self::from_json_str(::std::str::from_utf8(data).unwrap())
}

/// Create a new Spec from a JSON string.
pub fn from_json_str(s: &str) -> Spec {
let json = Json::from_str(s).expect("Json is invalid");
Self::from_json(json)
}

/// Create a new Morden chain spec.
pub fn new_morden() -> Spec { Self::from_json_utf8(include_bytes!("../res/morden.json")) }

/// Create a new Frontier chain spec.
pub fn new_frontier() -> Spec { Self::from_json_utf8(include_bytes!("../res/frontier.json")) }
}

#[cfg(test)]
Expand All @@ -349,24 +370,31 @@ mod tests {
use super::*;

#[test]
fn all() {
let morden = Spec::new_morden();
// let engine = morden.to_engine(); // Ethash doesn't exist as an engine yet, so would fail.
fn morden_manual() {
let morden = Spec::new_morden_manual();

assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
}

#[test]
fn morden_res() {
let morden_json = Json::from_str(::std::str::from_utf8(include_bytes!("../res/morden.json")).unwrap()).expect("Json is invalid");
let morden = Spec::from_json(morden_json);

// let engine = morden.to_engine(); // Ethash doesn't exist as an engine yet, so would fail.
fn morden() {
let morden = Spec::new_morden();

assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap());
let genesis = morden.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap());
}

#[test]
fn frontier() {
let frontier = Spec::new_frontier();

assert_eq!(*frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap());
let genesis = frontier.genesis_block();
assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap());

let engine = frontier.to_engine();
}
}
Loading