func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { gp := new(core.GasPool).AddGas(env.header.GasLimit) var coalescedLogs vm.Logs for _, tx := range transactions { // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := tx.From() // Check if it falls within margin. Txs from owned accounts are always processed. if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { // ignore the transaction and transactor. We ignore the transactor // because nonce will fail after ignoring this transaction so there's // no point env.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(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 env.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 !env.ownedAccounts.Has(from) { env.lowGasTxs = append(env.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 env.ignoredTransactors.Has(from) { continue } env.state.StartRecord(tx.Hash(), common.Hash{}, 0) err, logs := env.commitTransaction(tx, bc, gp) switch { 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) glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) case err != nil: env.remove.Add(tx.Hash()) if glog.V(logger.Detail) { glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) } default: env.tcount++ coalescedLogs = append(coalescedLogs, logs...) } } if len(coalescedLogs) > 0 || env.tcount > 0 { go func(logs vm.Logs, tcount int) { if len(logs) > 0 { mux.Post(core.PendingLogsEvent{Logs: logs}) } if tcount > 0 { mux.Post(core.PendingStateEvent{}) } }(coalescedLogs, env.tcount) } }
func TestCallbacks(t *testing.T) { var ( mux event.TypeMux fs = NewFilterSystem(&mux) blockDone = make(chan struct{}) txDone = make(chan struct{}) logDone = make(chan struct{}) removedLogDone = make(chan struct{}) pendingLogDone = make(chan struct{}) ) blockFilter := &Filter{ BlockCallback: func(*types.Block, vm.Logs) { close(blockDone) }, } txFilter := &Filter{ TransactionCallback: func(*types.Transaction) { close(txDone) }, } logFilter := &Filter{ LogCallback: func(l *vm.Log, oob bool) { if !oob { close(logDone) } }, } removedLogFilter := &Filter{ LogCallback: func(l *vm.Log, oob bool) { if oob { close(removedLogDone) } }, } pendingLogFilter := &Filter{ LogCallback: func(*vm.Log, bool) { close(pendingLogDone) }, } fs.Add(blockFilter, ChainFilter) fs.Add(txFilter, PendingTxFilter) fs.Add(logFilter, LogFilter) fs.Add(removedLogFilter, LogFilter) fs.Add(pendingLogFilter, PendingLogFilter) mux.Post(core.ChainEvent{}) mux.Post(core.TxPreEvent{}) mux.Post(vm.Logs{&vm.Log{}}) mux.Post(core.RemovedLogsEvent{Logs: vm.Logs{&vm.Log{}}}) mux.Post(core.PendingLogsEvent{Logs: vm.Logs{&vm.Log{}}}) const dura = 5 * time.Second failTimer := time.NewTimer(dura) select { case <-blockDone: case <-failTimer.C: t.Error("block filter failed to trigger (timeout)") } failTimer.Reset(dura) select { case <-txDone: case <-failTimer.C: t.Error("transaction filter failed to trigger (timeout)") } failTimer.Reset(dura) select { case <-logDone: case <-failTimer.C: t.Error("log filter failed to trigger (timeout)") } failTimer.Reset(dura) select { case <-removedLogDone: case <-failTimer.C: t.Error("removed log filter failed to trigger (timeout)") } failTimer.Reset(dura) select { case <-pendingLogDone: case <-failTimer.C: t.Error("pending log filter failed to trigger (timeout)") } }