From 5e34235a3645e7090c20e6804f5348098ba07074 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 22 Mar 2017 04:01:46 +0300 Subject: [PATCH] initial, fallable built-ins --- Cargo.lock | 18 ++++++ ethcore/Cargo.toml | 1 + ethcore/res/ethereum/foundation.json | 2 + ethcore/src/builtin.rs | 77 +++++++++++++++++++++++--- ethcore/src/evm/evm.rs | 9 +++ ethcore/src/executive.rs | 3 +- ethcore/src/lib.rs | 1 + ethcore/src/types/trace_types/error.rs | 7 ++- 8 files changed, 107 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e61658e16..42d6054e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,6 +175,21 @@ name = "bloomchain" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bn" +version = "0.4.2" +source = "git+https://github.com/paritytech/bn#78cf02fd7b35e4a2398fedd96f68c92953badea7" +dependencies = [ + "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.0.0" @@ -382,6 +397,7 @@ version = "1.7.0" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bn 0.4.2 (git+https://github.com/paritytech/bn)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2617,6 +2633,8 @@ dependencies = [ "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2" "checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d" +"checksum bn 0.4.2 (git+https://github.com/paritytech/bn)" = "" +"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27" "checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index d1c9d624b..c44e98763 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -45,6 +45,7 @@ ethcore-bloom-journal = { path = "../util/bloom" } hardware-wallet = { path = "../hw" } stats = { path = "../util/stats" } num = "0.1" +bn = { git = "https://github.com/paritytech/bn" } [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 77b0d5533..1debff1d6 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -190,6 +190,8 @@ "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x7fffffffffffff", "pricing": { "linear": { "base": 999999, "word": 0 } } } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index 9927435dd..7de9409a0 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -27,10 +27,19 @@ use util::{U256, H256, Uint, Hashable, BytesRef}; use ethkey::{Signature, recover as ec_recover}; use ethjson; +#[derive(Debug)] +pub struct Error(pub String); + +impl From<&'static str> for Error { + fn from(val: &'static str) -> Self { + Error(val.to_owned()) + } +} + /// Native implementation of a built-in contract. pub trait Impl: Send + Sync { /// execute this built-in on the given input, writing to the given output. - fn execute(&self, input: &[u8], output: &mut BytesRef); + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error>; } /// A gas pricing scheme for built-in contracts. @@ -102,7 +111,10 @@ impl Builtin { pub fn cost(&self, input: &[u8]) -> U256 { self.pricer.cost(input) } /// Simple forwarder for execute. - pub fn execute(&self, input: &[u8], output: &mut BytesRef) { self.native.execute(input, output) } + pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + self.native.execute(input, output); + Ok(()) + } /// Whether the builtin is activated at the given block number. pub fn is_active(&self, at: u64) -> bool { at >= self.activate_at } @@ -172,14 +184,21 @@ struct Ripemd160; #[derive(Debug)] struct ModexpImpl; +#[derive(Debug)] +struct Bn128AddImpl; + +#[derive(Debug)] +struct Bn128MulImpl; + impl Impl for Identity { - fn execute(&self, input: &[u8], output: &mut BytesRef) { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { output.write(0, input); + Ok(()) } } impl Impl for EcRecover { - fn execute(&self, i: &[u8], output: &mut BytesRef) { + fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), Error> { let len = min(i.len(), 128); let mut input = [0; 128]; @@ -192,7 +211,7 @@ impl Impl for EcRecover { let bit = match v[31] { 27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27, - _ => return, + _ => { return Ok(()); }, }; let s = Signature::from_rsv(&r, &s, bit); @@ -203,11 +222,13 @@ impl Impl for EcRecover { output.write(12, &r[12..r.len()]); } } + + Ok(()) } } impl Impl for Sha256 { - fn execute(&self, input: &[u8], output: &mut BytesRef) { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { let mut sha = Sha256Digest::new(); sha.input(input); @@ -215,11 +236,13 @@ impl Impl for Sha256 { sha.result(&mut out); output.write(0, &out); + + Ok(()) } } impl Impl for Ripemd160 { - fn execute(&self, input: &[u8], output: &mut BytesRef) { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { let mut sha = Ripemd160Digest::new(); sha.input(input); @@ -227,11 +250,13 @@ impl Impl for Ripemd160 { sha.result(&mut out[12..32]); output.write(0, &out); + + Ok(()) } } impl Impl for ModexpImpl { - fn execute(&self, input: &[u8], output: &mut BytesRef) { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { let mut reader = input.chain(io::repeat(0)); let mut buf = [0; 32]; @@ -294,9 +319,43 @@ impl Impl for ModexpImpl { let res_start = mod_len - bytes.len(); output.write(res_start, &bytes); } + + Ok(()) } } +impl Impl for Bn128AddImpl { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + use bn::{Fq, AffineG1, G1}; + + let mut buf = [0u8; 32]; + let mut next_coord = |reader: &mut io::Chain<&[u8], io::Repeat>| { + reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); + Fq::from_slice(&input[0..32]) + }; + + let mut padded_input = input.chain(io::repeat(0)); + + let p1x = next_coord(&mut padded_input).map_err(|_| Error::from("Invalid p1 x coordinate"))?; + let p1y = next_coord(&mut padded_input).map_err(|_| Error::from("Invalid p1 y coordinate"))?; + let p1: G1 = AffineG1::new(p1x, p1y).into(); + + let p2x = next_coord(&mut padded_input).map_err(|_| Error::from("Invalid p2 x coordinate"))?; + let p2y = next_coord(&mut padded_input).map_err(|_| Error::from("Invalid p2 y coordinate"))?; + let p2: G1 = AffineG1::new(p2x, p2y).into(); + + let sum = AffineG1::from_jacobian(p1 + p2).expect("Sum of two valid points is a valid point also; qed"); + + Ok(()) + } +} + +impl Impl for Bn128MulImpl { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{Builtin, Linear, ethereum_builtin, Pricer, Modexp}; @@ -574,4 +633,4 @@ mod tests { b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])); assert_eq!(i, o); } -} +} \ No newline at end of file diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 09a93f087..b561c271f 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -20,6 +20,7 @@ use std::{ops, cmp, fmt}; use util::{U128, U256, U512, Uint, trie}; use action_params::ActionParams; use evm::Ext; +use builtin; /// Evm errors. #[derive(Debug, Clone, PartialEq)] @@ -59,6 +60,7 @@ pub enum Error { /// What was the stack limit limit: usize }, + BuiltIn(String), /// Returned on evm internal error. Should never be ignored during development. /// Likely to cause consensus issues. Internal(String), @@ -70,6 +72,12 @@ impl From> for Error { } } +impl From for Error { + fn from(err: builtin::Error) -> Self { + Error::BuiltIn(err.0) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; @@ -79,6 +87,7 @@ impl fmt::Display for Error { BadInstruction { .. } => "Bad instruction", StackUnderflow { .. } => "Stack underflow", OutOfStack { .. } => "Out of stack", + BuiltIn { .. } => "Built-in failed", Internal(ref msg) => msg, }; message.fmt(f) diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index cdae8a85f..4fdd9cbbd 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -276,7 +276,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let cost = builtin.cost(data); if cost <= params.gas { - builtin.execute(data, &mut output); + builtin.execute(data, &mut output)?; self.state.discard_checkpoint(); // trace only top level calls to builtins to avoid DDoS attacks @@ -497,6 +497,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { | Err(evm::Error::BadJumpDestination {..}) | Err(evm::Error::BadInstruction {.. }) | Err(evm::Error::StackUnderflow {..}) + | Err(evm::Error::BuiltIn {..}) | Err(evm::Error::OutOfStack {..}) => { self.state.revert_to_checkpoint(); }, diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 75c8a80e1..641b841f3 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -107,6 +107,7 @@ extern crate ethabi; extern crate hardware_wallet; extern crate stats; extern crate num; +extern crate bn; #[macro_use] extern crate log; diff --git a/ethcore/src/types/trace_types/error.rs b/ethcore/src/types/trace_types/error.rs index 33ccf2bb7..c36d86728 100644 --- a/ethcore/src/types/trace_types/error.rs +++ b/ethcore/src/types/trace_types/error.rs @@ -34,7 +34,9 @@ pub enum Error { /// `StackUnderflow` when there is not enough stack elements to execute instruction StackUnderflow, /// When execution would exceed defined Stack Limit - OutOfStack, + OutOfStack, + /// When builtin contract failed on input data + BuiltIn, /// Returned on evm internal error. Should never be ignored during development. /// Likely to cause consensus issues. Internal, @@ -48,6 +50,7 @@ impl<'a> From<&'a EvmError> for Error { EvmError::BadInstruction { .. } => Error::BadInstruction, EvmError::StackUnderflow { .. } => Error::StackUnderflow, EvmError::OutOfStack { .. } => Error::OutOfStack, + EvmError::BuiltIn { .. } => Error::OutOfStack, EvmError::Internal(_) => Error::Internal, } } @@ -68,6 +71,7 @@ impl fmt::Display for Error { BadInstruction => "Bad instruction", StackUnderflow => "Stack underflow", OutOfStack => "Out of stack", + BuiltIn => "Built-in failed", Internal => "Internal error", }; message.fmt(f) @@ -84,6 +88,7 @@ impl Encodable for Error { StackUnderflow => 3, OutOfStack => 4, Internal => 5, + BuiltIn => 6, }; s.append_internal(&value);