Skip to content

Commit f96be2f

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 f3bcc7c commit f96be2f

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
@@ -58,6 +58,12 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
5858
// CalcBaseFee calculates the basefee of the header.
5959
// The time belongs to the new block to check if Canyon is activted or not
6060
func CalcBaseFee(config *params.ChainConfig, parent *types.Header, time uint64) *big.Int {
61+
// If this is the cel2 transition block and the parent block has a base fee
62+
// then use that.
63+
if config.Cel2Time != nil && *config.Cel2Time == time && parent.BaseFee != nil {
64+
return parent.BaseFee
65+
}
66+
6167
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
6268
if !config.IsLondon(parent.Number) {
6369
return new(big.Int).SetUint64(params.InitialBaseFee)

core/genesis.go

+3
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,9 @@ func (g *Genesis) ToBlock() *types.Block {
535535
if g.Difficulty == nil && g.Mixhash == (common.Hash{}) {
536536
head.Difficulty = params.GenesisDifficulty
537537
}
538+
} else if g.Difficulty == nil {
539+
// In the case of migrated chains we ensure a zero rather than nil difficulty.
540+
head.Difficulty = new(big.Int)
538541
}
539542
if g.Config != nil && g.Config.IsLondon(common.Big0) {
540543
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
@@ -600,13 +600,16 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
600600
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)
601601

602602
logIndex := uint(0)
603-
if len(txs) != len(rs) {
603+
604+
// If rs are one longer than txs it indicates the presence of a celo block receipt.
605+
if len(txs) != len(rs) && len(txs)+1 != len(rs) {
604606
return errors.New("transaction and receipt count mismatch")
605607
}
606-
for i := 0; i < len(rs); i++ {
608+
for i := 0; i < len(txs); i++ {
607609
// The transaction type and hash can be retrieved from the transaction itself
608610
rs[i].Type = txs[i].Type()
609611
rs[i].TxHash = txs[i].Hash()
612+
610613
// The CeloDynamicFeeTxs set the baseFee in the receipt
611614
if txs[i].Type() != CeloDynamicFeeTxV2Type {
612615
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
@@ -655,6 +658,20 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
655658
logIndex++
656659
}
657660
}
661+
662+
// This is a celo block receipt, which uses the block hash in place of the tx hash.
663+
if len(txs)+1 == len(rs) {
664+
j := len(txs)
665+
for k := 0; k < len(rs[j].Logs); k++ {
666+
rs[j].Logs[k].BlockNumber = number
667+
rs[j].Logs[k].BlockHash = hash
668+
rs[j].Logs[k].TxHash = hash
669+
rs[j].Logs[k].TxIndex = uint(j)
670+
rs[j].Logs[k].Index = logIndex
671+
logIndex++
672+
}
673+
}
674+
658675
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
659676
gasParams, err := extractL1GasParams(config, time, txs[0].Data())
660677
if err != nil {

eth/downloader/queue.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,8 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
792792
if txListHashes[index] != header.TxHash {
793793
return errInvalidBody
794794
}
795-
if uncleListHashes[index] != header.UncleHash {
795+
// Pre gingerbread headers do not have a valid uncle hash.
796+
if !header.IsPreGingerbread() && uncleListHashes[index] != header.UncleHash {
796797
return errInvalidBody
797798
}
798799
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
@@ -1032,7 +1032,8 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp
10321032
return nil, err
10331033
}
10341034
txs := block.Transactions()
1035-
if len(txs) != len(receipts) {
1035+
// Legacy Celo blocks sometimes include an extra block receipt. See https://docs.celo.org/developer/migrate/from-ethereum#core-contract-calls
1036+
if len(txs) != len(receipts) && len(txs)+1 != len(receipts) {
10361037
return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts))
10371038
}
10381039

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

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

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)