Browse Source

staticcall and delegatecall cannot have value specified

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 years ago
parent
commit
cf844ebe09
6 changed files with 165 additions and 47 deletions
  1. 5 0
      CHANGELOG.md
  2. 74 46
      src/emit/ewasm.rs
  3. 9 0
      src/sema/expression.rs
  4. 26 1
      tests/ewasm.rs
  5. 50 0
      tests/ewasm_tests/call.rs
  6. 1 0
      tests/ewasm_tests/mod.rs

+ 5 - 0
CHANGELOG.md

@@ -2,6 +2,11 @@
 All notable changes to [Solang](https://github.com/hyperledger-labs/solang/)
 will be documented here.
 
+## [Unreleased]
+
+### Fixed
+- ewasm: staticcall() and delegatecall() cannot take value argument
+
 ## [0.1.7]
 
 ### Added

+ 74 - 46
src/emit/ewasm.rs

@@ -387,7 +387,6 @@ impl EwasmTarget {
                 &[
                     u64_ty.into(),    // gas
                     u8_ptr_ty.into(), // address
-                    u8_ptr_ty.into(), // valueOffset
                     u8_ptr_ty.into(), // input offset
                     u32_ty.into(),    // input length
                 ],
@@ -401,7 +400,6 @@ impl EwasmTarget {
                 &[
                     u64_ty.into(),    // gas
                     u8_ptr_ty.into(), // address
-                    u8_ptr_ty.into(), // valueOffset
                     u8_ptr_ty.into(), // input offset
                     u32_ty.into(),    // input length
                 ],
@@ -1414,12 +1412,6 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         value: IntValue<'b>,
         callty: ast::CallTy,
     ) {
-        // value is a u128
-        let value_ptr = contract
-            .builder
-            .build_alloca(contract.value_type(), "balance");
-        contract.builder.build_store(value_ptr, value);
-
         // address needs its bytes reordered
         let be_address = contract
             .builder
@@ -1449,45 +1441,81 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
             "",
         );
 
-        // call create
-        let ret = contract
-            .builder
-            .build_call(
-                contract
-                    .module
-                    .get_function(match callty {
-                        ast::CallTy::Regular => "call",
-                        ast::CallTy::Static => "callStatic",
-                        ast::CallTy::Delegate => "callDelegate",
-                    })
-                    .unwrap(),
-                &[
-                    gas.into(),
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            be_address,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "address",
-                        )
-                        .into(),
+        let ret;
+
+        if callty == ast::CallTy::Regular {
+            // needs value
+
+            // value is a u128
+            let value_ptr = contract
+                .builder
+                .build_alloca(contract.value_type(), "balance");
+            contract.builder.build_store(value_ptr, value);
+
+            // call create
+            ret = contract
+                .builder
+                .build_call(
+                    contract.module.get_function("call").unwrap(),
+                    &[
+                        gas.into(),
+                        contract
+                            .builder
+                            .build_pointer_cast(
+                                be_address,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                                "address",
+                            )
+                            .into(),
+                        contract
+                            .builder
+                            .build_pointer_cast(
+                                value_ptr,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                                "value_transfer",
+                            )
+                            .into(),
+                        payload.into(),
+                        payload_len.into(),
+                    ],
+                    "",
+                )
+                .try_as_basic_value()
+                .left()
+                .unwrap()
+                .into_int_value();
+        } else {
+            ret = contract
+                .builder
+                .build_call(
                     contract
-                        .builder
-                        .build_pointer_cast(
-                            value_ptr,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "value_transfer",
-                        )
-                        .into(),
-                    payload.into(),
-                    payload_len.into(),
-                ],
-                "",
-            )
-            .try_as_basic_value()
-            .left()
-            .unwrap()
-            .into_int_value();
+                        .module
+                        .get_function(match callty {
+                            ast::CallTy::Regular => "call",
+                            ast::CallTy::Static => "callStatic",
+                            ast::CallTy::Delegate => "callDelegate",
+                        })
+                        .unwrap(),
+                    &[
+                        gas.into(),
+                        contract
+                            .builder
+                            .build_pointer_cast(
+                                be_address,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                                "address",
+                            )
+                            .into(),
+                        payload.into(),
+                        payload_len.into(),
+                    ],
+                    "",
+                )
+                .try_as_basic_value()
+                .left()
+                .unwrap()
+                .into_int_value();
+        }
 
         let is_success = contract.builder.build_int_compare(
             IntPredicate::EQ,

+ 9 - 0
src/sema/expression.rs

@@ -5855,6 +5855,15 @@ fn method_call_pos_args(
                 diagnostics,
             )?;
 
+            if ty != CallTy::Regular && call_args.value.is_some() {
+                diagnostics.push(Diagnostic::error(
+                    *loc,
+                    format!("‘{}’ cannot have value specifed", func.name,),
+                ));
+
+                return Err(());
+            }
+
             if args.len() != 1 {
                 diagnostics.push(Diagnostic::error(
                     *loc,

+ 26 - 1
tests/ewasm.rs

@@ -11,7 +11,7 @@ use wasmi::memory_units::Pages;
 use wasmi::*;
 
 use solang::file_cache::FileCache;
-use solang::sema::diagnostics;
+use solang::sema::{ast, diagnostics};
 use solang::{compile, Target};
 
 mod ewasm_tests;
@@ -843,6 +843,31 @@ fn simple_solidiy_compile_and_run() {
     );
 }
 
+pub fn parse_and_resolve(src: &'static str, target: Target) -> ast::Namespace {
+    let mut cache = FileCache::new();
+
+    cache.set_file_contents("test.sol", src.to_string());
+
+    solang::parse_and_resolve("test.sol", &mut cache, target)
+}
+
+pub fn first_error(errors: Vec<ast::Diagnostic>) -> String {
+    match errors.iter().find(|m| m.level == ast::Level::Error) {
+        Some(m) => m.message.to_owned(),
+        None => panic!("no errors found"),
+    }
+}
+
+pub fn no_errors(errors: Vec<ast::Diagnostic>) {
+    assert!(
+        errors
+            .iter()
+            .filter(|m| m.level == ast::Level::Error)
+            .count()
+            == 0
+    );
+}
+
 #[test]
 fn simple_loops() {
     let mut runtime = build_solidity(

+ 50 - 0
tests/ewasm_tests/call.rs

@@ -0,0 +1,50 @@
+use crate::{first_error, no_errors, parse_and_resolve};
+use solang::Target;
+
+#[test]
+fn call() {
+    let ns = parse_and_resolve(
+        r#"
+        contract x {
+            function f(address payable a) public {
+                (bool s, bytes memory bs) = a.delegatecall{value: 2}("");
+            }
+        }
+        "#,
+        Target::Ewasm,
+    );
+
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘delegatecall’ cannot have value specifed"
+    );
+
+    let ns = parse_and_resolve(
+        r#"
+        contract x {
+            function f(address payable a) public {
+                (bool s, bytes memory bs) = a.staticcall{value: 2}("");
+            }
+        }
+        "#,
+        Target::Ewasm,
+    );
+
+    assert_eq!(
+        first_error(ns.diagnostics),
+        "‘staticcall’ cannot have value specifed"
+    );
+
+    let ns = parse_and_resolve(
+        r#"
+        contract x {
+            function f(address payable a) public {
+                (bool s, bytes memory bs) = a.call{value: 2}("");
+            }
+        }
+        "#,
+        Target::Ewasm,
+    );
+
+    no_errors(ns.diagnostics);
+}

+ 1 - 0
tests/ewasm_tests/mod.rs

@@ -1,3 +1,4 @@
 mod abi;
+mod call;
 mod crypto;
 mod primitives;