Skip to content

Commit

Permalink
Implement EIP-1559 feature (#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
begmaroman committed Apr 25, 2023
1 parent a0ce316 commit 6cb9d24
Show file tree
Hide file tree
Showing 71 changed files with 2,903 additions and 852 deletions.
50 changes: 45 additions & 5 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ import (
)

const (
BlockGasTargetDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations
defaultCacheSize int = 100 // The default size for Blockchain LRU cache structures
// defaultBaseFeeChangeDenom is the value to bound the amount the base fee can change between blocks.
defaultBaseFeeChangeDenom = 8

// blockGasTargetDivisor is the bound divisor of the gas limit, used in update calculations
blockGasTargetDivisor uint64 = 1024

// defaultCacheSize is the default size for Blockchain LRU cache structures
defaultCacheSize int = 100
)

var (
Expand Down Expand Up @@ -378,7 +384,7 @@ func (b *Blockchain) calculateGasLimit(parentGasLimit uint64) uint64 {
return blockGasTarget
}

delta := parentGasLimit * 1 / BlockGasTargetDivisor
delta := parentGasLimit * 1 / blockGasTargetDivisor
if parentGasLimit < blockGasTarget {
// The gas limit is lower than the gas target, so it should
// increase towards the target
Expand Down Expand Up @@ -1033,7 +1039,7 @@ func (b *Blockchain) updateGasPriceAvgWithBlock(block *types.Block) {

gasPrices := make([]*big.Int, len(block.Transactions))
for i, transaction := range block.Transactions {
gasPrices[i] = transaction.GasPrice
gasPrices[i] = transaction.GetGasPrice(block.Header.BaseFee)
}

b.updateGasPriceAvg(gasPrices)
Expand Down Expand Up @@ -1135,7 +1141,7 @@ func (b *Blockchain) verifyGasLimit(header *types.Header, parentHeader *types.He
diff *= -1
}

limit := parentHeader.GasLimit / BlockGasTargetDivisor
limit := parentHeader.GasLimit / blockGasTargetDivisor
if uint64(diff) > limit {
return fmt.Errorf(
"invalid gas limit, limit = %d, want %d +- %d",
Expand Down Expand Up @@ -1414,3 +1420,37 @@ func (b *Blockchain) GetBlockByNumber(blockNumber uint64, full bool) (*types.Blo
func (b *Blockchain) Close() error {
return b.db.Close()
}

// CalculateBaseFee calculates the basefee of the header.
func (b *Blockchain) CalculateBaseFee(parent *types.Header) uint64 {
if !b.config.Params.Forks.IsLondon(parent.Number) {
return chain.GenesisBaseFee
}

parentGasTarget := parent.GasLimit / b.config.Genesis.BaseFeeEM

// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return parent.BaseFee
}

// If the parent block used more gas than its target, the baseFee should increase.
if parent.GasUsed > parentGasTarget {
gasUsedDelta := parent.GasUsed - parentGasTarget
baseFeeDelta := calcBaseFeeDelta(gasUsedDelta, parentGasTarget, parent.BaseFee)

return parent.BaseFee + common.Max(baseFeeDelta, 1)
}

// Otherwise, if the parent block used less gas than its target, the baseFee should decrease.
gasUsedDelta := parentGasTarget - parent.GasUsed
baseFeeDelta := calcBaseFeeDelta(gasUsedDelta, parentGasTarget, parent.BaseFee)

return common.Max(parent.BaseFee-baseFeeDelta, 0)
}

func calcBaseFeeDelta(gasUsedDelta, parentGasTarget, baseFee uint64) uint64 {
y := baseFee * gasUsedDelta / parentGasTarget

return y / defaultBaseFeeChangeDenom
}
56 changes: 56 additions & 0 deletions blockchain/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1335,3 +1335,59 @@ func TestBlockchain_VerifyBlockBody(t *testing.T) {
assert.ErrorIs(t, err, errUnableToExecute)
})
}

func TestBlockchain_CalculateBaseFee(t *testing.T) {
t.Parallel()

tests := []struct {
blockNumber uint64
parentBaseFee uint64
parentGasLimit uint64
parentGasUsed uint64
expectedBaseFee uint64
elasticityMultiplier uint64
}{
{6, chain.GenesisBaseFee, 20000000, 10000000, chain.GenesisBaseFee, 2}, // usage == target
{6, chain.GenesisBaseFee, 20000000, 10000000, 1125000000, 4}, // usage == target
{6, chain.GenesisBaseFee, 20000000, 9000000, 987500000, 2}, // usage below target
{6, chain.GenesisBaseFee, 20000000, 9000000, 1100000000, 4}, // usage below target
{6, chain.GenesisBaseFee, 20000000, 11000000, 1012500000, 2}, // usage above target
{6, chain.GenesisBaseFee, 20000000, 11000000, 1150000000, 4}, // usage above target
{6, chain.GenesisBaseFee, 20000000, 20000000, 1125000000, 2}, // usage full
{6, chain.GenesisBaseFee, 20000000, 20000000, 1375000000, 4}, // usage full
{6, chain.GenesisBaseFee, 20000000, 0, 875000000, 2}, // usage 0
{6, chain.GenesisBaseFee, 20000000, 0, 875000000, 4}, // usage 0
}

for i, test := range tests {
test := test

t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Parallel()

fork := chain.Fork(5)
blockchain := Blockchain{
config: &chain.Chain{
Params: &chain.Params{
Forks: &chain.Forks{
London: &fork,
},
},
Genesis: &chain.Genesis{
BaseFeeEM: test.elasticityMultiplier,
},
},
}

parent := &types.Header{
Number: test.blockNumber,
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: test.parentBaseFee,
}

got := blockchain.CalculateBaseFee(parent)
assert.Equal(t, test.expectedBaseFee, got, fmt.Sprintf("expected %d, got %d", test.expectedBaseFee, got))
})
}
}
56 changes: 45 additions & 11 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,28 @@ import (
"math/big"
"os"

"github.com/hashicorp/go-multierror"
"github.com/umbracle/ethgo"

"github.com/0xPolygon/polygon-edge/helper/common"
"github.com/0xPolygon/polygon-edge/helper/hex"
"github.com/0xPolygon/polygon-edge/types"
"github.com/hashicorp/go-multierror"
)

var (
const (
// GenesisBaseFeeEM is the initial base fee elasticity multiplier for EIP-1559 blocks.
GenesisBaseFeeEM = 2

// GenesisGasLimit is the default gas limit of the Genesis block.
GenesisGasLimit uint64 = 4712388

// GenesisDifficulty is the default difficulty of the Genesis block.
GenesisDifficulty = big.NewInt(131072)
GenesisDifficulty uint64 = 131072
)

var (
// GenesisBaseFee is the initial base fee for EIP-1559 blocks.
GenesisBaseFee = ethgo.Gwei(1).Uint64()
)

// Chain is the blockchain chain configuration
Expand All @@ -40,6 +51,8 @@ type Genesis struct {
Mixhash types.Hash `json:"mixHash"`
Coinbase types.Address `json:"coinbase"`
Alloc map[types.Address]*GenesisAccount `json:"alloc,omitempty"`
BaseFee uint64 `json:"baseFee"`
BaseFeeEM uint64 `json:"baseFeeEM"`

// Override
StateRoot types.Hash
Expand All @@ -66,6 +79,7 @@ func (g *Genesis) GenesisHeader() *types.Header {
ExtraData: g.ExtraData,
GasLimit: g.GasLimit,
GasUsed: g.GasUsed,
BaseFee: g.BaseFee,
Difficulty: g.Difficulty,
MixHash: g.Mixhash,
Miner: g.Coinbase.Bytes(),
Expand All @@ -81,7 +95,11 @@ func (g *Genesis) GenesisHeader() *types.Header {
}

if g.Difficulty == 0 {
head.Difficulty = GenesisDifficulty.Uint64()
head.Difficulty = GenesisDifficulty
}

if g.BaseFee == 0 {
head.BaseFee = GenesisBaseFee
}

return head
Expand Down Expand Up @@ -109,6 +127,8 @@ func (g *Genesis) MarshalJSON() ([]byte, error) {
Number *string `json:"number,omitempty"`
GasUsed *string `json:"gasUsed,omitempty"`
ParentHash types.Hash `json:"parentHash"`
BaseFee *string `json:"baseFee"`
BaseFeeEM *string `json:"baseFeeEM"`
}

var enc Genesis
Expand All @@ -119,6 +139,8 @@ func (g *Genesis) MarshalJSON() ([]byte, error) {

enc.GasLimit = types.EncodeUint64(g.GasLimit)
enc.Difficulty = types.EncodeUint64(g.Difficulty)
enc.BaseFee = types.EncodeUint64(g.BaseFee)
enc.BaseFeeEM = types.EncodeUint64(g.BaseFeeEM)

enc.Mixhash = g.Mixhash
enc.Coinbase = g.Coinbase
Expand Down Expand Up @@ -153,6 +175,8 @@ func (g *Genesis) UnmarshalJSON(data []byte) error {
Number *string `json:"number"`
GasUsed *string `json:"gasUsed"`
ParentHash *types.Hash `json:"parentHash"`
BaseFee *string `json:"baseFee"`
BaseFeeEM *string `json:"baseFeeEM"`
}

var dec Genesis
Expand All @@ -166,14 +190,14 @@ func (g *Genesis) UnmarshalJSON(data []byte) error {
err = multierror.Append(err, fmt.Errorf("%s: %w", field, subErr))
}

nonce, subErr := types.ParseUint64orHex(dec.Nonce)
nonce, subErr := common.ParseUint64orHex(dec.Nonce)
if subErr != nil {
parseError("nonce", subErr)
}

binary.BigEndian.PutUint64(g.Nonce[:], nonce)

g.Timestamp, subErr = types.ParseUint64orHex(dec.Timestamp)
g.Timestamp, subErr = common.ParseUint64orHex(dec.Timestamp)
if subErr != nil {
parseError("timestamp", subErr)
}
Expand All @@ -189,16 +213,26 @@ func (g *Genesis) UnmarshalJSON(data []byte) error {
return fmt.Errorf("field 'gaslimit' is required")
}

g.GasLimit, subErr = types.ParseUint64orHex(dec.GasLimit)
g.GasLimit, subErr = common.ParseUint64orHex(dec.GasLimit)
if subErr != nil {
parseError("gaslimit", subErr)
}

g.Difficulty, subErr = types.ParseUint64orHex(dec.Difficulty)
g.Difficulty, subErr = common.ParseUint64orHex(dec.Difficulty)
if subErr != nil {
parseError("difficulty", subErr)
}

g.BaseFee, subErr = common.ParseUint64orHex(dec.BaseFee)
if subErr != nil {
parseError("baseFee", subErr)
}

g.BaseFeeEM, subErr = common.ParseUint64orHex(dec.BaseFeeEM)
if subErr != nil {
parseError("baseFeeEM", subErr)
}

if dec.Mixhash != nil {
g.Mixhash = *dec.Mixhash
}
Expand All @@ -214,12 +248,12 @@ func (g *Genesis) UnmarshalJSON(data []byte) error {
}
}

g.Number, subErr = types.ParseUint64orHex(dec.Number)
g.Number, subErr = common.ParseUint64orHex(dec.Number)
if subErr != nil {
parseError("number", subErr)
}

g.GasUsed, subErr = types.ParseUint64orHex(dec.GasUsed)
g.GasUsed, subErr = common.ParseUint64orHex(dec.GasUsed)
if subErr != nil {
parseError("gasused", subErr)
}
Expand Down Expand Up @@ -318,7 +352,7 @@ func (g *GenesisAccount) UnmarshalJSON(data []byte) error {
parseError("balance", subErr)
}

g.Nonce, subErr = types.ParseUint64orHex(dec.Nonce)
g.Nonce, subErr = common.ParseUint64orHex(dec.Nonce)

if subErr != nil {
parseError("nonce", subErr)
Expand Down
35 changes: 35 additions & 0 deletions chain/params.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package chain

import (
"errors"
"math/big"
"sort"

"github.com/0xPolygon/polygon-edge/types"
)

var (
// ErrBurnContractAddressMissing is the error when a contract address is not provided
ErrBurnContractAddressMissing = errors.New("burn contract address missing")
)

// Params are all the set of params for the chain
type Params struct {
Forks *Forks `json:"forks"`
Expand All @@ -19,6 +26,9 @@ type Params struct {
ContractDeployerBlockList *AddressListConfig `json:"contractDeployerBlockList,omitempty"`
TransactionsAllowList *AddressListConfig `json:"transactionsAllowList,omitempty"`
TransactionsBlockList *AddressListConfig `json:"transactionsBlockList,omitempty"`

// Governance contract where the token will be sent to and burn in london fork
BurnContract map[uint64]string `json:"burnContract"`
}

type AddressListConfig struct {
Expand All @@ -29,6 +39,31 @@ type AddressListConfig struct {
EnabledAddresses []types.Address `json:"enabledAddresses,omitempty"`
}

// CalculateBurnContract calculates burn contract address for the given block number
func (p *Params) CalculateBurnContract(block uint64) (types.Address, error) {
blocks := make([]uint64, 0, len(p.BurnContract))

for startBlock := range p.BurnContract {
blocks = append(blocks, startBlock)
}

if len(blocks) == 0 {
return types.ZeroAddress, ErrBurnContractAddressMissing
}

sort.Slice(blocks, func(i, j int) bool {
return blocks[i] < blocks[j]
})

for i := 0; i < len(blocks)-1; i++ {
if block >= blocks[i] && block < blocks[i+1] {
return types.StringToAddress(p.BurnContract[blocks[i]]), nil
}
}

return types.StringToAddress(p.BurnContract[blocks[len(blocks)-1]]), nil
}

func (p *Params) GetEngine() string {
// We know there is already one
for k := range p.Engine {
Expand Down
Loading

0 comments on commit 6cb9d24

Please sign in to comment.