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

Add GASLIMIT opcode #2062

Merged
merged 13 commits into from
Jun 6, 2023
14 changes: 9 additions & 5 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,18 @@ type Header struct {
extraLock sync.Mutex
extraValue *IstanbulExtra
extraError error

GasLimit uint64 `json:"gasLimit" rlp:"optional"`
palango marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan here is to reorder the filed when we add the remaining ones?

}

// 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
Expand Down Expand Up @@ -289,6 +292,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,
}
Expand Down
6 changes: 6 additions & 0 deletions core/types/gen_header_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 5 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
1 change: 1 addition & 0 deletions core/vm/opcodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const (
COINBASE
TIMESTAMP
NUMBER
GASLIMIT OpCode = 0x45
CHAINID OpCode = 0x46
SELFBALANCE OpCode = 0x47
BASEFEE OpCode = 0x48
Expand Down
1 change: 1 addition & 0 deletions core/vm/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions core/vm/vmcontext/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down
51 changes: 49 additions & 2 deletions e2e_test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -619,8 +619,55 @@ func TestEthersJSCompatibilityDisable(t *testing.T) {
err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true)
require.NoError(t, err)

// 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.False(t, ok, "gasLimit field should not be present on RPC block")
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")
}
14 changes: 2 additions & 12 deletions e2e_test/ethersjs-api-check/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
43 changes: 27 additions & 16 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,8 @@ 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 err == nil {
addEthCompatibilityFields(ctx, response, s.b, block)
if number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
Expand All @@ -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
Expand All @@ -744,27 +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) {
hash := header.Hash()
numhash := rpc.BlockNumberOrHash{
BlockHash: &hash,
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
}
gasLimit, err := b.GetRealBlockGasLimit(ctx, numhash)
palango marked this conversation as resolved.
Show resolved Hide resolved
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)

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
Expand Down Expand Up @@ -1133,6 +1143,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,
Expand Down
3 changes: 3 additions & 0 deletions miner/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ func prepareBlock(w *worker) (*blockState, error) {
sysCtx: core.NewSysContractCallCtx(header, state.Copy(), w.chain),
}
b.gasPool = new(core.GasPool).AddGas(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) {
Expand Down
2 changes: 1 addition & 1 deletion monorepo_commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ad770ace57ec3fa1bc1fd540d0321ab2e7317076
d216e7599f499adec2e0200e599f601ea5424cde
1 change: 1 addition & 0 deletions mycelo/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down