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 ...@@ -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) 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) l2OutputRoot, err := rollup.ComputeL2OutputRootV0(head, proof.StorageHash)
if err != nil { if err != nil {
n.log.Error("Error computing L2 output root, nil ptr passed to hashing function") 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 ...@@ -128,7 +127,7 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et
} }
return &eth.OutputResponse{ return &eth.OutputResponse{
Version: l2OutputRootVersion, Version: eth.OutputVersionV0,
OutputRoot: l2OutputRoot, OutputRoot: l2OutputRoot,
BlockRef: ref, BlockRef: ref,
WithdrawalStorageRoot: proof.StorageHash, WithdrawalStorageRoot: proof.StorageHash,
......
...@@ -115,10 +115,3 @@ func (s *L1Client) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth. ...@@ -115,10 +115,3 @@ func (s *L1Client) L1BlockRefByHash(ctx context.Context, hash common.Hash) (eth.
s.l1BlockRefsCache.Add(ref.Hash, ref) s.l1BlockRefsCache.Add(ref.Hash, ref)
return ref, nil 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) ( ...@@ -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) { func (m *MockL1Source) ExpectL1BlockRefByHash(hash common.Hash, ref eth.L1BlockRef, err error) {
m.Mock.On("L1BlockRefByHash", hash).Once().Return(ref, &err) 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 ...@@ -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) { func (m *MockL2Client) ExpectSystemConfigByL2Hash(hash common.Hash, cfg eth.SystemConfig, err error) {
m.Mock.On("SystemConfigByL2Hash", hash).Once().Return(cfg, &err) 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 ( ...@@ -26,7 +26,7 @@ import (
) )
type L2Source struct { type L2Source struct {
*sources.L2Client *L2Client
*sources.DebugClient *sources.DebugClient
} }
...@@ -205,7 +205,7 @@ func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg * ...@@ -205,7 +205,7 @@ func makePrefetcher(ctx context.Context, logger log.Logger, kv kvstore.KV, cfg *
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L1 client: %w", err) 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 { if err != nil {
return nil, fmt.Errorf("failed to create L2 client: %w", err) 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 { ...@@ -23,13 +23,13 @@ type L1Source interface {
InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error) InfoByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, error)
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, 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) 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 { type L2Source interface {
InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error)
NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error)
CodeByHash(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 { type Prefetcher struct {
...@@ -100,7 +100,7 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error { ...@@ -100,7 +100,7 @@ func (p *Prefetcher) prefetch(ctx context.Context, hint string) error {
} }
return p.storeReceipts(receipts) return p.storeReceipts(receipts)
case l1.HintL2Output: case l1.HintL2Output:
output, err := p.l1Fetcher.L2OutputByRoot(ctx, hash) output, err := p.l2Fetcher.L2OutputByRoot(ctx, hash)
if err != nil { if err != nil {
return fmt.Errorf("failed to fetch L2 output root %s: %w", hash, err) 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 ...@@ -73,20 +73,6 @@ func (s *RetryingL1Source) FetchReceipts(ctx context.Context, blockHash common.H
return info, rcpts, err 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) var _ L1Source = (*RetryingL1Source)(nil)
type RetryingL2Source struct { type RetryingL2Source struct {
...@@ -139,6 +125,20 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([] ...@@ -139,6 +125,20 @@ func (s *RetryingL2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]
return code, err 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 { func NewRetryingL2Source(logger log.Logger, source L2Source) *RetryingL2Source {
return &RetryingL2Source{ return &RetryingL2Source{
logger: logger, logger: logger,
......
...@@ -229,4 +229,13 @@ func (m *MockL2Source) ExpectCodeByHash(hash common.Hash, code []byte, err error ...@@ -229,4 +229,13 @@ func (m *MockL2Source) ExpectCodeByHash(hash common.Hash, code []byte, err error
m.Mock.On("CodeByHash", hash).Once().Return(code, &err) 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) 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