浏览代码

Create writeStringData and writeBufferData (#987)

Lucas Steuernagel 3 年之前
父节点
当前提交
0a07eee7fd
共有 7 个文件被更改,包括 169 次插入1 次删除
  1. 13 0
      docs/language/builtins.rst
  2. 60 0
      src/codegen/expression.rs
  3. 2 0
      src/codegen/mod.rs
  4. 4 0
      src/codegen/tests.rs
  5. 2 0
      src/sema/ast.rs
  6. 23 1
      src/sema/builtin.rs
  7. 65 0
      tests/solana_tests/builtin.rs

+ 13 - 0
docs/language/builtins.rst

@@ -555,6 +555,19 @@ writeAddress(address value, uint32 offset)
 
 Write an ``address`` to the specified offset.
 
+writeString(string value, uint32 offset)
+++++++++++++++++++++++++++++++++++++++++++++
+
+Write the characters of a ``string`` to the specified offset. This function does not
+write the length of the string to the buffer.
+
+writeBytes(bytes value, uint32 offset)
+++++++++++++++++++++++++++++++++++++++++++
+
+Write the bytes of a Solidity dynamic bytes type ``bytes`` to the specified offset.
+This function does not write the length of the byte array to the buffer.
+
+
 Miscellaneous
 _____________
 

+ 60 - 0
src/codegen/expression.rs

@@ -1601,6 +1601,66 @@ fn expr_builtin(
 
             Expression::Undefined(tys[0].clone())
         }
+        ast::Builtin::WriteBytes | ast::Builtin::WriteString => {
+            let buffer = expression(&args[0], cfg, contract_no, func, ns, vartab, opt);
+            let data = expression(&args[1], cfg, contract_no, func, ns, vartab, opt);
+            let offset = expression(&args[2], cfg, contract_no, func, ns, vartab, opt);
+
+            let size = Expression::Builtin(
+                *loc,
+                vec![Type::Uint(32)],
+                Builtin::ArrayLength,
+                vec![data.clone()],
+            );
+
+            let cond = Expression::LessEqual(
+                *loc,
+                Box::new(Expression::Add(
+                    *loc,
+                    Type::Uint(32),
+                    false,
+                    Box::new(offset.clone()),
+                    Box::new(size.clone()),
+                )),
+                Box::new(Expression::Builtin(
+                    *loc,
+                    vec![Type::Uint(32)],
+                    Builtin::ArrayLength,
+                    vec![buffer.clone()],
+                )),
+            );
+
+            let in_bounds = cfg.new_basic_block("in_bounds".to_string());
+            let out_ouf_bounds = cfg.new_basic_block("out_of_bounds".to_string());
+
+            cfg.add(
+                vartab,
+                Instr::BranchCond {
+                    cond,
+                    true_block: in_bounds,
+                    false_block: out_ouf_bounds,
+                },
+            );
+
+            cfg.set_basic_block(out_ouf_bounds);
+            cfg.add(vartab, Instr::AssertFailure { expr: None });
+
+            cfg.set_basic_block(in_bounds);
+            let advanced_ptr = Expression::AdvancePointer {
+                pointer: Box::new(buffer),
+                bytes_offset: Box::new(offset),
+            };
+
+            cfg.add(
+                vartab,
+                Instr::MemCopy {
+                    source: data,
+                    destination: advanced_ptr,
+                    bytes: size,
+                },
+            );
+            Expression::Undefined(tys[0].clone())
+        }
         ast::Builtin::ReadInt8
         | ast::Builtin::ReadInt16LE
         | ast::Builtin::ReadInt32LE

+ 2 - 0
src/codegen/mod.rs

@@ -1268,6 +1268,7 @@ pub enum Builtin {
     WriteUint64LE,
     WriteUint128LE,
     WriteUint256LE,
+    WriteBytes,
 }
 
 impl From<&ast::Builtin> for Builtin {
@@ -1327,6 +1328,7 @@ impl From<&ast::Builtin> for Builtin {
             ast::Builtin::WriteUint64LE => Builtin::WriteUint64LE,
             ast::Builtin::WriteUint128LE => Builtin::WriteUint128LE,
             ast::Builtin::WriteUint256LE => Builtin::WriteUint256LE,
+            ast::Builtin::WriteBytes | ast::Builtin::WriteString => Builtin::WriteBytes,
             _ => panic!("Builtin should not be in the cfg"),
         }
     }

+ 4 - 0
src/codegen/tests.rs

@@ -59,6 +59,8 @@ fn test_builtin_conversion() {
         ast::Builtin::WriteUint64LE,
         ast::Builtin::WriteUint128LE,
         ast::Builtin::WriteUint256LE,
+        ast::Builtin::WriteString,
+        ast::Builtin::WriteBytes,
     ];
 
     let output: Vec<codegen::Builtin> = vec![
@@ -115,6 +117,8 @@ fn test_builtin_conversion() {
         codegen::Builtin::WriteUint64LE,
         codegen::Builtin::WriteUint128LE,
         codegen::Builtin::WriteUint256LE,
+        codegen::Builtin::WriteBytes,
+        codegen::Builtin::WriteBytes,
     ];
 
     for (i, item) in input.iter().enumerate() {

+ 2 - 0
src/sema/ast.rs

@@ -1165,6 +1165,8 @@ pub enum Builtin {
     WriteUint128LE,
     WriteUint256LE,
     WriteAddress,
+    WriteString,
+    WriteBytes,
     Accounts,
     UserTypeWrap,
     UserTypeUnwrap,

+ 23 - 1
src/sema/builtin.rs

@@ -537,7 +537,7 @@ static BUILTIN_VARIABLE: Lazy<[Prototype; 16]> = Lazy::new(|| {
 });
 
 // A list of all Solidity builtins methods
-static BUILTIN_METHODS: Lazy<[Prototype; 25]> = Lazy::new(|| {
+static BUILTIN_METHODS: Lazy<[Prototype; 27]> = Lazy::new(|| {
     [
         Prototype {
             builtin: Builtin::ReadInt8,
@@ -814,6 +814,28 @@ static BUILTIN_METHODS: Lazy<[Prototype; 25]> = Lazy::new(|| {
             doc: "Writes an address to the specified offset",
             constant: false,
         },
+        Prototype {
+            builtin: Builtin::WriteString,
+            namespace: None,
+            method: Some(Type::DynamicBytes),
+            name: "writeString",
+            params: vec![Type::String, Type::Uint(32)],
+            ret: vec![],
+            target: vec![],
+            doc: "Write the contents of a string (without its length) to the specified offset",
+            constant: false,
+        },
+        Prototype {
+            builtin: Builtin::WriteBytes,
+            namespace: None,
+            method: Some(Type::DynamicBytes),
+            name: "writeBytes",
+            params: vec![Type::DynamicBytes, Type::Uint(32)],
+            ret: vec![],
+            target: vec![],
+            doc: "Write the contents of a bytes array (without its length) to the specified offset",
+            constant: false,
+        },
     ]
 });
 

+ 65 - 0
tests/solana_tests/builtin.rs

@@ -154,3 +154,68 @@ fn pda() {
         panic!("{:?} not expected", returns);
     }
 }
+
+#[test]
+fn test_string_bytes_buffer_write() {
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+        function testStringAndBytes() public pure returns (bytes memory) {
+            string str = "coffee";
+            bytes memory b = new bytes(9);
+            b.writeString(str, 0);
+            bytes memory g = "tea";
+            b.writeBytes(g, 6);
+            return b;
+        }
+    }
+        "#,
+    );
+    vm.constructor("Testing", &[]);
+    let returns = vm.function("testStringAndBytes", &[], &[], None);
+    let bytes = returns[0].clone().into_bytes().unwrap();
+
+    assert_eq!(bytes.len(), 9);
+    assert_eq!(&bytes[0..6], b"coffee");
+    assert_eq!(&bytes[6..9], b"tea");
+}
+
+#[test]
+#[should_panic(expected = "unexpected return 0x100000000")]
+fn out_of_bounds_bytes_write() {
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+        function testBytesOut() public pure returns (bytes memory) {
+            bytes memory b = new bytes(9);
+            bytes memory g = "tea";
+            b.writeBytes(g, 30);
+            return b;
+        }
+    }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let _ = vm.function("testBytesOut", &[], &[], None);
+}
+
+#[test]
+#[should_panic(expected = "unexpected return 0x100000000")]
+fn out_of_bounds_string_write() {
+    let mut vm = build_solidity(
+        r#"
+    contract Testing {
+        function testStringOut() public pure returns (bytes memory) {
+            bytes memory b = new bytes(4);
+            string memory str = "cappuccino";
+            b.writeString(str, 0);
+            return b;
+        }
+    }
+        "#,
+    );
+
+    vm.constructor("Testing", &[]);
+    let _ = vm.function("testStringOut", &[], &[], None);
+}