Skip to content

Commit 4e0e36a

Browse files
authored
Merge pull request #10 from ethereum-optimism/tip/pcw109550/local-preimage-support
feat: Local Preimage Support
2 parents 8af5ae9 + d3ff8bd commit 4e0e36a

File tree

9 files changed

+330
-45
lines changed

9 files changed

+330
-45
lines changed

.github/workflows/ci.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: AsteriscCI
22
on: [push]
33

44
jobs:
5-
go-tests:
5+
rvgo-tests:
66
runs-on: ubuntu-latest
77
timeout-minutes: 20
88
steps:
@@ -23,6 +23,18 @@ jobs:
2323
- name: Run tests
2424
run: go test -v ./...
2525
working-directory: rvgo
26+
rvsol-tests:
27+
runs-on: ubuntu-latest
28+
timeout-minutes: 20
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
- name: Install Foundry
33+
uses: foundry-rs/foundry-toolchain@v1
34+
- name: Run foundry tests
35+
run: forge test -vvv
36+
working-directory: rvsol
37+
2638
# go-lint:
2739
# runs-on: ubuntu-latest
2840
# timeout-minutes: 20

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "rvsol/lib/forge-std"]
2+
path = rvsol/lib/forge-std
3+
url = https://github.com/foundry-rs/forge-std

rvgo/fast/evm.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import "github.com/ethereum/go-ethereum/crypto"
44

55
var (
66
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes,bytes32)"))[:4]
7-
CheatBytes4 = crypto.Keccak256([]byte("cheat(uint256,bytes32,bytes32,uint256)"))[:4]
8-
CheatLocalKeyBytes4 = crypto.Keccak256([]byte("cheatLocalKey(uint256,bytes32,bytes32,uint256,bytes32)"))[:4]
97
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4]
8+
LoadLocalDataBytes4 = crypto.Keccak256([]byte("loadLocalData(uint256,bytes32,bytes32,uint256,uint256)"))[:4]
109
)

rvgo/fast/witness.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,20 @@ func (wit *StepWitness) EncodePreimageOracleInput(localContext LocalContext) ([]
6363

6464
switch preimage.KeyType(wit.PreimageKey[0]) {
6565
case preimage.LocalKeyType:
66-
// We have no on-chain form of preparing the bootstrap pre-images onchain yet.
67-
// So instead we cheat them in.
68-
// In production usage there should be an on-chain contract that exposes this,
69-
// rather than going through the global keccak256 oracle.
66+
if len(wit.PreimageValue) > 32+8 {
67+
return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", wit.PreimageKey)
68+
}
7069
var input []byte
71-
input = append(input, CheatLocalKeyBytes4...)
72-
input = append(input, uint64ToBytes32(wit.PreimageOffset)...)
70+
input = append(input, LoadLocalDataBytes4...)
7371
input = append(input, wit.PreimageKey[:]...)
72+
input = append(input, common.Hash(localContext).Bytes()...)
73+
74+
preimagePart := wit.PreimageValue[8:]
7475
var tmp [32]byte
75-
copy(tmp[:], wit.PreimageValue[wit.PreimageOffset:])
76+
copy(tmp[:], preimagePart)
7677
input = append(input, tmp[:]...)
77-
input = append(input, uint64ToBytes32(uint64(len(wit.PreimageValue))-8)...)
78-
input = append(input, common.Hash(localContext).Bytes()...)
78+
input = append(input, uint64ToBytes32(uint64(len(wit.PreimageValue)-8))...)
79+
input = append(input, uint64ToBytes32(wit.PreimageOffset)...)
7980
// Note: we can pad calldata to 32 byte multiple, but don't strictly have to
8081
return input, nil
8182
case preimage.Keccak256KeyType:

rvsol/lib/forge-std

Submodule forge-std added at ae570fe

rvsol/src/PreimageKeyLib.sol

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.15;
3+
4+
/// @title PreimageKeyLib
5+
/// @notice Shared utilities for localizing local keys in the preimage oracle.
6+
library PreimageKeyLib {
7+
/// @notice Generates a context-specific local key for the given local data identifier.
8+
/// @dev See `localize` for a description of the localization operation.
9+
/// @param _ident The identifier of the local data. [0, 32) bytes in size.
10+
/// @param _localContext The local context for the key.
11+
/// @return key_ The context-specific local key.
12+
function localizeIdent(uint256 _ident, bytes32 _localContext) internal view returns (bytes32 key_) {
13+
assembly {
14+
// Set the type byte in the given identifier to `1` (Local). We only care about
15+
// the [1, 32) bytes in this value.
16+
key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF))))
17+
}
18+
// Localize the key with the given local context.
19+
key_ = localize(key_, _localContext);
20+
}
21+
22+
/// @notice Localizes a given local data key for the caller's context.
23+
/// @dev The localization operation is defined as:
24+
/// localize(k) = H(k .. sender .. local_context) & ~(0xFF << 248) | (0x01 << 248)
25+
/// where H is the Keccak-256 hash function.
26+
/// @param _key The local data key to localize.
27+
/// @param _localContext The local context for the key.
28+
/// @return localizedKey_ The localized local data key.
29+
function localize(bytes32 _key, bytes32 _localContext) internal view returns (bytes32 localizedKey_) {
30+
assembly {
31+
// Grab the current free memory pointer to restore later.
32+
let ptr := mload(0x40)
33+
// Store the local data key and caller next to each other in memory for hashing.
34+
mstore(0, _key)
35+
mstore(0x20, caller())
36+
mstore(0x40, _localContext)
37+
// Localize the key with the above `localize` operation.
38+
localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1))
39+
// Restore the free memory pointer.
40+
mstore(0x40, ptr)
41+
}
42+
}
43+
44+
/// @notice Computes and returns the key for a global keccak pre-image.
45+
/// @param _preimage The pre-image.
46+
/// @return key_ The pre-image key.
47+
function keccak256PreimageKey(bytes memory _preimage) internal pure returns (bytes32 key_) {
48+
assembly {
49+
// Grab the size of the `_preimage`
50+
let size := mload(_preimage)
51+
52+
// Compute the pre-image keccak256 hash (aka the pre-image key)
53+
let h := keccak256(add(_preimage, 0x20), size)
54+
55+
// Mask out prefix byte, replace with type 2 byte
56+
key_ := or(and(h, not(shl(248, 0xFF))), shl(248, 2))
57+
}
58+
}
59+
}

rvsol/src/PreimageOracle.sol

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.15;
33

4-
contract PreimageOracle {
4+
import {IPreimageOracle} from "./interfaces/IPreimageOracle.sol";
5+
import {PreimageKeyLib} from "./PreimageKeyLib.sol";
6+
7+
contract PreimageOracle is IPreimageOracle {
58
mapping(bytes32 => uint256) public preimageLengths;
69
mapping(bytes32 => mapping(uint256 => bytes32)) public preimageParts;
710
mapping(bytes32 => mapping(uint256 => bool)) public preimagePartOk;
@@ -21,42 +24,44 @@ contract PreimageOracle {
2124
dat_ = preimageParts[_key][_offset];
2225
}
2326

24-
// TODO(CLI-4104):
25-
// we need to mix-in the ID of the dispute for local-type keys to avoid collisions,
26-
// and restrict local pre-image insertion to the dispute-managing contract.
27-
// For now we permit anyone to write any pre-image unchecked, to make testing easy.
28-
// This method is DANGEROUS. And NOT FOR PRODUCTION.
29-
function cheat(uint256 partOffset, bytes32 key, bytes32 part, uint256 size) external {
30-
preimagePartOk[key][partOffset] = true;
31-
preimageParts[key][partOffset] = part;
32-
preimageLengths[key] = size;
33-
}
27+
function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset)
28+
external
29+
returns (bytes32 key_)
30+
{
31+
// Compute the localized key from the given local identifier.
32+
key_ = PreimageKeyLib.localizeIdent(_ident, _localContext);
33+
34+
// Revert if the given part offset is not within bounds.
35+
if (_partOffset > _size + 8 || _size > 32) {
36+
// Revert with "PartOffsetOOB()"
37+
assembly {
38+
// Store "PartOffsetOOB()"
39+
mstore(0, 0xfe254987)
40+
// Revert with "PartOffsetOOB()"
41+
revert(0x1c, 4)
42+
}
43+
// TODO: remove with revert PartOffsetOOB();
44+
}
3445

35-
// temporary method for localization. Will be removed to PreimageKeyLib.sol
36-
function localize(bytes32 _key, bytes32 _localContext) internal view returns (bytes32 localizedKey_) {
46+
// Prepare the local data part at the given offset
47+
bytes32 part;
3748
assembly {
38-
// Grab the current free memory pointer to restore later.
39-
let ptr := mload(0x40)
40-
// Store the local data key and caller next to each other in memory for hashing.
41-
mstore(0, _key)
42-
mstore(0x20, caller())
43-
mstore(0x40, _localContext)
44-
// Localize the key with the above `localize` operation.
45-
localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1))
46-
// Restore the free memory pointer.
47-
mstore(0x40, ptr)
49+
// Clean the memory in [0x20, 0x40)
50+
mstore(0x20, 0x00)
51+
52+
// Store the full local data in scratch space.
53+
mstore(0x00, shl(192, _size))
54+
mstore(0x08, _word)
55+
56+
// Prepare the local data part at the requested offset.
57+
part := mload(_partOffset)
4858
}
49-
}
5059

51-
// temporary method for localization. Will be removed to PreimageKeyLib.sol
52-
function cheatLocalKey(uint256 partOffset, bytes32 key, bytes32 part, uint256 size, bytes32 localContext) external {
53-
// sanity check key is local key using prefix
54-
require(uint8(key[0]) == 1, "must be used for local key");
55-
56-
bytes32 localizedKey = localize(key, localContext);
57-
preimagePartOk[localizedKey][partOffset] = true;
58-
preimageParts[localizedKey][partOffset] = part;
59-
preimageLengths[localizedKey] = size;
60+
// Store the first part with `_partOffset`.
61+
preimagePartOk[key_][_partOffset] = true;
62+
preimageParts[key_][_partOffset] = part;
63+
// Assign the length of the preimage at the localized key.
64+
preimageLengths[key_] = _size;
6065
}
6166

6267
// loadKeccak256PreimagePart prepares the pre-image to be read by keccak256 key,
@@ -95,4 +100,8 @@ contract PreimageOracle {
95100
preimageParts[key][_partOffset] = part;
96101
preimageLengths[key] = size;
97102
}
103+
104+
function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external {
105+
// TODO: fetch diff from cannon. Currently for only satisfying interface
106+
}
98107
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.15;
3+
4+
/// @title IPreimageOracle
5+
/// @notice Interface for a preimage oracle.
6+
interface IPreimageOracle {
7+
/// @notice Reads a preimage from the oracle.
8+
/// @param _key The key of the preimage to read.
9+
/// @param _offset The offset of the preimage to read.
10+
/// @return dat_ The preimage data.
11+
/// @return datLen_ The length of the preimage data.
12+
function readPreimage(bytes32 _key, uint256 _offset) external view returns (bytes32 dat_, uint256 datLen_);
13+
14+
/// @notice Loads of local data part into the preimage oracle.
15+
/// @param _ident The identifier of the local data.
16+
/// @param _localContext The local key context for the preimage oracle. Optionally, can be set as a constant
17+
/// if the caller only requires one set of local keys.
18+
/// @param _word The local data word.
19+
/// @param _size The number of bytes in `_word` to load.
20+
/// @param _partOffset The offset of the local data part to write to the oracle.
21+
/// @dev The local data parts are loaded into the preimage oracle under the context
22+
/// of the caller - no other account can write to the caller's context
23+
/// specific data.
24+
///
25+
/// There are 5 local data identifiers:
26+
/// ┌────────────┬────────────────────────┐
27+
/// │ Identifier │ Data │
28+
/// ├────────────┼────────────────────────┤
29+
/// │ 1 │ L1 Head Hash (bytes32) │
30+
/// │ 2 │ Output Root (bytes32) │
31+
/// │ 3 │ Root Claim (bytes32) │
32+
/// │ 4 │ L2 Block Number (u64) │
33+
/// │ 5 │ Chain ID (u64) │
34+
/// └────────────┴────────────────────────┘
35+
function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset)
36+
external
37+
returns (bytes32 key_);
38+
39+
/// @notice Prepares a preimage to be read by keccak256 key, starting at the given offset and up to 32 bytes
40+
/// (clipped at preimage length, if out of data).
41+
/// @param _partOffset The offset of the preimage to read.
42+
/// @param _preimage The preimage data.
43+
function loadKeccak256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external;
44+
45+
/// @notice Prepares a preimage to be read by sha256 key, starting at the given offset and up to 32 bytes
46+
/// (clipped at preimage length, if out of data).
47+
/// @param _partOffset The offset of the preimage to read.
48+
/// @param _preimage The preimage data.
49+
function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external;
50+
}

0 commit comments

Comments
 (0)