Use output script (ScriptPubKey) as key to output transactions
parent
858f196484
commit
3dacffda59
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue