github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/txpool_disk.go (about) 1 package network 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "github.com/piotrnar/gocoin/client/common" 10 "github.com/piotrnar/gocoin/lib/btc" 11 "io" 12 "os" 13 "time" 14 ) 15 16 var ( 17 END_MARKER = []byte("END_OF_FILE") 18 ) 19 20 const ( 21 MEMPOOL_FILE_NAME2 = "mempool.dmp" 22 ) 23 24 func bool2byte(v bool) byte { 25 if v { 26 return 1 27 } else { 28 return 0 29 } 30 } 31 32 func (t2s *OneTxToSend) WriteBytes(wr io.Writer) { 33 btc.WriteVlen(wr, uint64(len(t2s.Raw))) 34 wr.Write(t2s.Raw) 35 36 btc.WriteVlen(wr, uint64(len(t2s.Spent))) 37 binary.Write(wr, binary.LittleEndian, t2s.Spent[:]) 38 39 binary.Write(wr, binary.LittleEndian, t2s.Invsentcnt) 40 binary.Write(wr, binary.LittleEndian, t2s.SentCnt) 41 binary.Write(wr, binary.LittleEndian, uint32(t2s.Firstseen.Unix())) 42 binary.Write(wr, binary.LittleEndian, uint32(t2s.Lastsent.Unix())) 43 binary.Write(wr, binary.LittleEndian, t2s.Volume) 44 binary.Write(wr, binary.LittleEndian, t2s.Fee) 45 binary.Write(wr, binary.LittleEndian, t2s.SigopsCost) 46 binary.Write(wr, binary.LittleEndian, t2s.VerifyTime) 47 wr.Write([]byte{bool2byte(t2s.Local), t2s.Blocked, bool2byte(t2s.MemInputs != nil), bool2byte(t2s.Final)}) 48 } 49 50 func MempoolSave(force bool) { 51 if !force && !common.CFG.TXPool.SaveOnDisk { 52 os.Remove(common.GocoinHomeDir + MEMPOOL_FILE_NAME2) 53 return 54 } 55 56 f, er := os.Create(common.GocoinHomeDir + MEMPOOL_FILE_NAME2) 57 if er != nil { 58 println(er.Error()) 59 return 60 } 61 62 fmt.Println("Saving", MEMPOOL_FILE_NAME2) 63 wr := bufio.NewWriter(f) 64 65 wr.Write(common.Last.Block.BlockHash.Hash[:]) 66 67 btc.WriteVlen(wr, uint64(len(TransactionsToSend))) 68 for _, t2s := range TransactionsToSend { 69 t2s.WriteBytes(wr) 70 } 71 72 btc.WriteVlen(wr, uint64(len(SpentOutputs))) 73 for k, v := range SpentOutputs { 74 binary.Write(wr, binary.LittleEndian, k) 75 binary.Write(wr, binary.LittleEndian, v) 76 } 77 78 wr.Write(END_MARKER[:]) 79 wr.Flush() 80 f.Close() 81 } 82 83 func MempoolLoad2() bool { 84 var t2s *OneTxToSend 85 var totcnt, le uint64 86 var tmp [32]byte 87 var bi BIDX 88 var tina uint32 89 var i int 90 var cnt1, cnt2 uint 91 92 f, er := os.Open(common.GocoinHomeDir + MEMPOOL_FILE_NAME2) 93 if er != nil { 94 fmt.Println("MempoolLoad:", er.Error()) 95 return false 96 } 97 defer f.Close() 98 99 rd := bufio.NewReader(f) 100 if _, er = io.ReadFull(rd, tmp[:32]); er != nil { 101 goto fatal_error 102 } 103 if !bytes.Equal(tmp[:32], common.Last.Block.BlockHash.Hash[:]) { 104 er = errors.New(MEMPOOL_FILE_NAME2 + " is for different last block hash (try to load it with 'mpl' command)") 105 goto fatal_error 106 } 107 108 if totcnt, er = btc.ReadVLen(rd); er != nil { 109 goto fatal_error 110 } 111 112 TransactionsToSend = make(map[BIDX]*OneTxToSend, int(totcnt)) 113 for ; totcnt > 0; totcnt-- { 114 le, er = btc.ReadVLen(rd) 115 if er != nil { 116 goto fatal_error 117 } 118 119 t2s = new(OneTxToSend) 120 raw := make([]byte, int(le)) 121 122 _, er = io.ReadFull(rd, raw) 123 if er != nil { 124 goto fatal_error 125 } 126 127 t2s.Tx, i = btc.NewTx(raw) 128 if t2s.Tx == nil || i != len(raw) { 129 er = errors.New(fmt.Sprint("Error parsing tx from ", MEMPOOL_FILE_NAME2, " at idx", len(TransactionsToSend))) 130 goto fatal_error 131 } 132 t2s.Tx.SetHash(raw) 133 134 le, er = btc.ReadVLen(rd) 135 if er != nil { 136 goto fatal_error 137 } 138 t2s.Spent = make([]uint64, int(le)) 139 if er = binary.Read(rd, binary.LittleEndian, t2s.Spent[:]); er != nil { 140 goto fatal_error 141 } 142 143 if er = binary.Read(rd, binary.LittleEndian, &t2s.Invsentcnt); er != nil { 144 goto fatal_error 145 } 146 147 if er = binary.Read(rd, binary.LittleEndian, &t2s.SentCnt); er != nil { 148 goto fatal_error 149 } 150 151 if er = binary.Read(rd, binary.LittleEndian, &tina); er != nil { 152 goto fatal_error 153 } 154 t2s.Firstseen = time.Unix(int64(tina), 0) 155 156 if er = binary.Read(rd, binary.LittleEndian, &tina); er != nil { 157 goto fatal_error 158 } 159 t2s.Lastsent = time.Unix(int64(tina), 0) 160 161 if er = binary.Read(rd, binary.LittleEndian, &t2s.Volume); er != nil { 162 goto fatal_error 163 } 164 165 if er = binary.Read(rd, binary.LittleEndian, &t2s.Fee); er != nil { 166 goto fatal_error 167 } 168 169 if er = binary.Read(rd, binary.LittleEndian, &t2s.SigopsCost); er != nil { 170 goto fatal_error 171 } 172 173 if er = binary.Read(rd, binary.LittleEndian, &t2s.VerifyTime); er != nil { 174 goto fatal_error 175 } 176 177 if _, er = io.ReadFull(rd, tmp[:4]); er != nil { 178 goto fatal_error 179 } 180 t2s.Local = tmp[0] != 0 181 t2s.Blocked = tmp[1] 182 if tmp[2] != 0 { 183 t2s.MemInputs = make([]bool, len(t2s.TxIn)) 184 } 185 t2s.Final = tmp[3] != 0 186 187 t2s.Tx.Fee = t2s.Fee 188 189 TransactionsToSend[t2s.Hash.BIdx()] = t2s 190 TransactionsToSendSize += uint64(len(t2s.Raw)) 191 TransactionsToSendWeight += uint64(t2s.Weight()) 192 } 193 194 if totcnt, er = btc.ReadVLen(rd); er != nil { 195 goto fatal_error 196 } 197 198 SpentOutputs = make(map[uint64]BIDX, int(totcnt)) 199 for ; totcnt > 0; totcnt-- { 200 if er = binary.Read(rd, binary.LittleEndian, &le); er != nil { 201 goto fatal_error 202 } 203 204 if er = binary.Read(rd, binary.LittleEndian, &bi); er != nil { 205 goto fatal_error 206 } 207 208 SpentOutputs[le] = bi 209 } 210 211 if _, er = io.ReadFull(rd, tmp[:len(END_MARKER)]); er != nil { 212 goto fatal_error 213 } 214 if !bytes.Equal(tmp[:len(END_MARKER)], END_MARKER) { 215 er = errors.New(MEMPOOL_FILE_NAME2 + " has marker missing") 216 goto fatal_error 217 } 218 219 // recover MemInputs 220 for _, t2s := range TransactionsToSend { 221 if t2s.MemInputs != nil { 222 cnt1++ 223 for i := range t2s.TxIn { 224 if _, inmem := TransactionsToSend[btc.BIdx(t2s.TxIn[i].Input.Hash[:])]; inmem { 225 t2s.MemInputs[i] = true 226 t2s.MemInputCnt++ 227 cnt2++ 228 } 229 } 230 if t2s.MemInputCnt == 0 { 231 println("ERROR: MemInputs not nil but nothing found") 232 t2s.MemInputs = nil 233 } 234 } 235 } 236 237 fmt.Println(len(TransactionsToSend), "transactions taking", TransactionsToSendSize, "Bytes loaded from", MEMPOOL_FILE_NAME2) 238 fmt.Println(cnt1, "transactions use", cnt2, "memory inputs") 239 240 return true 241 242 fatal_error: 243 fmt.Println("Error loading", MEMPOOL_FILE_NAME2, ":", er.Error()) 244 TransactionsToSend = make(map[BIDX]*OneTxToSend) 245 TransactionsToSendSize = 0 246 TransactionsToSendWeight = 0 247 SpentOutputs = make(map[uint64]BIDX) 248 return false 249 } 250 251 252 // MempoolLoadNew is only called from TextUI. 253 func MempoolLoadNew(fname string, abort *bool) bool { 254 var ntx *TxRcvd 255 var idx, totcnt, le, tmp64, oneperc, cntdwn, perc uint64 256 var tmp [32]byte 257 var tina uint32 258 var i int 259 var cnt1, cnt2 uint 260 var t2s OneTxToSend 261 262 f, er := os.Open(fname) 263 if er != nil { 264 fmt.Println("MempoolLoad:", er.Error()) 265 return false 266 } 267 defer f.Close() 268 269 rd := bufio.NewReader(f) 270 if _, er = io.ReadFull(rd, tmp[:32]); er != nil { 271 goto fatal_error 272 } 273 274 if totcnt, er = btc.ReadVLen(rd); er != nil { 275 goto fatal_error 276 } 277 fmt.Println("Loading", totcnt, "transactions from", fname) 278 279 oneperc = totcnt/100 280 281 for idx = 0; idx < totcnt; idx++ { 282 if cntdwn==0 { 283 fmt.Print("\r", perc, "% complete...") 284 perc++ 285 cntdwn = oneperc 286 } 287 cntdwn-- 288 if abort != nil && *abort { 289 break 290 } 291 le, er = btc.ReadVLen(rd) 292 if er != nil { 293 goto fatal_error 294 } 295 296 ntx = new(TxRcvd) 297 raw := make([]byte, int(le)) 298 299 _, er = io.ReadFull(rd, raw) 300 if er != nil { 301 goto fatal_error 302 } 303 304 ntx.Tx, i = btc.NewTx(raw) 305 if ntx.Tx == nil || i != len(raw) { 306 er = errors.New(fmt.Sprint("Error parsing tx from ", fname, " at idx", idx)) 307 goto fatal_error 308 } 309 ntx.SetHash(raw) 310 311 le, er = btc.ReadVLen(rd) 312 if er != nil { 313 goto fatal_error 314 } 315 316 for le > 0 { 317 if er = binary.Read(rd, binary.LittleEndian, &tmp64); er != nil { 318 goto fatal_error 319 } 320 le-- 321 } 322 323 // discard all the rest... 324 binary.Read(rd, binary.LittleEndian, &t2s.Invsentcnt) 325 binary.Read(rd, binary.LittleEndian, &t2s.SentCnt) 326 binary.Read(rd, binary.LittleEndian, &tina) 327 binary.Read(rd, binary.LittleEndian, &tina) 328 binary.Read(rd, binary.LittleEndian, &t2s.Volume) 329 binary.Read(rd, binary.LittleEndian, &t2s.Fee) 330 binary.Read(rd, binary.LittleEndian, &t2s.SigopsCost) 331 binary.Read(rd, binary.LittleEndian, &t2s.VerifyTime) 332 if _, er = io.ReadFull(rd, tmp[:4]); er != nil { 333 goto fatal_error 334 } 335 336 // submit tx if we dont have it yet... 337 if NeedThisTx(&ntx.Hash, nil) { 338 cnt2++ 339 if HandleNetTx(ntx, true) { 340 cnt1++ 341 } 342 } 343 } 344 345 fmt.Print("\r \r") 346 fmt.Println(cnt1, "out of", cnt2, "new transactions accepted into memory pool") 347 348 return true 349 350 fatal_error: 351 fmt.Println("Error loading", fname, ":", er.Error()) 352 return false 353 }