Index ETH internal transactions

This commit is contained in:
Martin Boehm 2021-12-18 02:41:04 +01:00 committed by Martin
parent c374ef86fd
commit e3bb706ea2
7 changed files with 431 additions and 58 deletions

View File

@ -701,9 +701,9 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
continue
}
// filter only transactions of this contract
filter.Vout = i + 1
filter.Vout = i + db.ContractIndexOffset
}
t, err := w.getEthereumToken(i+1, addrDesc, c.Contract, details, int(c.Txs))
t, err := w.getEthereumToken(i+db.ContractIndexOffset, addrDesc, c.Contract, details, int(c.Txs))
if err != nil {
return nil, nil, nil, 0, 0, 0, err
}

View File

@ -70,6 +70,7 @@ func init() {
BlockChainFactories["Ethereum"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Ropsten Archive"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Goerli"] = eth.NewEthereumRPC
BlockChainFactories["Bcash"] = bch.NewBCashRPC
BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC

View File

@ -110,8 +110,11 @@ const (
// BitcoinType
cfAddressBalance
cfTxAddresses
__break__
// EthereumType
cfAddressContracts = cfAddressBalance
cfAddressContracts = iota - __break__ + cfAddressBalance - 1
cfInternalData
cfContracts
)

View File

@ -3,6 +3,7 @@ package db
import (
"bytes"
"encoding/hex"
"math/big"
vlq "github.com/bsm/go-vlq"
"github.com/flier/gorocksdb"
@ -12,6 +13,9 @@ import (
"github.com/trezor/blockbook/bchain/coins/eth"
)
const InternalTxIndexOffset = 1
const ContractIndexOffset = 2
// AddrContract is Contract address with number of transactions done by given address
type AddrContract struct {
Contract bchain.AddressDescriptor
@ -108,6 +112,11 @@ func isZeroAddress(addrDesc bchain.AddressDescriptor) bool {
return true
}
const transferTo = int32(0)
const transferFrom = ^int32(0)
const internalTransferTo = int32(1)
const internalTransferFrom = ^int32(1)
func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.AddressDescriptor, btxID []byte, index int32, contract bchain.AddressDescriptor, addresses addressesMap, addressContracts map[string]*AddrContracts, addTxCount bool) error {
var err error
strAddrDesc := string(addrDesc)
@ -127,7 +136,11 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address
}
if contract == nil {
if addTxCount {
ac.NonContractTxs++
if index == internalTransferFrom || index == internalTransferTo {
ac.InternalTxs++
} else {
ac.NonContractTxs++
}
}
} else {
// do not store contracts for 0x0000000000000000000000000000000000000000 address
@ -138,15 +151,21 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address
i = len(ac.Contracts)
ac.Contracts = append(ac.Contracts, AddrContract{Contract: contract})
}
// index 0 is for ETH transfers, contract indexes start with 1
// index 0 is for ETH transfers, index 1 (InternalTxIndexOffset) is for internal transfers, contract indexes start with 2 (ContractIndexOffset)
if index < 0 {
index = ^int32(i + 1)
index = ^int32(i + ContractIndexOffset)
} else {
index = int32(i + 1)
index = int32(i + ContractIndexOffset)
}
if addTxCount {
ac.Contracts[i].Txs++
}
} else {
if index < 0 {
index = transferFrom
} else {
index = transferTo
}
}
}
counted := addToAddressesMap(addresses, strAddrDesc, btxID, index)
@ -160,10 +179,23 @@ type ethBlockTxContract struct {
addr, contract bchain.AddressDescriptor
}
type ethInternalTransfer struct {
internalType bchain.EthereumInternalTransactionType
from, to bchain.AddressDescriptor
value big.Int
}
type ethInternalData struct {
internalType bchain.EthereumInternalTransactionType
contract bchain.AddressDescriptor
transfers []ethInternalTransfer
}
type ethBlockTx struct {
btxID []byte
from, to bchain.AddressDescriptor
contracts []ethBlockTxContract
btxID []byte
from, to bchain.AddressDescriptor
contracts []ethBlockTxContract
internalData *ethInternalData
}
func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses addressesMap, addressContracts map[string]*AddrContracts) ([]ethBlockTx, error) {
@ -184,12 +216,12 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output", err, block.Height, tx.Txid)
}
continue
} else {
if err = d.addToAddressesAndContractsEthereumType(to, btxID, transferTo, nil, addresses, addressContracts, true); err != nil {
return nil, err
}
blockTx.to = to
}
if err = d.addToAddressesAndContractsEthereumType(to, btxID, 0, nil, addresses, addressContracts, true); err != nil {
return nil, err
}
blockTx.to = to
}
// there is only one input address in EthereumType transaction, store it in format txid ^0
if len(tx.Vin) == 1 && len(tx.Vin[0].Addresses) == 1 {
@ -198,12 +230,68 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, input", err, block.Height, tx.Txid)
}
continue
} else {
if err = d.addToAddressesAndContractsEthereumType(from, btxID, transferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil {
return nil, err
}
blockTx.from = from
}
if err = d.addToAddressesAndContractsEthereumType(from, btxID, ^int32(0), nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil {
return nil, err
}
// process internal data
eid, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData)
if eid.InternalData != nil {
blockTx.internalData = &ethInternalData{
internalType: eid.InternalData.Type,
}
// index contract creation
if eid.InternalData.Type == bchain.CREATE {
to, err = d.chainParser.GetAddrDescFromAddress(eid.InternalData.Contract)
if err != nil {
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, create contract", err, block.Height, tx.Txid)
}
// set the internalType to CALL if incorrect contract so that it is not breaking the packing of data to DB
blockTx.internalData.internalType = bchain.CALL
} else {
blockTx.internalData.contract = to
if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil {
return nil, err
}
}
}
// index internal transfers
if len(eid.InternalData.Transfers) > 0 {
blockTx.internalData.transfers = make([]ethInternalTransfer, len(eid.InternalData.Transfers))
for i := range eid.InternalData.Transfers {
iti := &eid.InternalData.Transfers[i]
ito := &blockTx.internalData.transfers[i]
to, err = d.chainParser.GetAddrDescFromAddress(iti.To)
if err != nil {
// do not log ErrAddressMissing, transactions can be without to address (for example eth contracts)
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d to", err, block.Height, tx.Txid, i)
}
} else {
if err = d.addToAddressesAndContractsEthereumType(to, btxID, internalTransferTo, nil, addresses, addressContracts, true); err != nil {
return nil, err
}
ito.to = to
}
from, err = d.chainParser.GetAddrDescFromAddress(iti.From)
if err != nil {
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, internal transfer %d from", err, block.Height, tx.Txid, i)
}
} else {
if err = d.addToAddressesAndContractsEthereumType(from, btxID, internalTransferFrom, nil, addresses, addressContracts, !bytes.Equal(from, to)); err != nil {
return nil, err
}
ito.from = from
}
ito.internalType = iti.Type
ito.value = iti.Value
}
}
blockTx.from = from
}
// store erc20 transfers
erc20, err := d.chainParser.EthereumTypeGetErc20FromTx(&tx)
@ -249,14 +337,95 @@ func (d *RocksDB) processAddressesEthereumType(block *bchain.Block, addresses ad
return blockTxs, nil
}
var ethZeroAddress []byte = make([]byte, eth.EthereumTypeAddressDescriptorLen)
func packEthInternalData(data *ethInternalData) []byte {
// allocate enough for type+contract+all transfers with bigint value
buf := make([]byte, 0, (2*len(data.transfers)+1)*(eth.EthereumTypeAddressDescriptorLen+16))
appendAddress := func(a bchain.AddressDescriptor) {
if len(a) != eth.EthereumTypeAddressDescriptorLen {
buf = append(buf, ethZeroAddress...)
} else {
buf = append(buf, a...)
}
}
varBuf := make([]byte, maxPackedBigintBytes)
// internalType is one bit (CALL|CREATE), it is joined with count of internal transfers*2
l := packVaruint(uint(data.internalType)&1+uint(len(data.transfers))<<1, varBuf)
buf = append(buf, varBuf[:l]...)
if data.internalType == bchain.CREATE {
appendAddress(data.contract)
}
for i := range data.transfers {
t := &data.transfers[i]
buf = append(buf, byte(t.internalType))
appendAddress(t.from)
appendAddress(t.to)
l = packBigint(&t.value, varBuf)
buf = append(buf, varBuf[:l]...)
}
return buf
}
func (d *RocksDB) unpackEthInternalData(buf []byte) (*bchain.EthereumInternalData, error) {
id := bchain.EthereumInternalData{}
v, l := unpackVaruint(buf)
id.Type = bchain.EthereumInternalTransactionType(v & 1)
id.Transfers = make([]bchain.EthereumInternalTransfer, v>>1)
if id.Type == bchain.CREATE {
addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen])
l += eth.EthereumTypeAddressDescriptorLen
if len(addresses) > 0 {
id.Contract = addresses[0]
}
}
var ll int
for i := range id.Transfers {
t := &id.Transfers[i]
t.Type = bchain.EthereumInternalTransactionType(buf[l])
l++
addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen])
l += eth.EthereumTypeAddressDescriptorLen
if len(addresses) > 0 {
t.From = addresses[0]
}
addresses, _, _ = d.chainParser.GetAddressesFromAddrDesc(buf[l : l+eth.EthereumTypeAddressDescriptorLen])
l += eth.EthereumTypeAddressDescriptorLen
if len(addresses) > 0 {
t.To = addresses[0]
}
t.Value, ll = unpackBigint(buf[l:])
l += ll
}
return &id, nil
}
func (d *RocksDB) GetEthereumInternalData(txid string) (*bchain.EthereumInternalData, error) {
btxID, err := d.chainParser.PackTxid(txid)
if err != nil {
return nil, err
}
val, err := d.db.GetCF(d.ro, d.cfh[cfInternalData], btxID)
if err != nil {
return nil, err
}
defer val.Free()
buf := val.Data()
if len(buf) == 0 {
return nil, nil
}
return d.unpackEthInternalData(buf)
}
func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block, blockTxs []ethBlockTx) error {
pl := d.chainParser.PackedTxidLen()
buf := make([]byte, 0, (pl+2*eth.EthereumTypeAddressDescriptorLen)*len(blockTxs))
varBuf := make([]byte, vlq.MaxLen64)
zeroAddress := make([]byte, eth.EthereumTypeAddressDescriptorLen)
appendAddress := func(a bchain.AddressDescriptor) {
if len(a) != eth.EthereumTypeAddressDescriptorLen {
buf = append(buf, zeroAddress...)
buf = append(buf, ethZeroAddress...)
} else {
buf = append(buf, a...)
}
@ -266,7 +435,29 @@ func (d *RocksDB) storeAndCleanupBlockTxsEthereumType(wb *gorocksdb.WriteBatch,
buf = append(buf, blockTx.btxID...)
appendAddress(blockTx.from)
appendAddress(blockTx.to)
l := packVaruint(uint(len(blockTx.contracts)), varBuf)
// internal data - store the number of addresses, with odd number the CREATE tx type
var internalDataTransfers uint
if blockTx.internalData != nil {
wb.PutCF(d.cfh[cfInternalData], blockTx.btxID, packEthInternalData(blockTx.internalData))
internalDataTransfers = uint(len(blockTx.internalData.transfers)) * 2
if blockTx.internalData.internalType == bchain.CREATE {
internalDataTransfers++
}
}
l := packVaruint(internalDataTransfers, varBuf)
buf = append(buf, varBuf[:l]...)
if internalDataTransfers > 0 {
if blockTx.internalData.internalType == bchain.CREATE {
appendAddress(blockTx.internalData.contract)
}
for j := range blockTx.internalData.transfers {
c := &blockTx.internalData.transfers[j]
appendAddress(c.from)
appendAddress(c.to)
}
}
// contracts - store the number of address pairs
l = packVaruint(uint(len(blockTx.contracts)), varBuf)
buf = append(buf, varBuf[:l]...)
for j := range blockTx.contracts {
c := &blockTx.contracts[j]
@ -323,8 +514,33 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) {
if err != nil {
return nil, err
}
// internal data
var internalData *ethInternalData
cc, l := unpackVaruint(buf[i:])
i += l
if cc > 0 {
internalData = &ethInternalData{}
// odd count of internal transfers means it is CREATE transaction with the contract added to the list
if cc&1 == 1 {
internalData.internalType = bchain.CREATE
internalData.contract, i, err = getAddress(i)
if err != nil {
return nil, err
}
}
internalData.transfers = make([]ethInternalTransfer, cc/2)
for j := range internalData.transfers {
t := &internalData.transfers[j]
t.from, i, err = getAddress(i)
t.to, i, err = getAddress(i)
if err != nil {
return nil, err
}
}
}
// contracts
cc, l = unpackVaruint(buf[i:])
i += l
contracts := make([]ethBlockTxContract, cc)
for j := range contracts {
contracts[j].addr, i, err = getAddress(i)
@ -337,10 +553,11 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) {
}
}
bt = append(bt, ethBlockTx{
btxID: txid,
from: from,
to: to,
contracts: contracts,
btxID: txid,
from: from,
to: to,
internalData: internalData,
contracts: contracts,
})
}
return bt, nil
@ -349,7 +566,7 @@ func (d *RocksDB) getBlockTxsEthereumType(height uint32) ([]ethBlockTx, error) {
func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, height uint32, blockTxs []ethBlockTx, contracts map[string]*AddrContracts) error {
glog.Info("Disconnecting block ", height, " containing ", len(blockTxs), " transactions")
addresses := make(map[string]map[string]struct{})
disconnectAddress := func(btxID []byte, addrDesc, contract bchain.AddressDescriptor) error {
disconnectAddress := func(btxID []byte, internal bool, addrDesc, contract bchain.AddressDescriptor) error {
var err error
// do not process empty address
if len(addrDesc) == 0 {
@ -382,10 +599,18 @@ func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, heigh
c.TotalTxs--
}
if contract == nil {
if c.NonContractTxs > 0 {
c.NonContractTxs--
if internal {
if c.InternalTxs > 0 {
c.InternalTxs--
} else {
glog.Warning("AddressContracts ", addrDesc, ", InternalTxs would be negative, tx ", hex.EncodeToString(btxID))
}
} else {
glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID))
if c.NonContractTxs > 0 {
c.NonContractTxs--
} else {
glog.Warning("AddressContracts ", addrDesc, ", EthTxs would be negative, tx ", hex.EncodeToString(btxID))
}
}
} else {
i, found := findContractInAddressContracts(contract, c.Contracts)
@ -409,21 +634,41 @@ func (d *RocksDB) disconnectBlockTxsEthereumType(wb *gorocksdb.WriteBatch, heigh
}
for i := range blockTxs {
blockTx := &blockTxs[i]
if err := disconnectAddress(blockTx.btxID, blockTx.from, nil); err != nil {
if err := disconnectAddress(blockTx.btxID, false, blockTx.from, nil); err != nil {
return err
}
// if from==to, tx is counted only once and does not have to be disconnected again
if !bytes.Equal(blockTx.from, blockTx.to) {
if err := disconnectAddress(blockTx.btxID, blockTx.to, nil); err != nil {
if err := disconnectAddress(blockTx.btxID, false, blockTx.to, nil); err != nil {
return err
}
}
if blockTx.internalData != nil {
if blockTx.internalData.internalType == bchain.CREATE {
if err := disconnectAddress(blockTx.btxID, true, blockTx.internalData.contract, nil); err != nil {
return err
}
}
for j := range blockTx.internalData.transfers {
t := &blockTx.internalData.transfers[j]
if err := disconnectAddress(blockTx.btxID, true, t.from, nil); err != nil {
return err
}
// if from==to, tx is counted only once and does not have to be disconnected again
if !bytes.Equal(t.from, t.to) {
if err := disconnectAddress(blockTx.btxID, true, t.to, nil); err != nil {
return err
}
}
}
}
for _, c := range blockTx.contracts {
if err := disconnectAddress(blockTx.btxID, c.addr, c.contract); err != nil {
if err := disconnectAddress(blockTx.btxID, false, c.addr, c.contract); err != nil {
return err
}
}
wb.DeleteCF(d.cfh[cfTransactions], blockTx.btxID)
wb.DeleteCF(d.cfh[cfInternalData], blockTx.btxID)
}
for a := range addresses {
key := packAddressKey([]byte(a), height)

View File

@ -8,6 +8,7 @@ import (
"testing"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/eth"
"github.com/trezor/blockbook/tests/dbtestdata"
)
@ -33,10 +34,11 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
}
}
if err := checkColumn(d, cfAddresses, []keyPair{
{addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^1}), nil},
{addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil},
{addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil},
{addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0, 1}), nil},
}); err != nil {
{
t.Fatal(err)
@ -44,10 +46,26 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
}
if err := checkColumn(d, cfAddressContracts, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil},
}); err != nil {
{
t.Fatal(err)
}
}
if err := checkColumn(d, cfInternalData, []keyPair{
{
dbtestdata.EthTxidB1T2,
"06" +
"01" + dbtestdata.EthAddr9f + dbtestdata.EthAddrContract4a + "030f4240" +
"00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr9f + "030f4241" +
"00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr3e + "030f4242",
nil,
},
}); err != nil {
{
t.Fatal(err)
@ -62,9 +80,13 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
{
"0041eee8",
dbtestdata.EthTxidB1T1 +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" + "00" +
dbtestdata.EthTxidB1T2 +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) +
"06" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) +
"02" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser),
@ -97,15 +119,18 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) {
}
}
if err := checkColumn(d, cfAddresses, []keyPair{
{addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^1}), nil},
{addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr4b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^0, 1, ^2, 2, ^1}), nil},
{addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^1, 2}), nil},
{addressKeyHex(dbtestdata.EthAddr3e, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1, ^1}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{2}) + txIndexesHex(dbtestdata.EthTxidB1T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr20, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^0, ^2}), nil},
{addressKeyHex(dbtestdata.EthAddr9f, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{^1, 1}), nil},
{addressKeyHex(dbtestdata.EthAddrContract4a, 4321000, d), txIndexesHex(dbtestdata.EthTxidB1T2, []int32{0, 1}), nil},
{addressKeyHex(dbtestdata.EthAddr55, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^3, 2}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{^0}), nil},
{addressKeyHex(dbtestdata.EthAddr9f, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1, 1}) + txIndexesHex(dbtestdata.EthTxidB2T1, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddr4b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^0, ^1, 2, ^3, 3, ^2}), nil},
{addressKeyHex(dbtestdata.EthAddr7b, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^2, 3}), nil},
{addressKeyHex(dbtestdata.EthAddrContract0d, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{1}), nil},
{addressKeyHex(dbtestdata.EthAddrContract47, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{0}), nil},
{addressKeyHex(dbtestdata.EthAddrContract4a, 4321001, d), txIndexesHex(dbtestdata.EthTxidB2T2, []int32{^1}), nil},
}); err != nil {
{
t.Fatal(err)
@ -113,13 +138,14 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) {
}
if err := checkColumn(d, cfAddressContracts, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "040200" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "020102", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser), "010001", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil},
}); err != nil {
{
@ -127,13 +153,39 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) {
}
}
if err := checkColumn(d, cfInternalData, []keyPair{
{
dbtestdata.EthTxidB1T2,
"06" +
"01" + dbtestdata.EthAddr9f + dbtestdata.EthAddrContract4a + "030f4240" +
"00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr9f + "030f4241" +
"00" + dbtestdata.EthAddr3e + dbtestdata.EthAddr3e + "030f4242",
nil,
},
{
dbtestdata.EthTxidB2T2,
"05" + dbtestdata.EthAddrContract0d +
"00" + dbtestdata.EthAddr4b + dbtestdata.EthAddr9f + "030f424a" +
"02" + dbtestdata.EthAddrContract4a + dbtestdata.EthAddr9f + "030f424b",
nil,
},
}); err != nil {
{
t.Fatal(err)
}
}
if err := checkColumn(d, cfBlockTxs, []keyPair{
{
"0041eee9",
dbtestdata.EthTxidB2T1 +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" + "00" +
dbtestdata.EthTxidB2T2 +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser) +
"05" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) +
"08" +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) +
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) +
@ -152,6 +204,19 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) {
}
}
func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData {
out := *in
if out.Type == bchain.CREATE {
out.Contract = eth.EIP55AddressFromAddress(out.Contract)
}
for i := range out.Transfers {
t := &out.Transfers[i]
t.From = eth.EIP55AddressFromAddress(t.From)
t.To = eth.EIP55AddressFromAddress(t.To)
}
return &out
}
// TestRocksDB_Index_EthereumType is an integration test probing the whole indexing functionality for EthereumType chains
// It does the following:
// 1) Connect two blocks (inputs from 2nd block are spending some outputs from the 1st block)
@ -195,14 +260,27 @@ func TestRocksDB_Index_EthereumType(t *testing.T) {
// get transactions for various addresses / low-high ranges
verifyGetTransactions(t, d, "0x"+dbtestdata.EthAddr55, 0, 10000000, []txidIndex{
{"0x" + dbtestdata.EthTxidB2T2, ^2},
{"0x" + dbtestdata.EthTxidB2T2, 1},
{"0x" + dbtestdata.EthTxidB2T2, ^3},
{"0x" + dbtestdata.EthTxidB2T2, 2},
{"0x" + dbtestdata.EthTxidB2T1, ^0},
{"0x" + dbtestdata.EthTxidB1T2, 1},
{"0x" + dbtestdata.EthTxidB1T2, 2},
{"0x" + dbtestdata.EthTxidB1T1, 0},
}, nil)
verifyGetTransactions(t, d, "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eBad", 500000, 1000000, []txidIndex{}, errors.New("Address missing"))
id, err := d.GetEthereumInternalData(dbtestdata.EthTxidB1T1)
if err != nil || id != nil {
t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB1T1, id, nil, err)
}
id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB1T2)
if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx2InternalData)) {
t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB1T2, id, formatInternalData(dbtestdata.EthTx2InternalData), err)
}
id, err = d.GetEthereumInternalData(dbtestdata.EthTxidB2T2)
if err != nil || !reflect.DeepEqual(id, formatInternalData(dbtestdata.EthTx4InternalData)) {
t.Errorf("GetEthereumInternalData(%s) = %+v, want %+v, err %v", dbtestdata.EthTxidB2T2, id, formatInternalData(dbtestdata.EthTx4InternalData), err)
}
// GetBestBlock
height, hash, err := d.GetBestBlock()
if err != nil {

File diff suppressed because one or more lines are too long