From 3dd14969a108c5e6506bd320f55ad51c38f3d309 Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:03:40 +0530 Subject: [PATCH 1/4] chore(sc_keystore): initial implementation --- contracts/script/Deploy.s.sol | 6 ++-- contracts/src/Foo.sol | 8 ----- contracts/src/ScKeystore.sol | 40 +++++++++++++++++++++++ contracts/test/Foo.t.sol | 26 --------------- contracts/test/ScKeystore.t.sol | 56 +++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 37 deletions(-) delete mode 100644 contracts/src/Foo.sol create mode 100644 contracts/src/ScKeystore.sol delete mode 100644 contracts/test/Foo.t.sol create mode 100644 contracts/test/ScKeystore.t.sol diff --git a/contracts/script/Deploy.s.sol b/contracts/script/Deploy.s.sol index 68f0689..8d6b318 100644 --- a/contracts/script/Deploy.s.sol +++ b/contracts/script/Deploy.s.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.19 <=0.9.0; -import { Foo } from "../src/Foo.sol"; +import { ScKeystore } from "../src/ScKeystore.sol"; import { BaseScript } from "./Base.s.sol"; import { DeploymentConfig } from "./DeploymentConfig.s.sol"; contract Deploy is BaseScript { - function run() public returns (Foo foo, DeploymentConfig deploymentConfig) { + function run() public returns (ScKeystore scKeystore, DeploymentConfig deploymentConfig) { deploymentConfig = new DeploymentConfig(broadcaster); - foo = new Foo(); + scKeystore = new ScKeystore(); } } diff --git a/contracts/src/Foo.sol b/contracts/src/Foo.sol deleted file mode 100644 index d69be05..0000000 --- a/contracts/src/Foo.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19; - -contract Foo { - function id(uint256 value) external pure returns (uint256) { - return value; - } -} diff --git a/contracts/src/ScKeystore.sol b/contracts/src/ScKeystore.sol new file mode 100644 index 0000000..89266ca --- /dev/null +++ b/contracts/src/ScKeystore.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.24; + +import {IScKeystore, UserInfo, KeyPackage} from "./IScKeystore.sol"; + +error UserAlreadyExists(); +error MalformedKeyPackage(); +error MalformedUserInfo(); +error UserDoesNotExist(); + +contract ScKeystore is IScKeystore { + mapping(address user => UserInfo userInfo) private users; + + function userExists(address user) public view returns (bool) { + return users[user].signaturePubKey.length > 0; + } + + function addUser(UserInfo calldata userInfo) external { + if(userInfo.signaturePubKey.length == 0) revert MalformedUserInfo(); + if(userInfo.keyPackages.length != 1) revert MalformedUserInfo(); + if(userExists(msg.sender)) revert UserAlreadyExists(); + + users[msg.sender] = userInfo; + } + + function getUser(address user) external view returns (UserInfo memory) { + return users[user]; + } + + function addKeyPackage(KeyPackage calldata keyPackage) external { + if(keyPackage.data.length == 0) revert MalformedKeyPackage(); + if(!userExists(msg.sender)) revert UserDoesNotExist(); + + users[msg.sender].keyPackages.push(keyPackage); + } + + function getAvailableKeyPackage(address user) external view returns (KeyPackage memory) { + return users[user].keyPackages[users[user].keyPackages.length - 1]; + } +} diff --git a/contracts/test/Foo.t.sol b/contracts/test/Foo.t.sol deleted file mode 100644 index 6b15158..0000000 --- a/contracts/test/Foo.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; - -import { Test, console } from "forge-std/Test.sol"; - -import { Deploy } from "../script/Deploy.s.sol"; -import { DeploymentConfig } from "../script/DeploymentConfig.s.sol"; -import { Foo } from "../src/Foo.sol"; - -contract FooTest is Test { - Foo internal foo; - DeploymentConfig internal deploymentConfig; - - address internal deployer; - - function setUp() public virtual { - Deploy deployment = new Deploy(); - (foo, deploymentConfig) = deployment.run(); - } - - function test_Example() external { - console.log("Hello World"); - uint256 x = 42; - assertEq(foo.id(x), x, "value mismatch"); - } -} diff --git a/contracts/test/ScKeystore.t.sol b/contracts/test/ScKeystore.t.sol new file mode 100644 index 0000000..f0269cd --- /dev/null +++ b/contracts/test/ScKeystore.t.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <0.9.0; + +import { Test } from "forge-std/Test.sol"; +import { Deploy } from "../script/Deploy.s.sol"; +import { DeploymentConfig } from "../script/DeploymentConfig.s.sol"; +import "../src/ScKeystore.sol"; // solhint-disable-line + +contract ScKeystoreTest is Test { + ScKeystore internal s; + DeploymentConfig internal deploymentConfig; + address internal deployer; + + function setUp() public virtual { + Deploy deployment = new Deploy(); + (s, deploymentConfig) = deployment.run(); + } + + function addUser() internal { + KeyPackage[] memory keyPackages = new KeyPackage[](1); + keyPackages[0] = KeyPackage({data: new bytes[](0)}); + UserInfo memory userInfo = UserInfo({ + signaturePubKey: "0x", + keyPackages: keyPackages + }); + s.addUser(userInfo); + } + + function test__userExists__returnsFalse__whenUserDoesNotExist() public view { + assert(!s.userExists(address(this))); + } + + function test__addUser__reverts__whenUserInfoIsMalformed() public { + UserInfo memory userInfo; + vm.expectRevert(MalformedUserInfo.selector); + s.addUser(userInfo); + } + + function test__addUser__reverts__whenUserAlreadyExists() public { + addUser(); + vm.expectRevert(UserAlreadyExists.selector); + addUser(); + } + + function test__addUser__addsUser__whenUserInfoIsValid() public { + addUser(); + assert(s.userExists(address(this))); + } + + function test__getUser__returnsUserInfo__whenUserExists() public { + addUser(); + UserInfo memory userInfo = s.getUser(address(this)); + assert(userInfo.signaturePubKey.length == 2); + assert(userInfo.keyPackages.length == 1); + } +} From 564043e1188fa5298739198be945793971596aa2 Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:06:33 +0530 Subject: [PATCH 2/4] fix: add via_ir --- contracts/.gas-snapshot | 6 +++++- contracts/foundry.toml | 1 + contracts/src/ScKeystore.sol | 12 ++++++------ contracts/test/ScKeystore.t.sol | 7 ++----- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/contracts/.gas-snapshot b/contracts/.gas-snapshot index 73b8bfc..2e5a106 100644 --- a/contracts/.gas-snapshot +++ b/contracts/.gas-snapshot @@ -1 +1,5 @@ -FooTest:test_Example() (gas: 8662) \ No newline at end of file +ScKeystoreTest:test__addUser__addsUser__whenUserInfoIsValid() (gas: 56777) +ScKeystoreTest:test__addUser__reverts__whenUserAlreadyExists() (gas: 61532) +ScKeystoreTest:test__addUser__reverts__whenUserInfoIsMalformed() (gas: 9384) +ScKeystoreTest:test__getUser__returnsUserInfo__whenUserExists() (gas: 60956) +ScKeystoreTest:test__userExists__returnsFalse__whenUserDoesNotExist() (gas: 7976) \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 47f112c..109032c 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -16,6 +16,7 @@ solc = "0.8.24" src = "src" test = "test" + via_ir = true [profile.ci] fuzz = { runs = 10_000 } diff --git a/contracts/src/ScKeystore.sol b/contracts/src/ScKeystore.sol index 89266ca..e5db2c6 100644 --- a/contracts/src/ScKeystore.sol +++ b/contracts/src/ScKeystore.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.24; -import {IScKeystore, UserInfo, KeyPackage} from "./IScKeystore.sol"; +import { IScKeystore, UserInfo, KeyPackage } from "./IScKeystore.sol"; error UserAlreadyExists(); error MalformedKeyPackage(); @@ -16,9 +16,9 @@ contract ScKeystore is IScKeystore { } function addUser(UserInfo calldata userInfo) external { - if(userInfo.signaturePubKey.length == 0) revert MalformedUserInfo(); - if(userInfo.keyPackages.length != 1) revert MalformedUserInfo(); - if(userExists(msg.sender)) revert UserAlreadyExists(); + if (userInfo.signaturePubKey.length == 0) revert MalformedUserInfo(); + if (userInfo.keyPackages.length != 1) revert MalformedUserInfo(); + if (userExists(msg.sender)) revert UserAlreadyExists(); users[msg.sender] = userInfo; } @@ -28,8 +28,8 @@ contract ScKeystore is IScKeystore { } function addKeyPackage(KeyPackage calldata keyPackage) external { - if(keyPackage.data.length == 0) revert MalformedKeyPackage(); - if(!userExists(msg.sender)) revert UserDoesNotExist(); + if (keyPackage.data.length == 0) revert MalformedKeyPackage(); + if (!userExists(msg.sender)) revert UserDoesNotExist(); users[msg.sender].keyPackages.push(keyPackage); } diff --git a/contracts/test/ScKeystore.t.sol b/contracts/test/ScKeystore.t.sol index f0269cd..2697c00 100644 --- a/contracts/test/ScKeystore.t.sol +++ b/contracts/test/ScKeystore.t.sol @@ -18,11 +18,8 @@ contract ScKeystoreTest is Test { function addUser() internal { KeyPackage[] memory keyPackages = new KeyPackage[](1); - keyPackages[0] = KeyPackage({data: new bytes[](0)}); - UserInfo memory userInfo = UserInfo({ - signaturePubKey: "0x", - keyPackages: keyPackages - }); + keyPackages[0] = KeyPackage({ data: new bytes[](0) }); + UserInfo memory userInfo = UserInfo({ signaturePubKey: "0x", keyPackages: keyPackages }); s.addUser(userInfo); } From 120bc4c18d19163a16603c30f76e8784e9cf5d37 Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:26:40 +0530 Subject: [PATCH 3/4] fix: some cleanup --- contracts/src/IScKeystore.sol | 4 ++-- contracts/src/ScKeystore.sol | 38 +++++++++++++++++++++++++++------ contracts/test/ScKeystore.t.sol | 17 +++++++++------ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/contracts/src/IScKeystore.sol b/contracts/src/IScKeystore.sol index 237e806..ccc1449 100644 --- a/contracts/src/IScKeystore.sol +++ b/contracts/src/IScKeystore.sol @@ -7,13 +7,13 @@ struct KeyPackage { } struct UserInfo { - KeyPackage[] keyPackages; + uint256[] keyPackageIndices; bytes signaturePubKey; } interface IScKeystore { function userExists(address user) external view returns (bool); - function addUser(UserInfo calldata userInfo) external; + function addUser(bytes calldata signaturePubKey, KeyPackage calldata keyPackage) external; function getUser(address user) external view returns (UserInfo memory); function addKeyPackage(KeyPackage calldata) external; function getAvailableKeyPackage(address user) external view returns (KeyPackage memory); diff --git a/contracts/src/ScKeystore.sol b/contracts/src/ScKeystore.sol index e5db2c6..bea1119 100644 --- a/contracts/src/ScKeystore.sol +++ b/contracts/src/ScKeystore.sol @@ -9,18 +9,29 @@ error MalformedUserInfo(); error UserDoesNotExist(); contract ScKeystore is IScKeystore { + event UserAdded(address user, bytes signaturePubKey); + event UserKeyPackageAdded(address indexed user, uint256 index); + mapping(address user => UserInfo userInfo) private users; + KeyPackage[] private keyPackages; function userExists(address user) public view returns (bool) { return users[user].signaturePubKey.length > 0; } - function addUser(UserInfo calldata userInfo) external { - if (userInfo.signaturePubKey.length == 0) revert MalformedUserInfo(); - if (userInfo.keyPackages.length != 1) revert MalformedUserInfo(); + function addUser(bytes calldata signaturePubKey, KeyPackage calldata keyPackage) external { + if (signaturePubKey.length == 0) revert MalformedUserInfo(); + if (keyPackage.data.length == 0) revert MalformedKeyPackage(); if (userExists(msg.sender)) revert UserAlreadyExists(); - users[msg.sender] = userInfo; + keyPackages.push(keyPackage); + uint256 keyPackageIndex = keyPackages.length - 1; + + users[msg.sender] = UserInfo(new uint256[](0), signaturePubKey); + users[msg.sender].signaturePubKey = signaturePubKey; + users[msg.sender].keyPackageIndices.push(keyPackageIndex); + + emit UserAdded(msg.sender, signaturePubKey); } function getUser(address user) external view returns (UserInfo memory) { @@ -31,10 +42,25 @@ contract ScKeystore is IScKeystore { if (keyPackage.data.length == 0) revert MalformedKeyPackage(); if (!userExists(msg.sender)) revert UserDoesNotExist(); - users[msg.sender].keyPackages.push(keyPackage); + keyPackages.push(keyPackage); + uint256 keyPackageIndex = keyPackages.length - 1; + users[msg.sender].keyPackageIndices.push(keyPackageIndex); + + emit UserKeyPackageAdded(msg.sender, keyPackageIndex); } function getAvailableKeyPackage(address user) external view returns (KeyPackage memory) { - return users[user].keyPackages[users[user].keyPackages.length - 1]; + UserInfo memory userInfo = users[user]; + uint256 keyPackageIndex = userInfo.keyPackageIndices[userInfo.keyPackageIndices.length - 1]; + return keyPackages[keyPackageIndex]; + } + + function getAllKeyPackagesForUser(address user) external view returns (KeyPackage[] memory) { + UserInfo memory userInfo = users[user]; + KeyPackage[] memory userKeyPackages = new KeyPackage[](userInfo.keyPackageIndices.length); + for (uint256 i = 0; i < userInfo.keyPackageIndices.length; i++) { + userKeyPackages[i] = keyPackages[userInfo.keyPackageIndices[i]]; + } + return userKeyPackages; } } diff --git a/contracts/test/ScKeystore.t.sol b/contracts/test/ScKeystore.t.sol index 2697c00..b8a44b6 100644 --- a/contracts/test/ScKeystore.t.sol +++ b/contracts/test/ScKeystore.t.sol @@ -17,10 +17,8 @@ contract ScKeystoreTest is Test { } function addUser() internal { - KeyPackage[] memory keyPackages = new KeyPackage[](1); - keyPackages[0] = KeyPackage({ data: new bytes[](0) }); - UserInfo memory userInfo = UserInfo({ signaturePubKey: "0x", keyPackages: keyPackages }); - s.addUser(userInfo); + KeyPackage memory keyPackage = KeyPackage({ data: new bytes[](1) }); + s.addUser("0x", keyPackage); } function test__userExists__returnsFalse__whenUserDoesNotExist() public view { @@ -28,9 +26,8 @@ contract ScKeystoreTest is Test { } function test__addUser__reverts__whenUserInfoIsMalformed() public { - UserInfo memory userInfo; vm.expectRevert(MalformedUserInfo.selector); - s.addUser(userInfo); + s.addUser("", KeyPackage({ data: new bytes[](0) })); } function test__addUser__reverts__whenUserAlreadyExists() public { @@ -48,6 +45,12 @@ contract ScKeystoreTest is Test { addUser(); UserInfo memory userInfo = s.getUser(address(this)); assert(userInfo.signaturePubKey.length == 2); - assert(userInfo.keyPackages.length == 1); + assert(userInfo.keyPackageIndices.length == 1); + } + + function test__getAllKeyPackagesForUser__returnsKeyPackages__whenUserExists() public { + addUser(); + KeyPackage[] memory keyPackages = s.getAllKeyPackagesForUser(address(this)); + assert(keyPackages.length == 1); } } From d99905972ab53da95cd4025a38bb426bf8b67b26 Mon Sep 17 00:00:00 2001 From: rymnc <43716372+rymnc@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:28:06 +0530 Subject: [PATCH 4/4] fix: remove via-air --- contracts/foundry.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 109032c..47f112c 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -16,7 +16,6 @@ solc = "0.8.24" src = "src" test = "test" - via_ir = true [profile.ci] fuzz = { runs = 10_000 }