Browse Source

Add ethabi bytesN encoding/decoding

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 years ago
parent
commit
78568b88b0
4 changed files with 151 additions and 0 deletions
  1. 67 0
      src/emit/burrow.rs
  2. BIN
      stdlib/stdlib.bc
  3. 20 0
      stdlib/stdlib.c
  4. 64 0
      tests/burrow.rs

+ 67 - 0
src/emit/burrow.rs

@@ -266,6 +266,48 @@ impl BurrowTarget {
                     ],
                     "");
             }
+            ast::PrimitiveType::Bytes(b) => {
+                // first clear/set the upper bits
+                if *b < 32 {
+                    let dest8 = contract.builder.build_pointer_cast(dest,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "destvoid");
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__bzero8").unwrap(),
+                        &[ dest8.into(), contract.context.i32_type().const_int(4, false).into() ],
+                        "");
+                }
+
+                // no need to allocate space for each uint64
+                // allocate enough for type
+                let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
+                let type_size = int_type.size_of();
+
+                let store = if ty.stack_based() {
+                    val.into_pointer_value()
+                } else {
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_store(store, val);
+
+                    store
+                };
+
+                contract.builder.build_call(
+                    contract.module.get_function("__leNtobeN").unwrap(),
+                    &[
+                        contract.builder.build_pointer_cast(store,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "store").into(),
+                        contract.builder.build_pointer_cast(dest,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "dest").into(),
+                        contract.builder.build_int_truncate(type_size,
+                            contract.context.i32_type(), "").into()
+                    ],
+                    "");
+            }
             _ => unimplemented!(),
         }
     }
@@ -465,6 +507,31 @@ impl TargetRuntime for BurrowTarget {
                         store.into()
                     }
                 }
+                ast::PrimitiveType::Bytes(b) => {
+                    let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
+                    let type_size = int_type.size_of();
+
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__beNtoleN").unwrap(),
+                        &[
+                            contract.builder.build_pointer_cast(data,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                            contract.builder.build_pointer_cast(store,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                            contract.builder.build_int_truncate(type_size,
+                                contract.context.i32_type(), "size").into()
+                        ],
+                        ""
+                    );
+
+                    if *b <= 8 {
+                        contract.builder.build_load(store, &format!("bytes{}", *b))
+                    } else {
+                        store.into()
+                    }
+                }
                 _ => panic!(),
             });
 

BIN
stdlib/stdlib.bc


+ 20 - 0
stdlib/stdlib.c

@@ -200,6 +200,16 @@ void __be32toleN(uint8_t *from, uint8_t *to, uint32_t length)
 	} while (--length);
 }
 
+__attribute__((visibility("hidden")))
+void __beNtoleN(uint8_t *from, uint8_t *to, uint32_t length)
+{
+	from += length;
+
+	do {
+		*to++ = *--from;
+	} while (--length);
+}
+
 // This function is for used for abi encoding integers
 // ABI encoding is big endian.
 __attribute__((visibility("hidden")))
@@ -212,6 +222,16 @@ void __leNtobe32(uint8_t *from, uint8_t *to, uint32_t length)
 	} while (--length);
 }
 
+__attribute__((visibility("hidden")))
+void __leNtobeN(uint8_t *from, uint8_t *to, uint32_t length)
+{
+	to += length;
+
+	do {
+		*--to = *from++;
+	} while (--length);
+}
+
 /*
 	In wasm, the instruction for multiplying two 64 bit values results in a 64 bit value. In
     other words, the result is truncated. The largest values we can multiply without truncation

+ 64 - 0
tests/burrow.rs

@@ -109,6 +109,8 @@ impl TestRuntime {
                 let offset = offset as u32;
                 let returndata = store.memory.get(offset + mem::size_of::<u32>() as u32, 32).unwrap();
 
+                println!("RETURNDATA: {}", hex::encode(&returndata));
+
                 self.abi.functions[name].decode_output(&returndata).unwrap()
             }
             _ => panic!("expected return value when calling {}", name),
@@ -435,4 +437,66 @@ fn address() {
     // no arithmetic/bitwise allowed on address
     // no ordered comparison allowed
     // address 0x27b1fdb04752bbc536007a920d24acb045561C26 should be a warning
+}
+
+#[test]
+fn bytes() {
+    let (runtime, mut store) = build_solidity(r##"
+        contract bar {
+            bytes4 constant foo = hex"11223344";
+
+            function get_foo() public returns (bytes4) {
+                return foo;
+            }
+
+            function bytes4asuint32() public view returns (uint32) {
+                return uint32(foo);
+            }
+
+            function bytes4asuint64() public view returns (uint64) {
+                return uint64(bytes8(foo));
+            }
+
+            function bytes4asbytes2() public view returns (bytes2) {
+                return bytes2(foo);
+            }
+
+            function passthrough(bytes4 bar) public view returns (bytes4) {
+                return bar;
+            }
+/*
+            function entry(uint index) public view returns (byte) {
+                return foo[index];
+            }
+            function shiftedleft() public view returns (bytes4) {
+                return foo << 8;
+            }
+
+            function shiftedright() public view returns (bytes4) {
+                return foo >> 8;
+            }
+*/
+        }"##);
+
+    runtime.constructor(&mut store, &[]);
+
+    let ret = runtime.function(&mut store, "get_foo", &[]);
+
+    assert_eq!(ret, [ ethabi::Token::FixedBytes(vec!( 0x11, 0x22, 0x33, 0x44 )) ]);
+
+    let ret = runtime.function(&mut store, "bytes4asuint32", &[]);
+
+    assert_eq!(ret, [ ethabi::Token::Uint(ethereum_types::U256::from(0x11223344)) ]);
+
+    let ret = runtime.function(&mut store, "bytes4asuint64", &[]);
+
+    assert_eq!(ret, [ ethabi::Token::Uint(ethereum_types::U256::from(0x11223344_0000_0000u64)) ]);
+
+    let ret = runtime.function(&mut store, "bytes4asbytes2", &[]);
+
+    assert_eq!(ret, [ ethabi::Token::FixedBytes(vec!(0x11, 0x22)) ]);
+
+    let val = vec!(ethabi::Token::FixedBytes(vec!(0x41, 0x42, 0x43, 0x44)));
+
+    assert_eq!(runtime.function(&mut store, "passthrough", &val), val);
 }