Explorar o código

Use precompiles for hashing functions

Signed-off-by: Sean Young <sean@mess.org>
Sean Young %!s(int64=4) %!d(string=hai) anos
pai
achega
9c2abc2b2a
Modificáronse 6 ficheiros con 209 adicións e 65 borrados
  1. 2 0
      CHANGELOG.md
  2. 111 63
      src/emit/ewasm.rs
  3. 2 2
      src/emit/mod.rs
  4. 32 0
      tests/ewasm.rs
  5. 61 0
      tests/ewasm_tests/crypto.rs
  6. 1 0
      tests/ewasm_tests/mod.rs

+ 2 - 0
CHANGELOG.md

@@ -21,6 +21,8 @@ will be documented here.
 - Fixed ethereum abi encoding/decoding of structs and enums
 - Solana now returns an error if account data is not large enough
 - Fixed storage bytes push() and pop()
+- Ewasm uses precompiles for keccak hashing
+- Various ewasm fixes for Hyperledger Burrow
 
 ## [0.1.6]
 

+ 111 - 63
src/emit/ewasm.rs

@@ -1065,18 +1065,65 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         length: IntValue,
         dest: PointerValue,
     ) {
+        let balance = contract
+            .builder
+            .build_alloca(contract.value_type(), "balance");
+
+        contract
+            .builder
+            .build_store(balance, contract.value_type().const_zero());
+
+        let keccak256_pre_compile_address: [u8; 20] =
+            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20];
+
+        let address = contract.emit_global_string(
+            "keccak256_precompile",
+            &keccak256_pre_compile_address,
+            true,
+        );
+
         contract.builder.build_call(
-            contract.module.get_function("keccak256").unwrap(),
+            contract.module.get_function("call").unwrap(),
             &[
+                contract
+                    .context
+                    .i64_type()
+                    .const_int(i64::MAX as u64, false)
+                    .into(),
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        address,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "address",
+                    )
+                    .into(),
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        balance,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "balance",
+                    )
+                    .into(),
                 contract
                     .builder
                     .build_pointer_cast(
                         src,
                         contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                        "src",
+                        "source",
                     )
                     .into(),
                 length.into(),
+            ],
+            "",
+        );
+
+        // We're not checking return value or returnDataSize;
+        // assuming precompiles always succeed
+        contract.builder.build_call(
+            contract.module.get_function("returnDataCopy").unwrap(),
+            &[
                 contract
                     .builder
                     .build_pointer_cast(
@@ -1085,6 +1132,8 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
                         "dest",
                     )
                     .into(),
+                contract.context.i32_type().const_zero().into(),
+                contract.context.i32_type().const_int(32, false).into(),
             ],
             "",
         );
@@ -1632,9 +1681,18 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         input_len: IntValue<'b>,
     ) -> IntValue<'b> {
         let (precompile, hashlen) = match hash {
-            HashTy::Keccak256 => (0, 32),
-            HashTy::Ripemd160 => (3, 20),
-            HashTy::Sha256 => (2, 32),
+            HashTy::Keccak256 => (
+                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20],
+                32,
+            ),
+            HashTy::Ripemd160 => (
+                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
+                20,
+            ),
+            HashTy::Sha256 => (
+                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
+                32,
+            ),
             _ => unreachable!(),
         };
 
@@ -1644,69 +1702,59 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
             "res",
         );
 
-        if hash == HashTy::Keccak256 {
-            contract.builder.build_call(
-                contract.module.get_function("keccak256").unwrap(),
-                &[input.into(), input_len.into(), res.into()],
-                "",
-            );
-        } else {
-            let balance = contract
-                .builder
-                .build_alloca(contract.value_type(), "balance");
-
-            contract
-                .builder
-                .build_store(balance, contract.value_type().const_zero());
+        let balance = contract
+            .builder
+            .build_alloca(contract.value_type(), "balance");
 
-            let address = contract
-                .builder
-                .build_alloca(contract.address_type(), "address");
+        contract
+            .builder
+            .build_store(balance, contract.value_type().const_zero());
 
-            contract.builder.build_store(
-                address,
-                contract.address_type().const_int(precompile, false),
-            );
+        let address =
+            contract.emit_global_string(&format!("precompile_{}", hash), &precompile, true);
 
-            contract.builder.build_call(
-                contract.module.get_function("call").unwrap(),
-                &[
-                    contract.context.i64_type().const_zero().into(),
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            address,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "address",
-                        )
-                        .into(),
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            balance,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "balance",
-                        )
-                        .into(),
-                    input.into(),
-                    input_len.into(),
-                ],
-                "",
-            );
+        contract.builder.build_call(
+            contract.module.get_function("call").unwrap(),
+            &[
+                contract
+                    .context
+                    .i64_type()
+                    .const_int(i64::MAX as u64, false)
+                    .into(),
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        address,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "address",
+                    )
+                    .into(),
+                contract
+                    .builder
+                    .build_pointer_cast(
+                        balance,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "balance",
+                    )
+                    .into(),
+                input.into(),
+                input_len.into(),
+            ],
+            "",
+        );
 
-            // We're not checking return value or returnDataSize;
-            // assuming precompiles always succeed
+        // We're not checking return value or returnDataSize;
+        // assuming precompiles always succeed
 
-            contract.builder.build_call(
-                contract.module.get_function("returnDataCopy").unwrap(),
-                &[
-                    res.into(),
-                    contract.context.i32_type().const_zero().into(),
-                    contract.context.i32_type().const_int(hashlen, false).into(),
-                ],
-                "",
-            );
-        }
+        contract.builder.build_call(
+            contract.module.get_function("returnDataCopy").unwrap(),
+            &[
+                res.into(),
+                contract.context.i32_type().const_zero().into(),
+                contract.context.i32_type().const_int(hashlen, false).into(),
+            ],
+            "",
+        );
 
         // bytes32 needs to reverse bytes
         let temp = contract

+ 2 - 2
src/emit/mod.rs

@@ -6311,8 +6311,8 @@ fn load_stdlib<'a>(context: &'a Context, target: &Target) -> Module<'a> {
         module
             .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
             .unwrap();
-    } else {
-        // Substrate provides a keccak256 (sha3) host function, others do not
+    } else if Target::Ewasm != *target {
+        // Substrate and Ewasm provides a keccak256 (sha3) host function, others do not
         let memory = MemoryBuffer::create_from_memory_range(KECCAK256_IR, "sha3");
 
         module

+ 32 - 0
tests/ewasm.rs

@@ -2,8 +2,11 @@ use ethabi::{decode, RawLog, Token};
 use num_derive::FromPrimitive;
 use num_traits::FromPrimitive;
 use rand::Rng;
+use ripemd160::Ripemd160;
+use sha2::{Digest, Sha256};
 use std::collections::HashMap;
 use std::fmt;
+use tiny_keccak::{Hasher, Keccak};
 use wasmi::memory_units::Pages;
 use wasmi::*;
 
@@ -364,6 +367,35 @@ impl Externals for TestRuntime {
                     hex::encode(&buf)
                 );
 
+                // if the first 19 bytes are 0, it's a precompile
+                if addr[0..19].iter().all(|v| *v == 0) {
+                    match addr[19] {
+                        20 => {
+                            let mut hasher = Keccak::v256();
+                            let mut hash = [0u8; 32];
+                            hasher.update(&buf);
+                            hasher.finalize(&mut hash);
+                            self.vm.returndata = hash.to_vec();
+                            return Ok(Some(RuntimeValue::I32(0)));
+                        }
+                        2 => {
+                            let mut hasher = Sha256::new();
+                            hasher.update(&buf);
+                            self.vm.returndata = hasher.finalize().to_vec();
+                            return Ok(Some(RuntimeValue::I32(0)));
+                        }
+                        3 => {
+                            let mut hasher = Ripemd160::new();
+                            hasher.update(&buf);
+                            self.vm.returndata = hasher.finalize().to_vec();
+                            return Ok(Some(RuntimeValue::I32(0)));
+                        }
+                        n => {
+                            panic!("unknown precompile {}", n);
+                        }
+                    }
+                }
+
                 // when ewasm creates a contract, the abi encoded args are concatenated to the
                 // code. So, find which code is was and use that instead. Otherwise, the
                 // wasm validator will trip

+ 61 - 0
tests/ewasm_tests/crypto.rs

@@ -0,0 +1,61 @@
+use crate::build_solidity;
+
+#[test]
+fn hash_tests() {
+    let mut runtime = build_solidity(
+        r##"
+        contract tester {
+            function test() public {
+                bytes32 hash = keccak256("Hello, World!");
+
+                assert(hash == hex"acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f");
+            }
+        }"##,
+    );
+
+    runtime.constructor(&[]);
+    runtime.function("test", &[]);
+
+    let mut runtime = build_solidity(
+        r##"
+        contract tester {
+            function test() public {
+                bytes memory s = "Hello, World!";
+                bytes32 hash = keccak256(s);
+
+                assert(hash == hex"acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f");
+            }
+        }"##,
+    );
+
+    runtime.constructor(&[]);
+    runtime.function("test", &[]);
+
+    let mut runtime = build_solidity(
+        r##"
+        contract tester {
+            function test() public {
+                bytes32 hash = sha256("Hello, World!");
+
+                assert(hash == hex"dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f");
+            }
+        }"##,
+    );
+
+    runtime.constructor(&[]);
+    runtime.function("test", &[]);
+
+    let mut runtime = build_solidity(
+        r##"
+        contract tester {
+            function test() public {
+                bytes20 hash = ripemd160("Hello, World!");
+
+                assert(hash == hex"527a6a4b9a6da75607546842e0e00105350b1aaf");
+            }
+        }"##,
+    );
+
+    runtime.constructor(&[]);
+    runtime.function("test", &[]);
+}

+ 1 - 0
tests/ewasm_tests/mod.rs

@@ -1,2 +1,3 @@
 mod abi;
+mod crypto;
 mod primitives;