Skip to content

Commit

Permalink
Merge pull request ethereum#18 from QuarkChain/blob
Browse files Browse the repository at this point in the history
Increase code size limit from 24k to 256K
  • Loading branch information
qizhou authored Feb 17, 2022
2 parents c9e6cab + e045939 commit c833536
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 5 deletions.
2 changes: 1 addition & 1 deletion cmd/puppeth/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.MaxCodeSize = params.MaxCodeSize
spec.Params.MaxCodeSize = params.MaxCodeSizeHard
// geth has it set from zero
spec.Params.MaxCodeSizeTransition = 0

Expand Down
18 changes: 18 additions & 0 deletions core/rawdb/accessors_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package rawdb

import (
"encoding/binary"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -61,6 +63,16 @@ func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
return data
}

// ReadCodeSize retrieves the contract code size of the provided code hash.
// Return 0 if not found
func ReadCodeSize(db ethdb.KeyValueReader, hash common.Hash) int {
data, _ := db.Get(codeSizeKey(hash))
if len(data) != 4 {
return 0
}
return int(binary.BigEndian.Uint32(data))
}

// HasCodeWithPrefix checks if the contract code corresponding to the
// provided code hash is present in the db. This function will only check
// presence using the prefix-scheme.
Expand All @@ -74,6 +86,12 @@ func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
if err := db.Put(codeKey(hash), code); err != nil {
log.Crit("Failed to store contract code", "err", err)
}

var sizeData [4]byte
binary.BigEndian.PutUint32(sizeData[:], uint32(len(code)))
if err := db.Put(codeSizeKey(hash), sizeData[:]); err != nil {
log.Crit("Failed to store contract code size", "err", err)
}
}

// DeleteCode deletes the specified contract code from the database.
Expand Down
5 changes: 5 additions & 0 deletions core/rawdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ var (
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
CodePrefix = []byte("c") // CodePrefix + code hash -> account code
CodeSizePrefix = []byte("s") // CodePrefixSize

PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
configPrefix = []byte("ethereum-config-") // config prefix for the db
Expand Down Expand Up @@ -220,6 +221,10 @@ func codeKey(hash common.Hash) []byte {
return append(CodePrefix, hash.Bytes()...)
}

func codeSizeKey(hash common.Hash) []byte {
return append(CodeSizePrefix, hash.Bytes()...)
}

// IsCodeKey reports whether the given byte slice is the key of contract code,
// if so return the raw code hash as well.
func IsCodeKey(key []byte) (bool, []byte) {
Expand Down
6 changes: 6 additions & 0 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil
}

size := rawdb.ReadCodeSize(db.db.DiskDB(), codeHash)
if size != 0 {
return size, nil
}

code, err := db.ContractCode(addrHash, codeHash)
return len(code), err
}
Expand Down
2 changes: 1 addition & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const (
// non-trivial consequences: larger transactions are significantly harder and
// more expensive to propagate; larger transactions also take more resources
// to validate whether they fit into the pool or not.
txMaxSize = 4 * txSlotSize // 128KB
txMaxSize = 16 * txSlotSize // 512KB (tentative)
)

var (
Expand Down
1 change: 1 addition & 0 deletions core/vm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
ErrGasUintOverflow = errors.New("gas uint64 overflow")
ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
ErrCodeInsufficientStake = errors.New("insufficient staking for code")

// errStopToken is an internal token indicating interpreter loop termination,
// never returned to outside callers.
Expand Down
44 changes: 42 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas

if err == nil {
err = evm.checkContractStaking(contract, uint64(len(code)))
}
}
}
// When an error was returned by the EVM or when setting the creation code
Expand Down Expand Up @@ -284,6 +288,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas

if err == nil {
err = evm.checkContractStaking(contract, 0)
}
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
Expand Down Expand Up @@ -324,6 +332,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas

if err == nil {
err = evm.checkContractStaking(contract, 0)
}
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
Expand Down Expand Up @@ -390,6 +402,22 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
return ret, gas, err
}

func (evm *EVM) checkContractStaking(contract *Contract, codeSize uint64) error {
if codeSize == 0 {
codeSize = uint64(evm.StateDB.GetCodeSize(contract.Address()))
}
// Check if the remaining balance of the contract can cover the staking requirement
if !evm.StateDB.HasSuicided(contract.Address()) && codeSize > params.MaxCodeSizeSoft {
staking := big.NewInt(int64((codeSize - 1) / params.ExtcodeCopyChunkSize))
staking.Mul(staking, big.NewInt(int64(params.CodeStakingPerChunk)))

if !evm.Context.CanTransfer(evm.StateDB, contract.Address(), staking) {
return ErrCodeInsufficientStake
}
}
return nil
}

type codeAndHash struct {
code []byte
hash common.Hash
Expand Down Expand Up @@ -453,7 +481,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
ret, err := evm.interpreter.Run(contract, nil, false)

// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSizeHard {
err = ErrMaxCodeSizeExceeded
}

Expand All @@ -467,12 +495,24 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
// Be compatible with Ethereum gas metering when code size < 24 * 1024
baseDataGas := params.CreateDataGas
dataLen := uint64(len(ret))
createDataGas := dataLen * baseDataGas
// cap the data len and require staking for > 24KB data
if dataLen > params.MaxCodeSizeSoft {
createDataGas = params.MaxCodeSizeSoft * baseDataGas
}

if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(address, ret)
} else {
err = ErrCodeStoreOutOfGas
}

if err == nil {
err = evm.checkContractStaking(contract, dataLen)
}
}

// When an error was returned by the EVM or when setting the creation code
Expand Down
26 changes: 26 additions & 0 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,16 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
return gas, nil
}

func addGasExtraCodeSize(evm *EVM, address common.Address, gas uint64) (uint64, bool) {
codeSize := evm.StateDB.GetCodeSize(address)
if codeSize <= params.MaxCodeSizeSoft {
return gas, false
}

extraGas := (uint64(codeSize) - 1) / params.ExtcodeCopyChunkSize * params.CallGasEIP150
return math.SafeAdd(gas, extraGas)
}

func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
Expand Down Expand Up @@ -357,6 +367,10 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}

if gas, overflow = addGasExtraCodeSize(evm, address, gas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}

Expand All @@ -368,6 +382,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
var (
gas uint64
overflow bool
address = common.Address(stack.Back(1).Bytes20())
)
if stack.Back(2).Sign() != 0 {
gas += params.CallValueTransferGas
Expand All @@ -382,10 +397,14 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = addGasExtraCodeSize(evm, address, gas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}

func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := common.Address(stack.Back(1).Bytes20())
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
Expand All @@ -398,10 +417,14 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = addGasExtraCodeSize(evm, address, gas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}

func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := common.Address(stack.Back(1).Bytes20())
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
Expand All @@ -414,6 +437,9 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = addGasExtraCodeSize(evm, address, gas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}

Expand Down
7 changes: 7 additions & 0 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,13 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
uint64CodeOffset = 0xffffffffffffffff
}
addr := common.Address(a.Bytes20())
codeSize := interpreter.evm.StateDB.GetCodeSize(addr)
if codeSize > params.MaxCodeSizeSoft {
extraGas := (uint64(codeSize - 1)) / params.ExtcodeCopyChunkSize * params.ExtcodeCopyBasePerChunk
if !scope.Contract.UseGas(extraGas) {
return nil, ErrOutOfGas
}
}
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)

Expand Down
7 changes: 6 additions & 1 deletion params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ const (
// static portion of the gas. It was changed during EIP 150 (Tangerine)
ExtcodeCopyBaseFrontier uint64 = 20
ExtcodeCopyBaseEIP150 uint64 = 700
ExtcodeCopyBasePerChunk uint64 = 700
ExtcodeCopyChunkSize uint64 = MaxCodeSizeSoft

CodeStakingPerChunk uint64 = 1000000000000000000 // 1 token per 24k (reclaimable upon suicide)

// CreateBySelfdestructGas is used when the refunded account is one that does
// not exist. This logic is similar to call.
Expand All @@ -123,7 +127,8 @@ const (
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.

MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
MaxCodeSizeSoft = 24576 // Maximum bytecode to permit for a contract without staking
MaxCodeSizeHard = 512 * 1024 // Maximum bytecode to permit for a contract with staking (hardlimit)

// Precompiled contract gas prices

Expand Down

0 comments on commit c833536

Please sign in to comment.