Filter address transactions by input/output or token
parent
a08f568353
commit
9a04c862d6
23
api/types.go
23
api/types.go
|
@ -86,11 +86,12 @@ type Vout struct {
|
|||
|
||||
// Erc20Token contains info about ERC20 token held by an address
|
||||
type Erc20Token struct {
|
||||
Contract string `json:"contract"`
|
||||
Txs int `json:"txs"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Balance string `json:"balance"`
|
||||
Contract string `json:"contract"`
|
||||
Txs int `json:"txs"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Balance string `json:"balance"`
|
||||
ContractIndex string `json:"-"`
|
||||
}
|
||||
|
||||
// Erc20Transfer contains info about ERC20 transfer done in a transaction
|
||||
|
@ -136,6 +137,15 @@ type Paging struct {
|
|||
ItemsOnPage int `json:"itemsOnPage"`
|
||||
}
|
||||
|
||||
// AddressFilterNone disables filtering of transactions
|
||||
const AddressFilterNone = -1
|
||||
|
||||
// AddressFilterInputs specifies that only txs where the address is as input are returned
|
||||
const AddressFilterInputs = -2
|
||||
|
||||
// AddressFilterOutputs specifies that only txs where the address is as output are returned
|
||||
const AddressFilterOutputs = -3
|
||||
|
||||
// Address holds information about address and its transactions
|
||||
type Address struct {
|
||||
Paging
|
||||
|
@ -150,12 +160,13 @@ type Address struct {
|
|||
Txids []string `json:"transactions,omitempty"`
|
||||
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
|
||||
Erc20Tokens []Erc20Token `json:"erc20tokens,omitempty"`
|
||||
Filter string `json:"-"`
|
||||
}
|
||||
|
||||
// AddressUtxo holds information about address and its transactions
|
||||
type AddressUtxo struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
Vout int32 `json:"vout"`
|
||||
Amount string `json:"amount"`
|
||||
AmountSat big.Int `json:"satoshis"`
|
||||
Height int `json:"height,omitempty"`
|
||||
|
|
|
@ -51,7 +51,7 @@ func (w *Worker) getAddressesFromVout(vout *bchain.Vout) (bchain.AddressDescript
|
|||
// setSpendingTxToVout is helper function, that finds transaction that spent given output and sets it to the output
|
||||
// there is no direct index for the operation, it must be found using addresses -> txaddresses -> tx
|
||||
func (w *Worker) setSpendingTxToVout(vout *Vout, txid string, height uint32) error {
|
||||
err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index uint32, isOutput bool) error {
|
||||
err := w.db.GetAddrDescTransactions(vout.ScriptPubKey.AddrDesc, height, ^uint32(0), func(t string, index int32, isOutput bool) error {
|
||||
if isOutput == false {
|
||||
tsp, err := w.db.GetTxAddresses(t)
|
||||
if err != nil {
|
||||
|
@ -290,24 +290,37 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool) ([]string, error) {
|
||||
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter int) ([]string, error) {
|
||||
var err error
|
||||
txids := make([]string, 0, 4)
|
||||
if !mempool {
|
||||
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error {
|
||||
addFilteredTxid := func(txid string, vout int32, isOutput bool) error {
|
||||
if filter == AddressFilterNone ||
|
||||
(filter == AddressFilterInputs && !isOutput) ||
|
||||
(filter == AddressFilterOutputs && isOutput) ||
|
||||
(vout == int32(filter)) {
|
||||
txids = append(txids, txid)
|
||||
// glog.Info(txid, " ", vout, " ", isOutput)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if mempool {
|
||||
o, err := w.chain.GetMempoolTransactionsForAddrDesc(addrDesc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range o {
|
||||
vout := m.Vout
|
||||
isOutput := true
|
||||
if vout < 0 {
|
||||
isOutput = false
|
||||
vout = ^vout
|
||||
}
|
||||
addFilteredTxid(m.Txid, vout, isOutput)
|
||||
}
|
||||
} else {
|
||||
m, err := w.chain.GetMempoolTransactionsForAddrDesc(addrDesc)
|
||||
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), addFilteredTxid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txids = append(txids, m...)
|
||||
}
|
||||
return txids, nil
|
||||
}
|
||||
|
@ -463,11 +476,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||
return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
|
||||
}
|
||||
erc20t[i] = Erc20Token{
|
||||
Balance: bchain.AmountToDecimalString(b, ci.Decimals),
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
Txs: int(c.Txs),
|
||||
Balance: bchain.AmountToDecimalString(b, ci.Decimals),
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
Txs: int(c.Txs),
|
||||
ContractIndex: strconv.Itoa(i + 1),
|
||||
}
|
||||
}
|
||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||
|
@ -480,7 +494,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||
}
|
||||
|
||||
// GetAddress computes address value and gets transactions for given address
|
||||
func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetAddressOption) (*Address, error) {
|
||||
func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetAddressOption, filter int) (*Address, error) {
|
||||
start := time.Now()
|
||||
page--
|
||||
if page < 0 {
|
||||
|
@ -519,7 +533,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
if len(addresses) == 1 {
|
||||
address = addresses[0]
|
||||
}
|
||||
txc, err := w.getAddressTxids(addrDesc, false)
|
||||
txc, err := w.getAddressTxids(addrDesc, false, filter)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
|
||||
}
|
||||
|
@ -530,7 +544,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
ba = &db.AddrBalance{}
|
||||
page = 0
|
||||
}
|
||||
txm, err = w.getAddressTxids(addrDesc, true)
|
||||
txm, err = w.getAddressTxids(addrDesc, true, filter)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
|
||||
}
|
||||
|
@ -538,7 +552,11 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
// check if the address exist
|
||||
if len(txc)+len(txm) == 0 || option == ExistOnly {
|
||||
return &Address{
|
||||
AddrStr: address,
|
||||
AddrStr: address,
|
||||
Balance: w.chainParser.AmountToDecimalString(&ba.BalanceSat),
|
||||
TotalReceived: w.chainParser.AmountToDecimalString(ba.ReceivedSat()),
|
||||
TotalSent: w.chainParser.AmountToDecimalString(&ba.SentSat),
|
||||
TxApperances: int(ba.Txs),
|
||||
}, nil
|
||||
}
|
||||
bestheight, _, err := w.db.GetBestBlock()
|
||||
|
@ -574,7 +592,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
}
|
||||
}
|
||||
}
|
||||
if len(txc) != int(ba.Txs) && w.chainType == bchain.ChainBitcoinType {
|
||||
if len(txc) != int(ba.Txs) && w.chainType == bchain.ChainBitcoinType && filter == AddressFilterNone {
|
||||
glog.Warning("DB inconsistency for address ", address, ": number of txs from column addresses ", len(txc), ", from addressBalance ", ba.Txs)
|
||||
}
|
||||
for i := from; i < to; i++ {
|
||||
|
@ -620,7 +638,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||
Balance: w.chainParser.AmountToDecimalString(&ba.BalanceSat),
|
||||
TotalReceived: w.chainParser.AmountToDecimalString(ba.ReceivedSat()),
|
||||
TotalSent: w.chainParser.AmountToDecimalString(&ba.SentSat),
|
||||
TxApperances: len(txc),
|
||||
TxApperances: int(ba.Txs),
|
||||
UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat),
|
||||
UnconfirmedTxApperances: len(txm),
|
||||
Transactions: txs,
|
||||
|
@ -643,7 +661,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
r := make([]AddressUtxo, 0, 8)
|
||||
if !onlyConfirmed {
|
||||
// get utxo from mempool
|
||||
txm, err := w.getAddressTxids(addrDesc, true)
|
||||
txm, err := w.getAddressTxids(addrDesc, true, AddressFilterNone)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "getAddressTxids %v true", address)
|
||||
}
|
||||
|
@ -675,7 +693,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
if !e {
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: bchainTx.Txid,
|
||||
Vout: uint32(i),
|
||||
Vout: int32(i),
|
||||
AmountSat: vout.ValueSat,
|
||||
Amount: w.chainParser.AmountToDecimalString(&vout.ValueSat),
|
||||
})
|
||||
|
@ -693,14 +711,10 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
var checksum big.Int
|
||||
// ba can be nil if the address is only in mempool!
|
||||
if ba != nil && ba.BalanceSat.Uint64() > 0 {
|
||||
type outpoint struct {
|
||||
txid string
|
||||
vout uint32
|
||||
}
|
||||
outpoints := make([]outpoint, 0, 8)
|
||||
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout uint32, isOutput bool) error {
|
||||
outpoints := make([]bchain.Outpoint, 0, 8)
|
||||
err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, vout int32, isOutput bool) error {
|
||||
if isOutput {
|
||||
outpoints = append(outpoints, outpoint{txid, vout})
|
||||
outpoints = append(outpoints, bchain.Outpoint{txid, vout})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
@ -717,27 +731,27 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt
|
|||
bestheight := int(b)
|
||||
for i := len(outpoints) - 1; i >= 0 && checksum.Int64() > 0; i-- {
|
||||
o := outpoints[i]
|
||||
if lastTxid != o.txid {
|
||||
ta, err = w.db.GetTxAddresses(o.txid)
|
||||
if lastTxid != o.Txid {
|
||||
ta, err = w.db.GetTxAddresses(o.Txid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lastTxid = o.txid
|
||||
lastTxid = o.Txid
|
||||
}
|
||||
if ta == nil {
|
||||
glog.Warning("DB inconsistency: tx ", o.txid, ": not found in txAddresses")
|
||||
glog.Warning("DB inconsistency: tx ", o.Txid, ": not found in txAddresses")
|
||||
} else {
|
||||
if len(ta.Outputs) <= int(o.vout) {
|
||||
glog.Warning("DB inconsistency: txAddresses ", o.txid, " does not have enough outputs")
|
||||
if len(ta.Outputs) <= int(o.Vout) {
|
||||
glog.Warning("DB inconsistency: txAddresses ", o.Txid, " does not have enough outputs")
|
||||
} else {
|
||||
if !ta.Outputs[o.vout].Spent {
|
||||
v := ta.Outputs[o.vout].ValueSat
|
||||
if !ta.Outputs[o.Vout].Spent {
|
||||
v := ta.Outputs[o.Vout].ValueSat
|
||||
// report only outpoints that are not spent in mempool
|
||||
_, e := spentInMempool[o.txid+strconv.Itoa(int(o.vout))]
|
||||
_, e := spentInMempool[o.Txid+strconv.Itoa(int(o.Vout))]
|
||||
if !e {
|
||||
r = append(r, AddressUtxo{
|
||||
Txid: o.txid,
|
||||
Vout: o.vout,
|
||||
Txid: o.Txid,
|
||||
Vout: o.Vout,
|
||||
AmountSat: v,
|
||||
Amount: w.chainParser.AmountToDecimalString(&v),
|
||||
Height: int(ta.Height),
|
||||
|
|
|
@ -221,12 +221,12 @@ func (c *blockChainWithMetrics) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc
|
|||
return count, err
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactions(address string) (v []string, err error) {
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactions(address string) (v []bchain.Outpoint, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactions", s, err) }(time.Now())
|
||||
return c.b.GetMempoolTransactions(address)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) (v []string, err error) {
|
||||
func (c *blockChainWithMetrics) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) (v []bchain.Outpoint, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetMempoolTransactionsForAddrDesc", s, err) }(time.Now())
|
||||
return c.b.GetMempoolTransactionsForAddrDesc(addrDesc)
|
||||
}
|
||||
|
|
|
@ -709,12 +709,12 @@ func (b *BitcoinRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, err
|
|||
}
|
||||
|
||||
// GetMempoolTransactions returns slice of mempool transactions for given address
|
||||
func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) {
|
||||
func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]bchain.Outpoint, error) {
|
||||
return b.Mempool.GetTransactions(address)
|
||||
}
|
||||
|
||||
// GetMempoolTransactionsForAddrDesc returns slice of mempool transactions for given address descriptor
|
||||
func (b *BitcoinRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, error) {
|
||||
func (b *BitcoinRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]bchain.Outpoint, error) {
|
||||
return b.Mempool.GetAddrDescTransactions(addrDesc)
|
||||
}
|
||||
|
||||
|
|
|
@ -673,12 +673,12 @@ func (b *EthereumRPC) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (int, er
|
|||
}
|
||||
|
||||
// GetMempoolTransactions returns slice of mempool transactions for given address
|
||||
func (b *EthereumRPC) GetMempoolTransactions(address string) ([]string, error) {
|
||||
func (b *EthereumRPC) GetMempoolTransactions(address string) ([]bchain.Outpoint, error) {
|
||||
return b.Mempool.GetTransactions(address)
|
||||
}
|
||||
|
||||
// GetMempoolTransactionsForAddrDesc returns slice of mempool transactions for given address descriptor
|
||||
func (b *EthereumRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, error) {
|
||||
func (b *EthereumRPC) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) ([]bchain.Outpoint, error) {
|
||||
return b.Mempool.GetAddrDescTransactions(addrDesc)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,17 +7,11 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// addrIndex and outpoint are used also in non utxo mempool
|
||||
type addrIndex struct {
|
||||
addrDesc string
|
||||
n int32
|
||||
}
|
||||
|
||||
type outpoint struct {
|
||||
txid string
|
||||
vout int32
|
||||
}
|
||||
|
||||
type txidio struct {
|
||||
txid string
|
||||
io []addrIndex
|
||||
|
@ -28,7 +22,7 @@ type MempoolBitcoinType struct {
|
|||
chain BlockChain
|
||||
mux sync.Mutex
|
||||
txToInputOutput map[string][]addrIndex
|
||||
addrDescToTx map[string][]outpoint
|
||||
addrDescToTx map[string][]Outpoint
|
||||
chanTxid chan string
|
||||
chanAddrIndex chan txidio
|
||||
onNewTxAddr OnNewTxAddrFunc
|
||||
|
@ -44,7 +38,7 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int) *Mempo
|
|||
}
|
||||
for i := 0; i < workers; i++ {
|
||||
go func(i int) {
|
||||
chanInput := make(chan outpoint, 1)
|
||||
chanInput := make(chan Outpoint, 1)
|
||||
chanResult := make(chan *addrIndex, 1)
|
||||
for j := 0; j < subworkers; j++ {
|
||||
go func(j int) {
|
||||
|
@ -68,7 +62,7 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int) *Mempo
|
|||
}
|
||||
|
||||
// GetTransactions returns slice of mempool transactions for given address
|
||||
func (m *MempoolBitcoinType) GetTransactions(address string) ([]string, error) {
|
||||
func (m *MempoolBitcoinType) GetTransactions(address string) ([]Outpoint, error) {
|
||||
parser := m.chain.GetChainParser()
|
||||
addrDesc, err := parser.GetAddrDescFromAddress(address)
|
||||
if err != nil {
|
||||
|
@ -78,44 +72,39 @@ func (m *MempoolBitcoinType) GetTransactions(address string) ([]string, error) {
|
|||
}
|
||||
|
||||
// GetAddrDescTransactions returns slice of mempool transactions for given address descriptor
|
||||
func (m *MempoolBitcoinType) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]string, error) {
|
||||
func (m *MempoolBitcoinType) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]Outpoint, error) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
outpoints := m.addrDescToTx[string(addrDesc)]
|
||||
txs := make([]string, 0, len(outpoints))
|
||||
for _, o := range outpoints {
|
||||
txs = append(txs, o.txid)
|
||||
}
|
||||
return txs, nil
|
||||
return append([]Outpoint(nil), m.addrDescToTx[string(addrDesc)]...), nil
|
||||
}
|
||||
|
||||
func (m *MempoolBitcoinType) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
|
||||
func (m *MempoolBitcoinType) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]Outpoint) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
m.txToInputOutput = newTxToInputOutput
|
||||
m.addrDescToTx = newAddrDescToTx
|
||||
}
|
||||
|
||||
func (m *MempoolBitcoinType) getInputAddress(input outpoint) *addrIndex {
|
||||
itx, err := m.chain.GetTransactionForMempool(input.txid)
|
||||
func (m *MempoolBitcoinType) getInputAddress(input Outpoint) *addrIndex {
|
||||
itx, err := m.chain.GetTransactionForMempool(input.Txid)
|
||||
if err != nil {
|
||||
glog.Error("cannot get transaction ", input.txid, ": ", err)
|
||||
glog.Error("cannot get transaction ", input.Txid, ": ", err)
|
||||
return nil
|
||||
}
|
||||
if int(input.vout) >= len(itx.Vout) {
|
||||
glog.Error("Vout len in transaction ", input.txid, " ", len(itx.Vout), " input.Vout=", input.vout)
|
||||
if int(input.Vout) >= len(itx.Vout) {
|
||||
glog.Error("Vout len in transaction ", input.Txid, " ", len(itx.Vout), " input.Vout=", input.Vout)
|
||||
return nil
|
||||
}
|
||||
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.vout])
|
||||
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.Vout])
|
||||
if err != nil {
|
||||
glog.Error("error in addrDesc in ", input.txid, " ", input.vout, ": ", err)
|
||||
glog.Error("error in addrDesc in ", input.Txid, " ", input.Vout, ": ", err)
|
||||
return nil
|
||||
}
|
||||
return &addrIndex{string(addrDesc), ^input.vout}
|
||||
return &addrIndex{string(addrDesc), ^input.Vout}
|
||||
|
||||
}
|
||||
|
||||
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan outpoint, chanResult chan *addrIndex) ([]addrIndex, bool) {
|
||||
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, chanResult chan *addrIndex) ([]addrIndex, bool) {
|
||||
tx, err := m.chain.GetTransactionForMempool(txid)
|
||||
if err != nil {
|
||||
glog.Error("cannot get transaction ", txid, ": ", err)
|
||||
|
@ -141,7 +130,7 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan outpoint, ch
|
|||
if input.Coinbase != "" {
|
||||
continue
|
||||
}
|
||||
o := outpoint{input.Txid, int32(input.Vout)}
|
||||
o := Outpoint{input.Txid, int32(input.Vout)}
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
|
@ -181,13 +170,13 @@ func (m *MempoolBitcoinType) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
|
|||
glog.V(2).Info("mempool: resync ", len(txs), " txs")
|
||||
// allocate slightly larger capacity of the maps
|
||||
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
|
||||
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
|
||||
newAddrDescToTx := make(map[string][]Outpoint, len(m.addrDescToTx)+5)
|
||||
dispatched := 0
|
||||
onNewData := func(txid string, io []addrIndex) {
|
||||
if len(io) > 0 {
|
||||
newTxToInputOutput[txid] = io
|
||||
for _, si := range io {
|
||||
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
|
||||
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], Outpoint{txid, si.n})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ type MempoolEthereumType struct {
|
|||
chain BlockChain
|
||||
mux sync.Mutex
|
||||
txToInputOutput map[string][]addrIndex
|
||||
addrDescToTx map[string][]outpoint
|
||||
addrDescToTx map[string][]Outpoint
|
||||
}
|
||||
|
||||
// NewMempoolEthereumType creates new mempool handler.
|
||||
|
@ -21,7 +21,7 @@ func NewMempoolEthereumType(chain BlockChain) *MempoolEthereumType {
|
|||
}
|
||||
|
||||
// GetTransactions returns slice of mempool transactions for given address
|
||||
func (m *MempoolEthereumType) GetTransactions(address string) ([]string, error) {
|
||||
func (m *MempoolEthereumType) GetTransactions(address string) ([]Outpoint, error) {
|
||||
parser := m.chain.GetChainParser()
|
||||
addrDesc, err := parser.GetAddrDescFromAddress(address)
|
||||
if err != nil {
|
||||
|
@ -31,18 +31,13 @@ func (m *MempoolEthereumType) GetTransactions(address string) ([]string, error)
|
|||
}
|
||||
|
||||
// GetAddrDescTransactions returns slice of mempool transactions for given address descriptor
|
||||
func (m *MempoolEthereumType) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]string, error) {
|
||||
func (m *MempoolEthereumType) GetAddrDescTransactions(addrDesc AddressDescriptor) ([]Outpoint, error) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
outpoints := m.addrDescToTx[string(addrDesc)]
|
||||
txs := make([]string, 0, len(outpoints))
|
||||
for _, o := range outpoints {
|
||||
txs = append(txs, o.txid)
|
||||
}
|
||||
return txs, nil
|
||||
return append([]Outpoint(nil), m.addrDescToTx[string(addrDesc)]...), nil
|
||||
}
|
||||
|
||||
func (m *MempoolEthereumType) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]outpoint) {
|
||||
func (m *MempoolEthereumType) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrDescToTx map[string][]Outpoint) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
m.txToInputOutput = newTxToInputOutput
|
||||
|
@ -62,7 +57,7 @@ func (m *MempoolEthereumType) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
|
|||
parser := m.chain.GetChainParser()
|
||||
// allocate slightly larger capacity of the maps
|
||||
newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5)
|
||||
newAddrDescToTx := make(map[string][]outpoint, len(m.addrDescToTx)+5)
|
||||
newAddrDescToTx := make(map[string][]Outpoint, len(m.addrDescToTx)+5)
|
||||
for _, txid := range txs {
|
||||
io, exists := m.txToInputOutput[txid]
|
||||
if !exists {
|
||||
|
@ -105,7 +100,7 @@ func (m *MempoolEthereumType) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
|
|||
}
|
||||
newTxToInputOutput[txid] = io
|
||||
for _, si := range io {
|
||||
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], outpoint{txid, si.n})
|
||||
newAddrDescToTx[si.addrDesc] = append(newAddrDescToTx[si.addrDesc], Outpoint{txid, si.n})
|
||||
}
|
||||
}
|
||||
m.updateMappings(newTxToInputOutput, newAddrDescToTx)
|
||||
|
|
|
@ -33,6 +33,12 @@ var (
|
|||
ErrTxidMissing = errors.New("Txid missing")
|
||||
)
|
||||
|
||||
// Outpoint is txid together with output (or input) index
|
||||
type Outpoint struct {
|
||||
Txid string
|
||||
Vout int32
|
||||
}
|
||||
|
||||
// ScriptSig contains data about input script
|
||||
type ScriptSig struct {
|
||||
// Asm string `json:"asm"`
|
||||
|
@ -202,8 +208,8 @@ type BlockChain interface {
|
|||
SendRawTransaction(tx string) (string, error)
|
||||
// mempool
|
||||
ResyncMempool(onNewTxAddr OnNewTxAddrFunc) (int, error)
|
||||
GetMempoolTransactions(address string) ([]string, error)
|
||||
GetMempoolTransactionsForAddrDesc(addrDesc AddressDescriptor) ([]string, error)
|
||||
GetMempoolTransactions(address string) ([]Outpoint, error)
|
||||
GetMempoolTransactionsForAddrDesc(addrDesc AddressDescriptor) ([]Outpoint, error)
|
||||
GetMempoolEntry(txid string) (*MempoolEntry, error)
|
||||
// parser
|
||||
GetChainParser() BlockChainParser
|
||||
|
|
|
@ -545,7 +545,7 @@ func waitForSignalAndShutdown(internal *server.InternalServer, public *server.Pu
|
|||
}
|
||||
}
|
||||
|
||||
func printResult(txid string, vout uint32, isOutput bool) error {
|
||||
func printResult(txid string, vout int32, isOutput bool) error {
|
||||
glog.Info(txid, vout, isOutput)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ func (e *StopIteration) Error() string {
|
|||
|
||||
// GetTransactions finds all input/output transactions for address
|
||||
// Transaction are passed to callback function.
|
||||
func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) {
|
||||
func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string, vout int32, isOutput bool) error) (err error) {
|
||||
if glog.V(1) {
|
||||
glog.Infof("rocksdb: address get %s %d-%d ", address, lower, higher)
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, f
|
|||
|
||||
// GetAddrDescTransactions finds all input/output transactions for address descriptor
|
||||
// Transaction are passed to callback function.
|
||||
func (d *RocksDB) GetAddrDescTransactions(addrDesc bchain.AddressDescriptor, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) {
|
||||
func (d *RocksDB) GetAddrDescTransactions(addrDesc bchain.AddressDescriptor, lower uint32, higher uint32, fn func(txid string, vout int32, isOutput bool) error) (err error) {
|
||||
kstart := packAddressKey(addrDesc, lower)
|
||||
kstop := packAddressKey(addrDesc, higher)
|
||||
|
||||
|
@ -242,13 +242,13 @@ func (d *RocksDB) GetAddrDescTransactions(addrDesc bchain.AddressDescriptor, low
|
|||
glog.Infof("rocksdb: output %s: %s", hex.EncodeToString(key), hex.EncodeToString(val))
|
||||
}
|
||||
for _, o := range outpoints {
|
||||
var vout uint32
|
||||
var vout int32
|
||||
var isOutput bool
|
||||
if o.index < 0 {
|
||||
vout = uint32(^o.index)
|
||||
vout = int32(^o.index)
|
||||
isOutput = false
|
||||
} else {
|
||||
vout = uint32(o.index)
|
||||
vout = int32(o.index)
|
||||
isOutput = true
|
||||
}
|
||||
tx, err := d.chainParser.UnpackTxid(o.btxID)
|
||||
|
|
|
@ -371,13 +371,13 @@ func verifyAfterBitcoinTypeBlock2(t *testing.T, d *RocksDB) {
|
|||
|
||||
type txidVoutOutput struct {
|
||||
txid string
|
||||
vout uint32
|
||||
vout int32
|
||||
isOutput bool
|
||||
}
|
||||
|
||||
func verifyGetTransactions(t *testing.T, d *RocksDB, addr string, low, high uint32, wantTxids []txidVoutOutput, wantErr error) {
|
||||
gotTxids := make([]txidVoutOutput, 0)
|
||||
addToTxids := func(txid string, vout uint32, isOutput bool) error {
|
||||
addToTxids := func(txid string, vout int32, isOutput bool) error {
|
||||
gotTxids = append(gotTxids, txidVoutOutput{txid, vout, isOutput})
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -349,6 +349,7 @@ type TemplateData struct {
|
|||
PrevPage int
|
||||
NextPage int
|
||||
PagingRange []int
|
||||
PageParams template.URL
|
||||
TOSLink string
|
||||
SendTxHex string
|
||||
Status string
|
||||
|
@ -442,6 +443,8 @@ func (s *PublicServer) explorerSpendingTx(w http.ResponseWriter, r *http.Request
|
|||
|
||||
func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) {
|
||||
var address *api.Address
|
||||
var filter string
|
||||
var fn = api.AddressFilterNone
|
||||
var err error
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "address"}).Inc()
|
||||
if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 {
|
||||
|
@ -449,7 +452,21 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (
|
|||
if ec != nil {
|
||||
page = 0
|
||||
}
|
||||
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.TxHistory)
|
||||
filter = r.URL.Query().Get("filter")
|
||||
if len(filter) > 0 {
|
||||
if filter == "inputs" {
|
||||
fn = api.AddressFilterInputs
|
||||
} else if filter == "outputs" {
|
||||
fn = api.AddressFilterOutputs
|
||||
} else {
|
||||
fn, ec = strconv.Atoi(filter)
|
||||
if ec != nil || fn < 0 {
|
||||
filter = ""
|
||||
fn = api.AddressFilterNone
|
||||
}
|
||||
}
|
||||
}
|
||||
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsOnPage, api.TxHistory, fn)
|
||||
if err != nil {
|
||||
return errorTpl, nil, err
|
||||
}
|
||||
|
@ -459,6 +476,10 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) (
|
|||
data.Address = address
|
||||
data.Page = address.Page
|
||||
data.PagingRange, data.PrevPage, data.NextPage = getPagingRange(address.Page, address.TotalPages)
|
||||
if filter != "" {
|
||||
data.PageParams = template.URL("&filter=" + filter)
|
||||
data.Address.Filter = filter
|
||||
}
|
||||
return addressTpl, data, nil
|
||||
}
|
||||
|
||||
|
@ -533,7 +554,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
|
|||
http.Redirect(w, r, joinURL("/tx/", tx.Txid), 302)
|
||||
return noTpl, nil, nil
|
||||
}
|
||||
address, err = s.api.GetAddress(q, 0, 1, api.ExistOnly)
|
||||
address, err = s.api.GetAddress(q, 0, 1, api.ExistOnly, api.AddressFilterNone)
|
||||
if err == nil {
|
||||
http.Redirect(w, r, joinURL("/address/", address.AddrStr), 302)
|
||||
return noTpl, nil, nil
|
||||
|
@ -688,7 +709,7 @@ func (s *PublicServer) apiAddress(r *http.Request) (interface{}, error) {
|
|||
if ec != nil {
|
||||
page = 0
|
||||
}
|
||||
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory)
|
||||
address, err = s.api.GetAddress(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory, api.AddressFilterNone)
|
||||
}
|
||||
return address, err
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ func (s *SocketIoServer) getAddressTxids(addr []string, opts *addrOpts) (res res
|
|||
lower, higher := uint32(opts.End), uint32(opts.Start)
|
||||
for _, address := range addr {
|
||||
if !opts.QueryMempoolOnly {
|
||||
err = s.db.GetTransactions(address, lower, higher, func(txid string, vout uint32, isOutput bool) error {
|
||||
err = s.db.GetTransactions(address, lower, higher, func(txid string, vout int32, isOutput bool) error {
|
||||
txids = append(txids, txid)
|
||||
return nil
|
||||
})
|
||||
|
@ -224,11 +224,13 @@ func (s *SocketIoServer) getAddressTxids(addr []string, opts *addrOpts) (res res
|
|||
return res, err
|
||||
}
|
||||
} else {
|
||||
m, err := s.chain.GetMempoolTransactions(address)
|
||||
o, err := s.chain.GetMempoolTransactions(address)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
txids = append(txids, m...)
|
||||
for _, m := range o {
|
||||
txids = append(txids, m.Txid)
|
||||
}
|
||||
}
|
||||
}
|
||||
res.Result = api.UniqueTxidsInReverse(txids)
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<tr>
|
||||
<th>Contract</th>
|
||||
<th>Tokens</th>
|
||||
<th style="width: 15%;">No. Txs</th>
|
||||
<th style="width: 15%;">No. Txs</th>
|
||||
</tr>
|
||||
{{- range $et := $addr.Erc20Tokens -}}
|
||||
<tr>
|
||||
|
@ -88,10 +88,23 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{- end}}{{if $addr.Transactions -}}
|
||||
{{- end}}{{if or $addr.Transactions $addr.Filter -}}
|
||||
<div class="row h-container">
|
||||
<h3 class="col-md-6 col-sm-12">Transactions</h3>
|
||||
<nav class="col-md-6 col-sm-12">{{template "paging" $data}}</nav>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<select style="float: left;margin-top: 6px;" onchange="self.location='?filter='+options[selectedIndex].value">
|
||||
<option>All</option>
|
||||
<option {{if eq $addr.Filter "inputs" -}} selected{{end}} value="inputs">Inputs</option>
|
||||
<option {{if eq $addr.Filter "outputs" -}} selected{{end}} value="outputs">Outputs</option>
|
||||
{{- if $addr.Erc20Tokens -}}
|
||||
<option {{if eq $addr.Filter "0" -}} selected{{end}} value="0">Non-contract</option>
|
||||
{{- range $et := $addr.Erc20Tokens -}}
|
||||
<option {{if eq $addr.Filter $et.ContractIndex -}} selected{{end}} value="{{$et.ContractIndex}}">{{$et.Name}}</option>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</select>
|
||||
<nav>{{template "paging" $data}}</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-div">
|
||||
{{- range $tx := $addr.Transactions}}{{$data := setTxToTemplateData $data $tx}}{{template "txdetail" $data}}{{end -}}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{{- define "paging"}}{{$data := . -}}{{if $data.PagingRange -}}
|
||||
<ul class="pagination justify-content-end">
|
||||
<li class="page-item"><a class="page-link" href="?page={{$data.PrevPage}}"><</a></li>
|
||||
<li class="page-item"><a class="page-link" href="?page={{$data.PrevPage}}{{$data.PageParams}}"><</a></li>
|
||||
{{- range $p := $data.PagingRange -}}
|
||||
<li class="page-item{{if eq $data.Page $p}} active{{end}}">
|
||||
{{- if $p}}<a class="page-link" href="?page={{$p}}">{{$p}}</a>
|
||||
{{- if $p}}<a class="page-link" href="?page={{$p}}{{$data.PageParams}}">{{$p}}</a>
|
||||
{{- else -}}<span class="page-text">...</span>{{- end -}}
|
||||
</li>{{- end -}}
|
||||
<li class="page-item"><a class="page-link" href="?page={{$data.NextPage}}">></a></li>
|
||||
<li class="page-item"><a class="page-link" href="?page={{$data.NextPage}}{{$data.PageParams}}">></a></li>
|
||||
</ul>
|
||||
{{- end -}}{{- end -}}
|
|
@ -182,12 +182,12 @@ func (c *fakeBlockChain) ResyncMempool(onNewTxAddr bchain.OnNewTxAddrFunc) (coun
|
|||
return 0, errors.New("Not implemented")
|
||||
}
|
||||
|
||||
func (c *fakeBlockChain) GetMempoolTransactions(address string) (v []string, err error) {
|
||||
func (c *fakeBlockChain) GetMempoolTransactions(address string) (v []bchain.Outpoint, err error) {
|
||||
return nil, errors.New("Not implemented")
|
||||
}
|
||||
|
||||
func (c *fakeBlockChain) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) (v []string, err error) {
|
||||
return []string{}, nil
|
||||
func (c *fakeBlockChain) GetMempoolTransactionsForAddrDesc(addrDesc bchain.AddressDescriptor) (v []bchain.Outpoint, err error) {
|
||||
return []bchain.Outpoint{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeBlockChain) GetMempoolEntry(txid string) (v *bchain.MempoolEntry, err error) {
|
||||
|
|
Loading…
Reference in New Issue