Ensure ordering of address and xpub txs in the same block

pull/133/head
Martin Boehm 2019-03-05 13:48:11 +01:00
parent 3551c90590
commit 3d10d9c2c5
4 changed files with 30 additions and 8 deletions

View File

@ -34,9 +34,17 @@ type xpubTxid struct {
type xpubTxids []xpubTxid type xpubTxids []xpubTxid
func (a xpubTxids) Len() int { return len(a) } func (a xpubTxids) Len() int { return len(a) }
func (a xpubTxids) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a xpubTxids) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a xpubTxids) Less(i, j int) bool { return a[i].height >= a[j].height } func (a xpubTxids) Less(i, j int) bool {
// if the heights are equal, make inputs less than outputs
hi := a[i].height
hj := a[j].height
if hi == hj {
return (a[i].inputOutput & txInput) >= (a[j].inputOutput & txInput)
}
return hi > hj
}
type xpubAddress struct { type xpubAddress struct {
addrDesc bchain.AddressDescriptor addrDesc bchain.AddressDescriptor

View File

@ -353,37 +353,44 @@ type outpoint struct {
index int32 index int32
} }
// TxInput holds input data of the transaction in TxAddresses
type TxInput struct { type TxInput struct {
AddrDesc bchain.AddressDescriptor AddrDesc bchain.AddressDescriptor
ValueSat big.Int ValueSat big.Int
} }
// Addresses converts AddressDescriptor of the input to array of strings
func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) { func (ti *TxInput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) {
return p.GetAddressesFromAddrDesc(ti.AddrDesc) return p.GetAddressesFromAddrDesc(ti.AddrDesc)
} }
// TxOutput holds output data of the transaction in TxAddresses
type TxOutput struct { type TxOutput struct {
AddrDesc bchain.AddressDescriptor AddrDesc bchain.AddressDescriptor
Spent bool Spent bool
ValueSat big.Int ValueSat big.Int
} }
// Addresses converts AddressDescriptor of the output to array of strings
func (to *TxOutput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) { func (to *TxOutput) Addresses(p bchain.BlockChainParser) ([]string, bool, error) {
return p.GetAddressesFromAddrDesc(to.AddrDesc) return p.GetAddressesFromAddrDesc(to.AddrDesc)
} }
// TxAddresses stores transaction inputs and outputs with amounts
type TxAddresses struct { type TxAddresses struct {
Height uint32 Height uint32
Inputs []TxInput Inputs []TxInput
Outputs []TxOutput Outputs []TxOutput
} }
// AddrBalance stores number of transactions and balances of an address
type AddrBalance struct { type AddrBalance struct {
Txs uint32 Txs uint32
SentSat big.Int SentSat big.Int
BalanceSat big.Int BalanceSat big.Int
} }
// ReceivedSat computes received amount from total balance and sent amount
func (ab *AddrBalance) ReceivedSat() *big.Int { func (ab *AddrBalance) ReceivedSat() *big.Int {
var r big.Int var r big.Int
r.Add(&ab.BalanceSat, &ab.SentSat) r.Add(&ab.BalanceSat, &ab.SentSat)
@ -547,15 +554,18 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
return nil return nil
} }
// addToAddressesMap maintains mapping between addresses and transactions in one block
// the method assumes that outpus in the block are processed before the inputs
// the return value is true if the tx was processed before, to not to count the tx multiple times
func addToAddressesMap(addresses addressesMap, strAddrDesc string, btxID []byte, index int32) bool { func addToAddressesMap(addresses addressesMap, strAddrDesc string, btxID []byte, index int32) bool {
// check that the address was used already in this block // check that the address was already processed in this block
// if not found, it has certainly not been counted // if not found, it has certainly not been counted
at, found := addresses[strAddrDesc] at, found := addresses[strAddrDesc]
if found { if found {
// check that the address was already used in this tx // if the tx is already in the slice, append the index to the array of indexes
for i, t := range at { for i, t := range at {
if bytes.Equal(btxID, t.btxID) { if bytes.Equal(btxID, t.btxID) {
at[i].indexes = append(at[i].indexes, index) at[i].indexes = append(t.indexes, index)
return true return true
} }
} }

View File

@ -219,7 +219,7 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad
glog.Warningf("rocksdb: GetErc20FromTx %v - height %d, tx %v, transfer %v", err, block.Height, tx.Txid, t) glog.Warningf("rocksdb: GetErc20FromTx %v - height %d, tx %v, transfer %v", err, block.Height, tx.Txid, t)
continue continue
} }
if err = d.addToAddressesAndContractsEthereumType(from, btxID, ^int32(i), contract, addresses, addressContracts, true); err != nil { if err = d.addToAddressesAndContractsEthereumType(to, btxID, int32(i), contract, addresses, addressContracts, true); err != nil {
return nil, err return nil, err
} }
eq := bytes.Equal(from, to) eq := bytes.Equal(from, to)
@ -227,7 +227,7 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad
j++ j++
bc.addr = from bc.addr = from
bc.contract = contract bc.contract = contract
if err = d.addToAddressesAndContractsEthereumType(to, btxID, int32(i), contract, addresses, addressContracts, !eq); err != nil { if err = d.addToAddressesAndContractsEthereumType(from, btxID, ^int32(i), contract, addresses, addressContracts, !eq); err != nil {
return nil, err return nil, err
} }
// add to address to blockTx.contracts only if it is different from from address // add to address to blockTx.contracts only if it is different from from address

View File

@ -484,6 +484,10 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) {
verifyGetTransactions(t, d, dbtestdata.Addr8, 0, 1000000, []txidIndex{ verifyGetTransactions(t, d, dbtestdata.Addr8, 0, 1000000, []txidIndex{
{dbtestdata.TxidB2T2, 0}, {dbtestdata.TxidB2T2, 0},
}, nil) }, nil)
verifyGetTransactions(t, d, dbtestdata.Addr6, 0, 1000000, []txidIndex{
{dbtestdata.TxidB2T2, ^0},
{dbtestdata.TxidB2T1, 0},
}, nil)
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidIndex{}, errors.New("checksum mismatch")) verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidIndex{}, errors.New("checksum mismatch"))
// GetBestBlock // GetBestBlock