Use output script (ScriptPubKey) as key to output transactions

pull/1/head
Martin Boehm 2018-01-29 23:25:40 +01:00
parent 858f196484
commit 3dacffda59
5 changed files with 74 additions and 73 deletions

View File

@ -68,13 +68,13 @@ func (p *BitcoinBlockParser) ParseBlock(b []byte) (*Block, error) {
} }
vout := make([]Vout, len(t.TxOut)) vout := make([]Vout, len(t.TxOut))
for i, out := range t.TxOut { for i, out := range t.TxOut {
addrs, err := p.parseOutputScript(out.PkScript) // addrs, err := p.parseOutputScript(out.PkScript)
if err != nil { // if err != nil {
addrs = []string{} // addrs = []string{}
} // }
s := ScriptPubKey{ s := ScriptPubKey{
Hex: hex.EncodeToString(out.PkScript), Hex: hex.EncodeToString(out.PkScript),
Addresses: addrs, // missing Addresses,
// missing: Asm, // missing: Asm,
// missing: Type, // missing: Type,
} }

View File

@ -1,5 +1,10 @@
package bitcoin package bitcoin
import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
)
type ScriptSig struct { type ScriptSig struct {
Asm string `json:"asm"` Asm string `json:"asm"`
Hex string `json:"hex"` Hex string `json:"hex"`
@ -26,11 +31,17 @@ type Vout struct {
ScriptPubKey ScriptPubKey `json:"scriptPubKey"` ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
} }
func (vout *Vout) GetAddress() string { // AddressToOutputScript converts bitcoin address to ScriptPubKey
if len(vout.ScriptPubKey.Addresses) != 1 { func AddressToOutputScript(address string) ([]byte, error) {
return "" // output address not intelligible da, err := btcutil.DecodeAddress(address, GetChainParams()[0])
if err != nil {
return nil, err
} }
return vout.ScriptPubKey.Addresses[0] script, err := txscript.PayToAddrScript(da)
if err != nil {
return nil, err
}
return script, nil
} }
type Tx struct { type Tx struct {

View File

@ -28,7 +28,7 @@ type Blockchain interface {
type Index interface { type Index interface {
GetBestBlock() (uint32, string, error) GetBestBlock() (uint32, string, error)
GetBlockHash(height uint32) (string, error) GetBlockHash(height uint32) (string, error)
GetTransactions(address string, lower uint32, higher uint32, fn func(txid string) error) error GetTransactions(outputScript []byte, lower uint32, higher uint32, fn func(txid string) error) error
ConnectBlock(block *bitcoin.Block) error ConnectBlock(block *bitcoin.Block) error
DisconnectBlock(block *bitcoin.Block) error DisconnectBlock(block *bitcoin.Block) error
DisconnectBlocks(lower uint32, higher uint32) error DisconnectBlocks(lower uint32, higher uint32) error
@ -147,7 +147,11 @@ func main() {
address := *queryAddress address := *queryAddress
if address != "" { if address != "" {
if err = db.GetTransactions(address, height, until, printResult); err != nil { script, err := bitcoin.AddressToOutputScript(address)
if err != nil {
log.Fatalf("GetTransactions %v", err)
}
if err = db.GetTransactions(script, height, until, printResult); err != nil {
log.Fatalf("GetTransactions %v", err) log.Fatalf("GetTransactions %v", err)
} }
} else { } else {

View File

@ -9,7 +9,6 @@ import (
"log" "log"
"github.com/bsm/go-vlq" "github.com/bsm/go-vlq"
"github.com/btcsuite/btcutil/base58"
"github.com/tecbot/gorocksdb" "github.com/tecbot/gorocksdb"
) )
@ -84,14 +83,16 @@ func (d *RocksDB) Close() error {
return nil return nil
} }
func (d *RocksDB) GetTransactions(address string, lower uint32, higher uint32, fn func(txid string) error) (err error) { // GetTransactions finds all input/output transactions for address specified by outputScript.
log.Printf("rocksdb: address get %s %d-%d ", address, lower, higher) // Transaction are passed to callback function.
func (d *RocksDB) GetTransactions(outputScript []byte, lower uint32, higher uint32, fn func(txid string) error) (err error) {
log.Printf("rocksdb: address get %s %d-%d ", unpackOutputScript(outputScript), lower, higher)
kstart, err := packOutputKey(address, lower) kstart, err := packOutputKey(outputScript, lower)
if err != nil { if err != nil {
return err return err
} }
kstop, err := packOutputKey(address, higher) kstop, err := packOutputKey(outputScript, higher)
if err != nil { if err != nil {
return err return err
} }
@ -192,9 +193,9 @@ func (d *RocksDB) writeOutputs(
for _, tx := range block.Txs { for _, tx := range block.Txs {
for _, output := range tx.Vout { for _, output := range tx.Vout {
address := output.GetAddress() outputScript := output.ScriptPubKey.Hex
if address != "" { if outputScript != "" {
records[address] = append(records[address], outpoint{ records[outputScript] = append(records[outputScript], outpoint{
txid: tx.Txid, txid: tx.Txid,
vout: output.N, vout: output.N,
}) })
@ -202,15 +203,20 @@ func (d *RocksDB) writeOutputs(
} }
} }
for address, outpoints := range records { for outputScript, outpoints := range records {
key, err := packOutputKey(address, block.Height) bOutputScript, err := packOutputScript(outputScript)
if err != nil { if err != nil {
log.Printf("rocksdb: warning: %v", err) log.Printf("rocksdb: packOutputScript warning: %v - %d %s", err, block.Height, outputScript)
continue
}
key, err := packOutputKey(bOutputScript, block.Height)
if err != nil {
log.Printf("rocksdb: packOutputKey warning: %v - %d %s", err, block.Height, outputScript)
continue continue
} }
val, err := packOutputValue(outpoints) val, err := packOutputValue(outpoints)
if err != nil { if err != nil {
log.Printf("rocksdb: warning: %v", err) log.Printf("rocksdb: packOutputValue warning: %v", err)
continue continue
} }
@ -225,14 +231,10 @@ func (d *RocksDB) writeOutputs(
return nil return nil
} }
func packOutputKey(address string, height uint32) ([]byte, error) { func packOutputKey(outputScript []byte, height uint32) ([]byte, error) {
baddress, err := packAddress(address)
if err != nil {
return nil, err
}
bheight := packUint(height) bheight := packUint(height)
buf := make([]byte, 0, len(baddress)+len(bheight)) buf := make([]byte, 0, len(outputScript)+len(bheight))
buf = append(buf, baddress...) buf = append(buf, outputScript...)
buf = append(buf, bheight...) buf = append(buf, bheight...)
return buf, nil return buf, nil
} }
@ -453,34 +455,6 @@ func unpackVaruint(buf []byte) (uint32, int) {
return uint32(i), ofs return uint32(i), ofs
} }
func packAddress(address string) ([]byte, error) {
var buf []byte
if len(address) == 42 || len(address) == 50 {
var err error
buf, err = hex.DecodeString(address)
if err != nil {
return nil, err
}
} else {
buf = base58.Decode(address)
}
if len(buf) == 25 {
return buf[:len(buf)-4], nil // Slice off the checksum
}
if len(buf) == 21 { // address without checksum
return buf, nil
}
return nil, ErrInvalidAddress
}
func unpackAddress(buf []byte) (string, error) {
if len(buf) < 2 {
return "", ErrInvalidAddress
}
return base58.CheckEncode(buf[1:], buf[0]), nil
}
func packTxid(txid string) ([]byte, error) { func packTxid(txid string) ([]byte, error) {
return hex.DecodeString(txid) return hex.DecodeString(txid)
} }
@ -496,3 +470,11 @@ 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

@ -1,6 +1,7 @@
package server package server
import ( import (
"blockbook/bitcoin"
"blockbook/db" "blockbook/db"
"context" "context"
"encoding/json" "encoding/json"
@ -120,24 +121,27 @@ func (s *HttpServer) transactions(w http.ResponseWriter, r *http.Request) {
type transactionList struct { type transactionList struct {
Txid []string `json:"txid"` Txid []string `json:"txid"`
} }
txList := transactionList{}
address := mux.Vars(r)["address"] address := mux.Vars(r)["address"]
higher, err := strconv.ParseUint(mux.Vars(r)["higher"], 10, 32) higher, err := strconv.ParseUint(mux.Vars(r)["higher"], 10, 32)
lower, err2 := strconv.ParseUint(mux.Vars(r)["lower"], 10, 32)
if err == nil && err2 == nil {
err = s.db.GetTransactions(address, uint32(lower), uint32(higher), func(txid string) error {
txList.Txid = append(txList.Txid, txid)
return nil
})
} else {
if err == nil {
err = err2
}
}
if err != nil { if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address)) respondError(w, err, fmt.Sprintf("address %s", address))
} else {
json.NewEncoder(w).Encode(txList)
} }
lower, err := strconv.ParseUint(mux.Vars(r)["lower"], 10, 32)
if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address))
}
script, err := bitcoin.AddressToOutputScript(address)
if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address))
}
txList := transactionList{}
err = s.db.GetTransactions(script, uint32(lower), uint32(higher), func(txid string) error {
txList.Txid = append(txList.Txid, txid)
return nil
})
if err != nil {
respondError(w, err, fmt.Sprintf("address %s", address))
}
json.NewEncoder(w).Encode(txList)
} }