github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/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 "fmt" 13 "io/ioutil" 14 "math/big" 15 "os" 16 "reflect" 17 "sort" 18 "strings" 19 "testing" 20 21 vlq "github.com/bsm/go-vlq" 22 "github.com/jakm/btcutil/chaincfg" 23 "github.com/juju/errors" 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 func bitcoinTestnetParser() *btc.BitcoinParser { 37 return btc.NewBitcoinParser( 38 btc.GetChainParams("test"), 39 &btc.Configuration{BlockAddressesToKeep: 1}) 40 } 41 42 func setupRocksDB(t *testing.T, p bchain.BlockChainParser) *RocksDB { 43 tmp, err := ioutil.TempDir("", "testdb") 44 if err != nil { 45 t.Fatal(err) 46 } 47 d, err := NewRocksDB(tmp, 100000, -1, p, nil) 48 if err != nil { 49 t.Fatal(err) 50 } 51 is, err := d.LoadInternalState("btc-testnet") 52 if err != nil { 53 t.Fatal(err) 54 } 55 d.SetInternalState(is) 56 return d 57 } 58 59 func closeAndDestroyRocksDB(t *testing.T, d *RocksDB) { 60 if err := d.Close(); err != nil { 61 t.Fatal(err) 62 } 63 os.RemoveAll(d.path) 64 } 65 66 func inputAddressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 67 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 68 return hex.EncodeToString([]byte{byte(len(h) / 2)}) + h 69 } 70 71 func addressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 72 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 73 return hex.EncodeToString([]byte{byte(len(h))}) + h 74 } 75 76 func spentAddressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string { 77 h := dbtestdata.AddressToPubKeyHex(addr, d.chainParser) 78 return hex.EncodeToString([]byte{byte(len(h) + 1)}) + h 79 } 80 81 func bigintToHex(i *big.Int) string { 82 b := make([]byte, maxPackedBigintBytes) 83 l := packBigint(i, b) 84 return hex.EncodeToString(b[:l]) 85 } 86 87 func varuintToHex(i uint) string { 88 b := make([]byte, vlq.MaxLen64) 89 l := vlq.PutUint(b, uint64(i)) 90 return hex.EncodeToString(b[:l]) 91 } 92 93 func uintToHex(i uint32) string { 94 buf := make([]byte, 4) 95 binary.BigEndian.PutUint32(buf, i) 96 return hex.EncodeToString(buf) 97 } 98 99 // keyPair is used to compare given key value in DB with expected 100 // for more complicated compares it is possible to specify CompareFunc 101 type keyPair struct { 102 Key, Value string 103 CompareFunc func(string) bool 104 } 105 106 func compareFuncBlockAddresses(t *testing.T, v string, expected []string) bool { 107 for _, e := range expected { 108 lb := len(v) 109 v = strings.Replace(v, e, "", 1) 110 if lb == len(v) { 111 t.Error(e, " not found in ", v) 112 return false 113 } 114 } 115 if len(v) != 0 { 116 t.Error("not expected content ", v) 117 } 118 return len(v) == 0 119 } 120 121 func checkColumn(d *RocksDB, col int, kp []keyPair) error { 122 sort.Slice(kp, func(i, j int) bool { 123 return kp[i].Key < kp[j].Key 124 }) 125 it := d.db.NewIteratorCF(d.ro, d.cfh[col]) 126 defer it.Close() 127 i := 0 128 for it.SeekToFirst(); it.Valid(); it.Next() { 129 if i >= len(kp) { 130 return errors.Errorf("Expected less rows in column %v", cfNames[col]) 131 } 132 key := hex.EncodeToString(it.Key().Data()) 133 if key != kp[i].Key { 134 return errors.Errorf("Incorrect key %v found in column %v row %v, expecting %v", key, cfNames[col], i, kp[i].Key) 135 } 136 val := hex.EncodeToString(it.Value().Data()) 137 var valOK bool 138 if kp[i].CompareFunc == nil { 139 valOK = val == kp[i].Value 140 } else { 141 valOK = kp[i].CompareFunc(val) 142 } 143 if !valOK { 144 return errors.Errorf("Incorrect value %v found in column %v row %v key %v, expecting %v", val, cfNames[col], i, key, kp[i].Value) 145 } 146 i++ 147 } 148 if i != len(kp) { 149 return errors.Errorf("Expected more rows in column %v: got %v, expected %v", cfNames[col], i, len(kp)) 150 } 151 return nil 152 } 153 154 func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, afterDisconnect bool) { 155 if err := checkColumn(d, cfHeight, []keyPair{ 156 keyPair{ 157 "000370d5", 158 "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567), 159 nil, 160 }, 161 }); err != nil { 162 { 163 t.Fatal(err) 164 } 165 } 166 // the vout is encoded as signed varint, i.e. value * 2 for non negative values 167 if err := checkColumn(d, cfAddresses, []keyPair{ 168 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser) + "000370d5", dbtestdata.TxidB1T1 + "00", nil}, 169 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser) + "000370d5", dbtestdata.TxidB1T1 + "02", nil}, 170 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "00", nil}, 171 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "02", nil}, 172 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "04", nil}, 173 }); err != nil { 174 { 175 t.Fatal(err) 176 } 177 } 178 if err := checkColumn(d, cfTxAddresses, []keyPair{ 179 keyPair{ 180 dbtestdata.TxidB1T1, 181 varuintToHex(225493) + 182 "00" + 183 "02" + 184 addressToPubKeyHexWithLength(dbtestdata.Addr1, t, d) + bigintToHex(dbtestdata.SatB1T1A1) + 185 addressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2), 186 nil, 187 }, 188 keyPair{ 189 dbtestdata.TxidB1T2, 190 varuintToHex(225493) + 191 "00" + 192 "03" + 193 addressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 194 addressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 195 addressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5), 196 nil, 197 }, 198 }); err != nil { 199 { 200 t.Fatal(err) 201 } 202 } 203 if err := checkColumn(d, cfAddressBalance, []keyPair{ 204 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1), nil}, 205 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A2), nil}, 206 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A3), nil}, 207 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A4), nil}, 208 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A5), nil}, 209 }); err != nil { 210 { 211 t.Fatal(err) 212 } 213 } 214 215 var blockTxsKp []keyPair 216 if afterDisconnect { 217 blockTxsKp = []keyPair{} 218 } else { 219 blockTxsKp = []keyPair{ 220 keyPair{ 221 "000370d5", 222 dbtestdata.TxidB1T1 + "00" + dbtestdata.TxidB1T2 + "00", 223 nil, 224 }, 225 } 226 } 227 228 if err := checkColumn(d, cfBlockTxs, blockTxsKp); err != nil { 229 { 230 t.Fatal(err) 231 } 232 } 233 } 234 235 func verifyAfterUTXOBlock2(t *testing.T, d *RocksDB) { 236 if err := checkColumn(d, cfHeight, []keyPair{ 237 keyPair{ 238 "000370d5", 239 "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" + uintToHex(1534858021) + varuintToHex(2) + varuintToHex(1234567), 240 nil, 241 }, 242 keyPair{ 243 "000370d6", 244 "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" + uintToHex(1534859123) + varuintToHex(4) + varuintToHex(2345678), 245 nil, 246 }, 247 }); err != nil { 248 { 249 t.Fatal(err) 250 } 251 } 252 if err := checkColumn(d, cfAddresses, []keyPair{ 253 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser) + "000370d5", dbtestdata.TxidB1T1 + "00", nil}, 254 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser) + "000370d5", dbtestdata.TxidB1T1 + "02", nil}, 255 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "00", nil}, 256 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "02", nil}, 257 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser) + "000370d5", dbtestdata.TxidB1T2 + "04", nil}, 258 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr6, d.chainParser) + "000370d6", dbtestdata.TxidB2T1 + "00" + dbtestdata.TxidB2T2 + "01", nil}, 259 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr7, d.chainParser) + "000370d6", dbtestdata.TxidB2T1 + "02", nil}, 260 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr8, d.chainParser) + "000370d6", dbtestdata.TxidB2T2 + "00", nil}, 261 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr9, d.chainParser) + "000370d6", dbtestdata.TxidB2T2 + "02", nil}, 262 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser) + "000370d6", dbtestdata.TxidB2T1 + "01", nil}, 263 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser) + "000370d6", dbtestdata.TxidB2T1 + "03", nil}, 264 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser) + "000370d6", dbtestdata.TxidB2T3 + "00" + dbtestdata.TxidB2T3 + "01", nil}, 265 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.AddrA, d.chainParser) + "000370d6", dbtestdata.TxidB2T4 + "00", nil}, 266 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser) + "000370d6", dbtestdata.TxidB2T2 + "03", nil}, 267 }); err != nil { 268 { 269 t.Fatal(err) 270 } 271 } 272 if err := checkColumn(d, cfTxAddresses, []keyPair{ 273 keyPair{ 274 dbtestdata.TxidB1T1, 275 varuintToHex(225493) + 276 "00" + 277 "02" + 278 addressToPubKeyHexWithLength(dbtestdata.Addr1, t, d) + bigintToHex(dbtestdata.SatB1T1A1) + 279 spentAddressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2), 280 nil, 281 }, 282 keyPair{ 283 dbtestdata.TxidB1T2, 284 varuintToHex(225493) + 285 "00" + 286 "03" + 287 spentAddressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 288 spentAddressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 289 spentAddressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5), 290 nil, 291 }, 292 keyPair{ 293 dbtestdata.TxidB2T1, 294 varuintToHex(225494) + 295 "02" + 296 inputAddressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) + 297 inputAddressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2) + 298 "02" + 299 spentAddressToPubKeyHexWithLength(dbtestdata.Addr6, t, d) + bigintToHex(dbtestdata.SatB2T1A6) + 300 addressToPubKeyHexWithLength(dbtestdata.Addr7, t, d) + bigintToHex(dbtestdata.SatB2T1A7), 301 nil, 302 }, 303 keyPair{ 304 dbtestdata.TxidB2T2, 305 varuintToHex(225494) + 306 "02" + 307 inputAddressToPubKeyHexWithLength(dbtestdata.Addr6, t, d) + bigintToHex(dbtestdata.SatB2T1A6) + 308 inputAddressToPubKeyHexWithLength(dbtestdata.Addr4, t, d) + bigintToHex(dbtestdata.SatB1T2A4) + 309 "02" + 310 addressToPubKeyHexWithLength(dbtestdata.Addr8, t, d) + bigintToHex(dbtestdata.SatB2T2A8) + 311 addressToPubKeyHexWithLength(dbtestdata.Addr9, t, d) + bigintToHex(dbtestdata.SatB2T2A9), 312 nil, 313 }, 314 keyPair{ 315 dbtestdata.TxidB2T3, 316 varuintToHex(225494) + 317 "01" + 318 inputAddressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB1T2A5) + 319 "01" + 320 addressToPubKeyHexWithLength(dbtestdata.Addr5, t, d) + bigintToHex(dbtestdata.SatB2T3A5), 321 nil, 322 }, 323 keyPair{ 324 dbtestdata.TxidB2T4, 325 varuintToHex(225494) + 326 "01" + inputAddressToPubKeyHexWithLength("", t, d) + bigintToHex(dbtestdata.SatZero) + 327 "02" + 328 addressToPubKeyHexWithLength(dbtestdata.AddrA, t, d) + bigintToHex(dbtestdata.SatB2T4AA) + 329 addressToPubKeyHexWithLength("", t, d) + bigintToHex(dbtestdata.SatZero), 330 nil, 331 }, 332 }); err != nil { 333 { 334 t.Fatal(err) 335 } 336 } 337 if err := checkColumn(d, cfAddressBalance, []keyPair{ 338 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1), nil}, 339 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T1A2) + bigintToHex(dbtestdata.SatZero), nil}, 340 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A3) + bigintToHex(dbtestdata.SatZero), nil}, 341 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A4) + bigintToHex(dbtestdata.SatZero), nil}, 342 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A5) + bigintToHex(dbtestdata.SatB2T3A5), nil}, 343 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr6, d.chainParser), "02" + bigintToHex(dbtestdata.SatB2T1A6) + bigintToHex(dbtestdata.SatZero), nil}, 344 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr7, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T1A7), nil}, 345 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr8, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A8), nil}, 346 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr9, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A9), nil}, 347 keyPair{dbtestdata.AddressToPubKeyHex(dbtestdata.AddrA, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T4AA), nil}, 348 }); err != nil { 349 { 350 t.Fatal(err) 351 } 352 } 353 if err := checkColumn(d, cfBlockTxs, []keyPair{ 354 keyPair{ 355 "000370d6", 356 dbtestdata.TxidB2T1 + "02" + dbtestdata.TxidB1T2 + "00" + dbtestdata.TxidB1T1 + "02" + 357 dbtestdata.TxidB2T2 + "02" + dbtestdata.TxidB2T1 + "00" + dbtestdata.TxidB1T2 + "02" + 358 dbtestdata.TxidB2T3 + "01" + dbtestdata.TxidB1T2 + "04" + 359 dbtestdata.TxidB2T4 + "01" + "0000000000000000000000000000000000000000000000000000000000000000" + "00", 360 nil, 361 }, 362 }); err != nil { 363 { 364 t.Fatal(err) 365 } 366 } 367 } 368 369 type txidVoutOutput struct { 370 txid string 371 vout uint32 372 isOutput bool 373 } 374 375 func verifyGetTransactions(t *testing.T, d *RocksDB, addr string, low, high uint32, wantTxids []txidVoutOutput, wantErr error) { 376 gotTxids := make([]txidVoutOutput, 0) 377 addToTxids := func(txid string, vout uint32, isOutput bool) error { 378 gotTxids = append(gotTxids, txidVoutOutput{txid, vout, isOutput}) 379 return nil 380 } 381 if err := d.GetTransactions(addr, low, high, addToTxids); err != nil { 382 if wantErr == nil || wantErr.Error() != err.Error() { 383 t.Fatal(err) 384 } 385 } 386 if !reflect.DeepEqual(gotTxids, wantTxids) { 387 t.Errorf("GetTransactions() = %v, want %v", gotTxids, wantTxids) 388 } 389 } 390 391 type testBitcoinParser struct { 392 *btc.BitcoinParser 393 } 394 395 // override PackTx and UnpackTx to default BaseParser functionality 396 // BitcoinParser uses tx hex which is not available for the test transactions 397 func (p *testBitcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { 398 return p.BaseParser.PackTx(tx, height, blockTime) 399 } 400 401 func (p *testBitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { 402 return p.BaseParser.UnpackTx(buf) 403 } 404 405 func testTxCache(t *testing.T, d *RocksDB, b *bchain.Block, tx *bchain.Tx) { 406 if err := d.PutTx(tx, b.Height, tx.Blocktime); err != nil { 407 t.Fatal(err) 408 } 409 gtx, height, err := d.GetTx(tx.Txid) 410 if err != nil { 411 t.Fatal(err) 412 } 413 if b.Height != height { 414 t.Fatalf("GetTx: got height %v, expected %v", height, b.Height) 415 } 416 // Confirmations are not stored in the DB, set them from input tx 417 gtx.Confirmations = tx.Confirmations 418 if fmt.Sprint(gtx) != fmt.Sprint(tx) { 419 t.Errorf("GetTx: %v, want %v", gtx, tx) 420 } 421 if err := d.DeleteTx(tx.Txid); err != nil { 422 t.Fatal(err) 423 } 424 } 425 426 // TestRocksDB_Index_UTXO is an integration test probing the whole indexing functionality for UTXO chains 427 // It does the following: 428 // 1) Connect two blocks (inputs from 2nd block are spending some outputs from the 1st block) 429 // 2) GetTransactions for various addresses / low-high ranges 430 // 3) GetBestBlock, GetBlockHash 431 // 4) Test tx caching functionality 432 // 5) Disconnect block 2 - expect error 433 // 6) Disconnect the block 2 using BlockTxs column 434 // 7) Reconnect block 2 and check 435 // After each step, the content of DB is examined and any difference against expected state is regarded as failure 436 func TestRocksDB_Index_UTXO(t *testing.T) { 437 d := setupRocksDB(t, &testBitcoinParser{ 438 BitcoinParser: bitcoinTestnetParser(), 439 }) 440 defer closeAndDestroyRocksDB(t, d) 441 442 // connect 1st block - will log warnings about missing UTXO transactions in txAddresses column 443 block1 := dbtestdata.GetTestUTXOBlock1(d.chainParser) 444 if err := d.ConnectBlock(block1); err != nil { 445 t.Fatal(err) 446 } 447 verifyAfterUTXOBlock1(t, d, false) 448 449 // connect 2nd block - use some outputs from the 1st block as the inputs and 1 input uses tx from the same block 450 block2 := dbtestdata.GetTestUTXOBlock2(d.chainParser) 451 if err := d.ConnectBlock(block2); err != nil { 452 t.Fatal(err) 453 } 454 verifyAfterUTXOBlock2(t, d) 455 456 // get transactions for various addresses / low-high ranges 457 verifyGetTransactions(t, d, dbtestdata.Addr2, 0, 1000000, []txidVoutOutput{ 458 txidVoutOutput{dbtestdata.TxidB1T1, 1, true}, 459 txidVoutOutput{dbtestdata.TxidB2T1, 1, false}, 460 }, nil) 461 verifyGetTransactions(t, d, dbtestdata.Addr2, 225493, 225493, []txidVoutOutput{ 462 txidVoutOutput{dbtestdata.TxidB1T1, 1, true}, 463 }, nil) 464 verifyGetTransactions(t, d, dbtestdata.Addr2, 225494, 1000000, []txidVoutOutput{ 465 txidVoutOutput{dbtestdata.TxidB2T1, 1, false}, 466 }, nil) 467 verifyGetTransactions(t, d, dbtestdata.Addr2, 500000, 1000000, []txidVoutOutput{}, nil) 468 verifyGetTransactions(t, d, dbtestdata.Addr8, 0, 1000000, []txidVoutOutput{ 469 txidVoutOutput{dbtestdata.TxidB2T2, 0, true}, 470 }, nil) 471 verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidVoutOutput{}, errors.New("checksum mismatch")) 472 473 // GetBestBlock 474 height, hash, err := d.GetBestBlock() 475 if err != nil { 476 t.Fatal(err) 477 } 478 if height != 225494 { 479 t.Fatalf("GetBestBlock: got height %v, expected %v", height, 225494) 480 } 481 if hash != "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6" { 482 t.Fatalf("GetBestBlock: got hash %v, expected %v", hash, "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6") 483 } 484 485 // GetBlockHash 486 hash, err = d.GetBlockHash(225493) 487 if err != nil { 488 t.Fatal(err) 489 } 490 if hash != "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997" { 491 t.Fatalf("GetBlockHash: got hash %v, expected %v", hash, "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997") 492 } 493 494 // Not connected block 495 hash, err = d.GetBlockHash(225495) 496 if err != nil { 497 t.Fatal(err) 498 } 499 if hash != "" { 500 t.Fatalf("GetBlockHash: got hash '%v', expected ''", hash) 501 } 502 503 // GetBlockHash 504 info, err := d.GetBlockInfo(225494) 505 if err != nil { 506 t.Fatal(err) 507 } 508 iw := &BlockInfo{ 509 Hash: "00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6", 510 Txs: 4, 511 Size: 2345678, 512 Time: 1534859123, 513 Height: 225494, 514 } 515 if !reflect.DeepEqual(info, iw) { 516 t.Errorf("GetAddressBalance() = %+v, want %+v", info, iw) 517 } 518 519 // Test tx caching functionality, leave one tx in db to test cleanup in DisconnectBlock 520 testTxCache(t, d, block1, &block1.Txs[0]) 521 testTxCache(t, d, block2, &block2.Txs[0]) 522 if err = d.PutTx(&block2.Txs[1], block2.Height, block2.Txs[1].Blocktime); err != nil { 523 t.Fatal(err) 524 } 525 // check that there is only the last tx in the cache 526 packedTx, err := d.chainParser.PackTx(&block2.Txs[1], block2.Height, block2.Txs[1].Blocktime) 527 if err := checkColumn(d, cfTransactions, []keyPair{ 528 keyPair{block2.Txs[1].Txid, hex.EncodeToString(packedTx), nil}, 529 }); err != nil { 530 { 531 t.Fatal(err) 532 } 533 } 534 535 // DisconnectBlock for UTXO chains is not possible 536 err = d.DisconnectBlock(block2) 537 if err == nil || err.Error() != "DisconnectBlock is not supported for UTXO chains" { 538 t.Fatal(err) 539 } 540 verifyAfterUTXOBlock2(t, d) 541 542 // try to disconnect both blocks, however only the last one is kept, it is not possible 543 err = d.DisconnectBlockRangeUTXO(225493, 225494) 544 if err == nil || err.Error() != "Cannot disconnect blocks with height 225493 and lower. It is necessary to rebuild index." { 545 t.Fatal(err) 546 } 547 verifyAfterUTXOBlock2(t, d) 548 549 // disconnect the 2nd block, verify that the db contains only data from the 1st block with restored unspentTxs 550 // and that the cached tx is removed 551 err = d.DisconnectBlockRangeUTXO(225494, 225494) 552 if err != nil { 553 t.Fatal(err) 554 } 555 verifyAfterUTXOBlock1(t, d, true) 556 if err := checkColumn(d, cfTransactions, []keyPair{}); err != nil { 557 { 558 t.Fatal(err) 559 } 560 } 561 562 // connect block again and verify the state of db 563 if err := d.ConnectBlock(block2); err != nil { 564 t.Fatal(err) 565 } 566 verifyAfterUTXOBlock2(t, d) 567 568 // test public methods for address balance and tx addresses 569 570 ab, err := d.GetAddressBalance(dbtestdata.Addr5) 571 if err != nil { 572 t.Fatal(err) 573 } 574 abw := &AddrBalance{ 575 Txs: 2, 576 SentSat: *dbtestdata.SatB1T2A5, 577 BalanceSat: *dbtestdata.SatB2T3A5, 578 } 579 if !reflect.DeepEqual(ab, abw) { 580 t.Errorf("GetAddressBalance() = %+v, want %+v", ab, abw) 581 } 582 rs := ab.ReceivedSat() 583 rsw := dbtestdata.SatB1T2A5.Add(dbtestdata.SatB1T2A5, dbtestdata.SatB2T3A5) 584 if rs.Cmp(rsw) != 0 { 585 t.Errorf("GetAddressBalance().ReceivedSat() = %v, want %v", rs, rsw) 586 } 587 588 ta, err := d.GetTxAddresses(dbtestdata.TxidB2T1) 589 if err != nil { 590 t.Fatal(err) 591 } 592 taw := &TxAddresses{ 593 Height: 225494, 594 Inputs: []TxInput{ 595 { 596 AddrDesc: addressToAddrDesc(dbtestdata.Addr3, d.chainParser), 597 ValueSat: *dbtestdata.SatB1T2A3, 598 }, 599 { 600 AddrDesc: addressToAddrDesc(dbtestdata.Addr2, d.chainParser), 601 ValueSat: *dbtestdata.SatB1T1A2, 602 }, 603 }, 604 Outputs: []TxOutput{ 605 { 606 AddrDesc: addressToAddrDesc(dbtestdata.Addr6, d.chainParser), 607 Spent: true, 608 ValueSat: *dbtestdata.SatB2T1A6, 609 }, 610 { 611 AddrDesc: addressToAddrDesc(dbtestdata.Addr7, d.chainParser), 612 Spent: false, 613 ValueSat: *dbtestdata.SatB2T1A7, 614 }, 615 }, 616 } 617 if !reflect.DeepEqual(ta, taw) { 618 t.Errorf("GetTxAddresses() = %+v, want %+v", ta, taw) 619 } 620 ia, _, err := ta.Inputs[0].Addresses(d.chainParser) 621 if err != nil { 622 t.Fatal(err) 623 } 624 if !reflect.DeepEqual(ia, []string{dbtestdata.Addr3}) { 625 t.Errorf("GetTxAddresses().Inputs[0].Addresses() = %v, want %v", ia, []string{dbtestdata.Addr3}) 626 } 627 628 } 629 630 func Test_BulkConnect_UTXO(t *testing.T) { 631 d := setupRocksDB(t, &testBitcoinParser{ 632 BitcoinParser: bitcoinTestnetParser(), 633 }) 634 defer closeAndDestroyRocksDB(t, d) 635 636 bc, err := d.InitBulkConnect() 637 if err != nil { 638 t.Fatal(err) 639 } 640 641 if d.is.DbState != common.DbStateInconsistent { 642 t.Fatal("DB not in DbStateInconsistent") 643 } 644 645 if err := bc.ConnectBlock(dbtestdata.GetTestUTXOBlock1(d.chainParser), false); err != nil { 646 t.Fatal(err) 647 } 648 if err := checkColumn(d, cfBlockTxs, []keyPair{}); err != nil { 649 { 650 t.Fatal(err) 651 } 652 } 653 654 if err := bc.ConnectBlock(dbtestdata.GetTestUTXOBlock2(d.chainParser), true); err != nil { 655 t.Fatal(err) 656 } 657 658 if err := bc.Close(); err != nil { 659 t.Fatal(err) 660 } 661 662 if d.is.DbState != common.DbStateOpen { 663 t.Fatal("DB not in DbStateOpen") 664 } 665 666 verifyAfterUTXOBlock2(t, d) 667 } 668 669 func Test_packBigint_unpackBigint(t *testing.T) { 670 bigbig1, _ := big.NewInt(0).SetString("123456789123456789012345", 10) 671 bigbig2, _ := big.NewInt(0).SetString("12345678912345678901234512389012345123456789123456789012345123456789123456789012345", 10) 672 bigbigbig := big.NewInt(0) 673 bigbigbig.Mul(bigbig2, bigbig2) 674 bigbigbig.Mul(bigbigbig, bigbigbig) 675 bigbigbig.Mul(bigbigbig, bigbigbig) 676 tests := []struct { 677 name string 678 bi *big.Int 679 buf []byte 680 toobiglen int 681 }{ 682 { 683 name: "0", 684 bi: big.NewInt(0), 685 buf: make([]byte, maxPackedBigintBytes), 686 }, 687 { 688 name: "1", 689 bi: big.NewInt(1), 690 buf: make([]byte, maxPackedBigintBytes), 691 }, 692 { 693 name: "54321", 694 bi: big.NewInt(54321), 695 buf: make([]byte, 249), 696 }, 697 { 698 name: "12345678", 699 bi: big.NewInt(12345678), 700 buf: make([]byte, maxPackedBigintBytes), 701 }, 702 { 703 name: "123456789123456789", 704 bi: big.NewInt(123456789123456789), 705 buf: make([]byte, maxPackedBigintBytes), 706 }, 707 { 708 name: "bigbig1", 709 bi: bigbig1, 710 buf: make([]byte, maxPackedBigintBytes), 711 }, 712 { 713 name: "bigbig2", 714 bi: bigbig2, 715 buf: make([]byte, maxPackedBigintBytes), 716 }, 717 { 718 name: "bigbigbig", 719 bi: bigbigbig, 720 buf: make([]byte, maxPackedBigintBytes), 721 toobiglen: 242, 722 }, 723 } 724 for _, tt := range tests { 725 t.Run(tt.name, func(t *testing.T) { 726 // packBigint 727 got := packBigint(tt.bi, tt.buf) 728 if tt.toobiglen == 0 { 729 // create buffer that we expect 730 bb := tt.bi.Bytes() 731 want := append([]byte(nil), byte(len(bb))) 732 want = append(want, bb...) 733 if got != len(want) { 734 t.Errorf("packBigint() = %v, want %v", got, len(want)) 735 } 736 for i := 0; i < got; i++ { 737 if tt.buf[i] != want[i] { 738 t.Errorf("packBigint() buf = %v, want %v", tt.buf[:got], want) 739 break 740 } 741 } 742 // unpackBigint 743 got1, got2 := unpackBigint(tt.buf) 744 if got2 != len(want) { 745 t.Errorf("unpackBigint() = %v, want %v", got2, len(want)) 746 } 747 if tt.bi.Cmp(&got1) != 0 { 748 t.Errorf("unpackBigint() = %v, want %v", got1, tt.bi) 749 } 750 } else { 751 if got != tt.toobiglen { 752 t.Errorf("packBigint() = %v, want toobiglen %v", got, tt.toobiglen) 753 } 754 } 755 }) 756 } 757 } 758 759 func addressToAddrDesc(addr string, parser bchain.BlockChainParser) []byte { 760 b, err := parser.GetAddrDescFromAddress(addr) 761 if err != nil { 762 panic(err) 763 } 764 return b 765 } 766 767 func Test_packTxAddresses_unpackTxAddresses(t *testing.T) { 768 parser := bitcoinTestnetParser() 769 tests := []struct { 770 name string 771 hex string 772 data *TxAddresses 773 }{ 774 { 775 name: "1", 776 hex: "7b0216001443aac20a116e09ea4f7914be1c55e4c17aa600b70016001454633aa8bd2e552bd4e89c01e73c1b7905eb58460811207cb68a199872012d001443aac20a116e09ea4f7914be1c55e4c17aa600b70101", 777 data: &TxAddresses{ 778 Height: 123, 779 Inputs: []TxInput{ 780 { 781 AddrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser), 782 ValueSat: *big.NewInt(0), 783 }, 784 { 785 AddrDesc: addressToAddrDesc("tb1q233n429a9e2jh48gnsq7w0qm0yz7kkzx0qczw8", parser), 786 ValueSat: *big.NewInt(1234123421342341234), 787 }, 788 }, 789 Outputs: []TxOutput{ 790 { 791 AddrDesc: addressToAddrDesc("tb1qgw4vyzs3dcy75nmezjlpc40yc9a2vq9hghdyt2", parser), 792 ValueSat: *big.NewInt(1), 793 Spent: true, 794 }, 795 }, 796 }, 797 }, 798 { 799 name: "2", 800 hex: "e0390317a9149eb21980dc9d413d8eac27314938b9da920ee53e8705021918f2c017a91409f70b896169c37981d2b54b371df0d81a136a2c870501dd7e28c017a914e371782582a4addb541362c55565d2cdf56f6498870501a1e35ec0052fa9141d9ca71efa36d814424ea6ca1437e67287aebe348705012aadcac02ea91424fbc77cdc62702ade74dcf989c15e5d3f9240bc870501664894c02fa914afbfb74ee994c7d45f6698738bc4226d065266f7870501a1e35ec03276a914d2a37ce20ac9ec4f15dd05a7c6e8e9fbdb99850e88ac043b9943603376a9146b2044146a4438e6e5bfbc65f147afeb64d14fbb88ac05012a05f200", 801 data: &TxAddresses{ 802 Height: 12345, 803 Inputs: []TxInput{ 804 { 805 AddrDesc: addressToAddrDesc("2N7iL7AvS4LViugwsdjTB13uN4T7XhV1bCP", parser), 806 ValueSat: *big.NewInt(9011000000), 807 }, 808 { 809 AddrDesc: addressToAddrDesc("2Mt9v216YiNBAzobeNEzd4FQweHrGyuRHze", parser), 810 ValueSat: *big.NewInt(8011000000), 811 }, 812 { 813 AddrDesc: addressToAddrDesc("2NDyqJpHvHnqNtL1F9xAeCWMAW8WLJmEMyD", parser), 814 ValueSat: *big.NewInt(7011000000), 815 }, 816 }, 817 Outputs: []TxOutput{ 818 { 819 AddrDesc: addressToAddrDesc("2MuwoFGwABMakU7DCpdGDAKzyj2nTyRagDP", parser), 820 ValueSat: *big.NewInt(5011000000), 821 Spent: true, 822 }, 823 { 824 AddrDesc: addressToAddrDesc("2Mvcmw7qkGXNWzkfH1EjvxDcNRGL1Kf2tEM", parser), 825 ValueSat: *big.NewInt(6011000000), 826 }, 827 { 828 AddrDesc: addressToAddrDesc("2N9GVuX3XJGHS5MCdgn97gVezc6EgvzikTB", parser), 829 ValueSat: *big.NewInt(7011000000), 830 Spent: true, 831 }, 832 { 833 AddrDesc: addressToAddrDesc("mzii3fuRSpExMLJEHdHveW8NmiX8MPgavk", parser), 834 ValueSat: *big.NewInt(999900000), 835 }, 836 { 837 AddrDesc: addressToAddrDesc("mqHPFTRk23JZm9W1ANuEFtwTYwxjESSgKs", parser), 838 ValueSat: *big.NewInt(5000000000), 839 Spent: true, 840 }, 841 }, 842 }, 843 }, 844 { 845 name: "empty address", 846 hex: "baef9a1501000204d2020002162e010162", 847 data: &TxAddresses{ 848 Height: 123456789, 849 Inputs: []TxInput{ 850 { 851 AddrDesc: []byte{}, 852 ValueSat: *big.NewInt(1234), 853 }, 854 }, 855 Outputs: []TxOutput{ 856 { 857 AddrDesc: []byte{}, 858 ValueSat: *big.NewInt(5678), 859 }, 860 { 861 AddrDesc: []byte{}, 862 ValueSat: *big.NewInt(98), 863 Spent: true, 864 }, 865 }, 866 }, 867 }, 868 { 869 name: "empty", 870 hex: "000000", 871 data: &TxAddresses{ 872 Inputs: []TxInput{}, 873 Outputs: []TxOutput{}, 874 }, 875 }, 876 } 877 varBuf := make([]byte, maxPackedBigintBytes) 878 buf := make([]byte, 1024) 879 for _, tt := range tests { 880 t.Run(tt.name, func(t *testing.T) { 881 b := packTxAddresses(tt.data, buf, varBuf) 882 hex := hex.EncodeToString(b) 883 if !reflect.DeepEqual(hex, tt.hex) { 884 t.Errorf("packTxAddresses() = %v, want %v", hex, tt.hex) 885 } 886 got1, err := unpackTxAddresses(b) 887 if err != nil { 888 t.Errorf("unpackTxAddresses() error = %v", err) 889 return 890 } 891 if !reflect.DeepEqual(got1, tt.data) { 892 t.Errorf("unpackTxAddresses() = %+v, want %+v", got1, tt.data) 893 } 894 }) 895 } 896 }