Implement and test connectBlock for index v3
parent
95f831eefa
commit
a2bbf3f9de
450
db/rocksdb.go
450
db/rocksdb.go
|
@ -22,7 +22,8 @@ import (
|
|||
// when doing huge scan, it is better to close it and reopen from time to time to free the resources
|
||||
const refreshIterator = 5000000
|
||||
const packedHeightBytes = 4
|
||||
const dbVersion = 0
|
||||
const dbVersion = 3
|
||||
const maxAddrIDLen = 1024
|
||||
|
||||
// RepairRocksDB calls RocksDb db repair function
|
||||
func RepairRocksDB(name string) error {
|
||||
|
@ -203,11 +204,11 @@ func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, f
|
|||
for _, o := range outpoints {
|
||||
var vout uint32
|
||||
var isOutput bool
|
||||
if o.vout < 0 {
|
||||
vout = uint32(^o.vout)
|
||||
if o.index < 0 {
|
||||
vout = uint32(^o.index)
|
||||
isOutput = false
|
||||
} else {
|
||||
vout = uint32(o.vout)
|
||||
vout = uint32(o.index)
|
||||
isOutput = true
|
||||
}
|
||||
tx, err := d.chainParser.UnpackTxid(o.btxID)
|
||||
|
@ -272,18 +273,381 @@ func (d *RocksDB) writeBlock(block *bchain.Block, op int) error {
|
|||
|
||||
type outpoint struct {
|
||||
btxID []byte
|
||||
vout int32
|
||||
index int32
|
||||
}
|
||||
|
||||
type txAddress struct {
|
||||
addrID []byte
|
||||
spent bool
|
||||
valueSat big.Int
|
||||
}
|
||||
|
||||
type txAddresses struct {
|
||||
inputs []txAddress
|
||||
outputs []txAddress
|
||||
}
|
||||
|
||||
type addrBalance struct {
|
||||
txs uint32
|
||||
sentSat big.Int
|
||||
balanceSat big.Int
|
||||
}
|
||||
|
||||
func (d *RocksDB) writeAddressesUTXO(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error {
|
||||
if op == opDelete {
|
||||
// block does not contain mapping tx-> input address, which is necessary to recreate
|
||||
// unspentTxs; therefore it is not possible to DisconnectBlocks this way
|
||||
return errors.New("DisconnectBlock is not supported for UTXO chains")
|
||||
}
|
||||
addresses := make(map[string][]outpoint)
|
||||
blockTxids := make([][]byte, len(block.Txs))
|
||||
txAddressesMap := make(map[string]*txAddresses)
|
||||
balances := make(map[string]*addrBalance)
|
||||
// first process all outputs so that inputs can point to txs in this block
|
||||
for txi, tx := range block.Txs {
|
||||
btxID, err := d.chainParser.PackTxid(tx.Txid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockTxids[txi] = btxID
|
||||
ta := txAddresses{}
|
||||
ta.outputs = make([]txAddress, len(tx.Vout))
|
||||
txAddressesMap[string(btxID)] = &ta
|
||||
for i, output := range tx.Vout {
|
||||
tao := &ta.outputs[i]
|
||||
tao.valueSat = output.ValueSat
|
||||
addrID, err := d.chainParser.GetAddrIDFromVout(&output)
|
||||
if err != nil || len(addrID) == 0 || len(addrID) > maxAddrIDLen {
|
||||
if err != nil {
|
||||
// do not log ErrAddressMissing, transactions can be without to address (for example eth contracts)
|
||||
if err != bchain.ErrAddressMissing {
|
||||
glog.Warningf("rocksdb: addrID: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
|
||||
}
|
||||
} else {
|
||||
glog.Infof("rocksdb: block %d, skipping addrID of length %d", block.Height, len(addrID))
|
||||
}
|
||||
continue
|
||||
}
|
||||
tao.addrID = addrID
|
||||
strAddrID := string(addrID)
|
||||
// check that the address was used already in this block
|
||||
o, processed := addresses[strAddrID]
|
||||
if processed {
|
||||
// check that the address was already used in this tx
|
||||
processed = processedInTx(o, btxID)
|
||||
}
|
||||
addresses[strAddrID] = append(o, outpoint{
|
||||
btxID: btxID,
|
||||
index: int32(i),
|
||||
})
|
||||
ab, e := balances[strAddrID]
|
||||
if !e {
|
||||
ab, err = d.getAddressBalance(addrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ab == nil {
|
||||
ab = &addrBalance{}
|
||||
}
|
||||
balances[strAddrID] = ab
|
||||
}
|
||||
// add number of trx in balance only once, address can be multiple times in tx
|
||||
if !processed {
|
||||
ab.txs++
|
||||
}
|
||||
ab.balanceSat.Add(&ab.balanceSat, &output.ValueSat)
|
||||
}
|
||||
}
|
||||
// process inputs
|
||||
for txi, tx := range block.Txs {
|
||||
spendingTxid := blockTxids[txi]
|
||||
ta := txAddressesMap[string(spendingTxid)]
|
||||
ta.inputs = make([]txAddress, len(tx.Vin))
|
||||
for i, input := range tx.Vin {
|
||||
tai := &ta.inputs[i]
|
||||
btxID, err := d.chainParser.PackTxid(input.Txid)
|
||||
if err != nil {
|
||||
// do not process inputs without input txid
|
||||
if err == bchain.ErrTxidMissing {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
stxID := string(btxID)
|
||||
ita, e := txAddressesMap[stxID]
|
||||
if !e {
|
||||
ita, err = d.getTxAddresses(btxID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ita == nil {
|
||||
glog.Warningf("rocksdb: height %d, tx %v, input tx %v not found in txAddresses", block.Height, tx.Txid, input.Txid)
|
||||
continue
|
||||
}
|
||||
txAddressesMap[stxID] = ita
|
||||
}
|
||||
if len(ita.outputs) <= int(input.Vout) {
|
||||
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is out of bounds of stored tx", block.Height, tx.Txid, input.Txid, input.Vout)
|
||||
continue
|
||||
}
|
||||
ot := &ita.outputs[int(input.Vout)]
|
||||
if ot.spent {
|
||||
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is double spend", block.Height, tx.Txid, input.Txid, input.Vout)
|
||||
continue
|
||||
}
|
||||
tai.addrID = ot.addrID
|
||||
tai.valueSat = ot.valueSat
|
||||
// mark the output tx as spent
|
||||
ot.spent = true
|
||||
strAddrID := string(ot.addrID)
|
||||
// check that the address was used already in this block
|
||||
o, processed := addresses[strAddrID]
|
||||
if processed {
|
||||
// check that the address was already used in this tx
|
||||
processed = processedInTx(o, spendingTxid)
|
||||
}
|
||||
addresses[strAddrID] = append(o, outpoint{
|
||||
btxID: spendingTxid,
|
||||
index: ^int32(i),
|
||||
})
|
||||
ab, e := balances[strAddrID]
|
||||
if !e {
|
||||
ab, err = d.getAddressBalance(ot.addrID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ab == nil {
|
||||
ab = &addrBalance{}
|
||||
}
|
||||
balances[strAddrID] = ab
|
||||
}
|
||||
// add number of trx in balance only once, address can be multiple times in tx
|
||||
if !processed {
|
||||
ab.txs++
|
||||
}
|
||||
ab.balanceSat.Sub(&ab.balanceSat, &ot.valueSat)
|
||||
if ab.balanceSat.Sign() < 0 {
|
||||
ad, err := d.chainParser.OutputScriptToAddresses(ot.addrID)
|
||||
if err != nil {
|
||||
glog.Warningf("rocksdb: unparsable address reached negative balance %v, resetting to 0. Parser error %v", ab.balanceSat.String(), err)
|
||||
} else {
|
||||
glog.Warningf("rocksdb: address %v reached negative balance %v, resetting to 0", ab.balanceSat.String(), ad)
|
||||
}
|
||||
ab.balanceSat.SetInt64(0)
|
||||
}
|
||||
ab.sentSat.Add(&ab.sentSat, &ot.valueSat)
|
||||
}
|
||||
}
|
||||
if op == opInsert {
|
||||
if err := d.storeAddresses(wb, block, addresses); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.storeTxAddresses(wb, txAddressesMap); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.storeBalances(wb, balances); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.storeAndCleanupBlockTxids(wb, block, blockTxids); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processedInTx(o []outpoint, btxID []byte) bool {
|
||||
for _, op := range o {
|
||||
if bytes.Equal(btxID, op.btxID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *RocksDB) storeAddresses(wb *gorocksdb.WriteBatch, block *bchain.Block, addresses map[string][]outpoint) error {
|
||||
for addrID, outpoints := range addresses {
|
||||
ba := []byte(addrID)
|
||||
key := packAddressKey(ba, block.Height)
|
||||
val := d.packOutpoints(outpoints)
|
||||
wb.PutCF(d.cfh[cfAddresses], key, val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*txAddresses) error {
|
||||
varBuf := make([]byte, maxPackedBigintBytes)
|
||||
buf := make([]byte, 1024)
|
||||
for txID, ta := range am {
|
||||
buf = buf[:0]
|
||||
l := packVaruint(uint(len(ta.inputs)), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
for i := range ta.inputs {
|
||||
buf = appendTxAddress(buf, varBuf, &ta.inputs[i])
|
||||
}
|
||||
l = packVaruint(uint(len(ta.outputs)), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
for i := range ta.outputs {
|
||||
buf = appendTxAddress(buf, varBuf, &ta.outputs[i])
|
||||
}
|
||||
wb.PutCF(d.cfh[cfTxAddresses], []byte(txID), buf)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*addrBalance) error {
|
||||
// allocate buffer big enough for number of txs + 2 bigints
|
||||
buf := make([]byte, vlq.MaxLen32+2*maxPackedBigintBytes)
|
||||
for addrID, ab := range abm {
|
||||
l := packVaruint(uint(ab.txs), buf)
|
||||
ll := packBigint(&ab.sentSat, buf[l:])
|
||||
l += ll
|
||||
ll = packBigint(&ab.balanceSat, buf[l:])
|
||||
l += ll
|
||||
wb.PutCF(d.cfh[cfAddressBalance], []byte(addrID), buf[:l])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendTxAddress(buf []byte, varBuf []byte, txa *txAddress) []byte {
|
||||
la := len(txa.addrID)
|
||||
if txa.spent {
|
||||
la = ^la
|
||||
}
|
||||
l := packVarint(la, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
buf = append(buf, txa.addrID...)
|
||||
l = packBigint(&txa.valueSat, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *RocksDB) storeAndCleanupBlockTxids(wb *gorocksdb.WriteBatch, block *bchain.Block, txids [][]byte) error {
|
||||
pl := d.chainParser.PackedTxidLen()
|
||||
buf := make([]byte, pl*len(txids))
|
||||
i := 0
|
||||
for _, txid := range txids {
|
||||
copy(buf[i:], txid)
|
||||
i += pl
|
||||
}
|
||||
key := packUint(block.Height)
|
||||
wb.PutCF(d.cfh[cfBlockTxids], key, buf)
|
||||
keep := d.chainParser.KeepBlockAddresses()
|
||||
// cleanup old block address
|
||||
if block.Height > uint32(keep) {
|
||||
for rh := block.Height - uint32(keep); rh < block.Height; rh-- {
|
||||
key = packUint(rh)
|
||||
val, err := d.db.GetCF(d.ro, d.cfh[cfBlockTxids], key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val.Size() == 0 {
|
||||
break
|
||||
}
|
||||
val.Free()
|
||||
d.db.DeleteCF(d.wo, d.cfh[cfBlockTxids], key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) getAddressBalance(addrID []byte) (*addrBalance, error) {
|
||||
val, err := d.db.GetCF(d.ro, d.cfh[cfAddressBalance], addrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer val.Free()
|
||||
buf := val.Data()
|
||||
// 3 is minimum length of addrBalance - 1 byte txs, 1 byte sent, 1 byte balance
|
||||
if len(buf) < 3 {
|
||||
return nil, nil
|
||||
}
|
||||
txs, l := unpackVaruint(buf)
|
||||
sentSat, sl := unpackBigint(buf[l:])
|
||||
balanceSat, _ := unpackBigint(buf[l+sl:])
|
||||
return &addrBalance{
|
||||
txs: uint32(txs),
|
||||
sentSat: sentSat,
|
||||
balanceSat: balanceSat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) getTxAddresses(btxID []byte) (*txAddresses, error) {
|
||||
val, err := d.db.GetCF(d.ro, d.cfh[cfTxAddresses], btxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer val.Free()
|
||||
buf := val.Data()
|
||||
// 2 is minimum length of addrBalance - 1 byte inputs len, 1 byte outputs len
|
||||
if len(buf) < 2 {
|
||||
return nil, nil
|
||||
}
|
||||
ta := txAddresses{}
|
||||
inputs, l := unpackVaruint(buf)
|
||||
ta.inputs = make([]txAddress, inputs)
|
||||
for i := uint(0); i < inputs; i++ {
|
||||
l += unpackTxAddress(&ta.inputs[i], buf[l:])
|
||||
}
|
||||
outputs, ll := unpackVaruint(buf[l:])
|
||||
l += ll
|
||||
ta.outputs = make([]txAddress, outputs)
|
||||
for i := uint(0); i < outputs; i++ {
|
||||
l += unpackTxAddress(&ta.outputs[i], buf[l:])
|
||||
}
|
||||
return &ta, nil
|
||||
}
|
||||
|
||||
func unpackTxAddress(ta *txAddress, buf []byte) int {
|
||||
al, l := unpackVarint(buf)
|
||||
if al < 0 {
|
||||
ta.spent = true
|
||||
al ^= al
|
||||
}
|
||||
ta.addrID = make([]byte, al)
|
||||
copy(ta.addrID, buf[l:l+al])
|
||||
al += l
|
||||
ta.valueSat, l = unpackBigint(buf[al:])
|
||||
return l + al
|
||||
}
|
||||
|
||||
func (d *RocksDB) packOutpoints(outpoints []outpoint) []byte {
|
||||
buf := make([]byte, 0)
|
||||
bvout := make([]byte, vlq.MaxLen32)
|
||||
for _, o := range outpoints {
|
||||
l := packVarint32(o.index, bvout)
|
||||
buf = append(buf, []byte(o.btxID)...)
|
||||
buf = append(buf, bvout[:l]...)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *RocksDB) unpackOutpoints(buf []byte) ([]outpoint, error) {
|
||||
txidUnpackedLen := d.chainParser.PackedTxidLen()
|
||||
outpoints := make([]outpoint, 0)
|
||||
for i := 0; i < len(buf); {
|
||||
btxID := append([]byte(nil), buf[i:i+txidUnpackedLen]...)
|
||||
i += txidUnpackedLen
|
||||
vout, voutLen := unpackVarint32(buf[i:])
|
||||
i += voutLen
|
||||
outpoints = append(outpoints, outpoint{
|
||||
btxID: btxID,
|
||||
index: vout,
|
||||
})
|
||||
}
|
||||
return outpoints, nil
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
|
||||
func (d *RocksDB) packBlockAddress(addrID []byte, spentTxs map[string][]outpoint) []byte {
|
||||
vBuf := make([]byte, vlq.MaxLen32)
|
||||
vl := packVarint(int32(len(addrID)), vBuf)
|
||||
vl := packVarint(len(addrID), vBuf)
|
||||
blockAddress := append([]byte(nil), vBuf[:vl]...)
|
||||
blockAddress = append(blockAddress, addrID...)
|
||||
if spentTxs == nil {
|
||||
} else {
|
||||
addrUnspentTxs := spentTxs[string(addrID)]
|
||||
vl = packVarint(int32(len(addrUnspentTxs)), vBuf)
|
||||
vl = packVarint(len(addrUnspentTxs), vBuf)
|
||||
blockAddress = append(blockAddress, vBuf[:vl]...)
|
||||
buf := d.packOutpoints(addrUnspentTxs)
|
||||
blockAddress = append(blockAddress, buf...)
|
||||
|
@ -342,7 +706,7 @@ func (d *RocksDB) addAddrIDToRecords(op int, wb *gorocksdb.WriteBatch, records m
|
|||
strAddrID := string(addrID)
|
||||
records[strAddrID] = append(records[strAddrID], outpoint{
|
||||
btxID: btxid,
|
||||
vout: vout,
|
||||
index: vout,
|
||||
})
|
||||
if op == opDelete {
|
||||
// remove transactions from cache
|
||||
|
@ -370,10 +734,10 @@ func appendPackedAddrID(txAddrs []byte, addrID []byte, n uint32, remaining int)
|
|||
txAddrs = append(txAddrs, make([]byte, vlq.MaxLen32+len(addrID)+remaining*32)...)[:len(txAddrs)]
|
||||
}
|
||||
// addrID is packed as number of bytes of the addrID + bytes of addrID + vout
|
||||
lv := packVarint(int32(len(addrID)), txAddrs[len(txAddrs):len(txAddrs)+vlq.MaxLen32])
|
||||
lv := packVarint(len(addrID), txAddrs[len(txAddrs):len(txAddrs)+vlq.MaxLen32])
|
||||
txAddrs = txAddrs[:len(txAddrs)+lv]
|
||||
txAddrs = append(txAddrs, addrID...)
|
||||
lv = packVarint(int32(n), txAddrs[len(txAddrs):len(txAddrs)+vlq.MaxLen32])
|
||||
lv = packVarint(int(n), txAddrs[len(txAddrs):len(txAddrs)+vlq.MaxLen32])
|
||||
txAddrs = txAddrs[:len(txAddrs)+lv]
|
||||
return txAddrs
|
||||
}
|
||||
|
@ -399,7 +763,7 @@ func findAndRemoveUnspentAddr(unspentAddrs []byte, vout uint32) ([]byte, []byte)
|
|||
return nil, unspentAddrs
|
||||
}
|
||||
|
||||
func (d *RocksDB) writeAddressesUTXO(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error {
|
||||
func (d *RocksDB) writeAddressesUTXO_old(wb *gorocksdb.WriteBatch, block *bchain.Block, op int) error {
|
||||
if op == opDelete {
|
||||
// block does not contain mapping tx-> input address, which is necessary to recreate
|
||||
// unspentTxs; therefore it is not possible to DisconnectBlocks this way
|
||||
|
@ -563,36 +927,9 @@ func (d *RocksDB) unpackBlockAddresses(buf []byte) ([][]byte, [][]outpoint, erro
|
|||
return addresses, outpointsArray, nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) packOutpoints(outpoints []outpoint) []byte {
|
||||
buf := make([]byte, 0)
|
||||
bvout := make([]byte, vlq.MaxLen32)
|
||||
for _, o := range outpoints {
|
||||
l := packVarint(o.vout, bvout)
|
||||
buf = append(buf, []byte(o.btxID)...)
|
||||
buf = append(buf, bvout[:l]...)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *RocksDB) unpackOutpoints(buf []byte) ([]outpoint, error) {
|
||||
txidUnpackedLen := d.chainParser.PackedTxidLen()
|
||||
outpoints := make([]outpoint, 0)
|
||||
for i := 0; i < len(buf); {
|
||||
btxID := append([]byte(nil), buf[i:i+txidUnpackedLen]...)
|
||||
i += txidUnpackedLen
|
||||
vout, voutLen := unpackVarint(buf[i:])
|
||||
i += voutLen
|
||||
outpoints = append(outpoints, outpoint{
|
||||
btxID: btxID,
|
||||
vout: vout,
|
||||
})
|
||||
}
|
||||
return outpoints, nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) unpackNOutpoints(buf []byte) ([]outpoint, int, error) {
|
||||
txidUnpackedLen := d.chainParser.PackedTxidLen()
|
||||
n, p := unpackVarint(buf)
|
||||
n, p := unpackVarint32(buf)
|
||||
outpoints := make([]outpoint, n)
|
||||
for i := int32(0); i < n; i++ {
|
||||
if p+txidUnpackedLen >= len(buf) {
|
||||
|
@ -600,11 +937,11 @@ func (d *RocksDB) unpackNOutpoints(buf []byte) ([]outpoint, int, error) {
|
|||
}
|
||||
btxID := append([]byte(nil), buf[p:p+txidUnpackedLen]...)
|
||||
p += txidUnpackedLen
|
||||
vout, voutLen := unpackVarint(buf[p:])
|
||||
vout, voutLen := unpackVarint32(buf[p:])
|
||||
p += voutLen
|
||||
outpoints[i] = outpoint{
|
||||
btxID: btxID,
|
||||
vout: vout,
|
||||
index: vout,
|
||||
}
|
||||
}
|
||||
return outpoints, p, nil
|
||||
|
@ -616,7 +953,7 @@ func (d *RocksDB) packOutpoint(txid string, vout int32) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
bv := make([]byte, vlq.MaxLen32)
|
||||
l := packVarint(vout, bv)
|
||||
l := packVarint32(vout, bv)
|
||||
buf := make([]byte, 0, l+len(btxid))
|
||||
buf = append(buf, btxid...)
|
||||
buf = append(buf, bv[:l]...)
|
||||
|
@ -626,10 +963,12 @@ func (d *RocksDB) packOutpoint(txid string, vout int32) ([]byte, error) {
|
|||
func (d *RocksDB) unpackOutpoint(buf []byte) (string, int32, int) {
|
||||
txidUnpackedLen := d.chainParser.PackedTxidLen()
|
||||
txid, _ := d.chainParser.UnpackTxid(buf[:txidUnpackedLen])
|
||||
vout, o := unpackVarint(buf[txidUnpackedLen:])
|
||||
vout, o := unpackVarint32(buf[txidUnpackedLen:])
|
||||
return txid, vout, txidUnpackedLen + o
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// Block index
|
||||
|
||||
// GetBestBlock returns the block hash of the block with highest height in the db
|
||||
|
@ -799,7 +1138,7 @@ func (d *RocksDB) DisconnectBlockRange(lower uint32, higher uint32) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
txAddrs = appendPackedAddrID(txAddrs, addrID, uint32(o.vout), 1)
|
||||
txAddrs = appendPackedAddrID(txAddrs, addrID, uint32(o.index), 1)
|
||||
unspentTxs[stxID] = txAddrs
|
||||
}
|
||||
// delete unspentTxs from this block
|
||||
|
@ -1065,15 +1404,33 @@ func unpackUint(buf []byte) uint32 {
|
|||
return binary.BigEndian.Uint32(buf)
|
||||
}
|
||||
|
||||
func packVarint(i int32, buf []byte) int {
|
||||
func packVarint32(i int32, buf []byte) int {
|
||||
return vlq.PutInt(buf, int64(i))
|
||||
}
|
||||
|
||||
func unpackVarint(buf []byte) (int32, int) {
|
||||
func packVarint(i int, buf []byte) int {
|
||||
return vlq.PutInt(buf, int64(i))
|
||||
}
|
||||
|
||||
func packVaruint(i uint, buf []byte) int {
|
||||
return vlq.PutUint(buf, uint64(i))
|
||||
}
|
||||
|
||||
func unpackVarint32(buf []byte) (int32, int) {
|
||||
i, ofs := vlq.Int(buf)
|
||||
return int32(i), ofs
|
||||
}
|
||||
|
||||
func unpackVarint(buf []byte) (int, int) {
|
||||
i, ofs := vlq.Int(buf)
|
||||
return int(i), ofs
|
||||
}
|
||||
|
||||
func unpackVaruint(buf []byte) (uint, int) {
|
||||
i, ofs := vlq.Uint(buf)
|
||||
return uint(i), ofs
|
||||
}
|
||||
|
||||
const (
|
||||
// number of bits in a big.Word
|
||||
wordBits = 32 << (uint64(^big.Word(0)) >> 63)
|
||||
|
@ -1081,6 +1438,7 @@ const (
|
|||
wordBytes = wordBits / 8
|
||||
// max packed bigint words
|
||||
maxPackedBigintWords = (256 - wordBytes) / wordBytes
|
||||
maxPackedBigintBytes = 249
|
||||
)
|
||||
|
||||
// big int is packed in BigEndian order without memory allocation as 1 byte length followed by bytes of big int
|
||||
|
|
|
@ -60,6 +60,17 @@ func addressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string
|
|||
return strconv.FormatInt(int64(len(h)), 16) + h
|
||||
}
|
||||
|
||||
func spentAddressToPubKeyHexWithLength(addr string, t *testing.T, d *RocksDB) string {
|
||||
h := addressToPubKeyHex(addr, t, d)
|
||||
return strconv.FormatInt(int64(len(h)+1), 16) + h
|
||||
}
|
||||
|
||||
func bigintToHex(i *big.Int) string {
|
||||
b := make([]byte, maxPackedBigintBytes)
|
||||
l := packBigint(i, b)
|
||||
return hex.EncodeToString(b[:l])
|
||||
}
|
||||
|
||||
// keyPair is used to compare given key value in DB with expected
|
||||
// for more complicated compares it is possible to specify CompareFunc
|
||||
type keyPair struct {
|
||||
|
@ -105,7 +116,7 @@ func checkColumn(d *RocksDB, col int, kp []keyPair) error {
|
|||
valOK = kp[i].CompareFunc(val)
|
||||
}
|
||||
if !valOK {
|
||||
return errors.Errorf("Incorrect value %v found in column %v row %v, expecting %v", val, cfNames[col], i, kp[i].Value)
|
||||
return errors.Errorf("Incorrect value %v found in column %v row %v key %v, expecting %v", val, cfNames[col], i, key, kp[i].Value)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
@ -115,6 +126,38 @@ func checkColumn(d *RocksDB, col int, kp []keyPair) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
txidB1T1 = "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"
|
||||
txidB1T2 = "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"
|
||||
txidB2T1 = "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25"
|
||||
txidB2T2 = "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71"
|
||||
txidB2T3 = "05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"
|
||||
|
||||
addr1 = "mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti" // 76a914010d39800f86122416e28f485029acf77507169288ac
|
||||
addr2 = "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz" // 76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac
|
||||
addr3 = "mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw" // 76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac
|
||||
addr4 = "2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS" // a9144a21db08fb6882cb152e1ff06780a430740f770487
|
||||
addr5 = "2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1" // a914e921fc4912a315078f370d959f2c4f7b6d2a683c87
|
||||
addr6 = "mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX" // 76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac
|
||||
addr7 = "mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL" // 76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac
|
||||
addr8 = "mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC" // 76a914b434eb0c1a3b7a02e8a29cc616e791ef1e0bf51f88ac
|
||||
addr9 = "mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP" // 76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac
|
||||
)
|
||||
|
||||
var (
|
||||
satZero = big.NewInt(0)
|
||||
satB1T1A1 = big.NewInt(100000000)
|
||||
satB1T1A2 = big.NewInt(12345)
|
||||
satB1T2A3 = big.NewInt(1234567890123)
|
||||
satB1T2A4 = big.NewInt(1)
|
||||
satB1T2A5 = big.NewInt(9876)
|
||||
satB2T1A6 = big.NewInt(317283951061)
|
||||
satB2T1A7 = big.NewInt(917283951061)
|
||||
satB2T2A8 = big.NewInt(118641975500)
|
||||
satB2T2A9 = big.NewInt(198641975500)
|
||||
satB2T3A5 = big.NewInt(9000)
|
||||
)
|
||||
|
||||
func getTestUTXOBlock1(t *testing.T, d *RocksDB) *bchain.Block {
|
||||
return &bchain.Block{
|
||||
BlockHeader: bchain.BlockHeader{
|
||||
|
@ -123,44 +166,49 @@ func getTestUTXOBlock1(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
},
|
||||
Txs: []bchain.Tx{
|
||||
bchain.Tx{
|
||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||
Txid: txidB1T1,
|
||||
Vout: []bchain.Vout{
|
||||
bchain.Vout{
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d),
|
||||
Hex: addressToPubKeyHex(addr1, t, d),
|
||||
},
|
||||
ValueSat: *satB1T1A1,
|
||||
},
|
||||
bchain.Vout{
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d),
|
||||
Hex: addressToPubKeyHex(addr2, t, d),
|
||||
},
|
||||
ValueSat: *satB1T1A2,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549300000,
|
||||
Time: 22549300000,
|
||||
},
|
||||
bchain.Tx{
|
||||
Txid: "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75",
|
||||
Txid: txidB1T2,
|
||||
Vout: []bchain.Vout{
|
||||
bchain.Vout{
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d),
|
||||
Hex: addressToPubKeyHex(addr3, t, d),
|
||||
},
|
||||
ValueSat: *satB1T2A3,
|
||||
},
|
||||
bchain.Vout{
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d),
|
||||
Hex: addressToPubKeyHex(addr4, t, d),
|
||||
},
|
||||
ValueSat: *satB1T2A4,
|
||||
},
|
||||
bchain.Vout{
|
||||
N: 2,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d),
|
||||
Hex: addressToPubKeyHex(addr5, t, d),
|
||||
},
|
||||
ValueSat: *satB1T2A5,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549300001,
|
||||
|
@ -178,14 +226,16 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
},
|
||||
Txs: []bchain.Tx{
|
||||
bchain.Tx{
|
||||
Txid: "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25",
|
||||
Txid: txidB2T1,
|
||||
Vin: []bchain.Vin{
|
||||
// addr3
|
||||
bchain.Vin{
|
||||
Txid: "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75",
|
||||
Txid: txidB1T2,
|
||||
Vout: 0,
|
||||
},
|
||||
// addr2
|
||||
bchain.Vin{
|
||||
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||
Txid: txidB1T1,
|
||||
Vout: 1,
|
||||
},
|
||||
},
|
||||
|
@ -193,30 +243,32 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
bchain.Vout{
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX", t, d),
|
||||
Hex: addressToPubKeyHex(addr6, t, d),
|
||||
},
|
||||
ValueSat: *satB2T1A6,
|
||||
},
|
||||
bchain.Vout{
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL", t, d),
|
||||
Hex: addressToPubKeyHex(addr7, t, d),
|
||||
},
|
||||
ValueSat: *satB2T1A7,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400000,
|
||||
Time: 22549400000,
|
||||
},
|
||||
bchain.Tx{
|
||||
Txid: "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71",
|
||||
Txid: txidB2T2,
|
||||
Vin: []bchain.Vin{
|
||||
// spending an output in the same block
|
||||
// spending an output in the same block - addr6
|
||||
bchain.Vin{
|
||||
Txid: "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25",
|
||||
Txid: txidB2T1,
|
||||
Vout: 0,
|
||||
},
|
||||
// spending an output in the previous block
|
||||
// spending an output in the previous block - addr4
|
||||
bchain.Vin{
|
||||
Txid: "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75",
|
||||
Txid: txidB1T2,
|
||||
Vout: 1,
|
||||
},
|
||||
},
|
||||
|
@ -224,14 +276,16 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
bchain.Vout{
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC", t, d),
|
||||
Hex: addressToPubKeyHex(addr8, t, d),
|
||||
},
|
||||
ValueSat: *satB2T2A8,
|
||||
},
|
||||
bchain.Vout{
|
||||
N: 1,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP", t, d),
|
||||
Hex: addressToPubKeyHex(addr9, t, d),
|
||||
},
|
||||
ValueSat: *satB2T2A9,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400001,
|
||||
|
@ -239,10 +293,11 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
},
|
||||
// transaction from the same address in the previous block
|
||||
bchain.Tx{
|
||||
Txid: "05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07",
|
||||
Txid: txidB2T3,
|
||||
Vin: []bchain.Vin{
|
||||
// addr5
|
||||
bchain.Vin{
|
||||
Txid: "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75",
|
||||
Txid: txidB1T2,
|
||||
Vout: 2,
|
||||
},
|
||||
},
|
||||
|
@ -250,8 +305,9 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
bchain.Vout{
|
||||
N: 0,
|
||||
ScriptPubKey: bchain.ScriptPubKey{
|
||||
Hex: addressToPubKeyHex("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d),
|
||||
Hex: addressToPubKeyHex(addr5, t, d),
|
||||
},
|
||||
ValueSat: *satB2T3A5,
|
||||
},
|
||||
},
|
||||
Blocktime: 22549400002,
|
||||
|
@ -261,7 +317,7 @@ func getTestUTXOBlock2(t *testing.T, d *RocksDB) *bchain.Block {
|
|||
}
|
||||
}
|
||||
|
||||
func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, noBlockAddresses bool) {
|
||||
func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB) {
|
||||
if err := checkColumn(d, cfHeight, []keyPair{
|
||||
keyPair{"000370d5", "0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997", nil},
|
||||
}); err != nil {
|
||||
|
@ -271,62 +327,51 @@ func verifyAfterUTXOBlock1(t *testing.T, d *RocksDB, noBlockAddresses bool) {
|
|||
}
|
||||
// the vout is encoded as signed varint, i.e. value * 2 for non negative values
|
||||
if err := checkColumn(d, cfAddresses, []keyPair{
|
||||
keyPair{addressToPubKeyHex("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d) + "000370d5", "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840" + "00", nil},
|
||||
keyPair{addressToPubKeyHex("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "000370d5", "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "00", nil},
|
||||
keyPair{addressToPubKeyHex("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "04", nil},
|
||||
keyPair{addressToPubKeyHex(addr1, t, d) + "000370d5", txidB1T1 + "00", nil},
|
||||
keyPair{addressToPubKeyHex(addr2, t, d) + "000370d5", txidB1T1 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr3, t, d) + "000370d5", txidB1T2 + "00", nil},
|
||||
keyPair{addressToPubKeyHex(addr4, t, d) + "000370d5", txidB1T2 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr5, t, d) + "000370d5", txidB1T2 + "04", nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkColumn(d, cfUnspentTxs, []keyPair{
|
||||
if err := checkColumn(d, cfTxAddresses, []keyPair{
|
||||
keyPair{
|
||||
"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", "",
|
||||
func(v string) bool {
|
||||
return compareFuncBlockAddresses(t, v, []string{
|
||||
addressToPubKeyHexWithLength("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "02",
|
||||
})
|
||||
},
|
||||
txidB1T1,
|
||||
"00" + "02" +
|
||||
addressToPubKeyHexWithLength(addr1, t, d) + bigintToHex(satB1T1A1) +
|
||||
addressToPubKeyHexWithLength(addr2, t, d) + bigintToHex(satB1T1A2),
|
||||
nil,
|
||||
},
|
||||
keyPair{
|
||||
"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75", "",
|
||||
func(v string) bool {
|
||||
return compareFuncBlockAddresses(t, v, []string{
|
||||
addressToPubKeyHexWithLength("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "02",
|
||||
addressToPubKeyHexWithLength("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "04",
|
||||
})
|
||||
},
|
||||
txidB1T2,
|
||||
"00" + "03" +
|
||||
addressToPubKeyHexWithLength(addr3, t, d) + bigintToHex(satB1T2A3) +
|
||||
addressToPubKeyHexWithLength(addr4, t, d) + bigintToHex(satB1T2A4) +
|
||||
addressToPubKeyHexWithLength(addr5, t, d) + bigintToHex(satB1T2A5),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// after disconnect there are no blockaddresses for the previous block
|
||||
var blockAddressesKp []keyPair
|
||||
if noBlockAddresses {
|
||||
blockAddressesKp = []keyPair{}
|
||||
} else {
|
||||
// the values in cfBlockAddresses are in random order, must use CompareFunc
|
||||
blockAddressesKp = []keyPair{
|
||||
keyPair{"000370d5", "",
|
||||
func(v string) bool {
|
||||
return compareFuncBlockAddresses(t, v, []string{
|
||||
addressToPubKeyHexWithLength("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "00",
|
||||
})
|
||||
},
|
||||
},
|
||||
if err := checkColumn(d, cfAddressBalance, []keyPair{
|
||||
keyPair{addressToPubKeyHex(addr1, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T1A1), nil},
|
||||
keyPair{addressToPubKeyHex(addr2, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T1A2), nil},
|
||||
keyPair{addressToPubKeyHex(addr3, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T2A3), nil},
|
||||
keyPair{addressToPubKeyHex(addr4, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T2A4), nil},
|
||||
keyPair{addressToPubKeyHex(addr5, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T2A5), nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkColumn(d, cfBlockAddresses, blockAddressesKp); err != nil {
|
||||
if err := checkColumn(d, cfBlockTxids, []keyPair{
|
||||
keyPair{"000370d5", txidB1T1 + txidB1T2, nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -343,47 +388,66 @@ func verifyAfterUTXOBlock2(t *testing.T, d *RocksDB) {
|
|||
}
|
||||
}
|
||||
if err := checkColumn(d, cfAddresses, []keyPair{
|
||||
keyPair{addressToPubKeyHex("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d) + "000370d5", "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840" + "00", nil},
|
||||
keyPair{addressToPubKeyHex("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "000370d5", "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "00", nil},
|
||||
keyPair{addressToPubKeyHex("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "000370d5", "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "04", nil},
|
||||
keyPair{addressToPubKeyHex("mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX", t, d) + "000370d6", "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25" + "00" + "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71" + "01", nil},
|
||||
keyPair{addressToPubKeyHex("mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL", t, d) + "000370d6", "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC", t, d) + "000370d6", "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71" + "00", nil},
|
||||
keyPair{addressToPubKeyHex("mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP", t, d) + "000370d6", "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71" + "02", nil},
|
||||
keyPair{addressToPubKeyHex("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "000370d6", "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25" + "01", nil},
|
||||
keyPair{addressToPubKeyHex("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "000370d6", "7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25" + "03", nil},
|
||||
keyPair{addressToPubKeyHex("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "000370d6", "05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07" + "00" + "05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07" + "01", nil},
|
||||
keyPair{addressToPubKeyHex("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "000370d6", "3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71" + "03", nil},
|
||||
keyPair{addressToPubKeyHex(addr1, t, d) + "000370d5", txidB1T1 + "00", nil},
|
||||
keyPair{addressToPubKeyHex(addr2, t, d) + "000370d5", txidB1T1 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr3, t, d) + "000370d5", txidB1T2 + "00", nil},
|
||||
keyPair{addressToPubKeyHex(addr4, t, d) + "000370d5", txidB1T2 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr5, t, d) + "000370d5", txidB1T2 + "04", nil},
|
||||
keyPair{addressToPubKeyHex(addr6, t, d) + "000370d6", txidB2T1 + "00" + txidB2T2 + "01", nil},
|
||||
keyPair{addressToPubKeyHex(addr7, t, d) + "000370d6", txidB2T1 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr8, t, d) + "000370d6", txidB2T2 + "00", nil},
|
||||
keyPair{addressToPubKeyHex(addr9, t, d) + "000370d6", txidB2T2 + "02", nil},
|
||||
keyPair{addressToPubKeyHex(addr3, t, d) + "000370d6", txidB2T1 + "01", nil},
|
||||
keyPair{addressToPubKeyHex(addr2, t, d) + "000370d6", txidB2T1 + "03", nil},
|
||||
keyPair{addressToPubKeyHex(addr5, t, d) + "000370d6", txidB2T3 + "00" + txidB2T3 + "01", nil},
|
||||
keyPair{addressToPubKeyHex(addr4, t, d) + "000370d6", txidB2T2 + "03", nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkColumn(d, cfUnspentTxs, []keyPair{
|
||||
if err := checkColumn(d, cfTxAddresses, []keyPair{
|
||||
keyPair{
|
||||
"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
|
||||
addressToPubKeyHexWithLength("mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti", t, d) + "00",
|
||||
txidB1T1,
|
||||
"00" + "02" +
|
||||
addressToPubKeyHexWithLength(addr1, t, d) + bigintToHex(satB1T1A1) +
|
||||
spentAddressToPubKeyHexWithLength(addr2, t, d) + bigintToHex(satB1T1A2),
|
||||
nil,
|
||||
},
|
||||
keyPair{
|
||||
"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25",
|
||||
addressToPubKeyHexWithLength("mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL", t, d) + "02",
|
||||
txidB1T2,
|
||||
"00" + "03" +
|
||||
spentAddressToPubKeyHexWithLength(addr3, t, d) + bigintToHex(satB1T2A3) +
|
||||
spentAddressToPubKeyHexWithLength(addr4, t, d) + bigintToHex(satB1T2A4) +
|
||||
spentAddressToPubKeyHexWithLength(addr5, t, d) + bigintToHex(satB1T2A5),
|
||||
nil,
|
||||
},
|
||||
keyPair{
|
||||
"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71", "",
|
||||
func(v string) bool {
|
||||
return compareFuncBlockAddresses(t, v, []string{
|
||||
addressToPubKeyHexWithLength("mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP", t, d) + "02",
|
||||
})
|
||||
},
|
||||
txidB2T1,
|
||||
"02" +
|
||||
addressToPubKeyHexWithLength(addr3, t, d) + bigintToHex(satB1T2A3) +
|
||||
addressToPubKeyHexWithLength(addr2, t, d) + bigintToHex(satB1T1A2) +
|
||||
"02" +
|
||||
spentAddressToPubKeyHexWithLength(addr6, t, d) + bigintToHex(satB2T1A6) +
|
||||
addressToPubKeyHexWithLength(addr7, t, d) + bigintToHex(satB2T1A7),
|
||||
nil,
|
||||
},
|
||||
keyPair{
|
||||
"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07",
|
||||
addressToPubKeyHexWithLength("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "00",
|
||||
txidB2T2,
|
||||
"02" +
|
||||
addressToPubKeyHexWithLength(addr6, t, d) + bigintToHex(satB2T1A6) +
|
||||
addressToPubKeyHexWithLength(addr4, t, d) + bigintToHex(satB1T2A4) +
|
||||
"02" +
|
||||
addressToPubKeyHexWithLength(addr8, t, d) + bigintToHex(satB2T2A8) +
|
||||
addressToPubKeyHexWithLength(addr9, t, d) + bigintToHex(satB2T2A9),
|
||||
nil,
|
||||
},
|
||||
keyPair{
|
||||
txidB2T3,
|
||||
"01" +
|
||||
addressToPubKeyHexWithLength(addr5, t, d) + bigintToHex(satB1T2A5) +
|
||||
"01" +
|
||||
addressToPubKeyHexWithLength(addr5, t, d) + bigintToHex(satB2T3A5),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
|
@ -391,21 +455,23 @@ func verifyAfterUTXOBlock2(t *testing.T, d *RocksDB) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkColumn(d, cfBlockAddresses, []keyPair{
|
||||
keyPair{"000370d6", "",
|
||||
func(v string) bool {
|
||||
return compareFuncBlockAddresses(t, v, []string{
|
||||
addressToPubKeyHexWithLength("mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP", t, d) + "00",
|
||||
addressToPubKeyHexWithLength("mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw", t, d) + "02" + "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "00",
|
||||
addressToPubKeyHexWithLength("mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", t, d) + "02" + "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840" + "02",
|
||||
addressToPubKeyHexWithLength("2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS", t, d) + "02" + "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "02",
|
||||
addressToPubKeyHexWithLength("2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1", t, d) + "02" + "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75" + "04",
|
||||
})
|
||||
},
|
||||
},
|
||||
if err := checkColumn(d, cfAddressBalance, []keyPair{
|
||||
keyPair{addressToPubKeyHex(addr1, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB1T1A1), nil},
|
||||
keyPair{addressToPubKeyHex(addr2, t, d), "02" + bigintToHex(satB1T1A2) + bigintToHex(satZero), nil},
|
||||
keyPair{addressToPubKeyHex(addr3, t, d), "02" + bigintToHex(satB1T2A3) + bigintToHex(satZero), nil},
|
||||
keyPair{addressToPubKeyHex(addr4, t, d), "02" + bigintToHex(satB1T2A4) + bigintToHex(satZero), nil},
|
||||
keyPair{addressToPubKeyHex(addr5, t, d), "02" + bigintToHex(satB1T2A5) + bigintToHex(satB2T3A5), nil},
|
||||
keyPair{addressToPubKeyHex(addr6, t, d), "02" + bigintToHex(satB2T1A6) + bigintToHex(satZero), nil},
|
||||
keyPair{addressToPubKeyHex(addr7, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB2T1A7), nil},
|
||||
keyPair{addressToPubKeyHex(addr8, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB2T2A8), nil},
|
||||
keyPair{addressToPubKeyHex(addr9, t, d), "01" + bigintToHex(satZero) + bigintToHex(satB2T2A9), nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkColumn(d, cfBlockTxids, []keyPair{
|
||||
keyPair{"000370d6", txidB2T1 + txidB2T2 + txidB2T3, nil},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
|
@ -487,12 +553,12 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
|
|||
})
|
||||
defer closeAndDestroyRocksDB(t, d)
|
||||
|
||||
// connect 1st block - will log warnings about missing UTXO transactions in cfUnspentTxs column
|
||||
// connect 1st block - will log warnings about missing UTXO transactions in txAddresses column
|
||||
block1 := getTestUTXOBlock1(t, d)
|
||||
if err := d.ConnectBlock(block1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
verifyAfterUTXOBlock1(t, d, false)
|
||||
verifyAfterUTXOBlock1(t, d)
|
||||
|
||||
// connect 2nd block - use some outputs from the 1st block as the inputs and 1 input uses tx from the same block
|
||||
block2 := getTestUTXOBlock2(t, d)
|
||||
|
@ -502,19 +568,19 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
|
|||
verifyAfterUTXOBlock2(t, d)
|
||||
|
||||
// get transactions for various addresses / low-high ranges
|
||||
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", 0, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", 1, true},
|
||||
txidVoutOutput{"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25", 1, false},
|
||||
verifyGetTransactions(t, d, addr2, 0, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{txidB1T1, 1, true},
|
||||
txidVoutOutput{txidB2T1, 1, false},
|
||||
}, nil)
|
||||
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", 225493, 225493, []txidVoutOutput{
|
||||
txidVoutOutput{"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", 1, true},
|
||||
verifyGetTransactions(t, d, addr2, 225493, 225493, []txidVoutOutput{
|
||||
txidVoutOutput{txidB1T1, 1, true},
|
||||
}, nil)
|
||||
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", 225494, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25", 1, false},
|
||||
verifyGetTransactions(t, d, addr2, 225494, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{txidB2T1, 1, false},
|
||||
}, nil)
|
||||
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz", 500000, 1000000, []txidVoutOutput{}, nil)
|
||||
verifyGetTransactions(t, d, "mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC", 0, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71", 0, true},
|
||||
verifyGetTransactions(t, d, addr2, 500000, 1000000, []txidVoutOutput{}, nil)
|
||||
verifyGetTransactions(t, d, addr8, 0, 1000000, []txidVoutOutput{
|
||||
txidVoutOutput{txidB2T2, 0, true},
|
||||
}, nil)
|
||||
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidVoutOutput{}, errors.New("checksum mismatch"))
|
||||
|
||||
|
@ -569,7 +635,7 @@ func TestRocksDB_Index_UTXO(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
verifyAfterUTXOBlock1(t, d, true)
|
||||
verifyAfterUTXOBlock1(t, d)
|
||||
if err := checkColumn(d, cfTransactions, []keyPair{}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
|
@ -661,13 +727,13 @@ func Test_unpackBlockAddresses(t *testing.T) {
|
|||
want2: [][]hexoutpoint{
|
||||
[]hexoutpoint{},
|
||||
[]hexoutpoint{
|
||||
hexoutpoint{"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25", 0},
|
||||
hexoutpoint{"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", 3},
|
||||
hexoutpoint{txidB2T1, 0},
|
||||
hexoutpoint{txidB1T1, 3},
|
||||
},
|
||||
[]hexoutpoint{},
|
||||
[]hexoutpoint{},
|
||||
[]hexoutpoint{
|
||||
hexoutpoint{"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75", 1},
|
||||
hexoutpoint{txidB1T2, 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -679,13 +745,13 @@ func Test_unpackBlockAddresses(t *testing.T) {
|
|||
[]hexoutpoint{},
|
||||
[]hexoutpoint{},
|
||||
[]hexoutpoint{
|
||||
hexoutpoint{"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75", 0},
|
||||
hexoutpoint{txidB1T2, 0},
|
||||
},
|
||||
[]hexoutpoint{
|
||||
hexoutpoint{"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", 1},
|
||||
hexoutpoint{txidB1T1, 1},
|
||||
},
|
||||
[]hexoutpoint{
|
||||
hexoutpoint{"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75", 1},
|
||||
hexoutpoint{txidB1T2, 1},
|
||||
},
|
||||
[]hexoutpoint{},
|
||||
[]hexoutpoint{},
|
||||
|
@ -714,7 +780,7 @@ func Test_unpackBlockAddresses(t *testing.T) {
|
|||
for i, g := range got2 {
|
||||
ho := make([]hexoutpoint, len(g))
|
||||
for j, o := range g {
|
||||
ho[j] = hexoutpoint{hex.EncodeToString(o.btxID), o.vout}
|
||||
ho[j] = hexoutpoint{hex.EncodeToString(o.btxID), o.index}
|
||||
}
|
||||
h2[i] = ho
|
||||
}
|
||||
|
@ -741,12 +807,12 @@ func Test_packBigint_unpackBigint(t *testing.T) {
|
|||
{
|
||||
name: "0",
|
||||
bi: big.NewInt(0),
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "1",
|
||||
bi: big.NewInt(1),
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "54321",
|
||||
|
@ -756,27 +822,27 @@ func Test_packBigint_unpackBigint(t *testing.T) {
|
|||
{
|
||||
name: "12345678",
|
||||
bi: big.NewInt(12345678),
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "123456789123456789",
|
||||
bi: big.NewInt(123456789123456789),
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "bigbig1",
|
||||
bi: bigbig1,
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "bigbig2",
|
||||
bi: bigbig2,
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
},
|
||||
{
|
||||
name: "bigbigbig",
|
||||
bi: bigbigbig,
|
||||
buf: make([]byte, 249),
|
||||
buf: make([]byte, maxPackedBigintBytes),
|
||||
toobiglen: 242,
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue