瀏覽代碼

Add substrate builtins (#1045)

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Co-authored-by: Lucas Steuernagel <38472950+LucasSte@users.noreply.github.com>
Cyrill Leutwiler 3 年之前
父節點
當前提交
06496ee8f2

+ 19 - 0
docs/language/builtins.rst

@@ -326,6 +326,25 @@ are cast and checked against the function specified as the first argument.
         function bar(int a, bool b) public {}
     }
 
+Hash
+++++
+
+Only available on Substrate, it represents the ``Hash`` type from ``ink_primitives`` via user type definition.
+Its underlying type is ``bytes32``, but it will be reported correctly as the ``Hash`` type in the metadata.
+
+.. code-block:: solidity
+
+    import 'substrate';
+
+    contract c {
+        bytes32 current;
+
+        function set(Hash h) public returns (Hash) {
+            current = Hash.unwrap(h);
+            return Hash.wrap(current);
+        }
+    }
+
 Cryptography
 ____________
 

+ 23 - 0
docs/targets/substrate.rst

@@ -25,3 +25,26 @@ directory. Write this to flipper.sol and run:
 Now you should have a file called ``flipper.contract``. The file contains both the ABI and contract wasm.
 It can be used directly in the
 `Contracts UI <https://contracts-ui.substrate.io/>`_, as if the contract was written in ink!.
+
+Builtin Imports
+________________
+
+Some builtin functionality is only available after importing. The following types
+can be imported via the special import file ``substrate``.
+
+.. code-block:: solidity
+
+    import {Hash} from 'substrate';
+
+Note that ``{Hash}`` can be omitted, renamed or imported via
+import object.
+
+.. code-block:: solidity
+
+    // Now Hash will be known as InkHash
+    import {Hash as InkHash} from 'substrate';
+
+.. note::
+
+    The import file ``substrate`` is only available when compiling for the Substrate
+    target.

+ 9 - 0
src/emit/substrate/mod.rs

@@ -1799,6 +1799,15 @@ impl SubstrateTarget {
                     .i32_type()
                     .const_int(ns.address_length as u64 + 4, false)
             }
+            ast::Type::UserType(user_type) => Self::encoded_length(
+                arg,
+                load,
+                packed,
+                &ns.user_types[*user_type].ty,
+                function,
+                binary,
+                ns,
+            ),
             _ => unreachable!(),
         }
     }

+ 31 - 1
src/sema/builtin.rs

@@ -8,7 +8,7 @@ use super::diagnostics::Diagnostics;
 use super::eval::eval_const_number;
 use super::expression::{expression, ExprContext, ResolveTo};
 use super::symtable::Symtable;
-use crate::sema::ast::RetrieveType;
+use crate::sema::ast::{RetrieveType, Tag, UserTypeDecl};
 use crate::Target;
 use num_bigint::BigInt;
 use num_traits::One;
@@ -1616,4 +1616,34 @@ impl Namespace {
             Symbol::Function(vec![(pt::Loc::Builtin, func_no)])
         ));
     }
+
+    pub fn add_substrate_builtins(&mut self) {
+        let file_no = self.files.len();
+        self.files.push(File {
+            path: PathBuf::from("substrate"),
+            line_starts: Vec::new(),
+            cache_no: None,
+        });
+
+        // The Hash type from ink primitives.
+        let type_no = self.user_types.len();
+        self.user_types.push(UserTypeDecl {
+            tags: vec![Tag {
+                tag: "notice".into(),
+                no: 0,
+                value: "The Hash type from ink primitives".into(),
+            }],
+            loc: pt::Loc::Builtin,
+            name: "Hash".into(),
+            ty: Type::Bytes(32),
+            contract: None,
+        });
+
+        let id = pt::Identifier {
+            loc: pt::Loc::Builtin,
+            name: "Hash".into(),
+        };
+        let symbol = Symbol::UserType(pt::Loc::Builtin, type_no);
+        assert!(self.add_symbol(file_no, None, &id, symbol));
+    }
 }

+ 2 - 2
src/sema/dotgraphviz.rs

@@ -2153,10 +2153,10 @@ impl Namespace {
         }
 
         // user types
-        if !self.user_types.is_empty() {
+        if self.user_types.iter().any(|t| t.loc != pt::Loc::Builtin) {
             let types = dot.add_node(Node::new("types", Vec::new()), None, None);
 
-            for decl in &self.user_types {
+            for decl in self.user_types.iter().filter(|t| t.loc != pt::Loc::Builtin) {
                 let mut labels = vec![
                     format!("name:{} ty:{}", decl.name, decl.ty.to_string(self)),
                     self.loc_to_string(&decl.loc),

+ 4 - 2
src/sema/namespace.rs

@@ -56,8 +56,10 @@ impl Namespace {
             hover_overrides: HashMap::new(),
         };
 
-        if target == Target::Solana {
-            ns.add_solana_builtins();
+        match target {
+            Target::Solana => ns.add_solana_builtins(),
+            Target::Substrate { .. } => ns.add_substrate_builtins(),
+            _ => {}
         }
 
         ns

+ 39 - 0
tests/substrate_tests/builtins.rs

@@ -705,3 +705,42 @@ fn my_token() {
     );
     assert_eq!(&runtime.vm.caller[..], &runtime.vm.output[..]);
 }
+
+#[test]
+fn hash() {
+    let mut runtime = build_solidity(
+        r##"
+        import "substrate";
+
+        contract Foo {
+            Hash current;
+            bytes32 current2;
+
+            function set(Hash h) public returns (bytes32) {
+                current = h;
+                current2 = Hash.unwrap(h);
+                return current2;
+            }
+
+            function get() public view returns (Hash) {
+                return Hash.wrap(current2);
+            }
+        }
+        "##,
+    );
+
+    #[derive(Encode)]
+    struct Hash([u8; 32]);
+
+    let h = Hash([
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+        0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
+        0x31, 0x32,
+    ]);
+
+    runtime.function("set", h.encode());
+    assert_eq!(&runtime.vm.output[..], &h.0[..]);
+
+    runtime.function("get", vec![]);
+    assert_eq!(&runtime.vm.output[..], &h.0[..]);
+}