From 957dd36328cb1daf85afa249758d8e9e0ad5df01 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 4 Jan 2016 21:47:45 +0000 Subject: [PATCH] Make builtins work. --- Cargo.toml | 2 +- src/builtin.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++ src/spec.rs | 36 +++++++++++----- 3 files changed, 137 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a74bf589..81c7162b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Ethcore "] [dependencies] log = "0.3" env_logger = "0.3" -ethcore-util = "0.1.0" +ethcore-util = { path = "../ethcore-util" } rustc-serialize = "0.3" flate2 = "0.2" rocksdb = "0.2.1" diff --git a/src/builtin.rs b/src/builtin.rs index 12ed610a6..6a15c48b2 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,4 +1,6 @@ +use std::cmp::min; use util::uint::*; +use rustc_serialize::json::Json; /// Definition of a contract whose implementation is built-in. pub struct Builtin { @@ -8,3 +10,112 @@ pub struct Builtin { /// being placed into the second. pub execute: Box, } + +/* +ETH_REGISTER_PRECOMPILED(ecrecover)(bytesConstRef _in, bytesRef _out) +{ + struct inType + { + h256 hash; + h256 v; + h256 r; + h256 s; + } in; + + memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); + + h256 ret; + u256 v = (u256)in.v; + if (v >= 27 && v <= 28) + { + SignatureStruct sig(in.r, in.s, (byte)((int)v - 27)); + if (sig.isValid()) + { + try + { + if (Public rec = recover(sig, in.hash)) + { + ret = dev::sha3(rec); + memset(ret.data(), 0, 12); + ret.ref().copyTo(_out); + } + } + catch (...) {} + } + } +} + +ETH_REGISTER_PRECOMPILED(sha256)(bytesConstRef _in, bytesRef _out) +{ + dev::sha256(_in).ref().copyTo(_out); +} + +ETH_REGISTER_PRECOMPILED(ripemd160)(bytesConstRef _in, bytesRef _out) +{ + h256(dev::ripemd160(_in), h256::AlignRight).ref().copyTo(_out); +} +*/ + +impl Builtin { + /// Create a new object from components. + pub fn new(cost: Box U256>, execute: Box) -> Builtin { + Builtin {cost: cost, execute: execute} + } + + /// Create a new object from a builtin-function name with a linear cost associated with input size. + pub fn from_named_linear(name: &str, base_cost: usize, word_cost: usize) -> Option { + new_builtin_exec(name).map(|b| { + let cost = Box::new(move|s: usize| -> U256 {U256::from(base_cost) + U256::from(word_cost) * U256::from((s + 31) / 32)}); + Self::new(cost, b) + }) + } + + /// Create a builtin from JSON. + /// + /// JSON must be of the form `{ "name": "identity", "linear": {"base": 10, "word": 20} }`. + pub fn from_json(json: &Json) -> Option { + // NICE: figure out a more convenient means of handing errors here. + if let Json::String(ref name) = json["name"] { + if let Json::Object(ref o) = json["linear"] { + if let Json::U64(ref word) = o["word"] { + if let Json::U64(ref base) = o["base"] { + return Self::from_named_linear(&name[..], *base as usize, *word as usize); + } + } + } + } + None + } +} + +// TODO: turn in to a factory with dynamic registration. +pub fn new_builtin_exec(name: &str) -> Option> { + match name { + "identity" => Some(Box::new(move|input: &[u8], output: &mut[u8]| { + for i in 0..min(input.len(), output.len()) { + output[i] = input[i]; + } + })), + "ecrecover" => Some(Box::new(move|_input: &[u8], _output: &mut[u8]| { + unimplemented!(); + })), + "sha256" => Some(Box::new(move|_input: &[u8], _output: &mut[u8]| { + unimplemented!(); + })), + "ripemd160" => Some(Box::new(move|_input: &[u8], _output: &mut[u8]| { + unimplemented!(); + })), + _ => None + } +} + +#[test] +fn identity() { + let f = new_builtin_exec("identity").unwrap(); + let i = [0u8, 1, 2, 3]; + for osize in [2, 4, 8].iter() { + let mut o = vec![255u8; *osize]; + f(&i[..], &mut o[..]); + assert_eq!(i[..], o[..]); + } +} \ No newline at end of file diff --git a/src/spec.rs b/src/spec.rs index b79624787..8fd42faec 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -20,6 +20,8 @@ use null_engine::NullEngine; use denominations::*; use header::*; +// TODO: need a JSON->cost closure converter. + /// Converts file from base64 gzipped bytes to json pub fn base_to_json(source: &[u8]) -> Json { // there is probably no need to store genesis in based64 gzip, @@ -139,26 +141,29 @@ impl Spec { } } - impl Spec { /// Loads a chain-specification from a json data structure pub fn from_json(json: Json) -> Spec { // once we commit ourselves to some json parsing library (serde?) // move it to proper data structure let mut state = HashMap::new(); + let mut builtins = HashMap::new(); + let accounts = json["alloc"].as_object().expect("Missing genesis state"); for (address, acc) in accounts.iter() { let addr = Address::from_str(address).unwrap(); let o = acc.as_object().unwrap(); - let balance = U256::from_dec_str(o["balance"].as_string().unwrap()).unwrap(); - state.insert(addr, Account::new_basic(balance, U256::from(0))); + if let Json::Object(_) = o["precompiled"] { + if let Some(b) = Builtin::from_json(&o["precompiled"]) { + builtins.insert(addr.clone(), b); + } + } + let balance = U256::from_dec_str(o["balance"].as_string().unwrap_or("0")).unwrap(); + let nonce = U256::from_dec_str(o["nonce"].as_string().unwrap_or("0")).unwrap(); + // TODO: handle code & data is they exist. + state.insert(addr, Account::new_basic(balance, nonce)); } - let builtins = { - // TODO: populate from json. - HashMap::new() - }; - let (seal_fields, seal_rlp) = { if json.find("mixhash").is_some() && json.find("nonce").is_some() { let mut s = RlpStream::new(); @@ -189,6 +194,15 @@ impl Spec { } } + fn standard_builtins() -> HashMap { + let mut ret = HashMap::new(); + ret.insert(Address::from_str("0000000000000000000000000000000000000001").unwrap(), Builtin::from_named_linear("ecrecover", 3000, 0).unwrap()); + ret.insert(Address::from_str("0000000000000000000000000000000000000002").unwrap(), Builtin::from_named_linear("sha256", 60, 12).unwrap()); + ret.insert(Address::from_str("0000000000000000000000000000000000000003").unwrap(), Builtin::from_named_linear("ripemd160", 600, 120).unwrap()); + ret.insert(Address::from_str("0000000000000000000000000000000000000004").unwrap(), Builtin::from_named_linear("identity", 15, 3).unwrap()); + ret + } + /// Creates the Olympic network chain spec. pub fn olympic() -> Spec { Spec { @@ -207,7 +221,7 @@ impl Spec { acc.insert(vec.0.to_string(), vec.1); acc }), - builtins: HashMap::new(), // TODO: make correct + builtins: Self::standard_builtins(), parent_hash: H256::new(), author: Address::new(), difficulty: U256::from(131_072u64), @@ -245,7 +259,7 @@ impl Spec { acc.insert(vec.0.to_string(), vec.1); acc }), - builtins: HashMap::new(), // TODO: make correct + builtins: Self::standard_builtins(), parent_hash: H256::new(), author: Address::new(), difficulty: U256::from(131_072u64), @@ -283,7 +297,7 @@ impl Spec { acc.insert(vec.0.to_string(), vec.1); acc }), - builtins: HashMap::new(), // TODO: make correct + builtins: Self::standard_builtins(), parent_hash: H256::new(), author: Address::new(), difficulty: U256::from(131_072u64),