diff --git a/consensus/consortium/main.go b/consensus/consortium/main.go index ea0657949c..67fe97ca1a 100644 --- a/consensus/consortium/main.go +++ b/consensus/consortium/main.go @@ -1,18 +1,20 @@ package consortium import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" consortiumCommon "github.com/ethereum/go-ethereum/consensus/consortium/common" v1 "github.com/ethereum/go-ethereum/consensus/consortium/v1" v2 "github.com/ethereum/go-ethereum/consensus/consortium/v2" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "math/big" ) // Consortium is a proxy that decides the consensus version will be called @@ -184,10 +186,28 @@ func (c *Consortium) SetGetFenixValidators(fn func() ([]common.Address, error)) // IsSystemTransaction implements consensus.PoSA. It is only available on v2 since v1 doesn't have system contract func (c *Consortium) IsSystemTransaction(tx *types.Transaction, header *types.Header) (bool, error) { - return c.v2.IsSystemTransaction(tx, header) + msg, err := tx.AsMessage(types.MakeSigner(c.chainConfig, header.Number), header.BaseFee) + if err != nil { + return false, err + } + return c.v2.IsSystemMessage(msg, header) } // IsSystemContract implements consensus.PoSA. It is only available on v2 since v1 doesn't have system contract func (c *Consortium) IsSystemContract(to *common.Address) bool { return c.v2.IsSystemContract(to) } + +// Determine if the transaction is submitBlockReward transaction with +// non-zero msg.value. This function based on the fact that +// submitBlockReward is the only system transaction that may have +// non-zero msg.value +func (c *Consortium) IsSubmitBlockRewardNonZeroAmount(msg core.Message, header *types.Header) bool { + if c.chainConfig.IsConsortiumV2(new(big.Int).Add(header.Number, common.Big1)) { + isSystemMsg, _ := c.v2.IsSystemMessage(msg, header) + if isSystemMsg && msg.Value().Cmp(common.Big0) > 0 { + return true + } + } + return false +} diff --git a/consensus/consortium/v2/consortium.go b/consensus/consortium/v2/consortium.go index d65e0091c4..e7dc01d239 100644 --- a/consensus/consortium/v2/consortium.go +++ b/consensus/consortium/v2/consortium.go @@ -143,25 +143,21 @@ func New( return &consortium } -// IsSystemTransaction implements consensus.PoSA, checking whether a transaction is a system +// IsSystemMessage implements consensus.PoSA, checking whether a transaction is a system // transaction or not. // A system transaction is a transaction that has the recipient of the contract address // is defined in params.ConsortiumV2Contracts -func (c *Consortium) IsSystemTransaction(tx *types.Transaction, header *types.Header) (bool, error) { +func (c *Consortium) IsSystemMessage(msg core.Message, header *types.Header) (bool, error) { // deploy a contract - if tx.To() == nil { + if msg.To() == nil { return false, nil } - sender, err := types.Sender(c.signer, tx) - if err != nil { - return false, errors.New("UnAuthorized transaction") - } if c.chainConfig.IsBuba(header.Number) { - if sender == header.Coinbase && c.IsSystemContract(tx.To()) { + if msg.From() == header.Coinbase && c.IsSystemContract(msg.To()) { return true, nil } } else { - if sender == header.Coinbase && c.IsSystemContract(tx.To()) && tx.GasPrice().Cmp(big.NewInt(0)) == 0 { + if msg.From() == header.Coinbase && c.IsSystemContract(msg.To()) && msg.GasPrice().Cmp(big.NewInt(0)) == 0 { return true, nil } } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index bce99f353a..2fd69be719 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -19,9 +19,12 @@ package eth import ( "errors" "fmt" + "math/big" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/consortium" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -158,6 +161,20 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state return statedb, nil } +func (eth *Ethereum) handleSubmitBlockReward(statedb *state.StateDB, msg core.Message, block *types.Block) { + engine := eth.engine + consortium, ok := engine.(*consortium.Consortium) + if !ok { + return + } + + if consortium.IsSubmitBlockRewardNonZeroAmount(msg, block.Header()) { + balance := statedb.GetBalance(consensus.SystemAddress) + statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) + statedb.SetBalance(block.Coinbase(), balance) + } +} + // stateAtTransaction returns the execution environment of a certain transaction. func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { // Short circuit if it's genesis block. @@ -191,6 +208,7 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.Prepare(tx.Hash(), idx) + eth.handleSubmitBlockReward(statedb, msg, block) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 175873d984..284722ba2a 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "io/ioutil" + "math/big" "os" "runtime" "sync" @@ -31,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/consortium" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -115,6 +117,20 @@ func (context *chainContext) GetHeader(hash common.Hash, number uint64) *types.H return header } +func (api *API) handleSubmitBlockReward(statedb *state.StateDB, msg core.Message, block *types.Block) { + engine := api.backend.Engine() + consortium, ok := engine.(*consortium.Consortium) + if !ok { + return + } + + if consortium.IsSubmitBlockRewardNonZeroAmount(msg, block.Header()) { + balance := statedb.GetBalance(consensus.SystemAddress) + statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) + statedb.SetBalance(block.Coinbase(), balance) + } +} + // chainContext construts the context reader which is used by the evm for reading // the necessary chain context. func (api *API) chainContext(ctx context.Context) core.ChainContext { @@ -292,7 +308,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config, task.block) if err != nil { task.results[i] = &txTraceResult{Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -553,6 +569,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) statedb.Prepare(tx.Hash(), i) + api.handleSubmitBlockReward(statedb, msg, block) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) // We intentionally don't return the error here: if we do, then the RPC server will not @@ -627,7 +644,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, block) if err != nil { results[task.index] = &txTraceResult{Error: err.Error()} continue @@ -646,6 +663,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac msg, _ := tx.AsMessage(signer, block.BaseFee()) statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) + api.handleSubmitBlockReward(statedb, msg, block) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { failed = err break @@ -714,7 +732,7 @@ func (api *API) traceInternalsAndAccounts(ctx context.Context, block *types.Bloc TxIndex: task.index, TxHash: txHash, } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config, block) if err != nil { results.InternalTxs[task.index] = &txTraceResult{ TransactionHash: txHash, @@ -739,6 +757,7 @@ func (api *API) traceInternalsAndAccounts(ctx context.Context, block *types.Bloc msg, _ := tx.AsMessage(signer, block.BaseFee()) statedb.Prepare(tx.Hash(), i) vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{}) + api.handleSubmitBlockReward(statedb, msg, block) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { failed = err break @@ -865,6 +884,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.Prepare(tx.Hash(), i) + api.handleSubmitBlockReward(statedb, msg, block) _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) if writer != nil { writer.Flush() @@ -927,7 +947,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, msg, txctx, vmctx, statedb, config, block) } // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -981,13 +1001,21 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc Reexec: config.Reexec, } } - return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig, block) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx( + ctx context.Context, + message core.Message, + txctx *Context, + vmctx vm.BlockContext, + statedb *state.StateDB, + config *TraceConfig, + block *types.Block, +) (interface{}, error) { // Assemble the structured logger or the JavaScript tracer var ( tracer vm.EVMLogger @@ -1027,6 +1055,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex // Call Prepare to clear out the statedb access list statedb.Prepare(txctx.TxHash, txctx.TxIndex) + api.handleSubmitBlockReward(statedb, message, block) result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) diff --git a/les/state_accessor.go b/les/state_accessor.go index 112e6fd44d..30a3f8129e 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -20,7 +20,10 @@ import ( "context" "errors" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/consensus" + "github.com/ethereum/go-ethereum/consensus/consortium" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -33,6 +36,20 @@ func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, return light.NewState(ctx, block.Header(), leth.odr), nil } +func (leth *LightEthereum) handleSubmitBlockReward(statedb *state.StateDB, msg core.Message, block *types.Block) { + engine := leth.engine + consortium, ok := engine.(*consortium.Consortium) + if !ok { + return + } + + if consortium.IsSubmitBlockRewardNonZeroAmount(msg, block.Header()) { + balance := statedb.GetBalance(consensus.SystemAddress) + statedb.SetBalance(consensus.SystemAddress, big.NewInt(0)) + statedb.SetBalance(block.Coinbase(), balance) + } +} + // stateAtTransaction returns the execution environment of a certain transaction. func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) { // Short circuit if it's genesis block. @@ -64,6 +81,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) + leth.handleSubmitBlockReward(statedb, msg, block) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) }