Skip to content

Commit

Permalink
Merge pull request #22 from unruggable-labs/trusted-clones
Browse files Browse the repository at this point in the history
Redesign TrustedVerifier using Clones
  • Loading branch information
clowestab authored Feb 19, 2025
2 parents 000980d + d061e45 commit 4e610e7
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 170 deletions.
Binary file modified bun.lockb
Binary file not shown.
120 changes: 0 additions & 120 deletions contracts/TrustedVerifier.sol

This file was deleted.

93 changes: 93 additions & 0 deletions contracts/trusted/LazyOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

// works like OpenZeppelin Ownable except
// it uses initialize() instead of constructor()

import {Ownable, Context} from '@openzeppelin/contracts/access/Ownable.sol';

error LazyOwnableAlreadyInitialized();

abstract contract LazyOwnable is Context {
address private _owner;
bool private _disowned;

// constructor() {
// _disowned = true;
// }

function initialized() public view returns (bool) {
return _disowned || _owner != address(0);
}

function initialize(address initialOwner) internal virtual {
if (initialized()) {
revert LazyOwnableAlreadyInitialized();
}
if (initialOwner == address(0)) {
//revert Ownable.OwnableInvalidOwner(address(0));
// the standard behavior is stupid
// NOTE: this does not emit OwnershipTransferred()
_disowned = true;
} else {
_transferOwnership(initialOwner);
}
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}

/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}

/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (_disowned || owner() != _msgSender()) {
revert Ownable.OwnableUnauthorizedAccount(_msgSender());
}
}

/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_disowned = true;
_transferOwnership(address(0));
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert Ownable.OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}

/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit Ownable.OwnershipTransferred(oldOwner, newOwner);
}
}
119 changes: 119 additions & 0 deletions contracts/trusted/LazyTrustedVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IGatewayVerifier} from '../IGatewayVerifier.sol';
import {IVerifierHooks} from '../IVerifierHooks.sol';
import {GatewayRequest, GatewayVM, ProofSequence} from '../GatewayVM.sol';
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
import {LazyOwnable} from './LazyOwnable.sol';

event TrustedVerifierChanged();

contract LazyTrustedVerifier is LazyOwnable, IGatewayVerifier {

IVerifierHooks _hooks;
mapping(address => bool) _signers;
uint256 _expSec;
string[] _urls;

function init(
address _owner,
IVerifierHooks hooks,
string[] memory urls,
address[] memory signers,
uint256 expSec
) external {
initialize(_owner, hooks, urls, signers, expSec);
}

function initialize(
address _owner,
IVerifierHooks hooks,
string[] memory urls,
address[] memory signers,
uint256 expSec
) internal {
super.initialize(_owner);
_hooks = hooks;
_urls = urls;
for (uint256 i; i < signers.length; i++) {
_signers[signers[i]] = true;
}
_expSec = expSec;
}

function getExpSec() external view returns (uint256) {
return _expSec;
}

function getHooks() external view returns (IVerifierHooks) {
return _hooks;
}

function isSigner(address signer) external view returns (bool) {
return _signers[signer];
}

function setGatewayURLs(string[] memory urls) external onlyOwner {
_urls = urls;
emit TrustedVerifierChanged();
}

function setHooks(IVerifierHooks hooks) external onlyOwner {
_hooks = hooks;
emit TrustedVerifierChanged();
}

function setExpSec(uint256 expSec) external onlyOwner {
_expSec = expSec;
emit TrustedVerifierChanged();
}

function setSigner(address signer, bool allow) external onlyOwner {
_signers[signer] = allow;
emit TrustedVerifierChanged();
}

function gatewayURLs() external view returns (string[] memory) {
return _urls;
}

function getLatestContext() external view returns (bytes memory) {
return abi.encode(block.timestamp);
}

struct GatewayProof {
bytes signature;
uint64 signedAt;
bytes32 stateRoot;
bytes[] proofs;
bytes order;
}

function getStorageValues(
bytes memory context,
GatewayRequest memory req,
bytes memory proof
) external view returns (bytes[] memory, uint8 exitCode) {
uint256 t = abi.decode(context, (uint256));
GatewayProof memory p = abi.decode(proof, (GatewayProof));
bytes32 hash = keccak256(
// https://github.com/ethereum/eips/issues/191
abi.encodePacked(
hex'1900', // magic + version(0)
address(0), // unbound
p.signedAt,
p.stateRoot
)
);
address signer = ECDSA.recover(hash, p.signature);
require(_signers[signer], 'Trusted: signer');
uint256 dt = p.signedAt > t ? p.signedAt - t : t - p.signedAt;
require(dt <= _expSec, 'Trusted: expired');
return
GatewayVM.evalRequest(
req,
ProofSequence(0, p.stateRoot, p.proofs, p.order, _hooks)
);
}
}
15 changes: 15 additions & 0 deletions contracts/trusted/TrustedVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {LazyTrustedVerifier, IVerifierHooks} from './LazyTrustedVerifier.sol';

contract TrustedVerifier is LazyTrustedVerifier {
constructor(
IVerifierHooks hooks,
string[] memory urls,
address[] memory signers,
uint256 expSec
) {
initialize(msg.sender, hooks, urls, signers, expSec);
}
}
27 changes: 27 additions & 0 deletions contracts/trusted/TrustedVerifierFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {LazyTrustedVerifier, IVerifierHooks} from './LazyTrustedVerifier.sol';
import {Clones} from '@openzeppelin/contracts/proxy/Clones.sol';

event NewTrustedVerifier(address verifier);

contract TrustedVerifierFactory {
LazyTrustedVerifier public immutable impl;

constructor() {
impl = new LazyTrustedVerifier();
}

function create(
address owner,
IVerifierHooks hooks,
string[] calldata urls,
address[] calldata signers,
uint256 expSec
) external returns (LazyTrustedVerifier verifier) {
verifier = impl.initialized() ? LazyTrustedVerifier(Clones.clone(address(impl))) : impl;
verifier.init(owner, hooks, urls, signers, expSec);
emit NewTrustedVerifier(address(verifier));
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"contracts/ReadBytesAt.sol"
],
"devDependencies": {
"@adraffy/blocksmith": "^0.1.47",
"@adraffy/blocksmith": "^0.1.51",
"@types/bun": "latest",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
Expand Down
Loading

0 comments on commit 4e610e7

Please sign in to comment.