Skip to content
Merged
1 change: 1 addition & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Add pending releases here
- Upgrade to Go version 1.24
- Implement ACP-226: Set expected block gas cost to 0 in Granite network upgrade, removing block gas cost requirements for block building.
- Implement ACP-226: Add timeMilliseconds (Unix uint64) timestamp to block header for Granite upgrade.

## [v0.7.9](https://github.com/ava-labs/subnet-evm/releases/tag/v0.7.9)
Expand Down
12 changes: 6 additions & 6 deletions core/blockchain_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,7 @@ func GenerateChainInvalidBlockFee(t *testing.T, create createFunc) {
// Ensure that key1 has some funds in the genesis block.
genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether))
gspec := &Genesis{
Config: params.TestChainConfig,
Config: params.TestFortunaChainConfig,
Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
}

Expand All @@ -1404,10 +1404,10 @@ func GenerateChainInvalidBlockFee(t *testing.T, create createFunc) {
t.Cleanup(blockchain.Stop)

// This call generates a chain of 3 blocks.
signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
_, _, _, err = GenerateChainWithGenesis(gspec, blockchain.engine, 3, extras.TestChainConfig.FeeConfig.TargetBlockRate-1, func(_ int, gen *BlockGen) {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
ChainID: params.TestFortunaChainConfig.ChainID,
Nonce: gen.TxNonce(addr1),
To: &addr2,
Gas: ethparams.TxGas,
Expand Down Expand Up @@ -1436,7 +1436,7 @@ func InsertChainInvalidBlockFee(t *testing.T, create createFunc) {
// Ensure that key1 has some funds in the genesis block.
genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether))
gspec := &Genesis{
Config: params.TestChainConfig,
Config: params.TestFortunaChainConfig,
Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
}

Expand All @@ -1445,11 +1445,11 @@ func InsertChainInvalidBlockFee(t *testing.T, create createFunc) {
t.Cleanup(blockchain.Stop)

// This call generates a chain of 3 blocks.
signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
eng := dummy.NewFakerWithMode(dummy.Mode{ModeSkipBlockFee: true, ModeSkipCoinbase: true})
_, chain, _, err := GenerateChainWithGenesis(gspec, eng, 3, extras.TestChainConfig.FeeConfig.TargetBlockRate-1, func(_ int, gen *BlockGen) {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
ChainID: params.TestFortunaChainConfig.ChainID,
Nonce: gen.TxNonce(addr1),
To: &addr2,
Gas: ethparams.TxGas,
Expand Down
73 changes: 56 additions & 17 deletions eth/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,12 @@ func testGenBlock(t *testing.T, tip int64, numTx int) func(int, *core.BlockGen)
b.SetCoinbase(common.Address{1})

txTip := big.NewInt(tip * params.GWei)
signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
baseFee := b.BaseFee()
feeCap := new(big.Int).Add(baseFee, txTip)
for j := 0; j < numTx; j++ {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
ChainID: params.TestFortunaChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: ethparams.TxGas,
Expand Down Expand Up @@ -274,7 +274,7 @@ func TestSuggestTipCapNetworkUpgrades(t *testing.T) {

func TestSuggestTipCapSimple(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestFortunaChainConfig,
numBlocks: 3,
genBlock: testGenBlock(t, 55, 370),
expectedTip: big.NewInt(1_287_001_288),
Expand All @@ -283,7 +283,7 @@ func TestSuggestTipCapSimple(t *testing.T) {

func TestSuggestTipCapSimpleFloor(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestFortunaChainConfig,
numBlocks: 1,
genBlock: testGenBlock(t, 55, 370),
expectedTip: big.NewInt(643_500_644),
Expand All @@ -293,17 +293,17 @@ func TestSuggestTipCapSimpleFloor(t *testing.T) {
func TestSuggestTipCapSmallTips(t *testing.T) {
tip := big.NewInt(550 * params.GWei)
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestFortunaChainConfig,
numBlocks: 3,
genBlock: func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})

signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
baseFee := b.BaseFee()
feeCap := new(big.Int).Add(baseFee, tip)
for j := 0; j < 185; j++ {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
ChainID: params.TestFortunaChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: ethparams.TxGas,
Expand All @@ -317,7 +317,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) {
}
b.AddTx(tx)
tx = types.NewTx(&types.DynamicFeeTx{
ChainID: params.TestChainConfig.ChainID,
ChainID: params.TestFortunaChainConfig.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: ethparams.TxGas,
Expand All @@ -336,7 +336,7 @@ func TestSuggestTipCapSmallTips(t *testing.T) {

func TestSuggestTipCapMinGas(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestFortunaChainConfig,
numBlocks: 3,
genBlock: testGenBlock(t, 500, 50),
expectedTip: DefaultMinPrice,
Expand All @@ -352,10 +352,10 @@ func TestSuggestGasPriceSubnetEVM(t *testing.T) {
Percentile: 60,
}

backend := newTestBackend(t, params.TestChainConfig, 3, func(i int, b *core.BlockGen) {
backend := newTestBackend(t, params.TestFortunaChainConfig, 3, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})

signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
gasPrice := big.NewInt(legacy.BaseFee)
for j := 0; j < 50; j++ {
tx := types.NewTx(&types.LegacyTx{
Expand All @@ -379,18 +379,57 @@ func TestSuggestGasPriceSubnetEVM(t *testing.T) {
require.NoError(t, err)
}

// NOTE: [Oracle.SuggestTipCap] does NOT simply return the "required" (minimum) tip.
// The oracle computes a percentile of recent required tips (not observed on-chain tips)
// within a time/blocks lookback window and applies a small floor (e.g., 1 wei in tests):
//
// suggested = max(floor, recent-required-percentile)
//
// After Granite, BlockGasCost is 0 and per-block required tips are 0, so the oracle
// suggestion equals the floor (1 wei) in steady state, regardless of high on-chain tips.
// The cases below exercise behavior across forks using the same percentile logic and floor.
func TestSuggestTipCapMaxBlocksLookback(t *testing.T) {
cases := []struct {
chainConfig *params.ChainConfig
expectedTip *big.Int
}{
// TODO: remove Fortuna case when we activate Granite
{
chainConfig: params.TestFortunaChainConfig,
expectedTip: big.NewInt(1),
},
{
chainConfig: params.TestChainConfig,
expectedTip: big.NewInt(1),
},
}
for _, c := range cases {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: c.chainConfig,
numBlocks: 200,
genBlock: testGenBlock(t, 550, 80),
expectedTip: c.expectedTip,
}, defaultOracleConfig())
}
}

// Post-Granite, even very high observed tx tips should not affect SuggestTipCap, which
// is computed from required tips. Since required tips are 0 in Granite, the returned
// suggestion should be the floor (1 wei).
func TestSuggestTipCapIgnoresObservedTipsPostGranite(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestChainConfig, // Granite active in TestChainConfig
numBlocks: 20,
genBlock: testGenBlock(t, 550, 370),
expectedTip: big.NewInt(5_807_226_111),
// Generate blocks with very high on-chain tips to ensure they wouldn't bias the result
// if the oracle looked at observed tips. Expectation remains 1 wei.
genBlock: testGenBlock(t, 100_000, 80),
expectedTip: big.NewInt(1),
}, defaultOracleConfig())
}

func TestSuggestTipCapMaxBlocksSecondsLookback(t *testing.T) {
applyGasPriceTest(t, suggestTipCapTest{
chainConfig: params.TestChainConfig,
chainConfig: params.TestFortunaChainConfig,
numBlocks: 20,
genBlock: testGenBlock(t, 550, 370),
expectedTip: big.NewInt(10_384_877_852),
Expand All @@ -407,14 +446,14 @@ func TestEstimateBaseFeeAfterFeeConfigUpdate(t *testing.T) {
}

// create a chain config with fee manager enabled at genesis with [addr] as the admin
chainConfig := params.Copy(params.TestChainConfig)
chainConfig := params.Copy(params.TestFortunaChainConfig)
chainConfigExtra := params.GetExtra(&chainConfig)
chainConfigExtra.GenesisPrecompiles = extras.Precompiles{
feemanager.ConfigKey: feemanager.NewConfig(utils.NewUint64(0), []common.Address{addr}, nil, nil, nil),
}

// create a fee config with higher MinBaseFee and prepare it for inclusion in a tx
signer := types.LatestSigner(params.TestChainConfig)
signer := types.LatestSigner(params.TestFortunaChainConfig)
highFeeConfig := chainConfigExtra.FeeConfig
highFeeConfig.MinBaseFee = big.NewInt(28_000_000_000)
data, err := feemanager.PackSetFeeConfig(highFeeConfig)
Expand Down
6 changes: 6 additions & 0 deletions plugin/evm/customheader/block_gas_cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var (
// BlockGasCost calculates the required block gas cost based on the parent
// header and the timestamp of the new block.
// Prior to Subnet-EVM, the returned block gas cost will be nil.
// In Granite, the returned block gas cost will be 0.
func BlockGasCost(
config *extras.ChainConfig,
feeConfig commontype.FeeConfig,
Expand All @@ -37,7 +38,12 @@ func BlockGasCost(
if !config.IsSubnetEVM(timestamp) {
return nil
}

if config.IsGranite(timestamp) {
return big.NewInt(0)
}
step := feeConfig.BlockGasCostStep.Uint64()

// Treat an invalid parent/current time combination as 0 elapsed time.
//
// TODO: Does it even make sense to handle this? The timestamp should be
Expand Down
14 changes: 11 additions & 3 deletions plugin/evm/customheader/block_gas_cost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,29 @@ func BlockGasCostTest(t *testing.T, feeConfig commontype.FeeConfig) {
expected: nil,
},
{
name: "normal",
upgrades: extras.TestChainConfig.NetworkUpgrades,
name: "normal_pre_granite",
upgrades: extras.TestFortunaChainConfig.NetworkUpgrades,
parentTime: 10,
parentCost: maxBlockGasCostBig,
timestamp: 10 + targetBlockRate + 1,
expected: new(big.Int).SetUint64(maxBlockGasCost - blockGasCostStep),
},
{
name: "negative_time_elapsed",
upgrades: extras.TestChainConfig.NetworkUpgrades,
upgrades: extras.TestFortunaChainConfig.NetworkUpgrades,
parentTime: 10,
parentCost: feeConfig.MinBlockGasCost,
timestamp: 9,
expected: new(big.Int).SetUint64(minBlockGasCost + blockGasCostStep*targetBlockRate),
},
{
name: "granite_returns_zero",
upgrades: extras.TestGraniteChainConfig.NetworkUpgrades,
parentTime: 10,
parentCost: big.NewInt(int64(maxBlockGasCost)),
timestamp: 10 + targetBlockRate + 1,
expected: big.NewInt(0),
},
}

for _, test := range tests {
Expand Down
94 changes: 0 additions & 94 deletions plugin/evm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2179,100 +2179,6 @@ func testLastAcceptedBlockNumberAllow(t *testing.T, scheme string) {
}
}

// Regression test to ensure we can build blocks if we are starting with the
// Subnet EVM ruleset in genesis.
func TestBuildSubnetEVMBlock(t *testing.T) {
for _, scheme := range schemes {
t.Run(scheme, func(t *testing.T) {
testBuildSubnetEVMBlock(t, scheme)
})
}
}

func testBuildSubnetEVMBlock(t *testing.T, scheme string) {
tvm := newVM(t, testVMConfig{
genesisJSON: genesisJSONSubnetEVM,
configJSON: getConfig(scheme, ""),
})

defer func() {
if err := tvm.vm.Shutdown(context.Background()); err != nil {
t.Fatal(err)
}
}()

newTxPoolHeadChan := make(chan core.NewTxPoolReorgEvent, 1)
tvm.vm.txPool.SubscribeNewReorgEvent(newTxPoolHeadChan)

tx := types.NewTransaction(uint64(0), testEthAddrs[1], new(big.Int).Mul(firstTxAmount, big.NewInt(4)), 21000, big.NewInt(testMinGasPrice*3), nil)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainConfig.ChainID), testKeys[0].ToECDSA())
if err != nil {
t.Fatal(err)
}

txErrors := tvm.vm.txPool.AddRemotesSync([]*types.Transaction{signedTx})
for i, err := range txErrors {
if err != nil {
t.Fatalf("Failed to add tx at index %d: %s", i, err)
}
}

blk := issueAndAccept(t, tvm.vm)
newHead := <-newTxPoolHeadChan
if newHead.Head.Hash() != common.Hash(blk.ID()) {
t.Fatalf("Expected new block to match")
}

txs := make([]*types.Transaction, 10)
for i := 0; i < 10; i++ {
tx := types.NewTransaction(uint64(i), testEthAddrs[0], big.NewInt(10), 21000, big.NewInt(testMinGasPrice*3), nil)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(tvm.vm.chainConfig.ChainID), testKeys[1].ToECDSA())
if err != nil {
t.Fatal(err)
}
txs[i] = signedTx
}
errs := tvm.vm.txPool.AddRemotesSync(txs)
for i, err := range errs {
if err != nil {
t.Fatalf("Failed to add tx at index %d: %s", i, err)
}
}

blk = issueAndAccept(t, tvm.vm)
ethBlk := blk.(*chain.BlockWrapper).Block.(*wrappedBlock).ethBlock
if customtypes.BlockGasCost(ethBlk) == nil || customtypes.BlockGasCost(ethBlk).Cmp(big.NewInt(100)) < 0 {
t.Fatalf("expected blockGasCost to be at least 100 but got %d", customtypes.BlockGasCost(ethBlk))
}
chainConfig := params.GetExtra(tvm.vm.chainConfig)
minRequiredTip, err := customheader.EstimateRequiredTip(chainConfig, ethBlk.Header())
if err != nil {
t.Fatal(err)
}
if minRequiredTip == nil || minRequiredTip.Cmp(big.NewInt(0.05*utils.GWei)) < 0 {
t.Fatalf("expected minRequiredTip to be at least 0.05 gwei but got %d", minRequiredTip)
}

lastAcceptedID, err := tvm.vm.LastAccepted(context.Background())
if err != nil {
t.Fatal(err)
}
if lastAcceptedID != blk.ID() {
t.Fatalf("Expected last accepted blockID to be the accepted block: %s, but found %s", blk.ID(), lastAcceptedID)
}

// Confirm all txs are present
ethBlkTxs := tvm.vm.blockChain.GetBlockByNumber(2).Transactions()
for i, tx := range txs {
if len(ethBlkTxs) <= i {
t.Fatalf("missing transactions expected: %d but found: %d", len(txs), len(ethBlkTxs))
}
if ethBlkTxs[i].Hash() != tx.Hash() {
t.Fatalf("expected tx at index %d to have hash: %x but has: %x", i, txs[i].Hash(), tx.Hash())
}
}
}

func TestBuildAllowListActivationBlock(t *testing.T) {
for _, scheme := range schemes {
t.Run(scheme, func(t *testing.T) {
Expand Down
Loading