Use blockchain factory to create blockchain interface
parent
df4ba50672
commit
9d0547f608
|
@ -0,0 +1,28 @@
|
||||||
|
package coins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blockbook/bchain"
|
||||||
|
"blockbook/bchain/coins/btc"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type blockChainFactory func(url string, user string, password string, timeout time.Duration, parse bool) (bchain.BlockChain, error)
|
||||||
|
|
||||||
|
var blockChainFactories = make(map[string]blockChainFactory)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
blockChainFactories["btc"] = btc.NewBitcoinRPC
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockChain creates bchain.BlockChain of type defined by parameter coin
|
||||||
|
func NewBlockChain(coin string, url string, user string, password string, timeout time.Duration, parse bool) (bchain.BlockChain, error) {
|
||||||
|
bcf, ok := blockChainFactories[coin]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(fmt.Sprint("Unsupported coin ", coin, ". Must be one of ", reflect.ValueOf(blockChainFactories).MapKeys()))
|
||||||
|
}
|
||||||
|
return bcf(url, user, password, timeout, parse)
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
package btc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blockbook/bchain"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bitcoinwire parsing
|
||||||
|
|
||||||
|
type BitcoinBlockParser struct {
|
||||||
|
Params *chaincfg.Params
|
||||||
|
}
|
||||||
|
|
||||||
|
// getChainParams contains network parameters for the main Bitcoin network,
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToOutputScript converts bitcoin address to ScriptPubKey
|
||||||
|
func (p *BitcoinBlockParser) AddressToOutputScript(address string) ([]byte, error) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
||||||
|
func (p *BitcoinBlockParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
||||||
|
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rv := make([]string, len(addresses))
|
||||||
|
for i, a := range addresses {
|
||||||
|
rv[i] = a.EncodeAddress()
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BitcoinBlockParser) txFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
|
||||||
|
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 {
|
||||||
|
addrs, _ = p.OutputScriptToAddresses(out.PkScript)
|
||||||
|
}
|
||||||
|
s := bchain.ScriptPubKey{
|
||||||
|
Hex: hex.EncodeToString(out.PkScript),
|
||||||
|
Addresses: addrs,
|
||||||
|
// missing: Asm,
|
||||||
|
// missing: Type,
|
||||||
|
}
|
||||||
|
vout[i] = bchain.Vout{
|
||||||
|
Value: float64(out.Value) / 1E8,
|
||||||
|
N: uint32(i),
|
||||||
|
ScriptPubKey: s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx := bchain.Tx{
|
||||||
|
Txid: t.TxHash().String(),
|
||||||
|
// skip: Version,
|
||||||
|
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
|
||||||
|
func (p *BitcoinBlockParser) ParseTx(b []byte) (*bchain.Tx, error) {
|
||||||
|
t := wire.MsgTx{}
|
||||||
|
r := bytes.NewReader(b)
|
||||||
|
if err := t.Deserialize(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tx := p.txFromMsgTx(&t, true)
|
||||||
|
tx.Hex = hex.EncodeToString(b)
|
||||||
|
return &tx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBlock parses raw block to our Block struct
|
||||||
|
func (p *BitcoinBlockParser) ParseBlock(b []byte) (*bchain.Block, error) {
|
||||||
|
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 {
|
||||||
|
txs[ti] = p.txFromMsgTx(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bchain.Block{Txs: txs}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackTx packs transaction to byte array
|
||||||
|
func (p *BitcoinBlockParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
|
||||||
|
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
|
||||||
|
func (p *BitcoinBlockParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
|
||||||
|
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
|
||||||
|
return tx, height, nil
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package bchain
|
package btc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blockbook/bchain"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -109,14 +110,14 @@ func TestOutputScriptToAddresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var testTx1 = Tx{
|
var testTx1 = bchain.Tx{
|
||||||
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
|
Hex: "01000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700",
|
||||||
Blocktime: 1519053802,
|
Blocktime: 1519053802,
|
||||||
Txid: "056e3d82e5ffd0e915fb9b62797d76263508c34fe3e5dbed30dd3e943930f204",
|
Txid: "056e3d82e5ffd0e915fb9b62797d76263508c34fe3e5dbed30dd3e943930f204",
|
||||||
LockTime: 512115,
|
LockTime: 512115,
|
||||||
Vin: []Vin{
|
Vin: []bchain.Vin{
|
||||||
{
|
{
|
||||||
ScriptSig: ScriptSig{
|
ScriptSig: bchain.ScriptSig{
|
||||||
Hex: "4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80",
|
Hex: "4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80",
|
||||||
},
|
},
|
||||||
Txid: "425fed43ba74e9205875eb934d5bcf7bf338f146f70d4002d94bf5cbc9229a7f",
|
Txid: "425fed43ba74e9205875eb934d5bcf7bf338f146f70d4002d94bf5cbc9229a7f",
|
||||||
|
@ -124,11 +125,11 @@ var testTx1 = Tx{
|
||||||
Sequence: 4294967294,
|
Sequence: 4294967294,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Vout: []Vout{
|
Vout: []bchain.Vout{
|
||||||
{
|
{
|
||||||
Value: 0.00038812,
|
Value: 0.00038812,
|
||||||
N: 0,
|
N: 0,
|
||||||
ScriptPubKey: ScriptPubKey{
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
|
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
|
||||||
Addresses: []string{
|
Addresses: []string{
|
||||||
"3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK",
|
"3AZKvpKhSh1o8t1QrX3UeXG9d2BhCRnbcK",
|
||||||
|
@ -139,14 +140,14 @@ var testTx1 = Tx{
|
||||||
}
|
}
|
||||||
var testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
|
var testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
|
||||||
|
|
||||||
var testTx2 = Tx{
|
var testTx2 = bchain.Tx{
|
||||||
Hex: "010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000",
|
Hex: "010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000",
|
||||||
Blocktime: 1235678901,
|
Blocktime: 1235678901,
|
||||||
Txid: "474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7",
|
Txid: "474e6795760ebe81cb4023dc227e5a0efe340e1771c89a0035276361ed733de7",
|
||||||
LockTime: 0,
|
LockTime: 0,
|
||||||
Vin: []Vin{
|
Vin: []bchain.Vin{
|
||||||
{
|
{
|
||||||
ScriptSig: ScriptSig{
|
ScriptSig: bchain.ScriptSig{
|
||||||
Hex: "160014550da1f5d25a9dae2eafd6902b4194c4c6500af6",
|
Hex: "160014550da1f5d25a9dae2eafd6902b4194c4c6500af6",
|
||||||
},
|
},
|
||||||
Txid: "c13e32a4428e31f85d7aee4ec7344504b12e72aaffcbde0160200d2ac7f0649d",
|
Txid: "c13e32a4428e31f85d7aee4ec7344504b12e72aaffcbde0160200d2ac7f0649d",
|
||||||
|
@ -154,11 +155,11 @@ var testTx2 = Tx{
|
||||||
Sequence: 4294967295,
|
Sequence: 4294967295,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Vout: []Vout{
|
Vout: []bchain.Vout{
|
||||||
{
|
{
|
||||||
Value: .1,
|
Value: .1,
|
||||||
N: 0,
|
N: 0,
|
||||||
ScriptPubKey: ScriptPubKey{
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
|
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
|
||||||
Addresses: []string{
|
Addresses: []string{
|
||||||
"2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu",
|
"2NByHN6A8QYkBATzxf4pRGbCSHD5CEN2TRu",
|
||||||
|
@ -168,7 +169,7 @@ var testTx2 = Tx{
|
||||||
{
|
{
|
||||||
Value: 9.20081157,
|
Value: 9.20081157,
|
||||||
N: 1,
|
N: 1,
|
||||||
ScriptPubKey: ScriptPubKey{
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
|
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
|
||||||
Addresses: []string{
|
Addresses: []string{
|
||||||
"2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D",
|
"2MvZguYaGjM7JihBgNqgLF2Ca2Enb76Hj9D",
|
||||||
|
@ -181,7 +182,7 @@ var testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa7
|
||||||
|
|
||||||
func Test_PackTx(t *testing.T) {
|
func Test_PackTx(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
tx Tx
|
tx bchain.Tx
|
||||||
height uint32
|
height uint32
|
||||||
blockTime int64
|
blockTime int64
|
||||||
parser *BitcoinBlockParser
|
parser *BitcoinBlockParser
|
||||||
|
@ -238,7 +239,7 @@ func Test_UnpackTx(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want *Tx
|
want *bchain.Tx
|
||||||
want1 uint32
|
want1 uint32
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
|
@ -1,23 +1,17 @@
|
||||||
package bchain
|
package btc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blockbook/bchain"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
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/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
@ -32,12 +26,12 @@ type BitcoinRPC struct {
|
||||||
parser *BitcoinBlockParser
|
parser *BitcoinBlockParser
|
||||||
testnet bool
|
testnet bool
|
||||||
network string
|
network string
|
||||||
mempool *Mempool
|
mempool *bchain.Mempool
|
||||||
parseBlocks bool
|
parseBlocks bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||||
func NewBitcoinRPC(url string, user string, password string, timeout time.Duration, parse bool) (BlockChain, error) {
|
func NewBitcoinRPC(url string, user string, password string, timeout time.Duration, parse bool) (bchain.BlockChain, error) {
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
|
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
|
||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
|
@ -69,7 +63,7 @@ func NewBitcoinRPC(url string, user string, password string, timeout time.Durati
|
||||||
s.network = "testnet"
|
s.network = "testnet"
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mempool = NewMempool(s)
|
s.mempool = bchain.NewMempool(s)
|
||||||
|
|
||||||
glog.Info("rpc: block chain ", s.parser.Params.Name)
|
glog.Info("rpc: block chain ", s.parser.Params.Name)
|
||||||
return s, nil
|
return s, nil
|
||||||
|
@ -83,10 +77,6 @@ func (b *BitcoinRPC) GetNetworkName() string {
|
||||||
return b.network
|
return b.network
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *RPCError) Error() string {
|
|
||||||
return fmt.Sprintf("%d: %s", e.Code, e.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getblockhash
|
// getblockhash
|
||||||
|
|
||||||
type cmdGetBlockHash struct {
|
type cmdGetBlockHash struct {
|
||||||
|
@ -97,8 +87,8 @@ type cmdGetBlockHash struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockHash struct {
|
type resGetBlockHash struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getbestblockhash
|
// getbestblockhash
|
||||||
|
@ -108,8 +98,8 @@ type cmdGetBestBlockHash struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBestBlockHash struct {
|
type resGetBestBlockHash struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getblockcount
|
// getblockcount
|
||||||
|
@ -119,8 +109,8 @@ type cmdGetBlockCount struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockCount struct {
|
type resGetBlockCount struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result uint32 `json:"result"`
|
Result uint32 `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getblockchaininfo
|
// getblockchaininfo
|
||||||
|
@ -130,7 +120,7 @@ type cmdGetBlockChainInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockChainInfo struct {
|
type resGetBlockChainInfo struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result struct {
|
Result struct {
|
||||||
Chain string `json:"chain"`
|
Chain string `json:"chain"`
|
||||||
Blocks int `json:"blocks"`
|
Blocks int `json:"blocks"`
|
||||||
|
@ -146,8 +136,8 @@ type cmdGetMempool struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetMempool struct {
|
type resGetMempool struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result []string `json:"result"`
|
Result []string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getblockheader
|
// getblockheader
|
||||||
|
@ -161,13 +151,13 @@ type cmdGetBlockHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockHeaderRaw struct {
|
type resGetBlockHeaderRaw struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockHeaderVerbose struct {
|
type resGetBlockHeaderVerbose struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result BlockHeader `json:"result"`
|
Result bchain.BlockHeader `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getblock
|
// getblock
|
||||||
|
@ -181,18 +171,18 @@ type cmdGetBlock struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockRaw struct {
|
type resGetBlockRaw struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockThin struct {
|
type resGetBlockThin struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result ThinBlock `json:"result"`
|
Result bchain.ThinBlock `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetBlockFull struct {
|
type resGetBlockFull struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result Block `json:"result"`
|
Result bchain.Block `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getrawtransaction
|
// getrawtransaction
|
||||||
|
@ -206,13 +196,13 @@ type cmdGetRawTransaction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetRawTransactionRaw struct {
|
type resGetRawTransactionRaw struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetRawTransactionVerbose struct {
|
type resGetRawTransactionVerbose struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result Tx `json:"result"`
|
Result bchain.Tx `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// estimatesmartfee
|
// estimatesmartfee
|
||||||
|
@ -226,7 +216,7 @@ type cmdEstimateSmartFee struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resEstimateSmartFee struct {
|
type resEstimateSmartFee struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result struct {
|
Result struct {
|
||||||
Feerate float64 `json:"feerate"`
|
Feerate float64 `json:"feerate"`
|
||||||
Blocks int `json:"blocks"`
|
Blocks int `json:"blocks"`
|
||||||
|
@ -241,8 +231,8 @@ type cmdSendRawTransaction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resSendRawTransaction struct {
|
type resSendRawTransaction struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getmempoolentry
|
// getmempoolentry
|
||||||
|
@ -253,8 +243,8 @@ type cmdGetMempoolEntry struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type resGetMempoolEntry struct {
|
type resGetMempoolEntry struct {
|
||||||
Error *RPCError `json:"error"`
|
Error *bchain.RPCError `json:"error"`
|
||||||
Result *MempoolEntry `json:"result"`
|
Result *bchain.MempoolEntry `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBestBlockHash returns hash of the tip of the best-block-chain.
|
// GetBestBlockHash returns hash of the tip of the best-block-chain.
|
||||||
|
@ -328,7 +318,7 @@ func (b *BitcoinRPC) GetBlockHash(height uint32) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockHeader returns header of block with given hash.
|
// GetBlockHeader returns header of block with given hash.
|
||||||
func (b *BitcoinRPC) GetBlockHeader(hash string) (*BlockHeader, error) {
|
func (b *BitcoinRPC) GetBlockHeader(hash string) (*bchain.BlockHeader, error) {
|
||||||
glog.V(1).Info("rpc: getblockheader")
|
glog.V(1).Info("rpc: getblockheader")
|
||||||
|
|
||||||
res := resGetBlockHeaderVerbose{}
|
res := resGetBlockHeaderVerbose{}
|
||||||
|
@ -347,7 +337,7 @@ func (b *BitcoinRPC) GetBlockHeader(hash string) (*BlockHeader, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlock returns block with given hash.
|
// GetBlock returns block with given hash.
|
||||||
func (b *BitcoinRPC) GetBlock(hash string) (*Block, error) {
|
func (b *BitcoinRPC) GetBlock(hash string) (*bchain.Block, error) {
|
||||||
if !b.parseBlocks {
|
if !b.parseBlocks {
|
||||||
return b.GetBlockFull(hash)
|
return b.GetBlockFull(hash)
|
||||||
}
|
}
|
||||||
|
@ -369,7 +359,7 @@ func (b *BitcoinRPC) GetBlock(hash string) (*Block, error) {
|
||||||
|
|
||||||
// GetBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
|
// GetBlockWithoutHeader is an optimization - it does not call GetBlockHeader to get prev, next hashes
|
||||||
// instead it sets to header only block hash and height passed in parameters
|
// instead it sets to header only block hash and height passed in parameters
|
||||||
func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*Block, error) {
|
func (b *BitcoinRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
|
||||||
if !b.parseBlocks {
|
if !b.parseBlocks {
|
||||||
return b.GetBlockFull(hash)
|
return b.GetBlockFull(hash)
|
||||||
}
|
}
|
||||||
|
@ -407,7 +397,7 @@ func (b *BitcoinRPC) GetBlockRaw(hash string) ([]byte, error) {
|
||||||
|
|
||||||
// GetBlockList returns block with given hash by downloading block
|
// GetBlockList returns block with given hash by downloading block
|
||||||
// transactions one by one.
|
// transactions one by one.
|
||||||
func (b *BitcoinRPC) GetBlockList(hash string) (*Block, error) {
|
func (b *BitcoinRPC) GetBlockList(hash string) (*bchain.Block, error) {
|
||||||
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
|
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
|
||||||
|
|
||||||
res := resGetBlockThin{}
|
res := resGetBlockThin{}
|
||||||
|
@ -423,7 +413,7 @@ func (b *BitcoinRPC) GetBlockList(hash string) (*Block, error) {
|
||||||
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
return nil, errors.Annotatef(res.Error, "hash %v", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
txs := make([]Tx, len(res.Result.Txids))
|
txs := make([]bchain.Tx, len(res.Result.Txids))
|
||||||
for i, txid := range res.Result.Txids {
|
for i, txid := range res.Result.Txids {
|
||||||
tx, err := b.GetTransaction(txid)
|
tx, err := b.GetTransaction(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -431,7 +421,7 @@ func (b *BitcoinRPC) GetBlockList(hash string) (*Block, error) {
|
||||||
}
|
}
|
||||||
txs[i] = *tx
|
txs[i] = *tx
|
||||||
}
|
}
|
||||||
block := &Block{
|
block := &bchain.Block{
|
||||||
BlockHeader: res.Result.BlockHeader,
|
BlockHeader: res.Result.BlockHeader,
|
||||||
Txs: txs,
|
Txs: txs,
|
||||||
}
|
}
|
||||||
|
@ -439,7 +429,7 @@ func (b *BitcoinRPC) GetBlockList(hash string) (*Block, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockFull returns block with given hash.
|
// GetBlockFull returns block with given hash.
|
||||||
func (b *BitcoinRPC) GetBlockFull(hash string) (*Block, error) {
|
func (b *BitcoinRPC) GetBlockFull(hash string) (*bchain.Block, error) {
|
||||||
glog.V(1).Info("rpc: getblock (verbosity=2) ", hash)
|
glog.V(1).Info("rpc: getblock (verbosity=2) ", hash)
|
||||||
|
|
||||||
res := resGetBlockFull{}
|
res := resGetBlockFull{}
|
||||||
|
@ -475,7 +465,7 @@ func (b *BitcoinRPC) GetMempool() ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransaction returns a transaction by the transaction ID.
|
// GetTransaction returns a transaction by the transaction ID.
|
||||||
func (b *BitcoinRPC) GetTransaction(txid string) (*Tx, error) {
|
func (b *BitcoinRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||||
glog.V(1).Info("rpc: getrawtransaction ", txid)
|
glog.V(1).Info("rpc: getrawtransaction ", txid)
|
||||||
|
|
||||||
res := resGetRawTransactionVerbose{}
|
res := resGetRawTransactionVerbose{}
|
||||||
|
@ -550,7 +540,7 @@ func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) {
|
||||||
return res.Result, nil
|
return res.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BitcoinRPC) GetMempoolEntry(txid string) (*MempoolEntry, error) {
|
func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
|
||||||
glog.V(1).Info("rpc: getmempoolentry")
|
glog.V(1).Info("rpc: getmempoolentry")
|
||||||
|
|
||||||
res := resGetMempoolEntry{}
|
res := resGetMempoolEntry{}
|
||||||
|
@ -595,154 +585,6 @@ func (b *BitcoinRPC) call(req interface{}, res interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChainParser returns BlockChainParser
|
// GetChainParser returns BlockChainParser
|
||||||
func (b *BitcoinRPC) GetChainParser() BlockChainParser {
|
func (b *BitcoinRPC) GetChainParser() bchain.BlockChainParser {
|
||||||
return b.parser
|
return b.parser
|
||||||
}
|
}
|
||||||
|
|
||||||
// bitcoinwire parsing
|
|
||||||
|
|
||||||
type BitcoinBlockParser struct {
|
|
||||||
Params *chaincfg.Params
|
|
||||||
}
|
|
||||||
|
|
||||||
// getChainParams contains network parameters for the main Bitcoin network,
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressToOutputScript converts bitcoin address to ScriptPubKey
|
|
||||||
func (p *BitcoinBlockParser) AddressToOutputScript(address string) ([]byte, error) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputScriptToAddresses converts ScriptPubKey to bitcoin addresses
|
|
||||||
func (p *BitcoinBlockParser) OutputScriptToAddresses(script []byte) ([]string, error) {
|
|
||||||
_, addresses, _, err := txscript.ExtractPkScriptAddrs(script, p.Params)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rv := make([]string, len(addresses))
|
|
||||||
for i, a := range addresses {
|
|
||||||
rv[i] = a.EncodeAddress()
|
|
||||||
}
|
|
||||||
return rv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *BitcoinBlockParser) txFromMsgTx(t *wire.MsgTx, parseAddresses bool) Tx {
|
|
||||||
vin := make([]Vin, len(t.TxIn))
|
|
||||||
for i, in := range t.TxIn {
|
|
||||||
if blockchain.IsCoinBaseTx(t) {
|
|
||||||
vin[i] = Vin{
|
|
||||||
Coinbase: hex.EncodeToString(in.SignatureScript),
|
|
||||||
Sequence: in.Sequence,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
s := ScriptSig{
|
|
||||||
Hex: hex.EncodeToString(in.SignatureScript),
|
|
||||||
// missing: Asm,
|
|
||||||
}
|
|
||||||
vin[i] = Vin{
|
|
||||||
Txid: in.PreviousOutPoint.Hash.String(),
|
|
||||||
Vout: in.PreviousOutPoint.Index,
|
|
||||||
Sequence: in.Sequence,
|
|
||||||
ScriptSig: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vout := make([]Vout, len(t.TxOut))
|
|
||||||
for i, out := range t.TxOut {
|
|
||||||
addrs := []string{}
|
|
||||||
if parseAddresses {
|
|
||||||
addrs, _ = p.OutputScriptToAddresses(out.PkScript)
|
|
||||||
}
|
|
||||||
s := ScriptPubKey{
|
|
||||||
Hex: hex.EncodeToString(out.PkScript),
|
|
||||||
Addresses: addrs,
|
|
||||||
// missing: Asm,
|
|
||||||
// missing: Type,
|
|
||||||
}
|
|
||||||
vout[i] = Vout{
|
|
||||||
Value: float64(out.Value) / 1E8,
|
|
||||||
N: uint32(i),
|
|
||||||
ScriptPubKey: s,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tx := Tx{
|
|
||||||
Txid: t.TxHash().String(),
|
|
||||||
// skip: Version,
|
|
||||||
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
|
|
||||||
func (p *BitcoinBlockParser) ParseTx(b []byte) (*Tx, error) {
|
|
||||||
t := wire.MsgTx{}
|
|
||||||
r := bytes.NewReader(b)
|
|
||||||
if err := t.Deserialize(r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tx := p.txFromMsgTx(&t, true)
|
|
||||||
tx.Hex = hex.EncodeToString(b)
|
|
||||||
return &tx, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseBlock parses raw block to our Block struct
|
|
||||||
func (p *BitcoinBlockParser) ParseBlock(b []byte) (*Block, error) {
|
|
||||||
w := wire.MsgBlock{}
|
|
||||||
r := bytes.NewReader(b)
|
|
||||||
|
|
||||||
if err := w.Deserialize(r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
txs := make([]Tx, len(w.Transactions))
|
|
||||||
for ti, t := range w.Transactions {
|
|
||||||
txs[ti] = p.txFromMsgTx(t, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Block{Txs: txs}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PackTx packs transaction to byte array
|
|
||||||
func (p *BitcoinBlockParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, error) {
|
|
||||||
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
|
|
||||||
func (p *BitcoinBlockParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
|
|
||||||
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
|
|
||||||
return tx, height, nil
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package bchain
|
package bchain
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type ScriptSig struct {
|
type ScriptSig struct {
|
||||||
// Asm string `json:"asm"`
|
// Asm string `json:"asm"`
|
||||||
Hex string `json:"hex"`
|
Hex string `json:"hex"`
|
||||||
|
@ -79,6 +81,10 @@ type RPCError struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *RPCError) Error() string {
|
||||||
|
return fmt.Sprintf("%d: %s", e.Code, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
type BlockChain interface {
|
type BlockChain interface {
|
||||||
// chain info
|
// chain info
|
||||||
IsTestnet() bool
|
IsTestnet() bool
|
||||||
|
|
13
blockbook.go
13
blockbook.go
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
|
|
||||||
"blockbook/bchain"
|
"blockbook/bchain"
|
||||||
|
"blockbook/bchain/coins"
|
||||||
"blockbook/db"
|
"blockbook/db"
|
||||||
"blockbook/server"
|
"blockbook/server"
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ const resyncMempoolPeriodMs = 60017
|
||||||
const debounceResyncMempoolMs = 1009
|
const debounceResyncMempoolMs = 1009
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rpcURL = flag.String("rpcurl", "http://localhost:8332", "url of bitcoin RPC service")
|
rpcURL = flag.String("rpcurl", "http://localhost:8332", "url of blockchain RPC service")
|
||||||
rpcUser = flag.String("rpcuser", "rpc", "rpc username")
|
rpcUser = flag.String("rpcuser", "rpc", "rpc username")
|
||||||
rpcPass = flag.String("rpcpass", "rpc", "rpc password")
|
rpcPass = flag.String("rpcpass", "rpc", "rpc password")
|
||||||
rpcTimeout = flag.Uint("rpctimeout", 25, "rpc timeout in seconds")
|
rpcTimeout = flag.Uint("rpctimeout", 25, "rpc timeout in seconds")
|
||||||
|
@ -62,7 +63,9 @@ var (
|
||||||
|
|
||||||
zeroMQBinding = flag.String("zeromq", "", "binding to zeromq, if missing no zeromq connection")
|
zeroMQBinding = flag.String("zeromq", "", "binding to zeromq, if missing no zeromq connection")
|
||||||
|
|
||||||
explorerURL = flag.String("explorer", "", "address of the Bitcoin blockchain explorer")
|
explorerURL = flag.String("explorer", "", "address of blockchain explorer")
|
||||||
|
|
||||||
|
coin = flag.String("coin", "btc", "coin name (default btc)")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -102,13 +105,13 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if chain, err = bchain.NewBitcoinRPC(*rpcURL, *rpcUser, *rpcPass, time.Duration(*rpcTimeout)*time.Second, *parse); err != nil {
|
if chain, err = coins.NewBlockChain(*coin, *rpcURL, *rpcUser, *rpcPass, time.Duration(*rpcTimeout)*time.Second, *parse); err != nil {
|
||||||
glog.Fatal("NewBitcoinRPC ", err)
|
glog.Fatal("NewBlockChain: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
index, err = db.NewRocksDB(*dbPath, chain.GetChainParser())
|
index, err = db.NewRocksDB(*dbPath, chain.GetChainParser())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("NewRocksDB %v", err)
|
glog.Fatal("NewRocksDB: ", err)
|
||||||
}
|
}
|
||||||
defer index.Close()
|
defer index.Close()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue