github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/utxo/unspent_recc.go (about)

     1  package utxo
     2  
     3  import (
     4  	"github.com/piotrnar/gocoin/lib/btc"
     5  	"github.com/piotrnar/gocoin/lib/script"
     6  	"sync"
     7  )
     8  
     9  /*
    10  	These are functions for dealing with compressed UTXO records
    11  */
    12  
    13  var (
    14  	comp_pool_mutex sync.Mutex //<- consider using this mutex for multi-thread serializations
    15  	comp_val        []uint64
    16  	comp_scr        [][]byte
    17  	ComprScrLen     = []int{21, 21, 33, 33, 33, 33}
    18  )
    19  
    20  func FullUtxoRecC(dat []byte) *UtxoRec {
    21  	var key UtxoKeyType
    22  	copy(key[:], dat[:UtxoIdxLen])
    23  	return NewUtxoRecC(key, dat[UtxoIdxLen:])
    24  }
    25  
    26  func NewUtxoRecStaticC(key UtxoKeyType, dat []byte) *UtxoRec {
    27  	var off, n, i, rec_idx int
    28  	var u64, idx uint64
    29  
    30  	off = 32 - UtxoIdxLen
    31  	copy(sta_rec.TxID[:UtxoIdxLen], key[:])
    32  	copy(sta_rec.TxID[UtxoIdxLen:], dat[:off])
    33  
    34  	u64, n = btc.VULe(dat[off:])
    35  	off += n
    36  	sta_rec.InBlock = uint32(u64)
    37  
    38  	u64, n = btc.VULe(dat[off:])
    39  	off += n
    40  
    41  	sta_rec.Coinbase = (u64 & 1) != 0
    42  	u64 >>= 1
    43  	if len(rec_outs) < int(u64) {
    44  		rec_outs = make([]*UtxoTxOut, u64)
    45  		rec_pool = make([]UtxoTxOut, u64)
    46  	}
    47  	sta_rec.Outs = rec_outs[:u64]
    48  	for i := range sta_rec.Outs {
    49  		sta_rec.Outs[i] = nil
    50  	}
    51  
    52  	for off < len(dat) {
    53  		idx, n = btc.VULe(dat[off:])
    54  		off += n
    55  
    56  		sta_rec.Outs[idx] = &rec_pool[rec_idx]
    57  		rec_idx++
    58  
    59  		u64, n = btc.VULe(dat[off:])
    60  		off += n
    61  		sta_rec.Outs[idx].Value = btc.DecompressAmount(uint64(u64))
    62  
    63  		i, n = btc.VLen(dat[off:])
    64  		if i < 6 {
    65  			i = ComprScrLen[i]
    66  			sta_rec.Outs[idx].PKScr = script.DecompressScript(dat[off : off+i])
    67  		} else {
    68  			off += n
    69  			i -= 6
    70  			sta_rec.Outs[idx].PKScr = dat[off : off+i]
    71  		}
    72  		off += i
    73  	}
    74  
    75  	return &sta_rec
    76  }
    77  
    78  func NewUtxoRecC(key UtxoKeyType, dat []byte) *UtxoRec {
    79  	var off, n, i int
    80  	var u64, idx uint64
    81  	var rec UtxoRec
    82  
    83  	off = 32 - UtxoIdxLen
    84  	copy(rec.TxID[:UtxoIdxLen], key[:])
    85  	copy(rec.TxID[UtxoIdxLen:], dat[:off])
    86  
    87  	u64, n = btc.VULe(dat[off:])
    88  	off += n
    89  	rec.InBlock = uint32(u64)
    90  
    91  	u64, n = btc.VULe(dat[off:])
    92  	off += n
    93  
    94  	rec.Coinbase = (u64 & 1) != 0
    95  	rec.Outs = make([]*UtxoTxOut, u64>>1)
    96  
    97  	for off < len(dat) {
    98  		idx, n = btc.VULe(dat[off:])
    99  		off += n
   100  		rec.Outs[idx] = new(UtxoTxOut)
   101  
   102  		u64, n = btc.VULe(dat[off:])
   103  		off += n
   104  		rec.Outs[idx].Value = btc.DecompressAmount(uint64(u64))
   105  
   106  		i, n = btc.VLen(dat[off:])
   107  		if i < 6 {
   108  			i = ComprScrLen[i]
   109  			rec.Outs[idx].PKScr = script.DecompressScript(dat[off : off+i])
   110  		} else {
   111  			off += n
   112  			i -= 6
   113  			rec.Outs[idx].PKScr = dat[off : off+i]
   114  		}
   115  		off += i
   116  	}
   117  	return &rec
   118  }
   119  
   120  func OneUtxoRecC(key UtxoKeyType, dat []byte, vout uint32) *btc.TxOut {
   121  	var off, n, i int
   122  	var u64, idx uint64
   123  	var res btc.TxOut
   124  
   125  	off = 32 - UtxoIdxLen
   126  
   127  	u64, n = btc.VULe(dat[off:])
   128  	off += n
   129  	res.BlockHeight = uint32(u64)
   130  
   131  	u64, n = btc.VULe(dat[off:])
   132  	off += n
   133  
   134  	res.VoutCount = uint32(u64 >> 1)
   135  	if res.VoutCount <= vout {
   136  		return nil
   137  	}
   138  	res.WasCoinbase = (u64 & 1) != 0
   139  
   140  	for off < len(dat) {
   141  		idx, n = btc.VULe(dat[off:])
   142  		if uint32(idx) > vout {
   143  			return nil
   144  		}
   145  		off += n
   146  
   147  		u64, n = btc.VULe(dat[off:])
   148  		off += n
   149  
   150  		i, n = btc.VLen(dat[off:])
   151  
   152  		if uint32(idx) == vout {
   153  			res.Value = btc.DecompressAmount(uint64(u64))
   154  			if i < 6 {
   155  				i = ComprScrLen[i]
   156  				res.Pk_script = script.DecompressScript(dat[off : off+i])
   157  			} else {
   158  				off += n
   159  				i -= 6
   160  				res.Pk_script = dat[off : off+i]
   161  			}
   162  			return &res
   163  		}
   164  
   165  		if i < 6 {
   166  			i = ComprScrLen[i]
   167  		} else {
   168  			off += n
   169  			i -= 6
   170  		}
   171  		off += i
   172  	}
   173  	return nil
   174  }
   175  
   176  func SerializeC(rec *UtxoRec, full bool, use_buf []byte) (buf []byte) {
   177  	var le, of int
   178  	var any_out bool
   179  
   180  	outcnt := uint64(len(rec.Outs) << 1)
   181  	if rec.Coinbase {
   182  		outcnt |= 1
   183  	}
   184  
   185  	// <- consider anabling this for multi-thread serializations
   186  	comp_pool_mutex.Lock()
   187  	defer comp_pool_mutex.Unlock()
   188  
   189  	// Only allocate when used for a first time, so no mem is wasted when not using compression
   190  	if int(outcnt) > len(comp_val) {
   191  		if outcnt > 30001 {
   192  			comp_val = make([]uint64, outcnt)
   193  			comp_scr = make([][]byte, outcnt)
   194  		} else {
   195  			comp_val = make([]uint64, 30001)
   196  			comp_scr = make([][]byte, 30001)
   197  		}
   198  	}
   199  
   200  	if full {
   201  		le = 32
   202  	} else {
   203  		le = 32 - UtxoIdxLen
   204  	}
   205  
   206  	le += btc.VLenSize(uint64(rec.InBlock)) // block length
   207  	le += btc.VLenSize(outcnt)              // out count
   208  
   209  	for i, r := range rec.Outs {
   210  		if rec.Outs[i] != nil {
   211  			le += btc.VLenSize(uint64(i))
   212  			comp_val[i] = btc.CompressAmount(r.Value)
   213  			comp_scr[i] = script.CompressScript(r.PKScr)
   214  			le += btc.VLenSize(comp_val[i])
   215  			if comp_scr[i] != nil {
   216  				le += len(comp_scr[i])
   217  			} else {
   218  				le += btc.VLenSize(uint64(6 + len(r.PKScr)))
   219  				le += len(r.PKScr)
   220  			}
   221  			any_out = true
   222  		}
   223  	}
   224  	if !any_out {
   225  		return
   226  	}
   227  
   228  	if use_buf == nil {
   229  		buf = Memory_Malloc(le)
   230  	} else {
   231  		buf = use_buf[:le]
   232  	}
   233  	if full {
   234  		copy(buf[:32], rec.TxID[:])
   235  		of = 32
   236  	} else {
   237  		of = 32 - UtxoIdxLen
   238  		copy(buf[:of], rec.TxID[UtxoIdxLen:])
   239  	}
   240  
   241  	of += btc.PutULe(buf[of:], uint64(rec.InBlock))
   242  	of += btc.PutULe(buf[of:], outcnt)
   243  	for i, r := range rec.Outs {
   244  		if rec.Outs[i] != nil {
   245  			of += btc.PutULe(buf[of:], uint64(i))
   246  			of += btc.PutULe(buf[of:], comp_val[i])
   247  			if comp_scr[i] != nil {
   248  				copy(buf[of:], comp_scr[i])
   249  				of += len(comp_scr[i])
   250  			} else {
   251  				of += btc.PutULe(buf[of:], uint64(6+len(r.PKScr)))
   252  				copy(buf[of:], r.PKScr)
   253  				of += len(r.PKScr)
   254  			}
   255  		}
   256  	}
   257  	return
   258  }