Browse Source

Implement block.number and block.slot on Solana

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 years ago
parent
commit
327c1b98e1

+ 2 - 0
CHANGELOG.md

@@ -7,6 +7,8 @@ will be documented here.
 ### Added
 - Added supported for solc import mapppings using `--importmap`
 - Added supported for Events on Solana
+- `block.number` and `block.slot` are implemented for Solana
+- Verify ed25519 signatures with `signatureVerify()` on Solana
 
 ### Changed
 - On Solana, the return data is now provided in the program log. As a result,

+ 15 - 1
docs/language.rst

@@ -2950,6 +2950,20 @@ you are doing.
 
 The other block properties depend on which chain is being used.
 
+.. note::
+    Solana requires the `clock account <https://edge.docs.solana.com/developing/runtime-facilities/sysvars#clock>`_
+    to present in the account for the instruction to use any of the ``block`` fields.
+
+    On Solana, ``block.number`` gives the slot number rather than the block height.
+    For processing, you want to use the slot rather the block height. Slots
+    include empty blocks, which do not count towards the block height.
+
+Solana
+~~~~~~
+
+uint64 ``block.slot``
+    The current slot. This is an alias for ``block.number``.
+
 Parity Substrate
 ~~~~~~~~~~~~~~~~
 
@@ -3141,7 +3155,7 @@ This returns the ``bytes32`` blake2_256 hash of the bytes.
     This function is only available on Parity Substrate.
 
 signatureVerify(address public_key, bytes message, bytes signature)
-++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Verify the ed25519 signature given the public key, message, and signature. This
 function returns ``true`` if the signature matches, ``false`` otherwise.

+ 4 - 0
integration/solana/builtins.sol

@@ -15,4 +15,8 @@ contract builtins {
 	function mr_now() public returns (uint64) {
 		return block.timestamp;
 	}
+
+	function mr_slot() public returns (uint64) {
+		return block.slot;
+	}
 }

+ 11 - 1
integration/solana/builtins.spec.ts

@@ -1,6 +1,5 @@
 import expect from 'expect';
 import { establishConnection } from './index';
-import crypto from 'crypto';
 
 describe('Deploy solang contract and test', () => {
     it('builtins', async function () {
@@ -37,5 +36,16 @@ describe('Deploy solang contract and test', () => {
 
         expect(ts).toBeLessThanOrEqual(now);
         expect(ts).toBeGreaterThan(now - 120);
+
+        console.log("calling slot");
+        res = await hash_functions.call_function(conn, "mr_slot", []);
+
+        let sol_slot = Number(res[0]);
+
+        let rpc_slot = await conn.connection.getSlot();
+        console.log("slot from rpc " + rpc_slot);
+
+        expect(sol_slot).toBeGreaterThan(rpc_slot - 10);
+        expect(sol_slot).toBeLessThan(rpc_slot + 10);
     });
 });

+ 35 - 4
src/emit/solana.rs

@@ -3044,20 +3044,51 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
             ast::Expression::Builtin(_, _, ast::Builtin::Timestamp, _) => {
                 let parameters = self.sol_parameters(binary);
 
-                let sol_timestamp = binary.module.get_function("sol_timestamp").unwrap();
+                let sol_clock = binary.module.get_function("sol_clock").unwrap();
 
                 let arg1 = binary.builder.build_pointer_cast(
                     parameters,
-                    sol_timestamp.get_type().get_param_types()[0].into_pointer_type(),
+                    sol_clock.get_type().get_param_types()[0].into_pointer_type(),
                     "",
                 );
 
-                binary
+                let clock = binary
                     .builder
-                    .build_call(sol_timestamp, &[arg1.into()], "timestamp")
+                    .build_call(sol_clock, &[arg1.into()], "clock")
                     .try_as_basic_value()
                     .left()
                     .unwrap()
+                    .into_pointer_value();
+
+                let timestamp = binary
+                    .builder
+                    .build_struct_gep(clock, 4, "unix_timestamp")
+                    .unwrap();
+
+                binary.builder.build_load(timestamp, "timestamp")
+            }
+            ast::Expression::Builtin(_, _, ast::Builtin::BlockNumber | ast::Builtin::Slot, _) => {
+                let parameters = self.sol_parameters(binary);
+
+                let sol_clock = binary.module.get_function("sol_clock").unwrap();
+
+                let arg1 = binary.builder.build_pointer_cast(
+                    parameters,
+                    sol_clock.get_type().get_param_types()[0].into_pointer_type(),
+                    "",
+                );
+
+                let clock = binary
+                    .builder
+                    .build_call(sol_clock, &[arg1.into()], "clock")
+                    .try_as_basic_value()
+                    .left()
+                    .unwrap()
+                    .into_pointer_value();
+
+                let slot = binary.builder.build_struct_gep(clock, 0, "slot").unwrap();
+
+                binary.builder.build_load(slot, "timestamp")
             }
             ast::Expression::Builtin(_, _, ast::Builtin::Sender, _) => {
                 let parameters = self.sol_parameters(binary);

+ 1 - 0
src/sema/ast.rs

@@ -1323,6 +1323,7 @@ pub enum Builtin {
     BlockDifficulty,
     GasLimit,
     BlockNumber,
+    Slot,
     Timestamp,
     Calldata,
     Sender,

+ 11 - 1
src/sema/builtin.rs

@@ -270,7 +270,7 @@ static BUILTIN_FUNCTIONS: [Prototype; 24] = [
 ];
 
 // A list of all Solidity builtins variables
-static BUILTIN_VARIABLE: [Prototype; 13] = [
+static BUILTIN_VARIABLE: [Prototype; 14] = [
     Prototype {
         builtin: Builtin::BlockCoinbase,
         namespace: Some("block"),
@@ -311,6 +311,16 @@ static BUILTIN_VARIABLE: [Prototype; 13] = [
         doc: "Current block number",
         constant: false,
     },
+    Prototype {
+        builtin: Builtin::Slot,
+        namespace: Some("block"),
+        name: "slot",
+        args: &[],
+        ret: &[Type::Uint(64)],
+        target: Some(Target::Solana),
+        doc: "Current slot number",
+        constant: false,
+    },
     Prototype {
         builtin: Builtin::Timestamp,
         namespace: Some("block"),

BIN
stdlib/bpf/solana.bc


+ 2 - 2
stdlib/solana.c

@@ -327,7 +327,7 @@ struct clock_layout
     uint64_t unix_timestamp;
 };
 
-uint64_t sol_timestamp(SolParameters *params)
+struct clock_layout *sol_clock(SolParameters *params)
 {
     if (!params->ka_clock)
     {
@@ -337,7 +337,7 @@ uint64_t sol_timestamp(SolParameters *params)
 
     struct clock_layout *clock_data = (struct clock_layout *)params->ka_clock->data;
 
-    return clock_data->unix_timestamp;
+    return clock_data;
 }
 
 struct account_data_header

+ 20 - 0
tests/solana_tests/builtin.rs

@@ -9,6 +9,12 @@ fn timestamp() {
             function mr_now() public returns (uint64) {
                 return block.timestamp;
             }
+            function mr_slot() public returns (uint64) {
+                return block.slot;
+            }
+            function mr_blocknumber() public returns (uint64) {
+                return block.number;
+            }
         }"#,
     );
 
@@ -20,4 +26,18 @@ fn timestamp() {
         returns,
         vec![Token::Uint(ethereum_types::U256::from(1620656423))]
     );
+
+    let returns = vm.function("mr_slot", &[], &[]);
+
+    assert_eq!(
+        returns,
+        vec![Token::Uint(ethereum_types::U256::from(70818331))]
+    );
+
+    let returns = vm.function("mr_blocknumber", &[], &[]);
+
+    assert_eq!(
+        returns,
+        vec![Token::Uint(ethereum_types::U256::from(70818331))]
+    );
 }