Return address aliases from API

This commit is contained in:
Martin Boehm 2022-04-25 00:16:04 +02:00 committed by Martin
parent 77561e3567
commit 8bdc3da694
19 changed files with 276 additions and 60 deletions

View File

@ -211,6 +211,12 @@ type EthereumSpecific struct {
InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"` InternalTransfers []EthereumInternalTransfer `json:"internalTransfers,omitempty"`
} }
type AddressAlias struct {
Type string
Alias string
}
type AddressAliasesMap map[string]AddressAlias
// Tx holds information about a transaction // Tx holds information about a transaction
type Tx struct { type Tx struct {
Txid string `json:"txid"` Txid string `json:"txid"`
@ -231,6 +237,7 @@ type Tx struct {
CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty"` CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty"`
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"` TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"`
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"` EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"`
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
} }
// FeeStats contains detailed block fee statistics // FeeStats contains detailed block fee statistics
@ -298,6 +305,7 @@ type Address struct {
UsedTokens int `json:"usedTokens,omitempty"` UsedTokens int `json:"usedTokens,omitempty"`
Tokens []Token `json:"tokens,omitempty"` Tokens []Token `json:"tokens,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"` Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"`
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
// helpers for explorer // helpers for explorer
Filter string `json:"-"` Filter string `json:"-"`
XPubAddresses map[string]struct{} `json:"-"` XPubAddresses map[string]struct{} `json:"-"`
@ -428,8 +436,9 @@ type BlockInfo struct {
type Block struct { type Block struct {
Paging Paging
BlockInfo BlockInfo
TxCount int `json:"txCount"` TxCount int `json:"txCount"`
Transactions []*Tx `json:"txs,omitempty"` Transactions []*Tx `json:"txs,omitempty"`
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
} }
// BlockRaw contains raw block in hex // BlockRaw contains raw block in hex

View File

@ -23,27 +23,29 @@ import (
// Worker is handle to api worker // Worker is handle to api worker
type Worker struct { type Worker struct {
db *db.RocksDB db *db.RocksDB
txCache *db.TxCache txCache *db.TxCache
chain bchain.BlockChain chain bchain.BlockChain
chainParser bchain.BlockChainParser chainParser bchain.BlockChainParser
chainType bchain.ChainType chainType bchain.ChainType
mempool bchain.Mempool useAddressAliases bool
is *common.InternalState mempool bchain.Mempool
metrics *common.Metrics is *common.InternalState
metrics *common.Metrics
} }
// NewWorker creates new api worker // NewWorker creates new api worker
func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*Worker, error) { func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*Worker, error) {
w := &Worker{ w := &Worker{
db: db, db: db,
txCache: txCache, txCache: txCache,
chain: chain, chain: chain,
chainParser: chain.GetChainParser(), chainParser: chain.GetChainParser(),
chainType: chain.GetChainParser().GetChainType(), chainType: chain.GetChainParser().GetChainType(),
mempool: mempool, useAddressAliases: chain.GetChainParser().UseAddressAliases(),
is: is, mempool: mempool,
metrics: metrics, is: is,
metrics: metrics,
} }
if w.chainType == bchain.ChainBitcoinType { if w.chainType == bchain.ChainBitcoinType {
w.initXpubCache() w.initXpubCache()
@ -100,7 +102,7 @@ func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) err
// GetSpendingTxid returns transaction id of transaction that spent given output // GetSpendingTxid returns transaction id of transaction that spent given output
func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) { func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
start := time.Now() start := time.Now()
tx, err := w.GetTransaction(txid, false, false) tx, err := w.getTransaction(txid, false, false, nil)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -115,8 +117,65 @@ func (w *Worker) GetSpendingTxid(txid string, n int) (string, error) {
return tx.Vout[n].SpentTxID, nil return tx.Vout[n].SpentTxID, nil
} }
func aggregateAddress(m map[string]struct{}, a string) {
if m != nil && len(a) > 0 {
m[a] = struct{}{}
}
}
func aggregateAddresses(m map[string]struct{}, addresses []string, isAddress bool) {
if m != nil && isAddress {
for _, a := range addresses {
if len(a) > 0 {
m[a] = struct{}{}
}
}
}
}
func (w *Worker) newAddressesMapForAliases() map[string]struct{} {
if w.useAddressAliases {
return make(map[string]struct{})
}
return nil
}
func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliasesMap {
if len(addresses) > 0 {
aliases := make(AddressAliasesMap)
var t string
if w.chainType == bchain.ChainEthereumType {
t = "ENS"
} else {
t = "Alias"
}
for a := range addresses {
if w.chainType == bchain.ChainEthereumType {
// TODO get contract name
}
n := w.db.GetAddressAlias(a)
if len(n) > 0 {
aliases[a] = AddressAlias{Type: t, Alias: n}
}
}
return aliases
}
return nil
}
// GetTransaction reads transaction data from txid // GetTransaction reads transaction data from txid
func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) { func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool) (*Tx, error) {
addresses := w.newAddressesMapForAliases()
tx, err := w.getTransaction(txid, spendingTxs, specificJSON, addresses)
if err != nil {
return nil, err
}
tx.AddressAliases = w.getAddressAliases(addresses)
return tx, nil
}
// getTransaction reads transaction data from txid
func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
bchainTx, height, err := w.txCache.GetTransaction(txid) bchainTx, height, err := w.txCache.GetTransaction(txid)
if err != nil { if err != nil {
if err == bchain.ErrTxNotFound { if err == bchain.ErrTxNotFound {
@ -124,7 +183,7 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
} }
return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found (%v)", txid, err), true) return nil, NewAPIError(fmt.Sprintf("Transaction '%v' not found (%v)", txid, err), true)
} }
return w.GetTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON) return w.getTransactionFromBchainTx(bchainTx, height, spendingTxs, specificJSON, addresses)
} }
func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData { func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedInputData {
@ -144,8 +203,8 @@ func (w *Worker) getParsedEthereumInputData(data string) *bchain.EthereumParsedI
return eth.ParseInputData(signatures, data) return eth.ParseInputData(signatures, data)
} }
// GetTransactionFromBchainTx reads transaction data from txid // getTransactionFromBchainTx reads transaction data from txid
func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool) (*Tx, error) { func (w *Worker) getTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
var err error var err error
var ta *db.TxAddresses var ta *db.TxAddresses
var tokens []TokenTransfer var tokens []TokenTransfer
@ -199,6 +258,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.Warning("GetAddressesFromAddrDesc tx ", bchainVin.Txid, ", addrDesc ", vin.AddrDesc, ": ", err) glog.Warning("GetAddressesFromAddrDesc tx ", bchainVin.Txid, ", addrDesc ", vin.AddrDesc, ": ", err)
} }
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
continue continue
} }
return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid) return nil, errors.Annotatef(err, "txCache.GetTransaction %v", bchainVin.Txid)
@ -215,6 +275,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout) glog.Errorf("getAddressesFromVout error %v, vout %+v", err, vout)
} }
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
} else { } else {
if len(tas.Outputs) > int(vin.Vout) { if len(tas.Outputs) > int(vin.Vout) {
@ -225,6 +286,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i) glog.Errorf("output.Addresses error %v, tx %v, output %v", err, bchainVin.Txid, i)
} }
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
} }
if vin.ValueSat != nil { if vin.ValueSat != nil {
@ -239,6 +301,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
} }
vin.Addresses = bchainVin.Addresses vin.Addresses = bchainVin.Addresses
vin.IsAddress = true vin.IsAddress = true
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
} }
} }
@ -254,6 +317,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, bchainTx.Txid, bchainVout.N) glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, bchainTx.Txid, bchainVout.N)
} }
aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
if ta != nil { if ta != nil {
vout.Spent = ta.Outputs[i].Spent vout.Spent = ta.Outputs[i].Spent
if spendingTxs && vout.Spent { if spendingTxs && vout.Spent {
@ -276,7 +340,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.Errorf("GetTokenTransfersFromTx error %v, %v", err, bchainTx) glog.Errorf("GetTokenTransfersFromTx error %v, %v", err, bchainTx)
} }
tokens = w.getEthereumTokensTransfers(tokenTransfers) tokens = w.getEthereumTokensTransfers(tokenTransfers, addresses)
ethTxData := eth.GetEthereumTxData(bchainTx) ethTxData := eth.GetEthereumTxData(bchainTx)
var internalData *bchain.EthereumInternalData var internalData *bchain.EthereumInternalData
@ -314,7 +378,9 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
f := &internalData.Transfers[i] f := &internalData.Transfers[i]
t := &ethSpecific.InternalTransfers[i] t := &ethSpecific.InternalTransfers[i]
t.From = f.From t.From = f.From
aggregateAddress(addresses, t.From)
t.To = f.To t.To = f.To
aggregateAddress(addresses, t.To)
t.Type = f.Type t.Type = f.Type
t.Value = (*Amount)(&f.Value) t.Value = (*Amount)(&f.Value)
} }
@ -365,6 +431,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
var pValInSat *big.Int var pValInSat *big.Int
var tokens []TokenTransfer var tokens []TokenTransfer
var ethSpecific *EthereumSpecific var ethSpecific *EthereumSpecific
addresses := w.newAddressesMapForAliases()
vins := make([]Vin, len(mempoolTx.Vin)) vins := make([]Vin, len(mempoolTx.Vin))
rbf := false rbf := false
for i := range mempoolTx.Vin { for i := range mempoolTx.Vin {
@ -389,6 +456,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
if vin.ValueSat != nil { if vin.ValueSat != nil {
valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat)) valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
} }
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
} else if w.chainType == bchain.ChainEthereumType { } else if w.chainType == bchain.ChainEthereumType {
if len(bchainVin.Addresses) > 0 { if len(bchainVin.Addresses) > 0 {
@ -398,6 +466,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
} }
vin.Addresses = bchainVin.Addresses vin.Addresses = bchainVin.Addresses
vin.IsAddress = true vin.IsAddress = true
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
} }
} }
@ -413,6 +482,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
if err != nil { if err != nil {
glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N) glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N)
} }
aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
} }
if w.chainType == bchain.ChainBitcoinType { if w.chainType == bchain.ChainBitcoinType {
// for coinbase transactions valIn is 0 // for coinbase transactions valIn is 0
@ -425,7 +495,7 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
if len(mempoolTx.Vout) > 0 { if len(mempoolTx.Vout) > 0 {
valOutSat = mempoolTx.Vout[0].ValueSat valOutSat = mempoolTx.Vout[0].ValueSat
} }
tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers) tokens = w.getEthereumTokensTransfers(mempoolTx.TokenTransfers, addresses)
ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData) ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData)
ethSpecific = &EthereumSpecific{ ethSpecific = &EthereumSpecific{
GasLimit: ethTxData.GasLimit, GasLimit: ethTxData.GasLimit,
@ -450,11 +520,12 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
Vout: vouts, Vout: vouts,
TokenTransfers: tokens, TokenTransfers: tokens,
EthereumSpecific: ethSpecific, EthereumSpecific: ethSpecific,
AddressAliases: w.getAddressAliases(addresses),
} }
return r, nil return r, nil
} }
func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []TokenTransfer { func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, addresses map[string]struct{}) []TokenTransfer {
sort.Sort(transfers) sort.Sort(transfers)
tokens := make([]TokenTransfer, len(transfers)) tokens := make([]TokenTransfer, len(transfers))
for i := range transfers { for i := range transfers {
@ -482,6 +553,8 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers) []T
} else { } else {
value = (*Amount)(&t.Value) value = (*Amount)(&t.Value)
} }
aggregateAddress(addresses, t.From)
aggregateAddress(addresses, t.To)
tokens[i] = TokenTransfer{ tokens[i] = TokenTransfer{
Type: TokenTypeMap[t.Type], Type: TokenTypeMap[t.Type],
Token: t.Contract, Token: t.Contract,
@ -606,7 +679,7 @@ func GetUniqueTxids(txids []string) []string {
return ut[0:i] return ut[0:i]
} }
func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32) *Tx { func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockInfo, bestheight uint32, addresses map[string]struct{}) *Tx {
var err error var err error
var valInSat, valOutSat, feesSat big.Int var valInSat, valOutSat, feesSat big.Int
vins := make([]Vin, len(ta.Inputs)) vins := make([]Vin, len(ta.Inputs))
@ -620,6 +693,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
if err != nil { if err != nil {
glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai) glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai)
} }
aggregateAddresses(addresses, vin.Addresses, vin.IsAddress)
} }
vouts := make([]Vout, len(ta.Outputs)) vouts := make([]Vout, len(ta.Outputs))
for i := range ta.Outputs { for i := range ta.Outputs {
@ -633,6 +707,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn
glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao) glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao)
} }
vout.Spent = tao.Spent vout.Spent = tao.Spent
aggregateAddresses(addresses, vout.Addresses, vout.IsAddress)
} }
// for coinbase transactions valIn is 0 // for coinbase transactions valIn is 0
feesSat.Sub(&valInSat, &valOutSat) feesSat.Sub(&valInSat, &valOutSat)
@ -876,7 +951,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil
} }
func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo) (*Tx, error) { func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) {
var tx *Tx var tx *Tx
var err error var err error
// only ChainBitcoinType supports TxHistoryLight // only ChainBitcoinType supports TxHistoryLight
@ -888,9 +963,9 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail
if ta == nil { if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, get tx from backend // as fallback, get tx from backend
tx, err = w.GetTransaction(txid, false, false) tx, err = w.getTransaction(txid, false, false, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
} else { } else {
if blockInfo == nil { if blockInfo == nil {
@ -904,12 +979,12 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail
blockInfo = &db.BlockInfo{} blockInfo = &db.BlockInfo{}
} }
} }
tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight) tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight, addresses)
} }
} else { } else {
tx, err = w.GetTransaction(txid, false, false) tx, err = w.getTransaction(txid, false, false, addresses)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetTransaction %v", txid) return nil, errors.Annotatef(err, "getTransaction %v", txid)
} }
} }
return tx, nil return tx, nil
@ -1012,6 +1087,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
ba = &db.AddrBalance{} ba = &db.AddrBalance{}
page = 0 page = 0
} }
addresses := w.newAddressesMapForAliases()
// process mempool, only if toHeight is not specified // process mempool, only if toHeight is not specified
if filter.ToHeight == 0 && !filter.OnlyConfirmed { if filter.ToHeight == 0 && !filter.OnlyConfirmed {
txm, err = w.getAddressTxids(addrDesc, true, filter, maxInt) txm, err = w.getAddressTxids(addrDesc, true, filter, maxInt)
@ -1019,7 +1095,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc) return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
} }
for _, txid := range txm { for _, txid := range txm {
tx, err := w.GetTransaction(txid, false, true) tx, err := w.getTransaction(txid, false, true, addresses)
// mempool transaction may fail // mempool transaction may fail
if err != nil || tx == nil { if err != nil || tx == nil {
glog.Warning("GetTransaction in mempool: ", err) glog.Warning("GetTransaction in mempool: ", err)
@ -1070,7 +1146,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
if option == AccountDetailsTxidHistory { if option == AccountDetailsTxidHistory {
txids = append(txids, txid) txids = append(txids, txid)
} else { } else {
tx, err := w.txFromTxid(txid, bestheight, option, nil) tx, err := w.txFromTxid(txid, bestheight, option, nil, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1099,6 +1175,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
Tokens: tokens, Tokens: tokens,
Erc20Contract: erc20c, Erc20Contract: erc20c,
Nonce: nonce, Nonce: nonce,
AddressAliases: w.getAddressAliases(addresses),
} }
glog.Info("GetAddress ", address, ", ", time.Since(start)) glog.Info("GetAddress ", address, ", ", time.Since(start))
return r, nil return r, nil
@ -1786,8 +1863,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
pg, from, to, page := computePaging(txCount, page, txsOnPage) pg, from, to, page := computePaging(txCount, page, txsOnPage)
txs := make([]*Tx, to-from) txs := make([]*Tx, to-from)
txi := 0 txi := 0
addresses := w.newAddressesMapForAliases()
for i := from; i < to; i++ { for i := from; i < to; i++ {
txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi) txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1819,8 +1897,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
Txids: bi.Txids, Txids: bi.Txids,
Version: bi.Version, Version: bi.Version,
}, },
TxCount: txCount, TxCount: txCount,
Transactions: txs, Transactions: txs,
AddressAliases: w.getAddressAliases(addresses),
}, nil }, nil
} }
@ -1875,7 +1954,7 @@ func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Sig
glog.Info("ComputeFeeStats interrupted at height ", block) glog.Info("ComputeFeeStats interrupted at height ", block)
return db.ErrOperationInterrupted return db.ErrOperationInterrupted
default: default:
tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi) tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi, nil)
if err != nil { if err != nil {
return err return err
} }

View File

@ -437,6 +437,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
} }
filtered = true filtered = true
} }
addresses := w.newAddressesMapForAliases()
// process mempool, only if ToHeight is not specified // process mempool, only if ToHeight is not specified
if filter.ToHeight == 0 && !filter.OnlyConfirmed { if filter.ToHeight == 0 && !filter.OnlyConfirmed {
txmMap = make(map[string]*Tx) txmMap = make(map[string]*Tx)
@ -452,7 +453,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
// the same tx can have multiple addresses from the same xpub, get it from backend it only once // the same tx can have multiple addresses from the same xpub, get it from backend it only once
tx, foundTx := txmMap[txid.txid] tx, foundTx := txmMap[txid.txid]
if !foundTx { if !foundTx {
tx, err = w.GetTransaction(txid.txid, false, true) tx, err = w.getTransaction(txid.txid, false, true, addresses)
// mempool transaction may fail // mempool transaction may fail
if err != nil || tx == nil { if err != nil || tx == nil {
glog.Warning("GetTransaction in mempool: ", err) glog.Warning("GetTransaction in mempool: ", err)
@ -529,7 +530,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
if option == AccountDetailsTxidHistory { if option == AccountDetailsTxidHistory {
txids = append(txids, xpubTxid.txid) txids = append(txids, xpubTxid.txid)
} else { } else {
tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option, nil) tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option, nil, addresses)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -580,6 +581,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
UsedTokens: usedTokens, UsedTokens: usedTokens,
Tokens: tokens, Tokens: tokens,
XPubAddresses: xpubAddresses, XPubAddresses: xpubAddresses,
AddressAliases: w.getAddressAliases(addresses),
} }
glog.Info("GetXpubAddress ", xpub[:xpubLogPrefix], ", cache ", inCache, ", ", txCount, " txs, ", time.Since(start)) glog.Info("GetXpubAddress ", xpub[:xpubLogPrefix], ", cache ", inCache, ", ", txCount, " txs, ", time.Since(start))
return &addr, nil return &addr, nil

View File

@ -16,6 +16,7 @@ import (
type BaseParser struct { type BaseParser struct {
BlockAddressesToKeep int BlockAddressesToKeep int
AmountDecimalPoint int AmountDecimalPoint int
AddressAliases bool
} }
// ParseBlock parses raw block to our Block struct - currently not implemented // ParseBlock parses raw block to our Block struct - currently not implemented
@ -103,6 +104,11 @@ func (p *BaseParser) AmountDecimals() int {
return p.AmountDecimalPoint return p.AmountDecimalPoint
} }
// UseAddressAliases returns true if address aliases are enabled
func (p *BaseParser) UseAddressAliases() bool {
return p.AddressAliases
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct // ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
var tx Tx var tx Tx
@ -304,3 +310,8 @@ func (p *BaseParser) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor,
func (p *BaseParser) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) { func (p *BaseParser) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) {
return nil, errors.New("Not supported") return nil, errors.New("Not supported")
} }
// FormatAddressAlias makes possible to do coin specific formatting to an address alias
func (p *BaseParser) FormatAddressAlias(address string, name string) string {
return name
}

View File

@ -44,6 +44,7 @@ func NewBitcoinLikeParser(params *chaincfg.Params, c *Configuration) *BitcoinLik
BaseParser: &bchain.BaseParser{ BaseParser: &bchain.BaseParser{
BlockAddressesToKeep: c.BlockAddressesToKeep, BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8, AmountDecimalPoint: 8,
AddressAliases: c.AddressAliases,
}, },
Params: params, Params: params,
XPubMagic: c.XPubMagic, XPubMagic: c.XPubMagic,

View File

@ -43,6 +43,7 @@ type Configuration struct {
RPCUser string `json:"rpc_user"` RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"` RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"` RPCTimeout int `json:"rpc_timeout"`
AddressAliases bool `json:"address_aliases,omitempty"`
Parse bool `json:"parse"` Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"` MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"` Subversion string `json:"subversion"`

View File

@ -228,7 +228,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) {
} }
func Test_contractGetTransfersFromTx(t *testing.T) { func Test_contractGetTransfersFromTx(t *testing.T) {
p := NewEthereumParser(1) p := NewEthereumParser(1, false)
b1 := dbtestdata.GetTestEthereumTypeBlock1(p) b1 := dbtestdata.GetTestEthereumTypeBlock1(p)
b2 := dbtestdata.GetTestEthereumTypeBlock2(p) b2 := dbtestdata.GetTestEthereumTypeBlock2(p)
bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16)

View File

@ -28,10 +28,11 @@ type EthereumParser struct {
} }
// NewEthereumParser returns new EthereumParser instance // NewEthereumParser returns new EthereumParser instance
func NewEthereumParser(b int) *EthereumParser { func NewEthereumParser(b int, addressAliases bool) *EthereumParser {
return &EthereumParser{&bchain.BaseParser{ return &EthereumParser{&bchain.BaseParser{
BlockAddressesToKeep: b, BlockAddressesToKeep: b,
AmountDecimalPoint: EtherAmountDecimalPoint, AmountDecimalPoint: EtherAmountDecimalPoint,
AddressAliases: addressAliases,
}} }}
} }
@ -458,6 +459,11 @@ func (p *EthereumParser) EthereumTypeGetTokenTransfersFromTx(tx *bchain.Tx) (bch
return r, nil return r, nil
} }
// FormatAddressAlias adds .eth to a name alias
func (p *EthereumParser) FormatAddressAlias(address string, name string) string {
return name + ".eth"
}
// TxStatus is status of transaction // TxStatus is status of transaction
type TxStatus int type TxStatus int

View File

@ -54,7 +54,7 @@ func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser(1) p := NewEthereumParser(1, false)
got, err := p.GetAddrDescFromAddress(tt.args.address) got, err := p.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("EthParser.GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("EthParser.GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
@ -285,7 +285,7 @@ func TestEthereumParser_PackTx(t *testing.T) {
want: dbtestdata.EthTx1NoStatusPacked, want: dbtestdata.EthTx1NoStatusPacked,
}, },
} }
p := NewEthereumParser(1) p := NewEthereumParser(1, false)
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime) got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime)
@ -338,7 +338,7 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
want1: 4321000, want1: 4321000,
}, },
} }
p := NewEthereumParser(1) p := NewEthereumParser(1, false)
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
b, err := hex.DecodeString(tt.args.hex) b, err := hex.DecodeString(tt.args.hex)

View File

@ -41,6 +41,7 @@ type Configuration struct {
RPCURL string `json:"rpc_url"` RPCURL string `json:"rpc_url"`
RPCTimeout int `json:"rpc_timeout"` RPCTimeout int `json:"rpc_timeout"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"` BlockAddressesToKeep int `json:"block_addresses_to_keep"`
AddressAliases bool `json:"address_aliases,omitempty"`
MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"` MempoolTxTimeoutHours int `json:"mempoolTxTimeoutHours"`
QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"` QueryBackendOnMempoolResync bool `json:"queryBackendOnMempoolResync"`
ProcessInternalTransactions bool `json:"processInternalTransactions"` ProcessInternalTransactions bool `json:"processInternalTransactions"`
@ -97,7 +98,7 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
ProcessInternalTransactions = c.ProcessInternalTransactions ProcessInternalTransactions = c.ProcessInternalTransactions
// always create parser // always create parser
s.Parser = NewEthereumParser(c.BlockAddressesToKeep) s.Parser = NewEthereumParser(c.BlockAddressesToKeep, c.AddressAliases)
s.timeout = time.Duration(c.RPCTimeout) * time.Second s.timeout = time.Duration(c.RPCTimeout) * time.Second
// new blocks notifications handling // new blocks notifications handling
@ -648,6 +649,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
// error fetching internal data does not stop the block processing // error fetching internal data does not stop the block processing
var blockSpecificData *bchain.EthereumBlockSpecificData var blockSpecificData *bchain.EthereumBlockSpecificData
internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions) internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions)
// pass internalData error and ENS records in blockSpecificData to be stored
if err != nil || len(ens) > 0 { if err != nil || len(ens) > 0 {
blockSpecificData = &bchain.EthereumBlockSpecificData{} blockSpecificData = &bchain.EthereumBlockSpecificData{}
if err != nil { if err != nil {

View File

@ -305,6 +305,8 @@ type BlockChainParser interface {
KeepBlockAddresses() int KeepBlockAddresses() int
// AmountDecimals returns number of decimal places in coin amounts // AmountDecimals returns number of decimal places in coin amounts
AmountDecimals() int AmountDecimals() int
// UseAddressAliases returns true if address aliases are enabled
UseAddressAliases() bool
// MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent // MinimumCoinbaseConfirmations returns minimum number of confirmations a coinbase transaction must have before it can be spent
MinimumCoinbaseConfirmations() int MinimumCoinbaseConfirmations() int
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place // AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
@ -338,6 +340,8 @@ type BlockChainParser interface {
DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error) DeriveAddressDescriptorsFromTo(descriptor *XpubDescriptor, change uint32, fromIndex uint32, toIndex uint32) ([]AddressDescriptor, error)
// EthereumType specific // EthereumType specific
EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error) EthereumTypeGetTokenTransfersFromTx(tx *Tx) (TokenTransfers, error)
// AddressAlias
FormatAddressAlias(address string, name string) string
} }
// Mempool defines common interface to mempool // Mempool defines common interface to mempool

View File

@ -50,6 +50,7 @@
"mempool_sub_workers": 2, "mempool_sub_workers": 2,
"block_addresses_to_keep": 300, "block_addresses_to_keep": 300,
"additional_params": { "additional_params": {
"address_aliases": true,
"mempoolTxTimeoutHours": 48, "mempoolTxTimeoutHours": 48,
"processInternalTransactions": true, "processInternalTransactions": true,
"queryBackendOnMempoolResync": false, "queryBackendOnMempoolResync": false,

View File

@ -49,6 +49,7 @@
"mempool_sub_workers": 2, "mempool_sub_workers": 2,
"block_addresses_to_keep": 3000, "block_addresses_to_keep": 3000,
"additional_params": { "additional_params": {
"address_aliases": true,
"mempoolTxTimeoutHours": 12, "mempoolTxTimeoutHours": 12,
"processInternalTransactions": true, "processInternalTransactions": true,
"queryBackendOnMempoolResync": false, "queryBackendOnMempoolResync": false,

View File

@ -330,9 +330,9 @@ func (b *BulkConnect) connectBlockEthereumType(block *bchain.Block, storeBlockTx
glog.Info("rocksdb: height ", b.height, ", stored ", bac, " addresses, done in ", time.Since(start)) glog.Info("rocksdb: height ", b.height, ", stored ", bac, " addresses, done in ", time.Since(start))
} }
} else { } else {
// if there is InternalDataError, store it // if there are blockSpecificData, store them
blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData)
if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { if blockSpecificData != nil {
wb := gorocksdb.NewWriteBatch() wb := gorocksdb.NewWriteBatch()
defer wb.Destroy() defer wb.Destroy()
if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil { if err = b.d.storeBlockSpecificDataEthereumType(wb, block); err != nil {

View File

@ -10,6 +10,7 @@ import (
"path/filepath" "path/filepath"
"sort" "sort"
"strconv" "strconv"
"sync"
"time" "time"
"unsafe" "unsafe"
@ -90,6 +91,9 @@ const (
cfContracts cfContracts
cfFunctionSignatures cfFunctionSignatures
cfBlockInternalDataErrors cfBlockInternalDataErrors
// TODO move to common section
cfAddressAliases
) )
// common columns // common columns
@ -98,7 +102,7 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa
// type specific columns // type specific columns
var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"}
var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors"} var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts", "functionSignatures", "blockInternalDataErrors", "addressAliases"}
func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) {
// opts with bloom filter // opts with bloom filter
@ -1248,6 +1252,50 @@ func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, height uint32, bi *Block
return nil return nil
} }
// address alias support
var cachedAddressAliasRecords = make(map[string]string)
var cachedAddressAliasRecordsMux sync.Mutex
// InitAddressAliasRecords loads all records to cache
func (d *RocksDB) InitAddressAliasRecords() (int, error) {
count := 0
cachedAddressAliasRecordsMux.Lock()
defer cachedAddressAliasRecordsMux.Unlock()
it := d.db.NewIteratorCF(d.ro, d.cfh[cfAddressAliases])
defer it.Close()
for it.SeekToFirst(); it.Valid(); it.Next() {
address := string(it.Key().Data())
name := string(it.Value().Data())
if address != "" && name != "" {
cachedAddressAliasRecords[address] = d.chainParser.FormatAddressAlias(address, name)
count++
}
}
return count, nil
}
func (d *RocksDB) GetAddressAlias(address string) string {
cachedAddressAliasRecordsMux.Lock()
name := cachedAddressAliasRecords[address]
cachedAddressAliasRecordsMux.Unlock()
return name
}
func (d *RocksDB) storeAddressAliasRecords(wb *gorocksdb.WriteBatch, records []bchain.AddressAliasRecord) error {
if d.chainParser.UseAddressAliases() {
for i := range records {
r := &records[i]
if len(r.Name) > 0 {
wb.PutCF(d.cfh[cfAddressAliases], []byte(r.Address), []byte(r.Name))
cachedAddressAliasRecordsMux.Lock()
cachedAddressAliasRecords[r.Address] = d.chainParser.FormatAddressAlias(r.Address, r.Name)
cachedAddressAliasRecordsMux.Unlock()
}
}
}
return nil
}
// Disconnect blocks // Disconnect blocks
func (d *RocksDB) disconnectTxAddressesInputs(wb *gorocksdb.WriteBatch, btxID []byte, inputs []outpoint, txa *TxAddresses, txAddressesToUpdate map[string]*TxAddresses, func (d *RocksDB) disconnectTxAddressesInputs(wb *gorocksdb.WriteBatch, btxID []byte, inputs []outpoint, txa *TxAddresses, txAddressesToUpdate map[string]*TxAddresses,
@ -1642,6 +1690,15 @@ func (d *RocksDB) LoadInternalState(rpcCoin string) (*common.InternalState, erro
var t time.Time var t time.Time
is.LastMempoolSync = t is.LastMempoolSync = t
is.SyncMode = false is.SyncMode = false
if d.chainParser.UseAddressAliases() {
recordsCount, err := d.InitAddressAliasRecords()
if err != nil {
return nil, err
}
glog.Infof("loaded %d address alias records", recordsCount)
}
return is, nil return is, nil
} }

View File

@ -761,10 +761,17 @@ func (d *RocksDB) storeBlockInternalDataErrorEthereumType(wb *gorocksdb.WriteBat
func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block) error { func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, block *bchain.Block) error {
blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData) blockSpecificData, _ := block.CoinSpecificData.(*bchain.EthereumBlockSpecificData)
if blockSpecificData != nil && blockSpecificData.InternalDataError != "" { if blockSpecificData != nil {
glog.Info("storeBlockSpecificDataEthereumType ", block.Height, ": ", blockSpecificData.InternalDataError) if blockSpecificData.InternalDataError != "" {
if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil { glog.Info("storeBlockSpecificDataEthereumType ", block.Height, ": ", blockSpecificData.InternalDataError)
return err if err := d.storeBlockInternalDataErrorEthereumType(wb, block, blockSpecificData.InternalDataError); err != nil {
return err
}
}
if len(blockSpecificData.AddressAliasRecords) > 0 {
if err := d.storeAddressAliasRecords(wb, blockSpecificData.AddressAliasRecords); err != nil {
return err
}
} }
} }
return nil return nil

View File

@ -21,7 +21,7 @@ type testEthereumParser struct {
} }
func ethereumTestnetParser() *eth.EthereumParser { func ethereumTestnetParser() *eth.EthereumParser {
return eth.NewEthereumParser(1) return eth.NewEthereumParser(1, true)
} }
func bigintFromStringToHex(s string) string { func bigintFromStringToHex(s string) string {
@ -267,6 +267,25 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa
} }
} }
var addressAliases []keyPair
addressAliases = []keyPair{
{
hex.EncodeToString([]byte(dbtestdata.EthAddr7bEIP55)),
hex.EncodeToString([]byte("address7b")),
nil,
},
{
hex.EncodeToString([]byte(dbtestdata.EthAddr20EIP55)),
hex.EncodeToString([]byte("address20")),
nil,
},
}
if err := checkColumn(d, cfAddressAliases, addressAliases); err != nil {
{
t.Fatal(err)
}
}
var internalDataError []keyPair var internalDataError []keyPair
if wantBlockInternalDataError { if wantBlockInternalDataError {
internalDataError = []keyPair{ internalDataError = []keyPair{
@ -282,6 +301,7 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa
t.Fatal(err) t.Fatal(err)
} }
} }
} }
func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData { func formatInternalData(in *bchain.EthereumInternalData) *bchain.EthereumInternalData {
@ -359,9 +379,8 @@ func TestRocksDB_Index_EthereumType(t *testing.T) {
t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes)) t.Fatal("Expecting is.BlockTimes 1, got ", len(d.is.BlockTimes))
} }
// connect 2nd block, simulate InternalDataError // connect 2nd block, simulate InternalDataError and AddressAlias
block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser)
block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"}
if err := d.ConnectBlock(block2); err != nil { if err := d.ConnectBlock(block2); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -544,7 +563,6 @@ func Test_BulkConnect_EthereumType(t *testing.T) {
// connect 2nd block, simulate InternalDataError // connect 2nd block, simulate InternalDataError
block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser) block2 := dbtestdata.GetTestEthereumTypeBlock2(d.chainParser)
block2.CoinSpecificData = &bchain.EthereumBlockSpecificData{InternalDataError: "test error"}
if err := bc.ConnectBlock(block2, true); err != nil { if err := bc.ConnectBlock(block2, true); err != nil {
t.Fatal(err) t.Fatal(err)
} }

File diff suppressed because one or more lines are too long

View File

@ -13,9 +13,11 @@ const (
EthAddr3e = "3e3a3d69dc66ba10737f531ed088954a9ec89d97" EthAddr3e = "3e3a3d69dc66ba10737f531ed088954a9ec89d97"
EthAddr55 = "555ee11fbddc0e49a9bab358a8941ad95ffdb48f" EthAddr55 = "555ee11fbddc0e49a9bab358a8941ad95ffdb48f"
EthAddr20 = "20cd153de35d469ba46127a0c8f18626b59a256a" EthAddr20 = "20cd153de35d469ba46127a0c8f18626b59a256a"
EthAddr20EIP55 = "0x20cD153de35D469BA46127A0C8F18626b59a256A"
EthAddr9f = "9f4981531fda132e83c44680787dfa7ee31e4f8d" EthAddr9f = "9f4981531fda132e83c44680787dfa7ee31e4f8d"
EthAddr4b = "4bda106325c335df99eab7fe363cac8a0ba2a24d" EthAddr4b = "4bda106325c335df99eab7fe363cac8a0ba2a24d"
EthAddr7b = "7b62eb7fe80350dc7ec945c0b73242cb9877fb1b" EthAddr7b = "7b62eb7fe80350dc7ec945c0b73242cb9877fb1b"
EthAddr7bEIP55 = "0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b"
EthAddr83 = "837e3f699d85a4b0b99894567e9233dfb1dcb081" EthAddr83 = "837e3f699d85a4b0b99894567e9233dfb1dcb081"
EthAddrA3 = "a3950b823cb063dd9afc0d27f35008b805b3ed53" EthAddrA3 = "a3950b823cb063dd9afc0d27f35008b805b3ed53"
EthAddr5d = "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e" EthAddr5d = "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e"
@ -126,6 +128,20 @@ var EthTx4InternalData = &bchain.EthereumInternalData{
}, },
} }
var Block2SpecificData = &bchain.EthereumBlockSpecificData{
InternalDataError: "test error",
AddressAliasRecords: []bchain.AddressAliasRecord{
{
Address: EthAddr7bEIP55,
Name: "address7b",
},
{
Address: EthAddr20EIP55,
Name: "address20",
},
},
}
type packedAndInternal struct { type packedAndInternal struct {
packed string packed string
internal *bchain.EthereumInternalData internal *bchain.EthereumInternalData
@ -194,5 +210,6 @@ func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
}, { }, {
packed: EthTx8Packed, packed: EthTx8Packed,
}}, parser), }}, parser),
CoinSpecificData: Block2SpecificData,
} }
} }