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

     1  package network
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/piotrnar/gocoin/client/common"
     9  	"github.com/piotrnar/gocoin/lib/btc"
    10  )
    11  
    12  func (rec *OneTxToSend) IIdx(key uint64) int {
    13  	for i, o := range rec.TxIn {
    14  		if o.Input.UIdx() == key {
    15  			return i
    16  		}
    17  	}
    18  	return -1
    19  }
    20  
    21  // UnMarkChildrenForMem clears the MemInput flag of all the children (used when a tx is mined).
    22  func (tx *OneTxToSend) UnMarkChildrenForMem() {
    23  	// Go through all the tx's outputs and unmark MemInputs in txs that have been spending it
    24  	var po btc.TxPrevOut
    25  	po.Hash = tx.Hash.Hash
    26  	for po.Vout = 0; po.Vout < uint32(len(tx.TxOut)); po.Vout++ {
    27  		uidx := po.UIdx()
    28  		if val, ok := SpentOutputs[uidx]; ok {
    29  			if rec, _ := TransactionsToSend[val]; rec != nil {
    30  				if rec.MemInputs == nil {
    31  					common.CountSafe("TxMinedMeminER1")
    32  					fmt.Println("WTF?", po.String(), "just mined in", rec.Hash.String(), "- not marked as mem")
    33  					continue
    34  				}
    35  				idx := rec.IIdx(uidx)
    36  				if idx < 0 {
    37  					common.CountSafe("TxMinedMeminER2")
    38  					fmt.Println("WTF?", po.String(), " just mined. Was in SpentOutputs & mempool, but DUPA")
    39  					continue
    40  				}
    41  				rec.MemInputs[idx] = false
    42  				rec.MemInputCnt--
    43  				common.CountSafe("TxMinedMeminOut")
    44  				if rec.MemInputCnt == 0 {
    45  					common.CountSafe("TxMinedMeminTx")
    46  					rec.MemInputs = nil
    47  				}
    48  			} else {
    49  				common.CountSafe("TxMinedMeminERR")
    50  				fmt.Println("WTF?", po.String(), " in SpentOutputs, but not in mempool")
    51  			}
    52  		}
    53  	}
    54  }
    55  
    56  // tx_mined is called for each tx mined in a new block.
    57  func tx_mined(tx *btc.Tx) (wtg *OneWaitingList) {
    58  	h := tx.Hash
    59  	if rec, ok := TransactionsToSend[h.BIdx()]; ok {
    60  		common.CountSafe("TxMinedToSend")
    61  		rec.UnMarkChildrenForMem()
    62  		rec.Delete(false, 0)
    63  	}
    64  	if mr, ok := TransactionsRejected[h.BIdx()]; ok {
    65  		if mr.Tx != nil {
    66  			common.CountSafe(fmt.Sprint("TxMinedROK-", mr.Reason))
    67  		} else {
    68  			common.CountSafe(fmt.Sprint("TxMinedRNO-", mr.Reason))
    69  		}
    70  		deleteRejected(h.BIdx())
    71  	}
    72  	if _, ok := TransactionsPending[h.BIdx()]; ok {
    73  		common.CountSafe("TxMinedPending")
    74  		delete(TransactionsPending, h.BIdx())
    75  	}
    76  
    77  	// Go through all the inputs and make sure we are not leaving them in SpentOutputs
    78  	for i := range tx.TxIn {
    79  		idx := tx.TxIn[i].Input.UIdx()
    80  		if val, ok := SpentOutputs[idx]; ok {
    81  			if rec, _ := TransactionsToSend[val]; rec != nil {
    82  				// if we got here, the txs has been Malleabled
    83  				if rec.Local {
    84  					common.CountSafe("TxMinedMalleabled")
    85  					fmt.Println("Input from own ", rec.Tx.Hash.String(), " mined in ", tx.Hash.String())
    86  				} else {
    87  					common.CountSafe("TxMinedOtherSpend")
    88  				}
    89  				rec.Delete(true, 0)
    90  			} else {
    91  				common.CountSafe("TxMinedSpentERROR")
    92  				fmt.Println("WTF? Input from ", rec.Tx.Hash.String(), " in mem-spent, but tx not in the mem-pool")
    93  			}
    94  			delete(SpentOutputs, idx)
    95  		}
    96  	}
    97  
    98  	wtg = WaitingForInputs[h.BIdx()]
    99  	return
   100  }
   101  
   102  // BlockMined removes all the block's tx from the mempool.
   103  func BlockMined(bl *btc.Block) {
   104  	wtgs := make([]*OneWaitingList, len(bl.Txs)-1)
   105  	var wtg_cnt int
   106  	TxMutex.Lock()
   107  	for i := 1; i < len(bl.Txs); i++ {
   108  		wtg := tx_mined(bl.Txs[i])
   109  		if wtg != nil {
   110  			wtgs[wtg_cnt] = wtg
   111  			wtg_cnt++
   112  		}
   113  	}
   114  	TxMutex.Unlock()
   115  
   116  	// Try to redo waiting txs
   117  	if wtg_cnt > 0 {
   118  		common.CountSafeAdd("TxMinedGotInput", uint64(wtg_cnt))
   119  		for _, wtg := range wtgs[:wtg_cnt] {
   120  			RetryWaitingForInput(wtg)
   121  		}
   122  	}
   123  
   124  	expireTxsNow = true
   125  }
   126  
   127  // MarkChildrenForMem sets the MemInput flag of all the children (used when a tx is mined).
   128  func MarkChildrenForMem(tx *btc.Tx) {
   129  	// Go through all the tx's outputs and mark MemInputs in txs that have been spending it
   130  	var po btc.TxPrevOut
   131  	po.Hash = tx.Hash.Hash
   132  	for po.Vout = 0; po.Vout < uint32(len(tx.TxOut)); po.Vout++ {
   133  		uidx := po.UIdx()
   134  		if val, ok := SpentOutputs[uidx]; ok {
   135  			if rec, _ := TransactionsToSend[val]; rec != nil {
   136  				if rec.MemInputs == nil {
   137  					rec.MemInputs = make([]bool, len(rec.TxIn))
   138  				}
   139  				idx := rec.IIdx(uidx)
   140  				rec.MemInputs[idx] = true
   141  				rec.MemInputCnt++
   142  				common.CountSafe("TxPutBackMemIn")
   143  			} else {
   144  				common.CountSafe("TxPutBackMeminERR")
   145  				fmt.Println("MarkChildrenForMem WTF?", po.String(), " in SpentOutputs, but not in mempool")
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  func BlockUndone(bl *btc.Block) {
   152  	var cnt int
   153  	for _, tx := range bl.Txs[1:] {
   154  		// put it back into the mempool
   155  		ntx := &TxRcvd{Tx: tx, trusted: true}
   156  
   157  		if NeedThisTx(&ntx.Hash, nil) {
   158  			if HandleNetTx(ntx, true) {
   159  				common.CountSafe("TxPutBackOK")
   160  				cnt++
   161  			} else {
   162  				common.CountSafe("TxPutBackFail")
   163  			}
   164  		} else {
   165  			common.CountSafe("TxPutBackNoNeed")
   166  		}
   167  
   168  		// TODO: make sure to set MemInputs of ones using it back to true (issue #58)
   169  		MarkChildrenForMem(tx)
   170  	}
   171  	if cnt != len(bl.Txs)-1 {
   172  		println("WARNING: network.BlockUndone("+bl.Hash.String()+") - ", cnt, "of", len(bl.Txs)-1, "txs put back")
   173  	}
   174  }
   175  
   176  func (c *OneConnection) SendGetMP() error {
   177  	if len(c.GetMP) == 0 {
   178  		// TODO: remove it at some point (should not be happening)
   179  		println("ERROR: SendGetMP() called with no GetMP lock")
   180  		return nil
   181  	}
   182  	b := new(bytes.Buffer)
   183  	TxMutex.Lock()
   184  	if TransactionsToSendSize > common.MaxMempoolSize()>>1 {
   185  		// Don't send "getmp" messages if we have more than 50% of MaxMempoolSize() used
   186  		//fmt.Println("Mempool more than half full - not sending getmp message -", TransactionsToSendSize>>20, "/", common.MaxMempoolSize()>>20)
   187  		TxMutex.Unlock()
   188  		c.cntInc("GetMPHold")
   189  		return errors.New("SendGetMP: Mempool more than half full")
   190  	}
   191  	tcnt := len(TransactionsToSend) + len(TransactionsRejected)
   192  	if tcnt > MAX_GETMP_TXS {
   193  		fmt.Println("Too many transactions in the current pool", tcnt, "/", MAX_GETMP_TXS)
   194  		tcnt = MAX_GETMP_TXS
   195  	}
   196  	btc.WriteVlen(b, uint64(tcnt))
   197  	var cnt int
   198  	for k := range TransactionsToSend {
   199  		b.Write(k[:])
   200  		cnt++
   201  		if cnt == MAX_GETMP_TXS {
   202  			break
   203  		}
   204  	}
   205  	for k := range TransactionsRejected {
   206  		b.Write(k[:])
   207  		cnt++
   208  		if cnt == MAX_GETMP_TXS {
   209  			break
   210  		}
   211  	}
   212  	TxMutex.Unlock()
   213  	return c.SendRawMsg("getmp", b.Bytes())
   214  }
   215  
   216  func (c *OneConnection) ProcessGetMP(pl []byte) {
   217  	br := bytes.NewBuffer(pl)
   218  
   219  	cnt, er := btc.ReadVLen(br)
   220  	if er != nil {
   221  		println("getmp message does not have the length field")
   222  		c.DoS("GetMPError1")
   223  		return
   224  	}
   225  
   226  	has_this_one := make(map[BIDX]bool, cnt)
   227  	for i := 0; i < int(cnt); i++ {
   228  		var idx BIDX
   229  		if n, _ := br.Read(idx[:]); n != len(idx) {
   230  			println("getmp message too short")
   231  			c.DoS("GetMPError2")
   232  			return
   233  		}
   234  		has_this_one[idx] = true
   235  	}
   236  
   237  	var data_sent_so_far int
   238  	var redo [1]byte
   239  
   240  	TxMutex.Lock()
   241  	for k, v := range TransactionsToSend {
   242  		c.Mutex.Lock()
   243  		bts := c.BytesToSent()
   244  		c.Mutex.Unlock()
   245  		if bts > SendBufSize/4 {
   246  			redo[0] = 1
   247  			break
   248  		}
   249  		if !has_this_one[k] {
   250  			c.SendRawMsg("tx", v.Raw)
   251  			data_sent_so_far += 24 + len(v.Raw)
   252  		}
   253  	}
   254  	TxMutex.Unlock()
   255  
   256  	c.SendRawMsg("getmpdone", redo[:])
   257  }