func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (*types.Transaction, error) { if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { return nil, errors.New("Invalid address") } var ( from = common.HexToAddress(fromStr) to = common.HexToAddress(toStr) value = common.Big(valueStr) gas *big.Int price *big.Int data []byte contractCreation bool ) if len(gasStr) == 0 { gas = DefaultGas() } else { gas = common.Big(gasStr) } if len(gasPriceStr) == 0 { price = self.DefaultGasPrice() } else { price = common.Big(gasPriceStr) } data = common.FromHex(codeStr) if len(toStr) == 0 { contractCreation = true } var nonce uint64 if len(nonceStr) != 0 { nonce = common.Big(nonceStr).Uint64() } else { state := self.backend.TxPool().State() nonce = state.GetNonce(from) } var tx *types.Transaction if contractCreation { tx = types.NewContractCreation(nonce, value, gas, price, data) } else { tx = types.NewTransaction(nonce, to, value, gas, price, data) } signed, err := self.sign(tx, from, false) if err != nil { return nil, err } return signed, nil }
// validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { state := pool.currentState() for hash, tx := range pool.pending { from, _ := tx.From() // err already checked // perform light nonce validation if state.GetNonce(from) > tx.Nonce() { if glog.V(logger.Core) { glog.Infof("removed tx (%x) from pool: low tx nonce\n", hash[:4]) } delete(pool.pending, hash) } } }
// checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { state := pool.pendingState var addq txQueue for address, txs := range pool.queue { // guessed nonce is the nonce currently kept by the tx pool (pending state) guessedNonce := state.GetNonce(address) // true nonce is the nonce known by the last state trueNonce := pool.currentState().GetNonce(address) addq := addq[:0] for hash, tx := range txs { if tx.Nonce() < trueNonce { // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) } else { // Collect the remaining transactions for the next pass. addq = append(addq, txQueueEntry{hash, address, tx}) } } // Find the next consecutive nonce range starting at the // current account nonce. sort.Sort(addq) for i, e := range addq { // start deleting the transactions from the queue if they exceed the limit if i > maxQueued { delete(pool.queue[address], e.hash) continue } if e.Nonce() > guessedNonce { if len(addq)-i > maxQueued { if glog.V(logger.Debug) { glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(e.hash[:])) } for j := i + maxQueued; j < len(addq); j++ { delete(txs, addq[j].hash) } } break } delete(txs, e.hash) pool.addTx(e.hash, address, e.Transaction) } // Delete the entire queue entry if it became empty. if len(txs) == 0 { delete(pool.queue, address) } } }
// validatePool removes invalid and processed transactions from the main pool. // If a transaction is removed for being invalid (e.g. out of funds), all sub- // sequent (Still valid) transactions are moved back into the future queue. This // is important to prevent a drained account from DOSing the network with non // executable transactions. func (pool *TxPool) validatePool() { state, err := pool.currentState() if err != nil { glog.V(logger.Info).Infoln("failed to get current state: %v", err) return } balanceCache := make(map[common.Address]*big.Int) // Clean up the pending pool, accumulating invalid nonces gaps := make(map[common.Address]uint64) for hash, tx := range pool.pending { sender, _ := tx.From() // err already checked // Perform light nonce and balance validation balance := balanceCache[sender] if balance == nil { balance = state.GetBalance(sender) balanceCache[sender] = balance } if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 { // Remove an already past it invalidated transaction if glog.V(logger.Core) { glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx) } delete(pool.pending, hash) // Track the smallest invalid nonce to postpone subsequent transactions if !past { if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev { gaps[sender] = tx.Nonce() } } } } // Move all transactions after a gap back to the future queue if len(gaps) > 0 { for hash, tx := range pool.pending { sender, _ := tx.From() if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap { if glog.V(logger.Core) { glog.Infof("postponed tx (%v) due to introduced gap\n", tx) } pool.queueTx(hash, tx) delete(pool.pending, hash) } } } }
// demoteUnexecutables removes invalid and processed transactions from the pools // executable/pending queue and any subsequent transactions that become unexecutable // are moved back into the future queue. func (pool *TxPool) demoteUnexecutables() { // Retrieve the current state to allow nonce and balance checking state, err := pool.currentState() if err != nil { glog.V(logger.Info).Infoln("failed to get current state: %v", err) return } // Iterate over all accounts and demote any non-executable transactions for addr, list := range pool.pending { nonce := state.GetNonce(addr) // Drop all transactions that are deemed too old (low nonce) for _, tx := range list.Forward(nonce) { if glog.V(logger.Core) { glog.Infof("Removed old pending transaction: %v", tx) } delete(pool.all, tx.Hash()) } // Drop all transactions that are too costly (low balance), and queue any invalids back for later drops, invalids := list.Filter(state.GetBalance(addr)) for _, tx := range drops { if glog.V(logger.Core) { glog.Infof("Removed unpayable pending transaction: %v", tx) } delete(pool.all, tx.Hash()) } for _, tx := range invalids { if glog.V(logger.Core) { glog.Infof("Demoting pending transaction: %v", tx) } pool.enqueueTx(tx.Hash(), tx) } // Delete the entire queue entry if it became empty. if list.Empty() { delete(pool.pending, addr) delete(pool.beats, addr) } } }
// promoteExecutables moves transactions that have become processable from the // future queue to the set of pending transactions. During this process, all // invalidated transactions (low nonce, low balance) are deleted. func (pool *TxPool) promoteExecutables() { // Init delayed since tx pool could have been started before any state sync if pool.pendingState == nil { pool.resetState() } // Retrieve the current state to allow nonce and balance checking state, err := pool.currentState() if err != nil { glog.Errorf("Could not get current state: %v", err) return } // Iterate over all accounts and promote any executable transactions queued := uint64(0) for addr, list := range pool.queue { // Drop all transactions that are deemed too old (low nonce) for _, tx := range list.Forward(state.GetNonce(addr)) { if glog.V(logger.Core) { glog.Infof("Removed old queued transaction: %v", tx) } delete(pool.all, tx.Hash()) } // Drop all transactions that are too costly (low balance) drops, _ := list.Filter(state.GetBalance(addr)) for _, tx := range drops { if glog.V(logger.Core) { glog.Infof("Removed unpayable queued transaction: %v", tx) } delete(pool.all, tx.Hash()) } // Gather all executable transactions and promote them for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) { if glog.V(logger.Core) { glog.Infof("Promoting queued transaction: %v", tx) } pool.promoteTx(addr, tx.Hash(), tx) } // Drop all transactions over the allowed limit for _, tx := range list.Cap(int(maxQueuedPerAccount)) { if glog.V(logger.Core) { glog.Infof("Removed cap-exceeding queued transaction: %v", tx) } delete(pool.all, tx.Hash()) } queued += uint64(list.Len()) // Delete the entire queue entry if it became empty. if list.Empty() { delete(pool.queue, addr) } } // If we've queued more transactions than the hard limit, drop oldest ones if queued > maxQueuedInTotal { // Sort all accounts with queued transactions by heartbeat addresses := make(addresssByHeartbeat, 0, len(pool.queue)) for addr, _ := range pool.queue { addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]}) } sort.Sort(addresses) // Drop transactions until the total is below the limit for drop := queued - maxQueuedInTotal; drop > 0; { addr := addresses[len(addresses)-1] list := pool.queue[addr.address] addresses = addresses[:len(addresses)-1] // Drop all transactions if they are less than the overflow if size := uint64(list.Len()); size <= drop { for _, tx := range list.Flatten() { pool.removeTx(tx.Hash()) } drop -= size continue } // Otherwise drop only last few transactions txs := list.Flatten() for i := len(txs) - 1; i >= 0 && drop > 0; i-- { pool.removeTx(txs[i].Hash()) drop-- } } } }
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { // this minimalistic recoding is enough (works for natspec.js) var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, toStr, codeStr) if !self.ConfirmTransaction(jsontx) { err := fmt.Errorf("Transaction not confirmed") return "", err } if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { return "", errors.New("Invalid address") } var ( from = common.HexToAddress(fromStr) to = common.HexToAddress(toStr) value = common.Big(valueStr) gas *big.Int price *big.Int data []byte contractCreation bool ) if len(gasStr) == 0 { gas = DefaultGas() } else { gas = common.Big(gasStr) } if len(gasPriceStr) == 0 { price = self.DefaultGasPrice() } else { price = common.Big(gasPriceStr) } data = common.FromHex(codeStr) if len(toStr) == 0 { contractCreation = true } // 2015-05-18 Is this still needed? // TODO if no_private_key then //if _, exists := p.register[args.From]; exists { // p.register[args.From] = append(p.register[args.From], args) //} else { /* account := accounts.Get(common.FromHex(args.From)) if account != nil { if account.Unlocked() { if !unlockAccount(account) { return } } result, _ := account.Transact(common.FromHex(args.To), common.FromHex(args.Value), common.FromHex(args.Gas), common.FromHex(args.GasPrice), common.FromHex(args.Data)) if len(result) > 0 { *reply = common.ToHex(result) } } else if _, exists := p.register[args.From]; exists { p.register[ags.From] = append(p.register[args.From], args) } */ self.transactMu.Lock() defer self.transactMu.Unlock() var nonce uint64 if len(nonceStr) != 0 { nonce = common.Big(nonceStr).Uint64() } else { state := self.backend.TxPool().State() nonce = state.GetNonce(from) } var tx *types.Transaction if contractCreation { tx = types.NewContractCreation(nonce, value, gas, price, data) } else { tx = types.NewTransaction(nonce, to, value, gas, price, data) } signed, err := self.sign(tx, from, false) if err != nil { return "", err } if err = self.backend.TxPool().Add(signed); err != nil { return "", err } if contractCreation { addr := crypto.CreateAddress(from, nonce) glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signed.Hash().Hex(), addr.Hex()) } else { glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signed.Hash().Hex(), tx.To().Hex()) } return signed.Hash().Hex(), nil }