Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(sc_keystore): initial implementation #10

Merged
merged 4 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion contracts/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
FooTest:test_Example() (gas: 8662)
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)
6 changes: 3 additions & 3 deletions contracts/script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -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();
}
}
8 changes: 0 additions & 8 deletions contracts/src/Foo.sol

This file was deleted.

4 changes: 2 additions & 2 deletions contracts/src/IScKeystore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
66 changes: 66 additions & 0 deletions contracts/src/ScKeystore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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 {
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(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();

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) {
return users[user];
}

function addKeyPackage(KeyPackage calldata keyPackage) external {
if (keyPackage.data.length == 0) revert MalformedKeyPackage();
if (!userExists(msg.sender)) revert UserDoesNotExist();

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) {
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;
}
}
26 changes: 0 additions & 26 deletions contracts/test/Foo.t.sol

This file was deleted.

56 changes: 56 additions & 0 deletions contracts/test/ScKeystore.t.sol
Original file line number Diff line number Diff line change
@@ -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 keyPackage = KeyPackage({ data: new bytes[](1) });
s.addUser("0x", keyPackage);
}

function test__userExists__returnsFalse__whenUserDoesNotExist() public view {
assert(!s.userExists(address(this)));
}

function test__addUser__reverts__whenUserInfoIsMalformed() public {
vm.expectRevert(MalformedUserInfo.selector);
s.addUser("", KeyPackage({ data: new bytes[](0) }));
}

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.keyPackageIndices.length == 1);
}

function test__getAllKeyPackagesForUser__returnsKeyPackages__whenUserExists() public {
addUser();
KeyPackage[] memory keyPackages = s.getAllKeyPackagesForUser(address(this));
assert(keyPackages.length == 1);
}
}
Loading