From fbfb33cb5de625f261998bf800e15b0bd8cecf4c Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Tue, 20 Mar 2018 15:58:35 +0100 Subject: [PATCH] Output index uses abstract identifier instead of outputScript --- bchain/coins/btc/bitcoinparser.go | 16 +++++++++++++ bchain/coins/btc/bitcoinrpc.go | 6 ++--- bchain/mempool.go | 31 +++++++++++++----------- bchain/types.go | 6 ++++- blockbook.go | 7 +----- db/rocksdb.go | 39 +++++++++++++------------------ server/https.go | 29 ++++++++++++----------- server/socketio.go | 8 ++----- 8 files changed, 77 insertions(+), 65 deletions(-) diff --git a/bchain/coins/btc/bitcoinparser.go b/bchain/coins/btc/bitcoinparser.go index d60a41d5..415082a2 100644 --- a/bchain/coins/btc/bitcoinparser.go +++ b/bchain/coins/btc/bitcoinparser.go @@ -33,6 +33,22 @@ func GetChainParams(chain string) *chaincfg.Params { return &chaincfg.MainNetParams } +func (p *BitcoinBlockParser) GetUIDFromVout(output *bchain.Vout) string { + return output.ScriptPubKey.Hex +} + +func (p *BitcoinBlockParser) GetUIDFromAddress(address string) ([]byte, error) { + return p.AddressToOutputScript(address) +} + +func (p *BitcoinBlockParser) PackUID(script string) ([]byte, error) { + return hex.DecodeString(script) +} + +func (p *BitcoinBlockParser) UnpackUID(buf []byte) string { + return hex.EncodeToString(buf) +} + // AddressToOutputScript converts bitcoin address to ScriptPubKey func (p *BitcoinBlockParser) AddressToOutputScript(address string) ([]byte, error) { da, err := btcutil.DecodeAddress(address, p.Params) diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index a6051f49..cc4e6c9b 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -531,9 +531,9 @@ func (b *BitcoinRPC) ResyncMempool(onNewTxAddr func(txid string, addr string)) e return b.Mempool.Resync(onNewTxAddr) } -// GetMempoolTransactions returns slice of mempool transactions for given output script. -func (b *BitcoinRPC) GetMempoolTransactions(outputScript []byte) ([]string, error) { - return b.Mempool.GetTransactions(outputScript) +// GetMempoolTransactions returns slice of mempool transactions for given address. +func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) { + return b.Mempool.GetTransactions(address) } // GetMempoolSpentOutput returns transaction in mempool which spends given outpoint diff --git a/bchain/mempool.go b/bchain/mempool.go index 16d9a38a..37155fda 100644 --- a/bchain/mempool.go +++ b/bchain/mempool.go @@ -2,13 +2,13 @@ package bchain import ( "blockbook/common" - "encoding/hex" "sync" "time" "github.com/golang/glog" ) +// TODO rename type scriptIndex struct { script string n uint32 @@ -20,31 +20,36 @@ type outpoint struct { } type inputOutput struct { - outputScripts []scriptIndex - inputs []outpoint + outputs []scriptIndex + inputs []outpoint } // Mempool is mempool handle. type Mempool struct { chain BlockChain + chainParser BlockChainParser mux sync.Mutex txToInputOutput map[string]inputOutput - scriptToTx map[string][]outpoint + scriptToTx map[string][]outpoint // TODO rename all occurences inputs map[outpoint]string metrics *common.Metrics } // NewMempool creates new mempool handler. func NewMempool(chain BlockChain, metrics *common.Metrics) *Mempool { - return &Mempool{chain: chain, metrics: metrics} + return &Mempool{chain: chain, chainParser: chain.GetChainParser(), metrics: metrics} } // GetTransactions returns slice of mempool transactions for given output script. -func (m *Mempool) GetTransactions(outputScript []byte) ([]string, error) { +func (m *Mempool) GetTransactions(address string) ([]string, error) { m.mux.Lock() defer m.mux.Unlock() - scriptHex := hex.EncodeToString(outputScript) - outpoints := m.scriptToTx[scriptHex] + buf, err := m.chainParser.GetUIDFromAddress(address) + if err != nil { + return nil, err + } + outid := m.chainParser.UnpackUID(buf) + outpoints := m.scriptToTx[outid] txs := make([]string, 0, len(outpoints)+len(outpoints)/2) for _, o := range outpoints { txs = append(txs, o.txid) @@ -93,11 +98,11 @@ func (m *Mempool) Resync(onNewTxAddr func(txid string, addr string)) error { glog.Error("cannot get transaction ", txid, ": ", err) continue } - io.outputScripts = make([]scriptIndex, 0, len(tx.Vout)) + io.outputs = make([]scriptIndex, 0, len(tx.Vout)) for _, output := range tx.Vout { - outputScript := output.ScriptPubKey.Hex - if outputScript != "" { - io.outputScripts = append(io.outputScripts, scriptIndex{outputScript, output.N}) + outid := m.chainParser.GetUIDFromVout(&output) + if outid != "" { + io.outputs = append(io.outputs, scriptIndex{outid, output.N}) } if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 { onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0]) @@ -112,7 +117,7 @@ func (m *Mempool) Resync(onNewTxAddr func(txid string, addr string)) error { } } newTxToInputOutput[txid] = io - for _, si := range io.outputScripts { + for _, si := range io.outputs { newScriptToTx[si.script] = append(newScriptToTx[si.script], outpoint{txid, si.n}) } for _, i := range io.inputs { diff --git a/bchain/types.go b/bchain/types.go index 79e9a471..8d982021 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -103,7 +103,7 @@ type BlockChain interface { SendRawTransaction(tx string) (string, error) // mempool ResyncMempool(onNewTxAddr func(txid string, addr string)) error - GetMempoolTransactions(outputScript []byte) ([]string, error) + GetMempoolTransactions(address string) ([]string, error) GetMempoolSpentOutput(outputTxid string, vout uint32) string GetMempoolEntry(txid string) (*MempoolEntry, error) // parser @@ -111,6 +111,10 @@ type BlockChain interface { } type BlockChainParser interface { + GetUIDFromVout(output *Vout) string + GetUIDFromAddress(address string) ([]byte, error) + PackUID(script string) ([]byte, error) + UnpackUID(buf []byte) string AddressToOutputScript(address string) ([]byte, error) OutputScriptToAddresses(script []byte) ([]string, error) ParseTx(b []byte) (*Tx, error) diff --git a/blockbook.go b/blockbook.go index 8543ab22..a0266a29 100644 --- a/blockbook.go +++ b/blockbook.go @@ -229,12 +229,7 @@ func main() { address := *queryAddress if address != "" { - script, err := chain.GetChainParser().AddressToOutputScript(address) - if err != nil { - glog.Error("GetTransactions ", err) - return - } - if err = index.GetTransactions(script, height, until, printResult); err != nil { + if err = index.GetTransactions(address, height, until, printResult); err != nil { glog.Error("GetTransactions ", err) return } diff --git a/db/rocksdb.go b/db/rocksdb.go index 2098b77c..43d01260 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -136,18 +136,19 @@ func (d *RocksDB) Reopen() error { return nil } -// GetTransactions finds all input/output transactions for address specified by outputScript. +// GetTransactions finds all input/output transactions for address // Transaction are passed to callback function. -func (d *RocksDB) GetTransactions(outputScript []byte, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) { +func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string, vout uint32, isOutput bool) error) (err error) { if glog.V(1) { - glog.Infof("rocksdb: address get %s %d-%d ", unpackOutputScript(outputScript), lower, higher) + glog.Infof("rocksdb: address get %s %d-%d ", address, lower, higher) } + outid, err := d.chainParser.GetUIDFromAddress(address) - kstart, err := packOutputKey(outputScript, lower) + kstart, err := packOutputKey(outid, lower) if err != nil { return err } - kstop, err := packOutputKey(outputScript, higher) + kstop, err := packOutputKey(outid, higher) if err != nil { return err } @@ -240,12 +241,12 @@ func (d *RocksDB) writeOutputs(wb *gorocksdb.WriteBatch, block *bchain.Block, op for _, tx := range block.Txs { for _, output := range tx.Vout { - outputScript := output.ScriptPubKey.Hex - if outputScript != "" { - if len(outputScript) > 1024 { - glog.Infof("block %d, skipping outputScript of length %d", block.Height, len(outputScript)/2) + outid := d.chainParser.GetUIDFromVout(&output) + if outid != "" { + if len(outid) > 1024 { + glog.Infof("block %d, skipping outid of length %d", block.Height, len(outid)/2) } else { - records[outputScript] = append(records[outputScript], outpoint{ + records[outid] = append(records[outid], outpoint{ txid: tx.Txid, vout: output.N, }) @@ -262,15 +263,15 @@ func (d *RocksDB) writeOutputs(wb *gorocksdb.WriteBatch, block *bchain.Block, op } } - for outputScript, outpoints := range records { - bOutputScript, err := packOutputScript(outputScript) + for outid, outpoints := range records { + bOutid, err := d.chainParser.PackUID(outid) if err != nil { - glog.Warningf("rocksdb: packOutputScript: %v - %d %s", err, block.Height, outputScript) + glog.Warningf("rocksdb: packOutputID: %v - %d %s", err, block.Height, outid) continue } - key, err := packOutputKey(bOutputScript, block.Height) + key, err := packOutputKey(bOutid, block.Height) if err != nil { - glog.Warningf("rocksdb: packOutputKey: %v - %d %s", err, block.Height, outputScript) + glog.Warningf("rocksdb: packOutputKey: %v - %d %s", err, block.Height, outid) continue } val, err := packOutputValue(outpoints) @@ -663,11 +664,3 @@ func packBlockValue(hash string) ([]byte, error) { func unpackBlockValue(buf []byte) (string, error) { return hex.EncodeToString(buf), nil } - -func packOutputScript(script string) ([]byte, error) { - return hex.DecodeString(script) -} - -func unpackOutputScript(buf []byte) string { - return hex.EncodeToString(buf) -} diff --git a/server/https.go b/server/https.go index c592e829..e135818d 100644 --- a/server/https.go +++ b/server/https.go @@ -5,6 +5,7 @@ import ( "blockbook/db" "context" "encoding/json" + "errors" "fmt" "net/http" "os" @@ -135,14 +136,16 @@ func (s *HTTPServer) blockHash(w http.ResponseWriter, r *http.Request) { } } -func (s *HTTPServer) getAddress(r *http.Request) (address string, script []byte, err error) { - address = mux.Vars(r)["address"] - script, err = s.chainParser.AddressToOutputScript(address) +func (s *HTTPServer) getAddress(r *http.Request) (address string, err error) { + address, ok := mux.Vars(r)["address"] + if !ok { + err = errors.New("Empty address") + } return } -func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string, script []byte, lower, higher uint32, err error) { - address, script, err = s.getAddress(r) +func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string, lower, higher uint32, err error) { + address, err = s.getAddress(r) if err != nil { return } @@ -154,7 +157,7 @@ func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string, if err != nil { return } - return address, script, uint32(lower64), uint32(higher64), err + return address, uint32(lower64), uint32(higher64), err } type transactionList struct { @@ -162,11 +165,11 @@ type transactionList struct { } func (s *HTTPServer) unconfirmedTransactions(w http.ResponseWriter, r *http.Request) { - address, script, err := s.getAddress(r) + address, err := s.getAddress(r) if err != nil { respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address)) } - txs, err := s.chain.GetMempoolTransactions(script) + txs, err := s.chain.GetMempoolTransactions(address) if err != nil { respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address)) } @@ -175,12 +178,12 @@ func (s *HTTPServer) unconfirmedTransactions(w http.ResponseWriter, r *http.Requ } func (s *HTTPServer) confirmedTransactions(w http.ResponseWriter, r *http.Request) { - address, script, lower, higher, err := s.getAddressAndHeightRange(r) + address, lower, higher, err := s.getAddressAndHeightRange(r) if err != nil { respondError(w, err, fmt.Sprint("confirmedTransactions for address", address)) } txList := transactionList{} - err = s.db.GetTransactions(script, lower, higher, func(txid string, vout uint32, isOutput bool) error { + err = s.db.GetTransactions(address, lower, higher, func(txid string, vout uint32, isOutput bool) error { txList.Txid = append(txList.Txid, txid) return nil }) @@ -191,12 +194,12 @@ func (s *HTTPServer) confirmedTransactions(w http.ResponseWriter, r *http.Reques } func (s *HTTPServer) transactions(w http.ResponseWriter, r *http.Request) { - address, script, lower, higher, err := s.getAddressAndHeightRange(r) + address, lower, higher, err := s.getAddressAndHeightRange(r) if err != nil { respondError(w, err, fmt.Sprint("transactions for address", address)) } txList := transactionList{} - err = s.db.GetTransactions(script, lower, higher, func(txid string, vout uint32, isOutput bool) error { + err = s.db.GetTransactions(address, lower, higher, func(txid string, vout uint32, isOutput bool) error { txList.Txid = append(txList.Txid, txid) if isOutput { input := s.chain.GetMempoolSpentOutput(txid, vout) @@ -209,7 +212,7 @@ func (s *HTTPServer) transactions(w http.ResponseWriter, r *http.Request) { if err != nil { respondError(w, err, fmt.Sprint("transactions for address", address)) } - txs, err := s.chain.GetMempoolTransactions(script) + txs, err := s.chain.GetMempoolTransactions(address) if err != nil { respondError(w, err, fmt.Sprint("transactions for address", address)) } diff --git a/server/socketio.go b/server/socketio.go index 94913427..bd9d39e6 100644 --- a/server/socketio.go +++ b/server/socketio.go @@ -258,12 +258,8 @@ func (s *SocketIoServer) getAddressTxids(addr []string, rr *reqRange) (res resul txids := make([]string, 0) lower, higher := uint32(rr.To), uint32(rr.Start) for _, address := range addr { - script, err := s.chainParser.AddressToOutputScript(address) - if err != nil { - return res, err - } if !rr.QueryMempoolOnly { - err = s.db.GetTransactions(script, lower, higher, func(txid string, vout uint32, isOutput bool) error { + err = s.db.GetTransactions(address, lower, higher, func(txid string, vout uint32, isOutput bool) error { txids = append(txids, txid) if isOutput && rr.QueryMempol { input := s.chain.GetMempoolSpentOutput(txid, vout) @@ -278,7 +274,7 @@ func (s *SocketIoServer) getAddressTxids(addr []string, rr *reqRange) (res resul } } if rr.QueryMempoolOnly || rr.QueryMempol { - mtxids, err := s.chain.GetMempoolTransactions(script) + mtxids, err := s.chain.GetMempoolTransactions(address) if err != nil { return res, err }