Precompiles
LP-3550
ReviewVerkle Proof Verification Precompile
Precompiled contract for efficient Verkle proof verification
LP-3550: Verkle Proof Verification Precompile
Abstract
This LP specifies a precompiled contract for verifying Verkle tree proofs on-chain, enabling smart contracts to verify state proofs from other chains or L2s.
Motivation
Cross-chain bridges and L2 solutions need to verify state proofs:
- Bridge contracts verify source chain state
- Optimistic rollups verify fraud proofs
- ZK rollups verify state transitions
Specification
Precompile Address
0x0000000000000000000000000000000000000014
Input Format
| Field | Offset | Size | Description |
|----------------|--------|--------|--------------------------------|
| commitment | 0 | 32 | Tree root commitment |
| proof_offset | 32 | 32 | Offset to proof data |
| proof_length | 64 | 32 | Length of proof data |
| keys_offset | 96 | 32 | Offset to keys array |
| keys_count | 128 | 32 | Number of keys |
| values_offset | 160 | 32 | Offset to values array |
| proof_data | var | var | IPA or KZG proof |
| keys | var | var | Array of 32-byte keys |
| values | var | var | Array of 32-byte values |
Output Format
| Field | Offset | Size | Description |
|----------|--------|------|-------------------------------------|
| valid | 0 | 32 | 1 if proof valid, 0 otherwise |
Gas Cost
func verkleProofGas(keyCount int) uint64 {
return VERKLE_BASE_GAS + uint64(keyCount) * VERKLE_PER_KEY_GAS
}
const (
VERKLE_BASE_GAS = 3000 // Base verification cost
VERKLE_PER_KEY_GAS = 200 // Per key in multiproof
)
Implementation
type verkleProofPrecompile struct{}
func (p *verkleProofPrecompile) RequiredGas(input []byte) uint64 {
keyCount := binary.BigEndian.Uint64(input[128:160])
return VERKLE_BASE_GAS + keyCount * VERKLE_PER_KEY_GAS
}
func (p *verkleProofPrecompile) Run(input []byte) ([]byte, error) {
// Parse input
commitment := input[0:32]
proofOffset := binary.BigEndian.Uint64(input[32:64])
proofLength := binary.BigEndian.Uint64(input[64:96])
keysOffset := binary.BigEndian.Uint64(input[96:128])
keysCount := binary.BigEndian.Uint64(input[128:160])
valuesOffset := binary.BigEndian.Uint64(input[160:192])
proof := input[proofOffset:proofOffset+proofLength]
keys := parseKeys(input[keysOffset:], keysCount)
values := parseValues(input[valuesOffset:], keysCount)
// Verify proof
valid := verkle.VerifyMultiproof(commitment, proof, keys, values)
if valid {
return common.LeftPadBytes([]byte{1}, 32), nil
}
return make([]byte, 32), nil
}
Use Cases
L2 State Verification
function verifyL2State(
bytes32 stateRoot,
bytes calldata proof,
bytes32[] calldata keys,
bytes32[] calldata values
) external view returns (bool) {
bytes memory input = abi.encodePacked(
stateRoot,
// ... encode proof, keys, values
);
(bool success, bytes memory result) = VERKLE_PRECOMPILE.staticcall(input);
return success && abi.decode(result, (uint256)) == 1;
}
Rationale
Precompile chosen over library:
- Much lower gas cost (3000 + 200/key vs 100k+)
- Native curve operations
- Consistent implementation
Backwards Compatibility
This standard is fully backwards compatible with existing contracts and infrastructure. The standard is additive and does not modify existing functionality.
Security Considerations
- Input validation prevents malformed proofs
- Gas limits prevent DoS via large proofs
- Commitment binding prevents proof forgery
References
Copyright
Copyright and related rights waived via CC0.