Explorar el Código

Implement packed abi eth encoding

This is used for the builtin `bytes foo = abi.encodePacked("xyz", true);`,
and we want to re-use it to pack the instruction for Solana cross
program calls.

Note this fixes various endian problem with abi encoding, scale codec
or eth abi. The function selector should now have the correct type
`bytes4` rather than `uint32`.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young hace 4 años
padre
commit
d08d3bc5bf

+ 0 - 1
docs/targets.rst

@@ -90,7 +90,6 @@ Please use the latest master version of burrow, as ewasm support is still maturi
 
 Some language features have not been fully implemented yet on ewasm:
 
-- The built in function ``abi.encodePacked()``
 - Contract storage variables types ``string``, ``bytes`` and function types are not implemented
 
 Hyperledger Sawtooth

+ 1 - 1
src/abi/substrate.rs

@@ -642,7 +642,7 @@ fn parameter_to_abi(param: &ast::Parameter, ns: &ast::Namespace, registry: &mut
 
 /// Given an u32 selector, generate a byte string like: 0xF81E7E1A
 fn render_selector(f: &ast::Function) -> String {
-    format!("0x{}", hex::encode(f.selector().to_le_bytes()))
+    format!("0x{}", hex::encode(f.selector().to_be_bytes()))
 }
 
 /// Given a selector like "0xF81E7E1A", parse the bytes. This function

+ 7 - 12
src/codegen/cfg.rs

@@ -633,18 +633,13 @@ impl ControlFlowGraph {
                     .collect::<Vec<String>>()
                     .join(", ")
             ),
-            Expression::AbiEncode {
-                selector,
-                packed,
-                args,
-                ..
-            } => format!(
-                "(abiencode{}:(%{} {}))",
-                if *packed { "packed" } else { "" },
-                match selector {
-                    None => "".to_string(),
-                    Some(expr) => self.expr_to_string(contract, ns, expr),
-                },
+            Expression::AbiEncode { packed, args, .. } => format!(
+                "(abiencode packed:{} non-packed:{})",
+                packed
+                    .iter()
+                    .map(|expr| self.expr_to_string(contract, ns, expr))
+                    .collect::<Vec<String>>()
+                    .join(", "),
                 args.iter()
                     .map(|expr| self.expr_to_string(contract, ns, expr))
                     .collect::<Vec<String>>()

+ 5 - 7
src/codegen/constant_folding.rs

@@ -1026,24 +1026,22 @@ fn expression(
             loc,
             tys,
             packed,
-            selector,
             args,
         } => {
+            let packed = packed
+                .iter()
+                .map(|expr| expression(expr, vars, pos, cfg, ns).0)
+                .collect();
             let args = args
                 .iter()
                 .map(|expr| expression(expr, vars, pos, cfg, ns).0)
                 .collect();
 
-            let selector = selector
-                .as_ref()
-                .map(|expr| Box::new(expression(&expr, vars, pos, cfg, ns).0));
-
             (
                 Expression::AbiEncode {
                     loc: *loc,
                     tys: tys.clone(),
-                    packed: *packed,
-                    selector,
+                    packed,
                     args,
                 },
                 false,

+ 24 - 28
src/codegen/expression.rs

@@ -431,7 +431,7 @@ pub fn expression(
             if let Expression::ExternalFunction { function_no, .. } = &func[0] {
                 let selector = ns.functions[*function_no].selector();
 
-                Expression::NumberLiteral(*loc, Type::Uint(32), BigInt::from(selector))
+                Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::from(selector))
             } else {
                 let func = expression(&func[0], cfg, contract_no, ns, vartab);
 
@@ -907,8 +907,7 @@ pub fn expression(
                     expr: Expression::AbiEncode {
                         loc: *loc,
                         tys,
-                        selector: None,
-                        packed: false,
+                        packed: vec![],
                         args,
                     },
                 },
@@ -918,7 +917,7 @@ pub fn expression(
         }
         Expression::Builtin(loc, _, Builtin::AbiEncodePacked, args) => {
             let tys = args.iter().map(|a| a.ty()).collect();
-            let args = args
+            let packed = args
                 .iter()
                 .map(|v| expression(&v, cfg, contract_no, ns, vartab))
                 .collect();
@@ -939,9 +938,8 @@ pub fn expression(
                     expr: Expression::AbiEncode {
                         loc: *loc,
                         tys,
-                        selector: None,
-                        packed: true,
-                        args,
+                        packed,
+                        args: vec![],
                     },
                 },
             );
@@ -949,7 +947,7 @@ pub fn expression(
             Expression::Variable(*loc, Type::DynamicBytes, res)
         }
         Expression::Builtin(loc, _, Builtin::AbiEncodeWithSelector, args) => {
-            let tys = args.iter().skip(1).map(|a| a.ty()).collect();
+            let mut tys: Vec<Type> = args.iter().skip(1).map(|a| a.ty()).collect();
             // first argument is selector
             let mut args_iter = args.iter();
             let selector = expression(&args_iter.next().unwrap(), cfg, contract_no, ns, vartab);
@@ -965,6 +963,8 @@ pub fn expression(
                 &Type::DynamicBytes,
             );
 
+            tys.insert(0, Type::Bytes(4));
+
             cfg.add(
                 vartab,
                 Instr::Set {
@@ -973,8 +973,7 @@ pub fn expression(
                     expr: Expression::AbiEncode {
                         loc: *loc,
                         tys,
-                        selector: Some(Box::new(selector)),
-                        packed: false,
+                        packed: vec![selector],
                         args,
                     },
                 },
@@ -983,7 +982,7 @@ pub fn expression(
             Expression::Variable(*loc, Type::DynamicBytes, res)
         }
         Expression::Builtin(loc, _, Builtin::AbiEncodeWithSignature, args) => {
-            let tys = args.iter().skip(1).map(|a| a.ty()).collect();
+            let mut tys: Vec<Type> = args.iter().skip(1).map(|a| a.ty()).collect();
             // first argument is signature which needs hashing and shifting
             let mut args_iter = args.iter();
             let hash = Expression::Builtin(
@@ -1006,6 +1005,8 @@ pub fn expression(
                 &Type::DynamicBytes,
             );
 
+            tys.insert(0, Type::Bytes(4));
+
             cfg.add(
                 vartab,
                 Instr::Set {
@@ -1014,8 +1015,7 @@ pub fn expression(
                     expr: Expression::AbiEncode {
                         loc: *loc,
                         tys,
-                        selector: Some(Box::new(selector)),
-                        packed: false,
+                        packed: vec![selector],
                         args,
                     },
                 },
@@ -1299,7 +1299,7 @@ pub fn emit_function_call(
             } = function.as_ref()
             {
                 let ftype = &ns.functions[*function_no];
-                let tys = args.iter().map(|a| a.ty()).collect();
+                let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
                 let args = args
                     .iter()
                     .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
@@ -1310,21 +1310,16 @@ pub fn emit_function_call(
 
                 let dest_func = &ns.functions[*function_no];
 
-                let selector = if ns.target == Target::Substrate {
-                    dest_func.selector().to_be()
-                } else {
-                    dest_func.selector()
-                };
+                tys.insert(0, Type::Bytes(4));
 
                 let payload = Expression::AbiEncode {
                     loc: *loc,
-                    packed: false,
                     tys,
-                    selector: Some(Box::new(Expression::NumberLiteral(
+                    packed: vec![Expression::NumberLiteral(
                         *loc,
-                        Type::Uint(32),
-                        BigInt::from(selector),
-                    ))),
+                        Type::Bytes(4),
+                        BigInt::from(dest_func.selector()),
+                    )],
                     args,
                 };
 
@@ -1374,7 +1369,7 @@ pub fn emit_function_call(
                 ..
             } = function.ty()
             {
-                let tys = args.iter().map(|a| a.ty()).collect();
+                let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
                 let args = args
                     .iter()
                     .map(|a| expression(a, cfg, callee_contract_no, ns, vartab))
@@ -1385,7 +1380,7 @@ pub fn emit_function_call(
 
                 let selector = Expression::Builtin(
                     *loc,
-                    vec![Type::Uint(32)],
+                    vec![Type::Bytes(4)],
                     Builtin::ExternalFunctionSelector,
                     vec![function.clone()],
                 );
@@ -1396,11 +1391,12 @@ pub fn emit_function_call(
                     vec![function],
                 );
 
+                tys.insert(0, Type::Bytes(4));
+
                 let payload = Expression::AbiEncode {
                     loc: *loc,
-                    packed: false,
                     tys,
-                    selector: Some(Box::new(selector)),
+                    packed: vec![selector],
                     args,
                 };
 

+ 5 - 4
src/codegen/statements.rs

@@ -760,7 +760,9 @@ fn try_catch(
                 let gas = expression(gas, cfg, callee_contract_no, ns, vartab);
                 let function = expression(function, cfg, callee_contract_no, ns, vartab);
 
-                let tys = args.iter().map(|a| a.ty()).collect();
+                let mut tys: Vec<Type> = args.iter().map(|a| a.ty()).collect();
+
+                tys.insert(0, Type::Bytes(4));
 
                 let args = args
                     .iter()
@@ -769,7 +771,7 @@ fn try_catch(
 
                 let selector = Expression::Builtin(
                     *loc,
-                    vec![Type::Uint(32)],
+                    vec![Type::Bytes(4)],
                     Builtin::ExternalFunctionSelector,
                     vec![function.clone()],
                 );
@@ -783,9 +785,8 @@ fn try_catch(
 
                 let payload = Expression::AbiEncode {
                     loc: *loc,
-                    packed: false,
                     tys,
-                    selector: Some(Box::new(selector)),
+                    packed: vec![selector],
                     args,
                 };
 

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 895 - 121
src/emit/ethabiencoder.rs


+ 16 - 9
src/emit/ewasm.rs

@@ -677,16 +677,15 @@ impl EwasmTarget {
     fn encode<'b>(
         &self,
         contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
         constant: Option<(PointerValue<'b>, u64)>,
         load: bool,
         function: FunctionValue<'b>,
+        packed: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> (PointerValue<'b>, IntValue<'b>) {
-        let encoder = ethabiencoder::EncoderBuilder::new(
-            contract, function, selector, load, args, tys, false,
-        );
+        let encoder =
+            ethabiencoder::EncoderBuilder::new(contract, function, load, packed, args, tys, false);
 
         let mut length = encoder.encoded_length();
 
@@ -1139,13 +1138,12 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
     fn abi_encode_to_vector<'b>(
         &self,
         contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
         function: FunctionValue<'b>,
-        packed: bool,
+        packed: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> PointerValue<'b> {
-        ethabiencoder::encode_to_vector(contract, selector, function, packed, args, tys, false)
+        ethabiencoder::encode_to_vector(contract, function, packed, args, tys, false)
     }
 
     fn abi_encode<'b>(
@@ -1157,7 +1155,16 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> (PointerValue<'b>, IntValue<'b>) {
-        self.encode(contract, selector, None, load, function, args, tys)
+        let mut tys = tys.to_vec();
+
+        let packed = if let Some(selector) = selector {
+            tys.insert(0, ast::Type::Uint(32));
+            vec![selector.into()]
+        } else {
+            vec![]
+        };
+
+        self.encode(contract, None, load, function, &packed, args, &tys)
     }
 
     fn abi_decode<'b>(
@@ -1226,10 +1233,10 @@ impl<'a> TargetRuntime<'a> for EwasmTarget {
         // input
         let (input, input_len) = self.encode(
             contract,
-            None,
             Some((code, wasm.len() as u64)),
             false,
             function,
+            &[],
             args,
             &tys,
         );

+ 16 - 8
src/emit/generic.rs

@@ -514,14 +514,13 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
     /// ABI encode into a vector for abi.encode* style builtin functions
     fn abi_encode_to_vector<'b>(
         &self,
-        _contract: &Contract<'b>,
-        _selector: Option<IntValue<'b>>,
-        _function: FunctionValue<'b>,
-        _packed: bool,
-        _args: &[BasicValueEnum<'b>],
-        _spec: &[ast::Type],
+        contract: &Contract<'b>,
+        function: FunctionValue<'b>,
+        packed: &[BasicValueEnum<'b>],
+        args: &[BasicValueEnum<'b>],
+        tys: &[ast::Type],
     ) -> PointerValue<'b> {
-        unimplemented!();
+        ethabiencoder::encode_to_vector(contract, function, packed, args, tys, false)
     }
 
     fn abi_encode<'b>(
@@ -533,8 +532,17 @@ impl<'a> TargetRuntime<'a> for GenericTarget {
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> (PointerValue<'b>, IntValue<'b>) {
+        let mut tys = tys.to_vec();
+
+        let packed = if let Some(selector) = selector {
+            tys.insert(0, ast::Type::Uint(32));
+            vec![selector.into()]
+        } else {
+            vec![]
+        };
+
         let encoder = ethabiencoder::EncoderBuilder::new(
-            contract, function, selector, load, args, tys, false,
+            contract, function, load, args, &packed, &tys, false,
         );
 
         let length = encoder.encoded_length();

+ 13 - 28
src/emit/mod.rs

@@ -99,9 +99,8 @@ pub trait TargetRuntime<'a> {
     fn abi_encode_to_vector<'b>(
         &self,
         contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
         function: FunctionValue<'b>,
-        packed: bool,
+        packed: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> PointerValue<'b>;
@@ -2578,20 +2577,15 @@ pub trait TargetRuntime<'a> {
                     .into()
             }
             Expression::AbiEncode {
-                tys,
-                selector,
-                packed,
-                args,
-                ..
+                tys, packed, args, ..
             } => self
                 .abi_encode_to_vector(
                     contract,
-                    selector.as_ref().map(|s| {
-                        self.expression(contract, &s, vartab, function)
-                            .into_int_value()
-                    }),
                     function,
-                    *packed,
+                    &packed
+                        .iter()
+                        .map(|a| self.expression(contract, &a, vartab, function))
+                        .collect::<Vec<BasicValueEnum>>(),
                     &args
                         .iter()
                         .map(|a| self.expression(contract, &a, vartab, function))
@@ -2856,11 +2850,7 @@ pub trait TargetRuntime<'a> {
                     .expression(contract, address, vartab, function)
                     .into_int_value();
 
-                let mut selector = contract.ns.functions[*function_no].selector();
-
-                if contract.ns.target == Target::Substrate {
-                    selector = selector.to_be();
-                }
+                let selector = contract.ns.functions[*function_no].selector();
 
                 assert!(matches!(ty, ast::Type::ExternalFunction { .. }));
 
@@ -3613,11 +3603,7 @@ pub trait TargetRuntime<'a> {
                     Instr::AssertFailure { expr: Some(expr) } => {
                         let v = self.expression(contract, expr, &w.vars, function);
 
-                        let selector = if contract.ns.target == Target::Ewasm {
-                            0x08c3_79a0u32.to_be()
-                        } else {
-                            0x08c3_79a0u32
-                        };
+                        let selector = 0x08c3_79a0u32;
 
                         let (data, len) = self.abi_encode(
                             contract,
@@ -4035,11 +4021,10 @@ pub trait TargetRuntime<'a> {
                                 )
                                 .into_int_value();
 
-                            // ewasm stores the selector little endian
-                            let selector = if contract.ns.target == Target::Ewasm {
-                                (*selector).to_be()
-                            } else {
+                            let selector = if contract.ns.target == Target::Substrate {
                                 *selector
+                            } else {
+                                selector.to_be()
                             };
 
                             let correct_selector = contract.builder.build_int_compare(
@@ -4401,7 +4386,7 @@ pub trait TargetRuntime<'a> {
                 None,
                 true,
                 function,
-                &args[f.params.len()..],
+                &args[f.params.len()..f.params.len() + f.returns.len()],
                 &tys,
             );
 
@@ -4416,7 +4401,7 @@ pub trait TargetRuntime<'a> {
             contract
                 .context
                 .i32_type()
-                .const_int(f.selector as u64, false),
+                .const_int(f.selector.to_be() as u64, false),
             bb,
         ));
     }

+ 11 - 3
src/emit/sabre.rs

@@ -599,9 +599,8 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
     fn abi_encode_to_vector<'b>(
         &self,
         _contract: &Contract<'b>,
-        _selector: Option<IntValue<'b>>,
         _function: FunctionValue<'b>,
-        _packed: bool,
+        _packed: &[BasicValueEnum<'b>],
         _args: &[BasicValueEnum<'b>],
         _spec: &[ast::Type],
     ) -> PointerValue<'b> {
@@ -617,8 +616,17 @@ impl<'a> TargetRuntime<'a> for SabreTarget {
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> (PointerValue<'b>, IntValue<'b>) {
+        let mut tys = tys.to_vec();
+
+        let packed = if let Some(selector) = selector {
+            tys.insert(0, ast::Type::Uint(32));
+            vec![selector.into()]
+        } else {
+            vec![]
+        };
+
         let encoder = ethabiencoder::EncoderBuilder::new(
-            contract, function, selector, load, args, tys, false,
+            contract, function, load, args, &packed, &tys, false,
         );
 
         let length = encoder.encoded_length();

+ 14 - 4
src/emit/solana.rs

@@ -2246,13 +2246,12 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
     fn abi_encode_to_vector<'b>(
         &self,
         contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
         function: FunctionValue<'b>,
-        packed: bool,
+        packed: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> PointerValue<'b> {
-        ethabiencoder::encode_to_vector(contract, selector, function, packed, args, tys, false)
+        ethabiencoder::encode_to_vector(contract, function, packed, args, tys, true)
     }
 
     fn abi_encode(
@@ -2264,10 +2263,21 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         args: &[BasicValueEnum<'a>],
         tys: &[ast::Type],
     ) -> (PointerValue<'a>, IntValue<'a>) {
+        debug_assert_eq!(args.len(), tys.len());
+
         let (output_len, output, output_size) = self.return_buffer(contract);
 
+        let mut tys = tys.to_vec();
+
+        let packed = if let Some(selector) = selector {
+            tys.insert(0, ast::Type::Uint(32));
+            vec![selector.into()]
+        } else {
+            vec![]
+        };
+
         let encoder =
-            ethabiencoder::EncoderBuilder::new(contract, function, selector, load, args, tys, true);
+            ethabiencoder::EncoderBuilder::new(contract, function, load, &packed, args, &tys, true);
 
         let length = encoder.encoded_length();
 

+ 24 - 59
src/emit/substrate.rs

@@ -2985,32 +2985,35 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn abi_encode_to_vector<'b>(
         &self,
         contract: &Contract<'b>,
-        selector: Option<IntValue<'b>>,
         function: FunctionValue<'b>,
-        packed: bool,
+        packed: &[BasicValueEnum<'b>],
         args: &[BasicValueEnum<'b>],
         tys: &[ast::Type],
     ) -> PointerValue<'b> {
         // first calculate how much memory we need to allocate
         let mut length = contract.context.i32_type().const_zero();
 
+        debug_assert_eq!(packed.len() + args.len(), tys.len());
+
+        let mut tys_iter = tys.iter();
+
         // note that encoded_length return the exact value for packed encoding
-        for (i, ty) in tys.iter().enumerate() {
+        for arg in packed {
+            let ty = tys_iter.next().unwrap();
+
             length = contract.builder.build_int_add(
                 length,
-                self.encoded_length(args[i], false, packed, &ty, function, contract),
+                self.encoded_length(*arg, false, true, ty, function, contract),
                 "",
             );
         }
 
-        if selector.is_some() {
+        for arg in args {
+            let ty = tys_iter.next().unwrap();
+
             length = contract.builder.build_int_add(
                 length,
-                contract
-                    .context
-                    .i32_type()
-                    .size_of()
-                    .const_cast(contract.context.i32_type(), false),
+                self.encoded_length(*arg, false, false, ty, function, contract),
                 "",
             );
         }
@@ -3050,7 +3053,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         );
 
         // if it's packed, we have the correct length already
-        if packed {
+        if args.is_empty() {
             let data_len = unsafe {
                 contract.builder.build_gep(
                     v,
@@ -3098,59 +3101,21 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
         let mut argsdata = data;
 
-        if let Some(selector) = selector {
-            // we need to byte-swap our bytes4 type
-
-            let temp = contract
-                .builder
-                .build_alloca(selector.get_type(), "selector");
-
-            contract.builder.build_store(temp, selector);
+        let mut tys_iter = tys.iter();
 
-            // byte order needs to be reversed. e.g. hex"11223344" should be 0x10 0x11 0x22 0x33 0x44
-            contract.builder.build_call(
-                contract.module.get_function("__leNtobeN").unwrap(),
-                &[
-                    contract
-                        .builder
-                        .build_pointer_cast(
-                            temp,
-                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "",
-                        )
-                        .into(),
-                    data.into(),
-                    contract.context.i32_type().const_int(4, false).into(),
-                ],
-                "",
-            );
+        for arg in packed {
+            let ty = tys_iter.next().unwrap();
 
-            argsdata = unsafe {
-                contract.builder.build_gep(
-                    argsdata,
-                    &[contract
-                        .context
-                        .i32_type()
-                        .size_of()
-                        .const_cast(contract.context.i32_type(), false)],
-                    "",
-                )
-            };
+            self.encode_ty(contract, false, true, function, ty, *arg, &mut argsdata);
         }
 
-        for (i, ty) in tys.iter().enumerate() {
-            self.encode_ty(
-                contract,
-                false,
-                packed,
-                function,
-                &ty,
-                args[i],
-                &mut argsdata,
-            );
+        for arg in args {
+            let ty = tys_iter.next().unwrap();
+
+            self.encode_ty(contract, false, false, function, ty, *arg, &mut argsdata);
         }
 
-        if !packed {
+        if !args.is_empty() {
             let length = contract.builder.build_int_sub(
                 contract
                     .builder
@@ -3343,7 +3308,7 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
                 contract
                     .context
                     .i32_type()
-                    .const_int(constructor.selector() as u64, false),
+                    .const_int(constructor.selector().to_be() as u64, false),
             ),
             false,
             function,

+ 4 - 7
src/sema/ast.rs

@@ -188,7 +188,7 @@ impl Function {
         hasher.update(self.signature.as_bytes());
         hasher.finalize(&mut res);
 
-        u32::from_le_bytes([res[0], res[1], res[2], res[3]])
+        u32::from_be_bytes([res[0], res[1], res[2], res[3]])
     }
 
     /// Is this a constructor
@@ -556,8 +556,7 @@ pub enum Expression {
     AbiEncode {
         loc: pt::Loc,
         tys: Vec<Type>,
-        selector: Option<Box<Expression>>,
-        packed: bool,
+        packed: Vec<Expression>,
         args: Vec<Expression>,
     },
     List(pt::Loc, Vec<Expression>),
@@ -926,18 +925,16 @@ impl Expression {
                 Expression::AbiEncode {
                     loc,
                     tys,
-                    selector,
                     packed,
                     args,
                 } => {
+                    let packed = packed.iter().map(|e| filter(e, ctx)).collect();
                     let args = args.iter().map(|e| filter(e, ctx)).collect();
-                    let selector = selector.as_ref().map(|e| Box::new(filter(&e, ctx)));
 
                     Expression::AbiEncode {
                         loc: *loc,
                         tys: tys.clone(),
-                        selector,
-                        packed: *packed,
+                        packed,
                         args,
                     }
                 }

+ 6 - 2
src/sema/contracts.rs

@@ -57,8 +57,12 @@ impl ast::Contract {
         for cfg in &self.cfg {
             if !cfg.is_placeholder() {
                 out += &format!(
-                    "\n# {} {} public:{} nonpayable:{}\n",
-                    cfg.ty, cfg.name, cfg.public, cfg.nonpayable
+                    "\n# {} {} public:{} selector:{} nonpayable:{}\n",
+                    cfg.ty,
+                    cfg.name,
+                    cfg.public,
+                    hex::encode(cfg.selector.to_be_bytes()),
+                    cfg.nonpayable,
                 );
 
                 out += &format!(

+ 1 - 1
src/sema/expression.rs

@@ -4446,7 +4446,7 @@ fn member_access(
             if id.name == "selector" {
                 return Ok(Expression::Builtin(
                     e.loc(),
-                    vec![Type::Uint(32)],
+                    vec![Type::Bytes(4)],
                     Builtin::ExternalFunctionSelector,
                     vec![expr],
                 ));

+ 11 - 0
tests/ewasm.rs

@@ -2052,11 +2052,22 @@ fn external_call() {
             function test() public returns (int32) {
                 return x.get_x();
             }
+
+            function enc() public returns (bytes) {
+                return abi.encodeWithSignature("get_x()");
+            }
         }"##,
     );
 
     runtime.constructor(&[]);
 
+    let ret = runtime.function("enc", &[]);
+
+    assert_eq!(
+        ret,
+        vec!(ethabi::Token::Bytes(0x3829050au32.to_be_bytes().into()))
+    );
+
     let ret = runtime.function("test", &[]);
 
     assert_eq!(

+ 1 - 1
tests/ewasm_tests/abi.rs

@@ -34,7 +34,7 @@ fn abi_encode() {
 
     let returns = vm.function("test", &[]);
 
-    let mut bytes = vec![1, 2, 3, 4];
+    let mut bytes = vec![4, 3, 2, 1];
 
     bytes.extend(
         encode(&[

+ 43 - 0
tests/solana_tests/abi.rs

@@ -0,0 +1,43 @@
+use crate::build_solidity;
+
+#[test]
+fn packed() {
+    let mut vm = build_solidity(
+        r#"
+        struct s {
+            int32 f1;
+            uint8 f2;
+            string f3;
+            uint16[2] f4;
+        }
+
+        contract bar {
+            function test() public {
+                uint16 a = 0xfd01;
+                assert(abi.encodePacked(a) == hex"fd01");
+                uint32 b = 0xaabbccdd;
+                assert(abi.encodePacked(true, b, false) == hex"01aabbccdd00");
+            }
+
+            function test2() public {
+                string b = "foobar";
+                assert(abi.encodePacked(b) == "foobar");
+
+                assert(abi.encodePacked("foobar") == "foobar");
+                assert(abi.encodePacked("foo", "bar") == "foobar");
+            }
+
+            function test3() public {
+                s x = s({ f1: 511, f2: 0xf7, f3: "testie", f4: [ 4, 5 ] });
+
+                assert(abi.encodePacked(x) == hex"000001fff774657374696500040005");
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    vm.function("test", &[]);
+    vm.function("test2", &[]);
+    vm.function("test3", &[]);
+}

+ 1 - 0
tests/solana_tests/mod.rs

@@ -1,3 +1,4 @@
+mod abi;
 mod arrays;
 mod mappings;
 mod primitives;

+ 1 - 1
tests/substrate_tests/function_types.rs

@@ -482,7 +482,7 @@ fn ext() {
                 function(int32) external returns (bool) func = this.foo;
 
                 assert(address(this) == func.address);
-                assert(func.selector == 0x42761137);
+                assert(func.selector == hex"42761137");
             }
 
             function foo(int32) public returns (bool) {

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio