github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/tools/unspent4_to_UTXO/unspent4_to_UTXO.go (about)

     1  package main
     2  
     3  import (
     4  	"strings"
     5  	"strconv"
     6  	"bufio"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"github.com/piotrnar/gocoin/lib/btc"
    10  	"github.com/piotrnar/gocoin/lib/others/qdb"
    11  	"io/ioutil"
    12  	"os"
    13  	"time"
    14  )
    15  
    16  var (
    17  	block_height uint64
    18  	block_hash []byte
    19  )
    20  
    21  func load_map4() (ndb map[qdb.KeyType][]byte) {
    22  	var odb *qdb.DB
    23  	ndb = make(map[qdb.KeyType][]byte, 21e6)
    24  	for i := 0; i < 16; i++ {
    25  		fmt.Print("\r", i, " of 16 ... ")
    26  		er := qdb.NewDBExt(&odb, &qdb.NewDBOpts{Dir: fmt.Sprintf("unspent4/%06d", i),
    27  			Volatile: true, LoadData: true, WalkFunction: func(key qdb.KeyType, val []byte) uint32 {
    28  				if _, ok := ndb[key]; ok {
    29  					panic("duplicate")
    30  				}
    31  				ndb[key] = val
    32  				return 0
    33  			}})
    34  		if er != nil {
    35  			fmt.Println(er.Error())
    36  			return
    37  		}
    38  		odb.Close()
    39  	}
    40  	fmt.Print("\r                                                              \r")
    41  	return
    42  }
    43  
    44  
    45  func load_last_block() {
    46  	var maxbl_fn string
    47  
    48  	fis, _ := ioutil.ReadDir("unspent4/")
    49  	var maxbl, undobl int
    50  	for _, fi := range fis {
    51  		if !fi.IsDir() && fi.Size() >= 32 {
    52  			ss := strings.SplitN(fi.Name(), ".", 2)
    53  			cb, er := strconv.ParseUint(ss[0], 10, 32)
    54  			if er == nil && int(cb) > maxbl {
    55  				maxbl = int(cb)
    56  				maxbl_fn = fi.Name()
    57  				if len(ss) == 2 && ss[1] == "tmp" {
    58  					undobl = maxbl
    59  				}
    60  			}
    61  		}
    62  	}
    63  	if maxbl == 0 {
    64  		fmt.Println("This unspent4 database is corrupt")
    65  		return
    66  	}
    67  	if undobl == maxbl {
    68  		fmt.Println("This unspent4 database is not properly closed")
    69  		return
    70  	}
    71  
    72  	block_height = uint64(maxbl)
    73  	block_hash = make([]byte, 32)
    74  
    75  	f, _ := os.Open("unspent4/"+maxbl_fn)
    76  	f.Read(block_hash)
    77  	f.Close()
    78  
    79  }
    80  
    81  func save_map(ndb map[qdb.KeyType][]byte) {
    82  	var cnt_dwn, cnt_dwn_from, perc int
    83  	of, er := os.Create("UTXO.db")
    84  	if er != nil {
    85  		fmt.Println("Create file:", er.Error())
    86  		return
    87  	}
    88  
    89  	cnt_dwn_from = len(ndb) / 100
    90  	wr := bufio.NewWriter(of)
    91  	binary.Write(wr, binary.LittleEndian, uint64(block_height))
    92  	wr.Write(block_hash)
    93  	binary.Write(wr, binary.LittleEndian, uint64(len(ndb)))
    94  	for k, v := range ndb {
    95  		btc.WriteVlen(wr, uint64(len(v)+8))
    96  		binary.Write(wr, binary.LittleEndian, k)
    97  		//binary.Write(wr, binary.LittleEndian, uint32(len(v)))
    98  		_, er = wr.Write(v)
    99  		if er != nil {
   100  			fmt.Println("\n\007Fatal error:", er.Error())
   101  			break
   102  		}
   103  		if cnt_dwn == 0 {
   104  			fmt.Print("\rSaving UTXO.db - ", perc, "% complete ... ")
   105  			cnt_dwn = cnt_dwn_from
   106  			perc++
   107  		} else {
   108  			cnt_dwn--
   109  		}
   110  	}
   111  	wr.Flush()
   112  	of.Close()
   113  
   114  	fmt.Print("\r                                                              \r")
   115  }
   116  
   117  func main() {
   118  	var sta time.Time
   119  
   120  	if fi, er := os.Stat("unspent4"); er!=nil || !fi.IsDir() {
   121  		fmt.Println("ERROR: Input database not found.")
   122  		fmt.Println("Make sure to have unspent4/ directory, where you run this tool from")
   123  		return
   124  	}
   125  
   126  	load_last_block()
   127  	if len(block_hash)!=32 {
   128  		fmt.Println("ERROR: Could not recover last block's data from the input database", len(block_hash))
   129  		return
   130  	}
   131  
   132  	fmt.Println("Loading input database. Block", block_height, btc.NewUint256(block_hash).String())
   133  	sta = time.Now()
   134  	ndb := load_map4()
   135  	fmt.Println(len(ndb), "records loaded in", time.Now().Sub(sta).String())
   136  
   137  	sta = time.Now()
   138  	save_map(ndb)
   139  	fmt.Println("Saved in in", time.Now().Sub(sta).String())
   140  }