From 2780ad1f16e5ff70ea8bdb66f492c11ae62b9b49 Mon Sep 17 00:00:00 2001 From: Inphi Date: Tue, 29 Oct 2024 13:54:50 -0700 Subject: [PATCH] cannon: Implement MIPS64Memory.sol (#12653) * cannon: Implement MIPS64Memory.sol * add non-zero revert data; cleanup go-ffi script --- packages/contracts-bedrock/.gitignore | 1 + packages/contracts-bedrock/justfile | 8 +- .../scripts/go-ffi/differential-testing.go | 87 ++++---- .../src/cannon/libraries/MIPS64Memory.sol | 176 +++++++++++++++ .../test/cannon/MIPS64Memory.t.sol | 211 ++++++++++++++++++ .../test/setup/FFIInterface.sol | 84 +++++++ 6 files changed, 524 insertions(+), 43 deletions(-) create mode 100644 packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol create mode 100644 packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 6ca35454b291..ae6569a94ebe 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -16,6 +16,7 @@ kontrol_prove_report.xml # Scripts scripts/go-ffi/go-ffi +scripts/go-ffi/go-ffi-cannon64 # Environment Variables .envrc diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 23f91c5779c3..d77e93822462 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -27,9 +27,15 @@ forge-build: build: prebuild lint-fix-no-fail forge-build interfaces-check-no-build # Builds the go-ffi tool for contract tests. -build-go-ffi: +build-go-ffi-default: cd ./scripts/go-ffi && go build +# Builds the go-ffi tool for MIPS64 contract tests. +build-go-ffi-cannon64: + cd ./scripts/go-ffi && go build -tags=cannon64 -o ./go-ffi-cannon64 + +build-go-ffi: build-go-ffi-default build-go-ffi-cannon64 + # Cleans build artifacts and deployments. clean: rm -rf ./artifacts ./forge-artifacts ./cache ./scripts/go-ffi/go-ffi ./deployments/hardhat/* diff --git a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go index b81bbeb2b153..e35d2a82c303 100644 --- a/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go +++ b/packages/contracts-bedrock/scripts/go-ffi/differential-testing.go @@ -360,68 +360,71 @@ func DiffTestUtils() { // Print the output fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProof": - // - // Generates a memory proof of `memAddr` for a trie containing memValue and memValue2 + // + // Generates memory proofs of `memAddr0` for a trie containing memValue0 and `memAddr1` for a trie containing memValue1 and memValue2 + // For the cannon stf, this is equivalent to the prestate proofs of the program counter and memory access for instruction execution mem := memory.NewMemory() if len(args) != 3 && len(args) != 5 && len(args) != 7 { panic("Error: cannonMemoryProofWithProof requires 2, 4, or 6 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) - var insnProof, memProof [896]byte + var proof1 []byte if len(args) >= 5 { - memAddr, err := strconv.ParseUint(args[3], 10, 32) + memAddr, err := strconv.ParseUint(args[3], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) + memValue, err := strconv.ParseUint(args[4], 10, arch.WordSize) checkErr(err, "Error decoding memValue") mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) - memProof = mem.MerkleProof(arch.Word(memAddr)) + proof := mem.MerkleProof(arch.Word(memAddr)) + proof1 = proof[:] } if len(args) == 7 { - memAddr, err := strconv.ParseUint(args[5], 10, 32) + memAddr, err := strconv.ParseUint(args[5], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[6], 10, 32) + memValue, err := strconv.ParseUint(args[6], 10, arch.WordSize) checkErr(err, "Error decoding memValue") mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) - memProof = mem.MerkleProof(arch.Word(memAddr)) + proof := mem.MerkleProof(arch.Word(memAddr)) + proof1 = proof[:] } - insnProof = mem.MerkleProof(arch.Word(pc)) + proof0 := mem.MerkleProof(arch.Word(memAddr0)) output := struct { MemRoot common.Hash Proof []byte }{ MemRoot: mem.MerkleRoot(), - Proof: append(insnProof[:], memProof[:]...), + Proof: append(proof0[:], proof1...), } packed, err := cannonMemoryProofArgs.Pack(&output) checkErr(err, "Error encoding output") fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProof2": - // - // Generates a memory proof of memAddr2 for a trie containing memValue + // + // Generates memory proof of `memAddr2` for a trie containing `memValue0` and `memValue1` mem := memory.NewMemory() if len(args) != 6 { panic("Error: cannonMemoryProofWithProof2 requires 5 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) - var memProof [896]byte - memAddr, err := strconv.ParseUint(args[3], 10, 32) + var memProof [memory.MemProofSize]byte + memAddr, err := strconv.ParseUint(args[3], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) - checkErr(err, "Error decoding memValue") - mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memValue1, err := strconv.ParseUint(args[4], 10, arch.WordSize) + checkErr(err, "Error decoding memValue1") + mem.SetWord(arch.Word(memAddr), arch.Word(memValue1)) - memAddr2, err := strconv.ParseUint(args[5], 10, 32) + memAddr2, err := strconv.ParseUint(args[5], 10, arch.WordSize) checkErr(err, "Error decoding memAddr") memProof = mem.MerkleProof(arch.Word(memAddr2)) @@ -436,27 +439,27 @@ func DiffTestUtils() { checkErr(err, "Error encoding output") fmt.Print(hexutil.Encode(packed[32:])) case "cannonMemoryProofWrongLeaf": - // + // mem := memory.NewMemory() if len(args) != 5 { panic("Error: cannonMemoryProofWrongLeaf requires 4 arguments") } - pc, err := strconv.ParseUint(args[1], 10, 32) - checkErr(err, "Error decoding addr") - insn, err := strconv.ParseUint(args[2], 10, 32) - checkErr(err, "Error decoding insn") - mem.SetWord(arch.Word(pc), arch.Word(insn)) - - var insnProof, memProof [896]byte - memAddr, err := strconv.ParseUint(args[3], 10, 32) - checkErr(err, "Error decoding memAddr") - memValue, err := strconv.ParseUint(args[4], 10, 32) - checkErr(err, "Error decoding memValue") - mem.SetWord(arch.Word(memAddr), arch.Word(memValue)) + memAddr0, err := strconv.ParseUint(args[1], 10, arch.WordSize) + checkErr(err, "Error decoding memAddr0") + memValue0, err := strconv.ParseUint(args[2], 10, arch.WordSize) + checkErr(err, "Error decoding memValue0") + mem.SetWord(arch.Word(memAddr0), arch.Word(memValue0)) + + var insnProof, memProof [memory.MemProofSize]byte + memAddr1, err := strconv.ParseUint(args[3], 10, arch.WordSize) + checkErr(err, "Error decoding memAddr1") + memValue1, err := strconv.ParseUint(args[4], 10, arch.WordSize) + checkErr(err, "Error decoding memValue1") + mem.SetWord(arch.Word(memAddr1), arch.Word(memValue1)) // Compute a valid proof for the root, but for the wrong leaves. - memProof = mem.MerkleProof(arch.Word(memAddr + 32)) - insnProof = mem.MerkleProof(arch.Word(pc + 32)) + memProof = mem.MerkleProof(arch.Word(memAddr1 + arch.WordSize)) + insnProof = mem.MerkleProof(arch.Word(memAddr0 + arch.WordSize)) output := struct { MemRoot common.Hash diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol new file mode 100644 index 000000000000..23a6639bc9c4 --- /dev/null +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import "src/cannon/libraries/CannonErrors.sol"; + +library MIPS64Memory { + uint64 internal constant EXT_MASK = 0x7; + uint64 internal constant MEM_PROOF_LEAF_COUNT = 60; + uint256 internal constant U64_MASK = 0xFFFFFFFFFFFFFFFF; + + /// @notice Reads a 64-bit word from memory. + /// @param _memRoot The current memory root + /// @param _addr The address to read from. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return out_ The hashed MIPS state. + function readMem(bytes32 _memRoot, uint64 _addr, uint256 _proofOffset) internal pure returns (uint64 out_) { + bool valid; + (out_, valid) = readMemUnchecked(_memRoot, _addr, _proofOffset); + if (!valid) { + revert InvalidMemoryProof(); + } + } + + /// @notice Reads a 64-bit word from memory. + /// @param _memRoot The current memory root + /// @param _addr The address to read from. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return out_ The hashed MIPS state. + /// valid_ Whether the proof is valid. + function readMemUnchecked( + bytes32 _memRoot, + uint64 _addr, + uint256 _proofOffset + ) + internal + pure + returns (uint64 out_, bool valid_) + { + unchecked { + validateMemoryProofAvailability(_proofOffset); + assembly { + // Validate the address alignment. + if and(_addr, EXT_MASK) { + // revert InvalidAddress(); + let ptr := mload(0x40) + mstore(ptr, shl(224, 0xe6c4247b)) + revert(ptr, 0x4) + } + + // Load the leaf value. + let leaf := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + + // Convenience function to hash two nodes together in scratch space. + function hashPair(a, b) -> h { + mstore(0, a) + mstore(32, b) + h := keccak256(0, 64) + } + + // Start with the leaf node. + // Work back up by combining with siblings, to reconstruct the root. + let path := shr(5, _addr) + let node := leaf + let end := sub(MEM_PROOF_LEAF_COUNT, 1) + for { let i := 0 } lt(i, end) { i := add(i, 1) } { + let sibling := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + switch and(shr(i, path), 1) + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } + } + + // Verify the root matches. + valid_ := eq(node, _memRoot) + if valid_ { + // Bits to shift = (32 - 8 - (addr % 32)) * 8 + let shamt := shl(3, sub(sub(32, 8), and(_addr, 31))) + out_ := and(shr(shamt, leaf), U64_MASK) + } + } + } + } + + /// @notice Writes a 64-bit word to memory. + /// This function first overwrites the part of the leaf. + /// Then it recomputes the memory merkle root. + /// @param _addr The address to write to. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @param _val The value to write. + /// @return newMemRoot_ The new memory root after modification + function writeMem(uint64 _addr, uint256 _proofOffset, uint64 _val) internal pure returns (bytes32 newMemRoot_) { + unchecked { + validateMemoryProofAvailability(_proofOffset); + assembly { + // Validate the address alignment. + if and(_addr, EXT_MASK) { + // revert InvalidAddress(); + let ptr := mload(0x40) + mstore(ptr, shl(224, 0xe6c4247b)) + revert(ptr, 0x4) + } + + // Load the leaf value. + let leaf := calldataload(_proofOffset) + let shamt := shl(3, sub(sub(32, 8), and(_addr, 31))) + + // Mask out 8 bytes, and OR in the value + leaf := or(and(leaf, not(shl(shamt, U64_MASK))), shl(shamt, _val)) + _proofOffset := add(_proofOffset, 32) + + // Convenience function to hash two nodes together in scratch space. + function hashPair(a, b) -> h { + mstore(0, a) + mstore(32, b) + h := keccak256(0, 64) + } + + // Start with the leaf node. + // Work back up by combining with siblings, to reconstruct the root. + let path := shr(5, _addr) + let node := leaf + let end := sub(MEM_PROOF_LEAF_COUNT, 1) + for { let i := 0 } lt(i, end) { i := add(i, 1) } { + let sibling := calldataload(_proofOffset) + _proofOffset := add(_proofOffset, 32) + switch and(shr(i, path), 1) + case 0 { node := hashPair(node, sibling) } + case 1 { node := hashPair(sibling, node) } + } + + newMemRoot_ := node + } + return newMemRoot_; + } + } + + /// @notice Verifies a memory proof. + /// @param _memRoot The expected memory root + /// @param _addr The _addr proven. + /// @param _proofOffset The offset of the memory proof in calldata. + /// @return valid_ True iff it is a valid proof. + function isValidProof(bytes32 _memRoot, uint64 _addr, uint256 _proofOffset) internal pure returns (bool valid_) { + (, valid_) = readMemUnchecked(_memRoot, _addr, _proofOffset); + } + + /// @notice Computes the offset of a memory proof in the calldata. + /// @param _proofDataOffset The offset of the set of all memory proof data within calldata (proof.offset) + /// Equal to the offset of the first memory proof (at _proofIndex 0). + /// @param _proofIndex The index of the proof in the calldata. + /// @return offset_ The offset of the memory proof at the given _proofIndex in the calldata. + function memoryProofOffset(uint256 _proofDataOffset, uint8 _proofIndex) internal pure returns (uint256 offset_) { + unchecked { + // A proof of 64-bit memory, with 32-byte leaf values, is (64-5)=59 bytes32 entries. + // And the leaf value itself needs to be encoded as well: (59 + 1) = 60 bytes32 entries. + offset_ = _proofDataOffset + (uint256(_proofIndex) * (MEM_PROOF_LEAF_COUNT * 32)); + return offset_; + } + } + + /// @notice Validates that enough calldata is available to hold a full memory proof at the given offset + /// @param _proofStartOffset The index of the first byte of the target memory proof in calldata + function validateMemoryProofAvailability(uint256 _proofStartOffset) internal pure { + unchecked { + uint256 s = 0; + assembly { + s := calldatasize() + } + // A memory proof consists of MEM_PROOF_LEAF_COUNT bytes32 values - verify we have enough calldata + require( + s >= (_proofStartOffset + MEM_PROOF_LEAF_COUNT * 32), + "MIPS64Memory: check that there is enough calldata" + ); + } + } +} diff --git a/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol new file mode 100644 index 000000000000..a3a1160c7d48 --- /dev/null +++ b/packages/contracts-bedrock/test/cannon/MIPS64Memory.t.sol @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { CommonTest } from "test/setup/CommonTest.sol"; +import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; +import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; + +contract MIPS64Memory_Test is CommonTest { + MIPS64MemoryWithCalldata mem; + + error InvalidAddress(); + + function setUp() public virtual override { + super.setUp(); + mem = new MIPS64MemoryWithCalldata(); + } + + /// @dev Static unit test for basic memory access + function test_readMem_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reading from the zero address succeeds + function test_readMemAtZero_succeeds() external { + uint64 addr = 0x0; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reading from high memory area succeeds + function test_readMemHighMem_succeeds() external { + uint64 addr = 0xFF_FF_FF_FF_00_00_00_88; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + } + + /// @dev Static unit test asserting that reads revert when a misaligned memory address is provided + function test_readInvalidAddress_reverts() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + vm.expectRevert(InvalidAddress.selector); + mem.readMem(root, addr + 4, 0, proof); + } + + /// @dev Static unit test asserting that reads revert when an invalid proof is provided + function test_readInvalidProof_reverts() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word); + vm.assertTrue(proof[64] != 0x0); // make sure the proof is tampered + proof[64] = 0x00; + vm.expectRevert(InvalidMemoryProof.selector); + mem.readMem(root, addr, 0, proof); + } + + /// @dev Static unit test asserting that reads from a non-zero proof index succeeds + function test_readMemNonZeroProofIndex_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0xFF_FF_FF_FF_00_00_00_88; + uint64 word2 = 0xF1_F2_F3_F4_F5_F6_F7_F8; + bytes32 root; + bytes memory proof; + (root, proof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 readWord = mem.readMem(root, addr, 0, proof); + assertEq(readWord, word); + + readWord = mem.readMem(root, addr2, 1, proof); + assertEq(readWord, word2); + } + + /// @dev Static unit test asserting basic memory write functionality + function test_writeMem_succeeds() external { + uint64 addr = 0x100; + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(addr, 0); + + uint64 word = 0x11_22_33_44_55_66_77_88; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word); + + bytes32 newRoot = mem.writeMem(addr, word, 0, zeroProof); + assertEq(newRoot, expectedRoot); + } + + // @dev Static unit test asserting that writes to high memory succeeds + function test_writeMemHighMem_succeeds() external { + uint64 addr = 0xFF_FF_FF_FF_00_00_00_88; + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(addr, 0); + + uint64 word = 0x11_22_33_44_55_66_77_88; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word); + + bytes32 newRoot = mem.writeMem(addr, word, 0, zeroProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that non-zero memory word is overwritten + function test_writeMemNonZeroProofOffset_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0x108; + uint64 word2 = 0x55_55_55_55_77_77_77_77; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 word3 = 0x44_44_44_44_44_44_44_44; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr2, word2, addr2, word3); + + bytes32 newRoot = mem.writeMem(addr2, word3, 1, initProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that a zerod memory word is set for a non-zero memory proof + function test_writeMemUniqueAccess_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + uint64 addr2 = 0x108; + uint64 word2 = 0x55_55_55_55_77_77_77_77; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word, addr2, word2); + + uint64 addr3 = 0xAA_AA_AA_AA_00; + uint64 word3 = 0x44_44_44_44_44_44_44_44; + (, bytes memory addr3Proof) = ffi.getCannonMemory64Proof2(addr, word, addr2, word2, addr3); + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr2, word2, addr3, word3); + + bytes32 newRoot = mem.writeMem(addr3, word3, 0, addr3Proof); + assertEq(newRoot, expectedRoot); + + newRoot = mem.writeMem(addr3 + 8, word3, 0, addr3Proof); + assertNotEq(newRoot, expectedRoot); + + newRoot = mem.writeMem(addr3, word3 + 1, 0, addr3Proof); + assertNotEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that writes succeeds in overwriting a non-zero memory word + function test_writeMemNonZeroMem_succeeds() external { + uint64 addr = 0x100; + uint64 word = 0x11_22_33_44_55_66_77_88; + bytes memory initProof; + (, initProof) = ffi.getCannonMemory64Proof(addr, word); + + uint64 word2 = 0x55_55_55_55_77_77_77_77; + (bytes32 expectedRoot,) = ffi.getCannonMemory64Proof(addr, word, addr + 8, word2); + + bytes32 newRoot = mem.writeMem(addr + 8, word2, 0, initProof); + assertEq(newRoot, expectedRoot); + } + + /// @dev Static unit test asserting that writes revert when a misaligned memory address is provided + function test_writeMemInvalidAddress_reverts() external { + bytes memory zeroProof; + (, zeroProof) = ffi.getCannonMemory64Proof(0x100, 0); + vm.expectRevert(InvalidAddress.selector); + mem.writeMem(0x104, 0x0, 0, zeroProof); + } +} + +contract MIPS64MemoryWithCalldata { + function readMem( + bytes32 _root, + uint64 _addr, + uint8 _proofIndex, + bytes calldata /* _proof */ + ) + external + pure + returns (uint64 out_) + { + uint256 proofDataOffset = 4 + 32 + 32 + 32 + 32 + 32; + uint256 proofOffset = MIPS64Memory.memoryProofOffset(proofDataOffset, _proofIndex); + return MIPS64Memory.readMem(_root, _addr, proofOffset); + } + + function writeMem( + uint64 _addr, + uint64 _value, + uint8 _proofIndex, + bytes calldata /* _proof */ + ) + external + pure + returns (bytes32 root_) + { + uint256 proofDataOffset = 4 + 32 + 32 + 32 + 32 + 32; + uint256 proofOffset = MIPS64Memory.memoryProofOffset(proofDataOffset, _proofIndex); + return MIPS64Memory.writeMem(_addr, proofOffset, _value); + } +} diff --git a/packages/contracts-bedrock/test/setup/FFIInterface.sol b/packages/contracts-bedrock/test/setup/FFIInterface.sol index 727402a37c2c..c1e1612da8e6 100644 --- a/packages/contracts-bedrock/test/setup/FFIInterface.sol +++ b/packages/contracts-bedrock/test/setup/FFIInterface.sol @@ -317,6 +317,90 @@ contract FFIInterface { return (memRoot, proof); } + function getCannonMemory64Proof(uint64 addr, uint64 value) external returns (bytes32, bytes memory) { + string[] memory cmds = new string[](5); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr); + cmds[4] = vm.toString(value); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1 + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](7); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1, + uint64 memAddr2, + uint64 memVal2 + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](9); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + cmds[7] = vm.toString(memAddr2); + cmds[8] = vm.toString(memVal2); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + + function getCannonMemory64Proof2( + uint64 addr0, + uint64 value0, + uint64 addr1, + uint64 value1, + uint64 memAddrForProof + ) + external + returns (bytes32, bytes memory) + { + string[] memory cmds = new string[](8); + cmds[0] = "scripts/go-ffi/go-ffi-cannon64"; + cmds[1] = "diff"; + cmds[2] = "cannonMemoryProof2"; + cmds[3] = vm.toString(addr0); + cmds[4] = vm.toString(value0); + cmds[5] = vm.toString(addr1); + cmds[6] = vm.toString(value1); + cmds[7] = vm.toString(memAddrForProof); + bytes memory result = Process.run(cmds); + (bytes32 memRoot, bytes memory proof) = abi.decode(result, (bytes32, bytes)); + return (memRoot, proof); + } + function encodeScalarEcotone(uint32 _basefeeScalar, uint32 _blobbasefeeScalar) external returns (bytes32) { string[] memory cmds = new string[](5); cmds[0] = "scripts/go-ffi/go-ffi";