Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifications to support snap sync #152

Merged
merged 9 commits into from
Jun 25, 2024
6 changes: 6 additions & 0 deletions consensus/misc/eip1559/eip1559.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
// CalcBaseFee calculates the basefee of the header.
// The time belongs to the new block to check if Canyon is activted or not
func CalcBaseFee(config *params.ChainConfig, parent *types.Header, time uint64) *big.Int {
// If this is the cel2 transition block then we use the initial base fee
// from the Cel2Config of the chain config.
if config.Cel2Time != nil && *config.Cel2Time == time {
return new(big.Int).SetUint64(config.Cel2Config.TransitionBlockBaseFee)
palango marked this conversation as resolved.
Show resolved Hide resolved
}

// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number) {
return new(big.Int).SetUint64(params.InitialBaseFee)
Expand Down
3 changes: 3 additions & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,9 @@ func (g *Genesis) ToBlock() *types.Block {
if g.Difficulty == nil && g.Mixhash == (common.Hash{}) {
head.Difficulty = params.GenesisDifficulty
}
} else if g.Difficulty == nil {
// In the case of migrated chains we ensure a zero rather than nil difficulty.
head.Difficulty = new(big.Int)
palango marked this conversation as resolved.
Show resolved Hide resolved
}
if g.Config != nil && g.Config.IsLondon(common.Big0) {
if g.BaseFee != nil {
Expand Down
21 changes: 14 additions & 7 deletions core/types/celo_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {
h.GasUsed = decodedHeader.GasUsed
h.Time = decodedHeader.Time
h.Extra = decodedHeader.Extra
h.Difficulty = new(big.Int)
} else {
// After gingerbread
decodedHeader := afterGingerbreadHeader{}
Expand Down Expand Up @@ -85,13 +86,7 @@ func (h *Header) DecodeRLP(s *rlp.Stream) error {

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

return contentSize == common.AddressLength, nil
}

// Returns if the header is a gingerbread header by looking at the gas limit.
func (h *Header) IsPreGingerbread() bool {
// We check for a pre gingerbread header by looking for (GasLimit == 0)
// here. We don't use Difficulty because we ensure that headers have a zero
// difficulty, even if it's not set in the rlp encoded form (we do this
// because the go ethereum codebase assumed non nil difficulties) and post
// gingerbread difficulty is hardcoded to zero. Also testing for base fee
// is not reliable because some older eth blocks had no base fee and they
// are used in some tests.
return h.GasLimit == 0
}
21 changes: 19 additions & 2 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,16 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
signer := MakeSigner(config, new(big.Int).SetUint64(number), time)

logIndex := uint(0)
if len(txs) != len(rs) {

// If rs are one longer than txs it indicates the presence of a celo block receipt.
if len(txs) != len(rs) && len(txs)+1 != len(rs) {
palango marked this conversation as resolved.
Show resolved Hide resolved
return errors.New("transaction and receipt count mismatch")
}
for i := 0; i < len(rs); i++ {
for i := 0; i < len(txs); i++ {
// The transaction type and hash can be retrieved from the transaction itself
rs[i].Type = txs[i].Type()
rs[i].TxHash = txs[i].Hash()

// The CeloDynamicFeeTxs set the baseFee in the receipt
if txs[i].Type() != CeloDynamicFeeTxV2Type {
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
Expand Down Expand Up @@ -655,6 +658,20 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
logIndex++
}
}

// This is a celo block receipt, which uses the block hash in place of the tx hash.
if len(txs)+1 == len(rs) {
j := len(txs)
for k := 0; k < len(rs[j].Logs); k++ {
rs[j].Logs[k].BlockNumber = number
rs[j].Logs[k].BlockHash = hash
rs[j].Logs[k].TxHash = hash
rs[j].Logs[k].TxIndex = uint(j)
rs[j].Logs[k].Index = logIndex
logIndex++
}
}

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
gasParams, err := extractL1GasParams(config, time, txs[0].Data())
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion eth/downloader/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,8 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
if txListHashes[index] != header.TxHash {
return errInvalidBody
}
if uncleListHashes[index] != header.UncleHash {
// Pre gingerbread headers do not have a valid uncle hash.
if !header.IsPreGingerbread() && uncleListHashes[index] != header.UncleHash {
return errInvalidBody
}
if header.WithdrawalsHash == nil {
Expand Down
8 changes: 7 additions & 1 deletion eth/filters/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,13 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*typ
for i, log := range logs {
// Copy log not to modify cache elements
logcopy := *log
logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
// Block receipts reference a non existent transaction ocurring after the last transaction.
// We use the block hash in place of the transaction hash for the block receipt.
if logcopy.TxIndex == uint(len(body.Transactions)) {
logcopy.TxHash = logcopy.BlockHash
} else {
logcopy.TxHash = body.Transactions[logcopy.TxIndex].Hash()
}
logs[i] = &logcopy
}
return logs, nil
Expand Down
10 changes: 7 additions & 3 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,8 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.
return nil, err
}
txs := block.Transactions()
if len(txs) != len(receipts) {
// Legacy Celo blocks sometimes include an extra block receipt. See https://docs.celo.org/developer/migrate/from-ethereum#core-contract-calls
if len(txs) != len(receipts) && len(txs)+1 != len(receipts) {
return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts))
}

Expand All @@ -1024,9 +1025,12 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.

result := make([]map[string]interface{}, len(receipts))
for i, receipt := range receipts {
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i, s.b.ChainConfig())
if i == len(txs) {
result[i] = marshalBlockReceipt(receipt, block.Hash(), block.NumberU64(), i)
} else {
result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i, s.b.ChainConfig())
}
}

return result, nil
}

Expand Down
60 changes: 60 additions & 0 deletions internal/ethapi/celo_block_receipt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package ethapi

import (
"context"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
)

// GetBlockReceipt returns "system calls" receipt for the block with the given block hash.
func (s *BlockChainAPI) GetBlockReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
palango marked this conversation as resolved.
Show resolved Hide resolved
block, err := s.b.BlockByHash(ctx, hash)
if block == nil || err != nil {
// If no header with that hash is found, err gives "header for hash not found".
// But we return nil with no error, to match the behavior of eth_getBlockByHash and eth_getTransactionReceipt in these cases.
return nil, nil
}
index := block.Transactions().Len()
blockNumber := block.NumberU64()
receipts, err := s.b.GetReceipts(ctx, block.Hash())
// GetReceipts() doesn't return an error if things go wrong, so we also check len(receipts)
if err != nil || len(receipts) < index {
return nil, err
}

var receipt *types.Receipt
if len(receipts) == index {
// The block didn't have any logs from system calls and no receipt was created.
// So we create an empty receipt to return, similarly to how system receipts are created.
receipt = types.NewReceipt(nil, false, 0)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
} else {
receipt = receipts[index]
}
return marshalBlockReceipt(receipt, hash, blockNumber, index), nil
}

// marshalBlockReceipt marshals a Celo block receipt into a JSON object. See https://docs.celo.org/developer/migrate/from-ethereum#core-contract-calls
func marshalBlockReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, index int) map[string]interface{} {
fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
"transactionHash": blockHash,
"transactionIndex": hexutil.Uint64(index),
"from": common.Address{},
"to": nil,
"gasUsed": hexutil.Uint64(0),
"cumulativeGasUsed": hexutil.Uint64(0),
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
"type": hexutil.Uint(0),
"status": hexutil.Uint(types.ReceiptStatusSuccessful),
}
if receipt.Logs == nil {
fields["logs"] = []*types.Log{}
}
return fields
}
19 changes: 19 additions & 0 deletions params/celo_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package params

// Cel2Config holds config required when running a cel2 chain.
type Cel2Config struct {
// TransitionBlockBaseFee is the base fee for the transition block in a
// migrated chain. it must be set for migrated chains during the migration
// to the value in the transition block. For non migrated chains it does
// not need to be set.This is required because we need
// eip1559.CalcBaseFee(config *params.ChainConfig, parent *types.Header,
// time uint64) to be able to return the correct base fee for the
// transition block and CalcBaseFee does not have access to the current
// header so cannot know what the base fee should be. We can't just use the
// base fee of the parent either because if we are transitioning at a
// pre-gingerbread block then it won't have a base fee, so this seems like
// the least invasive approach. Alternatively we could change the signature
// of CalcBaseFee to include the current header but that would require
// changing code in a number of places.
TransitionBlockBaseFee uint64
}
2 changes: 2 additions & 0 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ type ChainConfig struct {

Cel2Time *uint64 `json:"cel2Time,omitempty"` // Cel2 switch time (nil = no fork, 0 = already on optimism cel2)

Cel2Config *Cel2Config `json:"cel2Config,omitempty"` // Cel2 config

// TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`
Expand Down