Send websocket notification on new tx for input addresses
parent
bc001ce3a3
commit
0a3ea6e225
154
api/worker.go
154
api/worker.go
|
@ -253,32 +253,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx)
|
glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx)
|
||||||
}
|
}
|
||||||
tokens = make([]TokenTransfer, len(ets))
|
tokens = w.getTokensFromErc20(ets)
|
||||||
for i := range ets {
|
|
||||||
e := &ets[i]
|
|
||||||
cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract)
|
|
||||||
}
|
|
||||||
if erc20c == nil {
|
|
||||||
erc20c = &bchain.Erc20Contract{Name: e.Contract}
|
|
||||||
}
|
|
||||||
tokens[i] = TokenTransfer{
|
|
||||||
Type: ERC20TokenType,
|
|
||||||
Token: e.Contract,
|
|
||||||
From: e.From,
|
|
||||||
To: e.To,
|
|
||||||
Decimals: erc20c.Decimals,
|
|
||||||
Value: (*Amount)(&e.Tokens),
|
|
||||||
Name: erc20c.Name,
|
|
||||||
Symbol: erc20c.Symbol,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ethTxData := eth.GetEthereumTxData(bchainTx)
|
ethTxData := eth.GetEthereumTxData(bchainTx)
|
||||||
// mempool txs do not have fees yet
|
// mempool txs do not have fees yet
|
||||||
if ethTxData.GasUsed != nil {
|
if ethTxData.GasUsed != nil {
|
||||||
|
@ -332,6 +307,133 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTransactionFromMempoolTx converts bchain.MempoolTx to Tx, with limited amount of data
|
||||||
|
// it is not doing any request to backend or to db
|
||||||
|
func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, error) {
|
||||||
|
var err error
|
||||||
|
var valInSat, valOutSat, feesSat big.Int
|
||||||
|
var pValInSat *big.Int
|
||||||
|
var tokens []TokenTransfer
|
||||||
|
var ethSpecific *EthereumSpecific
|
||||||
|
vins := make([]Vin, len(mempoolTx.Vin))
|
||||||
|
rbf := false
|
||||||
|
for i := range mempoolTx.Vin {
|
||||||
|
bchainVin := &mempoolTx.Vin[i]
|
||||||
|
vin := &vins[i]
|
||||||
|
vin.Txid = bchainVin.Txid
|
||||||
|
vin.N = i
|
||||||
|
vin.Vout = bchainVin.Vout
|
||||||
|
vin.Sequence = int64(bchainVin.Sequence)
|
||||||
|
// detect explicit Replace-by-Fee transactions as defined by BIP125
|
||||||
|
if bchainVin.Sequence < 0xffffffff-1 {
|
||||||
|
rbf = true
|
||||||
|
}
|
||||||
|
vin.Hex = bchainVin.ScriptSig.Hex
|
||||||
|
vin.Coinbase = bchainVin.Coinbase
|
||||||
|
if w.chainType == bchain.ChainBitcoinType {
|
||||||
|
// bchainVin.Txid=="" is coinbase transaction
|
||||||
|
if bchainVin.Txid != "" {
|
||||||
|
vin.ValueSat = (*Amount)(&bchainVin.ValueSat)
|
||||||
|
vin.AddrDesc = bchainVin.AddrDesc
|
||||||
|
vin.Addresses, vin.IsAddress, err = w.chainParser.GetAddressesFromAddrDesc(vin.AddrDesc)
|
||||||
|
if vin.ValueSat != nil {
|
||||||
|
valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if w.chainType == bchain.ChainEthereumType {
|
||||||
|
if len(bchainVin.Addresses) > 0 {
|
||||||
|
vin.AddrDesc, err = w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("GetAddrDescFromAddress error %v, tx %v, bchainVin %v", err, mempoolTx.Txid, bchainVin)
|
||||||
|
}
|
||||||
|
vin.Addresses = bchainVin.Addresses
|
||||||
|
vin.IsAddress = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vouts := make([]Vout, len(mempoolTx.Vout))
|
||||||
|
for i := range mempoolTx.Vout {
|
||||||
|
bchainVout := &mempoolTx.Vout[i]
|
||||||
|
vout := &vouts[i]
|
||||||
|
vout.N = i
|
||||||
|
vout.ValueSat = (*Amount)(&bchainVout.ValueSat)
|
||||||
|
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
|
||||||
|
vout.Hex = bchainVout.ScriptPubKey.Hex
|
||||||
|
vout.AddrDesc, vout.Addresses, vout.IsAddress, err = w.getAddressesFromVout(bchainVout)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.chainType == bchain.ChainBitcoinType {
|
||||||
|
// for coinbase transactions valIn is 0
|
||||||
|
feesSat.Sub(&valInSat, &valOutSat)
|
||||||
|
if feesSat.Sign() == -1 {
|
||||||
|
feesSat.SetUint64(0)
|
||||||
|
}
|
||||||
|
pValInSat = &valInSat
|
||||||
|
} else if w.chainType == bchain.ChainEthereumType {
|
||||||
|
if len(mempoolTx.Vout) > 0 {
|
||||||
|
valOutSat = mempoolTx.Vout[0].ValueSat
|
||||||
|
}
|
||||||
|
tokens = w.getTokensFromErc20(mempoolTx.Erc20)
|
||||||
|
ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData)
|
||||||
|
ethSpecific = &EthereumSpecific{
|
||||||
|
GasLimit: ethTxData.GasLimit,
|
||||||
|
GasPrice: (*Amount)(ethTxData.GasPrice),
|
||||||
|
GasUsed: ethTxData.GasUsed,
|
||||||
|
Nonce: ethTxData.Nonce,
|
||||||
|
Status: ethTxData.Status,
|
||||||
|
Data: ethTxData.Data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r := &Tx{
|
||||||
|
Blocktime: mempoolTx.Blocktime,
|
||||||
|
FeesSat: (*Amount)(&feesSat),
|
||||||
|
Locktime: mempoolTx.LockTime,
|
||||||
|
Txid: mempoolTx.Txid,
|
||||||
|
ValueInSat: (*Amount)(pValInSat),
|
||||||
|
ValueOutSat: (*Amount)(&valOutSat),
|
||||||
|
Version: mempoolTx.Version,
|
||||||
|
Hex: mempoolTx.Hex,
|
||||||
|
Rbf: rbf,
|
||||||
|
Vin: vins,
|
||||||
|
Vout: vouts,
|
||||||
|
TokenTransfers: tokens,
|
||||||
|
EthereumSpecific: ethSpecific,
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Worker) getTokensFromErc20(erc20 []bchain.Erc20Transfer) []TokenTransfer {
|
||||||
|
tokens := make([]TokenTransfer, len(erc20))
|
||||||
|
for i := range erc20 {
|
||||||
|
e := &erc20[i]
|
||||||
|
cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract)
|
||||||
|
}
|
||||||
|
if erc20c == nil {
|
||||||
|
erc20c = &bchain.Erc20Contract{Name: e.Contract}
|
||||||
|
}
|
||||||
|
tokens[i] = TokenTransfer{
|
||||||
|
Type: ERC20TokenType,
|
||||||
|
Token: e.Contract,
|
||||||
|
From: e.From,
|
||||||
|
To: e.To,
|
||||||
|
Decimals: erc20c.Decimals,
|
||||||
|
Value: (*Amount)(&e.Tokens),
|
||||||
|
Name: erc20c.Name,
|
||||||
|
Symbol: erc20c.Symbol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
|
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
|
||||||
var err error
|
var err error
|
||||||
txids := make([]string, 0, 4)
|
txids := make([]string, 0, 4)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package bchain
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type addrIndex struct {
|
type addrIndex struct {
|
||||||
|
@ -27,6 +28,7 @@ type BaseMempool struct {
|
||||||
txEntries map[string]txEntry
|
txEntries map[string]txEntry
|
||||||
addrDescToTx map[string][]Outpoint
|
addrDescToTx map[string][]Outpoint
|
||||||
OnNewTxAddr OnNewTxAddrFunc
|
OnNewTxAddr OnNewTxAddrFunc
|
||||||
|
OnNewTx OnNewTxFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactions returns slice of mempool transactions for given address
|
// GetTransactions returns slice of mempool transactions for given address
|
||||||
|
@ -113,3 +115,22 @@ func (m *BaseMempool) GetTransactionTime(txid string) uint32 {
|
||||||
}
|
}
|
||||||
return e.time
|
return e.time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *BaseMempool) txToMempoolTx(tx *Tx) *MempoolTx {
|
||||||
|
mtx := MempoolTx{
|
||||||
|
Hex: tx.Hex,
|
||||||
|
Blocktime: time.Now().Unix(),
|
||||||
|
LockTime: tx.LockTime,
|
||||||
|
Txid: tx.Txid,
|
||||||
|
Version: tx.Version,
|
||||||
|
Vout: tx.Vout,
|
||||||
|
CoinSpecificData: tx.CoinSpecificData,
|
||||||
|
}
|
||||||
|
mtx.Vin = make([]MempoolVin, len(tx.Vin))
|
||||||
|
for i, vin := range tx.Vin {
|
||||||
|
mtx.Vin[i] = MempoolVin{
|
||||||
|
Vin: vin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &mtx
|
||||||
|
}
|
||||||
|
|
|
@ -186,8 +186,8 @@ func (c *blockChainWithMetrics) CreateMempool(chain bchain.BlockChain) (bchain.M
|
||||||
return c.b.CreateMempool(chain)
|
return c.b.CreateMempool(chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *blockChainWithMetrics) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
|
func (c *blockChainWithMetrics) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
|
||||||
return c.b.InitializeMempool(addrDescForOutpoint, onNewTxAddr)
|
return c.b.InitializeMempool(addrDescForOutpoint, onNewTxAddr, onNewTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *blockChainWithMetrics) Shutdown(ctx context.Context) error {
|
func (c *blockChainWithMetrics) Shutdown(ctx context.Context) error {
|
||||||
|
|
|
@ -155,12 +155,13 @@ func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeMempool creates ZeroMQ subscription and sets AddrDescForOutpointFunc to the Mempool
|
// InitializeMempool creates ZeroMQ subscription and sets AddrDescForOutpointFunc to the Mempool
|
||||||
func (b *BitcoinRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
|
func (b *BitcoinRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
|
||||||
if b.Mempool == nil {
|
if b.Mempool == nil {
|
||||||
return errors.New("Mempool not created")
|
return errors.New("Mempool not created")
|
||||||
}
|
}
|
||||||
b.Mempool.AddrDescForOutpoint = addrDescForOutpoint
|
b.Mempool.AddrDescForOutpoint = addrDescForOutpoint
|
||||||
b.Mempool.OnNewTxAddr = onNewTxAddr
|
b.Mempool.OnNewTxAddr = onNewTxAddr
|
||||||
|
b.Mempool.OnNewTx = onNewTx
|
||||||
if b.mq == nil {
|
if b.mq == nil {
|
||||||
mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler)
|
mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -480,8 +480,13 @@ type EthereumTxData struct {
|
||||||
|
|
||||||
// GetEthereumTxData returns EthereumTxData from bchain.Tx
|
// GetEthereumTxData returns EthereumTxData from bchain.Tx
|
||||||
func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
|
func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
|
||||||
|
return GetEthereumTxDataFromSpecificData(tx.CoinSpecificData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData
|
||||||
|
func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData {
|
||||||
etd := EthereumTxData{Status: txStatusPending}
|
etd := EthereumTxData{Status: txStatusPending}
|
||||||
csd, ok := tx.CoinSpecificData.(completeTransaction)
|
csd, ok := coinSpecificData.(completeTransaction)
|
||||||
if ok {
|
if ok {
|
||||||
if csd.Tx != nil {
|
if csd.Tx != nil {
|
||||||
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
|
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
|
||||||
|
|
|
@ -178,7 +178,7 @@ func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeMempool creates subscriptions to newHeads and newPendingTransactions
|
// InitializeMempool creates subscriptions to newHeads and newPendingTransactions
|
||||||
func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
|
func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
|
||||||
if b.Mempool == nil {
|
if b.Mempool == nil {
|
||||||
return errors.New("Mempool not created")
|
return errors.New("Mempool not created")
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,7 @@ func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOu
|
||||||
}
|
}
|
||||||
|
|
||||||
b.Mempool.OnNewTxAddr = onNewTxAddr
|
b.Mempool.OnNewTxAddr = onNewTxAddr
|
||||||
|
b.Mempool.OnNewTx = onNewTx
|
||||||
|
|
||||||
if err = b.subscribeEvents(); err != nil {
|
if err = b.subscribeEvents(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package bchain
|
package bchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type chanInputPayload struct {
|
||||||
|
tx *MempoolTx
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
// MempoolBitcoinType is mempool handle.
|
// MempoolBitcoinType is mempool handle.
|
||||||
type MempoolBitcoinType struct {
|
type MempoolBitcoinType struct {
|
||||||
BaseMempool
|
BaseMempool
|
||||||
|
@ -28,12 +34,12 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int) *Mempo
|
||||||
}
|
}
|
||||||
for i := 0; i < workers; i++ {
|
for i := 0; i < workers; i++ {
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
chanInput := make(chan Outpoint, 1)
|
chanInput := make(chan chanInputPayload, 1)
|
||||||
chanResult := make(chan *addrIndex, 1)
|
chanResult := make(chan *addrIndex, 1)
|
||||||
for j := 0; j < subworkers; j++ {
|
for j := 0; j < subworkers; j++ {
|
||||||
go func(j int) {
|
go func(j int) {
|
||||||
for input := range chanInput {
|
for payload := range chanInput {
|
||||||
ai := m.getInputAddress(input)
|
ai := m.getInputAddress(&payload)
|
||||||
chanResult <- ai
|
chanResult <- ai
|
||||||
}
|
}
|
||||||
}(j)
|
}(j)
|
||||||
|
@ -51,38 +57,44 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int) *Mempo
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MempoolBitcoinType) getInputAddress(input Outpoint) *addrIndex {
|
func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrIndex {
|
||||||
var addrDesc AddressDescriptor
|
var addrDesc AddressDescriptor
|
||||||
|
var value *big.Int
|
||||||
|
vin := &payload.tx.Vin[payload.index]
|
||||||
if m.AddrDescForOutpoint != nil {
|
if m.AddrDescForOutpoint != nil {
|
||||||
addrDesc = m.AddrDescForOutpoint(input)
|
addrDesc, value = m.AddrDescForOutpoint(Outpoint{vin.Txid, int32(vin.Vout)})
|
||||||
}
|
}
|
||||||
if addrDesc == nil {
|
if addrDesc == nil {
|
||||||
itx, err := m.chain.GetTransactionForMempool(input.Txid)
|
itx, err := m.chain.GetTransactionForMempool(vin.Txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("cannot get transaction ", input.Txid, ": ", err)
|
glog.Error("cannot get transaction ", vin.Txid, ": ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if int(input.Vout) >= len(itx.Vout) {
|
if int(vin.Vout) >= len(itx.Vout) {
|
||||||
glog.Error("Vout len in transaction ", input.Txid, " ", len(itx.Vout), " input.Vout=", input.Vout)
|
glog.Error("Vout len in transaction ", vin.Txid, " ", len(itx.Vout), " input.Vout=", vin.Vout)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
addrDesc, err = m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.Vout])
|
addrDesc, err = m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[vin.Vout])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("error in addrDesc in ", input.Txid, " ", input.Vout, ": ", err)
|
glog.Error("error in addrDesc in ", vin.Txid, " ", vin.Vout, ": ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
value = &itx.Vout[vin.Vout].ValueSat
|
||||||
}
|
}
|
||||||
return &addrIndex{string(addrDesc), ^input.Vout}
|
vin.AddrDesc = addrDesc
|
||||||
|
vin.ValueSat = *value
|
||||||
|
return &addrIndex{string(addrDesc), ^int32(vin.Vout)}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, chanResult chan *addrIndex) ([]addrIndex, bool) {
|
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPayload, chanResult chan *addrIndex) ([]addrIndex, bool) {
|
||||||
tx, err := m.chain.GetTransactionForMempool(txid)
|
tx, err := m.chain.GetTransactionForMempool(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("cannot get transaction ", txid, ": ", err)
|
glog.Error("cannot get transaction ", txid, ": ", err)
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
|
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
|
||||||
|
mtx := m.txToMempoolTx(tx)
|
||||||
io := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
|
io := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
|
||||||
for _, output := range tx.Vout {
|
for _, output := range tx.Vout {
|
||||||
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&output)
|
addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&output)
|
||||||
|
@ -98,11 +110,12 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispatched := 0
|
dispatched := 0
|
||||||
for _, input := range tx.Vin {
|
for i := range tx.Vin {
|
||||||
|
input := &tx.Vin[i]
|
||||||
if input.Coinbase != "" {
|
if input.Coinbase != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
o := Outpoint{input.Txid, int32(input.Vout)}
|
payload := chanInputPayload{mtx, i}
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -113,7 +126,7 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, ch
|
||||||
}
|
}
|
||||||
dispatched--
|
dispatched--
|
||||||
// send input to be processed
|
// send input to be processed
|
||||||
case chanInput <- o:
|
case chanInput <- payload:
|
||||||
dispatched++
|
dispatched++
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
@ -125,6 +138,9 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, ch
|
||||||
io = append(io, *ai)
|
io = append(io, *ai)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if m.OnNewTx != nil {
|
||||||
|
m.OnNewTx(mtx)
|
||||||
|
}
|
||||||
return io, true
|
return io, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,16 +31,18 @@ func NewMempoolEthereumType(chain BlockChain, mempoolTxTimeoutHours int, queryBa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendAddress(io []addrIndex, i int32, a string, parser BlockChainParser) []addrIndex {
|
func appendAddress(io []addrIndex, i int32, a string, parser BlockChainParser) ([]addrIndex, AddressDescriptor) {
|
||||||
|
var addrDesc AddressDescriptor
|
||||||
|
var err error
|
||||||
if len(a) > 0 {
|
if len(a) > 0 {
|
||||||
addrDesc, err := parser.GetAddrDescFromAddress(a)
|
addrDesc, err = parser.GetAddrDescFromAddress(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("error in input addrDesc in ", a, ": ", err)
|
glog.Error("error in input addrDesc in ", a, ": ", err)
|
||||||
return io
|
return io, nil
|
||||||
}
|
}
|
||||||
io = append(io, addrIndex{string(addrDesc), i})
|
io = append(io, addrIndex{string(addrDesc), i})
|
||||||
}
|
}
|
||||||
return io
|
return io, addrDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry, bool) {
|
func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry, bool) {
|
||||||
|
@ -51,9 +53,10 @@ func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry
|
||||||
}
|
}
|
||||||
return txEntry{}, false
|
return txEntry{}, false
|
||||||
}
|
}
|
||||||
|
mtx := m.txToMempoolTx(tx)
|
||||||
parser := m.chain.GetChainParser()
|
parser := m.chain.GetChainParser()
|
||||||
addrIndexes := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
|
addrIndexes := make([]addrIndex, 0, len(mtx.Vout)+len(mtx.Vin))
|
||||||
for _, output := range tx.Vout {
|
for _, output := range mtx.Vout {
|
||||||
addrDesc, err := parser.GetAddrDescFromVout(&output)
|
addrDesc, err := parser.GetAddrDescFromVout(&output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != ErrAddressMissing {
|
if err != ErrAddressMissing {
|
||||||
|
@ -65,18 +68,20 @@ func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry
|
||||||
addrIndexes = append(addrIndexes, addrIndex{string(addrDesc), int32(output.N)})
|
addrIndexes = append(addrIndexes, addrIndex{string(addrDesc), int32(output.N)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, input := range tx.Vin {
|
for j := range mtx.Vin {
|
||||||
|
input := &mtx.Vin[j]
|
||||||
for i, a := range input.Addresses {
|
for i, a := range input.Addresses {
|
||||||
addrIndexes = appendAddress(addrIndexes, ^int32(i), a, parser)
|
addrIndexes, input.AddrDesc = appendAddress(addrIndexes, ^int32(i), a, parser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t, err := parser.EthereumTypeGetErc20FromTx(tx)
|
t, err := parser.EthereumTypeGetErc20FromTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("GetErc20FromTx for tx ", txid, ", ", err)
|
glog.Error("GetErc20FromTx for tx ", txid, ", ", err)
|
||||||
} else {
|
} else {
|
||||||
|
mtx.Erc20 = t
|
||||||
for i := range t {
|
for i := range t {
|
||||||
addrIndexes = appendAddress(addrIndexes, ^int32(i+1), t[i].From, parser)
|
addrIndexes, _ = appendAddress(addrIndexes, ^int32(i+1), t[i].From, parser)
|
||||||
addrIndexes = appendAddress(addrIndexes, int32(i+1), t[i].To, parser)
|
addrIndexes, _ = appendAddress(addrIndexes, int32(i+1), t[i].To, parser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.OnNewTxAddr != nil {
|
if m.OnNewTxAddr != nil {
|
||||||
|
@ -88,6 +93,9 @@ func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if m.OnNewTx != nil {
|
||||||
|
m.OnNewTx(mtx)
|
||||||
|
}
|
||||||
return txEntry{addrIndexes: addrIndexes, time: txTime}, true
|
return txEntry{addrIndexes: addrIndexes, time: txTime}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ type ScriptSig struct {
|
||||||
Hex string `json:"hex"`
|
Hex string `json:"hex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vin contains data about tx output
|
// Vin contains data about tx input
|
||||||
type Vin struct {
|
type Vin struct {
|
||||||
Coinbase string `json:"coinbase"`
|
Coinbase string `json:"coinbase"`
|
||||||
Txid string `json:"txid"`
|
Txid string `json:"txid"`
|
||||||
|
@ -92,6 +92,27 @@ type Tx struct {
|
||||||
CoinSpecificData interface{} `json:"-"`
|
CoinSpecificData interface{} `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MempoolVin contains data about tx input
|
||||||
|
type MempoolVin struct {
|
||||||
|
Vin
|
||||||
|
AddrDesc AddressDescriptor `json:"-"`
|
||||||
|
ValueSat big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MempoolTx is blockchain transaction in mempool
|
||||||
|
// optimized for onNewTx notification
|
||||||
|
type MempoolTx struct {
|
||||||
|
Hex string `json:"hex"`
|
||||||
|
Txid string `json:"txid"`
|
||||||
|
Version int32 `json:"version"`
|
||||||
|
LockTime uint32 `json:"locktime"`
|
||||||
|
Vin []MempoolVin `json:"vin"`
|
||||||
|
Vout []Vout `json:"vout"`
|
||||||
|
Blocktime int64 `json:"blocktime,omitempty"`
|
||||||
|
Erc20 []Erc20Transfer `json:"-"`
|
||||||
|
CoinSpecificData interface{} `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
// Block is block header and list of transactions
|
// Block is block header and list of transactions
|
||||||
type Block struct {
|
type Block struct {
|
||||||
BlockHeader
|
BlockHeader
|
||||||
|
@ -211,8 +232,11 @@ type OnNewBlockFunc func(hash string, height uint32)
|
||||||
// OnNewTxAddrFunc is used to send notification about a new transaction/address
|
// OnNewTxAddrFunc is used to send notification about a new transaction/address
|
||||||
type OnNewTxAddrFunc func(tx *Tx, desc AddressDescriptor)
|
type OnNewTxAddrFunc func(tx *Tx, desc AddressDescriptor)
|
||||||
|
|
||||||
// AddrDescForOutpointFunc defines function that returns address descriptorfor given outpoint or nil if outpoint not found
|
// OnNewTxFunc is used to send notification about a new transaction/address
|
||||||
type AddrDescForOutpointFunc func(outpoint Outpoint) AddressDescriptor
|
type OnNewTxFunc func(tx *MempoolTx)
|
||||||
|
|
||||||
|
// AddrDescForOutpointFunc returns address descriptor and value for given outpoint or nil if outpoint not found
|
||||||
|
type AddrDescForOutpointFunc func(outpoint Outpoint) (AddressDescriptor, *big.Int)
|
||||||
|
|
||||||
// BlockChain defines common interface to block chain daemon
|
// BlockChain defines common interface to block chain daemon
|
||||||
type BlockChain interface {
|
type BlockChain interface {
|
||||||
|
@ -222,7 +246,7 @@ type BlockChain interface {
|
||||||
// create mempool but do not initialize it
|
// create mempool but do not initialize it
|
||||||
CreateMempool(BlockChain) (Mempool, error)
|
CreateMempool(BlockChain) (Mempool, error)
|
||||||
// initialize mempool, create ZeroMQ (or other) subscription
|
// initialize mempool, create ZeroMQ (or other) subscription
|
||||||
InitializeMempool(AddrDescForOutpointFunc, OnNewTxAddrFunc) error
|
InitializeMempool(AddrDescForOutpointFunc, OnNewTxAddrFunc, OnNewTxFunc) error
|
||||||
// shutdown mempool, ZeroMQ and block chain connections
|
// shutdown mempool, ZeroMQ and block chain connections
|
||||||
Shutdown(ctx context.Context) error
|
Shutdown(ctx context.Context) error
|
||||||
// chain info
|
// chain info
|
||||||
|
|
10
blockbook.go
10
blockbook.go
|
@ -100,6 +100,7 @@ var (
|
||||||
internalState *common.InternalState
|
internalState *common.InternalState
|
||||||
callbacksOnNewBlock []bchain.OnNewBlockFunc
|
callbacksOnNewBlock []bchain.OnNewBlockFunc
|
||||||
callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc
|
callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc
|
||||||
|
callbacksOnNewTx []bchain.OnNewTxFunc
|
||||||
callbacksOnNewFiatRatesTicker []fiat.OnNewFiatRatesTicker
|
callbacksOnNewFiatRatesTicker []fiat.OnNewFiatRatesTicker
|
||||||
chanOsSignal chan os.Signal
|
chanOsSignal chan os.Signal
|
||||||
inShutdown int32
|
inShutdown int32
|
||||||
|
@ -298,7 +299,7 @@ func mainWithExitCode() int {
|
||||||
if chain.GetChainParser().GetChainType() == bchain.ChainBitcoinType {
|
if chain.GetChainParser().GetChainType() == bchain.ChainBitcoinType {
|
||||||
addrDescForOutpoint = index.AddrDescForOutpoint
|
addrDescForOutpoint = index.AddrDescForOutpoint
|
||||||
}
|
}
|
||||||
err = chain.InitializeMempool(addrDescForOutpoint, onNewTxAddr)
|
err = chain.InitializeMempool(addrDescForOutpoint, onNewTxAddr, onNewTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("initializeMempool ", err)
|
glog.Error("initializeMempool ", err)
|
||||||
return exitCodeFatal
|
return exitCodeFatal
|
||||||
|
@ -319,6 +320,7 @@ func mainWithExitCode() int {
|
||||||
// start full public interface
|
// start full public interface
|
||||||
callbacksOnNewBlock = append(callbacksOnNewBlock, publicServer.OnNewBlock)
|
callbacksOnNewBlock = append(callbacksOnNewBlock, publicServer.OnNewBlock)
|
||||||
callbacksOnNewTxAddr = append(callbacksOnNewTxAddr, publicServer.OnNewTxAddr)
|
callbacksOnNewTxAddr = append(callbacksOnNewTxAddr, publicServer.OnNewTxAddr)
|
||||||
|
callbacksOnNewTx = append(callbacksOnNewTx, publicServer.OnNewTx)
|
||||||
callbacksOnNewFiatRatesTicker = append(callbacksOnNewFiatRatesTicker, publicServer.OnNewFiatRatesTicker)
|
callbacksOnNewFiatRatesTicker = append(callbacksOnNewFiatRatesTicker, publicServer.OnNewFiatRatesTicker)
|
||||||
publicServer.ConnectFullPublicInterface()
|
publicServer.ConnectFullPublicInterface()
|
||||||
}
|
}
|
||||||
|
@ -620,6 +622,12 @@ func onNewTxAddr(tx *bchain.Tx, desc bchain.AddressDescriptor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onNewTx(tx *bchain.MempoolTx) {
|
||||||
|
for _, c := range callbacksOnNewTx {
|
||||||
|
c(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func pushSynchronizationHandler(nt bchain.NotificationType) {
|
func pushSynchronizationHandler(nt bchain.NotificationType) {
|
||||||
glog.V(1).Info("MQ: notification ", nt)
|
glog.V(1).Info("MQ: notification ", nt)
|
||||||
if atomic.LoadInt32(&inShutdown) != 0 {
|
if atomic.LoadInt32(&inShutdown) != 0 {
|
||||||
|
|
|
@ -1016,23 +1016,23 @@ func (d *RocksDB) GetTxAddresses(txid string) (*TxAddresses, error) {
|
||||||
return d.getTxAddresses(btxID)
|
return d.getTxAddresses(btxID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddrDescForOutpoint defines function that returns address descriptorfor given outpoint or nil if outpoint not found
|
// AddrDescForOutpoint is a function that returns address descriptor and value for given outpoint or nil if outpoint not found
|
||||||
func (d *RocksDB) AddrDescForOutpoint(outpoint bchain.Outpoint) bchain.AddressDescriptor {
|
func (d *RocksDB) AddrDescForOutpoint(outpoint bchain.Outpoint) (bchain.AddressDescriptor, *big.Int) {
|
||||||
ta, err := d.GetTxAddresses(outpoint.Txid)
|
ta, err := d.GetTxAddresses(outpoint.Txid)
|
||||||
if err != nil || ta == nil {
|
if err != nil || ta == nil {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if outpoint.Vout < 0 {
|
if outpoint.Vout < 0 {
|
||||||
vin := ^outpoint.Vout
|
vin := ^outpoint.Vout
|
||||||
if len(ta.Inputs) <= int(vin) {
|
if len(ta.Inputs) <= int(vin) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return ta.Inputs[vin].AddrDesc
|
return ta.Inputs[vin].AddrDesc, &ta.Inputs[vin].ValueSat
|
||||||
}
|
}
|
||||||
if len(ta.Outputs) <= int(outpoint.Vout) {
|
if len(ta.Outputs) <= int(outpoint.Vout) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return ta.Outputs[outpoint.Vout].AddrDesc
|
return ta.Outputs[outpoint.Vout].AddrDesc, &ta.Outputs[outpoint.Vout].ValueSat
|
||||||
}
|
}
|
||||||
|
|
||||||
func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte {
|
func packTxAddresses(ta *TxAddresses, buf []byte, varBuf []byte) []byte {
|
||||||
|
|
|
@ -219,10 +219,14 @@ func (s *PublicServer) OnNewFiatRatesTicker(ticker *db.CurrencyRatesTicker) {
|
||||||
s.websocket.OnNewFiatRatesTicker(ticker)
|
s.websocket.OnNewFiatRatesTicker(ticker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnNewTxAddr notifies users subscribed to bitcoind/addresstxid about new block
|
// OnNewTxAddr notifies users subscribed to notification about new tx
|
||||||
func (s *PublicServer) OnNewTxAddr(tx *bchain.Tx, desc bchain.AddressDescriptor) {
|
func (s *PublicServer) OnNewTxAddr(tx *bchain.Tx, desc bchain.AddressDescriptor) {
|
||||||
s.socketio.OnNewTxAddr(tx.Txid, desc)
|
s.socketio.OnNewTxAddr(tx.Txid, desc)
|
||||||
s.websocket.OnNewTxAddr(tx, desc)
|
}
|
||||||
|
|
||||||
|
// OnNewTx notifies users subscribed to notification about new tx
|
||||||
|
func (s *PublicServer) OnNewTx(tx *bchain.MempoolTx) {
|
||||||
|
s.websocket.OnNewTx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicServer) txRedirect(w http.ResponseWriter, r *http.Request) {
|
func (s *PublicServer) txRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -757,47 +757,91 @@ func (s *WebsocketServer) OnNewBlock(hash string, height uint32) {
|
||||||
glog.Info("broadcasting new block ", height, " ", hash, " to ", len(s.newBlockSubscriptions), " channels")
|
glog.Info("broadcasting new block ", height, " ", hash, " to ", len(s.newBlockSubscriptions), " channels")
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnNewTxAddr is a callback that broadcasts info about a tx affecting subscribed address
|
func (s *WebsocketServer) sendOnNewTxAddr(stringAddressDescriptor string, tx *api.Tx) {
|
||||||
func (s *WebsocketServer) OnNewTxAddr(tx *bchain.Tx, addrDesc bchain.AddressDescriptor) {
|
addrDesc := bchain.AddressDescriptor(stringAddressDescriptor)
|
||||||
// check if there is any subscription but release the lock immediately, GetTransactionFromBchainTx may take some time
|
addr, _, err := s.chainParser.GetAddressesFromAddrDesc(addrDesc)
|
||||||
s.addressSubscriptionsLock.Lock()
|
if err != nil {
|
||||||
as, ok := s.addressSubscriptions[string(addrDesc)]
|
glog.Error("GetAddressesFromAddrDesc error ", err, " for ", addrDesc)
|
||||||
lenAs := len(as)
|
return
|
||||||
s.addressSubscriptionsLock.Unlock()
|
}
|
||||||
if ok && lenAs > 0 {
|
if len(addr) == 1 {
|
||||||
addr, _, err := s.chainParser.GetAddressesFromAddrDesc(addrDesc)
|
data := struct {
|
||||||
if err != nil {
|
Address string `json:"address"`
|
||||||
glog.Error("GetAddressesFromAddrDesc error ", err, " for ", addrDesc)
|
Tx *api.Tx `json:"tx"`
|
||||||
return
|
}{
|
||||||
|
Address: addr[0],
|
||||||
|
Tx: tx,
|
||||||
}
|
}
|
||||||
if len(addr) == 1 {
|
// get the list of subscriptions again, this time keep the lock
|
||||||
atx, err := s.api.GetTransactionFromBchainTx(tx, 0, false, false)
|
s.addressSubscriptionsLock.Lock()
|
||||||
if err != nil {
|
defer s.addressSubscriptionsLock.Unlock()
|
||||||
glog.Error("GetTransactionFromBchainTx error ", err, " for ", tx.Txid)
|
as, ok := s.addressSubscriptions[stringAddressDescriptor]
|
||||||
return
|
if ok {
|
||||||
}
|
for c, id := range as {
|
||||||
data := struct {
|
if c.IsAlive() {
|
||||||
Address string `json:"address"`
|
c.out <- &websocketRes{
|
||||||
Tx *api.Tx `json:"tx"`
|
ID: id,
|
||||||
}{
|
Data: &data,
|
||||||
Address: addr[0],
|
|
||||||
Tx: atx,
|
|
||||||
}
|
|
||||||
// get the list of subscriptions again, this time keep the lock
|
|
||||||
s.addressSubscriptionsLock.Lock()
|
|
||||||
defer s.addressSubscriptionsLock.Unlock()
|
|
||||||
as, ok = s.addressSubscriptions[string(addrDesc)]
|
|
||||||
if ok {
|
|
||||||
for c, id := range as {
|
|
||||||
if c.IsAlive() {
|
|
||||||
c.out <- &websocketRes{
|
|
||||||
ID: id,
|
|
||||||
Data: &data,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.Info("broadcasting new tx ", tx.Txid, " for addr ", addr[0], " to ", len(as), " channels")
|
|
||||||
}
|
}
|
||||||
|
glog.Info("broadcasting new tx ", tx.Txid, ", addr ", addr[0], " to ", len(as), " channels")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnNewTx is a callback that broadcasts info about a tx affecting subscribed address
|
||||||
|
func (s *WebsocketServer) OnNewTx(tx *bchain.MempoolTx) {
|
||||||
|
// check if there is any subscription in inputs, outputs and erc20
|
||||||
|
// release the lock immediately, GetTransactionFromMempoolTx is potentially slow
|
||||||
|
subscribed := make(map[string]struct{})
|
||||||
|
s.addressSubscriptionsLock.Lock()
|
||||||
|
for i := range tx.Vin {
|
||||||
|
sad := string(tx.Vin[i].AddrDesc)
|
||||||
|
if len(sad) > 0 {
|
||||||
|
as, ok := s.addressSubscriptions[sad]
|
||||||
|
if ok && len(as) > 0 {
|
||||||
|
subscribed[sad] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range tx.Vout {
|
||||||
|
addrDesc, err := s.chainParser.GetAddrDescFromVout(&tx.Vout[i])
|
||||||
|
if err == nil && len(addrDesc) > 0 {
|
||||||
|
sad := string(addrDesc)
|
||||||
|
as, ok := s.addressSubscriptions[sad]
|
||||||
|
if ok && len(as) > 0 {
|
||||||
|
subscribed[sad] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range tx.Erc20 {
|
||||||
|
addrDesc, err := s.chainParser.GetAddrDescFromAddress(tx.Erc20[i].From)
|
||||||
|
if err == nil && len(addrDesc) > 0 {
|
||||||
|
sad := string(addrDesc)
|
||||||
|
as, ok := s.addressSubscriptions[sad]
|
||||||
|
if ok && len(as) > 0 {
|
||||||
|
subscribed[sad] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addrDesc, err = s.chainParser.GetAddrDescFromAddress(tx.Erc20[i].To)
|
||||||
|
if err == nil && len(addrDesc) > 0 {
|
||||||
|
sad := string(addrDesc)
|
||||||
|
as, ok := s.addressSubscriptions[sad]
|
||||||
|
if ok && len(as) > 0 {
|
||||||
|
subscribed[sad] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.addressSubscriptionsLock.Unlock()
|
||||||
|
if len(subscribed) > 0 {
|
||||||
|
atx, err := s.api.GetTransactionFromMempoolTx(tx)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("GetTransactionFromMempoolTx error ", err, " for ", tx.Txid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for stringAddressDescriptor := range subscribed {
|
||||||
|
s.sendOnNewTxAddr(stringAddressDescriptor, atx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (c *fakeBlockChain) Initialize() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeBlockChain) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
|
func (c *fakeBlockChain) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, bc
|
||||||
return nil, nil, fmt.Errorf("Mempool creation failed: %s", err)
|
return nil, nil, fmt.Errorf("Mempool creation failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chain.InitializeMempool(nil, nil)
|
err = chain.InitializeMempool(nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Mempool initialization failed: %s", err)
|
return nil, nil, fmt.Errorf("Mempool initialization failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue