github.com/jackcoble/blockbook@v0.3.2/bchain/mempool_bitcoin_type.go (about)

     1  package bchain
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/golang/glog"
     7  )
     8  
     9  // MempoolBitcoinType is mempool handle.
    10  type MempoolBitcoinType struct {
    11  	BaseMempool
    12  	chanTxid            chan string
    13  	chanAddrIndex       chan txidio
    14  	AddrDescForOutpoint AddrDescForOutpointFunc
    15  }
    16  
    17  // NewMempoolBitcoinType creates new mempool handler.
    18  // For now there is no cleanup of sync routines, the expectation is that the mempool is created only once per process
    19  func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int) *MempoolBitcoinType {
    20  	m := &MempoolBitcoinType{
    21  		BaseMempool: BaseMempool{
    22  			chain:        chain,
    23  			txEntries:    make(map[string]txEntry),
    24  			addrDescToTx: make(map[string][]Outpoint),
    25  		},
    26  		chanTxid:      make(chan string, 1),
    27  		chanAddrIndex: make(chan txidio, 1),
    28  	}
    29  	for i := 0; i < workers; i++ {
    30  		go func(i int) {
    31  			chanInput := make(chan Outpoint, 1)
    32  			chanResult := make(chan *addrIndex, 1)
    33  			for j := 0; j < subworkers; j++ {
    34  				go func(j int) {
    35  					for input := range chanInput {
    36  						ai := m.getInputAddress(input)
    37  						chanResult <- ai
    38  					}
    39  				}(j)
    40  			}
    41  			for txid := range m.chanTxid {
    42  				io, ok := m.getTxAddrs(txid, chanInput, chanResult)
    43  				if !ok {
    44  					io = []addrIndex{}
    45  				}
    46  				m.chanAddrIndex <- txidio{txid, io}
    47  			}
    48  		}(i)
    49  	}
    50  	glog.Info("mempool: starting with ", workers, "*", subworkers, " sync workers")
    51  	return m
    52  }
    53  
    54  func (m *MempoolBitcoinType) getInputAddress(input Outpoint) *addrIndex {
    55  	var addrDesc AddressDescriptor
    56  	if m.AddrDescForOutpoint != nil {
    57  		addrDesc = m.AddrDescForOutpoint(input)
    58  	}
    59  	if addrDesc == nil {
    60  		itx, err := m.chain.GetTransactionForMempool(input.Txid)
    61  		if err != nil {
    62  			glog.Error("cannot get transaction ", input.Txid, ": ", err)
    63  			return nil
    64  		}
    65  		if int(input.Vout) >= len(itx.Vout) {
    66  			glog.Error("Vout len in transaction ", input.Txid, " ", len(itx.Vout), " input.Vout=", input.Vout)
    67  			return nil
    68  		}
    69  		addrDesc, err = m.chain.GetChainParser().GetAddrDescFromVout(&itx.Vout[input.Vout])
    70  		if err != nil {
    71  			glog.Error("error in addrDesc in ", input.Txid, " ", input.Vout, ": ", err)
    72  			return nil
    73  		}
    74  	}
    75  	return &addrIndex{string(addrDesc), ^input.Vout}
    76  
    77  }
    78  
    79  func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan Outpoint, chanResult chan *addrIndex) ([]addrIndex, bool) {
    80  	tx, err := m.chain.GetTransactionForMempool(txid)
    81  	if err != nil {
    82  		glog.Error("cannot get transaction ", txid, ": ", err)
    83  		return nil, false
    84  	}
    85  	glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
    86  	io := make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))
    87  	for _, output := range tx.Vout {
    88  		addrDesc, err := m.chain.GetChainParser().GetAddrDescFromVout(&output)
    89  		if err != nil {
    90  			glog.Error("error in addrDesc in ", txid, " ", output.N, ": ", err)
    91  			continue
    92  		}
    93  		if len(addrDesc) > 0 {
    94  			io = append(io, addrIndex{string(addrDesc), int32(output.N)})
    95  		}
    96  		if m.OnNewTxAddr != nil {
    97  			m.OnNewTxAddr(tx, addrDesc)
    98  		}
    99  	}
   100  	dispatched := 0
   101  	for _, input := range tx.Vin {
   102  		if input.Coinbase != "" {
   103  			continue
   104  		}
   105  		o := Outpoint{input.Txid, int32(input.Vout)}
   106  	loop:
   107  		for {
   108  			select {
   109  			// store as many processed results as possible
   110  			case ai := <-chanResult:
   111  				if ai != nil {
   112  					io = append(io, *ai)
   113  				}
   114  				dispatched--
   115  			// send input to be processed
   116  			case chanInput <- o:
   117  				dispatched++
   118  				break loop
   119  			}
   120  		}
   121  	}
   122  	for i := 0; i < dispatched; i++ {
   123  		ai := <-chanResult
   124  		if ai != nil {
   125  			io = append(io, *ai)
   126  		}
   127  	}
   128  	return io, true
   129  }
   130  
   131  // Resync gets mempool transactions and maps outputs to transactions.
   132  // Resync is not reentrant, it should be called from a single thread.
   133  // Read operations (GetTransactions) are safe.
   134  func (m *MempoolBitcoinType) Resync() (int, error) {
   135  	start := time.Now()
   136  	glog.V(1).Info("mempool: resync")
   137  	txs, err := m.chain.GetMempoolTransactions()
   138  	if err != nil {
   139  		return 0, err
   140  	}
   141  	glog.V(2).Info("mempool: resync ", len(txs), " txs")
   142  	onNewEntry := func(txid string, entry txEntry) {
   143  		if len(entry.addrIndexes) > 0 {
   144  			m.mux.Lock()
   145  			m.txEntries[txid] = entry
   146  			for _, si := range entry.addrIndexes {
   147  				m.addrDescToTx[si.addrDesc] = append(m.addrDescToTx[si.addrDesc], Outpoint{txid, si.n})
   148  			}
   149  			m.mux.Unlock()
   150  		}
   151  	}
   152  	txsMap := make(map[string]struct{}, len(txs))
   153  	dispatched := 0
   154  	txTime := uint32(time.Now().Unix())
   155  	// get transaction in parallel using goroutines created in NewUTXOMempool
   156  	for _, txid := range txs {
   157  		txsMap[txid] = struct{}{}
   158  		_, exists := m.txEntries[txid]
   159  		if !exists {
   160  		loop:
   161  			for {
   162  				select {
   163  				// store as many processed transactions as possible
   164  				case tio := <-m.chanAddrIndex:
   165  					onNewEntry(tio.txid, txEntry{tio.io, txTime})
   166  					dispatched--
   167  				// send transaction to be processed
   168  				case m.chanTxid <- txid:
   169  					dispatched++
   170  					break loop
   171  				}
   172  			}
   173  		}
   174  	}
   175  	for i := 0; i < dispatched; i++ {
   176  		tio := <-m.chanAddrIndex
   177  		onNewEntry(tio.txid, txEntry{tio.io, txTime})
   178  	}
   179  
   180  	for txid, entry := range m.txEntries {
   181  		if _, exists := txsMap[txid]; !exists {
   182  			m.mux.Lock()
   183  			m.removeEntryFromMempool(txid, entry)
   184  			m.mux.Unlock()
   185  		}
   186  	}
   187  	glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txEntries), " transactions in mempool")
   188  	return len(m.txEntries), nil
   189  }