diff --git a/core/blockchain.go b/core/blockchain.go index 6bacdefd04..f3d55fc51d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -232,10 +232,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par futureBlocks, _ := lru.New(maxFutureBlocks) // override snapshot setting if chainConfig.Zktrie && cacheConfig.SnapshotLimit > 0 { - log.Warn("snapshot has been disabled by zktrie") + log.Warn("Snapshot has been disabled by zktrie") cacheConfig.SnapshotLimit = 0 } + if chainConfig.FeeVaultAddress != nil { + log.Warn("Using fee vault address", "FeeVaultAddress", *chainConfig.FeeVaultAddress) + } + bc := &BlockChain{ chainConfig: chainConfig, cacheConfig: cacheConfig, diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 3bb66a2c49..20e1fed727 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3093,3 +3093,79 @@ func TestPoseidonCodeHash(t *testing.T) { assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash1, "code hash mismatch") assert.Equal(t, common.HexToHash("0x2fa5836118b70a257defd2e54064ab63cc9bb2e91823eaacbdef32370050b5b2"), codeHash2, "code hash mismatch") } + +// TestFeeVault tests that the fee vault receives all tx fees correctly. +func TestFeeVault(t *testing.T) { + var ( + aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") + + // Generate a canonical chain to act as the main dataset + engine = ethash.NewFaker() + db = rawdb.NewMemoryDatabase() + + // A sender who makes transactions, has some funds + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr1: {Balance: funds}}, + } + ) + + gspec.Config.BerlinBlock = common.Big0 + gspec.Config.LondonBlock = common.Big0 + genesis := gspec.MustCommit(db) + signer := types.LatestSigner(gspec.Config) + + blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + + // One transaction to 0xAAAA + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &aa, + Gas: 30000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + AccessList: types.AccessList{}, + Data: []byte{}, + } + + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key1) + + b.AddTx(tx) + }) + + diskdb := rawdb.NewMemoryDatabase() + gspec.MustCommit(diskdb) + + chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + block := chain.GetBlockByNumber(1) + state, _ := chain.State() + + // Ensure that miner received only the miner reward + actual := state.GetBalance(block.Coinbase()) + expected := ethash.ConstantinopleBlockReward + + if actual.Cmp(expected) != 0 { + t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) + } + + // Ensure that the fee vault received all tx fees + actual = state.GetBalance(*params.TestChainConfig.FeeVaultAddress) + expected = new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64()) + + if actual.Cmp(expected) != 0 { + t.Fatalf("fee vault balance incorrect: expected %d, got %d", expected, actual) + } +} diff --git a/core/state_transition.go b/core/state_transition.go index ed2e9f8567..48cb02bea9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -336,7 +336,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if london { effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee)) } - st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) + st.state.AddBalance(st.evm.FeeRecipient(), new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)) return &ExecutionResult{ UsedGas: st.gasUsed(), diff --git a/core/vm/evm.go b/core/vm/evm.go index 11af046979..922176800d 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -532,3 +532,12 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +// FeeRecipient returns the environment's transaction fee recipient address. +func (evm *EVM) FeeRecipient() common.Address { + if evm.ChainConfig().FeeVaultAddress != nil { + return *evm.chainConfig.FeeVaultAddress + } else { + return evm.Context.Coinbase + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 56a1009066..90a723d124 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -454,7 +454,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) + scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.FeeRecipient().Bytes())) return nil, nil } diff --git a/params/config.go b/params/config.go index 3adde75bf9..e059631afa 100644 --- a/params/config.go +++ b/params/config.go @@ -258,16 +258,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, false} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, false, nil} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, false, &common.Address{123}} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -356,8 +356,11 @@ type ChainConfig struct { Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` - // Use zktrie + // Scroll genesis extension: Use zktrie Zktrie bool `json:"zktrie,omitempty"` + + // Scroll genesis extension: Transaction fee vault address [optional] + FeeVaultAddress *common.Address `json:"feeVaultAddress,omitempty"` } // EthashConfig is the consensus engine configs for proof-of-work based sealing.