Commit d5a210cb authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Add split adapter to convert claims to output proposal info.

parent 48a86642
......@@ -4,6 +4,7 @@ import (
"context"
"errors"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
......@@ -17,11 +18,11 @@ func NewOutputCannonTraceAccessor(ctx context.Context, logger log.Logger, rollup
return nil, err
}
cannonCreator := func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
cannonCreator := func(ctx context.Context, localContext uint64, agreed contracts.Proposal, claimed contracts.Proposal) (types.TraceProvider, error) {
// TODO(client-pod#43): Actually create the cannon trace provider for the trace between the given claims.
return nil, errors.New("not implemented")
}
selector := split.NewSplitProviderSelector(outputProvider, int(topDepth), cannonCreator)
selector := split.NewSplitProviderSelector(outputProvider, int(topDepth), OutputRootSplitAdapter(outputProvider, cannonCreator))
return trace.NewAccessor(selector), nil
}
package outputs
import (
"context"
"fmt"
"math"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)
type ProposalTraceProviderCreator func(ctx context.Context, localContext uint64, agreed contracts.Proposal, claimed contracts.Proposal) (types.TraceProvider, error)
func OutputRootSplitAdapter(topProvider *OutputTraceProvider, creator ProposalTraceProviderCreator) split.ProviderCreator {
return func(ctx context.Context, pre types.Claim, post types.Claim) (types.TraceProvider, error) {
usePrestateBlock := pre == (types.Claim{})
preContractIndex := pre.ContractIndex
if usePrestateBlock {
preContractIndex = math.MaxUint32
}
localContext := uint64(preContractIndex)<<32 + uint64(post.ContractIndex)
var agreed contracts.Proposal
if usePrestateBlock {
prestateRoot, err := topProvider.AbsolutePreStateCommitment(ctx)
if err != nil {
return nil, fmt.Errorf("failed to retrieve absolute prestate output root: %w", err)
}
agreed = contracts.Proposal{
L2BlockNumber: new(big.Int).SetUint64(topProvider.prestateBlock),
OutputRoot: prestateRoot,
}
} else {
preBlockNum, err := topProvider.BlockNumber(pre.Position)
if err != nil {
return nil, fmt.Errorf("unable to calculate pre-claim block number: %w", err)
}
agreed = contracts.Proposal{
L2BlockNumber: new(big.Int).SetUint64(preBlockNum),
OutputRoot: pre.Value,
}
}
postBlockNum, err := topProvider.BlockNumber(post.Position)
if err != nil {
return nil, fmt.Errorf("unable to calculate post-claim block number: %w", err)
}
claimed := contracts.Proposal{
L2BlockNumber: new(big.Int).SetUint64(postBlockNum),
OutputRoot: post.Value,
}
return creator(ctx, localContext, agreed, claimed)
}
}
package outputs
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/split"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
var creatorError = errors.New("captured args")
func TestOutputRootSplitAdapter(t *testing.T) {
tests := []struct {
name string
preTraceIndex int64
postTraceIndex int64
expectedAgreedBlockNum int64
expectedClaimedBlockNum int64
}{
{
name: "middleOfBlockRange",
preTraceIndex: 5,
postTraceIndex: 9,
expectedAgreedBlockNum: 26,
expectedClaimedBlockNum: 30,
},
{
name: "beyondPostBlock",
preTraceIndex: 5,
postTraceIndex: 50,
expectedAgreedBlockNum: 26,
expectedClaimedBlockNum: 40,
},
{
name: "firstBlock",
preTraceIndex: 0,
postTraceIndex: 1,
expectedAgreedBlockNum: 21,
expectedClaimedBlockNum: 22,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
topDepth := 10
adapter, creator := setupAdapterTest(t, topDepth)
preClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xaa},
Position: types.NewPosition(topDepth, big.NewInt(test.preTraceIndex)),
},
ContractIndex: 3,
ParentContractIndex: 2,
}
postClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xbb},
Position: types.NewPosition(topDepth, big.NewInt(test.postTraceIndex)),
},
ContractIndex: 7,
ParentContractIndex: 1,
}
expectedLocalContext := uint64(0x300000007)
expectedAgreed := contracts.Proposal{
L2BlockNumber: big.NewInt(test.expectedAgreedBlockNum),
OutputRoot: preClaim.Value,
}
expectedClaimed := contracts.Proposal{
L2BlockNumber: big.NewInt(test.expectedClaimedBlockNum),
OutputRoot: postClaim.Value,
}
_, err := adapter(context.Background(), preClaim, postClaim)
require.ErrorIs(t, err, creatorError)
require.Equal(t, expectedLocalContext, creator.localContext)
require.Equal(t, expectedAgreed, creator.agreed)
require.Equal(t, expectedClaimed, creator.claimed)
})
}
}
func TestOutputRootSplitAdapter_FromAbsolutePrestate(t *testing.T) {
topDepth := 10
adapter, creator := setupAdapterTest(t, topDepth)
postClaim := types.Claim{
ClaimData: types.ClaimData{
Value: common.Hash{0xbb},
Position: types.NewPosition(topDepth, big.NewInt(0)),
},
ContractIndex: 7,
ParentContractIndex: 1,
}
// Use MaxUint32 for the pre-state block contract index.
expectedLocalContext := uint64(0xffffffff00000007)
expectedAgreed := contracts.Proposal{
L2BlockNumber: big.NewInt(20),
OutputRoot: prestateOutputRoot, // Absolute prestate output root
}
expectedClaimed := contracts.Proposal{
L2BlockNumber: big.NewInt(21),
OutputRoot: postClaim.Value,
}
_, err := adapter(context.Background(), types.Claim{}, postClaim)
require.ErrorIs(t, err, creatorError)
require.Equal(t, expectedLocalContext, creator.localContext)
require.Equal(t, expectedAgreed, creator.agreed)
require.Equal(t, expectedClaimed, creator.claimed)
}
func setupAdapterTest(t *testing.T, topDepth int) (split.ProviderCreator, *capturingCreator) {
prestateBlock := uint64(20)
poststateBlock := uint64(40)
creator := &capturingCreator{}
rollupClient := &stubRollupClient{
outputs: map[uint64]*eth.OutputResponse{
prestateBlock: {
OutputRoot: eth.Bytes32(prestateOutputRoot),
},
},
}
topProvider := NewTraceProviderFromInputs(testlog.Logger(t, log.LvlInfo), rollupClient, uint64(topDepth), prestateBlock, poststateBlock)
adapter := OutputRootSplitAdapter(topProvider, creator.Create)
return adapter, creator
}
type capturingCreator struct {
localContext uint64
agreed contracts.Proposal
claimed contracts.Proposal
}
func (c *capturingCreator) Create(_ context.Context, localContext uint64, agreed contracts.Proposal, claimed contracts.Proposal) (types.TraceProvider, error) {
c.localContext = localContext
c.agreed = agreed
c.claimed = claimed
return nil, creatorError
}
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