Skip to content

Commit

Permalink
Merge pull request #10 from ethereum-optimism/tip/pcw109550/local-pre…
Browse files Browse the repository at this point in the history
…image-support

feat: Local Preimage Support
  • Loading branch information
pcw109550 authored Feb 13, 2024
2 parents 8af5ae9 + d3ff8bd commit 4e0e36a
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 45 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: AsteriscCI
on: [push]

jobs:
go-tests:
rvgo-tests:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand All @@ -23,6 +23,18 @@ jobs:
- name: Run tests
run: go test -v ./...
working-directory: rvgo
rvsol-tests:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Run foundry tests
run: forge test -vvv
working-directory: rvsol

# go-lint:
# runs-on: ubuntu-latest
# timeout-minutes: 20
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "rvsol/lib/forge-std"]
path = rvsol/lib/forge-std
url = https://github.com/foundry-rs/forge-std
3 changes: 1 addition & 2 deletions rvgo/fast/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import "github.com/ethereum/go-ethereum/crypto"

var (
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes,bytes32)"))[:4]
CheatBytes4 = crypto.Keccak256([]byte("cheat(uint256,bytes32,bytes32,uint256)"))[:4]
CheatLocalKeyBytes4 = crypto.Keccak256([]byte("cheatLocalKey(uint256,bytes32,bytes32,uint256,bytes32)"))[:4]
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4]
LoadLocalDataBytes4 = crypto.Keccak256([]byte("loadLocalData(uint256,bytes32,bytes32,uint256,uint256)"))[:4]
)
19 changes: 10 additions & 9 deletions rvgo/fast/witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ func (wit *StepWitness) EncodePreimageOracleInput(localContext LocalContext) ([]

switch preimage.KeyType(wit.PreimageKey[0]) {
case preimage.LocalKeyType:
// We have no on-chain form of preparing the bootstrap pre-images onchain yet.
// So instead we cheat them in.
// In production usage there should be an on-chain contract that exposes this,
// rather than going through the global keccak256 oracle.
if len(wit.PreimageValue) > 32+8 {
return nil, fmt.Errorf("local pre-image exceeds maximum size of 32 bytes with key 0x%x", wit.PreimageKey)
}
var input []byte
input = append(input, CheatLocalKeyBytes4...)
input = append(input, uint64ToBytes32(wit.PreimageOffset)...)
input = append(input, LoadLocalDataBytes4...)
input = append(input, wit.PreimageKey[:]...)
input = append(input, common.Hash(localContext).Bytes()...)

preimagePart := wit.PreimageValue[8:]
var tmp [32]byte
copy(tmp[:], wit.PreimageValue[wit.PreimageOffset:])
copy(tmp[:], preimagePart)
input = append(input, tmp[:]...)
input = append(input, uint64ToBytes32(uint64(len(wit.PreimageValue))-8)...)
input = append(input, common.Hash(localContext).Bytes()...)
input = append(input, uint64ToBytes32(uint64(len(wit.PreimageValue)-8))...)
input = append(input, uint64ToBytes32(wit.PreimageOffset)...)
// Note: we can pad calldata to 32 byte multiple, but don't strictly have to
return input, nil
case preimage.Keccak256KeyType:
Expand Down
1 change: 1 addition & 0 deletions rvsol/lib/forge-std
Submodule forge-std added at ae570f
59 changes: 59 additions & 0 deletions rvsol/src/PreimageKeyLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

/// @title PreimageKeyLib
/// @notice Shared utilities for localizing local keys in the preimage oracle.
library PreimageKeyLib {
/// @notice Generates a context-specific local key for the given local data identifier.
/// @dev See `localize` for a description of the localization operation.
/// @param _ident The identifier of the local data. [0, 32) bytes in size.
/// @param _localContext The local context for the key.
/// @return key_ The context-specific local key.
function localizeIdent(uint256 _ident, bytes32 _localContext) internal view returns (bytes32 key_) {
assembly {
// Set the type byte in the given identifier to `1` (Local). We only care about
// the [1, 32) bytes in this value.
key_ := or(shl(248, 1), and(_ident, not(shl(248, 0xFF))))
}
// Localize the key with the given local context.
key_ = localize(key_, _localContext);
}

/// @notice Localizes a given local data key for the caller's context.
/// @dev The localization operation is defined as:
/// localize(k) = H(k .. sender .. local_context) & ~(0xFF << 248) | (0x01 << 248)
/// where H is the Keccak-256 hash function.
/// @param _key The local data key to localize.
/// @param _localContext The local context for the key.
/// @return localizedKey_ The localized local data key.
function localize(bytes32 _key, bytes32 _localContext) internal view returns (bytes32 localizedKey_) {
assembly {
// Grab the current free memory pointer to restore later.
let ptr := mload(0x40)
// Store the local data key and caller next to each other in memory for hashing.
mstore(0, _key)
mstore(0x20, caller())
mstore(0x40, _localContext)
// Localize the key with the above `localize` operation.
localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1))
// Restore the free memory pointer.
mstore(0x40, ptr)
}
}

/// @notice Computes and returns the key for a global keccak pre-image.
/// @param _preimage The pre-image.
/// @return key_ The pre-image key.
function keccak256PreimageKey(bytes memory _preimage) internal pure returns (bytes32 key_) {
assembly {
// Grab the size of the `_preimage`
let size := mload(_preimage)

// Compute the pre-image keccak256 hash (aka the pre-image key)
let h := keccak256(add(_preimage, 0x20), size)

// Mask out prefix byte, replace with type 2 byte
key_ := or(and(h, not(shl(248, 0xFF))), shl(248, 2))
}
}
}
75 changes: 42 additions & 33 deletions rvsol/src/PreimageOracle.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

contract PreimageOracle {
import {IPreimageOracle} from "./interfaces/IPreimageOracle.sol";
import {PreimageKeyLib} from "./PreimageKeyLib.sol";

contract PreimageOracle is IPreimageOracle {
mapping(bytes32 => uint256) public preimageLengths;
mapping(bytes32 => mapping(uint256 => bytes32)) public preimageParts;
mapping(bytes32 => mapping(uint256 => bool)) public preimagePartOk;
Expand All @@ -21,42 +24,44 @@ contract PreimageOracle {
dat_ = preimageParts[_key][_offset];
}

// TODO(CLI-4104):
// we need to mix-in the ID of the dispute for local-type keys to avoid collisions,
// and restrict local pre-image insertion to the dispute-managing contract.
// For now we permit anyone to write any pre-image unchecked, to make testing easy.
// This method is DANGEROUS. And NOT FOR PRODUCTION.
function cheat(uint256 partOffset, bytes32 key, bytes32 part, uint256 size) external {
preimagePartOk[key][partOffset] = true;
preimageParts[key][partOffset] = part;
preimageLengths[key] = size;
}
function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset)
external
returns (bytes32 key_)
{
// Compute the localized key from the given local identifier.
key_ = PreimageKeyLib.localizeIdent(_ident, _localContext);

// Revert if the given part offset is not within bounds.
if (_partOffset > _size + 8 || _size > 32) {
// Revert with "PartOffsetOOB()"
assembly {
// Store "PartOffsetOOB()"
mstore(0, 0xfe254987)
// Revert with "PartOffsetOOB()"
revert(0x1c, 4)
}
// TODO: remove with revert PartOffsetOOB();
}

// temporary method for localization. Will be removed to PreimageKeyLib.sol
function localize(bytes32 _key, bytes32 _localContext) internal view returns (bytes32 localizedKey_) {
// Prepare the local data part at the given offset
bytes32 part;
assembly {
// Grab the current free memory pointer to restore later.
let ptr := mload(0x40)
// Store the local data key and caller next to each other in memory for hashing.
mstore(0, _key)
mstore(0x20, caller())
mstore(0x40, _localContext)
// Localize the key with the above `localize` operation.
localizedKey_ := or(and(keccak256(0, 0x60), not(shl(248, 0xFF))), shl(248, 1))
// Restore the free memory pointer.
mstore(0x40, ptr)
// Clean the memory in [0x20, 0x40)
mstore(0x20, 0x00)

// Store the full local data in scratch space.
mstore(0x00, shl(192, _size))
mstore(0x08, _word)

// Prepare the local data part at the requested offset.
part := mload(_partOffset)
}
}

// temporary method for localization. Will be removed to PreimageKeyLib.sol
function cheatLocalKey(uint256 partOffset, bytes32 key, bytes32 part, uint256 size, bytes32 localContext) external {
// sanity check key is local key using prefix
require(uint8(key[0]) == 1, "must be used for local key");

bytes32 localizedKey = localize(key, localContext);
preimagePartOk[localizedKey][partOffset] = true;
preimageParts[localizedKey][partOffset] = part;
preimageLengths[localizedKey] = size;
// Store the first part with `_partOffset`.
preimagePartOk[key_][_partOffset] = true;
preimageParts[key_][_partOffset] = part;
// Assign the length of the preimage at the localized key.
preimageLengths[key_] = _size;
}

// loadKeccak256PreimagePart prepares the pre-image to be read by keccak256 key,
Expand Down Expand Up @@ -95,4 +100,8 @@ contract PreimageOracle {
preimageParts[key][_partOffset] = part;
preimageLengths[key] = size;
}

function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external {
// TODO: fetch diff from cannon. Currently for only satisfying interface
}
}
50 changes: 50 additions & 0 deletions rvsol/src/interfaces/IPreimageOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

/// @title IPreimageOracle
/// @notice Interface for a preimage oracle.
interface IPreimageOracle {
/// @notice Reads a preimage from the oracle.
/// @param _key The key of the preimage to read.
/// @param _offset The offset of the preimage to read.
/// @return dat_ The preimage data.
/// @return datLen_ The length of the preimage data.
function readPreimage(bytes32 _key, uint256 _offset) external view returns (bytes32 dat_, uint256 datLen_);

/// @notice Loads of local data part into the preimage oracle.
/// @param _ident The identifier of the local data.
/// @param _localContext The local key context for the preimage oracle. Optionally, can be set as a constant
/// if the caller only requires one set of local keys.
/// @param _word The local data word.
/// @param _size The number of bytes in `_word` to load.
/// @param _partOffset The offset of the local data part to write to the oracle.
/// @dev The local data parts are loaded into the preimage oracle under the context
/// of the caller - no other account can write to the caller's context
/// specific data.
///
/// There are 5 local data identifiers:
/// ┌────────────┬────────────────────────┐
/// │ Identifier │ Data │
/// ├────────────┼────────────────────────┤
/// │ 1 │ L1 Head Hash (bytes32) │
/// │ 2 │ Output Root (bytes32) │
/// │ 3 │ Root Claim (bytes32) │
/// │ 4 │ L2 Block Number (u64) │
/// │ 5 │ Chain ID (u64) │
/// └────────────┴────────────────────────┘
function loadLocalData(uint256 _ident, bytes32 _localContext, bytes32 _word, uint256 _size, uint256 _partOffset)
external
returns (bytes32 key_);

/// @notice Prepares a preimage to be read by keccak256 key, starting at the given offset and up to 32 bytes
/// (clipped at preimage length, if out of data).
/// @param _partOffset The offset of the preimage to read.
/// @param _preimage The preimage data.
function loadKeccak256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external;

/// @notice Prepares a preimage to be read by sha256 key, starting at the given offset and up to 32 bytes
/// (clipped at preimage length, if out of data).
/// @param _partOffset The offset of the preimage to read.
/// @param _preimage The preimage data.
function loadSha256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external;
}
Loading

0 comments on commit 4e0e36a

Please sign in to comment.