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 }