Skip to content

Fusaka devnet 2 #31987

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

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 63 additions & 33 deletions consensus/misc/eip4844/eip4844.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params/forks"
)

var (
Expand Down Expand Up @@ -71,48 +70,75 @@ func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTim
parentExcessBlobGas = *parent.ExcessBlobGas
parentBlobGasUsed = *parent.BlobGasUsed
}
excessBlobGas := parentExcessBlobGas + parentBlobGasUsed
targetGas := uint64(targetBlobsPerBlock(config, headTimestamp)) * params.BlobTxBlobGasPerBlob
var (
excessBlobGas = parentExcessBlobGas + parentBlobGasUsed
target = targetBlobsPerBlock(config, headTimestamp)
targetGas = uint64(target) * params.BlobTxBlobGasPerBlob
)
if excessBlobGas < targetGas {
return 0
}
if !config.IsOsaka(config.LondonBlock, headTimestamp) {
return excessBlobGas - targetGas
}
// EIP-7918.
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
}

// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
var frac uint64
switch config.LatestFork(header.Time) {
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:
blobConfig := latestBlobConfig(config, header.Time)
if blobConfig == nil {
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.
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
if cfg.BlobScheduleConfig == nil {
blobConfig := latestBlobConfig(cfg, time)
if blobConfig == nil {
return 0
}
return blobConfig.Max
}

func latestBlobConfig(cfg *params.ChainConfig, time uint64) *params.BlobConfig {
if cfg.BlobScheduleConfig == nil {
return nil
}
var (
london = cfg.LondonBlock
s = cfg.BlobScheduleConfig
)
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:
return s.Osaka.Max
return s.Osaka
case cfg.IsPrague(london, time) && s.Prague != nil:
return s.Prague.Max
return s.Prague
case cfg.IsCancun(london, time) && s.Cancun != nil:
return s.Cancun.Max
return s.Cancun
default:
return 0
return nil
}
}

Expand All @@ -129,6 +155,16 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
return 0
}
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:
return s.Osaka.Max
case s.Prague != nil:
Expand All @@ -142,23 +178,11 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {

// targetBlobsPerBlock returns the target number of blobs in a block at the given timestamp.
func targetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
if cfg.BlobScheduleConfig == nil {
return 0
}
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:
blobConfig := latestBlobConfig(cfg, time)
if blobConfig == nil {
return 0
}
return blobConfig.Target
}

// fakeExponential approximates factor * e ** (numerator / denominator) using
Expand All @@ -177,3 +201,9 @@ func fakeExponential(factor, numerator, denominator *big.Int) *big.Int {
}
return output.Div(output, denominator)
}

// calcBlobPrice calculates the blob price based 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))
}
53 changes: 53 additions & 0 deletions consensus/misc/eip4844/eip4844_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,56 @@ func TestFakeExponential(t *testing.T) {
}
}
}

func TestCalcExcessBlobGasEIP7918(t *testing.T) {
// TODO: replace with a test config that has Osaka enabled.
c := *params.MainnetChainConfig
cfg := &c
cfg.OsakaTime = new(uint64)
pragueSchedule := *cfg.BlobScheduleConfig.Prague
cfg.BlobScheduleConfig.Osaka = &pragueSchedule

var (
targetBlobs = targetBlobsPerBlock(cfg, *cfg.CancunTime)
blobGasPerBlob = uint64(params.BlobTxBlobGasPerBlob)
blobGasTarget = uint64(targetBlobs) * blobGasPerBlob
)

makeHeader := func(
parentExcess uint64,
parentBaseFee uint64,
blobsUsed int,
) *types.Header {
blobGasUsed := uint64(blobsUsed) * blobGasPerBlob
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)
}
}
}
4 changes: 4 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Blob transactions may be present after the Cancun fork.
var blobs int
for i, tx := range block.Transactions() {
if v.config.IsOsaka(block.Number(), block.Time()) && tx.Gas() > params.MaxTxGas {
return fmt.Errorf("transaction exceeds maximum allowed gas limit (has %d gas)", tx.Gas())
}

// Count the number of blobs to validate against the header's blobGasUsed
blobs += len(tx.BlobHashes())

Expand Down
16 changes: 15 additions & 1 deletion core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,20 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
}
pool.mu.Lock()
if reset != nil {
if reset.newHead != nil && reset.oldHead != nil {
if pool.chainconfig.IsOsaka(reset.newHead.Number, reset.newHead.Time) && !pool.chainconfig.IsOsaka(reset.oldHead.Number, reset.oldHead.Time) {
var removeHashes []common.Hash
pool.all.Range(func(hash common.Hash, tx *types.Transaction) bool {
if tx.Gas() > params.MaxTxGas {
removeHashes = append(removeHashes, hash)
}
return true
})
for _, hash := range removeHashes {
pool.all.Remove(hash)
}
}
}
// Reset from the old head to the new, rescheduling any reorged transactions
pool.reset(reset.oldHead, reset.newHead)

Expand All @@ -1279,7 +1293,7 @@ func (pool *LegacyPool) runReorg(done chan struct{}, reset *txpoolResetRequest,
// because of another transaction (e.g. higher gas price).
if reset != nil {
pool.demoteUnexecutables()
if reset.newHead != nil {
if reset.newHead != reset.oldHead {
if pool.chainconfig.IsLondon(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) {
pendingBaseFee := eip1559.CalcBaseFee(pool.chainconfig, reset.newHead)
pool.priced.SetBaseFee(pendingBaseFee)
Expand Down
15 changes: 15 additions & 0 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type TxPool struct {
term chan struct{} // Termination channel to detect a closed pool

sync chan chan error // Testing / simulator channel to block until internal reset is done

headLock sync.RWMutex
head *types.Header // this reflects the state from the latest pool reset
}

// New creates a new transaction pool to gather, sort and filter inbound
Expand Down Expand Up @@ -103,6 +106,7 @@ func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) {
quit: make(chan chan error),
term: make(chan struct{}),
sync: make(chan chan error),
head: head,
}
reserver := NewReservationTracker()
for i, subpool := range subpools {
Expand Down Expand Up @@ -202,6 +206,9 @@ func (p *TxPool) loop(head *types.Header) {
}
select {
case resetDone <- newHead:
p.headLock.Lock()
p.head = newHead
p.headLock.Unlock()
case <-p.term:
}
}(oldHead, newHead)
Expand Down Expand Up @@ -256,6 +263,14 @@ func (p *TxPool) loop(head *types.Header) {
errc <- nil
}

// Head returns the header which corresponds to the most recent successful pool
// reset.
func (p *TxPool) Head() *types.Header {
p.headLock.RLock()
defer p.headLock.RUnlock()
return types.CopyHeader(p.head)
}

// SetGasTip updates the minimum gas tip required by the transaction pool for a
// new transaction, and drops all transactions below this threshold.
func (p *TxPool) SetGasTip(tip *big.Int) {
Expand Down
3 changes: 3 additions & 0 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize)
}
if rules.IsOsaka && tx.Gas() > params.MaxTxGas {
return fmt.Errorf("transaction gas exceeded max allowed (%d): %d", params.MaxTxGas, tx.Gas())
}
// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur for transactions created using the RPC.
if tx.Value().Sign() < 0 {
Expand Down
43 changes: 43 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/crypto/secp256r1"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)
Expand Down Expand Up @@ -161,6 +162,14 @@ var PrecompiledContractsOsaka = PrecompiledContracts{
common.BytesToAddress([]byte{0x0f}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x10}): &bls12381MapG1{},
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 (
Expand Down Expand Up @@ -1230,3 +1239,37 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {

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) {
// Required input length is 160 bytes
const p256VerifyInputLength = 160
// Check the input length
if len(input) != p256VerifyInputLength {
// Input length is invalid
return nil, nil
}

// Extract the 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 secp256r1 signature
if secp256r1.Verify(hash, r, s, x, y) {
// Signature is valid
return common.LeftPadBytes(common.Big1.Bytes(), 32), nil
} else {
// Signature is invalid
return nil, nil
}
}
14 changes: 14 additions & 0 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x0f, 0x0e}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x0f, 0x0f}): &bls12381MapG1{},
common.BytesToAddress([]byte{0x0f, 0x10}): &bls12381MapG2{},

common.BytesToAddress([]byte{0x0b}): &p256Verify{},
}

// EIP-152 test vectors
Expand Down Expand Up @@ -397,3 +399,15 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.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) }
Loading