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 }