Commit 48b9fe26 authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Load and decode preimage metadata. (#9094)

parent 51e29f36
...@@ -2,7 +2,9 @@ package contracts ...@@ -2,7 +2,9 @@ package contracts
import ( import (
"context" "context"
"encoding/binary"
"fmt" "fmt"
"math"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
...@@ -21,6 +23,7 @@ const ( ...@@ -21,6 +23,7 @@ const (
methodLoadKeccak256PreimagePart = "loadKeccak256PreimagePart" methodLoadKeccak256PreimagePart = "loadKeccak256PreimagePart"
methodProposalCount = "proposalCount" methodProposalCount = "proposalCount"
methodProposals = "proposals" methodProposals = "proposals"
methodProposalMetadata = "proposalMetadata"
) )
// PreimageOracleContract is a binding that works with contracts implementing the IPreimageOracle interface // PreimageOracleContract is a binding that works with contracts implementing the IPreimageOracle interface
...@@ -143,23 +146,113 @@ func abiEncodeStateMatrix(stateMatrix *matrix.StateMatrix) bindings.LibKeccakSta ...@@ -143,23 +146,113 @@ func abiEncodeStateMatrix(stateMatrix *matrix.StateMatrix) bindings.LibKeccakSta
} }
func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]gameTypes.LargePreimageMetaData, error) { func (c *PreimageOracleContract) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]gameTypes.LargePreimageMetaData, error) {
results, err := batching.ReadArray(ctx, c.multiCaller, batching.BlockByHash(blockHash), c.contract.Call(methodProposalCount), func(i *big.Int) *batching.ContractCall { block := batching.BlockByHash(blockHash)
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)
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load claims: %w", err) return nil, fmt.Errorf("failed to load claims: %w", err)
} }
var idents []gameTypes.LargePreimageIdent
for _, result := range results {
idents = append(idents, c.decodePreimageIdent(result))
}
// Fetch the metadata for each preimage
var calls []*batching.ContractCall
for _, ident := range idents {
calls = append(calls, c.contract.Call(methodProposalMetadata, ident.Claimant, ident.UUID))
}
results, err = c.multiCaller.Call(ctx, block, calls...)
if err != nil {
return nil, fmt.Errorf("failed to load proposal metadata: %w", err)
}
var proposals []gameTypes.LargePreimageMetaData var proposals []gameTypes.LargePreimageMetaData
for idx, result := range results { for i, result := range results {
proposals = append(proposals, c.decodeProposal(result, idx)) meta := metadata(result.GetBytes32(0))
proposals = append(proposals, gameTypes.LargePreimageMetaData{
LargePreimageIdent: idents[i],
Timestamp: meta.timestamp(),
PartOffset: meta.partOffset(),
ClaimedSize: meta.claimedSize(),
BlocksProcessed: meta.blocksProcessed(),
BytesProcessed: meta.bytesProcessed(),
Countered: meta.countered(),
})
} }
return proposals, nil return proposals, nil
} }
func (c *PreimageOracleContract) decodeProposal(result *batching.CallResult, idx int) gameTypes.LargePreimageMetaData { func (c *PreimageOracleContract) decodePreimageIdent(result *batching.CallResult) gameTypes.LargePreimageIdent {
return gameTypes.LargePreimageMetaData{ return gameTypes.LargePreimageIdent{
Claimant: result.GetAddress(0), Claimant: result.GetAddress(0),
UUID: result.GetBigInt(1), UUID: result.GetBigInt(1),
} }
} }
// metadata is the packed preimage metadata
// ┌─────────────┬────────────────────────────────────────────┐
// │ Bit Offsets │ Description │
// ├─────────────┼────────────────────────────────────────────┤
// │ [0, 64) │ Timestamp (Finalized - All data available) │
// │ [64, 96) │ Part Offset │
// │ [96, 128) │ Claimed Size │
// │ [128, 160) │ Blocks Processed (Inclusive of Padding) │
// │ [160, 192) │ Bytes Processed (Non-inclusive of Padding) │
// │ [192, 256) │ Countered │
// └─────────────┴────────────────────────────────────────────┘
type metadata [32]byte
func (m *metadata) setTimestamp(timestamp uint64) {
binary.BigEndian.PutUint64(m[0:8], timestamp)
}
func (m *metadata) timestamp() uint64 {
return binary.BigEndian.Uint64(m[0:8])
}
func (m *metadata) setPartOffset(value uint32) {
binary.BigEndian.PutUint32(m[8:12], value)
}
func (m *metadata) partOffset() uint32 {
return binary.BigEndian.Uint32(m[8:12])
}
func (m *metadata) setClaimedSize(value uint32) {
binary.BigEndian.PutUint32(m[12:16], value)
}
func (m *metadata) claimedSize() uint32 {
return binary.BigEndian.Uint32(m[12:16])
}
func (m *metadata) setBlocksProcessed(value uint32) {
binary.BigEndian.PutUint32(m[16:20], value)
}
func (m *metadata) blocksProcessed() uint32 {
return binary.BigEndian.Uint32(m[16:20])
}
func (m *metadata) setBytesProcessed(value uint32) {
binary.BigEndian.PutUint32(m[20:24], value)
}
func (m *metadata) bytesProcessed() uint32 {
return binary.BigEndian.Uint32(m[20:24])
}
func (m *metadata) setCountered(value bool) {
v := uint64(0)
if value {
v = math.MaxUint64
}
binary.BigEndian.PutUint64(m[24:32], v)
}
func (m *metadata) countered() bool {
return binary.BigEndian.Uint64(m[24:32]) != 0
}
...@@ -2,6 +2,8 @@ package contracts ...@@ -2,6 +2,8 @@ package contracts
import ( import (
"context" "context"
"fmt"
"math"
"math/big" "math/big"
"testing" "testing"
...@@ -117,16 +119,40 @@ func TestGetActivePreimages(t *testing.T) { ...@@ -117,16 +119,40 @@ func TestGetActivePreimages(t *testing.T) {
[]interface{}{big.NewInt(3)}) []interface{}{big.NewInt(3)})
preimage1 := gameTypes.LargePreimageMetaData{ preimage1 := gameTypes.LargePreimageMetaData{
Claimant: common.Address{0xaa}, LargePreimageIdent: gameTypes.LargePreimageIdent{
UUID: big.NewInt(1111), Claimant: common.Address{0xaa},
UUID: big.NewInt(1111),
},
Timestamp: 1234,
PartOffset: 1,
ClaimedSize: 100,
BlocksProcessed: 10,
BytesProcessed: 100,
Countered: false,
} }
preimage2 := gameTypes.LargePreimageMetaData{ preimage2 := gameTypes.LargePreimageMetaData{
Claimant: common.Address{0xbb}, LargePreimageIdent: gameTypes.LargePreimageIdent{
UUID: big.NewInt(2222), Claimant: common.Address{0xbb},
UUID: big.NewInt(2222),
},
Timestamp: 2345,
PartOffset: 2,
ClaimedSize: 200,
BlocksProcessed: 20,
BytesProcessed: 200,
Countered: true,
} }
preimage3 := gameTypes.LargePreimageMetaData{ preimage3 := gameTypes.LargePreimageMetaData{
Claimant: common.Address{0xcc}, LargePreimageIdent: gameTypes.LargePreimageIdent{
UUID: big.NewInt(3333), Claimant: common.Address{0xcc},
UUID: big.NewInt(3333),
},
Timestamp: 0,
PartOffset: 3,
ClaimedSize: 300,
BlocksProcessed: 30,
BytesProcessed: 233,
Countered: false,
} }
expectGetProposals(stubRpc, batching.BlockByHash(blockHash), preimage1, preimage2, preimage3) expectGetProposals(stubRpc, batching.BlockByHash(blockHash), preimage1, preimage2, preimage3)
preimages, err := oracle.GetActivePreimages(context.Background(), blockHash) preimages, err := oracle.GetActivePreimages(context.Background(), blockHash)
...@@ -150,6 +176,19 @@ func expectGetProposal(stubRpc *batchingTest.AbiBasedRpc, block batching.Block, ...@@ -150,6 +176,19 @@ func expectGetProposal(stubRpc *batchingTest.AbiBasedRpc, block batching.Block,
proposal.Claimant, proposal.Claimant,
proposal.UUID, proposal.UUID,
}) })
meta := new(metadata)
meta.setTimestamp(proposal.Timestamp)
meta.setPartOffset(proposal.PartOffset)
meta.setClaimedSize(proposal.ClaimedSize)
meta.setBlocksProcessed(proposal.BlocksProcessed)
meta.setBytesProcessed(proposal.BytesProcessed)
meta.setCountered(proposal.Countered)
stubRpc.SetResponse(
oracleAddr,
methodProposalMetadata,
block,
[]interface{}{proposal.Claimant, proposal.UUID},
[]interface{}{meta})
} }
func setupPreimageOracleTest(t *testing.T) (*batchingTest.AbiBasedRpc, *PreimageOracleContract) { func setupPreimageOracleTest(t *testing.T) (*batchingTest.AbiBasedRpc, *PreimageOracleContract) {
...@@ -162,3 +201,64 @@ func setupPreimageOracleTest(t *testing.T) (*batchingTest.AbiBasedRpc, *Preimage ...@@ -162,3 +201,64 @@ func setupPreimageOracleTest(t *testing.T) (*batchingTest.AbiBasedRpc, *Preimage
return stubRpc, oracleContract return stubRpc, oracleContract
} }
func TestMetadata(t *testing.T) {
uint32Values := []uint32{0, 1, 2, 3252354, math.MaxUint32}
tests := []struct {
name string
setter func(meta *metadata, val uint32)
getter func(meta *metadata) uint32
}{
{
name: "partOffset",
setter: (*metadata).setPartOffset,
getter: (*metadata).partOffset,
},
{
name: "claimedSize",
setter: (*metadata).setClaimedSize,
getter: (*metadata).claimedSize,
},
{
name: "blocksProcessed",
setter: (*metadata).setBlocksProcessed,
getter: (*metadata).blocksProcessed,
},
{
name: "bytesProcessed",
setter: (*metadata).setBytesProcessed,
getter: (*metadata).bytesProcessed,
},
}
for _, test := range tests {
test := test
for _, value := range uint32Values {
value := value
t.Run(fmt.Sprintf("%v-%v", test.name, value), func(t *testing.T) {
meta := new(metadata)
require.Zero(t, test.getter(meta))
test.setter(meta, value)
require.Equal(t, value, test.getter(meta))
})
}
}
}
func TestMetadata_Timestamp(t *testing.T) {
values := []uint64{0, 1, 2, 3252354, math.MaxUint32, math.MaxUint32 + 1, math.MaxUint64}
var meta metadata
require.Zero(t, meta.timestamp())
for _, value := range values {
meta.setTimestamp(value)
require.Equal(t, value, meta.timestamp())
}
}
func TestMetadata_Countered(t *testing.T) {
var meta metadata
require.False(t, meta.countered())
meta.setCountered(true)
require.True(t, meta.countered())
meta.setCountered(false)
require.False(t, meta.countered())
}
...@@ -44,11 +44,24 @@ type GameMetadata struct { ...@@ -44,11 +44,24 @@ type GameMetadata struct {
Proxy common.Address Proxy common.Address
} }
type LargePreimageMetaData struct { type LargePreimageIdent struct {
Claimant common.Address Claimant common.Address
UUID *big.Int 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
}
type LargePreimageOracle interface { type LargePreimageOracle interface {
Addr() common.Address Addr() common.Address
GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]LargePreimageMetaData, error) GetActivePreimages(ctx context.Context, blockHash common.Hash) ([]LargePreimageMetaData, error)
......
...@@ -133,3 +133,7 @@ func (c *CallResult) GetBigInt(i int) *big.Int { ...@@ -133,3 +133,7 @@ func (c *CallResult) GetBigInt(i int) *big.Int {
func (c *CallResult) GetStruct(i int, target interface{}) { func (c *CallResult) GetStruct(i int, target interface{}) {
abi.ConvertType(c.out[i], target) abi.ConvertType(c.out[i], target)
} }
func (c *CallResult) GetBytes32(i int) [32]byte {
return *abi.ConvertType(c.out[i], new([32]byte)).(*[32]byte)
}
...@@ -148,6 +148,13 @@ func TestCallResult_GetValues(t *testing.T) { ...@@ -148,6 +148,13 @@ func TestCallResult_GetValues(t *testing.T) {
}, },
expected: ([32]byte)(common.Hash{0xaa, 0xbb, 0xcc}), expected: ([32]byte)(common.Hash{0xaa, 0xbb, 0xcc}),
}, },
{
name: "GetBytes32",
getter: func(result *CallResult, i int) interface{} {
return result.GetBytes32(i)
},
expected: [32]byte{0xaa, 0xbb, 0xcc},
},
{ {
name: "GetBigInt", name: "GetBigInt",
getter: func(result *CallResult, i int) interface{} { getter: func(result *CallResult, i int) interface{} {
......
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