Output index uses abstract identifier instead of outputScript
This commit is contained in:
parent
fab41105b2
commit
fbfb33cb5d
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user