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 and the parent block has a base fee
// then use that.
if config.Cel2Time != nil && *config.Cel2Time == time && parent.BaseFee != nil {
return parent.BaseFee
}

// 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
}