github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/usif/textui/transactions.go (about)

     1  package textui
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/piotrnar/gocoin/client/common"
    12  	"github.com/piotrnar/gocoin/client/network"
    13  	"github.com/piotrnar/gocoin/client/usif"
    14  	"github.com/piotrnar/gocoin/lib/btc"
    15  )
    16  
    17  func load_tx(par string) {
    18  	if par == "" {
    19  		fmt.Println("Specify a name of a transaction file")
    20  		return
    21  	}
    22  	f, e := os.Open(par)
    23  	if e != nil {
    24  		println(e.Error())
    25  		return
    26  	}
    27  	n, _ := f.Seek(0, io.SeekStart)
    28  	f.Seek(0, io.SeekEnd)
    29  	buf := make([]byte, n)
    30  	f.Read(buf)
    31  	f.Close()
    32  	fmt.Println(usif.LoadRawTx(buf))
    33  }
    34  
    35  func send_tx(par string) {
    36  	txid := btc.NewUint256FromString(par)
    37  	if txid == nil {
    38  		fmt.Println("You must specify a valid transaction ID for this command.")
    39  		list_txs("")
    40  		return
    41  	}
    42  	network.TxMutex.Lock()
    43  	if ptx, ok := network.TransactionsToSend[txid.BIdx()]; ok {
    44  		network.TxMutex.Unlock()
    45  		cnt := network.NetRouteInv(1, txid, nil)
    46  		ptx.Invsentcnt += cnt
    47  		fmt.Println("INV for TxID", txid.String(), "sent to", cnt, "node(s)")
    48  		fmt.Println("If it does not appear in the chain, you may want to redo it.")
    49  	} else {
    50  		network.TxMutex.Unlock()
    51  		fmt.Println("No such transaction ID in the memory pool.")
    52  		list_txs("")
    53  	}
    54  }
    55  
    56  func send1_tx(par string) {
    57  	txid := btc.NewUint256FromString(par)
    58  	if txid == nil {
    59  		fmt.Println("You must specify a valid transaction ID for this command.")
    60  		list_txs("")
    61  		return
    62  	}
    63  	network.TxMutex.Lock()
    64  	if ptx, ok := network.TransactionsToSend[txid.BIdx()]; ok {
    65  		network.TxMutex.Unlock()
    66  		usif.SendInvToRandomPeer(1, txid)
    67  		ptx.Invsentcnt++
    68  		fmt.Println("INV for TxID", txid.String(), "sent to a random node")
    69  		fmt.Println("If it does not appear in the chain, you may want to redo it.")
    70  	} else {
    71  		network.TxMutex.Unlock()
    72  		fmt.Println("No such transaction ID in the memory pool.")
    73  		list_txs("")
    74  	}
    75  }
    76  
    77  func del_tx(par string) {
    78  	txid := btc.NewUint256FromString(par)
    79  	if txid == nil {
    80  		fmt.Println("You must specify a valid transaction ID for this command.")
    81  		list_txs("")
    82  		return
    83  	}
    84  	network.TxMutex.Lock()
    85  	defer network.TxMutex.Unlock()
    86  	tx, ok := network.TransactionsToSend[txid.BIdx()]
    87  	if !ok {
    88  		network.TxMutex.Unlock()
    89  		fmt.Println("No such transaction ID in the memory pool.")
    90  		list_txs("")
    91  		return
    92  	}
    93  	tx.Delete(true, 0)
    94  	fmt.Println("Transaction", txid.String(), "and all its children removed from the memory pool")
    95  }
    96  
    97  func dec_tx(par string) {
    98  	txid := btc.NewUint256FromString(par)
    99  	if txid == nil {
   100  		fmt.Println("You must specify a valid transaction ID for this command.")
   101  		list_txs("")
   102  		return
   103  	}
   104  	if tx, ok := network.TransactionsToSend[txid.BIdx()]; ok {
   105  		s, _, _, _, _ := usif.DecodeTx(tx.Tx)
   106  		fmt.Println(s)
   107  	} else {
   108  		fmt.Println("No such transaction ID in the memory pool.")
   109  	}
   110  }
   111  
   112  func save_tx(par string) {
   113  	txid := btc.NewUint256FromString(par)
   114  	if txid == nil {
   115  		fmt.Println("You must specify a valid transaction ID for this command.")
   116  		return
   117  	}
   118  	if tx, ok := network.TransactionsToSend[txid.BIdx()]; ok {
   119  		fn := tx.Hash.String() + ".tx"
   120  		ioutil.WriteFile(fn, tx.Raw, 0600)
   121  		fmt.Println("Saved to", fn)
   122  	} else {
   123  		fmt.Println("No such transaction ID in the memory pool.")
   124  	}
   125  }
   126  
   127  func mempool_stats(par string) {
   128  	fmt.Print(usif.MemoryPoolFees())
   129  }
   130  
   131  func list_txs(par string) {
   132  	var er error
   133  	var maxweigth uint64
   134  	maxweigth, er = strconv.ParseUint(par, 10, 64)
   135  	if er != nil || maxweigth > 4e6 {
   136  		maxweigth = 4e6
   137  	}
   138  	fmt.Println("Listing txs in mempool up to weight:", maxweigth)
   139  	cnt := 0
   140  	network.TxMutex.Lock()
   141  	defer network.TxMutex.Unlock()
   142  
   143  	sorted := network.GetSortedMempool()
   144  
   145  	var totlen, totweigth uint64
   146  	for cnt = 0; cnt < len(sorted); cnt++ {
   147  		v := sorted[cnt]
   148  		totweigth += uint64(v.Weight())
   149  		totlen += uint64(len(v.Raw))
   150  
   151  		if totweigth > maxweigth {
   152  			break
   153  		}
   154  
   155  		var snt string
   156  		if v.SentCnt == 0 {
   157  			snt += "tx never"
   158  		} else {
   159  			snt += fmt.Sprintf("tx %d times, last %s ago", v.SentCnt,
   160  				time.Since(v.Lastsent).String())
   161  		}
   162  		if v.Local {
   163  			snt += " *OWN*"
   164  		}
   165  
   166  		fmt.Printf("%5d) ...%7d/%7d %s %6d bytes / %4.1fspb - INV snt %d times, %s\n",
   167  			cnt, totlen, totweigth, v.Tx.Hash.String(), len(v.Raw), v.SPB(), v.Invsentcnt, snt)
   168  
   169  	}
   170  }
   171  
   172  func baned_txs(par string) {
   173  	fmt.Println("Rejected transactions:")
   174  	cnt := 0
   175  	network.TxMutex.Lock()
   176  	for k, v := range network.TransactionsRejected {
   177  		cnt++
   178  		fmt.Println("", cnt, btc.NewUint256(k[:]).String(), "-", v.Size, "bytes",
   179  			"-", v.Reason, "-", time.Since(v.Time).String(), "ago")
   180  	}
   181  	network.TxMutex.Unlock()
   182  }
   183  
   184  func send_all_tx(par string) {
   185  	var tmp []*network.OneTxToSend
   186  	network.TxMutex.Lock()
   187  	for _, v := range network.TransactionsToSend {
   188  		if v.Local {
   189  			tmp = append(tmp, v)
   190  		}
   191  	}
   192  	network.TxMutex.Unlock()
   193  	for _, v := range tmp {
   194  		cnt := network.NetRouteInv(1, &v.Tx.Hash, nil)
   195  		v.Invsentcnt += cnt
   196  		fmt.Println("INV for TxID", v.Tx.Hash.String(), "sent to", cnt, "node(s)")
   197  	}
   198  }
   199  
   200  func save_mempool(par string) {
   201  	network.MempoolSave(true)
   202  }
   203  
   204  func check_txs(par string) {
   205  	network.TxMutex.Lock()
   206  	err := network.MempoolCheck()
   207  	network.TxMutex.Unlock()
   208  	if !err {
   209  		fmt.Println("Memory Pool seems to be consistent")
   210  	}
   211  }
   212  
   213  func load_mempool(par string) {
   214  	if par == "" {
   215  		par = common.GocoinHomeDir + "mempool.dmp"
   216  	}
   217  	var abort bool
   218  	__exit := make(chan bool)
   219  	__done := make(chan bool)
   220  	go func() {
   221  		for {
   222  			select {
   223  			case s := <-common.KillChan:
   224  				fmt.Println(s)
   225  				abort = true
   226  			case <-__exit:
   227  				__done <- true
   228  				return
   229  			}
   230  		}
   231  	}()
   232  	fmt.Println("Press Ctrl+C to abort...")
   233  	network.MempoolLoadNew(par, &abort)
   234  	__exit <- true
   235  	<-__done
   236  	if abort {
   237  		fmt.Println("Aborted")
   238  	}
   239  }
   240  
   241  func get_mempool(par string) {
   242  	conid, e := strconv.ParseUint(par, 10, 32)
   243  	if e != nil {
   244  		fmt.Println("Specify ID of the peer")
   245  		return
   246  	}
   247  
   248  	fmt.Println("Getting mempool from connection ID", conid, "...")
   249  	network.GetMP(uint32(conid))
   250  }
   251  
   252  func init() {
   253  	newUi("txload tx", true, load_tx, "Load transaction data from the given file, decode it and store in memory")
   254  	newUi("txsend stx", true, send_tx, "Broadcast transaction from memory pool (identified by a given <txid>)")
   255  	newUi("tx1send stx1", true, send1_tx, "Broadcast transaction to a single random peer (identified by a given <txid>)")
   256  	newUi("txsendall stxa", true, send_all_tx, "Broadcast all the transactions (what you see after ltx)")
   257  	newUi("txdel dtx", true, del_tx, "Remove a transaction from memory pool (identified by a given <txid>)")
   258  	newUi("txdecode td", true, dec_tx, "Decode a transaction from memory pool (identified by a given <txid>)")
   259  	newUi("txlist ltx", true, list_txs, "List all the transaction loaded into memory pool up to <max_weigth> (default 4M)")
   260  	newUi("txlistban ltxb", true, baned_txs, "List the transaction that we have rejected")
   261  	newUi("mempool mp", true, mempool_stats, "Show the mempool statistics")
   262  	newUi("savetx txsave", true, save_tx, "Save raw transaction from memory pool to disk")
   263  	newUi("txmpsave mps", true, save_mempool, "Save memory pool to disk")
   264  	newUi("txcheck txc", true, check_txs, "Verify consistency of mempool")
   265  	newUi("txmpload mpl", true, load_mempool, "Load transaction from the given file (must be in mempool.dmp format)")
   266  	newUi("getmp mpg", true, get_mempool, "Send getmp message to the peer with the given ID")
   267  }