github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/txpool_core.go (about)

     1  package network
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/piotrnar/gocoin/client/common"
    11  	"github.com/piotrnar/gocoin/lib/btc"
    12  	"github.com/piotrnar/gocoin/lib/chain"
    13  	"github.com/piotrnar/gocoin/lib/script"
    14  )
    15  
    16  const (
    17  	TX_REJECTED_DISABLED = 1
    18  
    19  	TX_REJECTED_TOO_BIG      = 101
    20  	TX_REJECTED_FORMAT       = 102
    21  	TX_REJECTED_LEN_MISMATCH = 103
    22  	TX_REJECTED_EMPTY_INPUT  = 104
    23  
    24  	TX_REJECTED_OVERSPEND = 154
    25  	TX_REJECTED_BAD_INPUT = 157
    26  
    27  	// Anything from the list below might eventually get mined
    28  	TX_REJECTED_NO_TXOU     = 202
    29  	TX_REJECTED_LOW_FEE     = 205
    30  	TX_REJECTED_NOT_MINED   = 208
    31  	TX_REJECTED_CB_INMATURE = 209
    32  	TX_REJECTED_RBF_LOWFEE  = 210
    33  	TX_REJECTED_RBF_FINAL   = 211
    34  	TX_REJECTED_RBF_100     = 212
    35  	TX_REJECTED_REPLACED    = 213
    36  )
    37  
    38  var (
    39  	TxMutex sync.Mutex
    40  
    41  	// The actual memory pool:
    42  	TransactionsToSend       map[BIDX]*OneTxToSend = make(map[BIDX]*OneTxToSend)
    43  	TransactionsToSendSize   uint64
    44  	TransactionsToSendWeight uint64
    45  
    46  	// All the outputs that are currently spent in TransactionsToSend:
    47  	SpentOutputs map[uint64]BIDX = make(map[uint64]BIDX)
    48  
    49  	// Transactions that we downloaded, but rejected:
    50  	TransactionsRejected     map[BIDX]*OneTxRejected = make(map[BIDX]*OneTxRejected)
    51  	TransactionsRejectedSize uint64                  // only include those that have *Tx pointer set
    52  
    53  	// Transactions that are received from network (via "tx"), but not yet processed:
    54  	TransactionsPending map[BIDX]bool = make(map[BIDX]bool)
    55  
    56  	// Transactions that are waiting for inputs:
    57  	WaitingForInputs     map[BIDX]*OneWaitingList = make(map[BIDX]*OneWaitingList)
    58  	WaitingForInputsSize uint64
    59  )
    60  
    61  type OneTxToSend struct {
    62  	Invsentcnt, SentCnt uint32
    63  	Firstseen, Lastsent time.Time
    64  	Local               bool
    65  	Spent               []uint64 // Which records in SpentOutputs this TX added
    66  	Volume, Fee         uint64
    67  	*btc.Tx
    68  	Blocked     byte   // if non-zero, it gives you the reason why this tx nas not been routed
    69  	MemInputs   []bool // transaction is spending inputs from other unconfirmed tx(s)
    70  	MemInputCnt int
    71  	SigopsCost  uint64
    72  	Final       bool // if true RFB will not work on it
    73  	VerifyTime  time.Duration
    74  }
    75  
    76  type OneTxRejected struct {
    77  	Id *btc.Uint256
    78  	time.Time
    79  	Size     uint32
    80  	Reason   byte
    81  	Waiting4 *btc.Uint256
    82  	*btc.Tx
    83  }
    84  
    85  type OneWaitingList struct {
    86  	TxID  *btc.Uint256
    87  	TxLen uint32
    88  	Ids   map[BIDX]time.Time // List of pending tx ids
    89  }
    90  
    91  func ReasonToString(reason byte) string {
    92  	switch reason {
    93  	case 0:
    94  		return ""
    95  	case TX_REJECTED_DISABLED:
    96  		return "RELAY_OFF"
    97  	case TX_REJECTED_TOO_BIG:
    98  		return "TOO_BIG"
    99  	case TX_REJECTED_FORMAT:
   100  		return "FORMAT"
   101  	case TX_REJECTED_LEN_MISMATCH:
   102  		return "LEN_MISMATCH"
   103  	case TX_REJECTED_EMPTY_INPUT:
   104  		return "EMPTY_INPUT"
   105  	case TX_REJECTED_OVERSPEND:
   106  		return "OVERSPEND"
   107  	case TX_REJECTED_BAD_INPUT:
   108  		return "BAD_INPUT"
   109  	case TX_REJECTED_NO_TXOU:
   110  		return "NO_TXOU"
   111  	case TX_REJECTED_LOW_FEE:
   112  		return "LOW_FEE"
   113  	case TX_REJECTED_NOT_MINED:
   114  		return "NOT_MINED"
   115  	case TX_REJECTED_CB_INMATURE:
   116  		return "CB_INMATURE"
   117  	case TX_REJECTED_RBF_LOWFEE:
   118  		return "RBF_LOWFEE"
   119  	case TX_REJECTED_RBF_FINAL:
   120  		return "RBF_FINAL"
   121  	case TX_REJECTED_RBF_100:
   122  		return "RBF_100"
   123  	case TX_REJECTED_REPLACED:
   124  		return "REPLACED"
   125  	}
   126  	return fmt.Sprint("UNKNOWN_", reason)
   127  }
   128  
   129  func NeedThisTx(id *btc.Uint256, cb func()) (res bool) {
   130  	return NeedThisTxExt(id, cb) == 0
   131  }
   132  
   133  // NeedThisTxExt returns false if we do not want to receive a data for this tx.
   134  func NeedThisTxExt(id *btc.Uint256, cb func()) (why_not int) {
   135  	TxMutex.Lock()
   136  	if _, present := TransactionsToSend[id.BIdx()]; present {
   137  		why_not = 1
   138  	} else if _, present := TransactionsRejected[id.BIdx()]; present {
   139  		why_not = 2
   140  	} else if _, present := TransactionsPending[id.BIdx()]; present {
   141  		why_not = 3
   142  	} else if common.BlockChain.Unspent.TxPresent(id) {
   143  		why_not = 4
   144  		// This assumes that tx's out #0 has not been spent yet, which may not always be the case, but well...
   145  		common.CountSafe("TxAlreadyMined")
   146  	} else {
   147  		// why_not = 0
   148  		if cb != nil {
   149  			cb()
   150  		}
   151  	}
   152  	TxMutex.Unlock()
   153  	return
   154  }
   155  
   156  // TxInvNotify handles tx-inv notifications.
   157  func (c *OneConnection) TxInvNotify(hash []byte) {
   158  	if NeedThisTx(btc.NewUint256(hash), nil) {
   159  		var b [1 + 4 + 32]byte
   160  		b[0] = 1 // One inv
   161  		if (c.Node.Services & btc.SERVICE_SEGWIT) != 0 {
   162  			binary.LittleEndian.PutUint32(b[1:5], MSG_WITNESS_TX) // SegWit Tx
   163  			//println(c.ConnID, "getdata", btc.NewUint256(hash).String())
   164  		} else {
   165  			b[1] = MSG_TX // Tx
   166  		}
   167  		copy(b[5:37], hash)
   168  		c.SendRawMsg("getdata", b[:])
   169  	}
   170  }
   171  
   172  // RejectTx adds a transaction to the rejected list or not, if it has been mined already.
   173  // Make sure to call it with locked TxMutex.
   174  // Returns the OneTxRejected or nil if it has not been added.
   175  func RejectTx(tx *btc.Tx, why byte) *OneTxRejected {
   176  	rec := new(OneTxRejected)
   177  	rec.Time = time.Now()
   178  	rec.Size = uint32(len(tx.Raw))
   179  	rec.Reason = why
   180  
   181  	// TODO: only store tx for selected reasons
   182  	if why >= 200 {
   183  		rec.Tx = tx
   184  		rec.Id = &tx.Hash
   185  		TransactionsRejectedSize += uint64(rec.Size)
   186  	} else {
   187  		rec.Id = new(btc.Uint256)
   188  		rec.Id.Hash = tx.Hash.Hash
   189  	}
   190  
   191  	bidx := tx.Hash.BIdx()
   192  	TransactionsRejected[bidx] = rec
   193  
   194  	return rec
   195  }
   196  
   197  // ParseTxNet handles incoming "tx" messages.
   198  func (c *OneConnection) ParseTxNet(pl []byte) {
   199  	tx, le := btc.NewTx(pl)
   200  	if tx == nil {
   201  		c.DoS("TxRejectedBroken")
   202  		return
   203  	}
   204  	if le != len(pl) {
   205  		c.DoS("TxRejectedLenMismatch")
   206  		return
   207  	}
   208  	if len(tx.TxIn) < 1 {
   209  		c.Misbehave("TxRejectedNoInputs", 100)
   210  		return
   211  	}
   212  
   213  	tx.SetHash(pl)
   214  
   215  	if tx.Weight() > 4*int(common.GetUint32(&common.CFG.TXPool.MaxTxSize)) {
   216  		TxMutex.Lock()
   217  		RejectTx(tx, TX_REJECTED_TOO_BIG)
   218  		TxMutex.Unlock()
   219  		common.CountSafe("TxRejectedBig")
   220  		return
   221  	}
   222  
   223  	NeedThisTx(&tx.Hash, func() {
   224  		// This body is called with a locked TxMutex
   225  		tx.Raw = pl
   226  		select {
   227  		case NetTxs <- &TxRcvd{conn: c, Tx: tx, trusted: c.X.Authorized}:
   228  			TransactionsPending[tx.Hash.BIdx()] = true
   229  		default:
   230  			common.CountSafe("TxRejectedFullQ")
   231  			//println("NetTxsFULL")
   232  		}
   233  	})
   234  }
   235  
   236  // HandleNetTx must be called from the chain's thread.
   237  func HandleNetTx(ntx *TxRcvd, retry bool) (accepted bool) {
   238  	common.CountSafe("HandleNetTx")
   239  
   240  	tx := ntx.Tx
   241  	bidx := tx.Hash.BIdx()
   242  	start_time := time.Now()
   243  	var final bool // set to true if any of the inpits has a final sequence
   244  
   245  	var totinp, totout uint64
   246  	var frommem []bool
   247  	var frommemcnt int
   248  
   249  	TxMutex.Lock()
   250  
   251  	if !retry {
   252  		if _, present := TransactionsPending[bidx]; !present {
   253  			// It had to be mined in the meantime, so just drop it now
   254  			TxMutex.Unlock()
   255  			common.CountSafe("TxNotPending")
   256  			return
   257  		}
   258  		delete(TransactionsPending, bidx)
   259  	} else {
   260  		// In case case of retry, it is on the rejected list,
   261  		// so remove it now to free any tied WaitingForInputs
   262  		deleteRejected(bidx)
   263  	}
   264  
   265  	pos := make([]*btc.TxOut, len(tx.TxIn))
   266  	spent := make([]uint64, len(tx.TxIn))
   267  
   268  	var rbf_tx_list map[*OneTxToSend]bool
   269  
   270  	// Check if all the inputs exist in the chain
   271  	for i := range tx.TxIn {
   272  		if !final && tx.TxIn[i].Sequence >= 0xfffffffe {
   273  			final = true
   274  		}
   275  
   276  		spent[i] = tx.TxIn[i].Input.UIdx()
   277  
   278  		if so, ok := SpentOutputs[spent[i]]; ok {
   279  			// Can only be accepted as RBF...
   280  
   281  			if rbf_tx_list == nil {
   282  				rbf_tx_list = make(map[*OneTxToSend]bool)
   283  			}
   284  
   285  			ctx := TransactionsToSend[so]
   286  
   287  			if !ntx.trusted && ctx.Final {
   288  				RejectTx(ntx.Tx, TX_REJECTED_RBF_FINAL)
   289  				TxMutex.Unlock()
   290  				common.CountSafe("TxRejectedRBFFinal")
   291  				return
   292  			}
   293  
   294  			rbf_tx_list[ctx] = true
   295  			if !ntx.trusted && len(rbf_tx_list) > 100 {
   296  				RejectTx(ntx.Tx, TX_REJECTED_RBF_100)
   297  				TxMutex.Unlock()
   298  				common.CountSafe("TxRejectedRBF100+")
   299  				return
   300  			}
   301  
   302  			chlds := ctx.GetAllChildren()
   303  			for _, ctx = range chlds {
   304  				if !ntx.trusted && ctx.Final {
   305  					RejectTx(ntx.Tx, TX_REJECTED_RBF_FINAL)
   306  					TxMutex.Unlock()
   307  					common.CountSafe("TxRejectedRBF_Final")
   308  					return
   309  				}
   310  
   311  				rbf_tx_list[ctx] = true
   312  
   313  				if !ntx.trusted && len(rbf_tx_list) > 100 {
   314  					RejectTx(ntx.Tx, TX_REJECTED_RBF_100)
   315  					TxMutex.Unlock()
   316  					common.CountSafe("TxRejectedRBF100+")
   317  					return
   318  				}
   319  			}
   320  		}
   321  
   322  		if txinmem, ok := TransactionsToSend[btc.BIdx(tx.TxIn[i].Input.Hash[:])]; ok {
   323  			if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) {
   324  				RejectTx(ntx.Tx, TX_REJECTED_BAD_INPUT)
   325  				TxMutex.Unlock()
   326  				common.CountSafe("TxRejectedBadInput")
   327  				return
   328  			}
   329  
   330  			if !ntx.trusted && !common.CFG.TXPool.AllowMemInputs {
   331  				RejectTx(ntx.Tx, TX_REJECTED_NOT_MINED)
   332  				TxMutex.Unlock()
   333  				common.CountSafe("TxRejectedMemInput1")
   334  				return
   335  			}
   336  
   337  			pos[i] = txinmem.TxOut[tx.TxIn[i].Input.Vout]
   338  			common.CountSafe("TxInputInMemory")
   339  			if frommem == nil {
   340  				frommem = make([]bool, len(tx.TxIn))
   341  			}
   342  			frommem[i] = true
   343  			frommemcnt++
   344  		} else {
   345  			pos[i] = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input)
   346  			if pos[i] == nil {
   347  				var newone bool
   348  
   349  				if !common.CFG.TXPool.AllowMemInputs {
   350  					RejectTx(ntx.Tx, TX_REJECTED_NOT_MINED)
   351  					TxMutex.Unlock()
   352  					common.CountSafe("TxRejectedMemInput2")
   353  					return
   354  				}
   355  
   356  				if rej, ok := TransactionsRejected[btc.BIdx(tx.TxIn[i].Input.Hash[:])]; ok {
   357  					if rej.Reason != TX_REJECTED_NO_TXOU || rej.Waiting4 == nil {
   358  						RejectTx(ntx.Tx, TX_REJECTED_NO_TXOU)
   359  						TxMutex.Unlock()
   360  						common.CountSafe("TxRejectedParentRej")
   361  						return
   362  					}
   363  					common.CountSafe("TxWait4ParentsParent")
   364  				}
   365  
   366  				// In this case, let's "save" it for later...
   367  				missingid := btc.NewUint256(tx.TxIn[i].Input.Hash[:])
   368  				nrtx := RejectTx(ntx.Tx, TX_REJECTED_NO_TXOU)
   369  
   370  				if nrtx != nil && nrtx.Tx != nil {
   371  					nrtx.Waiting4 = missingid
   372  					//nrtx.Tx = ntx.Tx
   373  
   374  					// Add to waiting list:
   375  					var rec *OneWaitingList
   376  					if rec, _ = WaitingForInputs[missingid.BIdx()]; rec == nil {
   377  						rec = new(OneWaitingList)
   378  						rec.TxID = missingid
   379  						rec.TxLen = uint32(len(ntx.Raw))
   380  						rec.Ids = make(map[BIDX]time.Time)
   381  						newone = true
   382  						WaitingForInputsSize += uint64(rec.TxLen)
   383  					}
   384  					rec.Ids[bidx] = time.Now()
   385  					WaitingForInputs[missingid.BIdx()] = rec
   386  				}
   387  
   388  				TxMutex.Unlock()
   389  				if newone {
   390  					common.CountSafe("TxRejectedNoInpNew")
   391  				} else {
   392  					common.CountSafe("TxRejectedNoInpOld")
   393  				}
   394  				return
   395  			} else {
   396  				if pos[i].WasCoinbase {
   397  					if common.Last.BlockHeight()+1-pos[i].BlockHeight < chain.COINBASE_MATURITY {
   398  						RejectTx(ntx.Tx, TX_REJECTED_CB_INMATURE)
   399  						TxMutex.Unlock()
   400  						common.CountSafe("TxRejectedCBInmature")
   401  						fmt.Println(tx.Hash.String(), "trying to spend inmature coinbase block", pos[i].BlockHeight, "at", common.Last.BlockHeight())
   402  						return
   403  					}
   404  				}
   405  			}
   406  		}
   407  		totinp += pos[i].Value
   408  	}
   409  
   410  	// Check if total output value does not exceed total input
   411  	for i := range tx.TxOut {
   412  		totout += tx.TxOut[i].Value
   413  	}
   414  
   415  	if totout > totinp {
   416  		RejectTx(ntx.Tx, TX_REJECTED_OVERSPEND)
   417  		TxMutex.Unlock()
   418  		if ntx.conn != nil {
   419  			ntx.conn.DoS("TxOverspend")
   420  		}
   421  		return
   422  	}
   423  
   424  	// Check for a proper fee
   425  	fee := totinp - totout
   426  	if !ntx.local && fee < (uint64(tx.VSize())*common.MinFeePerKB()/1000) { // do not check minimum fee for locally loaded txs
   427  		RejectTx(ntx.Tx, TX_REJECTED_LOW_FEE)
   428  		TxMutex.Unlock()
   429  		common.CountSafe("TxRejectedLowFee")
   430  		return
   431  	}
   432  
   433  	if rbf_tx_list != nil {
   434  		var totweight int
   435  		var totfees uint64
   436  
   437  		for ctx := range rbf_tx_list {
   438  			totweight += ctx.Weight()
   439  			totfees += ctx.Fee
   440  		}
   441  
   442  		if !ntx.local && totfees*uint64(tx.Weight()) >= fee*uint64(totweight) {
   443  			RejectTx(ntx.Tx, TX_REJECTED_RBF_LOWFEE)
   444  			TxMutex.Unlock()
   445  			common.CountSafe("TxRejectedRBFLowFee")
   446  			return
   447  		}
   448  	}
   449  
   450  	sigops := btc.WITNESS_SCALE_FACTOR * tx.GetLegacySigOpCount()
   451  
   452  	if !ntx.trusted { // Verify scripts
   453  		var wg sync.WaitGroup
   454  		var ver_err_cnt uint32
   455  		ver_flags := common.CurrentScriptFlags()
   456  
   457  		tx.Spent_outputs = pos
   458  		prev_dbg_err := script.DBG_ERR
   459  		script.DBG_ERR = false // keep quiet for incorrect txs
   460  		for i := range tx.TxIn {
   461  			wg.Add(1)
   462  			go func(i int, tx *btc.Tx) {
   463  				if !script.VerifyTxScript(tx.Spent_outputs[i].Pk_script,
   464  					&script.SigChecker{Amount: tx.Spent_outputs[i].Value, Idx: i, Tx: tx}, ver_flags) {
   465  					atomic.AddUint32(&ver_err_cnt, 1)
   466  				}
   467  				wg.Done()
   468  			}(i, tx)
   469  		}
   470  
   471  		wg.Wait()
   472  		script.DBG_ERR = prev_dbg_err
   473  
   474  		if ver_err_cnt > 0 {
   475  			// not moving it to rejected, but baning the peer
   476  			TxMutex.Unlock()
   477  			if ntx.conn != nil {
   478  				ntx.conn.DoS("TxScriptFail")
   479  			}
   480  			if len(rbf_tx_list) > 0 {
   481  				fmt.Println("RBF try", ver_err_cnt, "script(s) failed!")
   482  				fmt.Print("> ")
   483  			}
   484  			return
   485  		}
   486  	}
   487  
   488  	for i := range tx.TxIn {
   489  		if btc.IsP2SH(pos[i].Pk_script) {
   490  			sigops += btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(tx.TxIn[i].ScriptSig)
   491  		}
   492  		sigops += uint(tx.CountWitnessSigOps(i, pos[i].Pk_script))
   493  	}
   494  
   495  	if rbf_tx_list != nil {
   496  		for ctx := range rbf_tx_list {
   497  			// we dont remove with children because we have all of them on the list
   498  			ctx.Delete(false, TX_REJECTED_REPLACED)
   499  			common.CountSafe("TxRemovedByRBF")
   500  		}
   501  	}
   502  
   503  	rec := &OneTxToSend{Spent: spent, Volume: totinp, Local: ntx.local,
   504  		Fee: fee, Firstseen: time.Now(), Tx: tx, MemInputs: frommem, MemInputCnt: frommemcnt,
   505  		SigopsCost: uint64(sigops), Final: final, VerifyTime: time.Now().Sub(start_time)}
   506  
   507  	TransactionsToSend[bidx] = rec
   508  
   509  	if maxpoolsize := common.MaxMempoolSize(); maxpoolsize != 0 {
   510  		newsize := TransactionsToSendSize + uint64(len(rec.Raw))
   511  		if TransactionsToSendSize < maxpoolsize && newsize >= maxpoolsize {
   512  			expireTxsNow = true
   513  		}
   514  		TransactionsToSendSize = newsize
   515  	} else {
   516  		TransactionsToSendSize += uint64(len(rec.Raw))
   517  	}
   518  	TransactionsToSendWeight += uint64(rec.Tx.Weight())
   519  
   520  	for i := range spent {
   521  		SpentOutputs[spent[i]] = bidx
   522  	}
   523  
   524  	wtg := WaitingForInputs[bidx]
   525  	if wtg != nil {
   526  		defer RetryWaitingForInput(wtg) // Redo waiting txs when leaving this function
   527  	}
   528  
   529  	TxMutex.Unlock()
   530  	common.CountSafe("TxAccepted")
   531  
   532  	if frommem != nil && !common.GetBool(&common.CFG.TXRoute.MemInputs) {
   533  		// By default Gocoin does not route txs that spend unconfirmed inputs
   534  		rec.Blocked = TX_REJECTED_NOT_MINED
   535  		common.CountSafe("TxRouteNotMined")
   536  	} else if !ntx.trusted && rec.isRoutable() {
   537  		// do not automatically route loacally loaded txs
   538  		rec.Invsentcnt += NetRouteInvExt(1, &tx.Hash, ntx.conn, 1000*fee/uint64(len(ntx.Raw)))
   539  		common.CountSafe("TxRouteOK")
   540  	}
   541  
   542  	if ntx.conn != nil {
   543  		ntx.conn.Mutex.Lock()
   544  		ntx.conn.txsCur++
   545  		ntx.conn.X.TxsReceived++
   546  		ntx.conn.Mutex.Unlock()
   547  	}
   548  
   549  	accepted = true
   550  	return
   551  }
   552  
   553  func (rec *OneTxToSend) isRoutable() bool {
   554  	if !common.CFG.TXRoute.Enabled {
   555  		common.CountSafe("TxRouteDisabled")
   556  		rec.Blocked = TX_REJECTED_DISABLED
   557  		return false
   558  	}
   559  	if rec.Weight() > 4*int(common.GetUint32(&common.CFG.TXRoute.MaxTxSize)) {
   560  		common.CountSafe("TxRouteTooBig")
   561  		rec.Blocked = TX_REJECTED_TOO_BIG
   562  		return false
   563  	}
   564  	if rec.Fee < (uint64(rec.VSize()) * common.RouteMinFeePerKB() / 1000) {
   565  		common.CountSafe("TxRouteLowFee")
   566  		rec.Blocked = TX_REJECTED_LOW_FEE
   567  		return false
   568  	}
   569  	return true
   570  }
   571  
   572  func RetryWaitingForInput(wtg *OneWaitingList) {
   573  	for k := range wtg.Ids {
   574  		pendtxrcv := &TxRcvd{Tx: TransactionsRejected[k].Tx}
   575  		if HandleNetTx(pendtxrcv, true) {
   576  			common.CountSafe("TxRetryAccepted")
   577  		} else {
   578  			common.CountSafe("TxRetryRejected")
   579  		}
   580  	}
   581  }
   582  
   583  // Delete deletes the tx from the mempool.
   584  // Deletes all the children as well if with_children is true.
   585  // If reason is not zero, add the deleted txs to the rejected list.
   586  // Make sure to call it with locked TxMutex.
   587  func (tx *OneTxToSend) Delete(with_children bool, reason byte) {
   588  	if with_children {
   589  		// remove all the children that are spending from tx
   590  		var po btc.TxPrevOut
   591  		po.Hash = tx.Hash.Hash
   592  		for po.Vout = 0; po.Vout < uint32(len(tx.TxOut)); po.Vout++ {
   593  			if so, ok := SpentOutputs[po.UIdx()]; ok {
   594  				if child, ok := TransactionsToSend[so]; ok {
   595  					child.Delete(true, reason)
   596  				}
   597  			}
   598  		}
   599  	}
   600  
   601  	for i := range tx.Spent {
   602  		delete(SpentOutputs, tx.Spent[i])
   603  	}
   604  
   605  	TransactionsToSendSize -= uint64(len(tx.Raw))
   606  	TransactionsToSendWeight -= uint64(tx.Weight())
   607  	delete(TransactionsToSend, tx.Hash.BIdx())
   608  	if reason != 0 {
   609  		RejectTx(tx.Tx, reason)
   610  	}
   611  }
   612  
   613  func txChecker(tx *btc.Tx) bool {
   614  	TxMutex.Lock()
   615  	rec, ok := TransactionsToSend[tx.Hash.BIdx()]
   616  	TxMutex.Unlock()
   617  	if ok && rec.Local {
   618  		common.CountSafe("TxScrOwn")
   619  		return false // Assume own txs as non-trusted
   620  	}
   621  	if ok {
   622  		ok = tx.WTxID().Equal(rec.WTxID())
   623  		if !ok {
   624  			//println("wTXID mismatch at", tx.Hash.String(), tx.WTxID().String(), rec.WTxID().String())
   625  			common.CountSafe("TxScrSWErr")
   626  		}
   627  	}
   628  	if ok {
   629  		common.CountSafe("TxScrBoosted")
   630  	} else {
   631  		common.CountSafe("TxScrMissed")
   632  	}
   633  	return ok
   634  }
   635  
   636  // Make sure to call it with locked TxMutex
   637  func deleteRejected(bidx BIDX) {
   638  	if tr, ok := TransactionsRejected[bidx]; ok {
   639  		if tr.Waiting4 != nil {
   640  			w4i, _ := WaitingForInputs[tr.Waiting4.BIdx()]
   641  			delete(w4i.Ids, bidx)
   642  			if len(w4i.Ids) == 0 {
   643  				WaitingForInputsSize -= uint64(w4i.TxLen)
   644  				delete(WaitingForInputs, tr.Waiting4.BIdx())
   645  			}
   646  		}
   647  		if tr.Tx != nil {
   648  			TransactionsRejectedSize -= uint64(TransactionsRejected[bidx].Size)
   649  		}
   650  		delete(TransactionsRejected, bidx)
   651  	}
   652  }
   653  
   654  func RemoveFromRejected(hash *btc.Uint256) {
   655  	TxMutex.Lock()
   656  	deleteRejected(hash.BIdx())
   657  	TxMutex.Unlock()
   658  }
   659  
   660  func SubmitLocalTx(tx *btc.Tx, rawtx []byte) bool {
   661  	return HandleNetTx(&TxRcvd{Tx: tx, trusted: true, local: true}, true)
   662  }
   663  
   664  func init() {
   665  	chain.TrustedTxChecker = txChecker
   666  }