Commit e3b54b0a authored by inphi's avatar inphi

Refactor OutputV0AtBlock

parent b85db10c
......@@ -53,6 +53,7 @@ type L2API interface {
InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error)
OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error)
}
func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, eng L2API, cfg *rollup.Config) *L2Verifier {
......
......@@ -4,12 +4,10 @@ import (
"context"
"fmt"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/version"
......@@ -20,6 +18,7 @@ type l2EthClient interface {
// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
// Optionally keys of the account storage trie can be specified to include with corresponding values in the proof.
GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error)
OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error)
}
type driverClient interface {
......@@ -99,39 +98,16 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et
return nil, fmt.Errorf("failed to get L2 block ref with sync status: %w", err)
}
head, err := n.client.InfoByHash(ctx, ref.Hash)
output, err := n.client.OutputV0AtBlock(ctx, ref.Hash)
if err != nil {
return nil, fmt.Errorf("failed to get L2 block by hash %s: %w", ref, err)
return nil, fmt.Errorf("failed to get L2 output at block %s: %w", ref, err)
}
if head == nil {
return nil, ethereum.NotFound
}
proof, err := n.client.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, ref.Hash.String())
if err != nil {
return nil, fmt.Errorf("failed to get contract proof at block %s: %w", ref, err)
}
if proof == nil {
return nil, fmt.Errorf("proof %w", ethereum.NotFound)
}
// make sure that the proof (including storage hash) that we retrieved is correct by verifying it against the state-root
if err := proof.Verify(head.Root()); err != nil {
n.log.Error("invalid withdrawal root detected in block", "stateRoot", head.Root(), "blocknum", number, "msg", err)
return nil, fmt.Errorf("invalid withdrawal root hash, state root was %s: %w", head.Root(), err)
}
l2OutputRoot, err := rollup.ComputeL2OutputRootV0(head, proof.StorageHash)
if err != nil {
n.log.Error("Error computing L2 output root, nil ptr passed to hashing function")
return nil, err
}
return &eth.OutputResponse{
Version: eth.OutputVersionV0,
OutputRoot: l2OutputRoot,
Version: output.Version(),
OutputRoot: eth.OutputRoot(output),
BlockRef: ref,
WithdrawalStorageRoot: proof.StorageHash,
StateRoot: head.Root(),
WithdrawalStorageRoot: common.Hash(output.MessagePasserStorageRoot),
StateRoot: common.Hash(output.StateRoot),
Status: status,
}, nil
}
......
......@@ -10,6 +10,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
......@@ -165,3 +166,31 @@ func (s *L2Client) SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (
s.systemConfigsCache.Add(hash, cfg)
return cfg, nil
}
func (s *L2Client) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) {
head, err := s.InfoByHash(ctx, blockHash)
if err != nil {
return nil, fmt.Errorf("failed to get L2 block by hash: %w", err)
}
if head == nil {
return nil, ethereum.NotFound
}
proof, err := s.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, blockHash.String())
if err != nil {
return nil, fmt.Errorf("failed to get contract proof at block %s: %w", blockHash, err)
}
if proof == nil {
return nil, fmt.Errorf("proof %w", ethereum.NotFound)
}
// make sure that the proof (including storage hash) that we retrieved is correct by verifying it against the state-root
if err := proof.Verify(head.Root()); err != nil {
return nil, fmt.Errorf("invalid withdrawal root hash, state root was %s: %w", head.Root(), err)
}
stateRoot := head.Root()
return &eth.OutputV0{
StateRoot: eth.Bytes32(stateRoot),
MessagePasserStorageRoot: eth.Bytes32(proof.StorageHash),
BlockHash: blockHash,
}, nil
}
......@@ -44,11 +44,11 @@ func (m *MockL2Client) ExpectSystemConfigByL2Hash(hash common.Hash, cfg eth.Syst
m.Mock.On("SystemConfigByL2Hash", hash).Once().Return(cfg, &err)
}
func (m *MockL2Client) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
out := m.Mock.MethodCalled("OutputByRoot", root)
return out[0].(eth.Output), *out[1].(*error)
func (m *MockL2Client) OutputV0AtBlock(ctx context.Context, blockHash common.Hash) (*eth.OutputV0, error) {
out := m.Mock.MethodCalled("OutputV0AtBlock", blockHash)
return out[0].(*eth.OutputV0), *out[1].(*error)
}
func (m *MockL2Client) ExpectOutputByRoot(root common.Hash, output eth.Output, err error) {
m.Mock.On("OutputByRoot", root).Once().Return(output, &err)
func (m *MockL2Client) ExpectOutputV0AtBlock(blockHash common.Hash, output *eth.OutputV0, err error) {
m.Mock.On("OutputV0AtBlock", blockHash).Once().Return(output, &err)
}
......@@ -4,12 +4,10 @@ import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/sources/caching"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
......@@ -38,7 +36,7 @@ func NewL2Client(client client.RPC, log log.Logger, metrics caching.Metrics, con
}
func (s *L2Client) OutputByRoot(ctx context.Context, l2OutputRoot common.Hash) (eth.Output, error) {
output, err := s.outputAtBlock(ctx, s.l2Head)
output, err := s.OutputV0AtBlock(ctx, s.l2Head)
if err != nil {
return nil, err
}
......@@ -49,31 +47,3 @@ func (s *L2Client) OutputByRoot(ctx context.Context, l2OutputRoot common.Hash) (
}
return output, nil
}
func (s *L2Client) outputAtBlock(ctx context.Context, blockHash common.Hash) (eth.Output, error) {
head, err := s.InfoByHash(ctx, blockHash)
if err != nil {
return nil, fmt.Errorf("failed to get L2 block by hash: %w", err)
}
if head == nil {
return nil, ethereum.NotFound
}
proof, err := s.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []common.Hash{}, blockHash.String())
if err != nil {
return nil, fmt.Errorf("failed to get contract proof at block %s: %w", blockHash, err)
}
if proof == nil {
return nil, fmt.Errorf("proof %w", ethereum.NotFound)
}
// make sure that the proof (including storage hash) that we retrieved is correct by verifying it against the state-root
if err := proof.Verify(head.Root()); err != nil {
return nil, fmt.Errorf("invalid withdrawal root hash, state root was %s: %w", head.Root(), err)
}
stateRoot := head.Root()
return &eth.OutputV0{
StateRoot: eth.Bytes32(stateRoot),
MessagePasserStorageRoot: eth.Bytes32(proof.StorageHash),
BlockHash: blockHash,
}, nil
}
......@@ -286,6 +286,15 @@ type l2Client struct {
*testutils.MockDebugClient
}
func (m *l2Client) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
out := m.Mock.MethodCalled("OutputByRoot", root)
return out[0].(eth.Output), *out[1].(*error)
}
func (m *l2Client) ExpectOutputByRoot(root common.Hash, output eth.Output, err error) {
m.Mock.On("OutputByRoot", root).Once().Return(output, &err)
}
func createPrefetcher(t *testing.T) (*Prefetcher, *testutils.MockL1Source, *l2Client, kvstore.KV) {
logger := testlog.Logger(t, log.LvlDebug)
kv := kvstore.NewMemKV()
......
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