Commit d27f4695 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Separate concepts of InputData and Leaf (#9143)

A Leaf must always be 136 bytes of data and includes keccak padding. InputData does not include the keccak padding.
parent 22b5a2db
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix" "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -33,23 +33,12 @@ type PreimageOracleContract struct { ...@@ -33,23 +33,12 @@ type PreimageOracleContract struct {
contract *batching.BoundContract contract *batching.BoundContract
} }
// Leaf is the keccak state matrix added to the large preimage merkle tree.
type Leaf struct {
// Input is the data absorbed for the block, exactly 136 bytes
Input [136]byte
// Index of the block in the absorption process
Index *big.Int
// StateCommitment is the hash of the internal state after absorbing the input.
StateCommitment common.Hash
}
// toPreimageOracleLeaf converts a Leaf to the contract [bindings.PreimageOracleLeaf] type. // toPreimageOracleLeaf converts a Leaf to the contract [bindings.PreimageOracleLeaf] type.
func (l Leaf) toPreimageOracleLeaf() bindings.PreimageOracleLeaf { func toPreimageOracleLeaf(l keccakTypes.Leaf) bindings.PreimageOracleLeaf {
commitment := ([32]byte)(l.StateCommitment.Bytes())
return bindings.PreimageOracleLeaf{ return bindings.PreimageOracleLeaf{
Input: l.Input[:], Input: l.Input[:],
Index: l.Index, Index: l.Index,
StateCommitment: commitment, StateCommitment: l.StateCommitment,
} }
} }
...@@ -98,7 +87,7 @@ func (c *PreimageOracleContract) InitLargePreimage(uuid *big.Int, partOffset uin ...@@ -98,7 +87,7 @@ func (c *PreimageOracleContract) InitLargePreimage(uuid *big.Int, partOffset uin
return call.ToTxCandidate() return call.ToTxCandidate()
} }
func (c *PreimageOracleContract) AddLeaves(uuid *big.Int, input []byte, commitments [][32]byte, finalize bool) (txmgr.TxCandidate, error) { func (c *PreimageOracleContract) AddLeaves(uuid *big.Int, input []byte, commitments []common.Hash, finalize bool) (txmgr.TxCandidate, error) {
call := c.contract.Call(methodAddLeavesLPP, uuid, input, commitments, finalize) call := c.contract.Call(methodAddLeavesLPP, uuid, input, commitments, finalize)
return call.ToTxCandidate() return call.ToTxCandidate()
} }
...@@ -107,9 +96,9 @@ func (c *PreimageOracleContract) Squeeze( ...@@ -107,9 +96,9 @@ func (c *PreimageOracleContract) Squeeze(
claimant common.Address, claimant common.Address,
uuid *big.Int, uuid *big.Int,
stateMatrix *matrix.StateMatrix, stateMatrix *matrix.StateMatrix,
preState Leaf, preState keccakTypes.Leaf,
preStateProof MerkleProof, preStateProof MerkleProof,
postState Leaf, postState keccakTypes.Leaf,
postStateProof MerkleProof, postStateProof MerkleProof,
) (txmgr.TxCandidate, error) { ) (txmgr.TxCandidate, error) {
call := c.contract.Call( call := c.contract.Call(
...@@ -117,9 +106,9 @@ func (c *PreimageOracleContract) Squeeze( ...@@ -117,9 +106,9 @@ func (c *PreimageOracleContract) Squeeze(
claimant, claimant,
uuid, uuid,
abiEncodeStateMatrix(stateMatrix), abiEncodeStateMatrix(stateMatrix),
preState.toPreimageOracleLeaf(), toPreimageOracleLeaf(preState),
preStateProof.toSized(), preStateProof.toSized(),
postState.toPreimageOracleLeaf(), toPreimageOracleLeaf(postState),
postStateProof.toSized(), postStateProof.toSized(),
) )
return call.ToTxCandidate() return call.ToTxCandidate()
...@@ -136,7 +125,7 @@ func abiEncodeStateMatrix(stateMatrix *matrix.StateMatrix) bindings.LibKeccakSta ...@@ -136,7 +125,7 @@ func abiEncodeStateMatrix(stateMatrix *matrix.StateMatrix) bindings.LibKeccakSta
return bindings.LibKeccakStateMatrix{State: *stateSlice} return bindings.LibKeccakStateMatrix{State: *stateSlice}
} }
func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]gameTypes.LargePreimageMetaData, error) { func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]keccakTypes.LargePreimageMetaData, error) {
block := batching.BlockByHash(blockHash) block := batching.BlockByHash(blockHash)
results, err := batching.ReadArray(ctx, c.multiCaller, block, c.contract.Call(methodProposalCount), func(i *big.Int) *batching.ContractCall { results, err := batching.ReadArray(ctx, c.multiCaller, block, c.contract.Call(methodProposalCount), func(i *big.Int) *batching.ContractCall {
return c.contract.Call(methodProposals, i) return c.contract.Call(methodProposals, i)
...@@ -145,7 +134,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa ...@@ -145,7 +134,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa
return nil, fmt.Errorf("failed to load claims: %w", err) return nil, fmt.Errorf("failed to load claims: %w", err)
} }
var idents []gameTypes.LargePreimageIdent var idents []keccakTypes.LargePreimageIdent
for _, result := range results { for _, result := range results {
idents = append(idents, c.decodePreimageIdent(result)) idents = append(idents, c.decodePreimageIdent(result))
} }
...@@ -153,7 +142,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa ...@@ -153,7 +142,7 @@ func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHa
return c.GetProposalMetadata(ctx, block, idents...) return c.GetProposalMetadata(ctx, block, idents...)
} }
func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block batching.Block, idents ...gameTypes.LargePreimageIdent) ([]gameTypes.LargePreimageMetaData, error) { func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block batching.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error) {
var calls []*batching.ContractCall var calls []*batching.ContractCall
for _, ident := range idents { for _, ident := range idents {
calls = append(calls, c.contract.Call(methodProposalMetadata, ident.Claimant, ident.UUID)) calls = append(calls, c.contract.Call(methodProposalMetadata, ident.Claimant, ident.UUID))
...@@ -162,10 +151,10 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block ...@@ -162,10 +151,10 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load proposal metadata: %w", err) return nil, fmt.Errorf("failed to load proposal metadata: %w", err)
} }
var proposals []gameTypes.LargePreimageMetaData var proposals []keccakTypes.LargePreimageMetaData
for i, result := range results { for i, result := range results {
meta := metadata(result.GetBytes32(0)) meta := metadata(result.GetBytes32(0))
proposals = append(proposals, gameTypes.LargePreimageMetaData{ proposals = append(proposals, keccakTypes.LargePreimageMetaData{
LargePreimageIdent: idents[i], LargePreimageIdent: idents[i],
Timestamp: meta.timestamp(), Timestamp: meta.timestamp(),
PartOffset: meta.partOffset(), PartOffset: meta.partOffset(),
...@@ -178,8 +167,8 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block ...@@ -178,8 +167,8 @@ func (c *PreimageOracleContract) GetProposalMetadata(ctx context.Context, block
return proposals, nil return proposals, nil
} }
func (c *PreimageOracleContract) decodePreimageIdent(result *batching.CallResult) gameTypes.LargePreimageIdent { func (c *PreimageOracleContract) decodePreimageIdent(result *batching.CallResult) keccakTypes.LargePreimageIdent {
return gameTypes.LargePreimageIdent{ return keccakTypes.LargePreimageIdent{
Claimant: result.GetAddress(0), Claimant: result.GetAddress(0),
UUID: result.GetBigInt(1), UUID: result.GetBigInt(1),
} }
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix" "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test" batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -57,7 +57,7 @@ func TestPreimageOracleContract_AddLeaves(t *testing.T) { ...@@ -57,7 +57,7 @@ func TestPreimageOracleContract_AddLeaves(t *testing.T) {
uuid := big.NewInt(123) uuid := big.NewInt(123)
input := []byte{0x12} input := []byte{0x12}
commitments := [][32]byte{{0x34}} commitments := []common.Hash{{0x34}}
finalize := true finalize := true
stubRpc.SetResponse(oracleAddr, methodAddLeavesLPP, batching.BlockLatest, []interface{}{ stubRpc.SetResponse(oracleAddr, methodAddLeavesLPP, batching.BlockLatest, []interface{}{
uuid, uuid,
...@@ -77,13 +77,13 @@ func TestPreimageOracleContract_Squeeze(t *testing.T) { ...@@ -77,13 +77,13 @@ func TestPreimageOracleContract_Squeeze(t *testing.T) {
claimant := common.Address{0x12} claimant := common.Address{0x12}
uuid := big.NewInt(123) uuid := big.NewInt(123)
stateMatrix := matrix.NewStateMatrix() stateMatrix := matrix.NewStateMatrix()
preState := Leaf{ preState := keccakTypes.Leaf{
Input: [136]byte{0x12}, Input: [136]byte{0x12},
Index: big.NewInt(123), Index: big.NewInt(123),
StateCommitment: common.Hash{0x34}, StateCommitment: common.Hash{0x34},
} }
preStateProof := MerkleProof{{0x34}} preStateProof := MerkleProof{{0x34}}
postState := Leaf{ postState := keccakTypes.Leaf{
Input: [136]byte{0x34}, Input: [136]byte{0x34},
Index: big.NewInt(456), Index: big.NewInt(456),
StateCommitment: common.Hash{0x56}, StateCommitment: common.Hash{0x56},
...@@ -93,9 +93,9 @@ func TestPreimageOracleContract_Squeeze(t *testing.T) { ...@@ -93,9 +93,9 @@ func TestPreimageOracleContract_Squeeze(t *testing.T) {
claimant, claimant,
uuid, uuid,
abiEncodeStateMatrix(stateMatrix), abiEncodeStateMatrix(stateMatrix),
preState.toPreimageOracleLeaf(), toPreimageOracleLeaf(preState),
preStateProof.toSized(), preStateProof.toSized(),
postState.toPreimageOracleLeaf(), toPreimageOracleLeaf(postState),
postStateProof.toSized(), postStateProof.toSized(),
}, nil) }, nil)
...@@ -127,7 +127,7 @@ func TestGetProposalMetadata(t *testing.T) { ...@@ -127,7 +127,7 @@ func TestGetProposalMetadata(t *testing.T) {
require.Equal(t, proposals, preimages) require.Equal(t, proposals, preimages)
// Fetching a proposal that doesn't exist should return an empty metadata object. // Fetching a proposal that doesn't exist should return an empty metadata object.
ident := gameTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)} ident := keccakTypes.LargePreimageIdent{Claimant: common.Address{0x12}, UUID: big.NewInt(123)}
meta := new(metadata) meta := new(metadata)
stubRpc.SetResponse( stubRpc.SetResponse(
oracleAddr, oracleAddr,
...@@ -137,10 +137,10 @@ func TestGetProposalMetadata(t *testing.T) { ...@@ -137,10 +137,10 @@ func TestGetProposalMetadata(t *testing.T) {
[]interface{}{meta}) []interface{}{meta})
preimages, err = oracle.GetProposalMetadata(context.Background(), batching.BlockByHash(blockHash), ident) preimages, err = oracle.GetProposalMetadata(context.Background(), batching.BlockByHash(blockHash), ident)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, []gameTypes.LargePreimageMetaData{{LargePreimageIdent: ident}}, preimages) require.Equal(t, []keccakTypes.LargePreimageMetaData{{LargePreimageIdent: ident}}, preimages)
} }
func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*batchingTest.AbiBasedRpc, *PreimageOracleContract, []gameTypes.LargePreimageMetaData) { func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*batchingTest.AbiBasedRpc, *PreimageOracleContract, []keccakTypes.LargePreimageMetaData) {
stubRpc, oracle := setupPreimageOracleTest(t) stubRpc, oracle := setupPreimageOracleTest(t)
stubRpc.SetResponse( stubRpc.SetResponse(
oracleAddr, oracleAddr,
...@@ -149,8 +149,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (* ...@@ -149,8 +149,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*
[]interface{}{}, []interface{}{},
[]interface{}{big.NewInt(3)}) []interface{}{big.NewInt(3)})
preimage1 := gameTypes.LargePreimageMetaData{ preimage1 := keccakTypes.LargePreimageMetaData{
LargePreimageIdent: gameTypes.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xaa}, Claimant: common.Address{0xaa},
UUID: big.NewInt(1111), UUID: big.NewInt(1111),
}, },
...@@ -161,8 +161,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (* ...@@ -161,8 +161,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*
BytesProcessed: 100, BytesProcessed: 100,
Countered: false, Countered: false,
} }
preimage2 := gameTypes.LargePreimageMetaData{ preimage2 := keccakTypes.LargePreimageMetaData{
LargePreimageIdent: gameTypes.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xbb}, Claimant: common.Address{0xbb},
UUID: big.NewInt(2222), UUID: big.NewInt(2222),
}, },
...@@ -173,8 +173,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (* ...@@ -173,8 +173,8 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*
BytesProcessed: 200, BytesProcessed: 200,
Countered: true, Countered: true,
} }
preimage3 := gameTypes.LargePreimageMetaData{ preimage3 := keccakTypes.LargePreimageMetaData{
LargePreimageIdent: gameTypes.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xcc}, Claimant: common.Address{0xcc},
UUID: big.NewInt(3333), UUID: big.NewInt(3333),
}, },
...@@ -186,7 +186,7 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (* ...@@ -186,7 +186,7 @@ func setupPreimageOracleTestWithProposals(t *testing.T, block batching.Block) (*
Countered: false, Countered: false,
} }
proposals := []gameTypes.LargePreimageMetaData{preimage1, preimage2, preimage3} proposals := []keccakTypes.LargePreimageMetaData{preimage1, preimage2, preimage3}
for i, proposal := range proposals { for i, proposal := range proposals {
stubRpc.SetResponse( stubRpc.SetResponse(
......
...@@ -11,7 +11,7 @@ import ( ...@@ -11,7 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix" "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
...@@ -23,23 +23,13 @@ var errNotSupported = errors.New("not supported") ...@@ -23,23 +23,13 @@ var errNotSupported = errors.New("not supported")
var _ PreimageUploader = (*LargePreimageUploader)(nil) var _ PreimageUploader = (*LargePreimageUploader)(nil)
// MaxLeafsPerChunk is the maximum number of leafs per chunk. // MaxBlocksPerChunk is the maximum number of keccak blocks per chunk.
const MaxLeafsPerChunk = 300 const MaxBlocksPerChunk = 300
// MaxChunkSize is the maximum size of a preimage chunk in bytes. // MaxChunkSize is the maximum size of a preimage chunk in bytes.
// Notice, the max chunk size must be a multiple of the leaf size. // Notice, the max chunk size must be a multiple of the leaf size.
// The max chunk size is roughly 0.04MB to avoid memory expansion. // The max chunk size is roughly 0.04MB to avoid memory expansion.
const MaxChunkSize = MaxLeafsPerChunk * matrix.LeafSize const MaxChunkSize = MaxBlocksPerChunk * keccakTypes.BlockSize
// Chunk is a contigous segment of preimage data.
type Chunk struct {
// Input is the preimage data.
Input []byte
// Commitments are the keccak commitments for each leaf in the chunk.
Commitments [][32]byte
// Finalize indicates whether the chunk is the final chunk.
Finalize bool
}
// LargePreimageUploader handles uploading large preimages by // LargePreimageUploader handles uploading large preimages by
// streaming the merkleized preimage to the PreimageOracle contract, // streaming the merkleized preimage to the PreimageOracle contract,
...@@ -56,7 +46,7 @@ func NewLargePreimageUploader(logger log.Logger, txMgr txmgr.TxManager, contract ...@@ -56,7 +46,7 @@ func NewLargePreimageUploader(logger log.Logger, txMgr txmgr.TxManager, contract
} }
func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint64, data *types.PreimageOracleData) error { func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint64, data *types.PreimageOracleData) error {
chunks, err := p.splitChunks(data) calls, err := p.splitCalls(data)
if err != nil { if err != nil {
return fmt.Errorf("failed to split preimage into chunks for data with oracle offset %d: %w", data.OracleOffset, err) return fmt.Errorf("failed to split preimage into chunks for data with oracle offset %d: %w", data.OracleOffset, err)
} }
...@@ -64,7 +54,7 @@ func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint6 ...@@ -64,7 +54,7 @@ func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint6
uuid := p.newUUID(data) uuid := p.newUUID(data)
// Fetch the current metadata for this preimage data, if it exists. // Fetch the current metadata for this preimage data, if it exists.
ident := gameTypes.LargePreimageIdent{Claimant: p.txMgr.From(), UUID: uuid} ident := keccakTypes.LargePreimageIdent{Claimant: p.txMgr.From(), UUID: uuid}
metadata, err := p.contract.GetProposalMetadata(ctx, batching.BlockLatest, ident) metadata, err := p.contract.GetProposalMetadata(ctx, batching.BlockLatest, ident)
if err != nil { if err != nil {
return fmt.Errorf("failed to get pre-image oracle metadata: %w", err) return fmt.Errorf("failed to get pre-image oracle metadata: %w", err)
...@@ -81,14 +71,14 @@ func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint6 ...@@ -81,14 +71,14 @@ func (p *LargePreimageUploader) UploadPreimage(ctx context.Context, parent uint6
// Filter out any chunks that have already been uploaded to the Preimage Oracle. // Filter out any chunks that have already been uploaded to the Preimage Oracle.
if len(metadata) > 0 { if len(metadata) > 0 {
numSkip := metadata[0].BytesProcessed / MaxChunkSize numSkip := metadata[0].BytesProcessed / MaxChunkSize
chunks = chunks[numSkip:] calls = calls[numSkip:]
// If the timestamp is non-zero, the preimage has been finalized. // If the timestamp is non-zero, the preimage has been finalized.
if metadata[0].Timestamp != 0 { if metadata[0].Timestamp != 0 {
chunks = chunks[len(chunks):] calls = calls[len(calls):]
} }
} }
err = p.addLargePreimageLeafs(ctx, uuid, chunks) err = p.addLargePreimageData(ctx, uuid, calls)
if err != nil { if err != nil {
return fmt.Errorf("failed to add leaves to large preimage with uuid: %s: %w", uuid, err) return fmt.Errorf("failed to add leaves to large preimage with uuid: %s: %w", uuid, err)
} }
...@@ -112,34 +102,22 @@ func (p *LargePreimageUploader) newUUID(data *types.PreimageOracleData) *big.Int ...@@ -112,34 +102,22 @@ func (p *LargePreimageUploader) newUUID(data *types.PreimageOracleData) *big.Int
} }
// splitChunks splits the preimage data into chunks of size [MaxChunkSize] (except the last chunk). // splitChunks splits the preimage data into chunks of size [MaxChunkSize] (except the last chunk).
func (p *LargePreimageUploader) splitChunks(data *types.PreimageOracleData) ([]Chunk, error) { func (p *LargePreimageUploader) splitCalls(data *types.PreimageOracleData) ([]keccakTypes.InputData, error) {
// Split the preimage data into chunks of size [MaxChunkSize] (except the last chunk).
stateMatrix := matrix.NewStateMatrix() stateMatrix := matrix.NewStateMatrix()
chunk := make([]byte, 0, MaxChunkSize) var calls []keccakTypes.InputData
chunks := []Chunk{}
commitments := make([][32]byte, 0, MaxLeafsPerChunk)
in := bytes.NewReader(data.OracleData) in := bytes.NewReader(data.OracleData)
for i := 0; ; i++ { for {
// Absorb the next preimage chunk leaf and run the keccak permutation. call, err := stateMatrix.AbsorbUpTo(in, MaxChunkSize)
leaf, err := stateMatrix.AbsorbNextLeaf(in)
chunk = append(chunk, leaf...)
commitments = append(commitments, stateMatrix.StateCommitment())
// SAFETY: the last leaf will always return an [io.EOF] error from [AbsorbNextLeaf].
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
chunks = append(chunks, Chunk{chunk, commitments[:], true}) calls = append(calls, call)
break break
} else if err != nil {
return nil, fmt.Errorf("failed to absorb data: %w", err)
} }
if err != nil { calls = append(calls, call)
return nil, fmt.Errorf("failed to absorb leaf: %w", err)
}
// Only create a call if the chunk is full.
if len(chunk) >= MaxChunkSize {
chunks = append(chunks, Chunk{chunk, commitments[:], false})
chunk = make([]byte, 0, MaxChunkSize)
commitments = make([][32]byte, 0, MaxLeafsPerChunk)
}
} }
return chunks, nil return calls, nil
} }
// initLargePreimage initializes the large preimage proposal. // initLargePreimage initializes the large preimage proposal.
...@@ -155,10 +133,10 @@ func (p *LargePreimageUploader) initLargePreimage(ctx context.Context, uuid *big ...@@ -155,10 +133,10 @@ func (p *LargePreimageUploader) initLargePreimage(ctx context.Context, uuid *big
return nil return nil
} }
// addLargePreimageLeafs adds leafs to the large preimage proposal. // addLargePreimageData adds data to the large preimage proposal.
// This method **must** be called after calling [initLargePreimage]. // This method **must** be called after calling [initLargePreimage].
// SAFETY: submits transactions in a [Queue] for latency while preserving submission order. // SAFETY: submits transactions in a [Queue] for latency while preserving submission order.
func (p *LargePreimageUploader) addLargePreimageLeafs(ctx context.Context, uuid *big.Int, chunks []Chunk) error { func (p *LargePreimageUploader) addLargePreimageData(ctx context.Context, uuid *big.Int, chunks []keccakTypes.InputData) error {
queue := txmgr.NewQueue[int](ctx, p.txMgr, 10) queue := txmgr.NewQueue[int](ctx, p.txMgr, 10)
receiptChs := make([]chan txmgr.TxReceipt[int], len(chunks)) receiptChs := make([]chan txmgr.TxReceipt[int], len(chunks))
for i, chunk := range chunks { for i, chunk := range chunks {
......
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix" "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
...@@ -150,12 +150,12 @@ func TestLargePreimageUploader_UploadPreimage(t *testing.T) { ...@@ -150,12 +150,12 @@ func TestLargePreimageUploader_UploadPreimage(t *testing.T) {
} }
func mockPreimageOracleData() types.PreimageOracleData { func mockPreimageOracleData() types.PreimageOracleData {
fullLeaf := make([]byte, matrix.LeafSize) fullLeaf := make([]byte, keccakTypes.BlockSize)
for i := 0; i < matrix.LeafSize; i++ { for i := 0; i < keccakTypes.BlockSize; i++ {
fullLeaf[i] = byte(i) fullLeaf[i] = byte(i)
} }
oracleData := make([]byte, 5*MaxLeafsPerChunk) oracleData := make([]byte, 5*MaxBlocksPerChunk)
for i := 0; i < 5*MaxLeafsPerChunk; i++ { for i := 0; i < 5*MaxBlocksPerChunk; i++ {
oracleData = append(oracleData, fullLeaf...) oracleData = append(oracleData, fullLeaf...)
} }
// Add a single byte to the end to make sure the last leaf is not processed. // Add a single byte to the end to make sure the last leaf is not processed.
...@@ -193,7 +193,7 @@ func (s *mockPreimageOracleContract) InitLargePreimage(_ *big.Int, _ uint32, _ u ...@@ -193,7 +193,7 @@ func (s *mockPreimageOracleContract) InitLargePreimage(_ *big.Int, _ uint32, _ u
} }
return txmgr.TxCandidate{}, nil return txmgr.TxCandidate{}, nil
} }
func (s *mockPreimageOracleContract) AddLeaves(_ *big.Int, input []byte, _ [][32]byte, _ bool) (txmgr.TxCandidate, error) { func (s *mockPreimageOracleContract) AddLeaves(_ *big.Int, input []byte, _ []common.Hash, _ bool) (txmgr.TxCandidate, error) {
s.addCalls++ s.addCalls++
s.addData = append(s.addData, input...) s.addData = append(s.addData, input...)
if s.addFails { if s.addFails {
...@@ -201,14 +201,14 @@ func (s *mockPreimageOracleContract) AddLeaves(_ *big.Int, input []byte, _ [][32 ...@@ -201,14 +201,14 @@ func (s *mockPreimageOracleContract) AddLeaves(_ *big.Int, input []byte, _ [][32
} }
return txmgr.TxCandidate{}, nil return txmgr.TxCandidate{}, nil
} }
func (s *mockPreimageOracleContract) Squeeze(_ common.Address, _ *big.Int, _ *matrix.StateMatrix, _ contracts.Leaf, _ contracts.MerkleProof, _ contracts.Leaf, _ contracts.MerkleProof) (txmgr.TxCandidate, error) { func (s *mockPreimageOracleContract) Squeeze(_ common.Address, _ *big.Int, _ *matrix.StateMatrix, _ keccakTypes.Leaf, _ contracts.MerkleProof, _ keccakTypes.Leaf, _ contracts.MerkleProof) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, nil return txmgr.TxCandidate{}, nil
} }
func (s *mockPreimageOracleContract) GetProposalMetadata(_ context.Context, _ batching.Block, idents ...gameTypes.LargePreimageIdent) ([]gameTypes.LargePreimageMetaData, error) { func (s *mockPreimageOracleContract) GetProposalMetadata(_ context.Context, _ batching.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error) {
if s.initialized || s.bytesProcessed > 0 { if s.initialized || s.bytesProcessed > 0 {
metadata := make([]gameTypes.LargePreimageMetaData, 0) metadata := make([]keccakTypes.LargePreimageMetaData, 0)
for _, ident := range idents { for _, ident := range idents {
metadata = append(metadata, gameTypes.LargePreimageMetaData{ metadata = append(metadata, keccakTypes.LargePreimageMetaData{
LargePreimageIdent: ident, LargePreimageIdent: ident,
ClaimedSize: s.claimedSize, ClaimedSize: s.claimedSize,
BytesProcessed: uint32(s.bytesProcessed), BytesProcessed: uint32(s.bytesProcessed),
...@@ -217,5 +217,5 @@ func (s *mockPreimageOracleContract) GetProposalMetadata(_ context.Context, _ ba ...@@ -217,5 +217,5 @@ func (s *mockPreimageOracleContract) GetProposalMetadata(_ context.Context, _ ba
} }
return metadata, nil return metadata, nil
} }
return []gameTypes.LargePreimageMetaData{{LargePreimageIdent: idents[0]}}, nil return []keccakTypes.LargePreimageMetaData{{LargePreimageIdent: idents[0]}}, nil
} }
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix" "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/matrix"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -25,7 +25,7 @@ type PreimageUploader interface { ...@@ -25,7 +25,7 @@ type PreimageUploader interface {
// PreimageOracleContract is the interface for interacting with the PreimageOracle contract. // PreimageOracleContract is the interface for interacting with the PreimageOracle contract.
type PreimageOracleContract interface { type PreimageOracleContract interface {
InitLargePreimage(uuid *big.Int, partOffset uint32, claimedSize uint32) (txmgr.TxCandidate, error) InitLargePreimage(uuid *big.Int, partOffset uint32, claimedSize uint32) (txmgr.TxCandidate, error)
AddLeaves(uuid *big.Int, input []byte, commitments [][32]byte, finalize bool) (txmgr.TxCandidate, error) AddLeaves(uuid *big.Int, input []byte, commitments []common.Hash, finalize bool) (txmgr.TxCandidate, error)
Squeeze(claimant common.Address, uuid *big.Int, stateMatrix *matrix.StateMatrix, preState contracts.Leaf, preStateProof contracts.MerkleProof, postState contracts.Leaf, postStateProof contracts.MerkleProof) (txmgr.TxCandidate, error) Squeeze(claimant common.Address, uuid *big.Int, stateMatrix *matrix.StateMatrix, preState keccakTypes.Leaf, preStateProof contracts.MerkleProof, postState keccakTypes.Leaf, postStateProof contracts.MerkleProof) (txmgr.TxCandidate, error)
GetProposalMetadata(ctx context.Context, block batching.Block, idents ...gameTypes.LargePreimageIdent) ([]gameTypes.LargePreimageMetaData, error) GetProposalMetadata(ctx context.Context, block batching.Block, idents ...keccakTypes.LargePreimageIdent) ([]keccakTypes.LargePreimageMetaData, error)
} }
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-challenger/metrics" "github.com/ethereum-optimism/optimism/op-challenger/metrics"
...@@ -26,7 +27,7 @@ var ( ...@@ -26,7 +27,7 @@ var (
type CloseFunc func() type CloseFunc func()
type Registry interface { type Registry interface {
RegisterGameType(gameType uint8, creator scheduler.PlayerCreator, oracle types.LargePreimageOracle) RegisterGameType(gameType uint8, creator scheduler.PlayerCreator, oracle keccakTypes.LargePreimageOracle)
} }
func RegisterGameTypes( func RegisterGameTypes(
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"io" "io"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
...@@ -15,10 +16,10 @@ type StateMatrix struct { ...@@ -15,10 +16,10 @@ type StateMatrix struct {
s *state s *state
} }
// LeafSize is the size in bytes required for leaf data. var (
const LeafSize = 136 ErrInvalidMaxLen = errors.New("invalid max length to absorb")
uint256Size = 32
var uint256Size = 32 )
// NewStateMatrix creates a new state matrix initialized with the initial, zero keccak block. // NewStateMatrix creates a new state matrix initialized with the initial, zero keccak block.
func NewStateMatrix() *StateMatrix { func NewStateMatrix() *StateMatrix {
...@@ -41,13 +42,43 @@ func (d *StateMatrix) PackState() []byte { ...@@ -41,13 +42,43 @@ func (d *StateMatrix) PackState() []byte {
return buf return buf
} }
// AbsorbNextLeaf reads up to [LeafSize] bytes from in and absorbs them into the state matrix. func (d *StateMatrix) AbsorbUpTo(in io.Reader, maxLen int) (types.InputData, error) {
if maxLen < types.BlockSize || maxLen%types.BlockSize != 0 {
return types.InputData{}, ErrInvalidMaxLen
}
input := make([]byte, 0, maxLen)
commitments := make([]common.Hash, 0, maxLen/types.BlockSize)
for len(input)+types.BlockSize <= maxLen {
readData, err := d.absorbNextLeafInput(in)
if errors.Is(err, io.EOF) {
input = append(input, readData...)
commitments = append(commitments, d.StateCommitment())
return types.InputData{
Input: input,
Commitments: commitments,
Finalize: true,
}, io.EOF
} else if err != nil {
return types.InputData{}, err
}
input = append(input, readData...)
commitments = append(commitments, d.StateCommitment())
}
return types.InputData{
Input: input,
Commitments: commitments,
Finalize: false,
}, nil
}
// absorbNextLeafInput reads up to [BlockSize] bytes from in and absorbs them into the state matrix.
// If EOF is reached while reading, the state matrix is finalized and [io.EOF] is returned. // If EOF is reached while reading, the state matrix is finalized and [io.EOF] is returned.
func (d *StateMatrix) AbsorbNextLeaf(in io.Reader) ([]byte, error) { func (d *StateMatrix) absorbNextLeafInput(in io.Reader) ([]byte, error) {
data := make([]byte, LeafSize) data := make([]byte, types.BlockSize)
read := 0 read := 0
final := false final := false
for read < LeafSize { for read < types.BlockSize {
n, err := in.Read(data[read:]) n, err := in.Read(data[read:])
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
final = true final = true
...@@ -57,19 +88,18 @@ func (d *StateMatrix) AbsorbNextLeaf(in io.Reader) ([]byte, error) { ...@@ -57,19 +88,18 @@ func (d *StateMatrix) AbsorbNextLeaf(in io.Reader) ([]byte, error) {
} }
read += n read += n
} }
leafData := data[:read] input := data[:read]
d.AbsorbLeaf(leafData, final) d.absorbLeafInput(input, final)
if final { if final {
return leafData, io.EOF return input, io.EOF
} }
return leafData, nil return input, nil
} }
// AbsorbLeaf absorbs the specified data into the keccak sponge. // absorbLeafInput absorbs the specified data into the keccak sponge.
// If final is true, the data is padded to the required length, otherwise it must be exactly // If final is true, the data is padded to the required length, otherwise it must be exactly [types.BlockSize] bytes.
// LeafSize bytes. func (d *StateMatrix) absorbLeafInput(data []byte, final bool) {
func (d *StateMatrix) AbsorbLeaf(data []byte, final bool) { if !final && len(data) != types.BlockSize {
if !final && len(data) != LeafSize {
panic("sha3: Incorrect leaf data length") panic("sha3: Incorrect leaf data length")
} }
_, _ = d.s.Write(data[:]) _, _ = d.s.Write(data[:])
......
...@@ -7,8 +7,11 @@ import ( ...@@ -7,8 +7,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/rand"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -61,13 +64,13 @@ func TestReferenceCommitments(t *testing.T) { ...@@ -61,13 +64,13 @@ func TestReferenceCommitments(t *testing.T) {
t.Run(fmt.Sprintf("Ref-%v", i), func(t *testing.T) { t.Run(fmt.Sprintf("Ref-%v", i), func(t *testing.T) {
s := NewStateMatrix() s := NewStateMatrix()
commitments := []common.Hash{s.StateCommitment()} commitments := []common.Hash{s.StateCommitment()}
for i := 0; i < len(test.Input); i += LeafSize { for i := 0; i < len(test.Input); i += types.BlockSize {
end := min(i+LeafSize, len(test.Input)) end := min(i+types.BlockSize, len(test.Input))
s.AbsorbLeaf(test.Input[i:end], end == len(test.Input)) s.absorbLeafInput(test.Input[i:end], end == len(test.Input))
commitments = append(commitments, s.StateCommitment()) commitments = append(commitments, s.StateCommitment())
} }
if len(test.Input) == 0 { if len(test.Input) == 0 {
s.AbsorbLeaf(nil, true) s.absorbLeafInput(nil, true)
commitments = append(commitments, s.StateCommitment()) commitments = append(commitments, s.StateCommitment())
} }
actual := s.Hash() actual := s.Hash()
...@@ -89,7 +92,7 @@ func TestReferenceCommitmentsFromReader(t *testing.T) { ...@@ -89,7 +92,7 @@ func TestReferenceCommitmentsFromReader(t *testing.T) {
commitments := []common.Hash{s.StateCommitment()} commitments := []common.Hash{s.StateCommitment()}
in := bytes.NewReader(test.Input) in := bytes.NewReader(test.Input)
for { for {
_, err := s.AbsorbNextLeaf(in) _, err := s.absorbNextLeafInput(in)
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
commitments = append(commitments, s.StateCommitment()) commitments = append(commitments, s.StateCommitment())
break break
...@@ -106,40 +109,96 @@ func TestReferenceCommitmentsFromReader(t *testing.T) { ...@@ -106,40 +109,96 @@ func TestReferenceCommitmentsFromReader(t *testing.T) {
} }
} }
func TestAbsorbUpTo_ReferenceCommitments(t *testing.T) {
var tests []testData
require.NoError(t, json.Unmarshal(refTests, &tests))
for i, test := range tests {
test := test
t.Run(fmt.Sprintf("Ref-%v", i), func(t *testing.T) {
s := NewStateMatrix()
commitments := []common.Hash{s.StateCommitment()}
in := bytes.NewReader(test.Input)
for {
input, err := s.AbsorbUpTo(in, types.BlockSize*3)
if errors.Is(err, io.EOF) {
commitments = append(commitments, input.Commitments...)
break
}
// Shouldn't get any error except EOF
require.NoError(t, err)
commitments = append(commitments, input.Commitments...)
}
actual := s.Hash()
expected := crypto.Keccak256Hash(test.Input)
require.Equal(t, expected, actual)
require.Equal(t, test.Commitments, commitments)
})
}
}
func TestAbsorbUpTo_LimitsDataRead(t *testing.T) {
s := NewStateMatrix()
data := testutils.RandomData(rand.New(rand.NewSource(2424)), types.BlockSize*6+20)
in := bytes.NewReader(data)
// Should fully read the first four leaves worth
inputData, err := s.AbsorbUpTo(in, types.BlockSize*4)
require.NoError(t, err)
require.Equal(t, data[0:types.BlockSize*4], inputData.Input)
require.Len(t, inputData.Commitments, 4)
require.False(t, inputData.Finalize)
// Should read the remaining data and return EOF
inputData, err = s.AbsorbUpTo(in, types.BlockSize*10)
require.ErrorIs(t, err, io.EOF)
require.Equal(t, data[types.BlockSize*4:], inputData.Input)
require.Len(t, inputData.Commitments, 3, "2 full leaves plus the final partial leaf")
require.True(t, inputData.Finalize)
}
func TestAbsorbUpTo_InvalidLengths(t *testing.T) {
s := NewStateMatrix()
lengths := []int{-types.BlockSize, -1, 0, 1, types.BlockSize - 1, types.BlockSize + 1, 2*types.BlockSize + 1}
for _, length := range lengths {
_, err := s.AbsorbUpTo(bytes.NewReader(nil), length)
require.ErrorIsf(t, err, ErrInvalidMaxLen, "Should get invalid length for length %v", length)
}
}
func TestMatrix_AbsorbNextLeaf(t *testing.T) { func TestMatrix_AbsorbNextLeaf(t *testing.T) {
fullLeaf := make([]byte, LeafSize) fullLeaf := make([]byte, types.BlockSize)
for i := 0; i < LeafSize; i++ { for i := 0; i < types.BlockSize; i++ {
fullLeaf[i] = byte(i) fullLeaf[i] = byte(i)
} }
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
leafs [][]byte leafInputs [][]byte
errs []error errs []error
}{ }{
{ {
name: "empty", name: "empty",
input: []byte{}, input: []byte{},
leafs: [][]byte{{}}, leafInputs: [][]byte{{}},
errs: []error{io.EOF}, errs: []error{io.EOF},
}, },
{ {
name: "single", name: "single",
input: fullLeaf, input: fullLeaf,
leafs: [][]byte{fullLeaf}, leafInputs: [][]byte{fullLeaf},
errs: []error{io.EOF}, errs: []error{io.EOF},
}, },
{ {
name: "single-overflow", name: "single-overflow",
input: append(fullLeaf, byte(9)), input: append(fullLeaf, byte(9)),
leafs: [][]byte{fullLeaf, {byte(9)}}, leafInputs: [][]byte{fullLeaf, {byte(9)}},
errs: []error{nil, io.EOF}, errs: []error{nil, io.EOF},
}, },
{ {
name: "double", name: "double",
input: append(fullLeaf, fullLeaf...), input: append(fullLeaf, fullLeaf...),
leafs: [][]byte{fullLeaf, fullLeaf}, leafInputs: [][]byte{fullLeaf, fullLeaf},
errs: []error{nil, io.EOF}, errs: []error{nil, io.EOF},
}, },
} }
...@@ -148,8 +207,8 @@ func TestMatrix_AbsorbNextLeaf(t *testing.T) { ...@@ -148,8 +207,8 @@ func TestMatrix_AbsorbNextLeaf(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
state := NewStateMatrix() state := NewStateMatrix()
in := bytes.NewReader(test.input) in := bytes.NewReader(test.input)
for i, leaf := range test.leafs { for i, leaf := range test.leafInputs {
buf, err := state.AbsorbNextLeaf(in) buf, err := state.absorbNextLeafInput(in)
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
require.Equal(t, test.errs[i], err) require.Equal(t, test.errs[i], err)
break break
...@@ -164,9 +223,9 @@ func TestMatrix_AbsorbNextLeaf(t *testing.T) { ...@@ -164,9 +223,9 @@ func TestMatrix_AbsorbNextLeaf(t *testing.T) {
func FuzzKeccak(f *testing.F) { func FuzzKeccak(f *testing.F) {
f.Fuzz(func(t *testing.T, number, time uint64, data []byte) { f.Fuzz(func(t *testing.T, number, time uint64, data []byte) {
s := NewStateMatrix() s := NewStateMatrix()
for i := 0; i < len(data); i += LeafSize { for i := 0; i < len(data); i += types.BlockSize {
end := min(i+LeafSize, len(data)) end := min(i+types.BlockSize, len(data))
s.AbsorbLeaf(data[i:end], end == len(data)) s.absorbLeafInput(data[i:end], end == len(data))
} }
actual := s.Hash() actual := s.Hash()
expected := crypto.Keccak256Hash(data) expected := crypto.Keccak256Hash(data)
......
...@@ -4,25 +4,25 @@ import ( ...@@ -4,25 +4,25 @@ import (
"context" "context"
"sync" "sync"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type Verifier interface { type Verifier interface {
Verify(ctx context.Context, oracle types.LargePreimageOracle, preimage types.LargePreimageMetaData) Verify(ctx context.Context, oracle keccakTypes.LargePreimageOracle, preimage keccakTypes.LargePreimageMetaData)
} }
type LargePreimageScheduler struct { type LargePreimageScheduler struct {
log log.Logger log log.Logger
ch chan common.Hash ch chan common.Hash
oracles []types.LargePreimageOracle oracles []keccakTypes.LargePreimageOracle
verifier Verifier verifier Verifier
cancel func() cancel func()
wg sync.WaitGroup wg sync.WaitGroup
} }
func NewLargePreimageScheduler(logger log.Logger, oracles []types.LargePreimageOracle, verifier Verifier) *LargePreimageScheduler { func NewLargePreimageScheduler(logger log.Logger, oracles []keccakTypes.LargePreimageOracle, verifier Verifier) *LargePreimageScheduler {
return &LargePreimageScheduler{ return &LargePreimageScheduler{
log: logger, log: logger,
ch: make(chan common.Hash, 1), ch: make(chan common.Hash, 1),
...@@ -76,7 +76,7 @@ func (s *LargePreimageScheduler) verifyPreimages(ctx context.Context, blockHash ...@@ -76,7 +76,7 @@ func (s *LargePreimageScheduler) verifyPreimages(ctx context.Context, blockHash
return nil return nil
} }
func (s *LargePreimageScheduler) verifyOraclePreimages(ctx context.Context, oracle types.LargePreimageOracle, blockHash common.Hash) error { func (s *LargePreimageScheduler) verifyOraclePreimages(ctx context.Context, oracle keccakTypes.LargePreimageOracle, blockHash common.Hash) error {
preimages, err := oracle.GetActivePreimages(ctx, blockHash) preimages, err := oracle.GetActivePreimages(ctx, blockHash)
for _, preimage := range preimages { for _, preimage := range preimages {
if preimage.ShouldVerify() { if preimage.ShouldVerify() {
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -17,32 +17,32 @@ import ( ...@@ -17,32 +17,32 @@ import (
func TestScheduleNextCheck(t *testing.T) { func TestScheduleNextCheck(t *testing.T) {
ctx := context.Background() ctx := context.Background()
logger := testlog.Logger(t, log.LvlInfo) logger := testlog.Logger(t, log.LvlInfo)
preimage1 := types.LargePreimageMetaData{ // Incomplete so won't be verified preimage1 := keccakTypes.LargePreimageMetaData{ // Incomplete so won't be verified
LargePreimageIdent: types.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xab}, Claimant: common.Address{0xab},
UUID: big.NewInt(111), UUID: big.NewInt(111),
}, },
} }
preimage2 := types.LargePreimageMetaData{ // Already countered so won't be verified preimage2 := keccakTypes.LargePreimageMetaData{ // Already countered so won't be verified
LargePreimageIdent: types.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xab}, Claimant: common.Address{0xab},
UUID: big.NewInt(222), UUID: big.NewInt(222),
}, },
Timestamp: 1234, Timestamp: 1234,
Countered: true, Countered: true,
} }
preimage3 := types.LargePreimageMetaData{ preimage3 := keccakTypes.LargePreimageMetaData{
LargePreimageIdent: types.LargePreimageIdent{ LargePreimageIdent: keccakTypes.LargePreimageIdent{
Claimant: common.Address{0xdd}, Claimant: common.Address{0xdd},
UUID: big.NewInt(333), UUID: big.NewInt(333),
}, },
Timestamp: 1234, Timestamp: 1234,
} }
oracle := &stubOracle{ oracle := &stubOracle{
images: []types.LargePreimageMetaData{preimage1, preimage2, preimage3}, images: []keccakTypes.LargePreimageMetaData{preimage1, preimage2, preimage3},
} }
verifier := &stubVerifier{} verifier := &stubVerifier{}
scheduler := NewLargePreimageScheduler(logger, []types.LargePreimageOracle{oracle}, verifier) scheduler := NewLargePreimageScheduler(logger, []keccakTypes.LargePreimageOracle{oracle}, verifier)
scheduler.Start(ctx) scheduler.Start(ctx)
defer scheduler.Close() defer scheduler.Close()
err := scheduler.Schedule(common.Hash{0xaa}, 3) err := scheduler.Schedule(common.Hash{0xaa}, 3)
...@@ -61,14 +61,14 @@ type stubOracle struct { ...@@ -61,14 +61,14 @@ type stubOracle struct {
m sync.Mutex m sync.Mutex
addr common.Address addr common.Address
getPreimagesCount int getPreimagesCount int
images []types.LargePreimageMetaData images []keccakTypes.LargePreimageMetaData
} }
func (s *stubOracle) Addr() common.Address { func (s *stubOracle) Addr() common.Address {
return s.addr return s.addr
} }
func (s *stubOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]types.LargePreimageMetaData, error) { func (s *stubOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]keccakTypes.LargePreimageMetaData, error) {
s.m.Lock() s.m.Lock()
defer s.m.Unlock() defer s.m.Unlock()
s.getPreimagesCount++ s.getPreimagesCount++
...@@ -83,19 +83,19 @@ func (s *stubOracle) GetPreimagesCount() int { ...@@ -83,19 +83,19 @@ func (s *stubOracle) GetPreimagesCount() int {
type stubVerifier struct { type stubVerifier struct {
m sync.Mutex m sync.Mutex
verified []types.LargePreimageMetaData verified []keccakTypes.LargePreimageMetaData
} }
func (s *stubVerifier) Verify(_ context.Context, _ types.LargePreimageOracle, image types.LargePreimageMetaData) { func (s *stubVerifier) Verify(_ context.Context, _ keccakTypes.LargePreimageOracle, image keccakTypes.LargePreimageMetaData) {
s.m.Lock() s.m.Lock()
defer s.m.Unlock() defer s.m.Unlock()
s.verified = append(s.verified, image) s.verified = append(s.verified, image)
} }
func (s *stubVerifier) Verified() []types.LargePreimageMetaData { func (s *stubVerifier) Verified() []keccakTypes.LargePreimageMetaData {
s.m.Lock() s.m.Lock()
defer s.m.Unlock() defer s.m.Unlock()
v := make([]types.LargePreimageMetaData, len(s.verified)) v := make([]keccakTypes.LargePreimageMetaData, len(s.verified))
copy(v, s.verified) copy(v, s.verified)
return v return v
} }
package types
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// BlockSize is the size in bytes required for leaf data.
const BlockSize = 136
// Leaf is the keccak state matrix added to the large preimage merkle tree.
type Leaf struct {
// Input is the data absorbed for the block, exactly 136 bytes
Input [BlockSize]byte
// Index of the block in the absorption process
Index *big.Int
// StateCommitment is the hash of the internal state after absorbing the input.
StateCommitment common.Hash
}
// InputData is a contiguous segment of preimage data.
type InputData struct {
// Input is the preimage data.
// When Finalize is false, len(Input) must equal len(Commitments)*BlockSize
// When Finalize is true, len(Input) must be between len(Commitments - 1)*BlockSize and len(Commitments)*BlockSize
Input []byte
// Commitments are the keccak commitments for each leaf in the chunk.
Commitments []common.Hash
// Finalize indicates whether the chunk is the final chunk.
Finalize bool
}
type LargePreimageIdent struct {
Claimant common.Address
UUID *big.Int
}
type LargePreimageMetaData struct {
LargePreimageIdent
// Timestamp is the time at which the proposal first became fully available.
// 0 when not all data is available yet
Timestamp uint64
PartOffset uint32
ClaimedSize uint32
BlocksProcessed uint32
BytesProcessed uint32
Countered bool
}
// ShouldVerify returns true if the preimage upload is complete and has not yet been countered.
// Note that the challenge period for the preimage may have expired but the image not yet been finalized.
func (m LargePreimageMetaData) ShouldVerify() bool {
return m.Timestamp > 0 && !m.Countered
}
type LargePreimageOracle interface {
Addr() common.Address
GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]LargePreimageMetaData, error)
}
...@@ -3,7 +3,7 @@ package keccak ...@@ -3,7 +3,7 @@ package keccak
import ( import (
"context" "context"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -17,6 +17,6 @@ func NewPreimageVerifier(logger log.Logger) *PreimageVerifier { ...@@ -17,6 +17,6 @@ func NewPreimageVerifier(logger log.Logger) *PreimageVerifier {
} }
} }
func (v *PreimageVerifier) Verify(ctx context.Context, oracle types.LargePreimageOracle, preimage types.LargePreimageMetaData) { func (v *PreimageVerifier) Verify(ctx context.Context, oracle keccakTypes.LargePreimageOracle, preimage keccakTypes.LargePreimageMetaData) {
// No verification currently performed. // No verification currently performed.
} }
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -16,19 +17,19 @@ var ( ...@@ -16,19 +17,19 @@ var (
type GameTypeRegistry struct { type GameTypeRegistry struct {
types map[uint8]scheduler.PlayerCreator types map[uint8]scheduler.PlayerCreator
oracles map[common.Address]types.LargePreimageOracle oracles map[common.Address]keccakTypes.LargePreimageOracle
} }
func NewGameTypeRegistry() *GameTypeRegistry { func NewGameTypeRegistry() *GameTypeRegistry {
return &GameTypeRegistry{ return &GameTypeRegistry{
types: make(map[uint8]scheduler.PlayerCreator), types: make(map[uint8]scheduler.PlayerCreator),
oracles: make(map[common.Address]types.LargePreimageOracle), oracles: make(map[common.Address]keccakTypes.LargePreimageOracle),
} }
} }
// RegisterGameType registers a scheduler.PlayerCreator to use for a specific game type. // RegisterGameType registers a scheduler.PlayerCreator to use for a specific game type.
// Panics if the same game type is registered multiple times, since this indicates a significant programmer error. // Panics if the same game type is registered multiple times, since this indicates a significant programmer error.
func (r *GameTypeRegistry) RegisterGameType(gameType uint8, creator scheduler.PlayerCreator, oracle types.LargePreimageOracle) { func (r *GameTypeRegistry) RegisterGameType(gameType uint8, creator scheduler.PlayerCreator, oracle keccakTypes.LargePreimageOracle) {
if _, ok := r.types[gameType]; ok { if _, ok := r.types[gameType]; ok {
panic(fmt.Errorf("duplicate creator registered for game type: %v", gameType)) panic(fmt.Errorf("duplicate creator registered for game type: %v", gameType))
} }
...@@ -49,6 +50,6 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc ...@@ -49,6 +50,6 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc
return creator(game, dir) return creator(game, dir)
} }
func (r *GameTypeRegistry) Oracles() []types.LargePreimageOracle { func (r *GameTypeRegistry) Oracles() []keccakTypes.LargePreimageOracle {
return maps.Values(r.oracles) return maps.Values(r.oracles)
} }
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler/test" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler/test"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
...@@ -63,6 +64,6 @@ func (s stubPreimageOracle) Addr() common.Address { ...@@ -63,6 +64,6 @@ func (s stubPreimageOracle) Addr() common.Address {
return common.Address(s) return common.Address(s)
} }
func (s stubPreimageOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]types.LargePreimageMetaData, error) { func (s stubPreimageOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]keccakTypes.LargePreimageMetaData, error) {
return nil, nil return nil, nil
} }
package types package types
import ( import (
"context"
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
...@@ -43,32 +41,3 @@ type GameMetadata struct { ...@@ -43,32 +41,3 @@ type GameMetadata struct {
Timestamp uint64 Timestamp uint64
Proxy common.Address Proxy common.Address
} }
type LargePreimageIdent struct {
Claimant common.Address
UUID *big.Int
}
type LargePreimageMetaData struct {
LargePreimageIdent
// Timestamp is the time at which the proposal first became fully available.
// 0 when not all data is available yet
Timestamp uint64
PartOffset uint32
ClaimedSize uint32
BlocksProcessed uint32
BytesProcessed uint32
Countered bool
}
// ShouldVerify returns true if the preimage upload is complete and has not yet been countered.
// Note that the challenge period for the preimage may have expired but the image not yet been finalized.
func (m LargePreimageMetaData) ShouldVerify() bool {
return m.Timestamp > 0 && !m.Countered
}
type LargePreimageOracle interface {
Addr() common.Address
GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]LargePreimageMetaData, error)
}
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