2018-03-08 10:36:01 -07:00
|
|
|
package btc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"blockbook/bchain"
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/hex"
|
2018-07-24 07:58:37 -06:00
|
|
|
"math/big"
|
2018-03-08 10:36:01 -07:00
|
|
|
|
|
|
|
vlq "github.com/bsm/go-vlq"
|
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
|
|
|
)
|
|
|
|
|
2018-05-29 11:08:17 -06:00
|
|
|
// OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
|
2018-08-28 16:25:26 -06:00
|
|
|
type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error)
|
2018-05-29 11:08:17 -06:00
|
|
|
|
2018-04-08 03:24:29 -06:00
|
|
|
// BitcoinParser handle
|
|
|
|
type BitcoinParser struct {
|
2018-04-08 07:50:19 -06:00
|
|
|
*bchain.BaseParser
|
2018-06-18 05:00:54 -06:00
|
|
|
Params *chaincfg.Params
|
|
|
|
OutputScriptToAddressesFunc OutputScriptToAddressesFunc
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
|
2018-05-18 07:04:40 -06:00
|
|
|
// NewBitcoinParser returns new BitcoinParser instance
|
2018-05-28 06:57:44 -06:00
|
|
|
func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser {
|
2018-08-28 16:25:26 -06:00
|
|
|
p := &BitcoinParser{
|
|
|
|
BaseParser: &bchain.BaseParser{
|
2018-05-28 06:57:44 -06:00
|
|
|
BlockAddressesToKeep: c.BlockAddressesToKeep,
|
2018-07-24 07:58:37 -06:00
|
|
|
AmountDecimalPoint: 8,
|
2018-05-18 07:04:40 -06:00
|
|
|
},
|
2018-08-28 16:25:26 -06:00
|
|
|
Params: params,
|
2018-05-18 07:04:40 -06:00
|
|
|
}
|
2018-08-28 16:25:26 -06:00
|
|
|
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
|
|
|
|
return p
|
2018-05-18 07:04:40 -06:00
|
|
|
}
|
|
|
|
|
2018-04-08 03:24:29 -06:00
|
|
|
// GetChainParams contains network parameters for the main Bitcoin network,
|
2018-03-08 10:36:01 -07:00
|
|
|
// the regression test Bitcoin network, the test Bitcoin network and
|
|
|
|
// the simulation test Bitcoin network, in this order
|
|
|
|
func GetChainParams(chain string) *chaincfg.Params {
|
|
|
|
switch chain {
|
|
|
|
case "test":
|
|
|
|
return &chaincfg.TestNet3Params
|
|
|
|
case "regtest":
|
|
|
|
return &chaincfg.RegressionNetParams
|
|
|
|
}
|
|
|
|
return &chaincfg.MainNetParams
|
|
|
|
}
|
|
|
|
|
2018-08-28 16:25:26 -06:00
|
|
|
// GetAddrDescFromVout returns internal address representation (descriptor) of given transaction output
|
|
|
|
func (p *BitcoinParser) GetAddrDescFromVout(output *bchain.Vout) ([]byte, error) {
|
2018-03-23 06:15:19 -06:00
|
|
|
return hex.DecodeString(output.ScriptPubKey.Hex)
|
2018-03-20 08:58:35 -06:00
|
|
|
}
|
|
|
|
|
2018-08-28 16:25:26 -06:00
|
|
|
// GetAddrDescFromAddress returns internal address representation (descriptor) of given address
|
|
|
|
func (p *BitcoinParser) GetAddrDescFromAddress(address string) ([]byte, error) {
|
|
|
|
return p.addressToOutputScript(address)
|
2018-03-20 08:58:35 -06:00
|
|
|
}
|
|
|
|
|
2018-08-28 16:25:26 -06:00
|
|
|
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
|
|
|
|
func (p *BitcoinParser) GetAddressesFromAddrDesc(addrDesc []byte) ([]string, bool, error) {
|
|
|
|
return p.outputScriptToAddresses(addrDesc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetScriptFromAddrDesc returns output script for given address descriptor
|
|
|
|
func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc []byte) ([]byte, error) {
|
|
|
|
return addrDesc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// addressToOutputScript converts bitcoin address to ScriptPubKey
|
|
|
|
func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) {
|
2018-03-08 10:36:01 -07:00
|
|
|
da, err := btcutil.DecodeAddress(address, p.Params)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
script, err := txscript.PayToAddrScript(da)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return script, nil
|
|
|
|
}
|
|
|
|
|
2018-05-29 11:08:17 -06:00
|
|
|
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
2018-08-28 16:25:26 -06:00
|
|
|
func (p *BitcoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
|
|
|
|
sc, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
|
2018-03-08 10:36:01 -07:00
|
|
|
if err != nil {
|
2018-08-28 16:25:26 -06:00
|
|
|
return nil, false, err
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
rv := make([]string, len(addresses))
|
|
|
|
for i, a := range addresses {
|
|
|
|
rv[i] = a.EncodeAddress()
|
|
|
|
}
|
2018-08-28 16:25:26 -06:00
|
|
|
var s bool
|
|
|
|
if sc != txscript.NonStandardTy && sc != txscript.NullDataTy {
|
|
|
|
s = true
|
|
|
|
}
|
|
|
|
return rv, s, nil
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
|
2018-06-06 03:34:50 -06:00
|
|
|
func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
|
2018-03-08 10:36:01 -07:00
|
|
|
vin := make([]bchain.Vin, len(t.TxIn))
|
|
|
|
for i, in := range t.TxIn {
|
|
|
|
if blockchain.IsCoinBaseTx(t) {
|
|
|
|
vin[i] = bchain.Vin{
|
|
|
|
Coinbase: hex.EncodeToString(in.SignatureScript),
|
|
|
|
Sequence: in.Sequence,
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s := bchain.ScriptSig{
|
|
|
|
Hex: hex.EncodeToString(in.SignatureScript),
|
|
|
|
// missing: Asm,
|
|
|
|
}
|
|
|
|
vin[i] = bchain.Vin{
|
|
|
|
Txid: in.PreviousOutPoint.Hash.String(),
|
|
|
|
Vout: in.PreviousOutPoint.Index,
|
|
|
|
Sequence: in.Sequence,
|
|
|
|
ScriptSig: s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vout := make([]bchain.Vout, len(t.TxOut))
|
|
|
|
for i, out := range t.TxOut {
|
|
|
|
addrs := []string{}
|
|
|
|
if parseAddresses {
|
2018-08-28 16:25:26 -06:00
|
|
|
addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript)
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
s := bchain.ScriptPubKey{
|
|
|
|
Hex: hex.EncodeToString(out.PkScript),
|
|
|
|
Addresses: addrs,
|
|
|
|
// missing: Asm,
|
|
|
|
// missing: Type,
|
|
|
|
}
|
2018-07-24 07:58:37 -06:00
|
|
|
var vs big.Int
|
|
|
|
vs.SetInt64(out.Value)
|
2018-03-08 10:36:01 -07:00
|
|
|
vout[i] = bchain.Vout{
|
2018-07-24 07:58:37 -06:00
|
|
|
ValueSat: vs,
|
2018-03-08 10:36:01 -07:00
|
|
|
N: uint32(i),
|
|
|
|
ScriptPubKey: s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tx := bchain.Tx{
|
2018-06-26 05:03:59 -06:00
|
|
|
Txid: t.TxHash().String(),
|
|
|
|
Version: t.Version,
|
2018-03-08 10:36:01 -07:00
|
|
|
LockTime: t.LockTime,
|
|
|
|
Vin: vin,
|
|
|
|
Vout: vout,
|
|
|
|
// skip: BlockHash,
|
|
|
|
// skip: Confirmations,
|
|
|
|
// skip: Time,
|
|
|
|
// skip: Blocktime,
|
|
|
|
}
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseTx parses byte array containing transaction and returns Tx struct
|
2018-04-08 03:24:29 -06:00
|
|
|
func (p *BitcoinParser) ParseTx(b []byte) (*bchain.Tx, error) {
|
2018-03-08 10:36:01 -07:00
|
|
|
t := wire.MsgTx{}
|
|
|
|
r := bytes.NewReader(b)
|
|
|
|
if err := t.Deserialize(r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-06 03:34:50 -06:00
|
|
|
tx := p.TxFromMsgTx(&t, true)
|
2018-03-08 10:36:01 -07:00
|
|
|
tx.Hex = hex.EncodeToString(b)
|
|
|
|
return &tx, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseBlock parses raw block to our Block struct
|
2018-04-08 03:24:29 -06:00
|
|
|
func (p *BitcoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
|
2018-03-08 10:36:01 -07:00
|
|
|
w := wire.MsgBlock{}
|
|
|
|
r := bytes.NewReader(b)
|
|
|
|
|
|
|
|
if err := w.Deserialize(r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txs := make([]bchain.Tx, len(w.Transactions))
|
|
|
|
for ti, t := range w.Transactions {
|
2018-06-06 03:34:50 -06:00
|
|
|
txs[ti] = p.TxFromMsgTx(t, false)
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
|
2018-08-21 08:36:14 -06:00
|
|
|
return &bchain.Block{
|
|
|
|
BlockHeader: bchain.BlockHeader{
|
|
|
|
Size: len(b),
|
|
|
|
Time: w.Header.Timestamp.Unix(),
|
|
|
|
},
|
|
|
|
Txs: txs,
|
|
|
|
}, nil
|
2018-03-08 10:36:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// PackTx packs transaction to byte array
|
2018-04-08 03:24:29 -06:00
|
|
|
func (p *BitcoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
|
2018-03-08 10:36:01 -07:00
|
|
|
buf := make([]byte, 4+vlq.MaxLen64+len(tx.Hex)/2)
|
|
|
|
binary.BigEndian.PutUint32(buf[0:4], height)
|
|
|
|
vl := vlq.PutInt(buf[4:4+vlq.MaxLen64], blockTime)
|
|
|
|
hl, err := hex.Decode(buf[4+vl:], []byte(tx.Hex))
|
|
|
|
return buf[0 : 4+vl+hl], err
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnpackTx unpacks transaction from byte array
|
2018-04-08 03:24:29 -06:00
|
|
|
func (p *BitcoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
|
2018-03-08 10:36:01 -07:00
|
|
|
height := binary.BigEndian.Uint32(buf)
|
|
|
|
bt, l := vlq.Int(buf[4:])
|
|
|
|
tx, err := p.ParseTx(buf[4+l:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
tx.Blocktime = bt
|
2018-04-18 17:49:56 -06:00
|
|
|
|
2018-03-08 10:36:01 -07:00
|
|
|
return tx, height, nil
|
|
|
|
}
|