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  }