1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package l1
import (
"encoding/binary"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-program/client/mpt"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
type Oracle interface {
// HeaderByBlockHash retrieves the block header with the given hash.
HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo
// TransactionsByBlockHash retrieves the transactions from the block with the given hash.
TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions)
// ReceiptsByBlockHash retrieves the receipts from the block with the given hash.
ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts)
// GetBlob retrieves the blob with the given hash.
GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *eth.Blob
// Precompile retrieves the result and success indicator of a precompile call for the given input.
Precompile(precompileAddress common.Address, input []byte) ([]byte, bool)
}
// 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) HeaderByBlockHash(blockHash common.Hash) eth.BlockInfo {
return eth.HeaderBlockInfo(p.headerByBlockHash(blockHash))
}
func (p *PreimageOracle) TransactionsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Transactions) {
header := p.headerByBlockHash(blockHash)
p.hint.Hint(TransactionsHint(blockHash))
opaqueTxs := mpt.ReadTrie(header.TxHash, func(key common.Hash) []byte {
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))
}
return eth.HeaderBlockInfo(header), txs
}
func (p *PreimageOracle) ReceiptsByBlockHash(blockHash common.Hash) (eth.BlockInfo, types.Receipts) {
info, txs := p.TransactionsByBlockHash(blockHash)
p.hint.Hint(ReceiptsHint(blockHash))
opaqueReceipts := mpt.ReadTrie(info.ReceiptHash(), func(key common.Hash) []byte {
return p.oracle.Get(preimage.Keccak256Key(key))
})
txHashes := eth.TransactionsToHashes(txs)
receipts, err := eth.DecodeRawReceipts(eth.ToBlockID(info), opaqueReceipts, txHashes)
if err != nil {
panic(fmt.Errorf("bad receipts data for block %s: %w", blockHash, err))
}
return info, receipts
}
func (p *PreimageOracle) GetBlob(ref eth.L1BlockRef, blobHash eth.IndexedBlobHash) *eth.Blob {
// Send a hint for the blob commitment & blob field elements.
blobReqMeta := make([]byte, 16)
binary.BigEndian.PutUint64(blobReqMeta[0:8], blobHash.Index)
binary.BigEndian.PutUint64(blobReqMeta[8:16], ref.Time)
p.hint.Hint(BlobHint(append(blobHash.Hash[:], blobReqMeta...)))
commitment := p.oracle.Get(preimage.Sha256Key(blobHash.Hash))
// Reconstruct the full blob from the 4096 field elements.
blob := eth.Blob{}
fieldElemKey := make([]byte, 80)
copy(fieldElemKey[:48], commitment)
for i := 0; i < params.BlobTxFieldElementsPerBlob; i++ {
binary.BigEndian.PutUint64(fieldElemKey[72:], uint64(i))
fieldElement := p.oracle.Get(preimage.BlobKey(crypto.Keccak256(fieldElemKey)))
copy(blob[i<<5:(i+1)<<5], fieldElement[:])
}
return &blob
}
func (p *PreimageOracle) Precompile(address common.Address, input []byte) ([]byte, bool) {
hintBytes := append(address.Bytes(), input...)
p.hint.Hint(PrecompileHint(hintBytes))
key := preimage.PrecompileKey(crypto.Keccak256Hash(hintBytes))
result := p.oracle.Get(key)
if len(result) == 0 { // must contain at least the status code
panic(fmt.Errorf("unexpected precompile oracle behavior, got result: %x", result))
}
return result[1:], result[0] == 1
}