From 7281fb27b14a3c9770cbbf5271a453d856768dbc Mon Sep 17 00:00:00 2001 From: Martin Boehm Date: Tue, 1 May 2018 23:43:36 +0200 Subject: [PATCH] Implement UTXO mempool indexv2 --- bchain/mempool_nonutxo.go | 5 ++-- bchain/mempool_utxo.go | 60 ++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/bchain/mempool_nonutxo.go b/bchain/mempool_nonutxo.go index 7a18ca4a..946dd89b 100644 --- a/bchain/mempool_nonutxo.go +++ b/bchain/mempool_nonutxo.go @@ -55,8 +55,9 @@ func (m *NonUTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) erro return err } parser := m.chain.GetChainParser() - newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+1) - newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+1) + // allocate slightly larger capacity of the maps + newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5) + newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5) for _, txid := range txs { io, exists := m.txToInputOutput[txid] if !exists { diff --git a/bchain/mempool_utxo.go b/bchain/mempool_utxo.go index d653e0a9..0ad1836f 100644 --- a/bchain/mempool_utxo.go +++ b/bchain/mempool_utxo.go @@ -18,21 +18,15 @@ type outpoint struct { vout int32 } -type inputOutput struct { - outputs []addrIndex - inputs []outpoint -} - // UTXOMempool is mempool handle. type UTXOMempool struct { chain BlockChain mux sync.Mutex - txToInputOutput map[string]inputOutput + txToInputOutput map[string][]addrIndex addrIDToTx map[string][]outpoint - inputs map[outpoint]string } -// NewMempool creates new mempool handler. +// NewUTXOMempool creates new mempool handler. func NewUTXOMempool(chain BlockChain) *UTXOMempool { return &UTXOMempool{chain: chain} } @@ -47,29 +41,18 @@ func (m *UTXOMempool) GetTransactions(address string) ([]string, error) { m.mux.Lock() defer m.mux.Unlock() outpoints := m.addrIDToTx[string(addrID)] - txs := make([]string, 0, len(outpoints)+len(outpoints)/2) + txs := make([]string, 0, len(outpoints)) for _, o := range outpoints { txs = append(txs, o.txid) - i := m.inputs[o] - if i != "" { - txs = append(txs, i) - } } return txs, nil } -// GetSpentOutput returns transaction which spends given outpoint -func (m *UTXOMempool) GetSpentOutput(outputTxid string, vout uint32) string { - o := outpoint{txid: outputTxid, vout: int32(vout)} - return m.inputs[o] -} - -func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string]inputOutput, newAddrIDToTx map[string][]outpoint, newInputs map[outpoint]string) { +func (m *UTXOMempool) updateMappings(newTxToInputOutput map[string][]addrIndex, newAddrIDToTx map[string][]outpoint) { m.mux.Lock() defer m.mux.Unlock() m.txToInputOutput = newTxToInputOutput m.addrIDToTx = newAddrIDToTx - m.inputs = newInputs } // Resync gets mempool transactions and maps outputs to transactions. @@ -83,9 +66,9 @@ func (m *UTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) error { return err } parser := m.chain.GetChainParser() - newTxToInputOutput := make(map[string]inputOutput, len(m.txToInputOutput)+1) - newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+1) - newInputs := make(map[outpoint]string, len(m.inputs)+1) + // allocate slightly larger capacity of the maps + newTxToInputOutput := make(map[string][]addrIndex, len(m.txToInputOutput)+5) + newAddrIDToTx := make(map[string][]outpoint, len(m.addrIDToTx)+5) for _, txid := range txs { io, exists := m.txToInputOutput[txid] if !exists { @@ -94,7 +77,7 @@ func (m *UTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) error { glog.Error("cannot get transaction ", txid, ": ", err) continue } - io.outputs = make([]addrIndex, 0, len(tx.Vout)) + io = make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin)) for _, output := range tx.Vout { addrID, err := parser.GetAddrIDFromVout(&output) if err != nil { @@ -102,29 +85,40 @@ func (m *UTXOMempool) Resync(onNewTxAddr func(txid string, addr string)) error { continue } if len(addrID) > 0 { - io.outputs = append(io.outputs, addrIndex{string(addrID), int32(output.N)}) + io = append(io, addrIndex{string(addrID), int32(output.N)}) } if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 { onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0]) } } - io.inputs = make([]outpoint, 0, len(tx.Vin)) for _, input := range tx.Vin { if input.Coinbase != "" { continue } - io.inputs = append(io.inputs, outpoint{input.Txid, int32(input.Vout)}) + // TODO - possibly get from DB unspenttxs - however some output txs can be in mempool only + itx, err := m.chain.GetTransaction(input.Txid) + if err != nil { + glog.Error("cannot get transaction ", input.Txid, ": ", err) + continue + } + if int(input.Vout) >= len(itx.Vout) { + glog.Error("Vout len in transaction ", input.Txid, " ", len(itx.Vout), " input.Vout=", input.Vout) + continue + } + addrID, err := parser.GetAddrIDFromVout(&itx.Vout[input.Vout]) + if err != nil { + glog.Error("error in addrID in ", input.Txid, " ", input.Vout, ": ", err) + continue + } + io = append(io, addrIndex{string(addrID), int32(^input.Vout)}) } } newTxToInputOutput[txid] = io - for _, si := range io.outputs { + for _, si := range io { newAddrIDToTx[si.addrID] = append(newAddrIDToTx[si.addrID], outpoint{txid, si.n}) } - for _, i := range io.inputs { - newInputs[i] = txid - } } - m.updateMappings(newTxToInputOutput, newAddrIDToTx, newInputs) + m.updateMappings(newTxToInputOutput, newAddrIDToTx) glog.Info("Mempool: resync finished in ", time.Since(start), ", ", len(m.txToInputOutput), " transactions in mempool") return nil }