Bladeren bron

Add integrations arrays and mapping test

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 4 jaren geleden
bovenliggende
commit
3ca53356fe
3 gewijzigde bestanden met toevoegingen van 156 en 2 verwijderingen
  1. 74 0
      integration/solana/arrays.sol
  2. 69 1
      integration/solana/simple.spec.ts
  3. 13 1
      src/sema/expression.rs

+ 74 - 0
integration/solana/arrays.sol

@@ -0,0 +1,74 @@
+enum Permission {
+	Perm1, Perm2, Perm3, Perm4, Perm5, Perm6, Perm7, Perm8
+}
+
+/// This is a test contract which tests arrays
+contract arrays {
+	uint64 user_count;
+
+	struct user {
+		string name;
+		bytes32 addr;
+		uint64 id;
+		Permission[] perms;
+	}
+
+	// declare a sparse array. Sparse arrays are arrays which are too large to
+	// fit into account data on Solana; this is not neccessarily a Solidity feature
+	user[type(uint64).max] users;
+
+	mapping (bytes32 => uint64) addressToUser;
+
+	function addUser(uint64 id, bytes32 addr, string name, Permission[] perms) public {
+		user storage u = users[id];
+
+		u.name = name;
+		u.addr = addr;
+		u.id = id;
+		u.perms = perms;
+
+		addressToUser[addr] = id;
+
+		assert(id <= users.length);
+	}
+
+	function getUserById(uint64 id) public view returns (user) {
+		assert(users[id].id == id);
+
+		return users[id];
+	}
+
+	function getUserByAddress(bytes32 addr) public view returns (user) {
+		uint64 id = addressToUser[addr];
+
+		assert(users[id].id == id);
+
+		return users[id];
+	}
+
+	function userExists(uint64 id) public view returns (bool) {
+		return users[id].id == id;
+	}
+
+	function removeUser(uint64 id) public {
+		bytes32 addr = users[id].addr;
+
+		delete users[id];
+		delete addressToUser[addr];
+	}
+
+	function hasPermission(uint64 id, Permission p) public view returns (bool) {
+		user storage u = users[id];
+
+		assert(u.id == id);
+
+
+		for (uint32 i = 0; i < u.perms.length; i++) {
+			if (u.perms[i] == p) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+}

+ 69 - 1
integration/solana/simple.spec.ts

@@ -1,5 +1,6 @@
 import expect from 'expect';
 import { establishConnection } from './index';
+import crypto from 'crypto';
 
 describe('Deploy solang contract and test', () => {
     it('flipper', async function () {
@@ -463,5 +464,72 @@ describe('Deploy solang contract and test', () => {
             .rejects
             .toThrowError(new Error('failed to send transaction: Transaction simulation failed: Error processing Instruction 0: account data too small for instruction'));
     });
-});
 
+    it('arrays in account storage', async function () {
+        this.timeout(50000);
+
+        let conn = await establishConnection();
+
+        // storage.sol needs 168 bytes on constructor, more for string data
+        let prog = await conn.loadProgram("arrays.so", "arrays.abi", 512, 4096);
+
+        await prog.call_constructor(conn, []);
+
+        let users = [];
+
+        for (let i = 0; i < 3; i++) {
+            let addr = '0x' + crypto.randomBytes(32).toString('hex');
+            let name = `name${i}`;
+            let id = crypto.randomBytes(4).readUInt32BE(0).toString();
+            let perms: string[] = [];
+
+            for (let j = 0; j < Math.random() * 3; j++) {
+                let p = Math.floor(Math.random() * 8);
+
+                perms.push(`${p}`);
+            }
+
+            await prog.call_function(conn, "addUser", [id, addr, name, perms]);
+
+
+            users.push([
+                name, addr, id, perms
+            ]);
+        }
+
+        function returns(res: Object) {
+            let arr = Object.values(res);
+            let length = arr.pop()
+            expect(arr.length).toEqual(length);
+            return JSON.stringify(arr);
+        }
+
+        let user = users[Math.floor(Math.random() * users.length)];
+
+        let res = returns(await prog.call_function(conn, "getUserById", [user[2]]));
+
+        expect(res).toStrictEqual(JSON.stringify([user]));
+
+        if (user[3].length > 0) {
+            let perms = user[3];
+
+            let p = perms[Math.floor(Math.random() * perms.length)];
+
+            res = returns(await prog.call_function(conn, "hasPermission", [user[2], p]));
+
+            expect(res).toBe(JSON.stringify([true]));
+        }
+
+        user = users[Math.floor(Math.random() * users.length)];
+
+        res = returns(await prog.call_function(conn, "getUserByAddress", [user[1]]));
+
+        expect(res).toStrictEqual(JSON.stringify([user]));
+
+        await prog.call_function(conn, "removeUser", [user[2]]);
+
+        res = returns(await prog.call_function(conn, "userExists", [user[2]]));
+
+        expect(res).toBe(JSON.stringify([false]));
+    });
+});

+ 13 - 1
src/sema/expression.rs

@@ -4293,10 +4293,22 @@ fn member_access(
                     Err(())
                 }
             }
-            Type::Array(_, _) => {
+            Type::Array(_, dim) => {
                 if id.name == "length" {
                     let elem_ty = expr.ty().storage_array_elem().deref_into();
 
+                    if let Some(dim) = &dim[0] {
+                        // sparse array could be large than ns.storage_type() on Solana
+                        if dim.bits() > ns.storage_type().bits(ns) as u64 {
+                            return Ok(Expression::StorageArrayLength {
+                                loc: id.loc,
+                                ty: Type::Uint(256),
+                                array: Box::new(expr),
+                                elem_ty,
+                            });
+                        }
+                    }
+
                     return Ok(Expression::StorageArrayLength {
                         loc: id.loc,
                         ty: ns.storage_type(),