Commit a4a3ff36 authored by inphi's avatar inphi

Compute output pre-image in L2 source

parent 9c858501
......@@ -120,7 +120,6 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et
return nil, fmt.Errorf("invalid withdrawal root hash, state root was %s: %w", head.Root(), err)
}
var l2OutputRootVersion eth.Bytes32 // it's zero for now
l2OutputRoot, err := rollup.ComputeL2OutputRootV0(head, proof.StorageHash)
if err != nil {
n.log.Error("Error computing L2 output root, nil ptr passed to hashing function")
......@@ -128,7 +127,7 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et
}
return &eth.OutputResponse{
Version: l2OutputRootVersion,
Version: eth.OutputVersionV0,
OutputRoot: l2OutputRoot,
BlockRef: ref,
WithdrawalStorageRoot: proof.StorageHash,
......
......@@ -115,10 +115,3 @@ func (s *L1Client) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth.
s.l1BlockRefsCache.Add(ref.Hash, ref)
return ref, nil
}
func (s *L1Client) L2OutputByRoot(ctx context.Context, l2OutputRoot common.Hash) (eth.Output, error) {
// TODO(inphi): Fetch Output from preset. Or directly from the oracle
//return s.OutputByRoot(ctx, l2OutputRoot)
var output eth.Output
return output, nil
}
......@@ -37,12 +37,3 @@ func (m *MockL1Source) L1BlockRefByHash(ctx context.Context, hash common.Hash) (
func (m *MockL1Source) ExpectL1BlockRefByHash(hash common.Hash, ref eth.L1BlockRef, err error) {
m.Mock.On("L1BlockRefByHash", hash).Once().Return(ref, &err)
}
func (m *MockL1Source) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
out := m.Mock.MethodCalled("L2OutputByRoot", root)
return out[0].(eth.Output), *out[1].(*error)
}
func (m *MockL1Source) ExpectL2OutputByRoot(root common.Hash, output eth.Output, err error) {
m.Mock.On("L2OutputByRoot", root).Once().Return(output, &err)
}
......@@ -43,3 +43,12 @@ func (m *MockL2Client) SystemConfigByL2Hash(ctx context.Context, hash common.Has
func (m *MockL2Client) ExpectSystemConfigByL2Hash(hash common.Hash, cfg eth.SystemConfig, err error) {
m.Mock.On("SystemConfigByL2Hash", hash).Once().Return(cfg, &err)
}
func (m *MockL2Client) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
out := m.Mock.MethodCalled("L2OutputByRoot", root)
return out[0].(eth.Output), *out[1].(*error)
}
func (m *MockL2Client) ExpectL2OutputByRoot(root common.Hash, output eth.Output, err error) {
m.Mock.On("L2OutputByRoot", root).Once().Return(output, &err)
}
......@@ -26,7 +26,7 @@ import (
)
type L2Source struct {
*sources.L2Client
*L2Client
*sources.DebugClient
}
......@@ -205,7 +205,7 @@ func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *
if err != nil {
return nil, fmt.Errorf("failed to create L1 client: %w", err)
}
l2Cl, err := sources.NewL2Client(l2RPC, logger, nil, l2ClCfg)
l2Cl, err := NewL2Client(l2RPC, logger, nil, &L2ClientConfig{L2ClientConfig: l2ClCfg, L2Head: cfg.L2Head})
if err != nil {
return nil, fmt.Errorf("failed to create L2 client: %w", err)
}
......
package host
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/crypto"
"github.com/ethereum/go-ethereum/log"
)
type L2Client struct {
*sources.L2Client
l2Head common.Hash
l2OutputOracleAddress common.Address
}
var (
L2OutputEventABI = "OutputProposed(bytes32,uint256,uint256,uint256)"
L2OutputEventABIHash = crypto.Keccak256Hash([]byte(L2OutputEventABI))
)
type L2ClientConfig struct {
*sources.L2ClientConfig
L2Head common.Hash
}
func NewL2Client(client client.RPC, log log.Logger, metrics caching.Metrics, config *L2ClientConfig) (*L2Client, error) {
l2Client, err := sources.NewL2Client(client, log, metrics, config.L2ClientConfig)
if err != nil {
return nil, err
}
return &L2Client{
L2Client: l2Client,
l2Head: config.L2Head,
}, nil
}
func (s *L2Client) L2OutputByRoot(ctx context.Context, l2OutputRoot common.Hash) (eth.Output, error) {
output, err := s.outputAtBlock(ctx, s.l2Head)
if err != nil {
return nil, err
}
if eth.OutputRoot(output) != eth.Bytes32(l2OutputRoot) {
// For fault proofs, we only reference outputs at the l2 head at boot time
// The caller shouldn't be requesting outputs at any other block
return nil, fmt.Errorf("unknown output root")
}
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
}
......@@ -23,13 +23,13 @@ type L1Source interface {
InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error)
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
L2OutputByRoot(ctx context.Context, l2OutputRoot common.Hash) (eth.Output, error)
}
type L2Source interface {
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error)
CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error)
L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error)
}
type Prefetcher struct {
......@@ -100,7 +100,7 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
}
return p.storeReceipts(receipts)
case l1.HintL2Output:
output, err := p.l1Fetcher.L2OutputByRoot(ctx, hash)
output, err := p.l2Fetcher.L2OutputByRoot(ctx, hash)
if err != nil {
return fmt.Errorf("failed to fetch L2 output root %s: %w", hash, err)
}
......
......@@ -73,20 +73,6 @@ func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.H
return info, rcpts, err
}
func (s *RetryingL1Source) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
var output eth.Output
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
o, err := s.source.L2OutputByRoot(ctx, root)
if err != nil {
s.logger.Warn("Failed to fetch l2 output", "root", root, "err", err)
return err
}
output = o
return nil
})
return output, err
}
var _ L1Source = (*RetryingL1Source)(nil)
type RetryingL2Source struct {
......@@ -139,6 +125,20 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]
return code, err
}
func (s *RetryingL2Source) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
var output eth.Output
err := backoff.DoCtx(ctx, maxAttempts, s.strategy, func() error {
o, err := s.source.L2OutputByRoot(ctx, root)
if err != nil {
s.logger.Warn("Failed to fetch l2 output", "root", root, "err", err)
return err
}
output = o
return nil
})
return output, err
}
func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return &RetryingL2Source{
logger: logger,
......
......@@ -229,4 +229,13 @@ func (m *MockL2Source) ExpectCodeByHash(hash common.Hash, code []byte, err error
m.Mock.On("CodeByHash", hash).Once().Return(code, &err)
}
func (m *MockL2Source) L2OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) {
out := m.Mock.MethodCalled("L2OutputByRoot", root)
return out[0].(eth.Output), *out[1].(*error)
}
func (m *MockL2Source) ExpectL2OutputByRoot(root common.Hash, output eth.Output, err error) {
m.Mock.On("L2OutputByRoot", root).Once().Return(output, &err)
}
var _ L2Source = (*MockL2Source)(nil)
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