Commit 94a01d5b authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6373 from ethereum-optimism/clabby/ctb/local-preimage-key-support

feat(ctb): Local preimage key support
parents f1359221 4ce2ad0f
......@@ -27,6 +27,7 @@ var (
StepBytes4 = crypto.Keccak256([]byte("step(bytes,bytes)"))[:4]
CheatBytes4 = crypto.Keccak256([]byte("cheat(uint256,bytes32,bytes32,uint256)"))[:4]
LoadKeccak256PreimagePartBytes4 = crypto.Keccak256([]byte("loadKeccak256PreimagePart(uint256,bytes)"))[:4]
LoadLocalDataBytes4 = crypto.Keccak256([]byte("loadLocalData(uint256,bytes32,uint256,uint256)"))[:4]
)
// LoadContracts loads the Cannon contracts, from op-bindings package
......
......@@ -37,9 +37,12 @@ func testContractsSetup(t require.TestingT) (*Contracts, *Addresses) {
}
func SourceMapTracer(t *testing.T, contracts *Contracts, addrs *Addresses) vm.EVMLogger {
mipsSrcMap, err := contracts.MIPS.SourceMap([]string{"../../packages/contracts-bedrock/contracts/cannon/MIPS.sol"})
t.Fatal("TODO(clabby): The source map tracer is disabled until source IDs have been added to foundry artifacts.")
contractsDir := "../../packages/contracts-bedrock"
mipsSrcMap, err := contracts.MIPS.SourceMap([]string{path.Join(contractsDir, "src/cannon/MIPS.sol")})
require.NoError(t, err)
oracleSrcMap, err := contracts.Oracle.SourceMap([]string{"../../packages/contracts-bedrock/contracts/cannon/PreimageOracle.sol"})
oracleSrcMap, err := contracts.Oracle.SourceMap([]string{path.Join(contractsDir, "src/cannon/PreimageOracle.sol")})
require.NoError(t, err)
return srcmap.NewSourceMapTracer(map[common.Address]*srcmap.SourceMap{addrs.MIPS: mipsSrcMap, addrs.Oracle: oracleSrcMap}, os.Stdout)
......@@ -76,7 +79,7 @@ func (m *MIPSEVM) Step(t *testing.T, stepWitness *StepWitness) []byte {
t.Logf("reading preimage key %x at offset %d", stepWitness.PreimageKey, stepWitness.PreimageOffset)
poInput, err := stepWitness.EncodePreimageOracleInput()
require.NoError(t, err, "encode preimage oracle input")
_, leftOverGas, err := m.env.Call(vm.AccountRef(m.addrs.Sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0))
_, leftOverGas, err := m.env.Call(vm.AccountRef(sender), m.addrs.Oracle, poInput, startingGas, big.NewInt(0))
require.NoErrorf(t, err, "evm should not fail, took %d gas", startingGas-leftOverGas)
}
......
......@@ -49,18 +49,19 @@ func (wit *StepWitness) EncodePreimageOracleInput() ([]byte, error) {
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, CheatBytes4...)
input = append(input, uint32ToBytes32(wit.PreimageOffset)...)
input = append(input, LoadLocalDataBytes4...)
input = append(input, wit.PreimageKey[:]...)
preimagePart := wit.PreimageValue[8:]
var tmp [32]byte
copy(tmp[:], wit.PreimageValue[wit.PreimageOffset:])
copy(tmp[:], preimagePart)
input = append(input, tmp[:]...)
input = append(input, uint32ToBytes32(uint32(len(wit.PreimageValue))-8)...)
input = append(input, uint32ToBytes32(uint32(len(wit.PreimageValue)-8))...)
input = append(input, uint32ToBytes32(wit.PreimageOffset)...)
// Note: we can pad calldata to 32 byte multiple, but don't strictly have to
return input, nil
case preimage.Keccak256KeyType:
......
......@@ -30,8 +30,8 @@ var (
// AlphabetVMMetaData contains all meta data concerning the AlphabetVM contract.
var AlphabetVMMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"Claim\",\"name\":\"_absolutePrestate\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_stateData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"step\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"postState_\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x60a060405234801561001057600080fd5b5060405161030438038061030483398101604081905261002f91610037565b608052610050565b60006020828403121561004957600080fd5b5051919050565b60805161029a61006a6000396000605c015261029a6000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f8e0cb9614610030575b600080fd5b61004361003e366004610157565b610055565b60405190815260200160405180910390f35b60008060007f0000000000000000000000000000000000000000000000000000000000000000878760405161008b9291906101c3565b6040518091039020036100af57600091506100a8868801886101d3565b90506100ce565b6100bb868801886101ec565b9092509050816100ca8161023d565b9250505b816100da826001610275565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261012057600080fd5b50813567ffffffffffffffff81111561013857600080fd5b60208301915083602082850101111561015057600080fd5b9250929050565b6000806000806040858703121561016d57600080fd5b843567ffffffffffffffff8082111561018557600080fd5b6101918883890161010e565b909650945060208701359150808211156101aa57600080fd5b506101b78782880161010e565b95989497509550505050565b8183823760009101908152919050565b6000602082840312156101e557600080fd5b5035919050565b600080604083850312156101ff57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361026e5761026e61020e565b5060010190565b600082198211156102885761028861020e565b50019056fea164736f6c634300080f000a",
ABI: "[{\"inputs\":[{\"internalType\":\"Claim\",\"name\":\"_absolutePrestate\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"oracle\",\"outputs\":[{\"internalType\":\"contractIPreimageOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_stateData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"step\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"postState_\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x60a060405234801561001057600080fd5b5060405161039838038061039883398101604081905261002f9161007a565b608081905261003c610062565b600080546001600160a01b0319166001600160a01b039290921691909117905550610093565b6460016000f36000908152600580601b83f091505090565b60006020828403121561008c57600080fd5b5051919050565b6080516102eb6100ad600039600060ad01526102eb6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100986100933660046101a8565b6100a6565b60405190815260200161007c565b60008060007f000000000000000000000000000000000000000000000000000000000000000087876040516100dc929190610214565b60405180910390200361010057600091506100f986880188610224565b905061011f565b61010c8688018861023d565b90925090508161011b8161028e565b9250505b8161012b8260016102c6565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261017157600080fd5b50813567ffffffffffffffff81111561018957600080fd5b6020830191508360208285010111156101a157600080fd5b9250929050565b600080600080604085870312156101be57600080fd5b843567ffffffffffffffff808211156101d657600080fd5b6101e28883890161015f565b909650945060208701359150808211156101fb57600080fd5b506102088782880161015f565b95989497509550505050565b8183823760009101908152919050565b60006020828403121561023657600080fd5b5035919050565b6000806040838503121561025057600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102bf576102bf61025f565b5060010190565b600082198211156102d9576102d961025f565b50019056fea164736f6c634300080f000a",
}
// AlphabetVMABI is the input ABI used to generate the binding from.
......@@ -201,6 +201,37 @@ func (_AlphabetVM *AlphabetVMTransactorRaw) Transact(opts *bind.TransactOpts, me
return _AlphabetVM.Contract.contract.Transact(opts, method, params...)
}
// Oracle is a free data retrieval call binding the contract method 0x7dc0d1d0.
//
// Solidity: function oracle() view returns(address)
func (_AlphabetVM *AlphabetVMCaller) Oracle(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _AlphabetVM.contract.Call(opts, &out, "oracle")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// Oracle is a free data retrieval call binding the contract method 0x7dc0d1d0.
//
// Solidity: function oracle() view returns(address)
func (_AlphabetVM *AlphabetVMSession) Oracle() (common.Address, error) {
return _AlphabetVM.Contract.Oracle(&_AlphabetVM.CallOpts)
}
// Oracle is a free data retrieval call binding the contract method 0x7dc0d1d0.
//
// Solidity: function oracle() view returns(address)
func (_AlphabetVM *AlphabetVMCallerSession) Oracle() (common.Address, error) {
return _AlphabetVM.Contract.Oracle(&_AlphabetVM.CallOpts)
}
// Step is a free data retrieval call binding the contract method 0xf8e0cb96.
//
// Solidity: function step(bytes _stateData, bytes ) view returns(bytes32 postState_)
......
......@@ -9,11 +9,11 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc"
)
const AlphabetVMStorageLayoutJSON = "{\"storage\":null,\"types\":{}}"
const AlphabetVMStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"test/FaultDisputeGame.t.sol:AlphabetVM\",\"label\":\"oracle\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_contract(IPreimageOracle)1001\"}],\"types\":{\"t_contract(IPreimageOracle)1001\":{\"encoding\":\"inplace\",\"label\":\"contract IPreimageOracle\",\"numberOfBytes\":\"20\"}}}"
var AlphabetVMStorageLayout = new(solc.StorageLayout)
var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f8e0cb9614610030575b600080fd5b61004361003e366004610157565b610055565b60405190815260200160405180910390f35b60008060007f0000000000000000000000000000000000000000000000000000000000000000878760405161008b9291906101c3565b6040518091039020036100af57600091506100a8868801886101d3565b90506100ce565b6100bb868801886101ec565b9092509050816100ca8161023d565b9250505b816100da826001610275565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261012057600080fd5b50813567ffffffffffffffff81111561013857600080fd5b60208301915083602082850101111561015057600080fd5b9250929050565b6000806000806040858703121561016d57600080fd5b843567ffffffffffffffff8082111561018557600080fd5b6101918883890161010e565b909650945060208701359150808211156101aa57600080fd5b506101b78782880161010e565b95989497509550505050565b8183823760009101908152919050565b6000602082840312156101e557600080fd5b5035919050565b600080604083850312156101ff57600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361026e5761026e61020e565b5060010190565b600082198211156102885761028861020e565b50019056fea164736f6c634300080f000a"
var AlphabetVMDeployedBin = "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637dc0d1d01461003b578063f8e0cb9614610085575b600080fd5b60005461005b9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b6100986100933660046101a8565b6100a6565b60405190815260200161007c565b60008060007f000000000000000000000000000000000000000000000000000000000000000087876040516100dc929190610214565b60405180910390200361010057600091506100f986880188610224565b905061011f565b61010c8688018861023d565b90925090508161011b8161028e565b9250505b8161012b8260016102c6565b6040805160208101939093528201526060016040516020818303038152906040528051906020012092505050949350505050565b60008083601f84011261017157600080fd5b50813567ffffffffffffffff81111561018957600080fd5b6020830191508360208285010111156101a157600080fd5b9250929050565b600080600080604085870312156101be57600080fd5b843567ffffffffffffffff808211156101d657600080fd5b6101e28883890161015f565b909650945060208701359150808211156101fb57600080fd5b506102088782880161015f565b95989497509550505050565b8183823760009101908152919050565b60006020828403121561023657600080fd5b5035919050565b6000806040838503121561025057600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102bf576102bf61025f565b5060010190565b600082198211156102d9576102d961025f565b50019056fea164736f6c634300080f000a"
func init() {
if err := json.Unmarshal([]byte(AlphabetVMStorageLayoutJSON), AlphabetVMStorageLayout); err != nil {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
package srcmap
import (
"path"
"strings"
"testing"
......@@ -11,17 +12,24 @@ import (
)
func TestSourcemap(t *testing.T) {
sourcePath := "../../packages/contracts-bedrock/src/cannon/MIPS.sol"
t.Skip("TODO(clabby): This test is disabled until source IDs have been added to foundry artifacts.")
contractsDir := "../../packages/contracts-bedrock"
sources := []string{path.Join(contractsDir, "src/cannon/MIPS.sol")}
for i, source := range sources {
sources[i] = path.Join(contractsDir, source)
}
deployedByteCode := hexutil.MustDecode(bindings.MIPSDeployedBin)
srcMap, err := ParseSourceMap(
[]string{sourcePath},
sources,
deployedByteCode,
bindings.MIPSDeployedSourceMap)
require.NoError(t, err)
for i := 0; i < len(deployedByteCode); i++ {
info := srcMap.FormattedInfo(uint64(i))
if !strings.HasPrefix(info, "generated:") && !strings.HasPrefix(info, sourcePath) {
if strings.HasPrefix(info, "unknown") {
t.Fatalf("unexpected info: %q", info)
}
}
......
......@@ -30,10 +30,11 @@ func setupFaultDisputeGame() (common.Address, *bind.TransactOpts, *backends.Simu
_, _, contract, err := bindings.DeployFaultDisputeGame(
opts,
backend,
[32]byte{0x01},
big.NewInt(15),
[32]byte{0x01}, // Absolute Prestate Claim
big.NewInt(15), // Max Game Depth
uint64(604800), // 7 days
common.Address{0xdd},
common.Address{0xdd}, // VM
common.Address{0xee}, // L2OutputOracle (Not used in Alphabet Game)
)
if err != nil {
return common.Address{}, nil, nil, nil, err
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)
......@@ -50,7 +51,7 @@ func deployDisputeGameContracts(require *require.Assertions, ctx context.Context
require.NoError(err)
// Deploy the fault dispute game implementation
_, tx, _, err = bindings.DeployFaultDisputeGame(opts, client, alphabetVMAbsolutePrestateClaim, big.NewInt(alphabetGameDepth), gameDuration, alphaVMAddr)
_, tx, _, err = bindings.DeployFaultDisputeGame(opts, client, alphabetVMAbsolutePrestateClaim, big.NewInt(alphabetGameDepth), gameDuration, alphaVMAddr, common.Address{0xBE, 0xEF})
require.NoError(err)
faultDisputeGameAddr, err := bind.WaitDeployed(ctx, client, tx)
require.NoError(err)
......
......@@ -85,35 +85,35 @@ FaucetTest:test_nonAdmin_drip_fails() (gas: 262520)
FaucetTest:test_receive_succeeds() (gas: 17401)
FaucetTest:test_withdraw_nonAdmin_reverts() (gas: 13145)
FaucetTest:test_withdraw_succeeds() (gas: 78359)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 498839)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 505685)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 502382)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 505561)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 504878)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 497604)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 504450)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 501147)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 502326)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 501643)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 501957)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 508759)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 505478)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 508657)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 507974)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot1:test_resolvesCorrectly_succeeds() (gas: 500722)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 507524)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 504243)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 505422)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot5:test_resolvesCorrectly_succeeds() (gas: 504739)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17404)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17917)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17939)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10315)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8260)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17669)
FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 416029)
FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 26399)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17624)
FaultDisputeGame_Test:test_move_clockCorrectness_succeeds() (gas: 419180)
FaultDisputeGame_Test:test_move_clockTimeExceeded_reverts() (gas: 26421)
FaultDisputeGame_Test:test_move_defendRoot_reverts() (gas: 13360)
FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 103254)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 408148)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10968)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24655)
FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 107356)
FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 224820)
FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 104120)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 411546)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 11012)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24677)
FaultDisputeGame_Test:test_move_simpleAttack_succeeds() (gas: 108110)
FaultDisputeGame_Test:test_resolve_challengeContested_succeeds() (gas: 226511)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9657)
FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 109773)
FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 21434)
FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 27263)
FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 395502)
FaultDisputeGame_Test:test_resolve_rootContested_succeeds() (gas: 110642)
FaultDisputeGame_Test:test_resolve_rootUncontestedClockNotExpired_succeeds() (gas: 21437)
FaultDisputeGame_Test:test_resolve_rootUncontested_succeeds() (gas: 27288)
FaultDisputeGame_Test:test_resolve_teamDeathmatch_succeeds() (gas: 398859)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8225)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352113)
......@@ -437,9 +437,11 @@ OptimistTest:test_tokenURI_returnsCorrectTokenURI_succeeds() (gas: 195908)
OptimistTest:test_transferFrom_soulbound_reverts() (gas: 75512)
PostSherlockL1:test_script_succeeds() (gas: 3078)
PostSherlockL2:test_script_succeeds() (gas: 3078)
PreimageOracle_Test:test_computePreimageKey_succeeds() (gas: 6242)
PreimageOracle_Test:test_loadKeccak256PreimagePart_outOfBoundsOffset_reverts() (gas: 9005)
PreimageOracle_Test:test_loadKeccak256PreimagePart_succeeds() (gas: 77502)
PreimageOracle_Test:test_keccak256PreimageKey_succeeds() (gas: 319)
PreimageOracle_Test:test_loadKeccak256PreimagePart_outOfBoundsOffset_reverts() (gas: 8993)
PreimageOracle_Test:test_loadKeccak256PreimagePart_succeeds() (gas: 76098)
PreimageOracle_Test:test_loadLocalData_onePart_succeeds() (gas: 75840)
PreimageOracle_Test:test_loadLocalData_outOfBoundsOffset_reverts() (gas: 8803)
ProxyAdmin_Test:test_chugsplashChangeProxyAdmin_succeeds() (gas: 35586)
ProxyAdmin_Test:test_chugsplashGetProxyAdmin_succeeds() (gas: 15675)
ProxyAdmin_Test:test_chugsplashGetProxyImplementation_succeeds() (gas: 51084)
......
......@@ -698,7 +698,8 @@ contract Deploy is Deployer {
_absolutePrestate: absolutePrestate,
_maxGameDepth: cfg.faultGameMaxDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
_vm: faultVm
_vm: faultVm,
_l2oo: L2OutputOracle(mustGetAddress("L2OutputOracleProxy"))
}));
console.log("DisputeGameFactory: set `FaultDisputeGame` implementation");
}
......
......@@ -14,6 +14,7 @@
"src/L2/L2StandardBridge.sol": "0x8ee5257e03ae4ba8555d9f7d13374c8a388315d62c16107bb4cadd450bfeb3d3",
"src/L2/L2ToL1MessagePasser.sol": "0x7e35c3c4f1dd3d131dd71db07676301f7c477f02b6d6bf0ec468ecf2bed8325b",
"src/L2/SequencerFeeVault.sol": "0x17b30ccaed8b8dbe965c892cb8aae7f594fb4a87e0edd3ca6cd8f94559b86df9",
"src/dispute/FaultDisputeGame.sol": "0xb36f6456d74a9ee93df97bb6d5a554dcb531d730e6e2b10dd442897875f2ca81",
"src/legacy/DeployerWhitelist.sol": "0x47277d9c8409d517501d172db6697d55090d3d3a9e4bb2b1adea83471d793b6b",
"src/legacy/L1BlockNumber.sol": "0x1a1690b8b5ab53cf2b5c8e85fb86028b4078ae656286ae482cabe68374334f2a",
"src/legacy/LegacyMessagePasser.sol": "0xc7f42e6165507b4c50a5169a950f66602e6b4b8cff17f5d95994e121abb18390",
......
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma solidity 0.8.15;
import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol";
import { PreimageKeyLib } from "./PreimageKeyLib.sol";
/// @title MIPS
/// @notice The MIPS contract emulates a single MIPS instruction.
......@@ -57,11 +58,13 @@ contract MIPS {
/// @notice Extends the value leftwards with its most significant bit (sign extension).
function SE(uint32 _dat, uint32 _idx) internal pure returns (uint32) {
unchecked {
bool isSigned = (_dat >> (_idx - 1)) != 0;
uint256 signed = ((1 << (32 - _idx)) - 1) << _idx;
uint256 mask = (1 << _idx) - 1;
return uint32(_dat & mask | (isSigned ? signed : 0));
}
}
/// @notice Computes the hash of the MIPS state.
/// @return out_ The hash of the MIPS state.
......@@ -109,11 +112,11 @@ contract MIPS {
// Compute the hash of the resulting MIPS state
out_ := keccak256(start, sub(to, start))
}
return out_;
}
/// @notice Handles a syscall.
function handleSyscall() internal returns (bytes32 out_) {
unchecked {
// Load state from memory
State memory state;
assembly {
......@@ -168,7 +171,12 @@ contract MIPS {
else if (a0 == FD_PREIMAGE_READ) {
// verify proof 1 is correct, and get the existing memory.
uint32 mem = readMem(a1 & 0xFFffFFfc, 1); // mask the addr to align it to 4 bytes
(bytes32 dat, uint256 datLen) = oracle.readPreimage(state.preimageKey, state.preimageOffset);
bytes32 preimageKey = state.preimageKey;
// If the preimage key is a local key, localize it in the context of the caller.
if (uint8(preimageKey[0]) == 1) {
preimageKey = PreimageKeyLib.localize(preimageKey);
}
(bytes32 dat, uint256 datLen) = oracle.readPreimage(preimageKey, state.preimageOffset);
// Transform data for writing to memory
// We use assembly for more precise ops, and no var count limit
......@@ -264,6 +272,7 @@ contract MIPS {
out_ = outputState();
}
}
/// @notice Handles a branch instruction, updating the MIPS state PC where needed.
/// @param _opcode The opcode of the branch instruction.
......@@ -272,6 +281,7 @@ contract MIPS {
/// @param _rs The register to be compared with the branch register.
/// @return out_ The hashed MIPS state.
function handleBranch(uint32 _opcode, uint32 _insn, uint32 _rtReg, uint32 _rs) internal returns (bytes32 out_) {
unchecked {
// Load state from memory
State memory state;
assembly {
......@@ -322,6 +332,7 @@ contract MIPS {
// Return the hash of the resulting state
out_ = outputState();
}
}
/// @notice Handles HI and LO register instructions.
/// @param _func The function code of the instruction.
......@@ -330,6 +341,7 @@ contract MIPS {
/// @param _storeReg The register to store the result in.
/// @return out_ The hash of the resulting MIPS state.
function handleHiLo(uint32 _func, uint32 _rs, uint32 _rt, uint32 _storeReg) internal returns (bytes32 out_) {
unchecked {
// Load state from memory
State memory state;
assembly {
......@@ -393,12 +405,14 @@ contract MIPS {
// Return the hash of the resulting state
out_ = outputState();
}
}
/// @notice Handles a jump instruction, updating the MIPS state PC where needed.
/// @param _linkReg The register to store the link to the instruction after the delay slot instruction.
/// @param _dest The destination to jump to.
/// @return out_ The hashed MIPS state.
function handleJump(uint32 _linkReg, uint32 _dest) internal returns (bytes32 out_) {
unchecked {
// Load state from memory.
State memory state;
assembly {
......@@ -418,6 +432,7 @@ contract MIPS {
// Return the hash of the resulting state.
out_ = outputState();
}
}
/// @notice Handles a storing a value into a register.
/// @param _storeReg The register to store the value into.
......@@ -425,6 +440,7 @@ contract MIPS {
/// @param _conditional Whether or not the store is conditional.
/// @return out_ The hashed MIPS state.
function handleRd(uint32 _storeReg, uint32 _val, bool _conditional) internal returns (bytes32 out_) {
unchecked {
// Load state from memory.
State memory state;
assembly {
......@@ -446,11 +462,13 @@ contract MIPS {
// Return the hash of the resulting state.
out_ = outputState();
}
}
/// @notice Computes the offset of the proof in the calldata.
/// @param _proofIndex The index of the proof in the calldata.
/// @return offset_ The offset of the proof in the calldata.
function proofOffset(uint8 _proofIndex) internal pure returns (uint256 offset_) {
unchecked {
// A proof of 32 bit memory, with 32-byte leaf values, is (32-5)=27 bytes32 entries.
// And the leaf value itself needs to be encoded as well. And proof.offset == 358
offset_ = 358 + (uint256(_proofIndex) * (28 * 32));
......@@ -459,12 +477,14 @@ contract MIPS {
require(s >= (offset_ + 28 * 32), "check that there is enough calldata");
return offset_;
}
}
/// @notice Reads a 32-bit value from memory.
/// @param _addr The address to read from.
/// @param _proofIndex The index of the proof in the calldata.
/// @return out_ The hashed MIPS state.
function readMem(uint32 _addr, uint8 _proofIndex) internal pure returns (uint32 out_) {
unchecked {
// Compute the offset of the proof in the calldata.
uint256 offset = proofOffset(_proofIndex);
......@@ -514,6 +534,7 @@ contract MIPS {
out_ := and(shr(shamt, leaf), 0xFFffFFff)
}
}
}
/// @notice Writes a 32-bit value to memory.
/// This function first overwrites the part of the leaf.
......@@ -522,6 +543,7 @@ contract MIPS {
/// @param _proofIndex The index of the proof in the calldata.
/// @param _val The value to write.
function writeMem(uint32 _addr, uint8 _proofIndex, uint32 _val) internal pure {
unchecked {
// Compute the offset of the proof in the calldata.
uint256 offset = proofOffset(_proofIndex);
......@@ -565,10 +587,12 @@ contract MIPS {
mstore(0x80, node)
}
}
}
/// @notice Executes a single step of the vm.
/// Will revert if any required input state is missing.
function step(bytes calldata stateData, bytes calldata proof) public returns (bytes32) {
unchecked {
State memory state;
// Packed calldata is ~6 times smaller than state size
......@@ -728,9 +752,11 @@ contract MIPS {
// write back the value to destination register
return handleRd(rdReg, val, true);
}
}
/// @notice Execute an instruction.
function execute(uint32 insn, uint32 rs, uint32 rt, uint32 mem) internal pure returns (uint32) {
unchecked {
uint32 opcode = insn >> 26; // 6-bits
uint32 func = insn & 0x3f; // 6-bits
// TODO(CLI-4136): deref the immed into a register
......@@ -915,4 +941,5 @@ contract MIPS {
revert("invalid instruction");
}
}
}
// 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.
/// @return key_ The context-specific local key.
function localizeIdent(uint256 _ident) 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))))
}
key_ = localize(key_);
}
/// @notice Localizes a given local data key for the caller's context.
/// @dev The localization operation is defined as:
/// localize(k) = H(k .. sender) & ~(0xFF << 248) | (0x01 << 248)
/// where H is the Keccak-256 hash function.
/// @param _key The local data key to localize.
/// @return localizedKey_ The localized local data key.
function localize(bytes32 _key) internal view returns (bytes32 localizedKey_) {
assembly {
// Store the local data key and caller next to each other in memory for hashing.
mstore(0, _key)
mstore(0x20, caller())
// Localize the key with the above `localize` operation.
localizedKey_ := or(and(keccak256(0, 0x40), not(shl(248, 0xFF))), shl(248, 1))
}
}
/// @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))
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
pragma solidity 0.8.15;
import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol";
import { PreimageKeyLib } from "./PreimageKeyLib.sol";
import "./libraries/CannonErrors.sol";
/// @title PreimageOracle
/// @notice A contract for storing permissioned pre-images.
contract PreimageOracle {
contract PreimageOracle is IPreimageOracle {
/// @notice Mapping of pre-image keys to pre-image lengths.
mapping(bytes32 => uint256) public preimageLengths;
/// @notice Mapping of pre-image keys to pre-image parts.
mapping(bytes32 => mapping(uint256 => bytes32)) public preimageParts;
/// @notice Mapping of pre-image keys to pre-image part offsets.
mapping(bytes32 => mapping(uint256 => bool)) public preimagePartOk;
/// @notice Reads a pre-image from the oracle.
/// @param _key The key of the pre-image to read.
/// @param _offset The offset of the pre-image to read.
/// @return dat_ The pre-image data.
/// @return datLen_ The length of the pre-image data.
/// @inheritdoc IPreimageOracle
function readPreimage(bytes32 _key, uint256 _offset)
external
view
......@@ -37,11 +35,11 @@ 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.
/// 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,
......@@ -53,37 +51,43 @@ contract PreimageOracle {
preimageLengths[key] = size;
}
/// @notice Computes and returns the key for a pre-image.
/// @param _preimage The pre-image.
/// @return key_ The pre-image key.
function computePreimageKey(bytes calldata _preimage) external pure returns (bytes32 key_) {
uint256 size;
assembly {
size := calldataload(0x24)
// Leave slots 0x40 and 0x60 untouched,
// and everything after as scratch-memory.
let ptr := 0x80
/// @inheritdoc IPreimageOracle
function loadLocalData(
uint256 _ident,
bytes32 _word,
uint256 _size,
uint256 _partOffset
) external returns (bytes32 key_) {
// Compute the localized key from the given local identifier.
key_ = PreimageKeyLib.localizeIdent(_ident);
// Store size as a big-endian uint64 at the start of pre-image
mstore(ptr, shl(192, size))
ptr := add(ptr, 8)
// Revert if the given part offset is not within bounds.
if (_partOffset > _size + 8 || _size > 32) {
revert PartOffsetOOB();
}
// Copy preimage payload into memory so we can hash and read it.
calldatacopy(ptr, _preimage.offset, size)
// Prepare the local data part at the given offset
bytes32 part;
assembly {
// Clean the memory in [0x20, 0x40)
mstore(0x20, 0x00)
// Compute the pre-image keccak256 hash (aka the pre-image key)
let h := keccak256(ptr, size)
// Store the full local data in scratch space.
mstore(0x00, shl(192, _size))
mstore(0x08, _word)
// Mask out prefix byte, replace with type 2 byte
key_ := or(and(h, not(shl(248, 0xFF))), shl(248, 2))
// Prepare the local data part at the requested offset.
part := mload(_partOffset)
}
// 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;
}
/// @notice Prepares a pre-image to be read by keccak256 key, starting at
/// the given offset and up to 32 bytes (clipped at pre-image length, if out of data).
/// @param _partOffset The offset of the pre-image to read.
/// @param _preimage The preimage data.
/// @inheritdoc IPreimageOracle
function loadKeccak256PreimagePart(uint256 _partOffset, bytes calldata _preimage) external {
uint256 size;
bytes32 key;
......@@ -94,7 +98,10 @@ contract PreimageOracle {
// revert if part offset > size+8 (i.e. parts must be within bounds)
if gt(_partOffset, add(size, 8)) {
revert(0, 0)
// Store "PartOffsetOOB()"
mstore(0, 0xfe254987)
// Revert with "PartOffsetOOB()"
revert(0x1c, 4)
}
// we leave solidity slots 0x40 and 0x60 untouched,
// and everything after as scratch-memory.
......
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma solidity 0.8.15;
/// @title IPreimageOracle
/// @notice Interface for a preimage oracle.
......@@ -14,10 +14,31 @@ interface IPreimageOracle {
view
returns (bytes32 dat_, uint256 datLen_);
/// @notice Computes and returns the key for a pre-image.
/// @param _preimage The pre-image.
/// @return key_ The pre-image key.
function computePreimageKey(bytes calldata _preimage) external pure returns (bytes32 key_);
/// @notice Loads of local data part into the preimage oracle.
/// @param _ident The identifier of the local data.
/// @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 _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).
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
/// @notice Thrown when a passed part offset is out of bounds.
error PartOffsetOOB();
......@@ -5,9 +5,11 @@ import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBigStepper } from "./interfaces/IBigStepper.sol";
import { IBigStepper, IPreimageOracle } from "./interfaces/IBigStepper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { Clone } from "../libraries/Clone.sol";
import { Types } from "../libraries/Types.sol";
import { Semver } from "../universal/Semver.sol";
import { LibHashing } from "./lib/LibHashing.sol";
import { LibPosition } from "./lib/LibPosition.sol";
......@@ -33,9 +35,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @notice The duration of the game.
Duration public immutable GAME_DURATION;
/// @notice A hypervisor that performs single instruction steps on a fault proof program trace.
/// @notice An onchain VM that performs single instruction steps on a fault proof program trace.
IBigStepper public immutable VM;
/// @notice The trusted L2OutputOracle contract.
L2OutputOracle public immutable L2_OUTPUT_ORACLE;
/// @notice The root claim's position is always at gindex 1.
Position internal constant ROOT_POSITION = Position.wrap(1);
......@@ -48,6 +53,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @inheritdoc IDisputeGame
IBondManager public bondManager;
/// @inheritdoc IFaultDisputeGame
Hash public l1Head;
/// @notice An append-only array of all claims made during the dispute game.
ClaimData[] public claimData;
......@@ -55,16 +63,24 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
mapping(ClaimHash => bool) internal claims;
/// @param _absolutePrestate The absolute prestate of the instruction trace.
/// @param _maxGameDepth The maximum depth of bisection.
/// @param _gameDuration The duration of the game.
/// @param _vm An onchain VM that performs single instruction steps on a fault proof program
/// trace.
/// @param _l2oo The trusted L2OutputOracle contract.
/// @custom:semver 0.0.4
constructor(
Claim _absolutePrestate,
uint256 _maxGameDepth,
Duration _gameDuration,
IBigStepper _vm
) Semver(0, 0, 3) {
IBigStepper _vm,
L2OutputOracle _l2oo
) Semver(0, 0, 4) {
ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth;
GAME_DURATION = _gameDuration;
VM = _vm;
L2_OUTPUT_ORACLE = _l2oo;
}
////////////////////////////////////////////////////////////////
......@@ -241,6 +257,38 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
move(_parentIndex, _claim, false);
}
/// @inheritdoc IFaultDisputeGame
function addLocalData(uint256 _ident, uint256 _partOffset) external {
// INVARIANT: Local data can only be added if the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();
IPreimageOracle oracle = VM.oracle();
if (_ident == 1) {
// Load the L1 head hash into the game's local context in the preimage oracle.
oracle.loadLocalData(_ident, Hash.unwrap(l1Head), 32, _partOffset);
} else if (_ident == 2) {
// Load the earliest output root that commits to the passed L2 block number
// into the game's local context in the preimage oracle.
Types.OutputProposal memory proposal = L2_OUTPUT_ORACLE.getL2OutputAfter(
l2BlockNumber()
);
oracle.loadLocalData(_ident, proposal.outputRoot, 32, _partOffset);
} else if (_ident == 3) {
// Load the root claim into the game's local context in the preimage oracle.
oracle.loadLocalData(_ident, Claim.unwrap(rootClaim()), 32, _partOffset);
} else if (_ident == 4) {
// Load the L2 block number into the game's local context in the preimage oracle.
// The L2 block number is stored as a big-endian uint64 in the upper 8 bytes of the
// passed word.
oracle.loadLocalData(_ident, bytes32(l2BlockNumber() << 192), 8, _partOffset);
} else if (_ident == 5) {
// Load the chain ID into the game's local context in the preimage oracle.
// The chain ID is stored as a big-endian uint64 in the upper 8 bytes of the
// passed word.
oracle.loadLocalData(_ident, bytes32(block.chainid << 192), 8, _partOffset);
}
}
/// @inheritdoc IFaultDisputeGame
function l2BlockNumber() public pure returns (uint256 l2BlockNumber_) {
l2BlockNumber_ = _getArgUint256(0x20);
......@@ -337,8 +385,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @inheritdoc IDisputeGame
function extraData() public pure returns (bytes memory extraData_) {
// The extra data starts at the second word within the cwia calldata.
// TODO: What data do we need to pass along to this contract from the factory?
// Block hash, preimage data, etc.?
extraData_ = _getArgDynBytes(0x20, 0x20);
}
......@@ -378,6 +424,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
countered: false
})
);
// Set the L1 head hash at the time of the game's creation.
l1Head = Hash.wrap(blockhash(block.number - 1));
}
/// @notice Returns the length of the `claimData` array.
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { IPreimageOracle } from "../../cannon/interfaces/IPreimageOracle.sol";
/// @title IBigStepper
/// @notice An interface for a contract with a state transition function that
/// will accept a pre state and return a post state.
......@@ -31,4 +33,7 @@ interface IBigStepper {
function step(bytes calldata _stateData, bytes calldata _proof)
external
returns (bytes32 postState_);
/// @notice Returns the preimage oracle used by the stepper.
function oracle() external view returns (IPreimageOracle oracle_);
}
......@@ -22,19 +22,18 @@ interface IDisputeGame is IInitializable {
function status() external view returns (GameStatus status_);
/// @notice Getter for the game type.
/// @dev `clones-with-immutable-args` argument #1
/// @dev The reference impl should be entirely different depending on the type (fault, validity)
/// i.e. The game type should indicate the security model.
/// @return gameType_ The type of proof system being used.
function gameType() external pure returns (GameType gameType_);
/// @notice Getter for the root claim.
/// @dev `clones-with-immutable-args` argument #2
/// @dev `clones-with-immutable-args` argument #1
/// @return rootClaim_ The root claim of the DisputeGame.
function rootClaim() external pure returns (Claim rootClaim_);
/// @notice Getter for the extra data.
/// @dev `clones-with-immutable-args` argument #3
/// @dev `clones-with-immutable-args` argument #2
/// @return extraData_ Any extra data supplied to the dispute game contract by the creator.
function extraData() external pure returns (bytes memory extraData_);
......
......@@ -53,6 +53,14 @@ interface IFaultDisputeGame is IDisputeGame {
bytes calldata _proof
) external;
/// @notice Posts the requested local data to the VM's `PreimageOralce`.
/// @param _ident The local identifier of the data to post.
/// @param _partOffset The offset of the data to post.
function addLocalData(uint256 _ident, uint256 _partOffset) external;
/// @notice Returns the L1 block hash at the time of the game's creation.
function l1Head() external view returns (Hash l1Head_);
/// @notice The l2BlockNumber that the `rootClaim` commits to. The trace being bisected within
/// the game is from `l2BlockNumber - 1` -> `l2BlockNumber`.
/// @return l2BlockNumber_ The l2BlockNumber that the `rootClaim` commits to.
......
......@@ -64,6 +64,7 @@ contract AssetReceiver is Transactor {
function withdrawETH(address payable _to, uint256 _amount) public onlyOwner {
// slither-disable-next-line reentrancy-unlimited-gas
(bool success, ) = _to.call{ value: _amount }("");
success; // Suppress warning; We ignore the low-level call result.
emit WithdrewETH(msg.sender, _to, _amount);
}
......
......@@ -4,14 +4,15 @@ pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol";
import { Vm } from "forge-std/Vm.sol";
import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "../src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "../src/dispute/FaultDisputeGame.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import "../src/libraries/DisputeTypes.sol";
import "../src/libraries/DisputeErrors.sol";
import { LibClock } from "../src/dispute/lib/LibClock.sol";
import { LibPosition } from "../src/dispute/lib/LibPosition.sol";
import { IBigStepper } from "../src/dispute/interfaces/IBigStepper.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";
import { LibClock } from "src/dispute/lib/LibClock.sol";
import { LibPosition } from "src/dispute/lib/LibPosition.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
contract FaultDisputeGame_Init is DisputeGameFactory_Init {
/// @dev The extra data passed to the game for initialization.
......@@ -28,12 +29,14 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
function init(Claim rootClaim, Claim absolutePrestate) public {
super.setUp();
// Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame(
absolutePrestate,
4,
Duration.wrap(7 days),
new AlphabetVM(absolutePrestate)
new AlphabetVM(absolutePrestate),
L2OutputOracle(deployNoop())
);
// Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl);
......@@ -889,9 +892,11 @@ contract VariableDivergentPlayer is GamePlayer {
contract AlphabetVM is IBigStepper {
Claim internal immutable ABSOLUTE_PRESTATE;
IPreimageOracle public oracle;
constructor(Claim _absolutePrestate) {
ABSOLUTE_PRESTATE = _absolutePrestate;
oracle = IPreimageOracle(deployNoop());
}
/// @inheritdoc IBigStepper
......@@ -915,3 +920,16 @@ contract AlphabetVM is IBigStepper {
postState_ = keccak256(abi.encode(traceIndex, claim + 1));
}
}
////////////////////////////////////////////////////////////////
// HELPERS //
////////////////////////////////////////////////////////////////
/// @notice Deploys a noop contract.
function deployNoop() returns (address noop_) {
assembly {
mstore(0x00, 0x60016000F3)
let size := 5
noop_ := create(0, sub(0x20, size), size)
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { PreimageOracle } from "../src/cannon/PreimageOracle.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { PreimageKeyLib } from "src/cannon/PreimageKeyLib.sol";
import "src/cannon/libraries/CannonErrors.sol";
contract PreimageOracle_Test is Test {
PreimageOracle oracle;
......@@ -15,18 +17,76 @@ contract PreimageOracle_Test is Test {
}
/// @notice Test the pre-image key computation with a known pre-image.
function test_computePreimageKey_succeeds() public {
function test_keccak256PreimageKey_succeeds() public {
bytes memory preimage = hex"deadbeef";
bytes32 key = oracle.computePreimageKey(preimage);
bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage);
bytes32 known = 0x02fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1;
assertEq(key, known);
}
/// @notice Tests that context-specific data [0, 24] bytes in length can be loaded correctly.
function test_loadLocalData_onePart_succeeds() public {
uint256 ident = 1;
bytes32 word = bytes32(uint256(0xdeadbeef) << 224);
uint8 size = 4;
uint8 partOffset = 0;
// Load the local data into the preimage oracle under the test contract's context.
bytes32 contextKey = oracle.loadLocalData(ident, word, size, partOffset);
// Validate that the pre-image part is set
bool ok = oracle.preimagePartOk(contextKey, partOffset);
assertTrue(ok);
// Validate the local data part
bytes32 expectedPart = 0x0000000000000004deadbeef0000000000000000000000000000000000000000;
assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
// Validate the local data length
uint256 length = oracle.preimageLengths(contextKey);
assertEq(length, size);
}
/// @notice Tests that context-specific data [0, 32] bytes in length can be loaded correctly.
function testFuzz_loadLocalData_varyingLength_succeeds(
uint256 ident,
bytes32 word,
uint256 size,
uint256 partOffset
) public {
// Bound the size to [0, 32]
size = bound(size, 0, 32);
// Bound the part offset to [0, size + 8]
partOffset = bound(partOffset, 0, size + 8);
// Load the local data into the preimage oracle under the test contract's context.
bytes32 contextKey = oracle.loadLocalData(ident, word, uint8(size), uint8(partOffset));
// Validate that the first local data part is set
bool ok = oracle.preimagePartOk(contextKey, partOffset);
assertTrue(ok);
// Validate the first local data part
bytes32 expectedPart;
assembly {
mstore(0x20, 0x00)
mstore(0x00, shl(192, size))
mstore(0x08, word)
expectedPart := mload(partOffset)
}
assertEq(oracle.preimageParts(contextKey, partOffset), expectedPart);
// Validate the local data length
uint256 length = oracle.preimageLengths(contextKey);
assertEq(length, size);
}
/// @notice Tests that a pre-image is correctly set.
function test_loadKeccak256PreimagePart_succeeds() public {
// Set the pre-image
bytes memory preimage = hex"deadbeef";
bytes32 key = oracle.computePreimageKey(preimage);
bytes32 key = PreimageKeyLib.keccak256PreimageKey(preimage);
uint256 offset = 0;
oracle.loadKeccak256PreimagePart(offset, preimage);
......@@ -44,12 +104,21 @@ contract PreimageOracle_Test is Test {
assertTrue(ok);
}
/// @notice Tests that a pre-image cannot be set with an out-of-bounds offset.
function test_loadLocalData_outOfBoundsOffset_reverts() public {
bytes32 preimage = bytes32(uint256(0xdeadbeef));
uint256 offset = preimage.length + 9;
vm.expectRevert(PartOffsetOOB.selector);
oracle.loadLocalData(1, preimage, 32, offset);
}
/// @notice Tests that a pre-image cannot be set with an out-of-bounds offset.
function test_loadKeccak256PreimagePart_outOfBoundsOffset_reverts() public {
bytes memory preimage = hex"deadbeef";
uint256 offset = preimage.length + 9;
vm.expectRevert();
vm.expectRevert(PartOffsetOOB.selector);
oracle.loadKeccak256PreimagePart(offset, preimage);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment