Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
ae40939
Add CustomDA proof validation interface and reference implementation
Tristan-Wilson Jun 18, 2025
f20cebe
Merge remote-tracking branch 'origin/develop' into customda-bold
gzeoneth Jun 18, 2025
ea132b7
format: yarn format
gzeoneth Jun 18, 2025
aad9ebc
fix: 0x01
gzeoneth Jun 18, 2025
c63dc45
wip: use create2 for factory deployment
godzillaba Jun 24, 2025
0040293
remove key from config
godzillaba Jun 24, 2025
40b97dd
fmt
godzillaba Jun 24, 2025
22ad2fa
fix signatures
godzillaba Jun 24, 2025
9b38201
Merge branch 'develop' into deterministic-factory-deployments
godzillaba Jun 24, 2025
ac0ba75
Merge remote-tracking branch 'origin/develop' into customda-bold
gzeoneth Jun 25, 2025
2854f5d
refactor: make OneStepProverHostIo validator immutable
gzeoneth Jun 25, 2025
a49b1a2
test: storage and 4bytes
gzeoneth Jun 25, 2025
54f5ac3
test: simple CustomDAProof test
gzeoneth Jun 25, 2025
08fa02b
chore: restore file
gzeoneth Jun 25, 2025
094b86f
fix: triage slither
gzeoneth Jun 25, 2025
17be158
inline up exec deployment
godzillaba Jun 25, 2025
5865eb7
Merge branch 'develop' into customda-bold
gzeoneth Jun 25, 2025
631b941
chore: move file to foundry test folder
gzeoneth Jun 26, 2025
8aed0c3
salt length check
godzillaba Jun 30, 2025
7c0fd72
update tests
godzillaba Jun 30, 2025
4412691
Merge branch 'develop' into deterministic-factory-deployments
godzillaba Jun 30, 2025
1a12320
fmt
godzillaba Jun 30, 2025
64a519a
Merge branch 'deterministic-factory-deployments' of https://github.co…
godzillaba Jun 30, 2025
c4887b3
remove SetTemplatesArgs struct
godzillaba Jul 1, 2025
f4ba76f
remove ownership from bridge creator
godzillaba Jul 1, 2025
5953155
add CREATE2_FACTORY env and deployment instructions
godzillaba Jul 1, 2025
9429824
fix signatures
godzillaba Jul 1, 2025
f760491
factory owner as deployAllContracts arg
godzillaba Jul 1, 2025
bd06fb1
set deployer as owner in local deployment
godzillaba Jul 1, 2025
a2d4228
chore: disable metahash and align hardhat foundry (#363)
godzillaba Jul 3, 2025
1b86d38
fix: deploy create2 factory for local deployment
gzeoneth Jul 3, 2025
89baf3a
fix: wait for funding
gzeoneth Jul 3, 2025
d74b01b
ci: use geth-allow-pre155
gzeoneth Jul 3, 2025
9373a97
fix: _uint256ToAddress helper
gzeoneth Jul 3, 2025
dbeef90
test: fix ReferenceDAProofValidatorTest
gzeoneth Jul 7, 2025
442e95b
Merge branch 'deterministic-factory-deployments' into customda-bold
gzeoneth Jul 7, 2025
9323db7
fix: 4bytes and storage
gzeoneth Jul 7, 2025
e508d7f
wip: make rollup creator support customda feature (#361)
gzeoneth Jul 7, 2025
98fd84a
Check certKeccak against proven hash in memory
Tristan-Wilson Jul 11, 2025
221ddc5
Move cert into standardized part of OSP validation
Tristan-Wilson Jul 11, 2025
656325c
Simplify CustomDA proof format and fix stack depth error
Tristan-Wilson Jul 11, 2025
1e23170
format: yarn format
gzeoneth Jul 11, 2025
9afeb45
test: fix
gzeoneth Jul 11, 2025
b36526e
test: refactor
gzeoneth Jul 11, 2025
7d0ab0b
test: add more coverage
gzeoneth Jul 11, 2025
e18761e
Add ValidatePreimage inst for CustomDA cert val OSP
Tristan-Wilson Jul 15, 2025
9204f87
feat: add trusted signer validation to ReferenceDAProofValidator
Tristan-Wilson Jul 21, 2025
555e3a2
Fix hostio range
Tristan-Wilson Aug 25, 2025
7274410
Merge remote-tracking branch 'origin/develop' into customda-bold
gzeoneth Sep 9, 2025
b85e20c
chore: disable forge lint_on_build
gzeoneth Sep 9, 2025
44b2eb4
refactor: avoid assembly
gzeoneth Sep 22, 2025
1dc6253
chore: check nonzero customDAValidator
gzeoneth Sep 22, 2025
8002d8a
refactor: isValid
gzeoneth Sep 22, 2025
2ba9919
refcator: avoid more assembly
gzeoneth Sep 22, 2025
50e5127
Rename VALIDATE_PREIMAGE opcode to VALIDATE_CERTIFICATE
Tristan-Wilson Sep 30, 2025
d8474b7
Replace hardcoded proof offset values with constants
Tristan-Wilson Sep 30, 2025
cba26f2
Return up to 32 bytes from validateReadPreimage
Tristan-Wilson Sep 30, 2025
8a9a3d8
Move ReferenceDAProofValidator.sol out of contracts
Oct 6, 2025
1160b95
Fix: Read certificate size from correct offset in validateAndCheckCer…
Oct 9, 2025
b4bf70c
Merge branch 'develop' into customda-bold
gzeoneth Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/osp/ICustomDAProofValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2021-2024, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

/**
* @title ICustomDAProofValidator
* @notice Interface for custom data availability proof validators
*/
interface ICustomDAProofValidator {
/**
* @notice Validates a custom DA proof and returns the preimage chunk
* @param proof The complete proof data (format determined by implementation)
* @return preimageChunk The 32-byte chunk of preimage data
*/
function validateReadPreimage(
bytes calldata proof
) external view returns (bytes memory preimageChunk);
}
24 changes: 24 additions & 0 deletions src/osp/OneStepProverHostIo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "../state/MultiStack.sol";
import "../state/Deserialize.sol";
import "../state/ModuleMemory.sol";
import "./IOneStepProver.sol";
import "./ICustomDAProofValidator.sol";
import "../bridge/Messages.sol";
import "../bridge/IBridge.sol";

Expand All @@ -29,6 +30,15 @@ contract OneStepProverHostIo is IOneStepProver {
uint64 private constant INBOX_HEADER_LEN = 40;
uint64 private constant DELAYED_HEADER_LEN = 112 + 1;

ICustomDAProofValidator public customDAValidator;

function setCustomDAValidator(
ICustomDAProofValidator _validator
) external {
// TODO: Add appropriate access control
customDAValidator = _validator;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't keep any storage in the OSP contract, so instead I think we have 2 options

  1. store the var in the rollup and have the OSP read from the rollup
  2. make this immutable and set in the constructor, but we have to consider how it works with the rollup creator

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to make this immutable because this allow more deterministic behavior, otherwise the rollup owner may change the DA contract and make a assertion invalid.

function setLeafByte(bytes32 oldLeaf, uint256 idx, uint8 val) internal pure returns (bytes32) {
require(idx < LEAF_SIZE, "BAD_SET_LEAF_BYTE_IDX");
// Take into account that we are casting the leaf to a big-endian integer
Expand Down Expand Up @@ -220,6 +230,20 @@ contract OneStepProverHostIo is IOneStepProver {

extracted = kzgProof[64:96];
}
} else if (inst.argumentData == 3) {
// The machine is asking for a CustomDA preimage
require(proofType == 0, "UNKNOWN_PREIMAGE_PROOF");
require(address(customDAValidator) != address(0), "CUSTOM_DA_VALIDATOR_NOT_SET");

// The OSP is completely agnostic to CustomDA proof format
// Just forward all remaining proof bytes to the validator
bytes calldata customProof = proof[proofOffset:];

// Delegate entirely to the custom validator
extracted = customDAValidator.validateReadPreimage(customProof);

// Ensure we got a valid response
require(extracted.length > 0 && extracted.length <= 32, "INVALID_CUSTOM_DA_RESPONSE");
} else {
revert("UNKNOWN_PREIMAGE_TYPE");
}
Expand Down
66 changes: 66 additions & 0 deletions src/osp/ReferenceDAProofValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2021-2024, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

import "./ICustomDAProofValidator.sol";

/**
* @title ReferenceDAProofValidator
* @notice Reference implementation of a CustomDA proof validator
*/
contract ReferenceDAProofValidator is ICustomDAProofValidator {
/**
* @notice Validates a ReferenceDA proof and returns the preimage chunk
* @param proof ReferenceDA proof format: [hash(32), offset(8), Version(1), PreimageSize(8), PreimageData]
* @return preimageChunk The 32-byte chunk at the specified offset
*/
function validateReadPreimage(
bytes calldata proof
) external pure override returns (bytes memory preimageChunk) {
// ReferenceDA proof format: [hash(32), offset(8), Version(1), PreimageSize(8), PreimageData]
require(proof.length >= 49, "Proof too short"); // 32 + 8 + 1 + 8

// Extract hash and offset that were included by the off-chain enhancer
bytes32 hash;
uint256 offset;
assembly {
hash := calldataload(add(proof.offset, 0))
offset := shr(192, calldataload(add(proof.offset, 32))) // Read 8 bytes as uint256
}

// Decode the actual proof data
require(proof[40] == 0x01, "Unsupported proof version");

uint256 preimageSize;
assembly {
preimageSize := shr(192, calldataload(add(proof.offset, 41))) // Read 8 bytes as uint256
}
require(proof.length == 49 + preimageSize, "Invalid proof length");

// Extract preimage data
bytes memory preimage = proof[49:];

// Verify hash
require(keccak256(preimage) == hash, "Invalid preimage");

// Extract chunk at offset
uint256 chunkStart = offset;
uint256 chunkEnd = offset + 32;
if (chunkEnd > preimage.length) {
chunkEnd = preimage.length;
}

uint256 chunkSize = chunkEnd - chunkStart;
preimageChunk = new bytes(32);

if (chunkSize > 0) {
for (uint256 i = 0; i < chunkSize; i++) {
preimageChunk[i] = preimage[chunkStart + i];
}
}

return preimageChunk;
}
}
166 changes: 166 additions & 0 deletions test/osp/ReferenceDAProofValidator.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../../src/osp/ReferenceDAProofValidator.sol";

contract ReferenceDAProofValidatorTest is Test {
ReferenceDAProofValidator validator;

function setUp() public {
validator = new ReferenceDAProofValidator();
}

function testValidateReadPreimage() public {
// Test preimage data
bytes memory preimage = "This is a test preimage that is longer than 32 bytes for testing chunk extraction";
bytes32 hash = keccak256(preimage);
uint256 offset = 16; // Read from byte 16

// Build proof: [hash(32), offset(8), Version(1), PreimageSize(8), PreimageData]
bytes memory proof = new bytes(49 + preimage.length);

// Copy hash
assembly {
mstore(add(proof, 32), hash)
}

// Copy offset (8 bytes)
assembly {
let offsetData := shl(192, offset) // Shift to make it 8 bytes at the beginning
mstore(add(proof, 64), offsetData)
}

// Set version
proof[40] = 1;

// Set preimage size (8 bytes)
uint256 preimageSize = preimage.length;
assembly {
let sizeData := shl(192, preimageSize) // Shift to make it 8 bytes at the beginning
mstore(add(proof, 73), sizeData)
}

// Copy preimage data
for (uint256 i = 0; i < preimage.length; i++) {
proof[49 + i] = preimage[i];
}

// Call validateReadPreimage
bytes memory chunk = validator.validateReadPreimage(proof);

// Verify the chunk
assertEq(chunk.length, 32, "Chunk should be 32 bytes");

// Verify chunk contents match the expected slice of preimage
for (uint256 i = 0; i < 32; i++) {
if (offset + i < preimage.length) {
assertEq(chunk[i], preimage[offset + i], "Chunk byte mismatch");
} else {
assertEq(chunk[i], 0, "Chunk padding should be zero");
}
}
}

function testValidateReadPreimageAtEnd() public {
// Test reading at the end of preimage (less than 32 bytes available)
bytes memory preimage = "Short preimage";
bytes32 hash = keccak256(preimage);
uint256 offset = 8; // Only 6 bytes available from offset 8

// Build proof
bytes memory proof = new bytes(49 + preimage.length);

// Copy hash
assembly {
mstore(add(proof, 32), hash)
}

// Copy offset
assembly {
let offsetData := shl(192, offset)
mstore(add(proof, 64), offsetData)
}

// Set version
proof[40] = 1;

// Set preimage size
assembly {
let sizeData := shl(192, 14) // "Short preimage" is 14 bytes
mstore(add(proof, 73), sizeData)
}

// Copy preimage
for (uint256 i = 0; i < preimage.length; i++) {
proof[49 + i] = preimage[i];
}

// Validate
bytes memory chunk = validator.validateReadPreimage(proof);

// Should get "eimage" (6 bytes) padded with zeros
assertEq(chunk.length, 32);
assertEq(chunk[0], bytes1("e"));
assertEq(chunk[1], bytes1("i"));
assertEq(chunk[2], bytes1("m"));
assertEq(chunk[3], bytes1("a"));
assertEq(chunk[4], bytes1("g"));
assertEq(chunk[5], bytes1("e"));

// Rest should be zeros
for (uint256 i = 6; i < 32; i++) {
assertEq(chunk[i], 0);
}
}

function testInvalidHash() public {
bytes memory preimage = "Test preimage";
bytes32 wrongHash = keccak256("Wrong data");
uint256 offset = 0;

// Build proof with wrong hash
bytes memory proof = new bytes(49 + preimage.length);

assembly {
mstore(add(proof, 32), wrongHash)
}
assembly {
let offsetData := shl(192, offset)
mstore(add(proof, 64), offsetData)
}
proof[40] = 1;
assembly {
let sizeData := shl(192, 13)
mstore(add(proof, 73), sizeData)
}
for (uint256 i = 0; i < preimage.length; i++) {
proof[49 + i] = preimage[i];
}

// Should revert
vm.expectRevert("Invalid preimage");
validator.validateReadPreimage(proof);
}

function testInvalidVersion() public {
bytes memory preimage = "Test";
bytes32 hash = keccak256(preimage);

bytes memory proof = new bytes(49 + preimage.length);
assembly {
mstore(add(proof, 32), hash)
}
proof[40] = 2; // Wrong version

vm.expectRevert("Unsupported proof version");
validator.validateReadPreimage(proof);
}

function testProofTooShort() public {
bytes memory proof = new bytes(48); // Too short

vm.expectRevert("Proof too short");
validator.validateReadPreimage(proof);
}
}
Loading