github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/usif/usif.go (about) 1 package usif 2 3 import ( 4 "encoding/binary" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "math/rand" 9 "sync" 10 "time" 11 12 "github.com/piotrnar/gocoin/client/common" 13 "github.com/piotrnar/gocoin/client/network" 14 "github.com/piotrnar/gocoin/client/network/peersdb" 15 "github.com/piotrnar/gocoin/lib/btc" 16 "github.com/piotrnar/gocoin/lib/others/qdb" 17 "github.com/piotrnar/gocoin/lib/others/sys" 18 "github.com/piotrnar/gocoin/lib/script" 19 ) 20 21 type OneUiReq struct { 22 Param string 23 Handler func(pars string) 24 Done sync.WaitGroup 25 } 26 27 // A thread that wants to lock the main thread calls: 28 // In.Add(1); Out.Add(1); [put msg into LocksChan]; In.Wait(); [do synchronized code]; Out.Done() 29 // The main thread, upon receiving the message, does: 30 // In.Done(); Out.Wait(); 31 type OneLock struct { 32 In sync.WaitGroup // main thread calls Done() on this one and then Stop.Wait() 33 Out sync.WaitGroup // the synchronized thread calls Done 34 } 35 36 var ( 37 UiChannel chan *OneUiReq = make(chan *OneUiReq, 1) 38 LocksChan chan *OneLock = make(chan *OneLock, 1) 39 40 FetchingBalances sys.SyncBool 41 Exit_now sys.SyncBool 42 ) 43 44 func DecodeTxSops(tx *btc.Tx) (s string, missinginp bool, totinp, totout uint64, sigops uint, e error) { 45 s += fmt.Sprintln("Transaction details (for your information):") 46 s += fmt.Sprintln(len(tx.TxIn), "Input(s):") 47 sigops = btc.WITNESS_SCALE_FACTOR * tx.GetLegacySigOpCount() 48 tx.Spent_outputs = make([]*btc.TxOut, len(tx.TxIn)) 49 ss := make([]string, len(tx.TxIn)) 50 for i := range tx.TxIn { 51 ss[i] += fmt.Sprintf(" %3d %s seq=0x%x", i, tx.TxIn[i].Input.String(), tx.TxIn[i].Sequence) 52 var po *btc.TxOut 53 54 inpid := btc.NewUint256(tx.TxIn[i].Input.Hash[:]) 55 if txinmem, ok := network.TransactionsToSend[inpid.BIdx()]; ok { 56 ss[i] += fmt.Sprint(" mempool") 57 if int(tx.TxIn[i].Input.Vout) >= len(txinmem.TxOut) { 58 ss[i] += fmt.Sprintf(" - Vout TOO BIG (%d/%d)!", int(tx.TxIn[i].Input.Vout), len(txinmem.TxOut)) 59 } else { 60 po = txinmem.TxOut[tx.TxIn[i].Input.Vout] 61 } 62 } else { 63 po = common.BlockChain.Unspent.UnspentGet(&tx.TxIn[i].Input) 64 if po != nil { 65 ss[i] += fmt.Sprintf("%8d", po.BlockHeight) 66 } 67 } 68 tx.Spent_outputs[i] = po 69 } 70 for i := range tx.TxIn { 71 s += ss[i] 72 po := tx.Spent_outputs[i] 73 if po != nil { 74 ok := script.VerifyTxScript(po.Pk_script, &script.SigChecker{Amount: po.Value, Idx: i, Tx: tx}, script.VER_P2SH|script.VER_DERSIG|script.VER_CLTV) 75 if !ok { 76 s += fmt.Sprintln("\nERROR: The transacion does not have a valid signature.") 77 e = errors.New("Invalid signature") 78 } 79 totinp += po.Value 80 81 ads := "???" 82 if ad := btc.NewAddrFromPkScript(po.Pk_script, common.Testnet); ad != nil { 83 ads = ad.String() 84 } 85 s += fmt.Sprintf("\n\t%15.8f BTC @ %s", float64(po.Value)/1e8, ads) 86 87 if btc.IsP2SH(po.Pk_script) { 88 so := btc.WITNESS_SCALE_FACTOR * btc.GetP2SHSigOpCount(tx.TxIn[i].ScriptSig) 89 s += fmt.Sprintf(" + %d sigops", so) 90 sigops += so 91 } 92 93 swo := tx.CountWitnessSigOps(i, po.Pk_script) 94 if swo > 0 { 95 s += fmt.Sprintf(" + %d segops", swo) 96 sigops += swo 97 } 98 99 s += "\n" 100 } else { 101 s += fmt.Sprintln(" - UNKNOWN INPUT") 102 missinginp = true 103 } 104 } 105 s += fmt.Sprintln(len(tx.TxOut), "Output(s):") 106 for i := range tx.TxOut { 107 totout += tx.TxOut[i].Value 108 adr := btc.NewAddrFromPkScript(tx.TxOut[i].Pk_script, common.Testnet) 109 if adr != nil { 110 s += fmt.Sprintf(" %15.8f BTC to adr %s\n", float64(tx.TxOut[i].Value)/1e8, adr.String()) 111 } else { 112 s += fmt.Sprintf(" %15.8f BTC to scr %s\n", float64(tx.TxOut[i].Value)/1e8, hex.EncodeToString(tx.TxOut[i].Pk_script)) 113 } 114 } 115 if missinginp { 116 s += fmt.Sprintln("WARNING: There are missing inputs and we cannot calc input BTC amount.") 117 s += fmt.Sprintln("If there is somethign wrong with this transaction, you can loose money...") 118 } else { 119 s += fmt.Sprintf("All OK: %.8f BTC in -> %.8f BTC out, with %.8f BTC fee (%.2f SPB)\n", float64(totinp)/1e8, 120 float64(totout)/1e8, float64(totinp-totout)/1e8, float64(totinp-totout)/float64(tx.VSize())) 121 } 122 123 s += fmt.Sprintln("ECDSA sig operations : ", sigops) 124 125 return 126 } 127 128 func DecodeTx(tx *btc.Tx) (s string, missinginp bool, totinp, totout uint64, e error) { 129 s, missinginp, totinp, totout, _, e = DecodeTxSops(tx) 130 return 131 } 132 133 func LoadRawTx(buf []byte) (s string) { 134 txd, er := hex.DecodeString(string(buf)) 135 if er != nil { 136 txd = buf 137 } 138 139 // At this place we should have raw transaction in txd 140 tx, le := btc.NewTx(txd) 141 if tx == nil || le != len(txd) { 142 s += fmt.Sprintln("Could not decode transaction file or it has some extra data") 143 return 144 } 145 tx.SetHash(txd) 146 147 s, _, _, _, _ = DecodeTx(tx) 148 149 network.RemoveFromRejected(&tx.Hash) // in case we rejected it eariler, to try it again as trusted 150 151 if why := network.NeedThisTxExt(&tx.Hash, nil); why != 0 { 152 s += fmt.Sprintln("Transaction not needed or not wanted", why) 153 network.TxMutex.Lock() 154 if t2s := network.TransactionsToSend[tx.Hash.BIdx()]; t2s != nil { 155 t2s.Local = true // make as own (if not needed) 156 } 157 network.TxMutex.Unlock() 158 return 159 } 160 161 if !network.SubmitLocalTx(tx, txd) { 162 network.TxMutex.Lock() 163 rr := network.TransactionsRejected[tx.Hash.BIdx()] 164 network.TxMutex.Unlock() 165 if rr != nil { 166 s += fmt.Sprintln("Transaction rejected", rr.Reason) 167 } else { 168 s += fmt.Sprintln("Transaction rejected in a weird way") 169 } 170 return 171 } 172 173 network.TxMutex.Lock() 174 _, ok := network.TransactionsToSend[tx.Hash.BIdx()] 175 network.TxMutex.Unlock() 176 if ok { 177 s += fmt.Sprintln("Transaction added to the memory pool. You can broadcast it now.") 178 } else { 179 s += fmt.Sprintln("Transaction not rejected, but also not accepted - very strange!") 180 } 181 182 return 183 } 184 185 func SendInvToRandomPeer(typ uint32, h *btc.Uint256) { 186 common.CountSafe(fmt.Sprint("NetSendOneInv", typ)) 187 188 // Prepare the inv 189 inv := new([36]byte) 190 binary.LittleEndian.PutUint32(inv[0:4], typ) 191 copy(inv[4:36], h.Bytes()) 192 193 // Append it to PendingInvs in a random connection 194 network.Mutex_net.Lock() 195 idx := rand.Intn(len(network.OpenCons)) 196 var cnt int 197 for _, v := range network.OpenCons { 198 if idx == cnt { 199 v.Mutex.Lock() 200 v.PendingInvs = append(v.PendingInvs, inv) 201 v.Mutex.Unlock() 202 break 203 } 204 cnt++ 205 } 206 network.Mutex_net.Unlock() 207 return 208 } 209 210 func GetNetworkHashRateNum() float64 { 211 hours := common.CFG.Stat.HashrateHrs 212 common.Last.Mutex.Lock() 213 end := common.Last.Block 214 common.Last.Mutex.Unlock() 215 now := time.Now().Unix() 216 cnt := 0 217 var diff float64 218 for ; end != nil; cnt++ { 219 if now-int64(end.Timestamp()) > int64(hours)*3600 { 220 break 221 } 222 diff += btc.GetDifficulty(end.Bits()) 223 end = end.Parent 224 } 225 if cnt == 0 { 226 return 0 227 } 228 diff /= float64(cnt) 229 bph := float64(cnt) / float64(hours) 230 return bph / 6 * diff * 7158278.826667 231 } 232 233 func ExecUiReq(req *OneUiReq) { 234 if FetchingBalances.Get() { 235 fmt.Println("Client is currently busy fetching wallet balance.\nYour command has been queued and will execute soon.") 236 } 237 238 //fmt.Println("main.go last seen in line", common.BusyIn()) 239 sta := time.Now().UnixNano() 240 req.Done.Add(1) 241 UiChannel <- req 242 go func() { 243 req.Done.Wait() 244 sto := time.Now().UnixNano() 245 fmt.Printf("Ready in %.3fs\n", float64(sto-sta)/1e9) 246 fmt.Print("> ") 247 }() 248 } 249 250 func MemoryPoolFees() (res string) { 251 res = fmt.Sprintln("Content of mempool sorted by fee's SPB:") 252 network.TxMutex.Lock() 253 defer network.TxMutex.Unlock() 254 255 sorted := network.GetSortedMempoolNew() 256 257 var totlen, rawlen uint64 258 for cnt := 0; cnt < len(sorted); cnt++ { 259 v := sorted[cnt] 260 newlen := totlen + uint64(v.VSize()) 261 rawlen += uint64(len(v.Raw)) 262 263 if cnt == 0 || cnt+1 == len(sorted) || (newlen/100e3) != (totlen/100e3) { 264 spb := float64(v.Fee) / float64(v.VSize()) 265 toprint := newlen 266 if cnt != 0 && cnt+1 != len(sorted) { 267 toprint = newlen / 100e3 * 100e3 268 } 269 res += fmt.Sprintf(" %9d / %9d bytes, %6d txs @ fee %8.1f Satoshis / byte\n", toprint, rawlen, cnt+1, spb) 270 } 271 if (newlen / 1e6) != (totlen / 1e6) { 272 res += "===========================================================\n" 273 } 274 275 totlen = newlen 276 } 277 return 278 } 279 280 // UnbanPeer unbans peer of a given IP or "all" banned peers 281 func UnbanPeer(par string) (s string) { 282 var ad *peersdb.PeerAddr 283 284 if par != "all" { 285 var er error 286 ad, er = peersdb.NewAddrFromString(par, false) 287 if er != nil { 288 s = fmt.Sprintln(par, er.Error()) 289 return 290 } 291 s += fmt.Sprintln("Unban", ad.Ip(), "...") 292 network.HammeringMutex.Lock() 293 delete(network.RecentlyDisconencted, ad.Ip4) 294 network.HammeringMutex.Unlock() 295 } else { 296 s += fmt.Sprintln("Unban all peers ...") 297 network.HammeringMutex.Lock() 298 network.RecentlyDisconencted = make(map[[4]byte]*network.RecentlyDisconenctedType) 299 network.HammeringMutex.Unlock() 300 } 301 302 var keys []qdb.KeyType 303 var vals [][]byte 304 peersdb.PeerDB.Browse(func(k qdb.KeyType, v []byte) uint32 { 305 peer := peersdb.NewPeer(v) 306 if peer.Banned != 0 { 307 if ad == nil || peer.Ip() == ad.Ip() { 308 s += fmt.Sprintln(" -", peer.NetAddr.String()) 309 peer.Banned = 0 310 keys = append(keys, k) 311 vals = append(vals, peer.Bytes()) 312 } 313 } 314 return 0 315 }) 316 for i := range keys { 317 peersdb.PeerDB.Put(keys[i], vals[i]) 318 } 319 320 s += fmt.Sprintln(len(keys), "peer(s) un-baned") 321 return 322 } 323 324 func GetReceivedBlockX(block *btc.Block) (rb *network.OneReceivedBlock, cbasetx *btc.Tx) { 325 network.MutexRcv.Lock() 326 rb = network.ReceivedBlocks[block.Hash.BIdx()] 327 if rb.TheWeight == 0 { 328 block.BuildTxListExt(false) 329 rb.NonWitnessSize = block.NoWitnessSize 330 rb.TheWeight = block.BlockWeight 331 rb.ThePaidVSize = block.PaidTxsVSize 332 rb.TheOrdCnt = block.OrbTxCnt 333 rb.TheOrdSize = block.OrbTxSize 334 rb.TheOrdWeight = block.OrbTxWeight 335 cbasetx = block.Txs[0] 336 } else { 337 cbasetx, _ = btc.NewTx(block.Raw[block.TxOffset:]) 338 } 339 network.MutexRcv.Unlock() 340 return 341 } 342 343 func init() { 344 rand.Seed(int64(time.Now().Nanosecond())) 345 }