Skip to content

Commit

Permalink
Merge pull request #11 from unruggable-labs/debug-verifier
Browse files Browse the repository at this point in the history
Add TrustedVerifier, DoubleNitroVerifier, better npm package
  • Loading branch information
clowestab authored Oct 31, 2024
2 parents dc46be3 + 0b9e39a commit 07c64ca
Show file tree
Hide file tree
Showing 59 changed files with 1,253 additions and 410 deletions.
9 changes: 8 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
"*/dist/**/*"
],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
]
}
}
Binary file modified bun.lockb
Binary file not shown.
7 changes: 3 additions & 4 deletions contracts/GatewayVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ library GatewayVM {
ret := 1 // assume zero
for {

} lt(p, e) { // while (p < e)

} lt(p, e) {
// while (p < e)
} {
x := mload(p) // remember last
p := add(p, 32) // step by word
Expand Down Expand Up @@ -486,10 +486,9 @@ library GatewayVM {
} else if (op == GatewayOP.EVAL_LOOP) {
uint8 flags = vm.readByte();
uint256 count = vm.popAsUint256();
Machine memory vm2;
Machine memory vm2 = createMachine();
vm2.buf = vm.popAsBytes();
vm2.proofs = vm.proofs;
vm2.stack = new uint256[](MAX_STACK);
if (count > vm.stackSize) count = vm.stackSize;
while (count > 0) {
--count;
Expand Down
13 changes: 13 additions & 0 deletions contracts/ReadBytesAt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ReadBytesAt {
// NOTE: pure seems like the wrong mutability annotation
function readBytesAt(uint256 slot) external pure returns (bytes memory) {
bytes storage v;
assembly {
v.slot := slot
}
return v;
}
}
2 changes: 1 addition & 1 deletion contracts/SelfVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.23;

import {AbstractVerifier, IVerifierHooks} from './AbstractVerifier.sol';
import {GatewayRequest, GatewayVM, ProofSequence, IVerifierHooks} from './GatewayVM.sol';
import {GatewayRequest, GatewayVM, ProofSequence} from './GatewayVM.sol';
import {RLPReader, RLPReaderExt} from './RLPReaderExt.sol';

contract SelfVerifier is AbstractVerifier {
Expand Down
120 changes: 120 additions & 0 deletions contracts/TrustedVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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 {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

contract TrustedVerifier is IGatewayVerifier {
event GatewayChanged(address indexed fetcher);

struct Config {
mapping(address signer => bool) signers;
string[] urls;
uint256 expSec;
IVerifierHooks hooks;
}

mapping(address fetcher => Config) _configs;

modifier _canModifyFetcher(address op, address fetcher) {
if (fetcher == op) {
require(fetcher.code.length != 0, 'Trusted: not code');
} else {
try Ownable(fetcher).owner() returns (address a) {
require(a == op, 'Trusted: not owner');
} catch {
revert('Trusted: not Ownable');
}
}
_;
}

function setConfig(
address fetcher,
string[] memory urls,
uint256 expSec,
IVerifierHooks hooks
) external _canModifyFetcher(msg.sender, fetcher) {
Config storage c = _configs[fetcher];
c.urls = urls;
c.expSec = expSec;
c.hooks = hooks;
emit GatewayChanged(fetcher);
}

function setSigner(
address fetcher,
address signer,
bool allow
) external _canModifyFetcher(msg.sender, fetcher) {
_configs[fetcher].signers[signer] = allow;
emit GatewayChanged(fetcher);
}

function getConfig(
address sender
)
external
view
returns (string[] memory urls, uint256 expSec, IVerifierHooks hooks)
{
Config storage c = _configs[sender];
urls = c.urls;
expSec = c.expSec;
hooks = c.hooks;
}

function isSigner(
address sender,
address signer
) external view returns (bool) {
return _configs[sender].signers[signer];
}

function gatewayURLs() external view returns (string[] memory) {
return _configs[msg.sender].urls;
}

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

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, address sender) = abi.decode(context, (uint256, address));
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),
p.signedAt,
p.stateRoot
)
);
address signer = ECDSA.recover(hash, p.signature);
Config storage c = _configs[sender];
require(c.signers[signer], 'Trusted: signer');
uint256 dt = p.signedAt > t ? p.signedAt - t : t - p.signedAt;
require(dt <= c.expSec, 'Trusted: expired');
return
GatewayVM.evalRequest(
req,
ProofSequence(0, p.stateRoot, p.proofs, p.order, c.hooks)
);
}
}
69 changes: 69 additions & 0 deletions contracts/nitro/DoubleNitroVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {NitroVerifier} from './NitroVerifier.sol';
import {IVerifierHooks} from '../IVerifierHooks.sol';
import {GatewayRequest, GatewayVM, ProofSequence} from '../GatewayVM.sol';
import {RLPReader, RLPReaderExt} from '../RLPReaderExt.sol';
import {IRollupCore, Node} from './IRollupCore.sol';

contract DoubleNitroVerifier is NitroVerifier {
address immutable _rollup2;
GatewayRequest _nodeRequest;

constructor(
string[] memory urls,
uint256 window,
IVerifierHooks hooks,
IRollupCore rollup,
uint256 minBlocks,
address rollup2,
GatewayRequest memory nodeRequest
) NitroVerifier(urls, window, hooks, rollup, minBlocks) {
_rollup2 = rollup2;
_nodeRequest = nodeRequest;
}

struct GatewayProof2 {
uint64 nodeNum;
bytes32 sendRoot;
bytes rlpEncodedBlock;
bytes[] proofs;
bytes order;
bytes32 sendRoot2;
bytes rlpEncodedBlock2;
bytes[] proofs2;
bytes order2;
}

function getStorageValues(
bytes memory context,
GatewayRequest memory req,
bytes memory proof
) external view override returns (bytes[] memory, uint8 exitCode) {
GatewayProof2 memory p = abi.decode(proof, (GatewayProof2));
Node memory node = _verifyNode(context, p.nodeNum);
bytes32 stateRoot = _verifyStateRoot(
node.confirmData,
p.rlpEncodedBlock,
p.sendRoot
);
(bytes[] memory outputs, ) = GatewayVM.evalRequest(
_nodeRequest,
ProofSequence(0, stateRoot, p.proofs, p.order, _hooks)
);
// outputs[0] = node
// outputs[1] = confirmData
// outputs[2] = createdAtBlock (not used yet)
bytes32 stateRoot2 = _verifyStateRoot(
bytes32(outputs[1]), // confirmData
p.rlpEncodedBlock2,
p.sendRoot2
);
return
GatewayVM.evalRequest(
req,
ProofSequence(0, stateRoot2, p.proofs2, p.order2, _hooks)
);
}
}
76 changes: 55 additions & 21 deletions contracts/nitro/NitroVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,34 @@ import {IRollupCore, Node} from './IRollupCore.sol';

contract NitroVerifier is AbstractVerifier {
IRollupCore immutable _rollup;
uint256 immutable _minBlocks;

constructor(
string[] memory urls,
uint256 window,
IVerifierHooks hooks,
IRollupCore rollup
IRollupCore rollup,
uint256 minBlocks
) AbstractVerifier(urls, window, hooks) {
_rollup = rollup;
_minBlocks = minBlocks;
}

function getLatestContext() external view returns (bytes memory) {
return abi.encode(_rollup.latestConfirmed());
if (_minBlocks == 0) {
return abi.encode(_rollup.latestConfirmed());
}
uint64 i = _rollup.latestNodeCreated();
uint256 b = block.number - _minBlocks;
while (true) {
Node memory node = _rollup.getNode(i);
if (node.createdAtBlock <= b) {
return abi.encode(i);
}
if (i == 0) break;
--i;
}
revert('Nitro: no node');
}

struct GatewayProof {
Expand All @@ -35,31 +51,49 @@ contract NitroVerifier is AbstractVerifier {
bytes memory context,
GatewayRequest memory req,
bytes memory proof
) external view returns (bytes[] memory, uint8 exitCode) {
uint64 nodeNum1 = abi.decode(context, (uint64));
) external view virtual returns (bytes[] memory, uint8 exitCode) {
GatewayProof memory p = abi.decode(proof, (GatewayProof));
Node memory node = _rollup.getNode(p.nodeNum);
if (p.nodeNum != nodeNum1) {
// it wasn't what we requested
Node memory node1 = _rollup.getNode(nodeNum1);
// check if node is between latest and our window
_checkWindow(node1.createdAtBlock, node.createdAtBlock);
// check if node is member of confirmed chain
while (node1.prevNum > p.nodeNum) {
node1 = _rollup.getNode(node1.prevNum);
}
require(node1.prevNum == p.nodeNum, 'Nitro: not confirmed');
}
bytes32 confirmData = keccak256(
abi.encodePacked(keccak256(p.rlpEncodedBlock), p.sendRoot)
Node memory node = _verifyNode(context, p.nodeNum);
bytes32 stateRoot = _verifyStateRoot(
node.confirmData,
p.rlpEncodedBlock,
p.sendRoot
);
require(confirmData == node.confirmData, 'Nitro: confirmData');
RLPReader.RLPItem[] memory v = RLPReader.readList(p.rlpEncodedBlock);
bytes32 stateRoot = RLPReaderExt.strictBytes32FromRLP(v[3]); // see: rlp.ts: encodeRlpBlock()
return
GatewayVM.evalRequest(
req,
ProofSequence(0, stateRoot, p.proofs, p.order, _hooks)
);
}

function _verifyNode(
bytes memory context,
uint64 nodeNum
) internal view returns (Node memory node) {
uint64 nodeNum1 = abi.decode(context, (uint64));
node = _rollup.getNode(nodeNum);
if (nodeNum != nodeNum1) {
Node memory node1 = _rollup.getNode(nodeNum1);
_checkWindow(node1.createdAtBlock, node.createdAtBlock);
if (_minBlocks == 0) {
while (node1.prevNum > nodeNum) {
node1 = _rollup.getNode(node1.prevNum);
}
require(node1.prevNum == nodeNum, 'Nitro: not finalized');
}
}
}

function _verifyStateRoot(
bytes32 confirmData,
bytes memory rlpEncodedBlock,
bytes32 sendRoot
) internal pure returns (bytes32) {
bytes32 computed = keccak256(
abi.encodePacked(keccak256(rlpEncodedBlock), sendRoot)
);
require(computed == confirmData, 'Nitro: confirmData');
RLPReader.RLPItem[] memory v = RLPReader.readList(rlpEncodedBlock);
return RLPReaderExt.strictBytes32FromRLP(v[3]);
}
}
Loading

0 comments on commit 07c64ca

Please sign in to comment.