Skip to content

Commit

Permalink
add sequencer sig verifier tests
Browse files Browse the repository at this point in the history
  • Loading branch information
RnkSngh committed Jan 13, 2025
1 parent c9c9eb3 commit 88317da
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 0 deletions.
8 changes: 8 additions & 0 deletions contracts/libs/ReceiptParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ struct Ics23Proof {
uint256 height;
}

// This is the proof we use to verify the apphash (state) updates.
struct OpL2StateProof {
bytes[] accountProof;
bytes[] outputRootProof;
bytes32 l2OutputProposalKey;
bytes32 l2BlockHash;
}

/**
* A library for helpers for proving peptide state
*/
Expand Down
6 changes: 6 additions & 0 deletions test/PolymerProofLib.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";

contract PolymerProofLibTest is Test {}
52 changes: 52 additions & 0 deletions test/SequencerVerifier.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "./utils/Signing.base.t.sol";

contract SequencerProofVerifierStateUpdate is SigningBase {
function test_verify_signature_state_update_sucess() public view {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign);

bytes memory signature = abi.encodePacked(r, s, v); // Annoyingly, v, r, s are in a different order than those
// returned from vm.sign

// This call should not revert as long as it's a valid sequencer signature
sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signature);
}

// Call should revert if invalid signature values are passed
function test_verify_state_update_invalid_signature() public {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign);

bytes memory signatureTooShort = abi.encodePacked(r, s);
bytes memory signatureTooLong = abi.encodePacked(r, s, v, uint256(123));

// This call should not revert as long as it's a valid sequencer signature
vm.expectRevert("ECDSA: invalid signature length");
sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signatureTooShort);
vm.expectRevert("ECDSA: invalid signature length");
sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, signatureTooLong);
}

// Valid signature but from the wrong signer should revert
function test_verify_state_update_not_signer() public {
(, uint256 notSequencerPkey) = makeAddrAndKey(unicode"😳");
(uint8 v, bytes32 r, bytes32 s) = vm.sign(notSequencerPkey, hashToSign);

bytes memory wrongSignerSignature = abi.encodePacked(r, s, v);

vm.expectRevert(abi.encodeWithSelector(ISignatureVerifier.InvalidSequencerSignature.selector));
sigVerifier.verifyStateUpdate(peptideBlockNumber, peptideAppHash, l1BlockHash, wrongSignerSignature);
}

// Valid signature but on the wrong header should revert
function test_verify_state_update_incorrect_header() public {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerPkey, hashToSign);

bytes memory signature = abi.encodePacked(r, s, v);

vm.expectRevert(abi.encodeWithSelector(ISignatureVerifier.InvalidSequencerSignature.selector));
sigVerifier.verifyStateUpdate(peptideBlockNumber + 1, peptideAppHash, l1BlockHash, signature);
}
}
1 change: 1 addition & 0 deletions test/payload/l1_block_0x4df537.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000604d00f3add4dfa340f263a29c611ca08de042d195efd8fab8ec18a102d5c6f63c00000000000000000000000000000000000000000000000000000000004df53700000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000068000000000000000000000000000000000000000000000000000000000000006c00000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000007a000000000000000000000000000000000000000000000000000000000000007e00000000000000000000000000000000000000000000000000000000000000021a07b7f1cd3400204ab518b143360ac525502f4bb581d427429f196fe1918ac6024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015946a7aa9b882d50bb7bc5da1a244719c99f12f06a300000000000000000000000000000000000000000000000000000000000000000000000000000000000021a04d00f3add4dfa340f263a29c611ca08de042d195efd8fab8ec18a102d5c6f63c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0055b2c032685cb2a61d961c90c07959746fed2d28645ffa111a4ff89ad43ccd7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0a04f47b60169ec4e651dd932ffe8d66b33de8c8548af12657eefda8895217593000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103b901000d70308f1a5cf2a1a33174d28ca50572c6f8000c4294a84d0ad019e92109402ac20a588289bf2aabd8298a2ac42486b3500d0e12318b57484511d09a12365a4d3209ad824fe56a70e1102a8b886da09486115c8409a9023583000a26406060203ff681dc075dc25d84a040774c32a995da8c2ec045248421aa9a8c1e308b0f148230ca093578200027fb11318ad072101be1b44b184780e579a7580f264018510e8ca70a1af0104b426901d26850b059d49230104a1a8c85604027a0256200b20381bb23122cd2504309e809691612d90475162002740a2640a6aa331207212918f809b700f112c12b8a3650a3a4211ea66964300a410301e70b1e8502d27a9d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004834df5370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058401c9c380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000584013577f400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058465a94dd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a99d883010d08846765746888676f312e32302e31856c696e75780000000000000000000000000000000000000000000000000000000000000000000000000021a021c3d44a811c2c122d61ca3aef8847c6d6f6defe5f02ff74a96b41ee38c8497500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000988000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006850cbade162400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a0ab226847f9728d2c1b0fd37a4431c938a65a196304dddc4e84615862767aa2ca00000000000000000000000000000000000000000000000000000000000000
21 changes: 21 additions & 0 deletions test/payload/l2_block_0x4b0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"baseFeePerGas": "0x0",
"difficulty": "0x0",
"extraData": "",
"gasLimit": "0x1c9c380",
"gasUsed": "0x0",
"hash": "0xf51d6bb7b2578b14d98849963eab7433110e4ad7fdac1636d03bcc9f9a852721",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"mixHash": "0x13368de1a039f35df18b6363569284e944460f3e408fabe7ce998c2c5d8f46d0",
"number": "0x4b0",
"parentHash": "0xb1c7530229b8155c745feaa0da2d9018f3c873fd8e4a8f226a08b8b1c8bc894e",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"stateRoot": "0x6cc4ecfce2112e8b6100e023edac48d81f554c1d3869bac8140b0609959586af",
"timestamp": "0x657ba6ba",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
}
}
69 changes: 69 additions & 0 deletions test/utils/Signing.base.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../../contracts/core/SequencerSignatureVerifier.sol";
import {RLPWriter} from "optimism/libraries/rlp/RLPWriter.sol";
import {L1Header} from "../../contracts/interfaces/IAppStateVerifier.sol";

contract SigningBase is Test {
using stdJson for string;

string rootDir = vm.projectRoot();

bytes32 l1BlockHash;
bytes32 peptideAppHash;
uint256 public peptideBlockNumber;
uint256 public sequencerPkey;
uint256 public notSequencerPkey;
address public sequencer;
bytes32 hashToSign;
bytes32 domain; // Domain will be empty so we can leave it as initialized to default 0x 32 bytes

SequencerSignatureVerifier public sigVerifier;
bytes32 PEPTIDE_CHAIN_ID = bytes32(uint256(444));

L1Header childl1Block; // Child block, represents the l1 origin of dest chain when peptide catches up to ancestor L1
// block
L1Header ancestorL1Block; // Ancestor block, represents the l1 origin of dest chain when peptide wants to submit a

// client update but is behind, so we need to checkpoint this block

constructor() {
(sequencer, sequencerPkey) = makeAddrAndKey("alice");
(, notSequencerPkey) = makeAddrAndKey(unicode"bob😈");
sigVerifier = new SequencerSignatureVerifier(sequencer, PEPTIDE_CHAIN_ID);

// generate the channel_proof.hex file with the following command:
// cd test-data-generator && go run ./cmd/ --type l1 > ../test/payload/l1_block_0x4df537.hex
// this is the "rlp" half-encoded header that would be sent by the relayer. this was produced
// by the test-data-generator tool.
L1Header memory l1header = abi.decode(
vm.parseBytes(vm.readFile(string.concat(rootDir, "/test/payload/l1_block_0x4df537.hex"))), (L1Header)
);

l1BlockHash = keccak256(RLPWriter.writeList(l1header.header)); // Blockhash that will be signed by sequencer

peptideBlockNumber = 101;

// this happens to be the polymer height when the L2OO was updated with the output proposal
// we are using in the test
string memory l2BlockJson = vm.readFile(string.concat(rootDir, "/test/payload/l2_block_0x4b0.json"));
peptideAppHash = abi.decode(l2BlockJson.parseRaw(".result.stateRoot"), (bytes32));

bytes32 payloadHash = keccak256(abi.encodePacked(peptideBlockNumber, peptideAppHash, l1BlockHash));
hashToSign = keccak256(bytes.concat(domain, PEPTIDE_CHAIN_ID, payloadHash));
}

// Read a specific bytes32 json property from a json file
function readBytes32FromJson(string memory fileName, string memory property) public view returns (bytes32) {
string memory blockJson = vm.readFile(string.concat(rootDir, fileName));
return abi.decode(blockJson.parseRaw(property), (bytes32));
}

// Read a specific uint64 json property from a json file
function readUint64FromJson(string memory fileName, string memory property) public view returns (uint64) {
string memory blockJson = vm.readFile(string.concat(rootDir, fileName));
return abi.decode(blockJson.parseRaw(property), (uint64));
}
}

0 comments on commit 88317da

Please sign in to comment.