Commit 10c981b0 authored by clabby's avatar clabby Committed by GitHub

feat(ctb): Keccak dispute DA gas estimation (#8986)

* init keccak proposals

* merklelization

* merk fixes

* Preimage parts, challenge tests

* Tests

* bindings + snapshots

* lint

* tests

* bind

* :broom:, checks, tests

* Contiguous state tests

* :broom:, r4r

* review

* rabbit

* @refcell review

* bindings

* bindings

* @tynes review

* @inphi review

* Keccak dispute DA gas estimation

* worst-case, all bits set

* bindings

* bindings

* Add challenger indexing

* slither

* Add append-only array of proposal keys

* rebase

* rebase
parent 81955146
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { Script } from "forge-std/Script.sol";
import { StdAssertions } from "forge-std/StdAssertions.sol";
import "src/cannon/libraries/CannonTypes.sol";
contract SubmitLPP is Script, StdAssertions {
/// @notice Test UUID
uint256 private constant TEST_UUID = uint256(keccak256("TEST_UUID"));
/// @notice Number of bytes to submit to the preimage oracle.
uint256 private constant BYTES_TO_SUBMIT = 4_012_000;
/// @notice Chunk size to submit to the preimage oracle.
uint256 private constant CHUNK_SIZE = 500 * 136;
PreimageOracle private oracle;
function post(address _po) external {
// Bootstrap
oracle = PreimageOracle(_po);
// Allocate chunk - worst case w/ all bits set.
bytes memory chunk = new bytes(CHUNK_SIZE);
for (uint256 i; i < chunk.length; i++) {
chunk[i] = 0xFF;
}
// Mock state commitments. Worst case w/ all bits set.
bytes32[] memory mockStateCommitments = new bytes32[](CHUNK_SIZE / 136);
bytes32[] memory mockStateCommitmentsLast = new bytes32[](CHUNK_SIZE / 136 + 1);
for (uint256 i; i < mockStateCommitments.length; i++) {
mockStateCommitments[i] = bytes32(type(uint256).max);
mockStateCommitmentsLast[i] = bytes32(type(uint256).max);
}
// Assign last mock state commitment to all bits set.
mockStateCommitmentsLast[mockStateCommitmentsLast.length - 1] = bytes32(type(uint256).max);
vm.broadcast();
oracle.initLPP({ _uuid: TEST_UUID, _partOffset: 0, _claimedSize: uint32(BYTES_TO_SUBMIT) });
// Submit LPP in 500 * 136 byte chunks.
for (uint256 i = 0; i < BYTES_TO_SUBMIT; i += CHUNK_SIZE) {
bool finalize = i + CHUNK_SIZE >= BYTES_TO_SUBMIT;
vm.broadcast();
oracle.addLeavesLPP({
_uuid: TEST_UUID,
_input: chunk,
_stateCommitments: finalize ? mockStateCommitmentsLast : mockStateCommitments,
_finalize: finalize
});
}
// Assert that all bytes were submitted.
LPPMetaData metaData = oracle.proposalMetadata(msg.sender, TEST_UUID);
assertEq(metaData.bytesProcessed(), BYTES_TO_SUBMIT);
assertEq(metaData.blocksProcessed(), (BYTES_TO_SUBMIT / 136) + 1);
}
}
#!/bin/bash
# Default Anvil private key
PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
PUB_KEY="$(cast wallet addr $PRIVATE_KEY)"
# 40 gwei base fee
BASE_FEE=40000000000
# Anvil RPC
export ETH_RPC_URL="http://localhost:8545"
# Start anvil in the background
anvil --base-fee $BASE_FEE &
# Capture the process ID
ANVIL_PID=$!
# Deploy the `PreimageOracle` contract to anvil.
PO_ADDR=$(forge create PreimageOracle --private-key $PRIVATE_KEY --rpc-url $ETH_RPC_URL --json | jq -r '.deployedTo')
# Capture the balance of the submitter prior to submitting all leaves.
BALANCE_BEFORE=$(cast balance --rpc-url http://localhost:8545 "$PUB_KEY")
BASE_FEE_BEFORE=$(cast 2d "$(cast rpc 'eth_gasPrice' | jq -r)")
# Run the `SubmitLPP` script to submit the LPP to the `PreimageOracle` contract.
forge script scripts/fpac/SubmitLPP.sol \
--sig "post(address)" "$PO_ADDR" \
--private-key $PRIVATE_KEY \
--rpc-url $ETH_RPC_URL \
--broadcast
BALANCE_AFTER=$(cast balance "$PUB_KEY")
BASE_FEE_AFTER=$(cast 2d "$(cast rpc 'eth_gasPrice' | jq -r)")
echo "Base Fee Before: $BASE_FEE_BEFORE"
echo "Base Fee After: $BASE_FEE_AFTER"
echo "Balance before: $BALANCE_BEFORE"
echo "Balance after: $BALANCE_AFTER"
echo "Cost: $(cast from-wei $((BALANCE_BEFORE - BALANCE_AFTER))) ETH"
# Kill anvil
kill $ANVIL_PID
......@@ -1096,10 +1096,10 @@
"impact": "Medium",
"confidence": "Medium",
"check": "uninitialized-local",
"description": "PreimageOracle.challengeFirstLPP(address,uint256,PreimageOracle.Leaf,bytes32[]).stateMatrix (src/cannon/PreimageOracle.sol#357) is a local variable never initialized\n",
"description": "PreimageOracle.challengeFirstLPP(address,uint256,PreimageOracle.Leaf,bytes32[]).stateMatrix (src/cannon/PreimageOracle.sol#383) is a local variable never initialized\n",
"type": "variable",
"name": "stateMatrix",
"start": 16305,
"start": 17601,
"length": 40,
"filename_relative": "src/cannon/PreimageOracle.sol"
},
......
......@@ -371,6 +371,59 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposalBlocks",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "_uuid",
"type": "uint256"
}
],
"name": "proposalBlocksLen",
"outputs": [
{
"internalType": "uint256",
"name": "len_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
......@@ -400,6 +453,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "proposalCount",
"outputs": [
{
"internalType": "uint256",
"name": "count_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
......@@ -448,6 +514,30 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposals",
"outputs": [
{
"internalType": "address",
"name": "claimant",
"type": "address"
},
{
"internalType": "uint256",
"name": "uuid",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
......
......@@ -29,23 +29,37 @@
},
{
"bytes": "32",
"label": "proposalBranches",
"label": "proposals",
"offset": 0,
"slot": "19",
"type": "struct PreimageOracle.LargePreimageProposalKeys[]"
},
{
"bytes": "32",
"label": "proposalBranches",
"offset": 0,
"slot": "20",
"type": "mapping(address => mapping(uint256 => bytes32[16]))"
},
{
"bytes": "32",
"label": "proposalMetadata",
"offset": 0,
"slot": "20",
"slot": "21",
"type": "mapping(address => mapping(uint256 => LPPMetaData))"
},
{
"bytes": "32",
"label": "proposalParts",
"offset": 0,
"slot": "21",
"slot": "22",
"type": "mapping(address => mapping(uint256 => bytes32))"
},
{
"bytes": "32",
"label": "proposalBlocks",
"offset": 0,
"slot": "23",
"type": "mapping(address => mapping(uint256 => uint64[]))"
}
]
\ No newline at end of file
......@@ -48,9 +48,19 @@ contract PreimageOracle is IPreimageOracle {
bytes32 stateCommitment;
}
/// @notice Unpacked keys for large preimage proposals.
struct LargePreimageProposalKeys {
/// @notice The claimant of the large preimage proposal.
address claimant;
/// @notice The UUID of the large preimage proposal.
uint256 uuid;
}
/// @notice Static padding hashes. These values are persisted in storage, but are entirely immutable
/// after the constructor's execution.
bytes32[KECCAK_TREE_DEPTH] public zeroHashes;
/// @notice Append-only array of large preimage proposals for off-chain reference.
LargePreimageProposalKeys[] public proposals;
/// @notice Mapping of claimants to proposal UUIDs to the current branch path of the merkleization process.
mapping(address => mapping(uint256 => bytes32[KECCAK_TREE_DEPTH])) public proposalBranches;
/// @notice Mapping of claimants to proposal UUIDs to the timestamp of creation of the proposal as well as the
......@@ -58,6 +68,8 @@ contract PreimageOracle is IPreimageOracle {
mapping(address => mapping(uint256 => LPPMetaData)) public proposalMetadata;
/// @notice Mapping of claimants to proposal UUIDs to the preimage part picked up during the absorbtion process.
mapping(address => mapping(uint256 => bytes32)) public proposalParts;
/// @notice Mapping of claimants to proposal UUIDs to blocks which leaves were added to the merkle tree.
mapping(address => mapping(uint256 => uint64[])) public proposalBlocks;
////////////////////////////////////////////////////////////////
// Constructor //
......@@ -170,12 +182,24 @@ contract PreimageOracle is IPreimageOracle {
// Large Preimage Proposals (External) //
////////////////////////////////////////////////////////////////
/// @notice Returns the length of the `proposals` array
function proposalCount() external view returns (uint256 count_) {
count_ = proposals.length;
}
/// @notice Initialize a large preimage proposal. Must be called before adding any leaves.
function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) external {
if (_partOffset >= _claimedSize + 8) revert PartOffsetOOB();
LPPMetaData metaData = proposalMetadata[msg.sender][_uuid];
proposalMetadata[msg.sender][_uuid] = metaData.setPartOffset(_partOffset).setClaimedSize(_claimedSize);
proposals.push(LargePreimageProposalKeys(msg.sender, _uuid));
}
/// @notice Returns the length of the array with the block numbers of `addLeavesLPP` calls for a given large
/// preimage proposal.
function proposalBlocksLen(address _claimant, uint256 _uuid) external view returns (uint256 len_) {
len_ = proposalBlocks[_claimant][_uuid].length;
}
/// @notice Adds a contiguous list of keccak state matrices to the merkle tree.
......@@ -291,6 +315,8 @@ contract PreimageOracle is IPreimageOracle {
// Perist the branch to storage.
proposalBranches[msg.sender][_uuid] = branch;
// Track the block number that these leaves were added at.
proposalBlocks[msg.sender][_uuid].push(uint64(block.number));
// Update the proposal metadata.
metaData =
......
......@@ -227,6 +227,11 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
// Initialize the proposal.
oracle.initLPP(TEST_UUID, 0, uint32(data.length));
// Ensure that the proposal keys are present in the array.
(address claimant, uint256 uuid) = oracle.proposals(0);
assertEq(oracle.proposalCount(), 1);
assertEq(claimant, address(this));
assertEq(uuid, TEST_UUID);
// Add the leaves to the tree (2 keccak blocks.)
LibKeccak.StateMatrix memory stateMatrix;
......@@ -251,6 +256,9 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
assertEq(metaData.bytesProcessed(), 136 * 2);
assertFalse(metaData.countered());
// Move ahead one block.
vm.roll(block.number + 1);
oracle.addLeavesLPP(TEST_UUID, Bytes.slice(data, 136 * 2, 136), commitmentsB, true);
// MetaData assertions
......@@ -266,6 +274,9 @@ contract PreimageOracle_LargePreimageProposals_Test is Test {
bytes32 expectedPart = bytes32((~uint256(0) & ~(uint256(type(uint64).max) << 192)) | (data.length << 192));
assertEq(oracle.proposalParts(address(this), TEST_UUID), expectedPart);
assertEq(oracle.proposalBlocks(address(this), TEST_UUID, 0), block.number - 1);
assertEq(oracle.proposalBlocks(address(this), TEST_UUID, 1), block.number);
// Should revert if we try to add new leaves.
vm.expectRevert(AlreadyFinalized.selector);
oracle.addLeavesLPP(TEST_UUID, data, stateCommitments, true);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment