github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/bchain/mempool_ethereum_type.go (about)

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