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 }