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  }