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))
for i, out := range t.TxOut {
addrs, err := p.parseOutputScript(out.PkScript)
if err != nil {
addrs = []string{}
}
// addrs, err := p.parseOutputScript(out.PkScript)
// if err != nil {
// addrs = []string{}
// }
s := ScriptPubKey{
Hex: hex.EncodeToString(out.PkScript),
Addresses: addrs,
Hex: hex.EncodeToString(out.PkScript),
// missing Addresses,
// missing: Asm,
// missing: Type,
}

View File

@ -1,5 +1,10 @@
package bitcoin
import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
)
type ScriptSig struct {
Asm string `json:"asm"`
Hex string `json:"hex"`
@ -26,11 +31,17 @@ type Vout struct {
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
}
func (vout *Vout) GetAddress() string {
if len(vout.ScriptPubKey.Addresses) != 1 {
return "" // output address not intelligible
// AddressToOutputScript converts bitcoin address to ScriptPubKey
func AddressToOutputScript(address string) ([]byte, error) {
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 {

View File

@ -28,7 +28,7 @@ type Blockchain interface {
type Index interface {
GetBestBlock() (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
DisconnectBlock(block *bitcoin.Block) error
DisconnectBlocks(lower uint32, higher uint32) error
@ -147,7 +147,11 @@ func main() {
address := *queryAddress
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)
}
} else {

View File

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

View File

@ -1,6 +1,7 @@
package server
import (
"blockbook/bitcoin"
"blockbook/db"
"context"
"encoding/json"
@ -120,24 +121,27 @@ func (s *HttpServer) transactions(w http.ResponseWriter, r *http.Request) {
type transactionList struct {
Txid []string `json:"txid"`
}
txList := transactionList{}
address := mux.Vars(r)["address"]
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 {
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)
}