From 9dcc7358b3eb60c597a23790e57ea84d144fbff7 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 5 Apr 2023 14:44:33 +0200 Subject: [PATCH 01/13] Add gasLimit to block header ...and include it in JSON-RPC responses. --- core/types/block.go | 13 ++++++++----- core/types/gen_header_json.go | 7 +++++++ internal/ethapi/api.go | 12 +----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index c035e0296e..a712762878 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -50,6 +50,7 @@ type Header struct { ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` @@ -62,11 +63,12 @@ type Header struct { // field type overrides for gencodec type headerMarshaling struct { - Number *hexutil.Big - GasUsed hexutil.Uint64 - Time hexutil.Uint64 - Extra hexutil.Bytes - Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Time hexutil.Uint64 + Extra hexutil.Bytes + Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -289,6 +291,7 @@ func CopyHeader(h *Header) *Header { ReceiptHash: h.ReceiptHash, Bloom: h.Bloom, Number: new(big.Int), + GasLimit: h.GasLimit, GasUsed: h.GasUsed, Time: h.Time, } diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index e2fc8096f0..29d6e49f99 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -23,6 +23,7 @@ func (h Header) MarshalJSON() ([]byte, error) { ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` @@ -36,6 +37,7 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.ReceiptHash = h.ReceiptHash enc.Bloom = h.Bloom enc.Number = (*hexutil.Big)(h.Number) + enc.GasLimit = hexutil.Uint64(h.GasLimit) enc.GasUsed = hexutil.Uint64(h.GasUsed) enc.Time = hexutil.Uint64(h.Time) enc.Extra = h.Extra @@ -53,6 +55,7 @@ func (h *Header) UnmarshalJSON(input []byte) error { ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` @@ -89,6 +92,10 @@ func (h *Header) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'number' for Header") } h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Header") + } + h.GasLimit = uint64(*dec.GasLimit) if dec.GasUsed == nil { return errors.New("missing required field 'gasUsed' for Header") } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1e2e7dfc3e..c6ec90a926 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -745,17 +745,6 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha // rpc response that ethers.js depends upon. // See https://github.com/celo-org/celo-blockchain/issues/1945 func addEthCompatibilityFields(ctx context.Context, block map[string]interface{}, b Backend, header *types.Header) { - hash := header.Hash() - numhash := rpc.BlockNumberOrHash{ - BlockHash: &hash, - } - gasLimit, err := b.GetRealBlockGasLimit(ctx, numhash) - if err != nil { - log.Debug("Not adding gasLimit to RPC response, failed to retrieve it", "block", header.Number.Uint64(), "err", err) - } else { - block["gasLimit"] = hexutil.Uint64(gasLimit) - } - // Providing nil as the currency address gets the gas price minimum for the native celo asset. baseFee, err := b.RealGasPriceMinimumForHeader(ctx, nil, header) if err != nil { @@ -1133,6 +1122,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} { "miner": head.Coinbase, "extraData": hexutil.Bytes(head.Extra), "size": hexutil.Uint64(head.Size()), + "gasLimit": hexutil.Uint64(head.GasLimit), "gasUsed": hexutil.Uint64(head.GasUsed), "timestamp": hexutil.Uint64(head.Time), "transactionsRoot": head.TxHash, From 85a61fd6be5ef8817f36c361412a179d386d169f Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 11 Apr 2023 12:08:34 +0200 Subject: [PATCH 02/13] Set GasLimit header field on prepareBlock --- miner/block.go | 1 + 1 file changed, 1 insertion(+) diff --git a/miner/block.go b/miner/block.go index c1d74655a7..de2436c5da 100644 --- a/miner/block.go +++ b/miner/block.go @@ -111,6 +111,7 @@ func prepareBlock(w *worker) (*blockState, error) { sysCtx: core.NewSysContractCallCtx(header, state.Copy(), w.chain), } b.gasPool = new(core.GasPool).AddGas(b.gasLimit) + header.GasLimit = b.gasLimit // Play our part in generating the random beacon. if w.isRunning() && random.IsRunning(vmRunner) { From 3df1593c479c21a2b47332494e8cd8cc1030362b Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 12 Apr 2023 10:43:56 +0200 Subject: [PATCH 03/13] Add GASLIMIT opcode --- core/vm/evm.go | 1 + core/vm/instructions.go | 5 +++++ core/vm/jump_table.go | 8 ++++++++ core/vm/opcodes.go | 1 + core/vm/runtime/runtime_test.go | 1 + core/vm/vmcontext/context.go | 1 + 6 files changed, 17 insertions(+) diff --git a/core/vm/evm.go b/core/vm/evm.go index d9b7e3a08c..0900dc0df0 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -89,6 +89,7 @@ type BlockContext struct { // Block information Coinbase common.Address // Provides information for COINBASE + GasLimit uint64 // Provides information for GASLIMIT BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 28b410ef69..6106fb2c9a 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -469,6 +469,11 @@ func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b return nil, nil } +func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) + return nil, nil +} + func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { scope.Stack.pop() return nil, nil diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 51a0f16658..ab1675e1a0 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -76,6 +76,14 @@ func newEspressoInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 + + // TODO: Move to instruction set for next hardfork + instructionSet[GASLIMIT] = &operation{ + execute: opGasLimit, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } return instructionSet } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index c1334c8df0..e910cb1277 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -99,6 +99,7 @@ const ( COINBASE TIMESTAMP NUMBER + GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 BASEFEE OpCode = 0x48 diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 7a64c17192..f648051e96 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -70,6 +70,7 @@ func TestEVM(t *testing.T) { Execute([]byte{ byte(vm.TIMESTAMP), + byte(vm.GASLIMIT), byte(vm.PUSH1), byte(vm.ORIGIN), byte(vm.BLOCKHASH), diff --git a/core/vm/vmcontext/context.go b/core/vm/vmcontext/context.go index 5a9c098ccf..8140094ebd 100644 --- a/core/vm/vmcontext/context.go +++ b/core/vm/vmcontext/context.go @@ -50,6 +50,7 @@ func NewBlockContext(header *types.Header, chain chainContext, txFeeRecipient *c GetHash: GetHashFn(header, chain), VerifySeal: VerifySealFn(header, chain), Coinbase: beneficiary, + GasLimit: header.GasLimit, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).SetUint64(header.Time), From 909e410fd1a10fc7cd3a0e5618fa006ac0584586 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 12 Apr 2023 15:31:28 +0200 Subject: [PATCH 04/13] No gasLimit when EthCompatible is off Preserve old Celo behavior when EthCompatible is turned off. --- internal/ethapi/api.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c6ec90a926..77a0144386 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -709,13 +709,17 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B if block != nil && err == nil { response, err := s.rpcMarshalBlock(ctx, block, true, fullTx) - if err == nil && s.b.RPCEthCompatibility() { - addEthCompatibilityFields(ctx, response, s.b, block.Header()) - if number == rpc.PendingBlockNumber { - // Pending blocks need to nil out a few fields - for _, field := range []string{"hash", "nonce", "miner"} { - response[field] = nil + if err == nil { + if s.b.RPCEthCompatibility() { + addEthCompatibilityFields(ctx, response, s.b, block.Header()) + if number == rpc.PendingBlockNumber { + // Pending blocks need to nil out a few fields + for _, field := range []string{"hash", "nonce", "miner"} { + response[field] = nil + } } + } else { + delete(response, "gasLimit") } } return response, err @@ -734,6 +738,8 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha } if s.b.RPCEthCompatibility() { addEthCompatibilityFields(ctx, result, s.b, block.Header()) + } else { + delete(result, "gasLimit") } return result, nil } From b05bb8cf67cf616aa6edd1c71414f43ad1ca9ccf Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 12 Apr 2023 15:32:17 +0200 Subject: [PATCH 05/13] tests: gasLimit is now available for pruned blocks --- e2e_test/ethersjs-api-check/test/test.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/e2e_test/ethersjs-api-check/test/test.ts b/e2e_test/ethersjs-api-check/test/test.ts index e3fc9943f9..b2ded59323 100644 --- a/e2e_test/ethersjs-api-check/test/test.ts +++ b/e2e_test/ethersjs-api-check/test/test.ts @@ -56,23 +56,13 @@ describe('ethers.js compatibility tests with state', () => { describe('ethers.js compatibility tests with no state', () => { - it('provider.getBlock throws exception (no gasLimit)', async () => { - let provider = new ethers.JsonRpcProvider(process.env.npm_config_networkaddr); - try { - await provider.getBlock(process.env.npm_config_blocknum as string); - } catch (e) { - return - } - assert.fail("Expecting exception to be thrown when getting block") - }); - - it('block has no gasLimit', async () => { + it('block has gasLimit', async () => { let provider = new ethers.JsonRpcProvider(process.env.npm_config_networkaddr); const fullBlock = await provider.send( 'eth_getBlockByNumber', [ethers.toQuantity(process.env.npm_config_blocknum as string), true] ) - assert.isFalse(fullBlock.hasOwnProperty('gasLimit')) + assert.isTrue(fullBlock.hasOwnProperty('gasLimit')) }); it('block has no baseFeePerGas', async () => { From 427732a1b03195eaf452dd60e6fd305dd34ec62b Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 12 Apr 2023 15:34:15 +0200 Subject: [PATCH 06/13] Optional gasLimit in RLP-serialized block headers to provide compatibility with blocks before the gasLimit has been added to the header. --- core/types/block.go | 3 ++- core/types/gen_header_json.go | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index a712762878..ba39db5434 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -50,7 +50,6 @@ type Header struct { ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` - GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` @@ -59,6 +58,8 @@ type Header struct { extraLock sync.Mutex extraValue *IstanbulExtra extraError error + + GasLimit uint64 `json:"gasLimit" rlp:"optional"` } // field type overrides for gencodec diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 29d6e49f99..fbdc0cf284 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -23,10 +23,10 @@ func (h Header) MarshalJSON() ([]byte, error) { ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" rlp:"optional"` Hash common.Hash `json:"hash"` } var enc Header @@ -37,10 +37,10 @@ func (h Header) MarshalJSON() ([]byte, error) { enc.ReceiptHash = h.ReceiptHash enc.Bloom = h.Bloom enc.Number = (*hexutil.Big)(h.Number) - enc.GasLimit = hexutil.Uint64(h.GasLimit) enc.GasUsed = hexutil.Uint64(h.GasUsed) enc.Time = hexutil.Uint64(h.Time) enc.Extra = h.Extra + enc.GasLimit = hexutil.Uint64(h.GasLimit) enc.Hash = h.Hash() return json.Marshal(&enc) } @@ -55,10 +55,10 @@ func (h *Header) UnmarshalJSON(input []byte) error { ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"` Number *hexutil.Big `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" rlp:"optional"` } var dec Header if err := json.Unmarshal(input, &dec); err != nil { @@ -92,10 +92,6 @@ func (h *Header) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'number' for Header") } h.Number = (*big.Int)(dec.Number) - if dec.GasLimit == nil { - return errors.New("missing required field 'gasLimit' for Header") - } - h.GasLimit = uint64(*dec.GasLimit) if dec.GasUsed == nil { return errors.New("missing required field 'gasUsed' for Header") } @@ -108,5 +104,8 @@ func (h *Header) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'extraData' for Header") } h.Extra = *dec.Extra + if dec.GasLimit != nil { + h.GasLimit = uint64(*dec.GasLimit) + } return nil } From b01e35f3069511a9f5b63562d322275df3a29499 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 30 May 2023 16:58:28 +0200 Subject: [PATCH 07/13] Keep gasLimit in RPC response if in block header See also https://github.com/celo-org/celo-blockchain/pull/2062#discussion_r1210288481 --- e2e_test/e2e_test.go | 3 +-- internal/ethapi/api.go | 18 ++++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 7dbc3e82b9..5510a9a45c 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -619,8 +619,7 @@ func TestEthersJSCompatibilityDisable(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) require.NoError(t, err) - _, ok = result["gasLimit"] - assert.False(t, ok, "gasLimit field should not be present on RPC block") + // gasLimit can be in the response, depending on whether it is in the block header or not _, ok = result["baseFeePerGas"] assert.False(t, ok, "baseFeePerGas field should not be present on RPC block") } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 77a0144386..c6ec90a926 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -709,17 +709,13 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B if block != nil && err == nil { response, err := s.rpcMarshalBlock(ctx, block, true, fullTx) - if err == nil { - if s.b.RPCEthCompatibility() { - addEthCompatibilityFields(ctx, response, s.b, block.Header()) - if number == rpc.PendingBlockNumber { - // Pending blocks need to nil out a few fields - for _, field := range []string{"hash", "nonce", "miner"} { - response[field] = nil - } + if err == nil && s.b.RPCEthCompatibility() { + addEthCompatibilityFields(ctx, response, s.b, block.Header()) + if number == rpc.PendingBlockNumber { + // Pending blocks need to nil out a few fields + for _, field := range []string{"hash", "nonce", "miner"} { + response[field] = nil } - } else { - delete(response, "gasLimit") } } return response, err @@ -738,8 +734,6 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha } if s.b.RPCEthCompatibility() { addEthCompatibilityFields(ctx, result, s.b, block.Header()) - } else { - delete(result, "gasLimit") } return result, nil } From ac8808dbe746806b3cfc6f941d53484db8bba1f0 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 30 May 2023 17:10:28 +0200 Subject: [PATCH 08/13] Move GASLIMIT opcode to G-fork --- core/vm/jump_table.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index ab1675e1a0..75829559f8 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -67,6 +67,12 @@ type JumpTable [256]*operation // constantinople, istanbul, petersburg, espresso and g-fork instructions. func newGforkInstructionSet() JumpTable { instructionSet := newEspressoInstructionSet() + instructionSet[GASLIMIT] = &operation{ + execute: opGasLimit, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + } return instructionSet } @@ -76,14 +82,6 @@ func newEspressoInstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 - - // TODO: Move to instruction set for next hardfork - instructionSet[GASLIMIT] = &operation{ - execute: opGasLimit, - constantGas: GasQuickStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - } return instructionSet } From 1ea72921e5ef6d68921fc7ed15b75234a0b7365f Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 31 May 2023 10:31:03 +0200 Subject: [PATCH 09/13] mycelo: enable GFork by default We always want to run and test the latest hard fork by default. --- mycelo/genesis/genesis.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mycelo/genesis/genesis.go b/mycelo/genesis/genesis.go index 0d1112f8c1..d2f68e62d1 100644 --- a/mycelo/genesis/genesis.go +++ b/mycelo/genesis/genesis.go @@ -28,6 +28,7 @@ func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Addr ChurritoBlock: common.Big0, DonutBlock: common.Big0, EspressoBlock: common.Big0, + GForkBlock: common.Big0, } // Make admin account manager of Governance & Reserve From c75a48d0f7cbb5fa2789536de338c63d384f7d54 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 31 May 2023 11:46:10 +0200 Subject: [PATCH 10/13] Ensure correct presence of gasLimit in RPC Before GFork, gasLimit is not in the header, so we have to * Add it if RPCEthCompatibility * Remove it if not RPCEthCompatibility, since it is now in the header struct and we would otherwise return a zero value After GFork it should be returned directly from the header, even if not RPCEthCompatibility, since it is now part of the header hash. --- e2e_test/e2e_test.go | 52 ++++++++++++++++++++++++++++++++++++++++-- internal/ethapi/api.go | 39 +++++++++++++++++++++++-------- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index 5510a9a45c..cfb227f5be 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -606,7 +606,7 @@ func TestEthersJSCompatibilityDisable(t *testing.T) { _, ok = result["baseFeePerGas"] assert.True(t, ok, "baseFeePerGas field should be present on RPC block") - // Turn of compatibility and check fields are not present + // Turn off compatibility and check fields are not present ec.RPCEthCompatibility = false network, shutdown, err = test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -619,7 +619,55 @@ func TestEthersJSCompatibilityDisable(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) require.NoError(t, err) - // gasLimit can be in the response, depending on whether it is in the block header or not + // After GFork, gasLimit should be returned directly from the header, even if + // RPCEthCompatibility is off, since it is now part of the header hash. + _, ok = result["gasLimit"] + assert.True(t, ok, "gasLimit field must be present on RPC block after GFork") + _, ok = result["baseFeePerGas"] + assert.False(t, ok, "baseFeePerGas field must be present on RPC block") +} + +// This test checks the functionality of the configuration to enable/disable +// returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks before the GFork happened. +// GFork is relevant because it added the gasLimit to the header. +func TestEthersJSCompatibilityDisableBeforeGFork(t *testing.T) { + ac := test.AccountConfig(1, 1) + gc, ec, err := test.BuildConfig(ac) + gc.Hardforks.GForkBlock = nil + require.NoError(t, err) + + // Check fields present (compatibility set by default) + network, shutdown, err := test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + result := make(map[string]interface{}) + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) + require.NoError(t, err) + + _, ok := result["gasLimit"] + assert.True(t, ok, "gasLimit field should be present on RPC block") + _, ok = result["baseFeePerGas"] + assert.True(t, ok, "baseFeePerGas field should be present on RPC block") + + // Turn off compatibility and check fields are not present + ec.RPCEthCompatibility = false + network, shutdown, err = test.NewNetwork(ac, gc, ec) + require.NoError(t, err) + defer shutdown() + + ctx, cancel = context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + result = make(map[string]interface{}) + err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) + require.NoError(t, err) + + _, ok = result["gasLimit"] + assert.False(t, ok, "gasLimit field should not be present on RPC block before GFork") _, ok = result["baseFeePerGas"] assert.False(t, ok, "baseFeePerGas field should not be present on RPC block") } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c6ec90a926..e640a59f07 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -709,9 +709,9 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B if block != nil && err == nil { response, err := s.rpcMarshalBlock(ctx, block, true, fullTx) - if err == nil && s.b.RPCEthCompatibility() { - addEthCompatibilityFields(ctx, response, s.b, block.Header()) - if number == rpc.PendingBlockNumber { + if err == nil { + addEthCompatibilityFields(ctx, response, s.b, block) + if s.b.RPCEthCompatibility() && number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil @@ -732,9 +732,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha if err != nil { return nil, err } - if s.b.RPCEthCompatibility() { - addEthCompatibilityFields(ctx, result, s.b, block.Header()) - } + addEthCompatibilityFields(ctx, result, s.b, block) return result, nil } return nil, err @@ -744,16 +742,39 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha // and ethers.js (and potentially other web3 clients) by adding fields to our // rpc response that ethers.js depends upon. // See https://github.com/celo-org/celo-blockchain/issues/1945 -func addEthCompatibilityFields(ctx context.Context, block map[string]interface{}, b Backend, header *types.Header) { +func addEthCompatibilityFields(ctx context.Context, response map[string]interface{}, b Backend, block *types.Block) { + isGFork := b.ChainConfig().IsGFork(block.Number()) + if !b.RPCEthCompatibility() { + if !isGFork { + delete(response, "gasLimit") + } + return + } + + header := block.Header() + if !isGFork { + // Before GFork, the header did not include the gasLimit, so we have to manually add it for eth-compatible RPC responses. + hash := header.Hash() + numhash := rpc.BlockNumberOrHash{ + BlockHash: &hash, + } + gasLimit, err := b.GetRealBlockGasLimit(ctx, numhash) + if err != nil { + log.Debug("Not adding gasLimit to RPC response, failed to retrieve it", "block", header.Number.Uint64(), "err", err) + } else { + response["gasLimit"] = hexutil.Uint64(gasLimit) + } + } + // Providing nil as the currency address gets the gas price minimum for the native celo asset. baseFee, err := b.RealGasPriceMinimumForHeader(ctx, nil, header) if err != nil { log.Debug("Not adding baseFeePerGas to RPC response, failed to retrieve gas price minimum", "block", header.Number.Uint64(), "err", err) } else { - block["baseFeePerGas"] = (*hexutil.Big)(baseFee) + response["baseFeePerGas"] = (*hexutil.Big)(baseFee) } - block["difficulty"] = "0x0" + response["difficulty"] = "0x0" } // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true From 423bd2a3d661971c02e5077e73437aaf306fefd7 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Wed, 31 May 2023 15:10:15 +0200 Subject: [PATCH 11/13] Only add gasLimit to header after GFork --- miner/block.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/miner/block.go b/miner/block.go index de2436c5da..cf01eeb44f 100644 --- a/miner/block.go +++ b/miner/block.go @@ -111,7 +111,9 @@ func prepareBlock(w *worker) (*blockState, error) { sysCtx: core.NewSysContractCallCtx(header, state.Copy(), w.chain), } b.gasPool = new(core.GasPool).AddGas(b.gasLimit) - header.GasLimit = b.gasLimit + if w.chainConfig.IsGFork(header.Number) { + header.GasLimit = b.gasLimit + } // Play our part in generating the random beacon. if w.isRunning() && random.IsRunning(vmRunner) { From 06abf2fc7d44ba1b8959752fde6112097d6aea1a Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Mon, 5 Jun 2023 12:23:32 +0200 Subject: [PATCH 12/13] Update monorepo_commit Required changes: * Using gasLimit in hash if present * Turn GFork off for CIP-35 test --- monorepo_commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monorepo_commit b/monorepo_commit index 980711852a..7d0ae641ec 100644 --- a/monorepo_commit +++ b/monorepo_commit @@ -1 +1 @@ -ad770ace57ec3fa1bc1fd540d0321ab2e7317076 +d216e7599f499adec2e0200e599f601ea5424cde From 8dda17706e074137872eafb97030e6846be50af0 Mon Sep 17 00:00:00 2001 From: Karl Bartel Date: Tue, 6 Jun 2023 12:23:27 +0200 Subject: [PATCH 13/13] Fix pending block when no RPCEthCompatibility The RPCEthCompatibility check was not intended here. See https://github.com/celo-org/celo-blockchain/pull/2062#discussion_r1218286282 --- internal/ethapi/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index e640a59f07..d54b319bc7 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -711,7 +711,7 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B if err == nil { addEthCompatibilityFields(ctx, response, s.b, block) - if s.b.RPCEthCompatibility() && number == rpc.PendingBlockNumber { + if number == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil