github.com/bchainhub/blockbook@v0.3.2/bchain/mempool_ethereum_type.go (about)

     1  package bchain
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/golang/glog"
     7  )
     8  
     9  const mempoolTimeoutRunPeriod = 10 * time.Minute
    10  
    11  // MempoolEthereumType is mempool handle of EthereumType chains
    12  type MempoolEthereumType struct {
    13  	BaseMempool
    14  	mempoolTimeoutTime   time.Duration
    15  	queryBackendOnResync bool
    16  	nextTimeoutRun       time.Time
    17  }
    18  
    19  // NewMempoolEthereumType creates new mempool handler.
    20  func NewMempoolEthereumType(chain BlockChain, mempoolTxTimeoutHours int, queryBackendOnResync bool) *MempoolEthereumType {
    21  	mempoolTimeoutTime := time.Duration(mempoolTxTimeoutHours) * time.Hour
    22  	return &MempoolEthereumType{
    23  		BaseMempool: BaseMempool{
    24  			chain:        chain,
    25  			txEntries:    make(map[string]txEntry),
    26  			addrDescToTx: make(map[string][]Outpoint),
    27  		},
    28  		mempoolTimeoutTime:   mempoolTimeoutTime,
    29  		queryBackendOnResync: queryBackendOnResync,
    30  		nextTimeoutRun:       time.Now().Add(mempoolTimeoutTime),
    31  	}
    32  }
    33  
    34  func appendAddress(io []addrIndex, i int32, a string, parser BlockChainParser) []addrIndex {
    35  	if len(a) > 0 {
    36  		addrDesc, err := parser.GetAddrDescFromAddress(a)
    37  		if err != nil {
    38  			glog.Error("error in input addrDesc in ", a, ": ", err)
    39  			return io
    40  		}
    41  		io = append(io, addrIndex{string(addrDesc), i})
    42  	}
    43  	return io
    44  }
    45  
    46  func (m *MempoolEthereumType) createTxEntry(txid string, txTime uint32) (txEntry, bool) {
    47  	tx, err := m.chain.GetTransactionForMempool(txid)
    48  	if err != nil {
    49  		if err != ErrTxNotFound {
    50  			glog.Warning("cannot get transaction ", txid, ": ", err)
    51  		}
    52  		return txEntry{}, false
    53  	}
    54  	parser := m.chain.GetChainParser()
    55  	addrIndexes := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
    56  	for _, output := range tx.Vout {
    57  		addrDesc, err := parser.GetAddrDescFromVout(&output)
    58  		if err != nil {
    59  			if err != ErrAddressMissing {
    60  				glog.Error("error in output addrDesc in ", txid, " ", output.N, ": ", err)
    61  			}
    62  			continue
    63  		}
    64  		if len(addrDesc) > 0 {
    65  			addrIndexes = append(addrIndexes, addrIndex{string(addrDesc), int32(output.N)})
    66  		}
    67  	}
    68  	for _, input := range tx.Vin {
    69  		for i, a := range input.Addresses {
    70  			addrIndexes = appendAddress(addrIndexes, ^int32(i), a, parser)
    71  		}
    72  	}
    73  	t, err := parser.EthereumTypeGetErc20FromTx(tx)
    74  	if err != nil {
    75  		glog.Error("GetErc20FromTx for tx ", txid, ", ", err)
    76  	} else {
    77  		for i := range t {
    78  			addrIndexes = appendAddress(addrIndexes, ^int32(i+1), t[i].From, parser)
    79  			addrIndexes = appendAddress(addrIndexes, int32(i+1), t[i].To, parser)
    80  		}
    81  	}
    82  	if m.OnNewTxAddr != nil {
    83  		sent := make(map[string]struct{})
    84  		for _, si := range addrIndexes {
    85  			if _, found := sent[si.addrDesc]; !found {
    86  				m.OnNewTxAddr(tx, AddressDescriptor(si.addrDesc))
    87  				sent[si.addrDesc] = struct{}{}
    88  			}
    89  		}
    90  	}
    91  	return txEntry{addrIndexes: addrIndexes, time: txTime}, true
    92  }
    93  
    94  // Resync ethereum type removes timed out transactions and returns number of transactions in mempool.
    95  // Transactions are added/removed by AddTransactionToMempool/RemoveTransactionFromMempool methods
    96  func (m *MempoolEthereumType) Resync() (int, error) {
    97  	if m.queryBackendOnResync {
    98  		txs, err := m.chain.GetMempoolTransactions()
    99  		if err != nil {
   100  			return 0, err
   101  		}
   102  		for _, txid := range txs {
   103  			m.AddTransactionToMempool(txid)
   104  		}
   105  	}
   106  	m.mux.Lock()
   107  	entries := len(m.txEntries)
   108  	now := time.Now()
   109  	if m.nextTimeoutRun.Before(now) {
   110  		threshold := now.Add(-m.mempoolTimeoutTime)
   111  		for txid, entry := range m.txEntries {
   112  			if time.Unix(int64(entry.time), 0).Before(threshold) {
   113  				m.removeEntryFromMempool(txid, entry)
   114  			}
   115  		}
   116  		removed := entries - len(m.txEntries)
   117  		entries = len(m.txEntries)
   118  		glog.Info("Mempool: cleanup, removed ", removed, " transactions from mempool")
   119  		m.nextTimeoutRun = now.Add(mempoolTimeoutRunPeriod)
   120  	}
   121  	m.mux.Unlock()
   122  	glog.Info("Mempool: resync ", entries, " transactions in mempool")
   123  	return entries, nil
   124  }
   125  
   126  // AddTransactionToMempool adds transactions to mempool
   127  func (m *MempoolEthereumType) AddTransactionToMempool(txid string) {
   128  	m.mux.Lock()
   129  	_, exists := m.txEntries[txid]
   130  	m.mux.Unlock()
   131  	if glog.V(1) {
   132  		glog.Info("AddTransactionToMempool ", txid, ", existed ", exists)
   133  	}
   134  	if !exists {
   135  		entry, ok := m.createTxEntry(txid, uint32(time.Now().Unix()))
   136  		if !ok {
   137  			return
   138  		}
   139  		m.mux.Lock()
   140  		m.txEntries[txid] = entry
   141  		for _, si := range entry.addrIndexes {
   142  			m.addrDescToTx[si.addrDesc] = append(m.addrDescToTx[si.addrDesc], Outpoint{txid, si.n})
   143  		}
   144  		m.mux.Unlock()
   145  	}
   146  }
   147  
   148  // RemoveTransactionFromMempool removes transaction from mempool
   149  func (m *MempoolEthereumType) RemoveTransactionFromMempool(txid string) {
   150  	m.mux.Lock()
   151  	entry, exists := m.txEntries[txid]
   152  	if glog.V(1) {
   153  		glog.Info("RemoveTransactionFromMempool ", txid, ", existed ", exists)
   154  	}
   155  	if exists {
   156  		m.removeEntryFromMempool(txid, entry)
   157  	}
   158  	m.mux.Unlock()
   159  }