Compare commits

...

9 commits

Author SHA1 Message Date
rjl493456442
c3168b3e56 eth/downloader: fix ancient limit in snap sync (#32188)
This pull request fixes an issue in disabling direct-ancient mode in
snap sync.

Specifically, if `origin >= frozen && origin != 0`, it implies a part of
chain data has been written into the key-value store, all the following 
writes into ancient store scheduled by downloader will be rejected 
with error 

`ERROR[07-10|03:46:57.924] Error importing chain data to ancients
err="can't add block 1166 hash: the append operation is out-order: have
1166 want 0"`.

This issue is detected by the https://github.com/ethpandaops/kurtosis-sync-test, 
which initiates the first snap sync cycle without the finalized header and
implicitly disables the direct-ancient mode. A few seconds later the second 
snap sync cycle is initiated with the finalized information and direct-ancient mode
is enabled incorrectly.
2025-07-11 20:41:09 +08:00
spencer-tb
9859e6d62a
params: fix 7934 size limit. 2025-07-03 08:09:02 -06:00
lightclient
18e79fdd7e
core/vm: implement eip-7939 CLZ instruction
Co-authored-by: Giulio <giulio.rebuffo@gmail.com>
Co-authored-by: spencer-tb <spencer@spencertaylorbrown.uk>
2025-07-03 08:09:02 -06:00
lightclient
4a4dc08ab1
core/vm, crypto/secp2561r1: implement secp256r1 precompile
Co-authored-by: Ulaş Erdoğan <uerdogan2001@hotmail.com>
2025-07-03 08:09:02 -06:00
lightclient
404baa9b27
core,miner,params: implement EIP-7934 - RLP Execution Block Size Limit
Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com>
2025-07-03 08:09:01 -06:00
lightclient
b58f8b3a5f
consensus: implement EIP-7918
Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com>
2025-07-03 08:09:01 -06:00
lightclient
8ec1db1a4a
all: add eip-7907 base without db table
Co-authored-by: lightclient <lightclient@proton.com>
Co-authored-by: Qi Zhou <qizhou@ethstorage.io>
2025-07-03 08:09:01 -06:00
lightclient
a3f2cb14a2
params: add bpo forks
Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
2025-07-03 08:09:01 -06:00
lightclient
e96f887e73
core/vm: implement EIP 7825 - Transaction Gas Limit Cap
Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com>
2025-07-03 08:09:01 -06:00
37 changed files with 6330 additions and 75 deletions

View file

@ -180,9 +180,12 @@ func Transaction(ctx *cli.Context) error {
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits") r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
} }
// Check whether the init code size has been exceeded. // Check whether the init code size has been exceeded.
if chainConfig.IsShanghai(new(big.Int), 0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { if chainConfig.IsShanghai(new(big.Int), 0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSizeEIP3860 {
r.Error = errors.New("max initcode size exceeded") r.Error = errors.New("max initcode size exceeded")
} }
if chainConfig.IsOsaka(new(big.Int), 0) && tx.Gas() > params.MaxTxGas {
r.Error = errors.New("gas limit exceeds maximum")
}
results = append(results, r) results = append(results, r)
} }
out, err := json.MarshalIndent(results, "", " ") out, err := json.MarshalIndent(results, "", " ")

View file

@ -23,7 +23,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params/forks"
) )
var ( var (
@ -71,48 +70,76 @@ func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTim
parentExcessBlobGas = *parent.ExcessBlobGas parentExcessBlobGas = *parent.ExcessBlobGas
parentBlobGasUsed = *parent.BlobGasUsed parentBlobGasUsed = *parent.BlobGasUsed
} }
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed var (
targetGas := uint64(targetBlobsPerBlock(config, headTimestamp)) * params.BlobTxBlobGasPerBlob excessBlobGas = parentExcessBlobGas + parentBlobGasUsed
target = targetBlobsPerBlock(config, headTimestamp)
targetGas = uint64(target) * params.BlobTxBlobGasPerBlob
)
if excessBlobGas < targetGas { if excessBlobGas < targetGas {
return 0 return 0
} }
if !config.IsOsaka(config.LondonBlock, headTimestamp) {
return excessBlobGas - targetGas
}
// EIP-7918 (post-Osaka).
var (
reservePrice = new(big.Int).Mul(parent.BaseFee, big.NewInt(params.BlobBaseCost))
blobPrice = calcBlobPrice(config, parent)
)
if reservePrice.Cmp(blobPrice) > 0 {
max := MaxBlobsPerBlock(config, headTimestamp)
scaledExcess := parentBlobGasUsed * uint64(max-target) / uint64(max)
return parentExcessBlobGas + scaledExcess
}
return excessBlobGas - targetGas return excessBlobGas - targetGas
} }
// CalcBlobFee calculates the blobfee from the header's excess blob gas field. // CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int { func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
var frac uint64 blobConfig := latestBlobConfig(config, header.Time)
switch config.LatestFork(header.Time) { if blobConfig == nil {
case forks.Osaka:
frac = config.BlobScheduleConfig.Osaka.UpdateFraction
case forks.Prague:
frac = config.BlobScheduleConfig.Prague.UpdateFraction
case forks.Cancun:
frac = config.BlobScheduleConfig.Cancun.UpdateFraction
default:
panic("calculating blob fee on unsupported fork") panic("calculating blob fee on unsupported fork")
} }
return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(*header.ExcessBlobGas), new(big.Int).SetUint64(frac)) return fakeExponential(minBlobGasPrice, new(big.Int).SetUint64(*header.ExcessBlobGas), new(big.Int).SetUint64(blobConfig.UpdateFraction))
} }
// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp. // MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
if cfg.BlobScheduleConfig == nil { blobConfig := latestBlobConfig(cfg, time)
if blobConfig == nil {
return 0 return 0
} }
return blobConfig.Max
}
func latestBlobConfig(cfg *params.ChainConfig, time uint64) *params.BlobConfig {
if cfg.BlobScheduleConfig == nil {
return nil
}
var ( var (
london = cfg.LondonBlock london = cfg.LondonBlock
s = cfg.BlobScheduleConfig s = cfg.BlobScheduleConfig
) )
switch { switch {
case cfg.IsBPO5(london, time) && s.BPO5 != nil:
return s.BPO5
case cfg.IsBPO4(london, time) && s.BPO4 != nil:
return s.BPO4
case cfg.IsBPO3(london, time) && s.BPO3 != nil:
return s.BPO3
case cfg.IsBPO2(london, time) && s.BPO2 != nil:
return s.BPO2
case cfg.IsBPO1(london, time) && s.BPO1 != nil:
return s.BPO1
case cfg.IsOsaka(london, time) && s.Osaka != nil: case cfg.IsOsaka(london, time) && s.Osaka != nil:
return s.Osaka.Max return s.Osaka
case cfg.IsPrague(london, time) && s.Prague != nil: case cfg.IsPrague(london, time) && s.Prague != nil:
return s.Prague.Max return s.Prague
case cfg.IsCancun(london, time) && s.Cancun != nil: case cfg.IsCancun(london, time) && s.Cancun != nil:
return s.Cancun.Max return s.Cancun
default: default:
return 0 return nil
} }
} }
@ -129,6 +156,16 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
return 0 return 0
} }
switch { switch {
case s.BPO5 != nil:
return s.BPO5.Max
case s.BPO4 != nil:
return s.BPO4.Max
case s.BPO3 != nil:
return s.BPO3.Max
case s.BPO2 != nil:
return s.BPO2.Max
case s.BPO1 != nil:
return s.BPO1.Max
case s.Osaka != nil: case s.Osaka != nil:
return s.Osaka.Max return s.Osaka.Max
case s.Prague != nil: case s.Prague != nil:
@ -142,23 +179,11 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
// targetBlobsPerBlock returns the target number of blobs in a block at the given timestamp. // targetBlobsPerBlock returns the target number of blobs in a block at the given timestamp.
func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int { func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
if cfg.BlobScheduleConfig == nil { blobConfig := latestBlobConfig(cfg, time)
return 0 if blobConfig == nil {
}
var (
london = cfg.LondonBlock
s = cfg.BlobScheduleConfig
)
switch {
case cfg.IsOsaka(london, time) && s.Osaka != nil:
return s.Osaka.Target
case cfg.IsPrague(london, time) && s.Prague != nil:
return s.Prague.Target
case cfg.IsCancun(london, time) && s.Cancun != nil:
return s.Cancun.Target
default:
return 0 return 0
} }
return blobConfig.Target
} }
// fakeExponential approximates factor * e ** (numerator / denominator) using // fakeExponential approximates factor * e ** (numerator / denominator) using
@ -177,3 +202,9 @@ func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
} }
return output.Div(output, denominator) return output.Div(output, denominator)
} }
// calcBlobPrice calculates the blob price for a block.
func calcBlobPrice(config *params.ChainConfig, header *types.Header) *big.Int {
blobBaseFee := CalcBlobFee(config, header)
return new(big.Int).Mul(blobBaseFee, big.NewInt(params.BlobTxBlobGasPerBlob))
}

View file

@ -127,3 +127,43 @@ func TestFakeExponential(t *testing.T) {
} }
} }
} }
func TestCalcExcessBlobGasEIP7918(t *testing.T) {
var (
cfg = params.MergedTestChainConfig
targetBlobs = targetBlobsPerBlock(cfg, *cfg.CancunTime)
blobGasTarget = uint64(targetBlobs) * params.BlobTxBlobGasPerBlob
)
makeHeader := func(parentExcess, parentBaseFee uint64, blobsUsed int) *types.Header {
blobGasUsed := uint64(blobsUsed) * params.BlobTxBlobGasPerBlob
return &types.Header{
BaseFee: big.NewInt(int64(parentBaseFee)),
ExcessBlobGas: &parentExcess,
BlobGasUsed: &blobGasUsed,
}
}
tests := []struct {
name string
header *types.Header
wantExcessGas uint64
}{
{
name: "BelowReservePrice",
header: makeHeader(0, 1_000_000_000, targetBlobs),
wantExcessGas: blobGasTarget * 3 / 9,
},
{
name: "AboveReservePrice",
header: makeHeader(0, 1, targetBlobs),
wantExcessGas: 0,
},
}
for _, tc := range tests {
got := CalcExcessBlobGas(cfg, tc.header, *cfg.CancunTime)
if got != tc.wantExcessGas {
t.Fatalf("%s: excess-blob-gas mismatch have %d, want %d",
tc.name, got, tc.wantExcessGas)
}
}
}

View file

@ -49,6 +49,10 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain) *Bloc
// header's transaction and uncle roots. The headers are assumed to be already // header's transaction and uncle roots. The headers are assumed to be already
// validated at this point. // validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error { func (v *BlockValidator) ValidateBody(block *types.Block) error {
// check EIP 7934 RLP-encoded block size cap
if v.config.IsOsaka(block.Number(), block.Time()) && block.Size() > params.BlockRLPSizeCap {
return ErrBlockOversized
}
// Check whether the block is already imported. // Check whether the block is already imported.
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock return ErrKnownBlock

View file

@ -45,6 +45,7 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/pebble" "github.com/ethereum/go-ethereum/ethdb/pebble"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256" "github.com/holiman/uint256"
@ -4515,3 +4516,76 @@ func TestGetCanonicalReceipt(t *testing.T) {
} }
} }
} }
func TestEIP7907(t *testing.T) {
glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
glogger.Verbosity(3)
log.SetDefault(log.NewLogger(glogger))
junk := make([]byte, 1024*250) // 250kb
for i := range junk {
junk[i] = byte(i)
}
code := program.New().Op(vm.ADDRESS).Op(vm.POP).ReturnViaCodeCopy(junk).Bytes()
var (
config = *params.MergedTestChainConfig
signer = types.LatestSigner(&config)
engine = beacon.New(ethash.NewFaker())
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
)
gspec := &Genesis{
Config: &config,
GasLimit: 70000000,
Alloc: types.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
aa: { // The address 0xAAAA calls into addr2
Code: code,
Nonce: 0,
Balance: big.NewInt(0),
},
bb: { // The address 0xBBBB copies and deploys the contract.
Code: program.New().ExtcodeCopy(aa, 0, 0, len(code)).Push0().Push(len(code)).Push0().Push0().Op(vm.CREATE).Op(vm.EXTCODESIZE).Bytes(),
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
b.SetCoinbase(aa)
txdata := &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: 0,
To: &bb,
Gas: 70000000,
GasFeeCap: newGwei(5),
GasTipCap: big.NewInt(2),
}
tx := types.MustSignNewTx(key1, signer, txdata)
b.AddTx(tx)
})
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
defer chain.Stop()
if n, err := chain.InsertChain(blocks); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
// Verify delegation designations were deployed.
created := crypto.CreateAddress(bb, 0)
fmt.Println(created.Hex())
state, _ := chain.State()
code, want := state.GetCode(created), junk
if !bytes.Equal(code, want) {
t.Fatalf("created code incorrect: got %d, want %d", len(code), len(want))
}
}

View file

@ -28,6 +28,10 @@ var (
// ErrNoGenesis is returned when there is no Genesis Block. // ErrNoGenesis is returned when there is no Genesis Block.
ErrNoGenesis = errors.New("genesis not found in chain") ErrNoGenesis = errors.New("genesis not found in chain")
// ErrBlockOversized is returned if the size of the RLP-encoded block
// exceeds the cap established by EIP 7934
ErrBlockOversized = errors.New("block RLP-encoded size exceeds maximum")
) )
// List of evm-call-message pre-checking errors. All state transition messages will // List of evm-call-message pre-checking errors. All state transition messages will
@ -122,6 +126,9 @@ var (
// Message validation errors: // Message validation errors:
ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list") ErrEmptyAuthList = errors.New("EIP-7702 transaction with empty auth list")
ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract") ErrSetCodeTxCreate = errors.New("EIP-7702 transaction cannot be used to create contract")
// -- EIP-7825 errors --
ErrGasLimitTooHigh = errors.New("transaction gas limit too high")
) )
// EIP-7702 state transition errors. // EIP-7702 state transition errors.

View file

@ -66,6 +66,16 @@ func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
return data return data
} }
// ReadCodeWithPrefix retrieves the contract code of the provided code hash.
// Return -1 if not found for legacy db.
func ReadCodeSizeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) int {
data, _ := db.Get(codeSizeKey(hash))
if len(data) != 4 {
return -1
}
return int(binary.BigEndian.Uint32(data))
}
// HasCode checks if the contract code corresponding to the // HasCode checks if the contract code corresponding to the
// provided code hash is present in the db. // provided code hash is present in the db.
func HasCode(db ethdb.KeyValueReader, hash common.Hash) bool { func HasCode(db ethdb.KeyValueReader, hash common.Hash) bool {
@ -91,12 +101,19 @@ func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
if err := db.Put(codeKey(hash), code); err != nil { if err := db.Put(codeKey(hash), code); err != nil {
log.Crit("Failed to store contract code", "err", err) 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. // DeleteCode deletes the specified contract code from the database.
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) { func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(codeKey(hash)); err != nil { if err := db.Delete(codeKey(hash)); err != nil {
log.Crit("Failed to delete contract code", "err", err) log.Crit("Failed to delete contract code", "err", err)
// Ignore error since the legacy db may not contain the size.
db.Delete(codeSizeKey(hash))
} }
} }

View file

@ -114,6 +114,7 @@ var (
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
CodePrefix = []byte("c") // CodePrefix + code hash -> account code CodePrefix = []byte("c") // CodePrefix + code hash -> account code
CodeSizePrefix = []byte("s") /// CodeSizePrefx + code hash -> code size
skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header
// Path-based storage scheme of merkle patricia trie. // Path-based storage scheme of merkle patricia trie.
@ -244,6 +245,11 @@ func codeKey(hash common.Hash) []byte {
return append(CodePrefix, hash.Bytes()...) return append(CodePrefix, hash.Bytes()...)
} }
// codeSizeKey = CodeSizePrefix + hash
func codeSizeKey(hash common.Hash) []byte {
return append(CodeSizePrefix, hash.Bytes()...)
}
// IsCodeKey reports whether the given byte slice is the key of contract code, // IsCodeKey reports whether the given byte slice is the key of contract code,
// if so return the raw code hash as well. // if so return the raw code hash as well.
func IsCodeKey(key []byte) (bool, []byte) { func IsCodeKey(key []byte) (bool, []byte) {

View file

@ -26,8 +26,9 @@ import (
) )
type accessList struct { type accessList struct {
addresses map[common.Address]int addresses map[common.Address]int
slots []map[common.Hash]struct{} slots []map[common.Hash]struct{}
addressCodes map[common.Address]struct{}
} }
// ContainsAddress returns true if the address is in the access list. // ContainsAddress returns true if the address is in the access list.
@ -36,6 +37,12 @@ func (al *accessList) ContainsAddress(address common.Address) bool {
return ok return ok
} }
// ContainsAddress returns true if the address is in the access list.
func (al *accessList) ContainsAddressCode(address common.Address) bool {
_, ok := al.addressCodes[address]
return ok
}
// Contains checks if a slot within an account is present in the access list, returning // Contains checks if a slot within an account is present in the access list, returning
// separate flags for the presence of the account and the slot respectively. // separate flags for the presence of the account and the slot respectively.
func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
@ -55,7 +62,8 @@ func (al *accessList) Contains(address common.Address, slot common.Hash) (addres
// newAccessList creates a new accessList. // newAccessList creates a new accessList.
func newAccessList() *accessList { func newAccessList() *accessList {
return &accessList{ return &accessList{
addresses: make(map[common.Address]int), addresses: make(map[common.Address]int),
addressCodes: make(map[common.Address]struct{}),
} }
} }
@ -67,6 +75,10 @@ func (al *accessList) Copy() *accessList {
for i, slotMap := range al.slots { for i, slotMap := range al.slots {
cp.slots[i] = maps.Clone(slotMap) cp.slots[i] = maps.Clone(slotMap)
} }
cp.addressCodes = make(map[common.Address]struct{}, len(al.addressCodes))
for addr := range al.addressCodes {
cp.addressCodes[addr] = struct{}{}
}
return cp return cp
} }
@ -80,6 +92,16 @@ func (al *accessList) AddAddress(address common.Address) bool {
return true return true
} }
// AddAddressCode adds an address code to the access list, and returns 'true' if
// the operation caused a change (addr was not previously in the list).
func (al *accessList) AddAddressCode(address common.Address) bool {
if _, present := al.addressCodes[address]; present {
return false
}
al.addressCodes[address] = struct{}{}
return true
}
// AddSlot adds the specified (addr, slot) combo to the access list. // AddSlot adds the specified (addr, slot) combo to the access list.
// Return values are: // Return values are:
// - address added // - address added
@ -142,6 +164,11 @@ func (al *accessList) Equal(other *accessList) bool {
return slices.EqualFunc(al.slots, other.slots, maps.Equal) return slices.EqualFunc(al.slots, other.slots, maps.Equal)
} }
// DeleteAddressCode removes an address code from the access list.
func (al *accessList) DeleteAddressCode(address common.Address) {
delete(al.addressCodes, address)
}
// PrettyPrint prints the contents of the access list in a human-readable form // PrettyPrint prints the contents of the access list in a human-readable form
func (al *accessList) PrettyPrint() string { func (al *accessList) PrettyPrint() string {
out := new(strings.Builder) out := new(strings.Builder)

View file

@ -221,6 +221,10 @@ func (j *journal) accessListAddAccount(addr common.Address) {
j.append(accessListAddAccountChange{addr}) j.append(accessListAddAccountChange{addr})
} }
func (j *journal) accessListAddAccountCode(addr common.Address) {
j.append(accessListAddAccountCodeChange{addr})
}
func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) { func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) {
j.append(accessListAddSlotChange{ j.append(accessListAddSlotChange{
address: addr, address: addr,
@ -282,6 +286,9 @@ type (
address common.Address address common.Address
slot common.Hash slot common.Hash
} }
accessListAddAccountCodeChange struct {
address common.Address
}
// Changes to transient storage // Changes to transient storage
transientStorageChange struct { transientStorageChange struct {
@ -485,6 +492,20 @@ func (ch accessListAddAccountChange) copy() journalEntry {
} }
} }
func (ch accessListAddAccountCodeChange) revert(s *StateDB) {
s.accessList.DeleteAddressCode(ch.address)
}
func (ch accessListAddAccountCodeChange) dirtied() *common.Address {
return nil
}
func (ch accessListAddAccountCodeChange) copy() journalEntry {
return accessListAddAccountCodeChange{
address: ch.address,
}
}
func (ch accessListAddSlotChange) revert(s *StateDB) { func (ch accessListAddSlotChange) revert(s *StateDB) {
s.accessList.DeleteSlot(ch.address, ch.slot) s.accessList.DeleteSlot(ch.address, ch.slot)
} }

View file

@ -140,6 +140,11 @@ func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash)
if cached, ok := r.codeSizeCache.Get(codeHash); ok { if cached, ok := r.codeSizeCache.Get(codeHash); ok {
return cached, nil return cached, nil
} }
codeSize := rawdb.ReadCodeSizeWithPrefix(r.db, codeHash)
if codeSize != -1 {
r.codeSizeCache.Add(codeHash, codeSize)
return codeSize, nil
}
code, err := r.Code(addr, codeHash) code, err := r.Code(addr, codeHash)
if err != nil { if err != nil {
return 0, err return 0, err

View file

@ -1381,6 +1381,10 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
al.AddAddress(sender) al.AddAddress(sender)
if dst != nil { if dst != nil {
al.AddAddress(*dst) al.AddAddress(*dst)
// TODO: add for devnet-3
// if rules.IsOsaka {
// al.AddAddressCode(*dst)
// }
// If it's a create-tx, the destination will be added inside evm.create // If it's a create-tx, the destination will be added inside evm.create
} }
for _, addr := range precompiles { for _, addr := range precompiles {
@ -1422,11 +1426,23 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
} }
} }
// AddAddressCodeToAccessList adds the given address to the access list
func (s *StateDB) AddAddressCodeToAccessList(addr common.Address) {
if s.accessList.AddAddressCode(addr) {
s.journal.accessListAddAccountCode(addr)
}
}
// AddressInAccessList returns true if the given address is in the access list. // AddressInAccessList returns true if the given address is in the access list.
func (s *StateDB) AddressInAccessList(addr common.Address) bool { func (s *StateDB) AddressInAccessList(addr common.Address) bool {
return s.accessList.ContainsAddress(addr) return s.accessList.ContainsAddress(addr)
} }
// AddressCodeInAccessList returns true if the given address code is in the access list.
func (s *StateDB) AddressCodeInAccessList(addr common.Address) bool {
return s.accessList.ContainsAddressCode(addr)
}
// SlotInAccessList returns true if the given (address, slot)-tuple is in the access list. // SlotInAccessList returns true if the given (address, slot)-tuple is in the access list.
func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) {
return s.accessList.Contains(addr, slot) return s.accessList.Contains(addr, slot)

View file

@ -125,10 +125,18 @@ func (s *hookedStateDB) SlotInAccessList(addr common.Address, slot common.Hash)
return s.inner.SlotInAccessList(addr, slot) return s.inner.SlotInAccessList(addr, slot)
} }
func (s *hookedStateDB) AddressCodeInAccessList(addr common.Address) bool {
return s.inner.AddressCodeInAccessList(addr)
}
func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) { func (s *hookedStateDB) AddAddressToAccessList(addr common.Address) {
s.inner.AddAddressToAccessList(addr) s.inner.AddAddressToAccessList(addr)
} }
func (s *hookedStateDB) AddAddressCodeToAccessList(addr common.Address) {
s.inner.AddAddressCodeToAccessList(addr)
}
func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { func (s *hookedStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
s.inner.AddSlotToAccessList(addr, slot) s.inner.AddSlotToAccessList(addr, slot)
} }

View file

@ -125,7 +125,7 @@ func TestStateProcessorErrors(t *testing.T) {
}, },
} }
blockchain, _ = NewBlockChain(db, gspec, beacon.New(ethash.NewFaker()), nil) blockchain, _ = NewBlockChain(db, gspec, beacon.New(ethash.NewFaker()), nil)
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{} tooBigInitCode = [params.MaxInitCodeSizeEIP3860 + 1]byte{}
) )
defer blockchain.Stop() defer blockchain.Stop()

View file

@ -394,6 +394,10 @@ func (st *stateTransition) preCheck() error {
return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From) return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)
} }
} }
// Verify tx gas limit does not exceed EIP-7825 cap.
if st.evm.ChainConfig().IsOsaka(st.evm.Context.BlockNumber, st.evm.Context.Time) && msg.GasLimit > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
}
return st.buyGas() return st.buyGas()
} }
@ -471,8 +475,11 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Check whether the init code size has been exceeded. // Check whether the init code size has been exceeded.
if rules.IsShanghai && contractCreation && len(msg.Data) > params.MaxInitCodeSize { if rules.IsOsaka && contractCreation && len(msg.Data) > params.MaxInitCodeSizeEIP7907 {
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(msg.Data), params.MaxInitCodeSize) return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(msg.Data), params.MaxInitCodeSizeEIP7907)
}
if !rules.IsOsaka && rules.IsShanghai && contractCreation && len(msg.Data) > params.MaxInitCodeSizeEIP3860 {
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(msg.Data), params.MaxInitCodeSizeEIP3860)
} }
// Execute the preparatory steps for state transition which includes: // Execute the preparatory steps for state transition which includes:

View file

@ -1638,6 +1638,11 @@ func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*tx
break // blobfee too low, cannot be included, discard rest of txs from the account break // blobfee too low, cannot be included, discard rest of txs from the account
} }
} }
if filter.GasLimitCap != 0 {
if tx.execGas > filter.GasLimitCap {
break // execution gas limit is too high
}
}
// Transaction was accepted according to the filter, append to the pending list // Transaction was accepted according to the filter, append to the pending list
lazies = append(lazies, &txpool.LazyTransaction{ lazies = append(lazies, &txpool.LazyTransaction{
Pool: p, Pool: p,

View file

@ -530,11 +530,19 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
txs := list.Flatten() txs := list.Flatten()
// If the miner requests tip enforcement, cap the lists now // If the miner requests tip enforcement, cap the lists now
if minTipBig != nil { if minTipBig != nil || filter.GasLimitCap != 0 {
for i, tx := range txs { for i, tx := range txs {
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 { if minTipBig != nil {
txs = txs[:i] if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
break txs = txs[:i]
break
}
}
if filter.GasLimitCap != 0 {
if tx.Gas() > filter.GasLimitCap {
txs = txs[:i]
break
}
} }
} }
} }
@ -1255,6 +1263,21 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
} }
pool.mu.Lock() pool.mu.Lock()
if reset != nil { if reset != nil {
if reset.newHead != nil && reset.oldHead != nil {
// Discard the transactions with the gas limit higher than the cap.
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
var hashes []common.Hash
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
if tx.Gas() > params.MaxTxGas {
hashes = append(hashes, hash)
}
return true
})
for _, hash := range hashes {
pool.removeTx(hash, true, true)
}
}
}
// Reset from the old head to the new, rescheduling any reorged transactions // Reset from the old head to the new, rescheduling any reorged transactions
pool.reset(reset.oldHead, reset.newHead) pool.reset(reset.oldHead, reset.newHead)

View file

@ -73,9 +73,10 @@ type LazyResolver interface {
// a very specific call site in mind and each one can be evaluated very cheaply // a very specific call site in mind and each one can be evaluated very cheaply
// by the pool implementations. Only add new ones that satisfy those constraints. // by the pool implementations. Only add new ones that satisfy those constraints.
type PendingFilter struct { type PendingFilter struct {
MinTip *uint256.Int // Minimum miner tip required to include a transaction MinTip *uint256.Int // Minimum miner tip required to include a transaction
BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction
BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction
GasLimitCap uint64 // Maximum gas can be used for a single transaction execution (0 means no limit)
OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling) OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling)
OnlyBlobTxs bool // Return only blob transactions (block blob-space filling) OnlyBlobTxs bool // Return only blob transactions (block blob-space filling)

View file

@ -87,8 +87,11 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
return fmt.Errorf("%w: type %d rejected, pool not yet in Prague", core.ErrTxTypeNotSupported, tx.Type()) return fmt.Errorf("%w: type %d rejected, pool not yet in Prague", core.ErrTxTypeNotSupported, tx.Type())
} }
// Check whether the init code size has been exceeded // Check whether the init code size has been exceeded
if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSizeEIP7907 {
return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSizeEIP7907)
}
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
return fmt.Errorf("%w (cap: %d, tx: %d)", core.ErrGasLimitTooHigh, params.MaxTxGas, tx.Gas())
} }
// Transactions can't be negative. This may never happen using RLP decoded // Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur for transactions created using the RPC. // transactions but may occur for transactions created using the RPC.

View file

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/crypto/secp256r1"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
@ -161,6 +162,14 @@ var PrecompiledContractsOsaka = PrecompiledContracts{
common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{}, common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x10}): &bls12381MapG1{}, common.BytesToAddress([]byte{0x10}): &bls12381MapG1{},
common.BytesToAddress([]byte{0x11}): &bls12381MapG2{}, common.BytesToAddress([]byte{0x11}): &bls12381MapG2{},
common.BytesToAddress([]byte{0x1, 0x00}): &p256Verify{},
}
// PrecompiledContractsP256Verify contains the precompiled Ethereum
// contract specified in EIP-7212. This is exported for testing purposes.
var PrecompiledContractsP256Verify = PrecompiledContracts{
common.BytesToAddress([]byte{0x1, 0x00}): &p256Verify{},
} }
var ( var (
@ -1232,3 +1241,31 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
return h return h
} }
// P256VERIFY (secp256r1 signature verification)
// implemented as a native contract
type p256Verify struct{}
// RequiredGas returns the gas required to execute the precompiled contract
func (c *p256Verify) RequiredGas(input []byte) uint64 {
return params.P256VerifyGas
}
// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas
func (c *p256Verify) Run(input []byte) ([]byte, error) {
const p256VerifyInputLength = 160
if len(input) != p256VerifyInputLength {
return nil, nil
}
// Extract hash, r, s, x, y from the input.
hash := input[0:32]
r, s := new(big.Int).SetBytes(input[32:64]), new(big.Int).SetBytes(input[64:96])
x, y := new(big.Int).SetBytes(input[96:128]), new(big.Int).SetBytes(input[128:160])
// Verify the signature.
if secp256r1.Verify(hash, r, s, x, y) {
return true32Byte, nil
}
return nil, nil
}

View file

@ -66,6 +66,8 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x0f, 0x0e}): &bls12381Pairing{}, common.BytesToAddress([]byte{0x0f, 0x0e}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x0f, 0x0f}): &bls12381MapG1{}, common.BytesToAddress([]byte{0x0f, 0x0f}): &bls12381MapG1{},
common.BytesToAddress([]byte{0x0f, 0x10}): &bls12381MapG2{}, common.BytesToAddress([]byte{0x0f, 0x10}): &bls12381MapG2{},
common.BytesToAddress([]byte{0x0b}): &p256Verify{},
} }
// EIP-152 test vectors // EIP-152 test vectors
@ -397,3 +399,15 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
} }
benchmarkPrecompiled("f0d", testcase, b) benchmarkPrecompiled("f0d", testcase, b)
} }
// Benchmarks the sample inputs from the P256VERIFY precompile.
func BenchmarkPrecompiledP256Verify(bench *testing.B) {
t := precompiledTest{
Input: "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
Expected: "0000000000000000000000000000000000000000000000000000000000000001",
Name: "p256Verify",
}
benchmarkPrecompiled("0b", t, bench)
}
func TestPrecompiledP256Verify(t *testing.T) { testJson("p256Verify", "0b", t) }

View file

@ -41,6 +41,8 @@ var activators = map[int]func(*JumpTable){
1153: enable1153, 1153: enable1153,
4762: enable4762, 4762: enable4762,
7702: enable7702, 7702: enable7702,
7907: enable7907,
7939: enable7939,
} }
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
@ -293,6 +295,13 @@ func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
return nil, nil return nil, nil
} }
func opCLZ(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := scope.Stack.pop()
// count leading zero bits in x
scope.Stack.push(new(uint256.Int).SetUint64(256 - uint64(x.BitLen())))
return nil, nil
}
// enable4844 applies EIP-4844 (BLOBHASH opcode) // enable4844 applies EIP-4844 (BLOBHASH opcode)
func enable4844(jt *JumpTable) { func enable4844(jt *JumpTable) {
jt[BLOBHASH] = &operation{ jt[BLOBHASH] = &operation{
@ -303,6 +312,15 @@ func enable4844(jt *JumpTable) {
} }
} }
func enable7939(jt *JumpTable) {
jt[CLZ] = &operation{
execute: opCLZ,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
}
// enable7516 applies EIP-7516 (BLOBBASEFEE opcode) // enable7516 applies EIP-7516 (BLOBBASEFEE opcode)
func enable7516(jt *JumpTable) { func enable7516(jt *JumpTable) {
jt[BLOBBASEFEE] = &operation{ jt[BLOBBASEFEE] = &operation{
@ -538,3 +556,14 @@ func enable7702(jt *JumpTable) {
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702 jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702 jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
} }
// enable7907 the EIP-7907 changes to support large contracts.
func enable7907(jt *JumpTable) {
jt[CALL].dynamicGas = gasCallEIP7907
jt[CALLCODE].dynamicGas = gasCallCodeEIP7907
jt[STATICCALL].dynamicGas = gasStaticCallEIP7907
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7907
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP7907
jt[CREATE].dynamicGas = gasCreateEip7907
jt[CREATE2].dynamicGas = gasCreate2Eip7907
}

View file

@ -496,6 +496,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
} }
gas = gas - consumed gas = gas - consumed
} }
// The contract code is added to the access list _after_ the contract code is successfully deployed.
if evm.chainRules.IsOsaka {
evm.StateDB.AddAddressCodeToAccessList(address)
}
evm.Context.Transfer(evm.StateDB, caller, address, value) evm.Context.Transfer(evm.StateDB, caller, address, value)
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
@ -526,7 +530,10 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
} }
// Check whether the max code size has been exceeded, assign err if the case. // Check whether the max code size has been exceeded, assign err if the case.
if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { if evm.chainRules.IsOsaka && len(ret) > params.MaxCodeSizeEIP7907 {
return ret, ErrMaxCodeSizeExceeded
}
if !evm.chainRules.IsOsaka && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSizeEIP170 {
return ret, ErrMaxCodeSizeExceeded return ret, ErrMaxCodeSizeExceeded
} }

View file

@ -312,7 +312,7 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if size > params.MaxInitCodeSize { if size > params.MaxInitCodeSizeEIP3860 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size) return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
} }
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
@ -331,7 +331,46 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
if size > params.MaxInitCodeSize { if size > params.MaxInitCodeSizeEIP3860 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
}
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasCreateEip7907(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if size > params.MaxInitCodeSizeEIP7907 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
}
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasCreate2Eip7907(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if size > params.MaxInitCodeSizeEIP7907 {
return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size) return 0, fmt.Errorf("%w: size %d", ErrMaxInitCodeSizeExceeded, size)
} }
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow

View file

@ -972,3 +972,47 @@ func TestPush(t *testing.T) {
} }
} }
} }
func TestOpCLZ(t *testing.T) {
// set up once
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
tests := []struct {
name string
inputHex string // hexadecimal input for clarity
want uint64 // expected CLZ result
}{
{"zero", "0x0", 256},
{"one", "0x1", 255},
{"all-ones (256 bits)", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0},
{"low-10-bytes ones", "0xffffffffff", 216}, // 10 bytes = 80 bits, so 256-80=176? Actually input is 0xffffffffff = 40 bits so 256-40=216
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// prepare a fresh stack and PC
stack := newstack()
pc := uint64(0)
// parse input
val := new(uint256.Int)
if _, err := fmt.Sscan(tc.inputHex, val); err != nil {
// fallback: try hex
val.SetFromHex(tc.inputHex)
}
stack.push(val)
opCLZ(&pc, evm.interpreter, &ScopeContext{Stack: stack})
if gotLen := stack.len(); gotLen != 1 {
t.Fatalf("stack length = %d; want 1", gotLen)
}
result := stack.pop()
if got := result.Uint64(); got != tc.want {
t.Fatalf("clz(%q) = %d; want %d", tc.inputHex, got, tc.want)
}
})
}
}

View file

@ -76,6 +76,7 @@ type StateDB interface {
Empty(common.Address) bool Empty(common.Address) bool
AddressInAccessList(addr common.Address) bool AddressInAccessList(addr common.Address) bool
AddressCodeInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet // even if the feature/fork is not active yet
@ -83,6 +84,9 @@ type StateDB interface {
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet // even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash) AddSlotToAccessList(addr common.Address, slot common.Hash)
// AddAddressCodeToAccessList adds the given address code to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddAddressCodeToAccessList(addr common.Address)
// PointCache returns the point cache used in computations // PointCache returns the point cache used in computations
PointCache() *utils.PointCache PointCache() *utils.PointCache

View file

@ -106,9 +106,13 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// If jump table was not initialised we set the default one. // If jump table was not initialised we set the default one.
var table *JumpTable var table *JumpTable
switch { switch {
case evm.chainRules.IsOsaka:
table = &osakaInstructionSet
case evm.chainRules.IsVerkle: case evm.chainRules.IsVerkle:
// TODO replace with proper instruction set when fork is specified // TODO replace with proper instruction set when fork is specified
table = &verkleInstructionSet table = &verkleInstructionSet
case evm.chainRules.IsOsaka:
table = &osakaInstructionSet
case evm.chainRules.IsPrague: case evm.chainRules.IsPrague:
table = &pragueInstructionSet table = &pragueInstructionSet
case evm.chainRules.IsCancun: case evm.chainRules.IsCancun:

View file

@ -62,6 +62,7 @@ var (
cancunInstructionSet = newCancunInstructionSet() cancunInstructionSet = newCancunInstructionSet()
verkleInstructionSet = newVerkleInstructionSet() verkleInstructionSet = newVerkleInstructionSet()
pragueInstructionSet = newPragueInstructionSet() pragueInstructionSet = newPragueInstructionSet()
osakaInstructionSet = newOsakaInstructionSet()
) )
// JumpTable contains the EVM opcodes supported at a given fork. // JumpTable contains the EVM opcodes supported at a given fork.
@ -91,6 +92,13 @@ func newVerkleInstructionSet() JumpTable {
return validate(instructionSet) return validate(instructionSet)
} }
func newOsakaInstructionSet() JumpTable {
instructionSet := newPragueInstructionSet()
enable7907(&instructionSet)
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
return validate(instructionSet)
}
func newPragueInstructionSet() JumpTable { func newPragueInstructionSet() JumpTable {
instructionSet := newCancunInstructionSet() instructionSet := newCancunInstructionSet()
enable7702(&instructionSet) // EIP-7702 Setcode transaction type enable7702(&instructionSet) // EIP-7702 Setcode transaction type

View file

@ -29,7 +29,7 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
case rules.IsVerkle: case rules.IsVerkle:
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet") return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
case rules.IsOsaka: case rules.IsOsaka:
return newPragueInstructionSet(), errors.New("osaka-fork not defined yet") return newOsakaInstructionSet(), nil
case rules.IsPrague: case rules.IsPrague:
return newPragueInstructionSet(), nil return newPragueInstructionSet(), nil
case rules.IsCancun: case rules.IsCancun:

View file

@ -62,6 +62,7 @@ const (
SHL OpCode = 0x1b SHL OpCode = 0x1b
SHR OpCode = 0x1c SHR OpCode = 0x1c
SAR OpCode = 0x1d SAR OpCode = 0x1d
CLZ OpCode = 0x1e
) )
// 0x20 range - crypto. // 0x20 range - crypto.
@ -282,6 +283,7 @@ var opCodeToString = [256]string{
SHL: "SHL", SHL: "SHL",
SHR: "SHR", SHR: "SHR",
SAR: "SAR", SAR: "SAR",
CLZ: "CLZ",
ADDMOD: "ADDMOD", ADDMOD: "ADDMOD",
MULMOD: "MULMOD", MULMOD: "MULMOD",
@ -484,6 +486,7 @@ var stringToOp = map[string]OpCode{
"SHL": SHL, "SHL": SHL,
"SHR": SHR, "SHR": SHR,
"SAR": SAR, "SAR": SAR,
"CLZ": CLZ,
"ADDMOD": ADDMOD, "ADDMOD": ADDMOD,
"MULMOD": MULMOD, "MULMOD": MULMOD,
"KECCAK256": KECCAK256, "KECCAK256": KECCAK256,

View file

@ -18,6 +18,7 @@ package vm
import ( import (
"errors" "errors"
"fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
@ -309,3 +310,142 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return total, nil return total, nil
} }
} }
var (
gasCallEIP7907 = makeCallVariantGasCallEIP7907(gasCall)
gasDelegateCallEIP7907 = makeCallVariantGasCallEIP7907(gasDelegateCall)
gasStaticCallEIP7907 = makeCallVariantGasCallEIP7907(gasStaticCall)
gasCallCodeEIP7907 = makeCallVariantGasCallEIP7907(gasCallCode)
)
// Rounds n up to the nearest multiple of 32.
func ceil32(n int) uint64 {
r := n % 32
if r == 0 {
return uint64(n)
} else {
return uint64(n + 32 - r)
}
}
func calcColdCodeAccessGasCost(evm *EVM, addr common.Address) uint64 {
size := evm.StateDB.GetCodeSize(addr)
// Only charge additional access cost for contracts larger than old limit.
if size <= params.MaxCodeSizeEIP170 {
return 0
}
excess := ceil32(size - params.MaxCodeSizeEIP170)
fmt.Println("excess", excess, "cost", (excess*params.CodeReadPerWordGasEIP7907)/32)
return (excess * params.CodeReadPerWordGasEIP7907) / 32
}
func makeCallVariantGasCallEIP7907(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
total uint64 // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20())
)
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += coldCost
}
// Check code presence in the access list
if !evm.StateDB.AddressCodeInAccessList(addr) {
cost := calcColdCodeAccessGasCost(evm, addr)
evm.StateDB.AddAddressCodeToAccessList(addr)
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
}
// TODO: reading code here would defeat the purpose of separate charging, so
// we should first see if the code size is 23 bytes before parsing.
// Check if code is a delegation and if so, charge for resolution.
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
var cost uint64
if evm.StateDB.AddressInAccessList(target) {
cost = params.WarmStorageReadCostEIP2929
} else {
evm.StateDB.AddAddressToAccessList(target)
cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
if !evm.StateDB.AddressCodeInAccessList(target) {
cost = calcColdCodeAccessGasCost(evm, target)
evm.StateDB.AddAddressCodeToAccessList(target)
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
}
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
if err != nil {
return old, err
}
// Temporarily add the gas charge back to the contract and return value. By
// adding it to the return, it will be charged outside of this function, as
// part of the dynamic gas. This will ensure it is correctly reported to
// tracers.
contract.Gas += total
var overflow bool
if total, overflow = math.SafeAdd(old, total); overflow {
return 0, ErrGasUintOverflow
}
return total, nil
}
}
func gasExtCodeCopyEIP7907(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
if err != nil {
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return 0, ErrGasUintOverflow
}
}
// Check address code presence in the access list
if !evm.StateDB.AddressCodeInAccessList(addr) {
cost := calcColdCodeAccessGasCost(evm, addr)
evm.StateDB.AddAddressCodeToAccessList(addr)
var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas
if gas, overflow = math.SafeAdd(gas, cost); overflow {
return 0, ErrGasUintOverflow
}
}
return gas, nil
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
// Copyright 2024 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package secp256r1 implements signature verification for the P256VERIFY precompile.
package secp256r1
import (
"crypto/ecdsa"
"crypto/elliptic"
"math/big"
)
// Verify checks the given signature (r, s) for the given hash and public key (x, y).
func Verify(hash []byte, r, s, x, y *big.Int) bool {
if x == nil || y == nil || !elliptic.P256().IsOnCurve(x, y) {
return false
}
pk := &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}
return ecdsa.Verify(pk, hash, r, s)
}

View file

@ -535,9 +535,15 @@ func (d *Downloader) syncToHead() (err error) {
// If a part of blockchain data has already been written into active store, // If a part of blockchain data has already been written into active store,
// disable the ancient style insertion explicitly. // disable the ancient style insertion explicitly.
if origin >= frozen && frozen != 0 { if origin >= frozen && origin != 0 {
d.ancientLimit = 0 d.ancientLimit = 0
log.Info("Disabling direct-ancient mode", "origin", origin, "ancient", frozen-1) var ancient string
if frozen == 0 {
ancient = "null"
} else {
ancient = fmt.Sprintf("%d", frozen-1)
}
log.Info("Disabling direct-ancient mode", "origin", origin, "ancient", ancient)
} else if d.ancientLimit > 0 { } else if d.ancientLimit > 0 {
log.Debug("Enabling direct-ancient mode", "ancient", d.ancientLimit) log.Debug("Enabling direct-ancient mode", "ancient", d.ancientLimit)
} }

View file

@ -50,6 +50,7 @@ type environment struct {
signer types.Signer signer types.Signer
state *state.StateDB // apply state changes here state *state.StateDB // apply state changes here
tcount int // tx count in cycle tcount int // tx count in cycle
size uint64 // size of the block we are building
gasPool *core.GasPool // available gas used to pack transactions gasPool *core.GasPool // available gas used to pack transactions
coinbase common.Address coinbase common.Address
evm *vm.EVM evm *vm.EVM
@ -68,6 +69,11 @@ const (
commitInterruptNewHead commitInterruptNewHead
commitInterruptResubmit commitInterruptResubmit
commitInterruptTimeout commitInterruptTimeout
// cap the size of blocks we will produce below the max allowed by
// EIP-7934. This gives us buffer room if the estimated size of the
// block we are building is off from the actual encoded size.
blockRLPSizeCapBuffer = 1_000_000
) )
// newPayloadResult is the result of payload generation. // newPayloadResult is the result of payload generation.
@ -95,12 +101,37 @@ type generateParams struct {
} }
// generateWork generates a sealing block based on the given parameters. // generateWork generates a sealing block based on the given parameters.
func (miner *Miner) generateWork(params *generateParams, witness bool) *newPayloadResult { func (miner *Miner) generateWork(genParam *generateParams, witness bool) *newPayloadResult {
work, err := miner.prepareWork(params, witness) work, err := miner.prepareWork(genParam, witness)
if err != nil { if err != nil {
return &newPayloadResult{err: err} return &newPayloadResult{err: err}
} }
if !params.noTxs { var includedWithdrawals types.Withdrawals
// If we are post-osaka, incorporate the requested withdrawals into the
// block size calculation up-front to ensure that all requested withdrawals
// can be included even if we hit the size cap when filling the block with
// txs.
//
// Also, ensure that including all requested withdrawals wouldn't bring us
// over the block size cap limit. The withdrawal cap ensures that this can't
// actually happen right now, but it doesn't hurt to make this code
// future-proof for a situation where the withdrawal cap is lifted.
if miner.chainConfig.IsOsaka(work.header.Number, work.header.Time) {
maxBlockSize := params.BlockRLPSizeCap - blockRLPSizeCapBuffer
for _, withdrawal := range genParam.withdrawals {
if int(work.size)+params.WithdrawalSize > maxBlockSize {
break
}
work.size += params.WithdrawalSize
includedWithdrawals = append(includedWithdrawals, withdrawal)
}
} else {
includedWithdrawals = genParam.withdrawals
}
if !genParam.noTxs {
interrupt := new(atomic.Int32) interrupt := new(atomic.Int32)
timer := time.AfterFunc(miner.config.Recommit, func() { timer := time.AfterFunc(miner.config.Recommit, func() {
interrupt.Store(commitInterruptTimeout) interrupt.Store(commitInterruptTimeout)
@ -112,8 +143,8 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit)) log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit))
} }
} }
body := types.Body{Transactions: work.txs, Withdrawals: includedWithdrawals}
body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals}
allLogs := make([]*types.Log, 0) allLogs := make([]*types.Log, 0)
for _, r := range work.receipts { for _, r := range work.receipts {
allLogs = append(allLogs, r.Logs...) allLogs = append(allLogs, r.Logs...)
@ -256,6 +287,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
return &environment{ return &environment{
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
state: state, state: state,
size: uint64(header.Size()),
coinbase: coinbase, coinbase: coinbase,
header: header, header: header,
witness: state.Witness(), witness: state.Witness(),
@ -273,6 +305,7 @@ func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) e
} }
env.txs = append(env.txs, tx) env.txs = append(env.txs, tx)
env.receipts = append(env.receipts, receipt) env.receipts = append(env.receipts, receipt)
env.size += tx.Size()
env.tcount++ env.tcount++
return nil return nil
} }
@ -298,6 +331,7 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
env.receipts = append(env.receipts, receipt) env.receipts = append(env.receipts, receipt)
env.sidecars = append(env.sidecars, sc) env.sidecars = append(env.sidecars, sc)
env.blobs += len(sc.Blobs) env.blobs += len(sc.Blobs)
env.size += tx.WithoutBlobTxSidecar().Size()
*env.header.BlobGasUsed += receipt.BlobGasUsed *env.header.BlobGasUsed += receipt.BlobGasUsed
env.tcount++ env.tcount++
return nil return nil
@ -318,7 +352,11 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*
} }
func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
gasLimit := env.header.GasLimit var (
isOsaka = miner.chainConfig.IsOsaka(env.header.Number, env.header.Time)
isCancun = miner.chainConfig.IsCancun(env.header.Number, env.header.Time)
gasLimit = env.header.GasLimit
)
if env.gasPool == nil { if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit) env.gasPool = new(core.GasPool).AddGas(gasLimit)
} }
@ -374,7 +412,7 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
// Most of the blob gas logic here is agnostic as to if the chain supports // Most of the blob gas logic here is agnostic as to if the chain supports
// blobs or not, however the max check panics when called on a chain without // blobs or not, however the max check panics when called on a chain without
// a defined schedule, so we need to verify it's safe to call. // a defined schedule, so we need to verify it's safe to call.
if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) { if isCancun {
left := eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) - env.blobs left := eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) - env.blobs
if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) { if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) {
log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob) log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob)
@ -391,8 +429,14 @@ func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *tran
continue continue
} }
// if inclusion of the transaction would put the block size over the
// maximum we allow, don't add any more txs to the payload.
if isOsaka && env.size+tx.Size() > params.BlockRLPSizeCap-blockRLPSizeCapBuffer {
break
}
// Make sure all transactions after osaka have cell proofs // Make sure all transactions after osaka have cell proofs
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) { if isOsaka {
if sidecar := tx.BlobTxSidecar(); sidecar != nil { if sidecar := tx.BlobTxSidecar(); sidecar != nil {
if sidecar.Version == 0 { if sidecar.Version == 0 {
log.Info("Including blob tx with v0 sidecar, recomputing proofs", "hash", ltx.Hash) log.Info("Including blob tx with v0 sidecar, recomputing proofs", "hash", ltx.Hash)
@ -462,6 +506,9 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment)
if env.header.ExcessBlobGas != nil { if env.header.ExcessBlobGas != nil {
filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(miner.chainConfig, env.header)) filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(miner.chainConfig, env.header))
} }
if miner.chainConfig.IsOsaka(env.header.Number, env.header.Time) {
filter.GasLimitCap = params.MaxTxGas
}
filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false
pendingPlainTxs := miner.txpool.Pending(filter) pendingPlainTxs := miner.txpool.Pending(filter)

View file

@ -411,6 +411,11 @@ type ChainConfig struct {
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague) PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka) OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka)
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle) VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
BPO1Time *uint64 `json:"bpo1Time,omitempty"` // BPO1 switch time (nil = no fork, 0 = already on bpo1)
BPO2Time *uint64 `json:"bpo2Time,omitempty"` // BPO2 switch time (nil = no fork, 0 = already on bpo2)
BPO3Time *uint64 `json:"bpo3Time,omitempty"` // BPO3 switch time (nil = no fork, 0 = already on bpo3)
BPO4Time *uint64 `json:"bpo4Time,omitempty"` // BPO4 switch time (nil = no fork, 0 = already on bpo4)
BPO5Time *uint64 `json:"bpo5Time,omitempty"` // BPO5 switch time (nil = no fork, 0 = already on bpo5)
// TerminalTotalDifficulty is the amount of total difficulty reached by // TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade. // the network that triggers the consensus upgrade.
@ -531,6 +536,21 @@ func (c *ChainConfig) Description() string {
if c.VerkleTime != nil { if c.VerkleTime != nil {
banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime)
} }
if c.BPO1Time != nil {
banner += fmt.Sprintf(" - BPO1: @%-10v\n", *c.BPO1Time)
}
if c.BPO2Time != nil {
banner += fmt.Sprintf(" - BPO2: @%-10v\n", *c.BPO2Time)
}
if c.BPO3Time != nil {
banner += fmt.Sprintf(" - BPO3: @%-10v\n", *c.BPO3Time)
}
if c.BPO4Time != nil {
banner += fmt.Sprintf(" - BPO4: @%-10v\n", *c.BPO4Time)
}
if c.BPO5Time != nil {
banner += fmt.Sprintf(" - BPO5: @%-10v\n", *c.BPO5Time)
}
return banner return banner
} }
@ -547,6 +567,11 @@ type BlobScheduleConfig struct {
Prague *BlobConfig `json:"prague,omitempty"` Prague *BlobConfig `json:"prague,omitempty"`
Osaka *BlobConfig `json:"osaka,omitempty"` Osaka *BlobConfig `json:"osaka,omitempty"`
Verkle *BlobConfig `json:"verkle,omitempty"` Verkle *BlobConfig `json:"verkle,omitempty"`
BPO1 *BlobConfig `json:"bpo1,omitempty"`
BPO2 *BlobConfig `json:"bpo2,omitempty"`
BPO3 *BlobConfig `json:"bpo3,omitempty"`
BPO4 *BlobConfig `json:"bpo4,omitempty"`
BPO5 *BlobConfig `json:"bpo5,omitempty"`
} }
// IsHomestead returns whether num is either equal to the homestead block or greater. // IsHomestead returns whether num is either equal to the homestead block or greater.
@ -654,6 +679,31 @@ func (c *ChainConfig) IsVerkle(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time) return c.IsLondon(num) && isTimestampForked(c.VerkleTime, time)
} }
// IsBPO1 returns whether time is either equal to the BPO1 fork time or greater.
func (c *ChainConfig) IsBPO1(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.BPO1Time, time)
}
// IsBPO2 returns whether time is either equal to the BPO2 fork time or greater.
func (c *ChainConfig) IsBPO2(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.BPO2Time, time)
}
// IsBPO3 returns whether time is either equal to the BPO3 fork time or greater.
func (c *ChainConfig) IsBPO3(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.BPO3Time, time)
}
// IsBPO4 returns whether time is either equal to the BPO4 fork time or greater.
func (c *ChainConfig) IsBPO4(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.BPO4Time, time)
}
// IsBPO5 returns whether time is either equal to the BPO5 fork time or greater.
func (c *ChainConfig) IsBPO5(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.BPO5Time, time)
}
// IsVerkleGenesis checks whether the verkle fork is activated at the genesis block. // IsVerkleGenesis checks whether the verkle fork is activated at the genesis block.
// //
// Verkle mode is considered enabled if the verkle fork time is configured, // Verkle mode is considered enabled if the verkle fork time is configured,
@ -778,6 +828,11 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
{name: "cancun", timestamp: c.CancunTime, config: bsc.Cancun}, {name: "cancun", timestamp: c.CancunTime, config: bsc.Cancun},
{name: "prague", timestamp: c.PragueTime, config: bsc.Prague}, {name: "prague", timestamp: c.PragueTime, config: bsc.Prague},
{name: "osaka", timestamp: c.OsakaTime, config: bsc.Osaka}, {name: "osaka", timestamp: c.OsakaTime, config: bsc.Osaka},
{name: "bpo1", timestamp: c.BPO1Time, config: bsc.BPO1},
{name: "bpo2", timestamp: c.BPO2Time, config: bsc.BPO2},
{name: "bpo3", timestamp: c.BPO3Time, config: bsc.BPO3},
{name: "bpo4", timestamp: c.BPO4Time, config: bsc.BPO4},
{name: "bpo5", timestamp: c.BPO5Time, config: bsc.BPO5},
} { } {
if cur.config != nil { if cur.config != nil {
if err := cur.config.validate(); err != nil { if err := cur.config.validate(); err != nil {

View file

@ -28,17 +28,18 @@ const (
MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1). MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block. GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis. MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero.
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions. TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. MaxTxGas uint64 = 30_000_000 // eip-7825 maximum transaction gas limit
TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
CallStipend uint64 = 2300 // Free gas given at beginning of call. LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
CallStipend uint64 = 2300 // Free gas given at beginning of call.
Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
@ -132,8 +133,12 @@ const (
DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have. DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks. InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract MaxCodeSizeEIP170 = 24576 // Maximum bytecode to permit for a contract
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions MaxInitCodeSizeEIP3860 = 2 * MaxCodeSizeEIP170 // Maximum initcode to permit in a creation transaction and create instructions
MaxCodeSizeEIP7907 = 262144 // Maximum bytecode permitted per contract after EIP-7907
MaxInitCodeSizeEIP7907 = 2 * MaxCodeSizeEIP7907 // Maximum initcode to permit in a creation transaction and create instructions
CodeReadPerWordGasEIP7907 = 2 // Cost per word to read code from disk.
// Precompiled contract gas prices // Precompiled contract gas prices
@ -163,6 +168,8 @@ const (
Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation
Bls12381MapG2Gas uint64 = 23800 // Gas price for BLS12-381 mapping field element to G2 operation Bls12381MapG2Gas uint64 = 23800 // Gas price for BLS12-381 mapping field element to G2 operation
P256VerifyGas uint64 = 3450 // secp256r1 elliptic curve signature verifier gas price
// The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529, // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529,
// up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529 // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529
RefundQuotient uint64 = 2 RefundQuotient uint64 = 2
@ -173,8 +180,12 @@ const (
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size) BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs
BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile. BlobTxPointEvaluationPrecompileGas = 50000 // Gas price for the point evaluation precompile.
BlobBaseCost = 1 << 14 // Base execution gas cost for a blob.
HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935. HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935.
WithdrawalSize = 23 // size of a withdrawal
BlockRLPSizeCap = 8_388_608 // maximum size of an RLP-encoded block
) )
// Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation // Bls12381G1MultiExpDiscountTable is the gas discount table for BLS12-381 G1 multi exponentiation operation