diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 43f563159a..159fca136e 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -248,7 +248,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Execute the call. msg := callmsg{call} - evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain) + evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 3c36545c45..8619bd1d8e 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -220,6 +220,12 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique { } } +// Author implements consensus.Engine, returning the Ethereum address recovered +// from the signature in the header's extra-data section. +func (c *Clique) Author(header *types.Header) (common.Address, error) { + return ecrecover(header) +} + // VerifyHeader checks whether a header conforms to the consensus rules. func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { return c.verifyHeader(chain, header, nil) diff --git a/consensus/consensus.go b/consensus/consensus.go index c199e627fd..8cbd32c883 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -49,6 +49,11 @@ type ChainReader interface { // Engine is an algorithm agnostic consensus engine. type Engine interface { + // Author retrieves the Ethereum address of the account that minted the given + // block, which may be different from the header's coinbase if a consensus + // engine is based on signatures. + Author(header *types.Header) (common.Address, error) + // VerifyHeader checks whether a header conforms to the consensus rules of a // given engine. Verifying the seal may be done optionally here, or explicitly // via the VerifySeal method. diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index f4f8f4b170..4b6e779d54 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -59,6 +59,12 @@ var ( errInvalidPoW = errors.New("invalid proof-of-work") ) +// Author implements consensus.Engine, returning the header's coinbase as the +// proof-of-work verified author of the block. +func (ethash *Ethash) Author(header *types.Header) (common.Address, error) { + return header.Coinbase, nil +} + // VerifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum ethash engine. func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { diff --git a/core/blockchain.go b/core/blockchain.go index d2232e50fd..d6f2653ae8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1398,3 +1398,6 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { // Config retrieves the blockchain's chain configuration. func (self *BlockChain) Config() *params.ChainConfig { return self.config } + +// Engine retrieves the blockchain's consensus engine. +func (self *BlockChain) Engine() consensus.Engine { return self.engine } diff --git a/core/chain_makers.go b/core/chain_makers.go index c47c719f64..f34279ba01 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -85,7 +85,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, err := ApplyTransaction(b.config, nil, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } diff --git a/core/evm.go b/core/evm.go index 6a5713075b..49a786a864 100644 --- a/core/evm.go +++ b/core/evm.go @@ -20,25 +20,36 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) -// BlockFetcher retrieves headers by their hash -type HeaderFetcher interface { - // GetHeader returns the hash corresponding to their hash +// ChainContext supports retrieving headers and consensus parameters from the +// current blockchain to be used during transaction processing. +type ChainContext interface { + // Engine retrieves the chain's consensus engine. + Engine() consensus.Engine + + // GetHeader returns the hash corresponding to their hash. GetHeader(common.Hash, uint64) *types.Header } // NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context { +func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { + // If we don't have an explicit author (i.e. not mining), extract from the header + var beneficiary common.Address + if author == nil { + beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation + } else { + beneficiary = *author + } return vm.Context{ CanTransfer: CanTransfer, Transfer: Transfer, GetHash: GetHashFn(header, chain), - Origin: msg.From(), - Coinbase: header.Coinbase, + Coinbase: beneficiary, BlockNumber: new(big.Int).Set(header.Number), Time: new(big.Int).Set(header.Time), Difficulty: new(big.Int).Set(header.Difficulty), @@ -48,7 +59,7 @@ func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Co } // GetHashFn returns a GetHashFunc which retrieves header hashes by number -func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash { +func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { return func(n uint64) common.Hash { for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { if header.Number.Uint64() == n { diff --git a/core/headerchain.go b/core/headerchain.go index f58afc6ca2..9bb7f17937 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -442,6 +442,9 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { // Config retrieves the header chain's chain configuration. func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config } +// Engine retrieves the header chain's consensus engine. +func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine } + // GetBlock implements consensus.ChainReader, and returns nil for every input as // a header chain does not have blocks available for retrieval. func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block { diff --git a/core/state_processor.go b/core/state_processor.go index aca2929eb2..4fc2f1eaed 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -19,6 +19,7 @@ package core import ( "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" @@ -69,7 +70,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Iterate over and process the individual transactions for i, tx := range block.Transactions() { statedb.StartRecord(tx.Hash(), block.Hash(), i) - receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) + receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { return nil, nil, nil, err } @@ -86,13 +87,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) { +func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { return nil, nil, err } // Create a new context to be used in the EVM environment - context := NewEVMContext(msg, header, bc) + context := NewEVMContext(msg, header, bc, author) // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. vmenv := vm.NewEVM(context, statedb, config, cfg) diff --git a/eth/api.go b/eth/api.go index 0decd57ca7..b386c08b45 100644 --- a/eth/api.go +++ b/eth/api.go @@ -548,7 +548,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. if err != nil { return nil, fmt.Errorf("sender retrieval failed: %v", err) } - context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain()) + context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain(), nil) // Mutate the state if we haven't reached the tracing transaction yet if uint64(idx) < txIndex { diff --git a/eth/api_backend.go b/eth/api_backend.go index 61e1844017..fe108d2720 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -114,7 +114,7 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha from.SetBalance(math.MaxBig256) vmError := func() error { return nil } - context := core.NewEVMContext(msg, header, b.eth.BlockChain()) + context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil } diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index aec8eb8bf6..c16163ace7 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -30,6 +30,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" @@ -54,6 +55,7 @@ type Service struct { server *p2p.Server // Peer-to-peer server to retrieve networking infos eth *eth.Ethereum // Full Ethereum service if monitoring a full node les *les.LightEthereum // Light Ethereum service if monitoring a light node + engine consensus.Engine // Consensus engine to retrieve variadic block fields node string // Name of the node to display on the monitoring page pass string // Password to authorize access to the monitoring page @@ -72,9 +74,16 @@ func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Servic return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) } // Assemble and return the stats service + var engine consensus.Engine + if ethServ != nil { + engine = ethServ.Engine() + } else { + engine = lesServ.Engine() + } return &Service{ eth: ethServ, les: lesServ, + engine: engine, node: parts[1], pass: parts[3], host: parts[4], @@ -493,12 +502,14 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) } // Assemble and return the block stats + author, _ := s.engine.Author(header) + return &blockStats{ Number: header.Number, Hash: header.Hash(), ParentHash: header.ParentHash, Timestamp: header.Time, - Miner: header.Coinbase, + Miner: author, GasUsed: new(big.Int).Set(header.GasUsed), GasLimit: new(big.Int).Set(header.GasLimit), Diff: header.Difficulty.String(), diff --git a/les/api_backend.go b/les/api_backend.go index 67de3bcd5c..7d69046deb 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -100,7 +100,7 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state etha from.SetBalance(math.MaxBig256) vmstate := light.NewVMState(ctx, stateDb) - context := core.NewEVMContext(msg, header, b.eth.blockchain) + context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil } diff --git a/les/backend.go b/les/backend.go index 3aad16fa0e..5670b77d25 100644 --- a/les/backend.go +++ b/les/backend.go @@ -176,6 +176,7 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } +func (s *LightEthereum) Engine() consensus.Engine { return s.engine } func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } diff --git a/les/odr_test.go b/les/odr_test.go index 6b074f1a22..532de4d80b 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -123,7 +123,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, bc) + context := core.NewEVMContext(msg, header, bc, nil) vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) @@ -141,7 +141,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(100000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc) + context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) //vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) diff --git a/light/lightchain.go b/light/lightchain.go index 4073e39e59..e8fd0ba5e2 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -213,6 +213,9 @@ func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { // Accessors +// Engine retrieves the light chain's consensus engine. +func (bc *LightChain) Engine() consensus.Engine { return bc.engine } + // Genesis returns the genesis block func (bc *LightChain) Genesis() *types.Block { return bc.genesisBlock diff --git a/light/odr_test.go b/light/odr_test.go index ca33db2463..576e3abc9a 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -175,7 +175,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, bc) + context := core.NewEVMContext(msg, header, bc, nil) vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) @@ -191,7 +191,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain from.SetBalance(math.MaxBig256) msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), big.NewInt(1000000), new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc) + context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, vmstate, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxBig256) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) diff --git a/miner/worker.go b/miner/worker.go index 8a67b12a6a..01241b3f30 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -252,7 +252,7 @@ func (self *worker) update() { txs := map[common.Address]types.Transactions{acc: {ev.Tx}} txset := types.NewTransactionsByPriceAndNonce(txs) - self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain) + self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain, self.coinbase) self.currentMu.Unlock() } } @@ -460,7 +460,7 @@ func (self *worker) commitNewWork() { return } txs := types.NewTransactionsByPriceAndNonce(pending) - work.commitTransactions(self.mux, txs, self.gasPrice, self.chain) + work.commitTransactions(self.mux, txs, self.gasPrice, self.chain, self.coinbase) self.eth.TxPool().RemoveBatch(work.lowGasTxs) self.eth.TxPool().RemoveBatch(work.failedTxs) @@ -515,7 +515,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error { return nil } -func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) { +func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain, coinbase common.Address) { gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs []*types.Log @@ -553,7 +553,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB // Start executing the transaction env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount) - err, logs := env.commitTransaction(tx, bc, gp) + err, logs := env.commitTransaction(tx, bc, coinbase, gp) switch err { case core.ErrGasLimitReached: // Pop the current out-of-gas transaction without shifting in the next from the account @@ -594,10 +594,10 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB } } -func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, []*types.Log) { +func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { snap := env.state.Snapshot() - receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) + receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) if err != nil { env.state.RevertToSnapshot(snap) return err, nil