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))
|
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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue