From cb1fa523e4930841f4cc92689c850c7831810d03 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:00:51 +0200 Subject: [PATCH 01/15] cmd/geth, cmd/mist, eth, flags: renamed loglevel to verbosity --- cmd/geth/main.go | 3 ++- cmd/mist/main.go | 2 +- cmd/utils/flags.go | 15 +++++++++++---- eth/backend.go | 18 +++++++++++------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index fd6925e6d..a2cee3bd9 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -244,6 +244,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.EtherbaseFlag, + utils.GasPriceFlag, utils.MinerThreadsFlag, utils.MiningEnabledFlag, utils.NATFlag, @@ -258,7 +259,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.ProtocolVersionFlag, utils.NetworkIdFlag, utils.RPCCORSDomainFlag, - utils.LogLevelFlag, + utils.VerbosityFlag, utils.BacktraceAtFlag, utils.LogToStdErrFlag, utils.LogVModuleFlag, diff --git a/cmd/mist/main.go b/cmd/mist/main.go index 9d92cc175..098afc7e2 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -73,7 +73,7 @@ func init() { utils.DataDirFlag, utils.ListenPortFlag, utils.LogFileFlag, - utils.LogLevelFlag, + utils.VerbosityFlag, utils.MaxPeersFlag, utils.MaxPendingPeersFlag, utils.MinerThreadsFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b18d9851f..dd3b6c8a2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "fmt" "log" + "math/big" "net/http" "os" "path" @@ -116,6 +117,11 @@ var ( Usage: "Public address for block mining rewards. By default the address of your primary account is used", Value: "primary", } + GasPriceFlag = cli.StringFlag{ + Name: "gasprice", + Usage: "Sets the minimal gasprice when mining transactions", + Value: new(big.Int).Mul(big.NewInt(10), common.Szabo).String(), + } UnlockedAccountFlag = cli.StringFlag{ Name: "unlock", @@ -133,8 +139,8 @@ var ( Name: "logfile", Usage: "Send log output to a file", } - LogLevelFlag = cli.IntFlag{ - Name: "loglevel", + VerbosityFlag = cli.IntFlag{ + Name: "verbosity", Usage: "Logging verbosity: 0-6 (0=silent, 1=error, 2=warn, 3=info, 4=core, 5=debug, 6=debug detail)", Value: int(logger.InfoLevel), } @@ -270,7 +276,7 @@ func GetNodeKey(ctx *cli.Context) (key *ecdsa.PrivateKey) { func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { // Set verbosity on glog - glog.SetV(ctx.GlobalInt(LogLevelFlag.Name)) + glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) // Set the log type //glog.SetToStderr(ctx.GlobalBool(LogToStdErrFlag.Name)) glog.SetToStderr(true) @@ -290,7 +296,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { SkipBcVersionCheck: false, NetworkId: ctx.GlobalInt(NetworkIdFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name), - LogLevel: ctx.GlobalInt(LogLevelFlag.Name), + Verbosity: ctx.GlobalInt(VerbosityFlag.Name), LogJSON: ctx.GlobalString(LogJSONFlag.Name), Etherbase: ctx.GlobalString(EtherbaseFlag.Name), MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), @@ -305,6 +311,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { Shh: ctx.GlobalBool(WhisperEnabledFlag.Name), Dial: true, BootNodes: ctx.GlobalString(BootnodesFlag.Name), + GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)), } } diff --git a/eth/backend.go b/eth/backend.go index 0f23cde2f..8f0789467 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/big" "os" "path" "path/filepath" @@ -53,12 +54,12 @@ type Config struct { BlockChainVersion int SkipBcVersionCheck bool // e.g. blockchain export - DataDir string - LogFile string - LogLevel int - LogJSON string - VmDebug bool - NatSpec bool + DataDir string + LogFile string + Verbosity int + LogJSON string + VmDebug bool + NatSpec bool MaxPeers int MaxPendingPeers int @@ -76,6 +77,7 @@ type Config struct { Dial bool Etherbase string + GasPrice *big.Int MinerThreads int AccountManager *accounts.Manager @@ -200,7 +202,7 @@ type Ethereum struct { func New(config *Config) (*Ethereum, error) { // Bootstrap database - logger.New(config.DataDir, config.LogFile, config.LogLevel) + logger.New(config.DataDir, config.LogFile, config.Verbosity) if len(config.LogJSON) > 0 { logger.NewJSONsystem(config.DataDir, config.LogJSON) } @@ -266,6 +268,8 @@ func New(config *Config) (*Ethereum, error) { eth.blockProcessor = core.NewBlockProcessor(stateDb, extraDb, eth.pow, eth.txPool, eth.chainManager, eth.EventMux()) eth.chainManager.SetProcessor(eth.blockProcessor) eth.miner = miner.New(eth, eth.pow, config.MinerThreads) + eth.miner.SetGasPrice(config.GasPrice) + eth.protocolManager = NewProtocolManager(config.ProtocolVersion, config.NetworkId, eth.eventMux, eth.txPool, eth.chainManager, eth.downloader) if config.Shh { eth.whisper = whisper.New() From 13ddf20bd20707141309fbfa969183a7afbe5a80 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:04:00 +0200 Subject: [PATCH 02/15] miner, cmd/geth: settable gas price from flags and console * --gasprice "" flag * admin.miner.setGasPrice( ) --- cmd/geth/admin.go | 12 ++++++++++++ miner/miner.go | 4 ++++ miner/worker.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/cmd/geth/admin.go b/cmd/geth/admin.go index 49e2dc6f8..2b9956638 100644 --- a/cmd/geth/admin.go +++ b/cmd/geth/admin.go @@ -70,6 +70,7 @@ func (js *jsre) adminBindings() { miner.Set("stop", js.stopMining) miner.Set("hashrate", js.hashrate) miner.Set("setExtra", js.setExtra) + miner.Set("setGasPrice", js.setGasPrice) admin.Set("debug", struct{}{}) t, _ = admin.Get("debug") @@ -236,6 +237,17 @@ func (js *jsre) setExtra(call otto.FunctionCall) otto.Value { return otto.UndefinedValue() } +func (js *jsre) setGasPrice(call otto.FunctionCall) otto.Value { + gasPrice, err := call.Argument(0).ToString() + if err != nil { + fmt.Println(err) + return otto.UndefinedValue() + } + + js.ethereum.Miner().SetGasPrice(common.String2Big(gasPrice)) + return otto.UndefinedValue() +} + func (js *jsre) hashrate(otto.FunctionCall) otto.Value { return js.re.ToVal(js.ethereum.Miner().HashRate()) } diff --git a/miner/miner.go b/miner/miner.go index bff0026dc..52b9f2a69 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -37,6 +37,10 @@ func (self *Miner) Mining() bool { return self.mining } +func (m *Miner) SetGasPrice(price *big.Int) { + m.worker.gasPrice = price +} + func (self *Miner) Start(coinbase common.Address) { self.mining = true self.worker.coinbase = coinbase diff --git a/miner/worker.go b/miner/worker.go index 87d17dfd6..bb8b70c86 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -72,6 +72,7 @@ type worker struct { proc *core.BlockProcessor coinbase common.Address + gasPrice *big.Int extra []byte currentMu sync.Mutex @@ -93,6 +94,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker { eth: eth, mux: eth.EventMux(), recv: make(chan *types.Block), + gasPrice: new(big.Int), chain: eth.ChainManager(), proc: eth.BlockProcessor(), possibleUncles: make(map[common.Hash]*types.Block), @@ -239,6 +241,19 @@ func (self *worker) makeCurrent() { self.current.coinbase.SetGasPool(core.CalcGasLimit(parent)) } +func (w *worker) setGasPrice(p *big.Int) { + w.mu.Lock() + defer w.mu.Unlock() + w.gasPrice = p +} + +func (w *worker) gasprice(pct int64) *big.Int { + p := new(big.Int).Set(w.gasPrice) + p.Div(p, big.NewInt(100)) + p.Mul(p, big.NewInt(pct)) + return p +} + func (self *worker) commitNewWork() { self.mu.Lock() defer self.mu.Unlock() @@ -259,9 +274,22 @@ func (self *worker) commitNewWork() { ignoredTransactors = set.New() ) + var pct int64 = 90 + minprice := self.gasprice(pct) for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() + + // check if it falls within margin + if tx.GasPrice().Cmp(minprice) < 0 { + // ignore the transaction and transactor. We ignore the transactor + // because nonce will fail after ignoring this transaction so there's + // no point + ignoredTransactors.Add(from) + glog.V(logger.Info).Infof("transaction(%x) below gas price (<%d%% ask price). All sequential txs from this address(%x) will fail\n", tx.Hash().Bytes()[:4], pct, from[:4]) + continue + } + // Move on to the next transaction when the transactor is in ignored transactions set // This may occur when a transaction hits the gas limit. When a gas limit is hit and // the transaction is processed (that could potentially be included in the block) it From a7705fc2037836f037de5e5ea5aef8475424a1be Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:13:46 +0200 Subject: [PATCH 03/15] miner: moved gasprice to non-method --- miner/worker.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index bb8b70c86..7adf298ed 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -247,13 +247,6 @@ func (w *worker) setGasPrice(p *big.Int) { w.gasPrice = p } -func (w *worker) gasprice(pct int64) *big.Int { - p := new(big.Int).Set(w.gasPrice) - p.Div(p, big.NewInt(100)) - p.Mul(p, big.NewInt(pct)) - return p -} - func (self *worker) commitNewWork() { self.mu.Lock() defer self.mu.Unlock() @@ -274,8 +267,9 @@ func (self *worker) commitNewWork() { ignoredTransactors = set.New() ) - var pct int64 = 90 - minprice := self.gasprice(pct) + const pct = int64(90) + // calculate the minimal gas price the miner accepts when sorting out transactions. + minprice := gasprice(self.gasPrice, pct) for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() @@ -411,3 +405,12 @@ func (self *worker) HashRate() int64 { return tot } + +// gasprice calculates a reduced gas price based on the pct +// XXX Use big.Rat? +func gasprice(price *big.Int, pct int64) *big.Int { + p := new(big.Int).Set(price) + p.Div(p, big.NewInt(100)) + p.Mul(p, big.NewInt(pct)) + return p +} From e3a08875f6435b1674e62bd1d923941ec2092b69 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:32:36 +0200 Subject: [PATCH 04/15] miner: start/stop mutex locked. Closes #887 --- miner/worker.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/miner/worker.go b/miner/worker.go index 7adf298ed..22493c235 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -125,6 +125,9 @@ func (self *worker) pendingBlock() *types.Block { } func (self *worker) start() { + self.mu.Lock() + defer self.mu.Unlock() + // spin up agents for _, agent := range self.agents { agent.Start() @@ -134,6 +137,9 @@ func (self *worker) start() { } func (self *worker) stop() { + self.mu.Lock() + defer self.mu.Unlock() + if atomic.LoadInt32(&self.mining) == 1 { // stop all agents for _, agent := range self.agents { @@ -146,6 +152,9 @@ func (self *worker) stop() { } func (self *worker) register(agent Agent) { + self.mu.Lock() + defer self.mu.Unlock() + self.agents = append(self.agents, agent) agent.SetReturnCh(self.recv) } From 7eed47fad5ac971895a3853c83a22dff652dac02 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:51:40 +0200 Subject: [PATCH 05/15] miner, tests: fixed block test --- miner/miner.go | 5 +++++ tests/block_test.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/miner/miner.go b/miner/miner.go index 52b9f2a69..d5ea9a146 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -38,6 +38,11 @@ func (self *Miner) Mining() bool { } func (m *Miner) SetGasPrice(price *big.Int) { + // FIXME block tests set a nil gas price. Quick dirty fix + if price == nil { + return + } + m.worker.gasPrice = price } diff --git a/tests/block_test.go b/tests/block_test.go index 79e3335b1..e72f2b548 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -103,7 +103,7 @@ func testEthConfig() *eth.Config { return ð.Config{ DataDir: common.DefaultDataDir(), - LogLevel: 5, + Verbosity: 5, Etherbase: "primary", AccountManager: accounts.NewManager(ks), NewDB: func(path string) (common.Database, error) { return ethdb.NewMemDatabase() }, From 658ac3c257e0d97c44cf7924c84c050032256d61 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 12:54:27 +0200 Subject: [PATCH 06/15] cmd/geth: bump version 0.9.18 --- cmd/geth/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a2cee3bd9..723353b0b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -51,7 +51,7 @@ import _ "net/http/pprof" const ( ClientIdentifier = "Geth" - Version = "0.9.17" + Version = "0.9.18" ) var ( From 45dc690947ebfe9fb3dc64ba3c74fb433b0f474d Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 9 May 2015 13:37:22 +0200 Subject: [PATCH 07/15] cmd/mist: version bump --- cmd/mist/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/mist/main.go b/cmd/mist/main.go index 098afc7e2..b7737aaab 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -37,7 +37,7 @@ import ( const ( ClientIdentifier = "Mist" - Version = "0.9.0" + Version = "0.9.18" ) var ( From 05715f27cfd68387f720db115bb8461f8725a341 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 10 May 2015 00:34:07 +0200 Subject: [PATCH 08/15] eth: added a cancel method for the downloader Added a cancel method to the downloader which gracefully shuts down any active syncing process (hash fetching or block downloading) and resets the queue and remove any pending blocks. Issue with the downloader which would stall because of an active ongoing process when an invalid block was found. --- eth/downloader/downloader.go | 62 ++++++++++++++++++++++++++++++- eth/downloader/downloader_test.go | 43 +++++++++++++++++++++ eth/sync.go | 3 ++ 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 18f8d2ba8..14ca2cd3d 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -34,6 +34,9 @@ var ( errPeersUnavailable = errors.New("no peers available or all peers tried for block download process") errAlreadyInPool = errors.New("hash already in pool") errBlockNumberOverflow = errors.New("received block which overflows") + errCancelHashFetch = errors.New("hash fetching cancelled (requested)") + errCancelBlockFetch = errors.New("block downloading cancelled (requested)") + errNoSyncActive = errors.New("no sync active") ) type hashCheckFn func(common.Hash) bool @@ -74,6 +77,7 @@ type Downloader struct { newPeerCh chan *peer hashCh chan hashPack blockCh chan blockPack + cancelCh chan struct{} } func New(hasBlock hashCheckFn, getBlock getBlockFn) *Downloader { @@ -129,6 +133,9 @@ func (d *Downloader) Synchronise(id string, hash common.Hash) error { } defer atomic.StoreInt32(&d.synchronising, 0) + // Create cancel channel for aborting midflight + d.cancelCh = make(chan struct{}) + // Abort if the queue still contains some leftover data if _, cached := d.queue.Size(); cached > 0 && d.queue.GetHeadBlock() != nil { return errPendingQueue @@ -161,7 +168,6 @@ func (d *Downloader) Has(hash common.Hash) bool { } func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool) (err error) { - d.activePeer = p.id defer func() { // reset on error @@ -191,6 +197,42 @@ func (d *Downloader) getFromPeer(p *peer, hash common.Hash, ignoreInitial bool) return nil } +// Cancel cancels all of the operations and resets the queue. It returns true +// if the cancel operation was completed. +func (d *Downloader) Cancel() bool { + hs, bs := d.queue.Size() + // If we're not syncing just return. + if atomic.LoadInt32(&d.synchronising) == 0 && hs == 0 && bs == 0 { + return false + } + + close(d.cancelCh) + + // clean up +hashDone: + for { + select { + case <-d.hashCh: + default: + break hashDone + } + } + +blockDone: + for { + select { + case <-d.blockCh: + default: + break blockDone + } + } + + // reset the queue + d.queue.Reset() + + return true +} + // XXX Make synchronous func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial bool) error { glog.V(logger.Debug).Infof("Downloading hashes (%x) from %s", h[:4], p.id) @@ -217,6 +259,8 @@ func (d *Downloader) startFetchingHashes(p *peer, h common.Hash, ignoreInitial b out: for { select { + case <-d.cancelCh: + return errCancelHashFetch case hashPack := <-d.hashCh: // Make sure the active peer is giving us the hashes if hashPack.peerId != activePeer.id { @@ -305,6 +349,8 @@ func (d *Downloader) startFetchingBlocks(p *peer) error { out: for { select { + case <-d.cancelCh: + return errCancelBlockFetch case blockPack := <-d.blockCh: // If the peer was previously banned and failed to deliver it's pack // in a reasonable time frame, ignore it's message. @@ -394,11 +440,23 @@ out: // Deliver a chunk to the downloader. This is usually done through the BlocksMsg by // the protocol handler. -func (d *Downloader) DeliverChunk(id string, blocks []*types.Block) { +func (d *Downloader) DeliverChunk(id string, blocks []*types.Block) error { + // Make sure the downloader is active + if atomic.LoadInt32(&d.synchronising) == 0 { + return errNoSyncActive + } + d.blockCh <- blockPack{id, blocks} + + return nil } func (d *Downloader) AddHashes(id string, hashes []common.Hash) error { + // Make sure the downloader is active + if atomic.LoadInt32(&d.synchronising) == 0 { + return errNoSyncActive + } + // make sure that the hashes that are being added are actually from the peer // that's the current active peer. hashes that have been received from other // peers are dropped and ignored. diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 8ccc4d1a5..d0f8d4c8f 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -182,6 +182,49 @@ func TestTaking(t *testing.T) { } } +func TestInactiveDownloader(t *testing.T) { + targetBlocks := 1000 + hashes := createHashes(0, targetBlocks) + blocks := createBlocksFromHashSet(createHashSet(hashes)) + tester := newTester(t, hashes, nil) + + err := tester.downloader.AddHashes("bad peer 001", hashes) + if err != errNoSyncActive { + t.Error("expected no sync error, got", err) + } + + err = tester.downloader.DeliverChunk("bad peer 001", blocks) + if err != errNoSyncActive { + t.Error("expected no sync error, got", err) + } +} + +func TestCancel(t *testing.T) { + minDesiredPeerCount = 4 + blockTtl = 1 * time.Second + + targetBlocks := 1000 + hashes := createHashes(0, targetBlocks) + blocks := createBlocksFromHashes(hashes) + tester := newTester(t, hashes, blocks) + + tester.newPeer("peer1", big.NewInt(10000), hashes[0]) + + err := tester.sync("peer1", hashes[0]) + if err != nil { + t.Error("download error", err) + } + + if !tester.downloader.Cancel() { + t.Error("cancel operation unsuccessfull") + } + + hashSize, blockSize := tester.downloader.queue.Size() + if hashSize > 0 || blockSize > 0 { + t.Error("block (", blockSize, ") or hash (", hashSize, ") not 0") + } +} + func TestThrottling(t *testing.T) { minDesiredPeerCount = 4 blockTtl = 1 * time.Second diff --git a/eth/sync.go b/eth/sync.go index c49f5209d..d955eaa50 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -63,6 +63,9 @@ func (pm *ProtocolManager) processBlocks() error { max := int(math.Min(float64(len(blocks)), float64(blockProcAmount))) _, err := pm.chainman.InsertChain(blocks[:max]) if err != nil { + // cancel download process + pm.downloader.Cancel() + return err } blocks = blocks[max:] From 0e211ae203935afe6478494f130a975f57261765 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 10 May 2015 01:55:39 +0200 Subject: [PATCH 09/15] ethdb: set open file limit to 128 for all leveldbs --- ethdb/database.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ethdb/database.go b/ethdb/database.go index 57a3f9ee6..15af02fdf 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -8,8 +8,11 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" ) +const openFileLimit = 128 + type LDBDatabase struct { fn string @@ -23,7 +26,7 @@ type LDBDatabase struct { func NewLDBDatabase(file string) (*LDBDatabase, error) { // Open the db - db, err := leveldb.OpenFile(file, nil) + db, err := leveldb.OpenFile(file, &opt.Options{OpenFilesCacheCapacity: openFileLimit}) if err != nil { return nil, err } From 7551264fa6305d34f95d4eadd5d381228f4020a8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 10 May 2015 03:38:46 +0200 Subject: [PATCH 10/15] cmd/geth, cmd/mist: version 0.9.19 --- cmd/geth/main.go | 2 +- cmd/mist/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 723353b0b..fd7aae4c2 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -51,7 +51,7 @@ import _ "net/http/pprof" const ( ClientIdentifier = "Geth" - Version = "0.9.18" + Version = "0.9.19" ) var ( diff --git a/cmd/mist/main.go b/cmd/mist/main.go index b7737aaab..4b55b3026 100644 --- a/cmd/mist/main.go +++ b/cmd/mist/main.go @@ -37,7 +37,7 @@ import ( const ( ClientIdentifier = "Mist" - Version = "0.9.18" + Version = "0.9.19" ) var ( From 0ad5898c0f9b0d777818d89356b74606f4b3c988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Mon, 11 May 2015 11:53:53 +0300 Subject: [PATCH 11/15] rpc, xeth: fix #881, gracefully handle offline whisper --- rpc/api.go | 33 +++++++++++++++++++++++++++++++++ rpc/http.go | 2 +- rpc/types.go | 16 ++++++++++++++++ xeth/xeth.go | 4 +++- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/rpc/api.go b/rpc/api.go index 6ba0d93e2..6a629ce8e 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -439,10 +439,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = newHexData(res) case "shh_version": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Retrieves the currently running whisper protocol version *reply = api.xeth().WhisperVersion() case "shh_post": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Injects a new message into the whisper network args := new(WhisperMessageArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -455,10 +463,18 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = true case "shh_newIdentity": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Creates a new whisper identity to use for sending/receiving messages *reply = api.xeth().Whisper().NewIdentity() case "shh_hasIdentity": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Checks if an identity if owned or not args := new(WhisperIdentityArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -467,6 +483,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = api.xeth().Whisper().HasIdentity(args.Identity) case "shh_newFilter": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Create a new filter to watch and match messages with args := new(WhisperFilterArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -476,6 +496,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = newHexNum(big.NewInt(int64(id)).Bytes()) case "shh_uninstallFilter": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Remove an existing filter watching messages args := new(FilterIdArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -484,6 +508,10 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = api.xeth().UninstallWhisperFilter(args.Id) case "shh_getFilterChanges": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Retrieve all the new messages arrived since the last request args := new(FilterIdArgs) if err := json.Unmarshal(req.Params, &args); err != nil { @@ -492,12 +520,17 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err *reply = api.xeth().WhisperMessagesChanged(args.Id) case "shh_getMessages": + // Short circuit if whisper is not running + if api.xeth().Whisper() == nil { + return NewNotAvailableError(req.Method, "whisper offline") + } // Retrieve all the cached messages matching a specific, existing filter args := new(FilterIdArgs) if err := json.Unmarshal(req.Params, &args); err != nil { return err } *reply = api.xeth().WhisperMessages(args.Id) + case "eth_hashrate": *reply = newHexNum(api.xeth().HashRate()) diff --git a/rpc/http.go b/rpc/http.go index 4760601d8..c5bb10c80 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -116,7 +116,7 @@ func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} { switch reserr.(type) { case nil: response = &RpcSuccessResponse{Jsonrpc: jsonrpcver, Id: request.Id, Result: reply} - case *NotImplementedError: + case *NotImplementedError, *NotAvailableError: jsonerr := &RpcErrorObject{-32601, reserr.Error()} response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError: diff --git a/rpc/types.go b/rpc/types.go index 1784759a4..e6eb4f856 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -209,6 +209,22 @@ func NewNotImplementedError(method string) *NotImplementedError { } } +type NotAvailableError struct { + Method string + Reason string +} + +func (e *NotAvailableError) Error() string { + return fmt.Sprintf("%s method not available: %s", e.Method, e.Reason) +} + +func NewNotAvailableError(method string, reason string) *NotAvailableError { + return &NotAvailableError{ + Method: method, + Reason: reason, + } +} + type DecodeParamError struct { err string } diff --git a/xeth/xeth.go b/xeth/xeth.go index ad8596803..b875fa6f1 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -79,7 +79,6 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth { xeth := &XEth{ backend: eth, frontend: frontend, - whisper: NewWhisper(eth.Whisper()), quit: make(chan struct{}), filterManager: filter.NewFilterManager(eth.EventMux()), logQueue: make(map[int]*logQueue), @@ -88,6 +87,9 @@ func New(eth *eth.Ethereum, frontend Frontend) *XEth { messages: make(map[int]*whisperFilter), agent: miner.NewRemoteAgent(), } + if eth.Whisper() != nil { + xeth.whisper = NewWhisper(eth.Whisper()) + } eth.Miner().Register(xeth.agent) if frontend == nil { xeth.frontend = dummyFrontend{} From a2919b5e17197afcb689b8f4144f255a5872f85d Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 10 May 2015 23:12:18 +0200 Subject: [PATCH 12/15] core, eth, miner: improved tx removal & fatal error on db sync err * core: Added GasPriceChange event * eth: When one of the DB flush methods error a fatal error log message is given. Hopefully this will prevent corrupted databases from occuring. * miner: remove transactions with low gas price. Closes #906, #903 --- common/size.go | 6 ------ common/size_test.go | 14 -------------- core/events.go | 6 +++++- core/manager.go | 2 ++ eth/backend.go | 8 +++++--- miner/worker.go | 42 +++++++++++++++++++++++++++++++++++------- 6 files changed, 47 insertions(+), 31 deletions(-) diff --git a/common/size.go b/common/size.go index 0d9dbf558..4ea7f7b11 100644 --- a/common/size.go +++ b/common/size.go @@ -44,12 +44,6 @@ func CurrencyToString(num *big.Int) string { ) switch { - case num.Cmp(Douglas) >= 0: - fin = new(big.Int).Div(num, Douglas) - denom = "Douglas" - case num.Cmp(Einstein) >= 0: - fin = new(big.Int).Div(num, Einstein) - denom = "Einstein" case num.Cmp(Ether) >= 0: fin = new(big.Int).Div(num, Ether) denom = "Ether" diff --git a/common/size_test.go b/common/size_test.go index 1cbeff0a8..cfe7efe31 100644 --- a/common/size_test.go +++ b/common/size_test.go @@ -25,8 +25,6 @@ func (s *SizeSuite) TestStorageSizeString(c *checker.C) { } func (s *CommonSuite) TestCommon(c *checker.C) { - douglas := CurrencyToString(BigPow(10, 43)) - einstein := CurrencyToString(BigPow(10, 22)) ether := CurrencyToString(BigPow(10, 19)) finney := CurrencyToString(BigPow(10, 16)) szabo := CurrencyToString(BigPow(10, 13)) @@ -35,8 +33,6 @@ func (s *CommonSuite) TestCommon(c *checker.C) { ada := CurrencyToString(BigPow(10, 4)) wei := CurrencyToString(big.NewInt(10)) - c.Assert(douglas, checker.Equals, "10 Douglas") - c.Assert(einstein, checker.Equals, "10 Einstein") c.Assert(ether, checker.Equals, "10 Ether") c.Assert(finney, checker.Equals, "10 Finney") c.Assert(szabo, checker.Equals, "10 Szabo") @@ -45,13 +41,3 @@ func (s *CommonSuite) TestCommon(c *checker.C) { c.Assert(ada, checker.Equals, "10 Ada") c.Assert(wei, checker.Equals, "10 Wei") } - -func (s *CommonSuite) TestLarge(c *checker.C) { - douglaslarge := CurrencyToString(BigPow(100000000, 43)) - adalarge := CurrencyToString(BigPow(100000000, 4)) - weilarge := CurrencyToString(big.NewInt(100000000)) - - c.Assert(douglaslarge, checker.Equals, "10000E298 Douglas") - c.Assert(adalarge, checker.Equals, "10000E7 Einstein") - c.Assert(weilarge, checker.Equals, "100 Babbage") -} diff --git a/core/events.go b/core/events.go index 3da668af5..1ea35c2f4 100644 --- a/core/events.go +++ b/core/events.go @@ -1,8 +1,10 @@ package core import ( - "github.com/ethereum/go-ethereum/core/types" + "math/big" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" ) // TxPreEvent is posted when a transaction enters the transaction pool. @@ -44,6 +46,8 @@ type ChainUncleEvent struct { type ChainHeadEvent struct{ Block *types.Block } +type GasPriceChanged struct{ Price *big.Int } + // Mining operation events type StartMining struct{} type TopMining struct{} diff --git a/core/manager.go b/core/manager.go index 9b5407a9e..433ada7ee 100644 --- a/core/manager.go +++ b/core/manager.go @@ -1,12 +1,14 @@ package core import ( + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" ) type Backend interface { + AccountManager() *accounts.Manager BlockProcessor() *BlockProcessor ChainManager() *ChainManager TxPool() *TxPool diff --git a/eth/backend.go b/eth/backend.go index 8f0789467..cdbe35b26 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -451,6 +451,8 @@ func (s *Ethereum) Start() error { return nil } +// sync databases every minute. If flushing fails we exit immediatly. The system +// may not continue under any circumstances. func (s *Ethereum) syncDatabases() { ticker := time.NewTicker(1 * time.Minute) done: @@ -459,13 +461,13 @@ done: case <-ticker.C: // don't change the order of database flushes if err := s.extraDb.Flush(); err != nil { - glog.V(logger.Error).Infof("error: flush extraDb: %v\n", err) + glog.Fatalf("fatal error: flush extraDb: %v\n", err) } if err := s.stateDb.Flush(); err != nil { - glog.V(logger.Error).Infof("error: flush stateDb: %v\n", err) + glog.Fatalf("fatal error: flush stateDb: %v\n", err) } if err := s.blockDb.Flush(); err != nil { - glog.V(logger.Error).Infof("error: flush blockDb: %v\n", err) + glog.Fatalf("fatal error: flush blockDb: %v\n", err) } case <-s.shutdownChan: break done diff --git a/miner/worker.go b/miner/worker.go index 22493c235..4ba566eec 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -7,6 +7,7 @@ import ( "sync" "sync/atomic" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" @@ -253,7 +254,12 @@ func (self *worker) makeCurrent() { func (w *worker) setGasPrice(p *big.Int) { w.mu.Lock() defer w.mu.Unlock() - w.gasPrice = p + + // calculate the minimal gas price the miner accepts when sorting out transactions. + const pct = int64(90) + w.gasPrice = gasprice(p, pct) + + w.mux.Post(core.GasPriceChanged{w.gasPrice}) } func (self *worker) commitNewWork() { @@ -269,27 +275,40 @@ func (self *worker) commitNewWork() { transactions := self.eth.TxPool().GetTransactions() sort.Sort(types.TxByNonce{transactions}) + accounts, _ := self.eth.AccountManager().Accounts() // Keep track of transactions which return errors so they can be removed var ( remove = set.New() tcount = 0 ignoredTransactors = set.New() + lowGasTransactors = set.New() + ownedAccounts = accountAddressesSet(accounts) + lowGasTxs types.Transactions ) - const pct = int64(90) - // calculate the minimal gas price the miner accepts when sorting out transactions. - minprice := gasprice(self.gasPrice, pct) for _, tx := range transactions { // We can skip err. It has already been validated in the tx pool from, _ := tx.From() // check if it falls within margin - if tx.GasPrice().Cmp(minprice) < 0 { + if tx.GasPrice().Cmp(self.gasPrice) < 0 { // ignore the transaction and transactor. We ignore the transactor // because nonce will fail after ignoring this transaction so there's // no point - ignoredTransactors.Add(from) - glog.V(logger.Info).Infof("transaction(%x) below gas price (<%d%% ask price). All sequential txs from this address(%x) will fail\n", tx.Hash().Bytes()[:4], pct, from[:4]) + lowGasTransactors.Add(from) + + glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4]) + } + + // Continue with the next transaction if the transaction sender is included in + // the low gas tx set. This will also remove the tx and all sequential transaction + // from this transactor + if lowGasTransactors.Has(from) { + // add tx to the low gas set. This will be removed at the end of the run + // owned accounts are ignored + if !ownedAccounts.Has(from) { + lowGasTxs = append(lowGasTxs, tx) + } continue } @@ -327,6 +346,7 @@ func (self *worker) commitNewWork() { tcount++ } } + self.eth.TxPool().RemoveTransactions(lowGasTxs) var ( uncles []*types.Header @@ -423,3 +443,11 @@ func gasprice(price *big.Int, pct int64) *big.Int { p.Mul(p, big.NewInt(pct)) return p } + +func accountAddressesSet(accounts []accounts.Account) *set.Set { + accountSet := set.New() + for _, account := range accounts { + accountSet.Add(common.BytesToAddress(account.Address)) + } + return accountSet +} From df323cdb4e1aaab8e57cb1809a0bc5e47d307260 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 May 2015 01:24:40 +0200 Subject: [PATCH 13/15] rpc: display error message to stdout --- rpc/jeth.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpc/jeth.go b/rpc/jeth.go index 4739316b2..a08f9be8f 100644 --- a/rpc/jeth.go +++ b/rpc/jeth.go @@ -2,6 +2,7 @@ package rpc import ( "encoding/json" + "fmt" "github.com/ethereum/go-ethereum/jsre" "github.com/robertkrimen/otto" @@ -52,6 +53,7 @@ func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { var respif interface{} err = self.ethApi.GetRequestReply(&req, &respif) if err != nil { + fmt.Println("Error response:", err) return self.err(call, -32603, err.Error(), req.Id) } call.Otto.Set("ret_jsonrpc", jsonrpcver) From 3c6c89168049fbcffa0a02690c27c324f6f0264d Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 May 2015 01:28:15 +0200 Subject: [PATCH 14/15] core: optimise pending transaction processing --- core/transaction_pool.go | 2 +- miner/worker.go | 186 +++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 86 deletions(-) diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 6898a4bda..e68f7406a 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -21,7 +21,7 @@ var ( ErrInvalidSender = errors.New("Invalid sender") ErrNonce = errors.New("Nonce too low") ErrBalance = errors.New("Insufficient balance") - ErrNonExistentAccount = errors.New("Account does not exist") + ErrNonExistentAccount = errors.New("Account does not exist or account balance too low") ErrInsufficientFunds = errors.New("Insufficient funds for gas * price + value") ErrIntrinsicGas = errors.New("Intrinsic gas too low") ErrGasLimit = errors.New("Exceeds block gas limit") diff --git a/miner/worker.go b/miner/worker.go index 4ba566eec..e3dbae717 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -22,12 +22,18 @@ import ( var jsonlogger = logger.NewJsonLogger() type environment struct { - totalUsedGas *big.Int - state *state.StateDB - coinbase *state.StateObject - block *types.Block - family *set.Set - uncles *set.Set + totalUsedGas *big.Int + state *state.StateDB + coinbase *state.StateObject + block *types.Block + family *set.Set + uncles *set.Set + remove *set.Set + tcount int + ignoredTransactors *set.Set + lowGasTransactors *set.Set + ownedAccounts *set.Set + lowGasTxs types.Transactions } func env(block *types.Block, eth core.Backend) *environment { @@ -129,12 +135,13 @@ func (self *worker) start() { self.mu.Lock() defer self.mu.Unlock() + atomic.StoreInt32(&self.mining, 1) + // spin up agents for _, agent := range self.agents { agent.Start() } - atomic.StoreInt32(&self.mining, 1) } func (self *worker) stop() { @@ -175,8 +182,11 @@ out: self.possibleUncles[ev.Block.Hash()] = ev.Block self.uncleMu.Unlock() case core.TxPreEvent: + // Apply transaction to the pending state if we're not mining if atomic.LoadInt32(&self.mining) == 0 { - self.commitNewWork() + self.mu.Lock() + self.commitTransactions(types.Transactions{ev.Tx}) + self.mu.Unlock() } } case <-self.quit: @@ -242,13 +252,22 @@ func (self *worker) makeCurrent() { } block.Header().Extra = self.extra - self.current = env(block, self.eth) + current := env(block, self.eth) for _, ancestor := range self.chain.GetAncestors(block, 7) { - self.current.family.Add(ancestor.Hash()) + current.family.Add(ancestor.Hash()) } + accounts, _ := self.eth.AccountManager().Accounts() + // Keep track of transactions which return errors so they can be removed + current.remove = set.New() + current.tcount = 0 + current.ignoredTransactors = set.New() + current.lowGasTransactors = set.New() + current.ownedAccounts = accountAddressesSet(accounts) - parent := self.chain.GetBlock(self.current.block.ParentHash()) - self.current.coinbase.SetGasPool(core.CalcGasLimit(parent)) + parent := self.chain.GetBlock(current.block.ParentHash()) + current.coinbase.SetGasPool(core.CalcGasLimit(parent)) + + self.current = current } func (w *worker) setGasPrice(p *big.Int) { @@ -271,82 +290,14 @@ func (self *worker) commitNewWork() { defer self.currentMu.Unlock() self.makeCurrent() + current := self.current transactions := self.eth.TxPool().GetTransactions() sort.Sort(types.TxByNonce{transactions}) - accounts, _ := self.eth.AccountManager().Accounts() - // Keep track of transactions which return errors so they can be removed - var ( - remove = set.New() - tcount = 0 - ignoredTransactors = set.New() - lowGasTransactors = set.New() - ownedAccounts = accountAddressesSet(accounts) - lowGasTxs types.Transactions - ) - - for _, tx := range transactions { - // We can skip err. It has already been validated in the tx pool - from, _ := tx.From() - - // check if it falls within margin - if tx.GasPrice().Cmp(self.gasPrice) < 0 { - // ignore the transaction and transactor. We ignore the transactor - // because nonce will fail after ignoring this transaction so there's - // no point - lowGasTransactors.Add(from) - - glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4]) - } - - // Continue with the next transaction if the transaction sender is included in - // the low gas tx set. This will also remove the tx and all sequential transaction - // from this transactor - if lowGasTransactors.Has(from) { - // add tx to the low gas set. This will be removed at the end of the run - // owned accounts are ignored - if !ownedAccounts.Has(from) { - lowGasTxs = append(lowGasTxs, tx) - } - continue - } - - // Move on to the next transaction when the transactor is in ignored transactions set - // This may occur when a transaction hits the gas limit. When a gas limit is hit and - // the transaction is processed (that could potentially be included in the block) it - // will throw a nonce error because the previous transaction hasn't been processed. - // Therefor we need to ignore any transaction after the ignored one. - if ignoredTransactors.Has(from) { - continue - } - - self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0) - - err := self.commitTransaction(tx) - switch { - case core.IsNonceErr(err) || core.IsInvalidTxErr(err): - // Remove invalid transactions - from, _ := tx.From() - - self.chain.TxState().RemoveNonce(from, tx.Nonce()) - remove.Add(tx.Hash()) - - if glog.V(logger.Detail) { - glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) - } - case state.IsGasLimitErr(err): - from, _ := tx.From() - // 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. - ignoredTransactors.Add(from) - - glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) - default: - tcount++ - } - } - self.eth.TxPool().RemoveTransactions(lowGasTxs) + // commit transactions for this run + self.commitTransactions(transactions) + self.eth.TxPool().RemoveTransactions(current.lowGasTxs) var ( uncles []*types.Header @@ -372,7 +323,7 @@ func (self *worker) commitNewWork() { // We only care about logging if we're actually mining if atomic.LoadInt32(&self.mining) == 1 { - glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", self.current.block.Number(), tcount, len(uncles)) + glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles\n", current.block.Number(), current.tcount, len(uncles)) } for _, hash := range badUncles { @@ -412,6 +363,71 @@ func (self *worker) commitUncle(uncle *types.Header) error { return nil } +func (self *worker) commitTransactions(transactions types.Transactions) { + current := self.current + + for _, tx := range transactions { + // We can skip err. It has already been validated in the tx pool + from, _ := tx.From() + + // check if it falls within margin + if tx.GasPrice().Cmp(self.gasPrice) < 0 { + // ignore the transaction and transactor. We ignore the transactor + // because nonce will fail after ignoring this transaction so there's + // no point + current.lowGasTransactors.Add(from) + + glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(self.gasPrice), from[:4]) + } + + // Continue with the next transaction if the transaction sender is included in + // the low gas tx set. This will also remove the tx and all sequential transaction + // from this transactor + if current.lowGasTransactors.Has(from) { + // add tx to the low gas set. This will be removed at the end of the run + // owned accounts are ignored + if !current.ownedAccounts.Has(from) { + current.lowGasTxs = append(current.lowGasTxs, tx) + } + continue + } + + // Move on to the next transaction when the transactor is in ignored transactions set + // This may occur when a transaction hits the gas limit. When a gas limit is hit and + // the transaction is processed (that could potentially be included in the block) it + // will throw a nonce error because the previous transaction hasn't been processed. + // Therefor we need to ignore any transaction after the ignored one. + if current.ignoredTransactors.Has(from) { + continue + } + + self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0) + + err := self.commitTransaction(tx) + switch { + case core.IsNonceErr(err) || core.IsInvalidTxErr(err): + // Remove invalid transactions + from, _ := tx.From() + + self.chain.TxState().RemoveNonce(from, tx.Nonce()) + current.remove.Add(tx.Hash()) + + if glog.V(logger.Detail) { + glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) + } + case state.IsGasLimitErr(err): + from, _ := tx.From() + // 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. + current.ignoredTransactors.Add(from) + + glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) + default: + current.tcount++ + } + } +} + func (self *worker) commitTransaction(tx *types.Transaction) error { snap := self.current.state.Copy() receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true) From 6ecba12650f2d20eded5f4f09fb312d84e81d909 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 11 May 2015 11:36:09 +0200 Subject: [PATCH 15/15] miner: added log message for mining operation. #912 --- miner/miner.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miner/miner.go b/miner/miner.go index d5ea9a146..efe6d3051 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -7,6 +7,8 @@ import ( "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/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/pow" ) @@ -47,6 +49,8 @@ func (m *Miner) SetGasPrice(price *big.Int) { } func (self *Miner) Start(coinbase common.Address) { + glog.V(logger.Info).Infoln("Starting mining operation") + self.mining = true self.worker.coinbase = coinbase self.worker.start()