github.com/cerberus-wallet/blockbook@v0.3.2/db/rocksdb_test.go (about) 1 // +build unittest 2 3 package db 4 5 import ( 6 "blockbook/bchain" 7 "blockbook/bchain/coins/btc" 8 "blockbook/common" 9 "blockbook/tests/dbtestdata" 10 "encoding/binary" 11 "encoding/hex" 12 "io/ioutil" 13 "math/big" 14 "os" 15 "reflect" 16 "sort" 17 "strings" 18 "testing" 19 "time" 20 21 vlq "github.com/bsm/go-vlq" 22 "github.com/juju/errors" 23 "github.com/martinboehm/btcutil/chaincfg" 24 ) 25 26 // simplified explanation of signed varint packing, used in many index data structures 27 // for number n, the packing is: 2*n if n>=0 else 2*(-n)-1 28 // takes only 1 byte if abs(n)<127 29 30 func TestMain(m *testing.M) { 31 c := m.Run() 32 chaincfg.ResetParams() 33 os.Exit(c) 34 } 35 36 type testBitcoinParser struct { 37 *btc.BitcoinParser 38 } 39 40 func bitcoinTestnetParser() *btc.BitcoinParser { 41 return btc.NewBitcoinParser( 42 btc.GetChainParams("test"), 43 &btc.Configuration{BlockAddressesToKeep: 1}) 44 } 45 46 func setupRocksDB(t *testing.T, p bchain.BlockChainParser) *RocksDB { 47 tmp, err := ioutil.TempDir("", "testdb") 48 if err != nil { 49 t.Fatal(err) 50 } 51 d, err := NewRocksDB(tmp, 100000, -1, p, nil) 52 if err != nil { 53 t.Fatal(err) 54 } 55 is, err := d.LoadInternalState("coin-unittest") 56 if err != nil { 57 t.Fatal(err) 58 } 59 d.SetInternalState(is) 60 return d 61 } 62 63 func closeAndDestroyRocksDB(t *testing.T, d *RocksDB) { 64 if err := d.Close(); err != nil { 65 t.Fatal(err) 66 } 67 os.RemoveAll(d.path) 68 } 69 70 func inputAddressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 71 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 72 return hex.EncodeToString([]byte{byte(len(h) / 2)}) + h 73 } 74 75 func addressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 76 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 77 return hex.EncodeToString([]byte{byte(len(h))}) + h 78 } 79 80 func spentAddressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 81 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 82 return hex.EncodeToString([]byte{byte(len(h) + 1)}) + h 83 } 84 85 func bigintToHex(i *big.Int) string { 86 b := make([]byte, maxPackedBigintBytes) 87 l := packBigint(i, b) 88 return hex.EncodeToString(b[:l]) 89 } 90 91 func varuintToHex(i uint) string { 92 b := make([]byte, vlq.MaxLen64) 93 l := vlq.PutUint(b, uint64(i)) 94 return hex.EncodeToString(b[:l]) 95 } 96 97 func uintToHex(i uint32) string { 98 buf := make([]byte, 4) 99 binary.BigEndian.PutUint32(buf, i) 100 return hex.EncodeToString(buf) 101 } 102 103 func hexToBytes(h string) []byte { 104 b, _ := hex.DecodeString(h) 105 return b 106 } 107 108 func addressKeyHex(a string, height uint32, d *RocksDB) string { 109 return dbtestdata.AddressToPubKeyHex(a, d.chainParser) + uintToHex(^height) 110 } 111 112 func txIndexesHex(tx string, indexes []int32) string { 113 buf := make([]byte, vlq.MaxLen32) 114 for i, index := range indexes { 115 index <<= 1 116 if i == len(indexes)-1 { 117 index |= 1 118 } 119 l := packVarint32(index, buf) 120 tx += hex.EncodeToString(buf[:l]) 121 } 122 return tx 123 } 124 125 // keyPair is used to compare given key value in DB with expected 126 // for more complicated compares it is possible to specify CompareFunc 127 type keyPair struct { 128 Key, Value string 129 CompareFunc func(string) bool 130 } 131 132 func compareFuncBlockAddresses(t *testing.T, v string, expected []string) bool { 133 for _, e := range expected { 134 lb := len(v) 135 v = strings.Replace(v, e, "", 1) 136 if lb == len(v) { 137 t.Error(e, " not found in ", v) 138 return false 139 } 140 } 141 if len(v) != 0 { 142 t.Error("not expected content ", v) 143 } 144 return len(v) == 0 145 } 146 147 func checkColumn(d *RocksDB, col int, kp []keyPair) error { 148 sort.Slice(kp, func(i, j int) bool { 149 return kp[i].Key < kp[j].Key 150 }) 151 it := d.db.NewIteratorCF(d.ro, d.cfh[col]) 152 defer it.Close() 153 i := 0 154 for it.SeekToFirst(); it.Valid(); it.Next() { 155 if i >= len(kp) { 156 return errors.Errorf("Expected less rows in column %v", cfNames[col]) 157 } 158 key := hex.EncodeToString(it.Key().Data()) 159 if key != kp[i].Key { 160 return errors.Errorf("Incorrect key %v found in column %v row %v, expecting %v", key, cfNames[col], i, kp[i].Key) 161 } 162 val := hex.EncodeToString(it.Value().Data()) 163 var valOK bool 164 if kp[i].CompareFunc == nil { 165 valOK = val == kp[i].Value 166 } else { 167 valOK = kp[i].CompareFunc(val) 168 } 169 if !valOK { 170 return errors.Errorf("Incorrect value %v found in column %v row %v key %v, expecting %v", val, cfNames[col], i, key, kp[i].Value) 171 } 172 i++ 173 } 174 if i != len(kp) { 175 return errors.Errorf("Expected more rows in column %v: got %v, expected %v", cfNames[col], i, len(kp)) 176 } 177 return nil 178 } 179 180 func verifyAfterBitcoinTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) { 181 if err := checkColumn(d, cfHeight, []keyPair{ 182 { 183 "000370d5", 184 "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1521515026) + varuintToHex(2) + varuintToHex(1234567), 185 nil, 186 }, 187 }); err != nil { 188 { 189 t.Fatal(err) 190 } 191 } 192 // the vout is encoded as signed varint, i.e. value * 2 for non negative values 193 if err := checkColumn(d, cfAddresses, []keyPair{ 194 {addressKeyHex(dbtestdata.Addr1, 225493, d), txIndexesHex(dbtestdata.TxidB1T1, []int32{0}), nil}, 195 {addressKeyHex(dbtestdata.Addr2, 225493, d), txIndexesHex(dbtestdata.TxidB1T1, []int32{1, 2}), nil}, 196 {addressKeyHex(dbtestdata.Addr3, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{0}), nil}, 197 {addressKeyHex(dbtestdata.Addr4, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{1}), nil}, 198 {addressKeyHex(dbtestdata.Addr5, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{2}), nil}, 199 }); err != nil { 200 { 201 t.Fatal(err) 202 } 203 } 204 if err := checkColumn(d, cfTxAddresses, []keyPair{ 205 { 206 dbtestdata.TxidB1T1, 207 varuintToHex(225493) + 208 "00" + 209 "03" + 210 addressToPubKeyHexWithLength(dbtestdata.Addr1, t, d) + bigintToHex(dbtestdata.SatB1T1A1) + 211 addressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2) + 212 addressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2), 213 nil, 214 }, 215 { 216 dbtestdata.TxidB1T2, 217 varuintToHex(225493) + 218 "00" + 219 "03" + 220 addressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 221 addressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 222 addressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5), 223 nil, 224 }, 225 }); err != nil { 226 { 227 t.Fatal(err) 228 } 229 } 230 if err := checkColumn(d, cfAddressBalance, []keyPair{ 231 { 232 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), 233 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1) + 234 dbtestdata.TxidB1T1 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A1), 235 nil, 236 }, 237 { 238 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), 239 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A2Double) + 240 dbtestdata.TxidB1T1 + varuintToHex(1) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A2) + 241 dbtestdata.TxidB1T1 + varuintToHex(2) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A2), 242 nil, 243 }, 244 { 245 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), 246 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A3) + 247 dbtestdata.TxidB1T2 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A3), 248 nil, 249 }, 250 { 251 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), 252 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A4) + 253 dbtestdata.TxidB1T2 + varuintToHex(1) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A4), 254 nil, 255 }, 256 { 257 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), 258 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A5) + 259 dbtestdata.TxidB1T2 + varuintToHex(2) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A5), 260 nil, 261 }, 262 }); err != nil { 263 { 264 t.Fatal(err) 265 } 266 } 267 268 var blockTxsKp []keyPair 269 if afterDisconnect { 270 blockTxsKp = []keyPair{} 271 } else { 272 blockTxsKp = []keyPair{ 273 { 274 "000370d5", 275 dbtestdata.TxidB1T1 + "00" + dbtestdata.TxidB1T2 + "00", 276 nil, 277 }, 278 } 279 } 280 281 if err := checkColumn(d, cfBlockTxs, blockTxsKp); err != nil { 282 { 283 t.Fatal(err) 284 } 285 } 286 } 287 288 func verifyAfterBitcoinTypeBlock2(t *testing.T, d *RocksDB) { 289 if err := checkColumn(d, cfHeight, []keyPair{ 290 { 291 "000370d5", 292 "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1521515026) + varuintToHex(2) + varuintToHex(1234567), 293 nil, 294 }, 295 { 296 "000370d6", 297 "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" + uintToHex(1521595678) + varuintToHex(4) + varuintToHex(2345678), 298 nil, 299 }, 300 }); err != nil { 301 { 302 t.Fatal(err) 303 } 304 } 305 if err := checkColumn(d, cfAddresses, []keyPair{ 306 {addressKeyHex(dbtestdata.Addr1, 225493, d), txIndexesHex(dbtestdata.TxidB1T1, []int32{0}), nil}, 307 {addressKeyHex(dbtestdata.Addr2, 225493, d), txIndexesHex(dbtestdata.TxidB1T1, []int32{1, 2}), nil}, 308 {addressKeyHex(dbtestdata.Addr3, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{0}), nil}, 309 {addressKeyHex(dbtestdata.Addr4, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{1}), nil}, 310 {addressKeyHex(dbtestdata.Addr5, 225493, d), txIndexesHex(dbtestdata.TxidB1T2, []int32{2}), nil}, 311 {addressKeyHex(dbtestdata.Addr6, 225494, d), txIndexesHex(dbtestdata.TxidB2T2, []int32{^0}) + txIndexesHex(dbtestdata.TxidB2T1, []int32{0}), nil}, 312 {addressKeyHex(dbtestdata.Addr7, 225494, d), txIndexesHex(dbtestdata.TxidB2T1, []int32{1}), nil}, 313 {addressKeyHex(dbtestdata.Addr8, 225494, d), txIndexesHex(dbtestdata.TxidB2T2, []int32{0}), nil}, 314 {addressKeyHex(dbtestdata.Addr9, 225494, d), txIndexesHex(dbtestdata.TxidB2T2, []int32{1}), nil}, 315 {addressKeyHex(dbtestdata.Addr3, 225494, d), txIndexesHex(dbtestdata.TxidB2T1, []int32{^0}), nil}, 316 {addressKeyHex(dbtestdata.Addr2, 225494, d), txIndexesHex(dbtestdata.TxidB2T1, []int32{^1}), nil}, 317 {addressKeyHex(dbtestdata.Addr5, 225494, d), txIndexesHex(dbtestdata.TxidB2T3, []int32{0, ^0}), nil}, 318 {addressKeyHex(dbtestdata.AddrA, 225494, d), txIndexesHex(dbtestdata.TxidB2T4, []int32{0}), nil}, 319 {addressKeyHex(dbtestdata.Addr4, 225494, d), txIndexesHex(dbtestdata.TxidB2T2, []int32{^1}), nil}, 320 }); err != nil { 321 { 322 t.Fatal(err) 323 } 324 } 325 if err := checkColumn(d, cfTxAddresses, []keyPair{ 326 { 327 dbtestdata.TxidB1T1, 328 varuintToHex(225493) + 329 "00" + 330 "03" + 331 addressToPubKeyHexWithLength(dbtestdata.Addr1, t, d) + bigintToHex(dbtestdata.SatB1T1A1) + 332 spentAddressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2) + 333 addressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2), 334 nil, 335 }, 336 { 337 dbtestdata.TxidB1T2, 338 varuintToHex(225493) + 339 "00" + 340 "03" + 341 spentAddressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 342 spentAddressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 343 spentAddressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5), 344 nil, 345 }, 346 { 347 dbtestdata.TxidB2T1, 348 varuintToHex(225494) + 349 "02" + 350 inputAddressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 351 inputAddressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2) + 352 "03" + 353 spentAddressToPubKeyHexWithLength(dbtestdata.Addr6, t, d) + bigintToHex(dbtestdata.SatB2T1A6) + 354 addressToPubKeyHexWithLength(dbtestdata.Addr7, t, d) + bigintToHex(dbtestdata.SatB2T1A7) + 355 hex.EncodeToString([]byte{byte(len(dbtestdata.TxidB2T1Output3OpReturn))}) + dbtestdata.TxidB2T1Output3OpReturn + bigintToHex(dbtestdata.SatZero), 356 nil, 357 }, 358 { 359 dbtestdata.TxidB2T2, 360 varuintToHex(225494) + 361 "02" + 362 inputAddressToPubKeyHexWithLength(dbtestdata.Addr6, t, d) + bigintToHex(dbtestdata.SatB2T1A6) + 363 inputAddressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 364 "02" + 365 addressToPubKeyHexWithLength(dbtestdata.Addr8, t, d) + bigintToHex(dbtestdata.SatB2T2A8) + 366 addressToPubKeyHexWithLength(dbtestdata.Addr9, t, d) + bigintToHex(dbtestdata.SatB2T2A9), 367 nil, 368 }, 369 { 370 dbtestdata.TxidB2T3, 371 varuintToHex(225494) + 372 "01" + 373 inputAddressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5) + 374 "01" + 375 addressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB2T3A5), 376 nil, 377 }, 378 { 379 dbtestdata.TxidB2T4, 380 varuintToHex(225494) + 381 "01" + inputAddressToPubKeyHexWithLength("", t, d) + bigintToHex(dbtestdata.SatZero) + 382 "02" + 383 addressToPubKeyHexWithLength(dbtestdata.AddrA, t, d) + bigintToHex(dbtestdata.SatB2T4AA) + 384 addressToPubKeyHexWithLength("", t, d) + bigintToHex(dbtestdata.SatZero), 385 nil, 386 }, 387 }); err != nil { 388 { 389 t.Fatal(err) 390 } 391 } 392 if err := checkColumn(d, cfAddressBalance, []keyPair{ 393 { 394 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), 395 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1) + 396 dbtestdata.TxidB1T1 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A1), 397 nil, 398 }, 399 { 400 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), 401 "02" + bigintToHex(dbtestdata.SatB1T1A2) + bigintToHex(dbtestdata.SatB1T1A2) + 402 dbtestdata.TxidB1T1 + varuintToHex(2) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A2), 403 nil, 404 }, 405 { 406 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), 407 "02" + bigintToHex(dbtestdata.SatB1T2A3) + bigintToHex(dbtestdata.SatZero), 408 nil, 409 }, 410 { 411 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), 412 "02" + bigintToHex(dbtestdata.SatB1T2A4) + bigintToHex(dbtestdata.SatZero), 413 nil, 414 }, 415 { 416 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), 417 "02" + bigintToHex(dbtestdata.SatB1T2A5) + bigintToHex(dbtestdata.SatB2T3A5) + 418 dbtestdata.TxidB2T3 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T3A5), 419 nil, 420 }, 421 { 422 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr6, d.chainParser), 423 "02" + bigintToHex(dbtestdata.SatB2T1A6) + bigintToHex(dbtestdata.SatZero), 424 nil, 425 }, 426 { 427 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr7, d.chainParser), 428 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T1A7) + 429 dbtestdata.TxidB2T1 + varuintToHex(1) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T1A7), 430 nil, 431 }, 432 { 433 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr8, d.chainParser), 434 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A8) + 435 dbtestdata.TxidB2T2 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T2A8), 436 nil, 437 }, 438 { 439 dbtestdata.AddressToPubKeyHex(dbtestdata.Addr9, d.chainParser), 440 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A9) + 441 dbtestdata.TxidB2T2 + varuintToHex(1) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T2A9), 442 nil, 443 }, 444 { 445 dbtestdata.AddressToPubKeyHex(dbtestdata.AddrA, d.chainParser), 446 "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T4AA) + 447 dbtestdata.TxidB2T4 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T4AA), 448 nil, 449 }, 450 }); err != nil { 451 { 452 t.Fatal(err) 453 } 454 } 455 if err := checkColumn(d, cfBlockTxs, []keyPair{ 456 { 457 "000370d6", 458 dbtestdata.TxidB2T1 + "02" + dbtestdata.TxidB1T2 + "00" + dbtestdata.TxidB1T1 + "02" + 459 dbtestdata.TxidB2T2 + "02" + dbtestdata.TxidB2T1 + "00" + dbtestdata.TxidB1T2 + "02" + 460 dbtestdata.TxidB2T3 + "01" + dbtestdata.TxidB1T2 + "04" + 461 dbtestdata.TxidB2T4 + "01" + "0000000000000000000000000000000000000000000000000000000000000000" + "00", 462 nil, 463 }, 464 }); err != nil { 465 { 466 t.Fatal(err) 467 } 468 } 469 } 470 471 type txidIndex struct { 472 txid string 473 index int32 474 } 475 476 func verifyGetTransactions(t *testing.T, d *RocksDB, addr string, low, high uint32, wantTxids []txidIndex, wantErr error) { 477 gotTxids := make([]txidIndex, 0) 478 addToTxids := func(txid string, height uint32, indexes []int32) error { 479 for _, index := range indexes { 480 gotTxids = append(gotTxids, txidIndex{txid, index}) 481 } 482 return nil 483 } 484 if err := d.GetTransactions(addr, low, high, addToTxids); err != nil { 485 if wantErr == nil || wantErr.Error() != err.Error() { 486 t.Fatal(err) 487 } 488 } 489 if !reflect.DeepEqual(gotTxids, wantTxids) { 490 t.Errorf("GetTransactions() = %v, want %v", gotTxids, wantTxids) 491 } 492 } 493 494 // override PackTx and UnpackTx to default BaseParser functionality 495 // BitcoinParser uses tx hex which is not available for the test transactions 496 func (p *testBitcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 497 return p.BaseParser.PackTx(tx, height, blockTime) 498 } 499 500 func (p *testBitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 501 return p.BaseParser.UnpackTx(buf) 502 } 503 504 func testTxCache(t *testing.T, d *RocksDB, b *bchain.Block, tx *bchain.Tx) { 505 if err := d.PutTx(tx, b.Height, tx.Blocktime); err != nil { 506 t.Fatal(err) 507 } 508 gtx, height, err := d.GetTx(tx.Txid) 509 if err != nil { 510 t.Fatal(err) 511 } 512 if b.Height != height { 513 t.Fatalf("GetTx: got height %v, expected %v", height, b.Height) 514 } 515 // Confirmations are not stored in the DB, set them from input tx 516 gtx.Confirmations = tx.Confirmations 517 if !reflect.DeepEqual(gtx, tx) { 518 t.Errorf("GetTx: %v, want %v", gtx, tx) 519 } 520 if err := d.DeleteTx(tx.Txid); err != nil { 521 t.Fatal(err) 522 } 523 } 524 525 // TestRocksDB_Index_BitcoinType is an integration test probing the whole indexing functionality for BitcoinType chains 526 // It does the following: 527 // 1) Connect two blocks (inputs from 2nd block are spending some outputs from the 1st block) 528 // 2) GetTransactions for various addresses / low-high ranges 529 // 3) GetBestBlock, GetBlockHash 530 // 4) Test tx caching functionality 531 // 5) Disconnect the block 2 using BlockTxs column 532 // 6) Reconnect block 2 and check 533 // After each step, the content of DB is examined and any difference against expected state is regarded as failure 534 func TestRocksDB_Index_BitcoinType(t *testing.T) { 535 d := setupRocksDB(t, &testBitcoinParser{ 536 BitcoinParser: bitcoinTestnetParser(), 537 }) 538 defer closeAndDestroyRocksDB(t, d) 539 540 if len(d.is.BlockTimes) != 0 { 541 t.Fatal("Expecting is.BlockTimes 0, got ", len(d.is.BlockTimes)) 542 } 543 544 // connect 1st block - will log warnings about missing UTXO transactions in txAddresses column 545 block1 := dbtestdata.GetTestBitcoinTypeBlock1(d.chainParser) 546 if err := d.ConnectBlock(block1); err != nil { 547 t.Fatal(err) 548 } 549 verifyAfterBitcoinTypeBlock1(t, d, false) 550 551 if len(d.is.BlockTimes) != 1 { 552 t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) 553 } 554 555 // connect 2nd block - use some outputs from the 1st block as the inputs and 1 input uses tx from the same block 556 block2 := dbtestdata.GetTestBitcoinTypeBlock2(d.chainParser) 557 if err := d.ConnectBlock(block2); err != nil { 558 t.Fatal(err) 559 } 560 verifyAfterBitcoinTypeBlock2(t, d) 561 562 if len(d.is.BlockTimes) != 2 { 563 t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) 564 } 565 566 // get transactions for various addresses / low-high ranges 567 verifyGetTransactions(t, d, dbtestdata.Addr2, 0, 1000000, []txidIndex{ 568 {dbtestdata.TxidB2T1, ^1}, 569 {dbtestdata.TxidB1T1, 1}, 570 {dbtestdata.TxidB1T1, 2}, 571 }, nil) 572 verifyGetTransactions(t, d, dbtestdata.Addr2, 225493, 225493, []txidIndex{ 573 {dbtestdata.TxidB1T1, 1}, 574 {dbtestdata.TxidB1T1, 2}, 575 }, nil) 576 verifyGetTransactions(t, d, dbtestdata.Addr2, 225494, 1000000, []txidIndex{ 577 {dbtestdata.TxidB2T1, ^1}, 578 }, nil) 579 verifyGetTransactions(t, d, dbtestdata.Addr2, 500000, 1000000, []txidIndex{}, nil) 580 verifyGetTransactions(t, d, dbtestdata.Addr8, 0, 1000000, []txidIndex{ 581 {dbtestdata.TxidB2T2, 0}, 582 }, nil) 583 verifyGetTransactions(t, d, dbtestdata.Addr6, 0, 1000000, []txidIndex{ 584 {dbtestdata.TxidB2T2, ^0}, 585 {dbtestdata.TxidB2T1, 0}, 586 }, nil) 587 verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidIndex{}, errors.New("checksum mismatch")) 588 589 // GetBestBlock 590 height, hash, err := d.GetBestBlock() 591 if err != nil { 592 t.Fatal(err) 593 } 594 if height != 225494 { 595 t.Fatalf("GetBestBlock: got height %v, expected %v", height, 225494) 596 } 597 if hash != "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" { 598 t.Fatalf("GetBestBlock: got hash %v, expected %v", hash, "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6") 599 } 600 601 // GetBlockHash 602 hash, err = d.GetBlockHash(225493) 603 if err != nil { 604 t.Fatal(err) 605 } 606 if hash != "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" { 607 t.Fatalf("GetBlockHash: got hash %v, expected %v", hash, "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997") 608 } 609 610 // Not connected block 611 hash, err = d.GetBlockHash(225495) 612 if err != nil { 613 t.Fatal(err) 614 } 615 if hash != "" { 616 t.Fatalf("GetBlockHash: got hash '%v', expected ''", hash) 617 } 618 619 // GetBlockHash 620 info, err := d.GetBlockInfo(225494) 621 if err != nil { 622 t.Fatal(err) 623 } 624 iw := &BlockInfo{ 625 Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6", 626 Txs: 4, 627 Size: 2345678, 628 Time: 1521595678, 629 Height: 225494, 630 } 631 if !reflect.DeepEqual(info, iw) { 632 t.Errorf("GetBlockInfo() = %+v, want %+v", info, iw) 633 } 634 635 // Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock 636 testTxCache(t, d, block1, &block1.Txs[0]) 637 testTxCache(t, d, block2, &block2.Txs[0]) 638 if err = d.PutTx(&block2.Txs[1], block2.Height, block2.Txs[1].Blocktime); err != nil { 639 t.Fatal(err) 640 } 641 // check that there is only the last tx in the cache 642 packedTx, err := d.chainParser.PackTx(&block2.Txs[1], block2.Height, block2.Txs[1].Blocktime) 643 if err := checkColumn(d, cfTransactions, []keyPair{ 644 {block2.Txs[1].Txid, hex.EncodeToString(packedTx), nil}, 645 }); err != nil { 646 { 647 t.Fatal(err) 648 } 649 } 650 651 // try to disconnect both blocks, however only the last one is kept, it is not possible 652 err = d.DisconnectBlockRangeBitcoinType(225493, 225494) 653 if err == nil || err.Error() != "Cannot disconnect blocks with height 225493 and lower. It is necessary to rebuild index." { 654 t.Fatal(err) 655 } 656 verifyAfterBitcoinTypeBlock2(t, d) 657 658 // disconnect the 2nd block, verify that the db contains only data from the 1st block with restored unspentTxs 659 // and that the cached tx is removed 660 err = d.DisconnectBlockRangeBitcoinType(225494, 225494) 661 if err != nil { 662 t.Fatal(err) 663 } 664 verifyAfterBitcoinTypeBlock1(t, d, true) 665 if err := checkColumn(d, cfTransactions, []keyPair{}); err != nil { 666 { 667 t.Fatal(err) 668 } 669 } 670 671 if len(d.is.BlockTimes) != 1 { 672 t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) 673 } 674 675 // connect block again and verify the state of db 676 if err := d.ConnectBlock(block2); err != nil { 677 t.Fatal(err) 678 } 679 verifyAfterBitcoinTypeBlock2(t, d) 680 681 if len(d.is.BlockTimes) != 2 { 682 t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) 683 } 684 685 // test public methods for address balance and tx addresses 686 ab, err := d.GetAddressBalance(dbtestdata.Addr5, AddressBalanceDetailUTXO) 687 if err != nil { 688 t.Fatal(err) 689 } 690 abw := &AddrBalance{ 691 Txs: 2, 692 SentSat: *dbtestdata.SatB1T2A5, 693 BalanceSat: *dbtestdata.SatB2T3A5, 694 Utxos: []Utxo{ 695 { 696 BtxID: hexToBytes(dbtestdata.TxidB2T3), 697 Vout: 0, 698 Height: 225494, 699 ValueSat: *dbtestdata.SatB2T3A5, 700 }, 701 }, 702 } 703 if !reflect.DeepEqual(ab, abw) { 704 t.Errorf("GetAddressBalance() = %+v, want %+v", ab, abw) 705 } 706 rs := ab.ReceivedSat() 707 rsw := dbtestdata.SatB1T2A5.Add(dbtestdata.SatB1T2A5, dbtestdata.SatB2T3A5) 708 if rs.Cmp(rsw) != 0 { 709 t.Errorf("GetAddressBalance().ReceivedSat() = %v, want %v", rs, rsw) 710 } 711 712 ta, err := d.GetTxAddresses(dbtestdata.TxidB2T1) 713 if err != nil { 714 t.Fatal(err) 715 } 716 taw := &TxAddresses{ 717 Height: 225494, 718 Inputs: []TxInput{ 719 { 720 AddrDesc: addressToAddrDesc(dbtestdata.Addr3, d.chainParser), 721 ValueSat: *dbtestdata.SatB1T2A3, 722 }, 723 { 724 AddrDesc: addressToAddrDesc(dbtestdata.Addr2, d.chainParser), 725 ValueSat: *dbtestdata.SatB1T1A2, 726 }, 727 }, 728 Outputs: []TxOutput{ 729 { 730 AddrDesc: addressToAddrDesc(dbtestdata.Addr6, d.chainParser), 731 Spent: true, 732 ValueSat: *dbtestdata.SatB2T1A6, 733 }, 734 { 735 AddrDesc: addressToAddrDesc(dbtestdata.Addr7, d.chainParser), 736 Spent: false, 737 ValueSat: *dbtestdata.SatB2T1A7, 738 }, 739 { 740 AddrDesc: hexToBytes(dbtestdata.TxidB2T1Output3OpReturn), 741 Spent: false, 742 ValueSat: *dbtestdata.SatZero, 743 }, 744 }, 745 } 746 if !reflect.DeepEqual(ta, taw) { 747 t.Errorf("GetTxAddresses() = %+v, want %+v", ta, taw) 748 } 749 ia, _, err := ta.Inputs[0].Addresses(d.chainParser) 750 if err != nil { 751 t.Fatal(err) 752 } 753 if !reflect.DeepEqual(ia, []string{dbtestdata.Addr3}) { 754 t.Errorf("GetTxAddresses().Inputs[0].Addresses() = %v, want %v", ia, []string{dbtestdata.Addr3}) 755 } 756 757 } 758 759 func Test_BulkConnect_BitcoinType(t *testing.T) { 760 d := setupRocksDB(t, &testBitcoinParser{ 761 BitcoinParser: bitcoinTestnetParser(), 762 }) 763 defer closeAndDestroyRocksDB(t, d) 764 765 bc, err := d.InitBulkConnect() 766 if err != nil { 767 t.Fatal(err) 768 } 769 770 if d.is.DbState != common.DbStateInconsistent { 771 t.Fatal("DB not in DbStateInconsistent") 772 } 773 774 if len(d.is.BlockTimes) != 0 { 775 t.Fatal("Expecting is.BlockTimes 0, got ", len(d.is.BlockTimes)) 776 } 777 778 if err := bc.ConnectBlock(dbtestdata.GetTestBitcoinTypeBlock1(d.chainParser), false); err != nil { 779 t.Fatal(err) 780 } 781 if err := checkColumn(d, cfBlockTxs, []keyPair{}); err != nil { 782 { 783 t.Fatal(err) 784 } 785 } 786 787 if err := bc.ConnectBlock(dbtestdata.GetTestBitcoinTypeBlock2(d.chainParser), true); err != nil { 788 t.Fatal(err) 789 } 790 791 if err := bc.Close(); err != nil { 792 t.Fatal(err) 793 } 794 795 if d.is.DbState != common.DbStateOpen { 796 t.Fatal("DB not in DbStateOpen") 797 } 798 799 verifyAfterBitcoinTypeBlock2(t, d) 800 801 if len(d.is.BlockTimes) != 225495 { 802 t.Fatal("Expecting is.BlockTimes 225495, got ", len(d.is.BlockTimes)) 803 } 804 } 805 806 func Test_packBigint_unpackBigint(t *testing.T) { 807 bigbig1, _ := big.NewInt(0).SetString("123456789123456789012345", 10) 808 bigbig2, _ := big.NewInt(0).SetString("12345678912345678901234512389012345123456789123456789012345123456789123456789012345", 10) 809 bigbigbig := big.NewInt(0) 810 bigbigbig.Mul(bigbig2, bigbig2) 811 bigbigbig.Mul(bigbigbig, bigbigbig) 812 bigbigbig.Mul(bigbigbig, bigbigbig) 813 tests := []struct { 814 name string 815 bi *big.Int 816 buf []byte 817 toobiglen int 818 }{ 819 { 820 name: "0", 821 bi: big.NewInt(0), 822 buf: make([]byte, maxPackedBigintBytes), 823 }, 824 { 825 name: "1", 826 bi: big.NewInt(1), 827 buf: make([]byte, maxPackedBigintBytes), 828 }, 829 { 830 name: "54321", 831 bi: big.NewInt(54321), 832 buf: make([]byte, 249), 833 }, 834 { 835 name: "12345678", 836 bi: big.NewInt(12345678), 837 buf: make([]byte, maxPackedBigintBytes), 838 }, 839 { 840 name: "123456789123456789", 841 bi: big.NewInt(123456789123456789), 842 buf: make([]byte, maxPackedBigintBytes), 843 }, 844 { 845 name: "bigbig1", 846 bi: bigbig1, 847 buf: make([]byte, maxPackedBigintBytes), 848 }, 849 { 850 name: "bigbig2", 851 bi: bigbig2, 852 buf: make([]byte, maxPackedBigintBytes), 853 }, 854 { 855 name: "bigbigbig", 856 bi: bigbigbig, 857 buf: make([]byte, maxPackedBigintBytes), 858 toobiglen: 242, 859 }, 860 } 861 for _, tt := range tests { 862 t.Run(tt.name, func(t *testing.T) { 863 // packBigint 864 got := packBigint(tt.bi, tt.buf) 865 if tt.toobiglen == 0 { 866 // create buffer that we expect 867 bb := tt.bi.Bytes() 868 want := append([]byte(nil), byte(len(bb))) 869 want = append(want, bb...) 870 if got != len(want) { 871 t.Errorf("packBigint() = %v, want %v", got, len(want)) 872 } 873 for i := 0; i < got; i++ { 874 if tt.buf[i] != want[i] { 875 t.Errorf("packBigint() buf = %v, want %v", tt.buf[:got], want) 876 break 877 } 878 } 879 // unpackBigint 880 got1, got2 := unpackBigint(tt.buf) 881 if got2 != len(want) { 882 t.Errorf("unpackBigint() = %v, want %v", got2, len(want)) 883 } 884 if tt.bi.Cmp(&got1) != 0 { 885 t.Errorf("unpackBigint() = %v, want %v", got1, tt.bi) 886 } 887 } else { 888 if got != tt.toobiglen { 889 t.Errorf("packBigint() = %v, want toobiglen %v", got, tt.toobiglen) 890 } 891 } 892 }) 893 } 894 } 895 896 func addressToAddrDesc(addr string, parser bchain.BlockChainParser) []byte { 897 b, err := parser.GetAddrDescFromAddress(addr) 898 if err != nil { 899 panic(err) 900 } 901 return b 902 } 903 904 func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { 905 parser := bitcoinTestnetParser() 906 tests := []struct { 907 name string 908 hex string 909 data *TxAddresses 910 }{ 911 { 912 name: "1", 913 hex: "7b0216001443aac20a116e09ea4f7914be1c55e4c17aa600b70016001454633aa8bd2e552bd4e89c01e73c1b7905eb58460811207cb68a199872012d001443aac20a116e09ea4f7914be1c55e4c17aa600b70101", 914 data: &TxAddresses{ 915 Height: 123, 916 Inputs: []TxInput{ 917 { 918 AddrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser), 919 ValueSat: *big.NewInt(0), 920 }, 921 { 922 AddrDesc: addressToAddrDesc("tb1q233n429a9e2jh48gnsq7w0qm0yz7kkzx0qczw8", parser), 923 ValueSat: *big.NewInt(1234123421342341234), 924 }, 925 }, 926 Outputs: []TxOutput{ 927 { 928 AddrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser), 929 ValueSat: *big.NewInt(1), 930 Spent: true, 931 }, 932 }, 933 }, 934 }, 935 { 936 name: "2", 937 hex: "e0390317a9149eb21980dc9d413d8eac27314938b9da920ee53e8705021918f2c017a91409f70b896169c37981d2b54b371df0d81a136a2c870501dd7e28c017a914e371782582a4addb541362c55565d2cdf56f6498870501a1e35ec0052fa9141d9ca71efa36d814424ea6ca1437e67287aebe348705012aadcac02ea91424fbc77cdc62702ade74dcf989c15e5d3f9240bc870501664894c02fa914afbfb74ee994c7d45f6698738bc4226d065266f7870501a1e35ec03276a914d2a37ce20ac9ec4f15dd05a7c6e8e9fbdb99850e88ac043b9943603376a9146b2044146a4438e6e5bfbc65f147afeb64d14fbb88ac05012a05f200", 938 data: &TxAddresses{ 939 Height: 12345, 940 Inputs: []TxInput{ 941 { 942 AddrDesc: addressToAddrDesc("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser), 943 ValueSat: *big.NewInt(9011000000), 944 }, 945 { 946 AddrDesc: addressToAddrDesc("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser), 947 ValueSat: *big.NewInt(8011000000), 948 }, 949 { 950 AddrDesc: addressToAddrDesc("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser), 951 ValueSat: *big.NewInt(7011000000), 952 }, 953 }, 954 Outputs: []TxOutput{ 955 { 956 AddrDesc: addressToAddrDesc("2MuwoFGwABMakU7DCpdGDAKzyj2nTyRagDP", parser), 957 ValueSat: *big.NewInt(5011000000), 958 Spent: true, 959 }, 960 { 961 AddrDesc: addressToAddrDesc("2Mvcmw7qkGXNWzkfH1EjvxDcNRGL1Kf2tEM", parser), 962 ValueSat: *big.NewInt(6011000000), 963 }, 964 { 965 AddrDesc: addressToAddrDesc("2N9GVuX3XJGHS5MCdgn97gVezc6EgvzikTB", parser), 966 ValueSat: *big.NewInt(7011000000), 967 Spent: true, 968 }, 969 { 970 AddrDesc: addressToAddrDesc("mzii3fuRSpExMLJEHdHveW8NmiX8MPgavk", parser), 971 ValueSat: *big.NewInt(999900000), 972 }, 973 { 974 AddrDesc: addressToAddrDesc("mqHPFTRk23JZm9W1ANuEFtwTYwxjESSgKs", parser), 975 ValueSat: *big.NewInt(5000000000), 976 Spent: true, 977 }, 978 }, 979 }, 980 }, 981 { 982 name: "empty address", 983 hex: "baef9a1501000204d2020002162e010162", 984 data: &TxAddresses{ 985 Height: 123456789, 986 Inputs: []TxInput{ 987 { 988 AddrDesc: []byte(nil), 989 ValueSat: *big.NewInt(1234), 990 }, 991 }, 992 Outputs: []TxOutput{ 993 { 994 AddrDesc: []byte(nil), 995 ValueSat: *big.NewInt(5678), 996 }, 997 { 998 AddrDesc: []byte(nil), 999 ValueSat: *big.NewInt(98), 1000 Spent: true, 1001 }, 1002 }, 1003 }, 1004 }, 1005 { 1006 name: "empty", 1007 hex: "000000", 1008 data: &TxAddresses{ 1009 Inputs: []TxInput{}, 1010 Outputs: []TxOutput{}, 1011 }, 1012 }, 1013 } 1014 varBuf := make([]byte, maxPackedBigintBytes) 1015 buf := make([]byte, 1024) 1016 for _, tt := range tests { 1017 t.Run(tt.name, func(t *testing.T) { 1018 b := packTxAddresses(tt.data, buf, varBuf) 1019 hex := hex.EncodeToString(b) 1020 if !reflect.DeepEqual(hex, tt.hex) { 1021 t.Errorf("packTxAddresses() = %v, want %v", hex, tt.hex) 1022 } 1023 got1, err := unpackTxAddresses(b) 1024 if err != nil { 1025 t.Errorf("unpackTxAddresses() error = %v", err) 1026 return 1027 } 1028 if !reflect.DeepEqual(got1, tt.data) { 1029 t.Errorf("unpackTxAddresses() = %+v, want %+v", got1, tt.data) 1030 } 1031 }) 1032 } 1033 } 1034 1035 func Test_packAddrBalance_unpackAddrBalance(t *testing.T) { 1036 parser := bitcoinTestnetParser() 1037 tests := []struct { 1038 name string 1039 hex string 1040 data *AddrBalance 1041 }{ 1042 { 1043 name: "no utxos", 1044 hex: "7b060b44cc1af8520514faf980ac", 1045 data: &AddrBalance{ 1046 BalanceSat: *big.NewInt(90110001324), 1047 SentSat: *big.NewInt(12390110001234), 1048 Txs: 123, 1049 Utxos: []Utxo{}, 1050 }, 1051 }, 1052 { 1053 name: "utxos", 1054 hex: "7b060b44cc1af8520514faf980ac00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa38400c87c440060b2fd12177a6effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac750098faf659010105e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b0782c6df6d84ccd88552087e9cba87a275ffff", 1055 data: &AddrBalance{ 1056 BalanceSat: *big.NewInt(90110001324), 1057 SentSat: *big.NewInt(12390110001234), 1058 Txs: 123, 1059 Utxos: []Utxo{ 1060 { 1061 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1062 Vout: 12, 1063 Height: 123456, 1064 ValueSat: *big.NewInt(12390110001234 - 90110001324), 1065 }, 1066 { 1067 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1068 Vout: 0, 1069 Height: 52345689, 1070 ValueSat: *big.NewInt(1), 1071 }, 1072 { 1073 BtxID: hexToBytes(dbtestdata.TxidB2T3), 1074 Vout: 5353453, 1075 Height: 1234567890, 1076 ValueSat: *big.NewInt(9123372036854775807), 1077 }, 1078 }, 1079 }, 1080 }, 1081 { 1082 name: "empty", 1083 hex: "000000", 1084 data: &AddrBalance{ 1085 Utxos: []Utxo{}, 1086 }, 1087 }, 1088 } 1089 varBuf := make([]byte, maxPackedBigintBytes) 1090 buf := make([]byte, 32) 1091 for _, tt := range tests { 1092 t.Run(tt.name, func(t *testing.T) { 1093 b := packAddrBalance(tt.data, buf, varBuf) 1094 hex := hex.EncodeToString(b) 1095 if !reflect.DeepEqual(hex, tt.hex) { 1096 t.Errorf("packTxAddresses() = %v, want %v", hex, tt.hex) 1097 } 1098 got1, err := unpackAddrBalance(b, parser.PackedTxidLen(), AddressBalanceDetailUTXO) 1099 if err != nil { 1100 t.Errorf("unpackTxAddresses() error = %v", err) 1101 return 1102 } 1103 if !reflect.DeepEqual(got1, tt.data) { 1104 t.Errorf("unpackTxAddresses() = %+v, want %+v", got1, tt.data) 1105 } 1106 }) 1107 } 1108 } 1109 1110 func createUtxoMap(ab *AddrBalance) { 1111 l := len(ab.Utxos) 1112 ab.utxosMap = make(map[string]int, 32) 1113 for i := 0; i < l; i++ { 1114 s := string(ab.Utxos[i].BtxID) 1115 if _, e := ab.utxosMap[s]; !e { 1116 ab.utxosMap[s] = i 1117 } 1118 } 1119 } 1120 func TestAddrBalance_utxo_methods(t *testing.T) { 1121 ab := &AddrBalance{ 1122 Txs: 10, 1123 SentSat: *big.NewInt(10000), 1124 BalanceSat: *big.NewInt(1000), 1125 } 1126 1127 // addUtxo 1128 ab.addUtxo(&Utxo{ 1129 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1130 Vout: 1, 1131 Height: 5000, 1132 ValueSat: *big.NewInt(100), 1133 }) 1134 ab.addUtxo(&Utxo{ 1135 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1136 Vout: 4, 1137 Height: 5000, 1138 ValueSat: *big.NewInt(100), 1139 }) 1140 ab.addUtxo(&Utxo{ 1141 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1142 Vout: 0, 1143 Height: 5001, 1144 ValueSat: *big.NewInt(800), 1145 }) 1146 want := &AddrBalance{ 1147 Txs: 10, 1148 SentSat: *big.NewInt(10000), 1149 BalanceSat: *big.NewInt(1000), 1150 Utxos: []Utxo{ 1151 { 1152 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1153 Vout: 1, 1154 Height: 5000, 1155 ValueSat: *big.NewInt(100), 1156 }, 1157 { 1158 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1159 Vout: 4, 1160 Height: 5000, 1161 ValueSat: *big.NewInt(100), 1162 }, 1163 { 1164 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1165 Vout: 0, 1166 Height: 5001, 1167 ValueSat: *big.NewInt(800), 1168 }, 1169 }, 1170 } 1171 if !reflect.DeepEqual(ab, want) { 1172 t.Errorf("addUtxo, got %+v, want %+v", ab, want) 1173 } 1174 1175 // addUtxoInDisconnect 1176 ab.addUtxoInDisconnect(&Utxo{ 1177 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1178 Vout: 0, 1179 Height: 5003, 1180 ValueSat: *big.NewInt(800), 1181 }) 1182 ab.addUtxoInDisconnect(&Utxo{ 1183 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1184 Vout: 1, 1185 Height: 5003, 1186 ValueSat: *big.NewInt(800), 1187 }) 1188 ab.addUtxoInDisconnect(&Utxo{ 1189 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1190 Vout: 10, 1191 Height: 5000, 1192 ValueSat: *big.NewInt(100), 1193 }) 1194 ab.addUtxoInDisconnect(&Utxo{ 1195 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1196 Vout: 2, 1197 Height: 5000, 1198 ValueSat: *big.NewInt(100), 1199 }) 1200 ab.addUtxoInDisconnect(&Utxo{ 1201 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1202 Vout: 0, 1203 Height: 5000, 1204 ValueSat: *big.NewInt(100), 1205 }) 1206 want = &AddrBalance{ 1207 Txs: 10, 1208 SentSat: *big.NewInt(10000), 1209 BalanceSat: *big.NewInt(1000), 1210 Utxos: []Utxo{ 1211 { 1212 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1213 Vout: 0, 1214 Height: 5000, 1215 ValueSat: *big.NewInt(100), 1216 }, 1217 { 1218 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1219 Vout: 1, 1220 Height: 5000, 1221 ValueSat: *big.NewInt(100), 1222 }, 1223 { 1224 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1225 Vout: 2, 1226 Height: 5000, 1227 ValueSat: *big.NewInt(100), 1228 }, 1229 { 1230 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1231 Vout: 4, 1232 Height: 5000, 1233 ValueSat: *big.NewInt(100), 1234 }, 1235 { 1236 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1237 Vout: 10, 1238 Height: 5000, 1239 ValueSat: *big.NewInt(100), 1240 }, 1241 { 1242 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1243 Vout: 0, 1244 Height: 5001, 1245 ValueSat: *big.NewInt(800), 1246 }, 1247 { 1248 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1249 Vout: 0, 1250 Height: 5003, 1251 ValueSat: *big.NewInt(800), 1252 }, 1253 { 1254 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1255 Vout: 1, 1256 Height: 5003, 1257 ValueSat: *big.NewInt(800), 1258 }, 1259 }, 1260 } 1261 if !reflect.DeepEqual(ab, want) { 1262 t.Errorf("addUtxoInDisconnect, got %+v, want %+v", ab, want) 1263 } 1264 1265 // markUtxoAsSpent 1266 ab.markUtxoAsSpent(hexToBytes(dbtestdata.TxidB2T1), 0) 1267 want.Utxos[6].Vout = -1 1268 if !reflect.DeepEqual(ab, want) { 1269 t.Errorf("markUtxoAsSpent, got %+v, want %+v", ab, want) 1270 } 1271 1272 // addUtxo with utxosMap 1273 for i := 0; i < 20; i += 2 { 1274 utxo := Utxo{ 1275 BtxID: hexToBytes(dbtestdata.TxidB2T2), 1276 Vout: int32(i), 1277 Height: 5009, 1278 ValueSat: *big.NewInt(800), 1279 } 1280 ab.addUtxo(&utxo) 1281 want.Utxos = append(want.Utxos, utxo) 1282 } 1283 createUtxoMap(want) 1284 if !reflect.DeepEqual(ab, want) { 1285 t.Errorf("addUtxo with utxosMap, got %+v, want %+v", ab, want) 1286 } 1287 1288 // markUtxoAsSpent with utxosMap 1289 ab.markUtxoAsSpent(hexToBytes(dbtestdata.TxidB2T1), 1) 1290 want.Utxos[7].Vout = -1 1291 if !reflect.DeepEqual(ab, want) { 1292 t.Errorf("markUtxoAsSpent with utxosMap, got %+v, want %+v", ab, want) 1293 } 1294 1295 // addUtxoInDisconnect with utxosMap 1296 ab.addUtxoInDisconnect(&Utxo{ 1297 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1298 Vout: 3, 1299 Height: 5000, 1300 ValueSat: *big.NewInt(100), 1301 }) 1302 want.Utxos = append(want.Utxos, Utxo{}) 1303 copy(want.Utxos[3+1:], want.Utxos[3:]) 1304 want.Utxos[3] = Utxo{ 1305 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1306 Vout: 3, 1307 Height: 5000, 1308 ValueSat: *big.NewInt(100), 1309 } 1310 want.utxosMap = nil 1311 if !reflect.DeepEqual(ab, want) { 1312 t.Errorf("addUtxoInDisconnect with utxosMap, got %+v, want %+v", ab, want) 1313 } 1314 1315 } 1316 1317 func Test_reorderUtxo(t *testing.T) { 1318 utxos := []Utxo{ 1319 { 1320 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1321 Vout: 3, 1322 }, 1323 { 1324 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1325 Vout: 1, 1326 }, 1327 { 1328 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1329 Vout: 0, 1330 }, 1331 { 1332 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1333 Vout: 0, 1334 }, 1335 { 1336 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1337 Vout: 2, 1338 }, 1339 { 1340 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1341 Vout: 1, 1342 }, 1343 { 1344 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1345 Vout: 2, 1346 }, 1347 { 1348 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1349 Vout: 0, 1350 }, 1351 } 1352 tests := []struct { 1353 name string 1354 utxos []Utxo 1355 index int 1356 want []Utxo 1357 }{ 1358 { 1359 name: "middle", 1360 utxos: utxos, 1361 index: 4, 1362 want: []Utxo{ 1363 { 1364 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1365 Vout: 3, 1366 }, 1367 { 1368 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1369 Vout: 1, 1370 }, 1371 { 1372 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1373 Vout: 0, 1374 }, 1375 { 1376 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1377 Vout: 0, 1378 }, 1379 { 1380 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1381 Vout: 1, 1382 }, 1383 { 1384 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1385 Vout: 2, 1386 }, 1387 { 1388 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1389 Vout: 2, 1390 }, 1391 { 1392 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1393 Vout: 0, 1394 }, 1395 }, 1396 }, 1397 { 1398 name: "start", 1399 utxos: utxos, 1400 index: 1, 1401 want: []Utxo{ 1402 { 1403 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1404 Vout: 0, 1405 }, 1406 { 1407 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1408 Vout: 1, 1409 }, 1410 { 1411 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1412 Vout: 3, 1413 }, 1414 { 1415 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1416 Vout: 0, 1417 }, 1418 { 1419 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1420 Vout: 1, 1421 }, 1422 { 1423 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1424 Vout: 2, 1425 }, 1426 { 1427 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1428 Vout: 2, 1429 }, 1430 { 1431 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1432 Vout: 0, 1433 }, 1434 }, 1435 }, 1436 { 1437 name: "end", 1438 utxos: utxos, 1439 index: 6, 1440 want: []Utxo{ 1441 { 1442 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1443 Vout: 0, 1444 }, 1445 { 1446 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1447 Vout: 1, 1448 }, 1449 { 1450 BtxID: hexToBytes(dbtestdata.TxidB1T1), 1451 Vout: 3, 1452 }, 1453 { 1454 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1455 Vout: 0, 1456 }, 1457 { 1458 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1459 Vout: 1, 1460 }, 1461 { 1462 BtxID: hexToBytes(dbtestdata.TxidB1T2), 1463 Vout: 2, 1464 }, 1465 { 1466 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1467 Vout: 0, 1468 }, 1469 { 1470 BtxID: hexToBytes(dbtestdata.TxidB2T1), 1471 Vout: 2, 1472 }, 1473 }, 1474 }, 1475 } 1476 for _, tt := range tests { 1477 t.Run(tt.name, func(t *testing.T) { 1478 reorderUtxo(tt.utxos, tt.index) 1479 if !reflect.DeepEqual(tt.utxos, tt.want) { 1480 t.Errorf("reorderUtxo %s, got %+v, want %+v", tt.name, tt.utxos, tt.want) 1481 } 1482 }) 1483 } 1484 } 1485 1486 func TestRocksTickers(t *testing.T) { 1487 d := setupRocksDB(t, &testBitcoinParser{ 1488 BitcoinParser: bitcoinTestnetParser(), 1489 }) 1490 defer closeAndDestroyRocksDB(t, d) 1491 1492 // Test valid formats 1493 for _, date := range []string{"20190130", "2019013012", "201901301250", "20190130125030"} { 1494 _, err := FiatRatesConvertDate(date) 1495 if err != nil { 1496 t.Errorf("%v", err) 1497 } 1498 } 1499 1500 // Test invalid formats 1501 for _, date := range []string{"01102019", "10201901", "", "abc", "20190130xxx"} { 1502 _, err := FiatRatesConvertDate(date) 1503 if err == nil { 1504 t.Errorf("Wrongly-formatted date \"%v\" marked as valid!", date) 1505 } 1506 } 1507 1508 // Test storing & finding tickers 1509 key, _ := time.Parse(FiatRatesTimeFormat, "20190627000000") 1510 futureKey, _ := time.Parse(FiatRatesTimeFormat, "20190630000000") 1511 1512 ts1, _ := time.Parse(FiatRatesTimeFormat, "20190628000000") 1513 ticker1 := &CurrencyRatesTicker{ 1514 Timestamp: &ts1, 1515 Rates: map[string]float64{ 1516 "usd": 20000, 1517 }, 1518 } 1519 1520 ts2, _ := time.Parse(FiatRatesTimeFormat, "20190629000000") 1521 ticker2 := &CurrencyRatesTicker{ 1522 Timestamp: &ts2, 1523 Rates: map[string]float64{ 1524 "usd": 30000, 1525 }, 1526 } 1527 err := d.FiatRatesStoreTicker(ticker1) 1528 if err != nil { 1529 t.Errorf("Error storing ticker! %v", err) 1530 } 1531 d.FiatRatesStoreTicker(ticker2) 1532 if err != nil { 1533 t.Errorf("Error storing ticker! %v", err) 1534 } 1535 1536 ticker, err := d.FiatRatesFindTicker(&key) // should find the closest key (ticker1) 1537 if err != nil { 1538 t.Errorf("TestRocksTickers err: %+v", err) 1539 } else if ticker == nil { 1540 t.Errorf("Ticker not found") 1541 } else if ticker.Timestamp.Format(FiatRatesTimeFormat) != ticker1.Timestamp.Format(FiatRatesTimeFormat) { 1542 t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) 1543 } 1544 1545 ticker, err = d.FiatRatesFindLastTicker() // should find the last key (ticker2) 1546 if err != nil { 1547 t.Errorf("TestRocksTickers err: %+v", err) 1548 } else if ticker == nil { 1549 t.Errorf("Ticker not found") 1550 } else if ticker.Timestamp.Format(FiatRatesTimeFormat) != ticker2.Timestamp.Format(FiatRatesTimeFormat) { 1551 t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp) 1552 } 1553 1554 ticker, err = d.FiatRatesFindTicker(&futureKey) // should not find anything 1555 if err != nil { 1556 t.Errorf("TestRocksTickers err: %+v", err) 1557 } else if ticker != nil { 1558 t.Errorf("Ticker found, but the timestamp is older than the last ticker entry.") 1559 } 1560 }