瀏覽代碼

abi decode dynamic storage arrays for substrate

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 年之前
父節點
當前提交
b04411798f
共有 7 個文件被更改,包括 264 次插入88 次删除
  1. 51 1
      src/emit/mod.rs
  2. 90 3
      src/emit/substrate.rs
  3. 二進制
      stdlib/stdlib.bc
  4. 0 84
      stdlib/stdlib.c
  5. 二進制
      stdlib/substrate.bc
  6. 90 0
      stdlib/substrate.c
  7. 33 0
      tests/substrate_arrays/mod.rs

+ 51 - 1
src/emit/mod.rs

@@ -442,6 +442,56 @@ impl<'a> Contract<'a> {
         *data_ref = data_phi.as_basic_value().into_int_value();
     }
 
+    /// Emit a loop from `from` to `to`, checking the condition _before_ the body.
+    pub fn emit_loop_cond_first_with_pointer<F>(
+        &self,
+        function: FunctionValue,
+        from: IntValue<'a>,
+        to: IntValue<'a>,
+        data_ref: &mut PointerValue<'a>,
+        mut insert_body: F,
+    ) where
+        F: FnMut(IntValue<'a>, &mut PointerValue<'a>),
+    {
+        let cond = self.context.append_basic_block(function, "cond");
+        let body = self.context.append_basic_block(function, "body");
+        let done = self.context.append_basic_block(function, "done");
+        let entry = self.builder.get_insert_block().unwrap();
+
+        self.builder.build_unconditional_branch(cond);
+        self.builder.position_at_end(cond);
+
+        let loop_ty = from.get_type();
+        let loop_phi = self.builder.build_phi(loop_ty, "index");
+        let data_phi = self.builder.build_phi(data_ref.get_type(), "data");
+        let mut data = data_phi.as_basic_value().into_pointer_value();
+
+        let loop_var = loop_phi.as_basic_value().into_int_value();
+
+        let next = self
+            .builder
+            .build_int_add(loop_var, loop_ty.const_int(1, false), "next_index");
+
+        let comp = self
+            .builder
+            .build_int_compare(IntPredicate::ULT, loop_var, to, "loop_cond");
+        self.builder.build_conditional_branch(comp, body, done);
+
+        self.builder.position_at_end(body);
+        // add loop body
+        insert_body(loop_var, &mut data);
+
+        let body = self.builder.get_insert_block().unwrap();
+
+        loop_phi.add_incoming(&[(&from, entry), (&next, body)]);
+        data_phi.add_incoming(&[(&*data_ref, entry), (&data, body)]);
+
+        self.builder.build_unconditional_branch(cond);
+
+        self.builder.position_at_end(done);
+
+        *data_ref = data_phi.as_basic_value().into_pointer_value();
+    }
     fn emit_functions(&mut self, runtime: &dyn TargetRuntime) {
         for func in &self.ns.functions {
             let name = if func.name != "" {
@@ -1417,7 +1467,7 @@ impl<'a> Contract<'a> {
 
                     dest.into()
                 } else {
-                    // FIXME: iterate over dynamic array
+                    // iterate over dynamic array
                     let slot_ty = resolver::Type::Uint(256);
 
                     let size = self.builder.build_int_truncate(

+ 90 - 3
src/emit/substrate.rs

@@ -481,11 +481,98 @@ impl SubstrateTarget {
                             }
                         },
                     );
+
+                    to.into()
                 } else {
-                    // FIXME
-                }
+                    let len = contract
+                        .builder
+                        .build_alloca(contract.context.i32_type(), "length");
 
-                to.into()
+                    *data = contract
+                        .builder
+                        .build_call(
+                            contract.module.get_function("compact_decode_u32").unwrap(),
+                            &[(*data).into(), len.into()],
+                            "",
+                        )
+                        .try_as_basic_value()
+                        .left()
+                        .unwrap()
+                        .into_pointer_value();
+
+                    let len = contract
+                        .builder
+                        .build_load(len, "array.len")
+                        .into_int_value();
+
+                    // details about our array elements
+                    let elem_ty = contract.llvm_type(&ty.array_elem());
+                    let elem_size = contract.builder.build_int_truncate(
+                        elem_ty.size_of().unwrap(),
+                        contract.context.i32_type(),
+                        "size_of",
+                    );
+
+                    let init = contract.builder.build_int_to_ptr(
+                        contract.context.i32_type().const_all_ones(),
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "invalid",
+                    );
+
+                    let v = contract
+                        .builder
+                        .build_call(
+                            contract.module.get_function("vector_new").unwrap(),
+                            &[len.into(), elem_size.into(), init.into()],
+                            "",
+                        )
+                        .try_as_basic_value()
+                        .left()
+                        .unwrap()
+                        .into_pointer_value();
+
+                    contract.emit_loop_cond_first_with_pointer(
+                        function,
+                        contract.context.i32_type().const_zero(),
+                        len,
+                        data,
+                        |elem_no: IntValue<'b>, data: &mut PointerValue<'b>| {
+                            let index = contract.builder.build_int_mul(elem_no, elem_size, "");
+
+                            let element_start = unsafe {
+                                contract.builder.build_gep(
+                                    v,
+                                    &[
+                                        contract.context.i32_type().const_zero(),
+                                        contract.context.i32_type().const_int(2, false),
+                                        index,
+                                    ],
+                                    "data",
+                                )
+                            };
+
+                            let elem = contract.builder.build_pointer_cast(
+                                element_start,
+                                elem_ty.ptr_type(AddressSpace::Generic),
+                                "entry",
+                            );
+
+                            let ty = ty.array_deref();
+
+                            if ty.is_reference_type() {
+                                let val = contract
+                                    .builder
+                                    .build_alloca(contract.llvm_type(&ty.deref()), "");
+                                self.decode_ty(contract, function, &ty, Some(val), data);
+                                contract.builder.build_store(elem, val);
+                            } else {
+                                self.decode_ty(contract, function, &ty, Some(elem), data);
+                            }
+                        },
+                    );
+
+                    v.into()
+                }
             }
             resolver::Type::String | resolver::Type::DynamicBytes => {
                 let from = contract.builder.build_alloca(

二進制
stdlib/stdlib.bc


+ 0 - 84
stdlib/stdlib.c

@@ -450,87 +450,3 @@ struct vector *concat(uint8_t *left, uint32_t left_len, uint8_t *right, uint32_t
 
 	return v;
 }
-
-// Encode an 32 bit integer as as scale compact integer
-// https://substrate.dev/docs/en/conceptual/core/codec#vectors-lists-series-sets
-uint8_t *compact_encode_u32(uint8_t *dest, uint32_t val)
-{
-	if (val < 64)
-	{
-		*dest++ = val << 2;
-	}
-	else if (val < 0x4000)
-	{
-		*((uint16_t *)dest) = (val << 2) | 1;
-		dest += 2;
-	}
-	else if (val < 0x40000000)
-	{
-		*((uint32_t *)dest) = (val << 2) | 2;
-		dest += 4;
-	}
-	else
-	{
-		*dest++ = 3;
-		*((uint32_t *)dest) = val;
-		dest += 4;
-	}
-
-	return dest;
-}
-
-uint8_t *scale_encode_string(uint8_t *dest, struct vector *s)
-{
-	uint32_t len = s->len;
-	uint8_t *data_dst = compact_encode_u32(dest, len);
-	uint8_t *data = s->data;
-
-	while (len--)
-	{
-		*data_dst++ = *data++;
-	}
-
-	return data_dst;
-}
-
-struct vector *scale_decode_string(uint8_t **from)
-{
-	uint8_t *src = *from;
-	uint8_t upperbits = *src >> 2;
-	uint32_t size_array;
-
-	switch (*src & 0x03)
-	{
-	case 0:
-		size_array = upperbits;
-		src += 1;
-		break;
-	case 1:
-		size_array = *((uint16_t *)src) >> 2;
-		src += 2;
-		break;
-	case 2:
-		size_array = *((uint32_t *)src) >> 2;
-		src += 4;
-		break;
-	default:
-		// sizes of 2**30 (1GB) or larger are not allowed
-		__builtin_unreachable();
-	}
-
-	struct vector *v = __malloc(sizeof(*v) + size_array);
-
-	v->len = size_array;
-	v->size = size_array;
-
-	uint8_t *data = v->data;
-
-	while (size_array--)
-	{
-		*data++ = *src++;
-	}
-
-	*from = src;
-
-	return v;
-}

二進制
stdlib/substrate.bc


+ 90 - 0
stdlib/substrate.c

@@ -128,3 +128,93 @@ uint32_t substrate_string_length(uint8_t *slot)
 
     return ext_scratch_size();
 }
+
+// 32 bit integer as as scale compact integer
+// https://substrate.dev/docs/en/conceptual/core/codec#vectors-lists-series-sets
+uint8_t *compact_encode_u32(uint8_t *dest, uint32_t val)
+{
+    if (val < 64)
+    {
+        *dest++ = val << 2;
+    }
+    else if (val < 0x4000)
+    {
+        *((uint16_t *)dest) = (val << 2) | 1;
+        dest += 2;
+    }
+    else if (val < 0x40000000)
+    {
+        *((uint32_t *)dest) = (val << 2) | 2;
+        dest += 4;
+    }
+    else
+    {
+        *dest++ = 3;
+        *((uint32_t *)dest) = val;
+        dest += 4;
+    }
+
+    return dest;
+}
+
+uint8_t *compact_decode_u32(uint8_t *dest, uint32_t *val)
+{
+    switch (*dest & 3)
+    {
+    case 0:
+        *val = *dest >> 2;
+        dest += 1;
+        break;
+    case 1:
+        *val = *((uint16_t *)dest) >> 2;
+        dest += 2;
+        break;
+    case 2:
+        *val = *((uint32_t *)dest) >> 2;
+        dest += 4;
+        break;
+    default:
+        // sizes of 2**30 (1GB) or larger are not allowed
+        __builtin_unreachable();
+    }
+
+    return dest;
+}
+
+uint8_t *scale_encode_string(uint8_t *dest, struct vector *s)
+{
+    uint32_t len = s->len;
+    uint8_t *data_dst = compact_encode_u32(dest, len);
+    uint8_t *data = s->data;
+
+    while (len--)
+    {
+        *data_dst++ = *data++;
+    }
+
+    return data_dst;
+}
+
+struct vector *scale_decode_string(uint8_t **from)
+{
+    uint8_t *src = *from;
+    uint32_t size_array;
+
+    src = compact_decode_u32(src, &size_array);
+
+    struct vector *v = __malloc(sizeof(*v) + size_array);
+
+    v->len = size_array;
+    v->size = size_array;
+
+    uint8_t *data = v->data;
+
+    while (size_array--)
+    {
+        *data++ = *src++;
+    }
+
+    *from = src;
+
+    return v;
+}

+ 33 - 0
tests/substrate_arrays/mod.rs

@@ -1444,3 +1444,36 @@ fn abi_encode_dynamic_array() {
         Int32Array(vec!(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30)).encode()
     );
 }
+
+#[test]
+fn abi_decode_dynamic_array() {
+    #[derive(Debug, PartialEq, Encode, Decode)]
+    struct Int32Array(Vec<i32>);
+
+    let (runtime, mut store) = build_solidity(
+        r#"
+        contract c {
+            function decode(int32[] bar) pure public {
+                assert(bar.length == 11);
+
+                for (int32 i = 0; i <11; i++) {
+                    assert(bar[uint32(i)] == i * 3);
+                }
+            }
+
+            function decode_empty(int32[] bar) pure public {
+                assert(bar.length == 0);
+            }
+        }
+        "#,
+    );
+
+    runtime.constructor(&mut store, 0, Vec::new());
+    runtime.function(
+        &mut store,
+        "decode",
+        Int32Array(vec![0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]).encode(),
+    );
+
+    runtime.function(&mut store, "decode_empty", Int32Array(vec![]).encode());
+}