Skip to content

Add CustomDA validation to Solidity contracts #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
119 changes: 119 additions & 0 deletions src/osp/OneStepProverHostIo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,77 @@ contract OneStepProverHostIo is IOneStepProver {
return uint256(bytes32(modExpOutput));
}

function executeValidatePreimage(
ExecutionContext calldata,
Machine memory mach,
Module memory mod,
Instruction calldata,
bytes calldata proof
) internal view {
uint256 hashPtr = mach.valueStack.pop().assumeI32();
uint256 preimageType = mach.valueStack.pop().assumeI32();

// Check for valid memory access
if (hashPtr + 32 > mod.moduleMemory.size || hashPtr % LEAF_SIZE != 0) {
mach.status = MachineStatus.ERRORED;
return;
}

uint256 leafIdx = hashPtr / LEAF_SIZE;
uint256 proofOffset = 0;
bytes32 hash;
MerkleProof memory merkleProof;
(hash, proofOffset, merkleProof) =
mod.moduleMemory.proveLeaf(leafIdx, proof, proofOffset);

// For types 0-2 (existing preimage types), validation is always successful
if (preimageType < 3) {
mach.valueStack.push(ValueLib.newI32(1)); // Return true (1)
return;
}

// For type 3 (CustomDA), we need to validate the proof
if (preimageType == 3) {
// Read proof type
require(proof.length > proofOffset, "INVALID_CUSTOMDA_PROOF");
uint8 proofType = uint8(proof[proofOffset]);
proofOffset++;

bool valid = false;

if (proofType == 0) {
// Simple proof - direct hash check
// Just verify that the hash is valid (exists in the proof)
bytes calldata preimage = proof[proofOffset:];
valid = keccak256(preimage) == hash;
} else if (proofType == 1) {
// Custom validator contract proof
// Format: [validatorAddress (20 bytes)][proof data]
require(proof.length >= proofOffset + 20, "INVALID_CUSTOMDA_PROOF");
address validatorAddress = address(bytes20(proof[proofOffset:proofOffset+20]));
bytes calldata customProof = proof[proofOffset+20:];

// Call the validator contract
(bool success, bytes memory validationResult) = validatorAddress.staticcall(
abi.encodeWithSignature(
"validateCustomDAProof(bytes32,bytes)",
hash,
customProof
)
);

valid = success && validationResult.length >= 32 &&
abi.decode(validationResult, (bool));
}

mach.valueStack.push(ValueLib.newI32(valid ? 1 : 0));
return;
}

// For any other type, return false (0)
mach.valueStack.push(ValueLib.newI32(0));
}

function executeReadPreImage(
ExecutionContext calldata,
Machine memory mach,
Expand Down Expand Up @@ -220,6 +291,52 @@ contract OneStepProverHostIo is IOneStepProver {

extracted = kzgProof[64:96];
}
} else if (inst.argumentData == 3) {
// The machine is asking for a CustomDA preimage

if (proofType == 0) {
// Simple preimage proof format
bytes calldata preimage = proof[proofOffset:];
require(keccak256(preimage) == leafContents, "BAD_PREIMAGE");

uint256 preimageEnd = preimageOffset + 32;
if (preimageEnd > preimage.length) {
preimageEnd = preimage.length;
}
extracted = preimage[preimageOffset:preimageEnd];
} else if (proofType == 1) {
// Custom DA proof format that delegates to a custom validator contract
// Format: [validatorAddress (20 bytes)][proof data]
require(proof.length >= proofOffset + 20, "INVALID_CUSTOMDA_PROOF");
address validatorAddress = address(bytes20(proof[proofOffset:proofOffset+20]));
bytes calldata customProof = proof[proofOffset+20:];

// Call the validator contract to verify the proof
(bool success, bytes memory validationResult) = validatorAddress.staticcall(
abi.encodeWithSignature(
"validateCustomDAProof(bytes32,uint256,bytes)",
leafContents,
preimageOffset,
customProof
)
);
require(success && validationResult.length >= 32, "CUSTOMDA_VALIDATION_FAILED");
require(abi.decode(validationResult, (bool)), "INVALID_CUSTOMDA_PROOF");

// Extract the data from the proof
(success, validationResult) = validatorAddress.staticcall(
abi.encodeWithSignature(
"extractCustomDAData(bytes32,uint256,bytes)",
leafContents,
preimageOffset,
customProof
)
);
require(success, "CUSTOMDA_EXTRACTION_FAILED");
extracted = validationResult;
} else {
revert("UNKNOWN_CUSTOMDA_PROOF_TYPE");
}
} else {
revert("UNKNOWN_PREIMAGE_TYPE");
}
Expand Down Expand Up @@ -597,6 +714,8 @@ contract OneStepProverHostIo is IOneStepProver {
&& opcode <= Instructions.SET_GLOBAL_STATE_U64
) {
impl = executeGlobalStateAccess;
} else if (opcode == Instructions.VALIDATE_PREIMAGE) {
impl = executeValidatePreimage;
} else if (opcode == Instructions.READ_PRE_IMAGE) {
impl = executeReadPreImage;
} else if (opcode == Instructions.READ_INBOX_MESSAGE) {
Expand Down
1 change: 1 addition & 0 deletions src/state/Instructions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ library Instructions {
uint16 internal constant GET_GLOBAL_STATE_U64 = 0x8012;
uint16 internal constant SET_GLOBAL_STATE_U64 = 0x8013;

uint16 internal constant VALIDATE_PREIMAGE = 0x8019;
uint16 internal constant READ_PRE_IMAGE = 0x8020;
uint16 internal constant READ_INBOX_MESSAGE = 0x8021;
uint16 internal constant HALT_AND_SET_FINISHED = 0x8022;
Expand Down
Loading