Output index uses abstract identifier instead of outputScript

This commit is contained in:
Jakub Matys 2018-03-20 15:58:35 +01:00
parent fab41105b2
commit fbfb33cb5d
8 changed files with 77 additions and 65 deletions

View File

@ -33,6 +33,22 @@ func GetChainParams(chain string) *chaincfg.Params {
return &chaincfg.MainNetParams 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 // AddressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinBlockParser) AddressToOutputScript(address string) ([]byte, error) { func (p *BitcoinBlockParser) AddressToOutputScript(address string) ([]byte, error) {
da, err := btcutil.DecodeAddress(address, p.Params) da, err := btcutil.DecodeAddress(address, p.Params)

View File

@ -531,9 +531,9 @@ func (b *BitcoinRPC) ResyncMempool(onNewTxAddr func(txid string, addr string)) e
return b.Mempool.Resync(onNewTxAddr) return b.Mempool.Resync(onNewTxAddr)
} }
// GetMempoolTransactions returns slice of mempool transactions for given output script. // GetMempoolTransactions returns slice of mempool transactions for given address.
func (b *BitcoinRPC) GetMempoolTransactions(outputScript []byte) ([]string, error) { func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) {
return b.Mempool.GetTransactions(outputScript) return b.Mempool.GetTransactions(address)
} }
// GetMempoolSpentOutput returns transaction in mempool which spends given outpoint // GetMempoolSpentOutput returns transaction in mempool which spends given outpoint

View File

@ -2,13 +2,13 @@ package bchain
import ( import (
"blockbook/common" "blockbook/common"
"encoding/hex"
"sync" "sync"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
) )
// TODO rename
type scriptIndex struct { type scriptIndex struct {
script string script string
n uint32 n uint32
@ -20,31 +20,36 @@ type outpoint struct {
} }
type inputOutput struct { type inputOutput struct {
outputScripts []scriptIndex outputs []scriptIndex
inputs []outpoint inputs []outpoint
} }
// Mempool is mempool handle. // Mempool is mempool handle.
type Mempool struct { type Mempool struct {
chain BlockChain chain BlockChain
chainParser BlockChainParser
mux sync.Mutex mux sync.Mutex
txToInputOutput map[string]inputOutput txToInputOutput map[string]inputOutput
scriptToTx map[string][]outpoint scriptToTx map[string][]outpoint // TODO rename all occurences
inputs map[outpoint]string inputs map[outpoint]string
metrics *common.Metrics metrics *common.Metrics
} }
// NewMempool creates new mempool handler. // NewMempool creates new mempool handler.
func NewMempool(chain BlockChain, metrics *common.Metrics) *Mempool { 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. // 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() m.mux.Lock()
defer m.mux.Unlock() defer m.mux.Unlock()
scriptHex := hex.EncodeToString(outputScript) buf, err := m.chainParser.GetUIDFromAddress(address)
outpoints := m.scriptToTx[scriptHex] if err != nil {
return nil, err
}
outid := m.chainParser.UnpackUID(buf)
outpoints := m.scriptToTx[outid]
txs := make([]string, 0, len(outpoints)+len(outpoints)/2) txs := make([]string, 0, len(outpoints)+len(outpoints)/2)
for _, o := range outpoints { for _, o := range outpoints {
txs = append(txs, o.txid) 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) glog.Error("cannot get transaction ", txid, ": ", err)
continue continue
} }
io.outputScripts = make([]scriptIndex, 0, len(tx.Vout)) io.outputs = make([]scriptIndex, 0, len(tx.Vout))
for _, output := range tx.Vout { for _, output := range tx.Vout {
outputScript := output.ScriptPubKey.Hex outid := m.chainParser.GetUIDFromVout(&output)
if outputScript != "" { if outid != "" {
io.outputScripts = append(io.outputScripts, scriptIndex{outputScript, output.N}) io.outputs = append(io.outputs, scriptIndex{outid, output.N})
} }
if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 { if onNewTxAddr != nil && len(output.ScriptPubKey.Addresses) == 1 {
onNewTxAddr(tx.Txid, output.ScriptPubKey.Addresses[0]) 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 newTxToInputOutput[txid] = io
for _, si := range io.outputScripts { for _, si := range io.outputs {
newScriptToTx[si.script] = append(newScriptToTx[si.script], outpoint{txid, si.n}) newScriptToTx[si.script] = append(newScriptToTx[si.script], outpoint{txid, si.n})
} }
for _, i := range io.inputs { for _, i := range io.inputs {

View File

@ -103,7 +103,7 @@ type BlockChain interface {
SendRawTransaction(tx string) (string, error) SendRawTransaction(tx string) (string, error)
// mempool // mempool
ResyncMempool(onNewTxAddr func(txid string, addr string)) error ResyncMempool(onNewTxAddr func(txid string, addr string)) error
GetMempoolTransactions(outputScript []byte) ([]string, error) GetMempoolTransactions(address string) ([]string, error)
GetMempoolSpentOutput(outputTxid string, vout uint32) string GetMempoolSpentOutput(outputTxid string, vout uint32) string
GetMempoolEntry(txid string) (*MempoolEntry, error) GetMempoolEntry(txid string) (*MempoolEntry, error)
// parser // parser
@ -111,6 +111,10 @@ type BlockChain interface {
} }
type BlockChainParser 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) AddressToOutputScript(address string) ([]byte, error)
OutputScriptToAddresses(script []byte) ([]string, error) OutputScriptToAddresses(script []byte) ([]string, error)
ParseTx(b []byte) (*Tx, error) ParseTx(b []byte) (*Tx, error)

View File

@ -229,12 +229,7 @@ func main() {
address := *queryAddress address := *queryAddress
if address != "" { if address != "" {
script, err := chain.GetChainParser().AddressToOutputScript(address) if err = index.GetTransactions(address, height, until, printResult); err != nil {
if err != nil {
glog.Error("GetTransactions ", err)
return
}
if err = index.GetTransactions(script, height, until, printResult); err != nil {
glog.Error("GetTransactions ", err) glog.Error("GetTransactions ", err)
return return
} }

View File

@ -136,18 +136,19 @@ func (d *RocksDB) Reopen() error {
return nil 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. // 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) { 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 { if err != nil {
return err return err
} }
kstop, err := packOutputKey(outputScript, higher) kstop, err := packOutputKey(outid, higher)
if err != nil { if err != nil {
return err return err
} }
@ -240,12 +241,12 @@ func (d *RocksDB) writeOutputs(wb *gorocksdb.WriteBatch, block *bchain.Block, op
for _, tx := range block.Txs { for _, tx := range block.Txs {
for _, output := range tx.Vout { for _, output := range tx.Vout {
outputScript := output.ScriptPubKey.Hex outid := d.chainParser.GetUIDFromVout(&output)
if outputScript != "" { if outid != "" {
if len(outputScript) > 1024 { if len(outid) > 1024 {
glog.Infof("block %d, skipping outputScript of length %d", block.Height, len(outputScript)/2) glog.Infof("block %d, skipping outid of length %d", block.Height, len(outid)/2)
} else { } else {
records[outputScript] = append(records[outputScript], outpoint{ records[outid] = append(records[outid], outpoint{
txid: tx.Txid, txid: tx.Txid,
vout: output.N, vout: output.N,
}) })
@ -262,15 +263,15 @@ func (d *RocksDB) writeOutputs(wb *gorocksdb.WriteBatch, block *bchain.Block, op
} }
} }
for outputScript, outpoints := range records { for outid, outpoints := range records {
bOutputScript, err := packOutputScript(outputScript) bOutid, err := d.chainParser.PackUID(outid)
if err != nil { 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 continue
} }
key, err := packOutputKey(bOutputScript, block.Height) key, err := packOutputKey(bOutid, block.Height)
if err != nil { 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 continue
} }
val, err := packOutputValue(outpoints) val, err := packOutputValue(outpoints)
@ -663,11 +664,3 @@ func packBlockValue(hash string) ([]byte, error) {
func unpackBlockValue(buf []byte) (string, error) { func unpackBlockValue(buf []byte) (string, error) {
return hex.EncodeToString(buf), nil return hex.EncodeToString(buf), nil
} }
func packOutputScript(script string) ([]byte, error) {
return hex.DecodeString(script)
}
func unpackOutputScript(buf []byte) string {
return hex.EncodeToString(buf)
}

View File

@ -5,6 +5,7 @@ import (
"blockbook/db" "blockbook/db"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"os" "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) { func (s *HTTPServer) getAddress(r *http.Request) (address string, err error) {
address = mux.Vars(r)["address"] address, ok := mux.Vars(r)["address"]
script, err = s.chainParser.AddressToOutputScript(address) if !ok {
err = errors.New("Empty address")
}
return return
} }
func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string, script []byte, lower, higher uint32, err error) { func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string, lower, higher uint32, err error) {
address, script, err = s.getAddress(r) address, err = s.getAddress(r)
if err != nil { if err != nil {
return return
} }
@ -154,7 +157,7 @@ func (s *HTTPServer) getAddressAndHeightRange(r *http.Request) (address string,
if err != nil { if err != nil {
return return
} }
return address, script, uint32(lower64), uint32(higher64), err return address, uint32(lower64), uint32(higher64), err
} }
type transactionList struct { type transactionList struct {
@ -162,11 +165,11 @@ type transactionList struct {
} }
func (s *HTTPServer) unconfirmedTransactions(w http.ResponseWriter, r *http.Request) { func (s *HTTPServer) unconfirmedTransactions(w http.ResponseWriter, r *http.Request) {
address, script, err := s.getAddress(r) address, err := s.getAddress(r)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address)) respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address))
} }
txs, err := s.chain.GetMempoolTransactions(script) txs, err := s.chain.GetMempoolTransactions(address)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprint("unconfirmedTransactions for address", address)) 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) { 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 { if err != nil {
respondError(w, err, fmt.Sprint("confirmedTransactions for address", address)) respondError(w, err, fmt.Sprint("confirmedTransactions for address", address))
} }
txList := transactionList{} 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) txList.Txid = append(txList.Txid, txid)
return nil 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) { 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 { if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address)) respondError(w, err, fmt.Sprint("transactions for address", address))
} }
txList := transactionList{} 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) txList.Txid = append(txList.Txid, txid)
if isOutput { if isOutput {
input := s.chain.GetMempoolSpentOutput(txid, vout) input := s.chain.GetMempoolSpentOutput(txid, vout)
@ -209,7 +212,7 @@ func (s *HTTPServer) transactions(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address)) respondError(w, err, fmt.Sprint("transactions for address", address))
} }
txs, err := s.chain.GetMempoolTransactions(script) txs, err := s.chain.GetMempoolTransactions(address)
if err != nil { if err != nil {
respondError(w, err, fmt.Sprint("transactions for address", address)) respondError(w, err, fmt.Sprint("transactions for address", address))
} }

View File

@ -258,12 +258,8 @@ func (s *SocketIoServer) getAddressTxids(addr []string, rr *reqRange) (res resul
txids := make([]string, 0) txids := make([]string, 0)
lower, higher := uint32(rr.To), uint32(rr.Start) lower, higher := uint32(rr.To), uint32(rr.Start)
for _, address := range addr { for _, address := range addr {
script, err := s.chainParser.AddressToOutputScript(address)
if err != nil {
return res, err
}
if !rr.QueryMempoolOnly { 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) txids = append(txids, txid)
if isOutput && rr.QueryMempol { if isOutput && rr.QueryMempol {
input := s.chain.GetMempoolSpentOutput(txid, vout) 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 { if rr.QueryMempoolOnly || rr.QueryMempol {
mtxids, err := s.chain.GetMempoolTransactions(script) mtxids, err := s.chain.GetMempoolTransactions(address)
if err != nil { if err != nil {
return res, err return res, err
} }