Skip to content

Commit

Permalink
Merge pull request #4 from shutter-network/shutter-api
Browse files Browse the repository at this point in the history
Shutter api
  • Loading branch information
blockchainluffy authored Jan 8, 2025
2 parents 9337160 + e5f6733 commit 3b74317
Show file tree
Hide file tree
Showing 9 changed files with 886 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ gnoshcontracts/
shopcontracts/
out/
.tool-versions
.env
609 changes: 609 additions & 0 deletions bindings/shutterregistry/shutterregistry.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.22"
solc = "0.8.28"

extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout']
bytecode_hash = 'none'
Expand Down
1 change: 1 addition & 0 deletions gen_bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CONTRACTS=(
"EonKeyPublish"
"KeyBroadcastContract"
"Inbox"
"ShutterRegistry"
)
OUTPUT_DIR="bindings"
PACKAGE_NAME="bindings"
Expand Down
53 changes: 53 additions & 0 deletions script/Deploy.service.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import "../src/common/KeyBroadcastContract.sol";
import "../src/common/KeyperSet.sol";
import "../src/common/KeyperSetManager.sol";
import "../src/shutter-service/ShutterRegistry.sol";

contract Deploy is Script {
function deployKeyperSetManager(
address deployerAddress
) public returns (KeyperSetManager) {
KeyperSetManager ksm = new KeyperSetManager(deployerAddress);
ksm.initialize(deployerAddress, deployerAddress);
console.log("keyper set manager initialised");

// add bootstrap keyper set
KeyperSet fakeKeyperset = new KeyperSet();
fakeKeyperset.setFinalized();
ksm.addKeyperSet(0, address(fakeKeyperset));

console.log("KeyperSetManager:", address(ksm));
return ksm;
}

function deployKeyBroadcastContract(
KeyperSetManager ksm
) public returns (KeyBroadcastContract) {
KeyBroadcastContract kbc = new KeyBroadcastContract(address(ksm));
console.log("KeyBroadcastContract:", address(kbc));
return kbc;
}

function deployRegistry() public returns (ShutterRegistry) {
ShutterRegistry s = new ShutterRegistry();
console.log("Registry:", address(s));
return s;
}

function run() external {
uint256 deployKey = vm.envUint("DEPLOY_KEY");
address deployerAddress = vm.addr(deployKey);
console.log("Deployer:", deployerAddress);
vm.startBroadcast(deployKey);

KeyperSetManager ksm = deployKeyperSetManager(deployerAddress);
deployKeyBroadcastContract(ksm);
deployRegistry();

vm.stopBroadcast();
}
}
22 changes: 22 additions & 0 deletions script/DeployRegistry.service.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import "../src/shutter-service/ShutterRegistry.sol";

contract Deploy is Script {
function run() external {
uint256 deployKey = vm.envUint("DEPLOY_KEY");
address deployerAddress = vm.addr(deployKey);
console.log("Deployer:", deployerAddress);
vm.startBroadcast(deployKey);
deploySequencer();
vm.stopBroadcast();
}

function deploySequencer() public returns (ShutterRegistry) {
ShutterRegistry s = new ShutterRegistry();
console.log("ShutterRegistry:", address(s));
return s;
}
}
21 changes: 21 additions & 0 deletions script/SubmitTransaction.service.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import {ShutterRegistry} from "src/shutter-service/ShutterRegistry.sol";

contract SubmitTransaction is Script {
function run() external {
uint256 privateKey = vm.envUint("TX_SENDER_KEY");
ShutterRegistry registry = ShutterRegistry(
vm.envAddress("REGISTRY_ADDRESS")
);
uint64 eon = uint64(vm.envUint("EON"));
bytes32 identityPrefix = vm.envBytes32("IDENTITY_PREFIX");
uint64 ts = uint64(vm.envUint("TIMESTAMP"));

vm.startBroadcast(privateKey);
registry.register(eon, identityPrefix, ts);
vm.stopBroadcast();
}
}
86 changes: 86 additions & 0 deletions src/shutter-service/ShutterRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "openzeppelin/contracts/access/Ownable.sol";

/**
* @title ShutterRegistry
* @dev A contract for managing the registration of identities with timestamps, ensuring unique and future-dated registrations.
* Inherits from OpenZeppelin's Ownable contract to enable ownership-based access control.
*/
contract ShutterRegistry is Ownable {
// Custom error for when an identity is already registered.
error AlreadyRegistered();

// Custom error for when a provided timestamp is in the past.
error TimestampInThePast();

// Custom error for when a identityPrefix provided is empty.
error InvalidIdentityPrefix();

struct RegistrationData {
uint64 eon;
uint64 timestamp;
}
/**
* @dev Mapping to store registration data for each identity.
* The identity is represented as a `bytes32` hash and mapped to struct RegistrationData.
*/
mapping(bytes32 identity => RegistrationData) public registrations;

/**
* @dev Emitted when a new identity is successfully registered.
* @param eon The eon associated with the identity.
* @param identityPrefix The raw prefix input used to derive the registered identity hash.
* @param sender The address of the account that performed the registration.
* @param timestamp The timestamp associated with the registered identity.
*/
event IdentityRegistered(
uint64 eon,
bytes32 identityPrefix,
address sender,
uint64 timestamp
);

/**
* @dev Initializes the contract and assigns ownership to the deployer.
*/
constructor() Ownable(msg.sender) {}

/**
* @notice Registers a new identity with a specified timestamp and eon.
* @dev The identity is derived by hashing the provided `identityPrefix` concatenated with the sender's address.
* @param eon The eon associated with the identity.
* @param identityPrefix The input used to derive the identity hash.
* @param timestamp The future timestamp to be associated with the identity.
* @custom:requirements
* - The identity must not already be registered.
* - The provided timestamp must not be in the past.
*/
function register(
uint64 eon,
bytes32 identityPrefix,
uint64 timestamp
) external {
// Ensure the timestamp is not in the past.
require(timestamp >= block.timestamp, TimestampInThePast());

// Ensure identityPrefix passed in correct.
require(identityPrefix != bytes32(0), InvalidIdentityPrefix());

// Generate the identity hash from the provided prefix and the sender's address.
bytes32 identity = keccak256(
abi.encodePacked(identityPrefix, msg.sender)
);
RegistrationData storage registrationData = registrations[identity];
// Ensure the identity is not already registered.
require(registrationData.timestamp == 0, AlreadyRegistered());

// Store the registration timestamp.
registrationData.eon = eon;
registrationData.timestamp = timestamp;

// Emit the IdentityRegistered event.
emit IdentityRegistered(eon, identityPrefix, msg.sender, timestamp);
}
}
92 changes: 92 additions & 0 deletions test/ShutterRegistry.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import "forge-std/Test.sol";
import "../src/shutter-service/ShutterRegistry.sol";

contract ShutterRegistryTest is Test {
ShutterRegistry public shutterRegistry;

function setUp() public {
shutterRegistry = new ShutterRegistry();
}

function testIdentityRegistration() public {
uint64 eon = 5;
bytes32 identityPrefix = hex"001122";
uint64 timestamp = uint64(block.timestamp) + 100;
address sender = makeAddr("sender");

vm.expectEmit(address(shutterRegistry));
emit ShutterRegistry.IdentityRegistered(
eon,
identityPrefix,
sender,
timestamp
);

hoax(sender);
shutterRegistry.register(eon, identityPrefix, timestamp);

bytes32 identity = keccak256(abi.encodePacked(identityPrefix, sender));
(uint64 registeredEon, uint64 registeredTimestamp) = shutterRegistry
.registrations(identity);

//verifying registered timestamp
assertEqUint(registeredEon, eon);
assertEqUint(registeredTimestamp, timestamp);
}

function testDuplicateRegistration() public {
uint64 eon = 5;
bytes32 identityPrefix = hex"001122";
uint64 timestamp = uint64(block.timestamp) + 100;
address sender = makeAddr("sender");

vm.expectEmit(address(shutterRegistry));
emit ShutterRegistry.IdentityRegistered(
eon,
identityPrefix,
sender,
timestamp
);

hoax(sender);
shutterRegistry.register(eon, identityPrefix, timestamp);

uint64 newTimestamp = uint64(block.timestamp) + 200;
vm.expectRevert(ShutterRegistry.AlreadyRegistered.selector);
hoax(sender);
shutterRegistry.register(eon, identityPrefix, newTimestamp);

//verifying registered timestamp
bytes32 identity = keccak256(abi.encodePacked(identityPrefix, sender));
(, uint64 registeredTimestamp) = shutterRegistry.registrations(
identity
);
assertEqUint(registeredTimestamp, timestamp);
}

function testInvalidTimestamp() public {
uint64 eon = 5;
bytes32 identityPrefix = hex"001122";
uint64 timestamp = uint64(block.timestamp) - 1;
address sender = makeAddr("sender");

vm.expectRevert(ShutterRegistry.TimestampInThePast.selector);
hoax(sender);
shutterRegistry.register(eon, identityPrefix, timestamp);
}

function testMissingIdentity() public {
uint64 eon = 5;
// zero bytes for identity prefix should fail
bytes32 identityPrefix = hex"00";
uint64 timestamp = uint64(block.timestamp) + 100;
address sender = makeAddr("sender");

vm.expectRevert(ShutterRegistry.InvalidIdentityPrefix.selector);
hoax(sender);
shutterRegistry.register(eon, identityPrefix, timestamp);
}
}

0 comments on commit 3b74317

Please sign in to comment.