oracle.go 3.32 KB
Newer Older
1 2
package l2

3
import (
4 5
	"fmt"

6 7
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
8 9
	"github.com/ethereum/go-ethereum/rlp"

10
	preimage "github.com/ethereum-optimism/optimism/op-preimage"
11
	"github.com/ethereum-optimism/optimism/op-program/client/mpt"
12
	"github.com/ethereum-optimism/optimism/op-service/eth"
13
)
14 15 16 17 18 19 20

// StateOracle defines the high-level API used to retrieve L2 state data pre-images
// The returned data is always the preimage of the requested hash.
type StateOracle interface {
	// NodeByHash retrieves the merkle-patricia trie node pre-image for a given hash.
	// Trie nodes may be from the world state trie or any account storage trie.
	// Contract code is not stored as part of the trie and must be retrieved via CodeByHash
21
	NodeByHash(nodeHash common.Hash) []byte
22 23 24

	// CodeByHash retrieves the contract code pre-image for a given hash.
	// codeHash should be retrieved from the world state account for a contract.
25
	CodeByHash(codeHash common.Hash) []byte
26
}
27 28 29 30 31 32 33

// Oracle defines the high-level API used to retrieve L2 data.
// The returned data is always the preimage of the requested hash.
type Oracle interface {
	StateOracle

	// BlockByHash retrieves the block with the given hash.
34
	BlockByHash(blockHash common.Hash) *types.Block
35 36

	OutputByRoot(root common.Hash) eth.Output
37
}
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

// PreimageOracle implements Oracle using by interfacing with the pure preimage.Oracle
// to fetch pre-images to decode into the requested data.
type PreimageOracle struct {
	oracle preimage.Oracle
	hint   preimage.Hinter
}

var _ Oracle = (*PreimageOracle)(nil)

func NewPreimageOracle(raw preimage.Oracle, hint preimage.Hinter) *PreimageOracle {
	return &PreimageOracle{
		oracle: raw,
		hint:   hint,
	}
}

func (p *PreimageOracle) headerByBlockHash(blockHash common.Hash) *types.Header {
	p.hint.Hint(BlockHeaderHint(blockHash))
	headerRlp := p.oracle.Get(preimage.Keccak256Key(blockHash))
	var header types.Header
	if err := rlp.DecodeBytes(headerRlp, &header); err != nil {
		panic(fmt.Errorf("invalid block header %s: %w", blockHash, err))
	}
	return &header
}

func (p *PreimageOracle) BlockByHash(blockHash common.Hash) *types.Block {
	header := p.headerByBlockHash(blockHash)
67 68 69 70 71 72
	txs := p.LoadTransactions(blockHash, header.TxHash)

	return types.NewBlockWithHeader(header).WithBody(txs, nil)
}

func (p *PreimageOracle) LoadTransactions(blockHash common.Hash, txHash common.Hash) []*types.Transaction {
73 74
	p.hint.Hint(TransactionsHint(blockHash))

75
	opaqueTxs := mpt.ReadTrie(txHash, func(key common.Hash) []byte {
76 77 78 79 80 81 82
		return p.oracle.Get(preimage.Keccak256Key(key))
	})

	txs, err := eth.DecodeTransactions(opaqueTxs)
	if err != nil {
		panic(fmt.Errorf("failed to decode list of txs: %w", err))
	}
83
	return txs
84 85 86 87 88 89 90 91 92 93 94
}

func (p *PreimageOracle) NodeByHash(nodeHash common.Hash) []byte {
	p.hint.Hint(StateNodeHint(nodeHash))
	return p.oracle.Get(preimage.Keccak256Key(nodeHash))
}

func (p *PreimageOracle) CodeByHash(codeHash common.Hash) []byte {
	p.hint.Hint(CodeHint(codeHash))
	return p.oracle.Get(preimage.Keccak256Key(codeHash))
}
95 96 97 98 99 100 101 102 103 104

func (p *PreimageOracle) OutputByRoot(l2OutputRoot common.Hash) eth.Output {
	p.hint.Hint(L2OutputHint(l2OutputRoot))
	data := p.oracle.Get(preimage.Keccak256Key(l2OutputRoot))
	output, err := eth.UnmarshalOutput(data)
	if err != nil {
		panic(fmt.Errorf("invalid L2 output data for root %s: %w", l2OutputRoot, err))
	}
	return output
}