aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2015-09-02 18:55:11 +0800
committerFelix Lange <fjl@twurst.com>2015-10-17 16:24:34 +0800
commitde8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608 (patch)
tree59930f3d627940e1b73030ee2c5685104a04ee34
parent10ed107ba2001d1aabba3d319ba88c5ce6e8fdc0 (diff)
downloadgo-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar.gz
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar.bz2
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar.lz
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar.xz
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.tar.zst
go-tangerine-de8d5aaa92ff843e6960eb9b7bd7ec1c1ebc6608.zip
core, core/state: move gas tracking out of core/state
The amount of gas available for tx execution was tracked in the StateObject representing the coinbase account. This commit makes the gas counter a separate type in package core, which avoids unintended consequences of intertwining the counter with state logic.
-rw-r--r--core/block_processor.go41
-rw-r--r--core/chain_makers.go11
-rw-r--r--core/error.go13
-rw-r--r--core/state/errors.go39
-rw-r--r--core/state/state_object.go37
-rw-r--r--core/state/state_test.go5
-rw-r--r--core/state_transition.go50
-rw-r--r--miner/worker.go25
-rw-r--r--tests/state_test_util.go8
-rw-r--r--xeth/xeth.go5
10 files changed, 92 insertions, 142 deletions
diff --git a/core/block_processor.go b/core/block_processor.go
index a07d79bcf..7032c077c 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -58,16 +58,31 @@ type BlockProcessor struct {
eventMux *event.TypeMux
}
-// TODO: type GasPool big.Int
-//
-// GasPool is implemented by state.StateObject. This is a historical
-// coincidence. Gas tracking should move out of StateObject.
-
// GasPool tracks the amount of gas available during
// execution of the transactions in a block.
-type GasPool interface {
- AddGas(gas, price *big.Int)
- SubGas(gas, price *big.Int) error
+// The zero value is a pool with zero gas available.
+type GasPool big.Int
+
+// AddGas makes gas available for execution.
+func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
+ i := (*big.Int)(gp)
+ i.Add(i, amount)
+ return gp
+}
+
+// SubGas deducts the given amount from the pool if enough gas is
+// available and returns an error otherwise.
+func (gp *GasPool) SubGas(amount *big.Int) error {
+ i := (*big.Int)(gp)
+ if i.Cmp(amount) < 0 {
+ return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount}
+ }
+ i.Sub(i, amount)
+ return nil
+}
+
+func (gp *GasPool) String() string {
+ return (*big.Int)(gp).String()
}
func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor {
@@ -82,8 +97,10 @@ func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, e
}
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
- gp := statedb.GetOrNewStateObject(block.Coinbase())
- gp.SetGasLimit(block.GasLimit())
+ gp := new(GasPool).AddGas(block.GasLimit())
+ if glog.V(logger.Core) {
+ glog.Infof("%x: gas (+ %v)", block.Coinbase(), gp)
+ }
// Process the transactions on to parent state
receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess)
@@ -94,7 +111,7 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
return receipts, nil
}
-func (self *BlockProcessor) ApplyTransaction(gp GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
+func (self *BlockProcessor) ApplyTransaction(gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp)
if err != nil {
return nil, nil, err
@@ -128,7 +145,7 @@ func (self *BlockProcessor) BlockChain() *BlockChain {
return self.bc
}
-func (self *BlockProcessor) ApplyTransactions(gp GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
+func (self *BlockProcessor) ApplyTransactions(gp *GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
var (
receipts types.Receipts
totalUsedGas = big.NewInt(0)
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 85a4955db..e20a05c7d 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -54,7 +54,7 @@ type BlockGen struct {
header *types.Header
statedb *state.StateDB
- coinbase *state.StateObject
+ gasPool *GasPool
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
@@ -63,15 +63,14 @@ type BlockGen struct {
// SetCoinbase sets the coinbase of the generated block.
// It can be called at most once.
func (b *BlockGen) SetCoinbase(addr common.Address) {
- if b.coinbase != nil {
+ if b.gasPool != nil {
if len(b.txs) > 0 {
panic("coinbase must be set before adding transactions")
}
panic("coinbase can only be set once")
}
b.header.Coinbase = addr
- b.coinbase = b.statedb.GetOrNewStateObject(addr)
- b.coinbase.SetGasLimit(b.header.GasLimit)
+ b.gasPool = new(GasPool).AddGas(b.header.GasLimit)
}
// SetExtra sets the extra data field of the generated block.
@@ -88,10 +87,10 @@ func (b *BlockGen) SetExtra(data []byte) {
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
func (b *BlockGen) AddTx(tx *types.Transaction) {
- if b.coinbase == nil {
+ if b.gasPool == nil {
b.SetCoinbase(common.Address{})
}
- _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.coinbase)
+ _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.gasPool)
if err != nil {
panic(err)
}
diff --git a/core/error.go b/core/error.go
index 5e32124a7..6498194cd 100644
--- a/core/error.go
+++ b/core/error.go
@@ -188,3 +188,16 @@ func IsBadHashError(err error) bool {
_, ok := err.(BadHashError)
return ok
}
+
+type GasLimitErr struct {
+ Have, Want *big.Int
+}
+
+func IsGasLimitErr(err error) bool {
+ _, ok := err.(*GasLimitErr)
+ return ok
+}
+
+func (err *GasLimitErr) Error() string {
+ return fmt.Sprintf("GasLimit reached. Have %d gas, transaction requires %d", err.Have, err.Want)
+}
diff --git a/core/state/errors.go b/core/state/errors.go
deleted file mode 100644
index a08ed2d23..000000000
--- a/core/state/errors.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
-
-package state
-
-import (
- "fmt"
- "math/big"
-)
-
-type GasLimitErr struct {
- Message string
- Is, Max *big.Int
-}
-
-func IsGasLimitErr(err error) bool {
- _, ok := err.(*GasLimitErr)
-
- return ok
-}
-func (err *GasLimitErr) Error() string {
- return err.Message
-}
-func GasLimitError(is, max *big.Int) *GasLimitErr {
- return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
-}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 40af9ed9c..c06e3d227 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -75,11 +75,6 @@ type StateObject struct {
// Cached storage (flushed when updated)
storage Storage
- // Total gas pool is the total amount of gas currently
- // left if this object is the coinbase. Gas is directly
- // purchased of the coinbase.
- gasPool *big.Int
-
// Mark for deletion
// When an object is marked for deletion it will be delete from the trie
// during the "update" phase of the state transition
@@ -89,10 +84,9 @@ type StateObject struct {
}
func NewStateObject(address common.Address, db ethdb.Database) *StateObject {
- object := &StateObject{db: db, address: address, balance: new(big.Int), gasPool: new(big.Int), dirty: true}
+ object := &StateObject{db: db, address: address, balance: new(big.Int), dirty: true}
object.trie, _ = trie.NewSecure(common.Hash{}, db)
object.storage = make(Storage)
- object.gasPool = new(big.Int)
return object
}
@@ -121,7 +115,6 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
object.codeHash = extobject.CodeHash
object.trie = trie
object.storage = make(map[string]common.Hash)
- object.gasPool = new(big.Int)
object.code, _ = db.Get(extobject.CodeHash)
return object
}
@@ -209,36 +202,9 @@ func (c *StateObject) St() Storage {
return c.storage
}
-//
-// Gas setters and getters
-//
-
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
-func (self *StateObject) SetGasLimit(gasLimit *big.Int) {
- self.gasPool = new(big.Int).Set(gasLimit)
- self.dirty = true
-
- if glog.V(logger.Core) {
- glog.Infof("%x: gas (+ %v)", self.Address(), self.gasPool)
- }
-}
-
-func (self *StateObject) SubGas(gas, price *big.Int) error {
- if self.gasPool.Cmp(gas) < 0 {
- return GasLimitError(self.gasPool, gas)
- }
- self.gasPool.Sub(self.gasPool, gas)
- self.dirty = true
- return nil
-}
-
-func (self *StateObject) AddGas(gas, price *big.Int) {
- self.gasPool.Add(self.gasPool, gas)
- self.dirty = true
-}
-
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.db)
stateObject.balance.Set(self.balance)
@@ -248,7 +214,6 @@ func (self *StateObject) Copy() *StateObject {
stateObject.code = common.CopyBytes(self.code)
stateObject.initCode = common.CopyBytes(self.initCode)
stateObject.storage = self.storage.Copy()
- stateObject.gasPool.Set(self.gasPool)
stateObject.remove = self.remove
stateObject.dirty = self.dirty
stateObject.deleted = self.deleted
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 08fbc47fa..7ddbe11a1 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -138,7 +138,6 @@ func TestSnapshot2(t *testing.T) {
so0 := state.GetStateObject(stateobjaddr0)
so0.balance = big.NewInt(42)
so0.nonce = 43
- so0.gasPool = big.NewInt(44)
so0.code = []byte{'c', 'a', 'f', 'e'}
so0.codeHash = so0.CodeHash()
so0.remove = true
@@ -150,7 +149,6 @@ func TestSnapshot2(t *testing.T) {
so1 := state.GetStateObject(stateobjaddr1)
so1.balance = big.NewInt(52)
so1.nonce = 53
- so1.gasPool = big.NewInt(54)
so1.code = []byte{'c', 'a', 'f', 'e', '2'}
so1.codeHash = so1.CodeHash()
so1.remove = true
@@ -207,9 +205,6 @@ func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
}
}
- if so0.gasPool.Cmp(so1.gasPool) != 0 {
- t.Fatalf("GasPool mismatch: have %v, want %v", so0.gasPool, so1.gasPool)
- }
if so0.remove != so1.remove {
t.Fatalf("Remove mismatch: have %v, want %v", so0.remove, so1.remove)
}
diff --git a/core/state_transition.go b/core/state_transition.go
index e83019229..7ecc01d4c 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -21,7 +21,6 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
@@ -29,23 +28,24 @@ import (
)
/*
- * The State transitioning model
- *
- * A state transition is a change made when a transaction is applied to the current world state
- * The state transitioning model does all all the necessary work to work out a valid new state root.
- * 1) Nonce handling
- * 2) Pre pay / buy gas of the coinbase (miner)
- * 3) Create a new state object if the recipient is \0*32
- * 4) Value transfer
- * == If contract creation ==
- * 4a) Attempt to run transaction data
- * 4b) If valid, use result as code for the new state object
- * == end ==
- * 5) Run Script section
- * 6) Derive new state root
- */
+The State Transitioning Model
+
+A state transition is a change made when a transaction is applied to the current world state
+The state transitioning model does all all the necessary work to work out a valid new state root.
+
+1) Nonce handling
+2) Pre pay gas
+3) Create a new state object if the recipient is \0*32
+4) Value transfer
+== If contract creation ==
+ 4a) Attempt to run transaction data
+ 4b) If valid, use result as code for the new state object
+== end ==
+5) Run Script section
+6) Derive new state root
+*/
type StateTransition struct {
- gp GasPool
+ gp *GasPool
msg Message
gas, gasPrice *big.Int
initialGas *big.Int
@@ -94,7 +94,7 @@ func IntrinsicGas(data []byte) *big.Int {
return igas
}
-func ApplyMessage(env vm.Environment, msg Message, gp GasPool) ([]byte, *big.Int, error) {
+func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
var st = StateTransition{
gp: gp,
env: env,
@@ -158,7 +158,7 @@ func (self *StateTransition) buyGas() error {
if sender.Balance().Cmp(mgval) < 0 {
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
}
- if err = self.gp.SubGas(mgas, self.gasPrice); err != nil {
+ if err = self.gp.SubGas(mgas); err != nil {
return err
}
self.addGas(mgas)
@@ -180,9 +180,9 @@ func (self *StateTransition) preCheck() (err error) {
return NonceError(msg.Nonce(), n)
}
- // Pre-pay gas / Buy gas of the coinbase account
+ // Pre-pay gas
if err = self.buyGas(); err != nil {
- if state.IsGasLimitErr(err) {
+ if IsGasLimitErr(err) {
return err
}
return InvalidTxError(err)
@@ -246,17 +246,21 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
}
func (self *StateTransition) refundGas() {
+ // Return eth for remaining gas to the sender account,
+ // exchanged at the original rate.
sender, _ := self.from() // err already checked
- // Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining)
+ // Apply refund counter, capped to half of the used gas.
uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.GetRefund())
self.gas.Add(self.gas, refund)
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
- self.gp.AddGas(self.gas, self.gasPrice)
+ // Also return remaining gas to the block gas counter so it is
+ // available for the next transaction.
+ self.gp.AddGas(self.gas)
}
func (self *StateTransition) gasUsed() *big.Int {
diff --git a/miner/worker.go b/miner/worker.go
index 5bce32f21..d827cb97d 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -62,13 +62,12 @@ type uint64RingBuffer struct {
// environment is the workers current environment and holds
// all of the current state information
type Work struct {
- state *state.StateDB // apply state changes here
- coinbase *state.StateObject // the miner's account
- ancestors *set.Set // ancestor set (used for checking uncle parent validity)
- family *set.Set // family set (used for checking uncle invalidity)
- uncles *set.Set // uncle set
- remove *set.Set // tx which will be removed
- tcount int // tx count in cycle
+ state *state.StateDB // apply state changes here
+ ancestors *set.Set // ancestor set (used for checking uncle parent validity)
+ family *set.Set // family set (used for checking uncle invalidity)
+ uncles *set.Set // uncle set
+ remove *set.Set // tx which will be removed
+ tcount int // tx count in cycle
ignoredTransactors *set.Set
lowGasTransactors *set.Set
ownedAccounts *set.Set
@@ -366,7 +365,6 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
family: set.New(),
uncles: set.New(),
header: header,
- coinbase: state.GetOrNewStateObject(self.coinbase),
createdAt: time.Now(),
}
@@ -514,7 +512,6 @@ func (self *worker) commitNewWork() {
transactions := append(singleTxOwner, multiTxOwner...)
*/
- work.coinbase.SetGasLimit(header.GasLimit)
work.commitTransactions(transactions, self.gasPrice, self.proc)
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
@@ -575,6 +572,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
}
func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) {
+ gp := new(core.GasPool).AddGas(env.header.GasLimit)
+
for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from, _ := tx.From()
@@ -612,9 +611,9 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
- err := env.commitTransaction(tx, proc)
+ err := env.commitTransaction(tx, proc, gp)
switch {
- case state.IsGasLimitErr(err):
+ case core.IsGasLimitErr(err):
// ignore the transactor so no nonce errors will be thrown for this account
// next time the worker is run, they'll be picked up again.
env.ignoredTransactors.Add(from)
@@ -632,9 +631,9 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
}
}
-func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error {
+func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error {
snap := env.state.Copy()
- receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true)
+ receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true)
if err != nil {
env.state.Set(snap)
return err
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 676d9ed8c..352fe3570 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -223,7 +223,6 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Log
price = common.Big(tx["gasPrice"])
value = common.Big(tx["value"])
nonce = common.Big(tx["nonce"]).Uint64()
- caddr = common.HexToAddress(env["currentCoinbase"])
)
var to *common.Address
@@ -235,16 +234,15 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Log
vm.Precompiled = vm.PrecompiledContracts()
snapshot := statedb.Copy()
- coinbase := statedb.GetOrNewStateObject(caddr)
- coinbase.SetGasLimit(common.Big(env["currentGasLimit"]))
+ gaspool := new(core.GasPool).AddGas(common.Big(env["currentGasLimit"]))
key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce)
vmenv := NewEnvFromMap(statedb, env, tx)
vmenv.origin = addr
- ret, _, err := core.ApplyMessage(vmenv, message, coinbase)
- if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || state.IsGasLimitErr(err) {
+ ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
+ if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {
statedb.Set(snapshot)
}
statedb.Commit()
diff --git a/xeth/xeth.go b/xeth/xeth.go
index baa8314ad..1cb072f0d 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -850,7 +850,6 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
}
from.SetBalance(common.MaxBig)
- from.SetGasLimit(common.MaxBig)
msg := callmsg{
from: from,
@@ -874,8 +873,8 @@ func (self *XEth) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr st
header := self.CurrentBlock().Header()
vmenv := core.NewEnv(statedb, self.backend.BlockChain(), msg, header)
-
- res, gas, err := core.ApplyMessage(vmenv, msg, from)
+ gp := new(core.GasPool).AddGas(common.MaxBig)
+ res, gas, err := core.ApplyMessage(vmenv, msg, gp)
return common.ToHex(res), gas.String(), err
}