Skip to content

Commit 99d2925

Browse files
piersykarlb
authored andcommitted
Modifications to support snap sync (#152)
* Add code to handle block receipts Add code to handle the extra block receipt added by celo to the list of receipts in a block and also adds the GetBlockReceipt rpc api which was an additional api added by celo to allow retrieveal of the block receipt. * Ensure non nil difficulty in pre-gingerbread blocks Nil difficulty in headers causes problems because it is assumed by the go-ethereum codebase that difficulty is non nil. The specific problem requiring this fix was a NPE in Downloader.processHeaders where the total difficulty (td) is calculated by adding individual header difficulty values. Rather than try to fix this specific case it seems safer to ensure blocks have a non nil Difficulty which should help future proof our code since it upholds the assumptions of the go-ethereum codebase. * Skip uncle hash check for pre gingerbread headers Pre gingerbread headers do not have a valid uncle hash because they have no notion of uncles. * Handle choosing the cel2 transition block base fee If the parent block has a base fee then we use that, otherwise we use the initial base fee.
1 parent 78dcd8a commit 99d2925

File tree

8 files changed

+118
-14
lines changed

8 files changed

+118
-14
lines changed

consensus/misc/eip1559/eip1559.go

+6
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ func ValidateHoloceneExtraData(extra []byte) error {
136136
// CalcBaseFee calculates the basefee of the header.
137137
// The time belongs to the new block to check which upgrades are active.
138138
func CalcBaseFee(config *params.ChainConfig, parent *types.Header, time uint64) *big.Int {
139+
// If this is the cel2 transition block and the parent block has a base fee
140+
// then use that.
141+
if config.Cel2Time != nil && *config.Cel2Time == time && parent.BaseFee != nil {
142+
return parent.BaseFee
143+
}
144+
139145
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
140146
if !config.IsLondon(parent.Number) {
141147
return new(big.Int).SetUint64(params.InitialBaseFee)

core/genesis.go

+3
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,9 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
522522
if g.Difficulty == nil && g.Mixhash == (common.Hash{}) {
523523
head.Difficulty = params.GenesisDifficulty
524524
}
525+
} else if g.Difficulty == nil {
526+
// In the case of migrated chains we ensure a zero rather than nil difficulty.
527+
head.Difficulty = new(big.Int)
525528
}
526529
if g.Config != nil && g.Config.IsLondon(common.Big0) {
527530
if g.BaseFee != nil {

core/types/celo_block.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {
5353
h.GasUsed = decodedHeader.GasUsed
5454
h.Time = decodedHeader.Time
5555
h.Extra = decodedHeader.Extra
56+
h.Difficulty = new(big.Int)
5657
} else {
5758
// After gingerbread
5859
decodedHeader := afterGingerbreadHeader{}
@@ -85,13 +86,7 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {
8586

8687
// EncodeRLP implements encodes the Header to an RLP data stream.
8788
func (h *Header) EncodeRLP(w io.Writer) error {
88-
// We check for a pre gingerbread header by looking for (GasLimit == 0)
89-
// here. We don't use Difficulty because CopyHeader can end up setting a
90-
// nil Difficulty to a zero difficulty, so testing for nil difficulty is
91-
// not reliable, and post gingerbread difficulty is hardcoded to zero. Also
92-
// testing for base fee is not reliable because some older eth blocks had
93-
// no base fee and they are used in some tests.
94-
if h.GasLimit == 0 {
89+
if h.IsPreGingerbread() {
9590
// Encode the header
9691
encodedHeader := beforeGingerbreadHeader{
9792
ParentHash: h.ParentHash,
@@ -152,3 +147,15 @@ func isPreGingerbreadHeader(buf []byte) (bool, error) {
152147

153148
return contentSize == common.AddressLength, nil
154149
}
150+
151+
// Returns if the header is a gingerbread header by looking at the gas limit.
152+
func (h *Header) IsPreGingerbread() bool {
153+
// We check for a pre gingerbread header by looking for (GasLimit == 0)
154+
// here. We don't use Difficulty because we ensure that headers have a zero
155+
// difficulty, even if it's not set in the rlp encoded form (we do this
156+
// because the go ethereum codebase assumed non nil difficulties) and post
157+
// gingerbread difficulty is hardcoded to zero. Also testing for base fee
158+
// is not reliable because some older eth blocks had no base fee and they
159+
// are used in some tests.
160+
return h.GasLimit == 0
161+
}

core/types/receipt.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -556,13 +556,16 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
556556
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)
557557

558558
logIndex := uint(0)
559-
if len(txs) != len(rs) {
559+
560+
// If rs are one longer than txs it indicates the presence of a celo block receipt.
561+
if len(txs) != len(rs) && len(txs)+1 != len(rs) {
560562
return errors.New("transaction and receipt count mismatch")
561563
}
562-
for i := 0; i < len(rs); i++ {
564+
for i := 0; i < len(txs); i++ {
563565
// The transaction type and hash can be retrieved from the transaction itself
564566
rs[i].Type = txs[i].Type()
565567
rs[i].TxHash = txs[i].Hash()
568+
566569
// The CeloDynamicFeeTxs set the baseFee in the receipt
567570
if txs[i].Type() != CeloDynamicFeeTxV2Type {
568571
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
@@ -611,6 +614,20 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
611614
logIndex++
612615
}
613616
}
617+
618+
// This is a celo block receipt, which uses the block hash in place of the tx hash.
619+
if len(txs)+1 == len(rs) {
620+
j := len(txs)
621+
for k := 0; k < len(rs[j].Logs); k++ {
622+
rs[j].Logs[k].BlockNumber = number
623+
rs[j].Logs[k].BlockHash = hash
624+
rs[j].Logs[k].TxHash = hash
625+
rs[j].Logs[k].TxIndex = uint(j)
626+
rs[j].Logs[k].Index = logIndex
627+
logIndex++
628+
}
629+
}
630+
614631
if config.Optimism != nil && len(txs) >= 2 && config.IsBedrock(new(big.Int).SetUint64(number)) { // need at least an info tx and a non-info tx
615632
gasParams, err := extractL1GasParams(config, time, txs[0].Data())
616633
if err != nil {

eth/downloader/queue.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,8 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
793793
if txListHashes[index] != header.TxHash {
794794
return errInvalidBody
795795
}
796-
if uncleListHashes[index] != header.UncleHash {
796+
// Pre gingerbread headers do not have a valid uncle hash.
797+
if !header.IsPreGingerbread() && uncleListHashes[index] != header.UncleHash {
797798
return errInvalidBody
798799
}
799800
if header.WithdrawalsHash == nil {

eth/filters/filter.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,13 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*typ
307307
for i, log := range logs {
308308
// Copy log not to modify cache elements
309309
logcopy := *log
310-
logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
310+
// Block receipts reference a non existent transaction ocurring after the last transaction.
311+
// We use the block hash in place of the transaction hash for the block receipt.
312+
if logcopy.TxIndex == uint(len(body.Transactions)) {
313+
logcopy.TxHash = logcopy.BlockHash
314+
} else {
315+
logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
316+
}
311317
logs[i] = &logcopy
312318
}
313319
return logs, nil

internal/ethapi/api.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,8 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
10331033
return nil, err
10341034
}
10351035
txs := block.Transactions()
1036-
if len(txs) != len(receipts) {
1036+
// Legacy Celo blocks sometimes include an extra block receipt. See https://docs.celo.org/developer/migrate/from-ethereum#core-contract-calls
1037+
if len(txs) != len(receipts) && len(txs)+1 != len(receipts) {
10371038
return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts))
10381039
}
10391040

@@ -1042,9 +1043,12 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
10421043

10431044
result := make([]map[string]interface{}, len(receipts))
10441045
for i, receipt := range receipts {
1045-
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i, api.b.ChainConfig())
1046+
if i == len(txs) {
1047+
result[i] = marshalBlockReceipt(receipt, block.Hash(), block.NumberU64(), i)
1048+
} else {
1049+
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i, api.b.ChainConfig())
1050+
}
10461051
}
1047-
10481052
return result, nil
10491053
}
10501054

internal/ethapi/celo_block_receipt.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package ethapi
2+
3+
import (
4+
"context"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/ethereum/go-ethereum/common/hexutil"
8+
"github.com/ethereum/go-ethereum/core/types"
9+
)
10+
11+
// GetBlockReceipt returns "system calls" receipt for the block with the given block hash.
12+
func (s *BlockChainAPI) GetBlockReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
13+
block, err := s.b.BlockByHash(ctx, hash)
14+
if block == nil || err != nil {
15+
// If no header with that hash is found, err gives "header for hash not found".
16+
// But we return nil with no error, to match the behavior of eth_getBlockByHash and eth_getTransactionReceipt in these cases.
17+
return nil, nil
18+
}
19+
index := block.Transactions().Len()
20+
blockNumber := block.NumberU64()
21+
receipts, err := s.b.GetReceipts(ctx, block.Hash())
22+
// GetReceipts() doesn't return an error if things go wrong, so we also check len(receipts)
23+
if err != nil || len(receipts) < index {
24+
return nil, err
25+
}
26+
27+
var receipt *types.Receipt
28+
if len(receipts) == index {
29+
// The block didn't have any logs from system calls and no receipt was created.
30+
// So we create an empty receipt to return, similarly to how system receipts are created.
31+
receipt = types.NewReceipt(nil, false, 0)
32+
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
33+
} else {
34+
receipt = receipts[index]
35+
}
36+
return marshalBlockReceipt(receipt, hash, blockNumber, index), nil
37+
}
38+
39+
// marshalBlockReceipt marshals a Celo block receipt into a JSON object. See https://docs.celo.org/developer/migrate/from-ethereum#core-contract-calls
40+
func marshalBlockReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, index int) map[string]interface{} {
41+
fields := map[string]interface{}{
42+
"blockHash": blockHash,
43+
"blockNumber": hexutil.Uint64(blockNumber),
44+
"transactionHash": blockHash,
45+
"transactionIndex": hexutil.Uint64(index),
46+
"from": common.Address{},
47+
"to": nil,
48+
"gasUsed": hexutil.Uint64(0),
49+
"cumulativeGasUsed": hexutil.Uint64(0),
50+
"contractAddress": nil,
51+
"logs": receipt.Logs,
52+
"logsBloom": receipt.Bloom,
53+
"type": hexutil.Uint(0),
54+
"status": hexutil.Uint(types.ReceiptStatusSuccessful),
55+
}
56+
if receipt.Logs == nil {
57+
fields["logs"] = []*types.Log{}
58+
}
59+
return fields
60+
}

0 commit comments

Comments
 (0)