Merge branch 'indexv3' into explorer

This commit is contained in:
Martin Boehm 2018-08-19 23:20:43 +02:00
commit 6bfc7240a9
48 changed files with 2022 additions and 990 deletions

View File

@ -1,5 +1,7 @@
package api package api
import "math/big"
type ScriptSig struct { type ScriptSig struct {
Hex string `json:"hex"` Hex string `json:"hex"`
Asm string `json:"asm,omitempty"` Asm string `json:"asm,omitempty"`
@ -12,8 +14,8 @@ type Vin struct {
N int `json:"n"` N int `json:"n"`
ScriptSig ScriptSig `json:"scriptSig"` ScriptSig ScriptSig `json:"scriptSig"`
Addr string `json:"addr"` Addr string `json:"addr"`
ValueSat int64 `json:"valueSat"` Value string `json:"value"`
Value float64 `json:"value"` ValueSat big.Int `json:"-"`
} }
type ScriptPubKey struct { type ScriptPubKey struct {
@ -23,7 +25,8 @@ type ScriptPubKey struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} }
type Vout struct { type Vout struct {
Value float64 `json:"value"` Value string `json:"value"`
ValueSat big.Int `json:"-"`
N int `json:"n"` N int `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"` ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
SpentTxID string `json:"spentTxId,omitempty"` SpentTxID string `json:"spentTxId,omitempty"`
@ -32,34 +35,30 @@ type Vout struct {
} }
type Tx struct { type Tx struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Version int32 `json:"version,omitempty"` Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"` Locktime uint32 `json:"locktime,omitempty"`
Vin []Vin `json:"vin"` Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"` Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"` Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"` Blockheight int `json:"blockheight"`
Confirmations uint32 `json:"confirmations"` Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"` Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"` Blocktime int64 `json:"blocktime"`
ValueOut float64 `json:"valueOut"` ValueOut string `json:"valueOut"`
Size int `json:"size,omitempty"` Size int `json:"size,omitempty"`
ValueIn float64 `json:"valueIn"` ValueIn string `json:"valueIn"`
Fees float64 `json:"fees"` Fees string `json:"fees"`
WithSpends bool `json:"withSpends,omitempty"` WithSpends bool `json:"withSpends,omitempty"`
} }
type Address struct { type Address struct {
AddrStr string `json:"addrStr"` AddrStr string `json:"addrStr"`
Balance float64 `json:"balance"` Balance string `json:"balance"`
BalanceSat int64 `json:"balanceSat"` TotalReceived string `json:"totalReceived"`
TotalReceived float64 `json:"totalReceived"` TotalSent string `json:"totalSent"`
TotalReceivedSat int64 `json:"totalReceivedSat"` UnconfirmedBalance string `json:"unconfirmedBalance"`
TotalSent float64 `json:"totalSent"` UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TotalSentSat int64 `json:"totalSentSat"` TxApperances int `json:"txApperances"`
UnconfirmedBalance float64 `json:"unconfirmedBalance"` Transactions []*Tx `json:"transactions"`
UnconfirmedBalanceSat int64 `json:"unconfirmedBalanceSat"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"`
Transactions []*Tx `json:"transactions"`
} }

View File

@ -4,6 +4,7 @@ import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/common" "blockbook/common"
"blockbook/db" "blockbook/db"
"math/big"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -44,7 +45,7 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
return nil, err return nil, err
} }
} }
var valIn, valOut, fees float64 var valInSat, valOutSat, feesSat big.Int
vins := make([]Vin, len(bchainTx.Vin)) vins := make([]Vin, len(bchainTx.Vin))
for i := range bchainTx.Vin { for i := range bchainTx.Vin {
bchainVin := &bchainTx.Vin[i] bchainVin := &bchainTx.Vin[i]
@ -61,9 +62,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
} }
if len(otx.Vout) > int(vin.Vout) { if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout] vout := &otx.Vout[vin.Vout]
vin.Value = vout.Value vin.ValueSat = vout.ValueSat
valIn += vout.Value vin.Value = w.chainParser.AmountToDecimalString(&vout.ValueSat)
vin.ValueSat = int64(vout.Value*1E8 + 0.5) valInSat.Add(&valInSat, &vout.ValueSat)
if vout.Address != nil { if vout.Address != nil {
a := vout.Address.String() a := vout.Address.String()
vin.Addr = a vin.Addr = a
@ -76,8 +77,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
bchainVout := &bchainTx.Vout[i] bchainVout := &bchainTx.Vout[i]
vout := &vouts[i] vout := &vouts[i]
vout.N = i vout.N = i
vout.Value = bchainVout.Value vout.ValueSat = bchainVout.ValueSat
valOut += bchainVout.Value vout.Value = w.chainParser.AmountToDecimalString(&bchainVout.ValueSat)
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex vout.ScriptPubKey.Hex = bchainVout.ScriptPubKey.Hex
vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses vout.ScriptPubKey.Addresses = bchainVout.ScriptPubKey.Addresses
if spendingTx { if spendingTx {
@ -85,9 +87,9 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
} }
} }
// for coinbase transactions valIn is 0 // for coinbase transactions valIn is 0
fees = valIn - valOut feesSat.Sub(&valInSat, &valOutSat)
if fees < 0 { if feesSat.Sign() == -1 {
fees = 0 feesSat.SetUint64(0)
} }
// for now do not return size, we would have to compute vsize of segwit transactions // for now do not return size, we would have to compute vsize of segwit transactions
// size:=len(bchainTx.Hex) / 2 // size:=len(bchainTx.Hex) / 2
@ -96,13 +98,13 @@ func (w *Worker) GetTransaction(txid string, bestheight uint32, spendingTx bool)
Blockheight: int(height), Blockheight: int(height),
Blocktime: bchainTx.Blocktime, Blocktime: bchainTx.Blocktime,
Confirmations: bchainTx.Confirmations, Confirmations: bchainTx.Confirmations,
Fees: fees, Fees: w.chainParser.AmountToDecimalString(&feesSat),
Locktime: bchainTx.LockTime, Locktime: bchainTx.LockTime,
WithSpends: spendingTx, WithSpends: spendingTx,
Time: bchainTx.Time, Time: bchainTx.Time,
Txid: bchainTx.Txid, Txid: bchainTx.Txid,
ValueIn: valIn, ValueIn: w.chainParser.AmountToDecimalString(&valInSat),
ValueOut: valOut, ValueOut: w.chainParser.AmountToDecimalString(&valOutSat),
Version: bchainTx.Version, Version: bchainTx.Version,
Vin: vins, Vin: vins,
Vout: vouts, Vout: vouts,
@ -131,26 +133,26 @@ func (s *Worker) getAddressTxids(address string, mempool bool) ([]string, error)
return txids, nil return txids, nil
} }
func (t *Tx) getAddrVoutValue(addrID string) float64 { func (t *Tx) getAddrVoutValue(addrID string) *big.Int {
var val float64 var val big.Int
for _, vout := range t.Vout { for _, vout := range t.Vout {
for _, a := range vout.ScriptPubKey.Addresses { for _, a := range vout.ScriptPubKey.Addresses {
if a == addrID { if a == addrID {
val += vout.Value val.Add(&val, &vout.ValueSat)
} }
} }
} }
return val return &val
} }
func (t *Tx) getAddrVinValue(addrID string) float64 { func (t *Tx) getAddrVinValue(addrID string) *big.Int {
var val float64 var val big.Int
for _, vin := range t.Vin { for _, vin := range t.Vin {
if vin.Addr == addrID { if vin.Addr == addrID {
val += vin.Value val.Add(&val, &vin.ValueSat)
} }
} }
return val return &val
} }
// UniqueTxidsInReverse reverts the order of transactions (so that newest are first) and removes duplicate transactions // UniqueTxidsInReverse reverts the order of transactions (so that newest are first) and removes duplicate transactions
@ -181,6 +183,7 @@ func (w *Worker) GetAddress(addrID string, page int) (*Address, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
txm = UniqueTxidsInReverse(txm)
bestheight, _, err := w.db.GetBestBlock() bestheight, _, err := w.db.GetBestBlock()
if err != nil { if err != nil {
return nil, err return nil, err
@ -191,14 +194,14 @@ func (w *Worker) GetAddress(addrID string, page int) (*Address, error) {
} }
txs := make([]*Tx, len(txm)+lc) txs := make([]*Tx, len(txm)+lc)
txi := 0 txi := 0
var uBal, bal, totRecv, totSent float64 var uBalSat, balSat, totRecvSat, totSentSat big.Int
for _, tx := range txm { for _, tx := range txm {
tx, err := w.GetTransaction(tx, bestheight, false) tx, err := w.GetTransaction(tx, bestheight, false)
// mempool transaction may fail // mempool transaction may fail
if err != nil { if err != nil {
glog.Error("GetTransaction ", tx, ": ", err) glog.Error("GetTransaction ", tx, ": ", err)
} else { } else {
uBal = tx.getAddrVoutValue(addrID) - tx.getAddrVinValue(addrID) uBalSat.Sub(tx.getAddrVoutValue(addrID), tx.getAddrVinValue(addrID))
txs[txi] = tx txs[txi] = tx
txi++ txi++
} }
@ -216,26 +219,23 @@ func (w *Worker) GetAddress(addrID string, page int) (*Address, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else { } else {
totRecv += tx.getAddrVoutValue(addrID) totRecvSat.Add(&totRecvSat, tx.getAddrVoutValue(addrID))
totSent += tx.getAddrVinValue(addrID) totSentSat.Add(&totSentSat, tx.getAddrVinValue(addrID))
if i >= from && i < to { if i >= from && i < to {
txs[txi] = tx txs[txi] = tx
txi++ txi++
} }
} }
} }
bal = totRecv - totSent balSat.Sub(&totRecvSat, &totSentSat)
r := &Address{ r := &Address{
AddrStr: addrID, AddrStr: addrID,
Balance: bal, Balance: w.chainParser.AmountToDecimalString(&balSat),
BalanceSat: int64(bal*1E8 + 0.5), TotalReceived: w.chainParser.AmountToDecimalString(&totRecvSat),
TotalReceived: totRecv, TotalSent: w.chainParser.AmountToDecimalString(&totSentSat),
TotalReceivedSat: int64(totRecv*1E8 + 0.5),
TotalSent: totSent,
TotalSentSat: int64(totSent*1E8 + 0.5),
Transactions: txs[:txi], Transactions: txs[:txi],
TxApperances: len(txc), TxApperances: len(txc),
UnconfirmedBalance: uBal, UnconfirmedBalance: w.chainParser.AmountToDecimalString(&uBalSat),
UnconfirmedTxApperances: len(txm), UnconfirmedTxApperances: len(txm),
} }
glog.Info(addrID, " finished") glog.Info(addrID, " finished")

View File

@ -3,6 +3,8 @@ package bchain
import ( import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/big"
"strings"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
@ -14,6 +16,7 @@ type AddressFactoryFunc func(string) (Address, error)
type BaseParser struct { type BaseParser struct {
AddressFactory AddressFactoryFunc AddressFactory AddressFactoryFunc
BlockAddressesToKeep int BlockAddressesToKeep int
AmountDecimalPoint int
} }
// AddressToOutputScript converts address to ScriptPubKey - currently not implemented // AddressToOutputScript converts address to ScriptPubKey - currently not implemented
@ -36,7 +39,52 @@ func (p *BaseParser) ParseTx(b []byte) (*Tx, error) {
return nil, errors.New("ParseTx: not implemented") return nil, errors.New("ParseTx: not implemented")
} }
// ParseTxFromJson parses JSON message containing transaction and returs Tx struct const zeros = "0000000000000000000000000000000000000000"
// AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding
func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) {
var r big.Int
s := string(n)
i := strings.IndexByte(s, '.')
if i == -1 {
s = s + zeros[:p.AmountDecimalPoint]
} else {
z := p.AmountDecimalPoint - len(s) + i + 1
if z > 0 {
s = s[:i] + s[i+1:] + zeros[:z]
} else {
s = s[:i] + s[i+1:len(s)+z]
}
}
if _, ok := r.SetString(s, 10); !ok {
return r, errors.New("AmountToBigInt: failed to convert")
}
return r, nil
}
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
func (p *BaseParser) AmountToDecimalString(a *big.Int) string {
n := a.String()
var s string
if n[0] == '-' {
n = n[1:]
s = "-"
}
if len(n) <= p.AmountDecimalPoint {
n = zeros[:p.AmountDecimalPoint-len(n)+1] + n
}
i := len(n) - p.AmountDecimalPoint
ad := strings.TrimRight(n[i:], "0")
if len(ad) > 0 {
n = n[:i] + "." + ad
} else {
n = n[:i]
}
return s + n
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) { func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
var tx Tx var tx Tx
err := json.Unmarshal(msg, &tx) err := json.Unmarshal(msg, &tx)
@ -44,13 +92,20 @@ func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
return nil, err return nil, err
} }
for i, vout := range tx.Vout { for i := range tx.Vout {
vout := &tx.Vout[i]
// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
vout.JsonValue = ""
if len(vout.ScriptPubKey.Addresses) == 1 { if len(vout.ScriptPubKey.Addresses) == 1 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0]) a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
tx.Vout[i].Address = a vout.Address = a
} }
} }
@ -127,7 +182,7 @@ func (p *BaseParser) PackTx(tx *Tx, height uint32, blockTime int64) ([]byte, err
Addresses: vo.ScriptPubKey.Addresses, Addresses: vo.ScriptPubKey.Addresses,
N: vo.N, N: vo.N,
ScriptPubKeyHex: hex, ScriptPubKeyHex: hex,
Value: vo.Value, ValueSat: vo.ValueSat.Bytes(),
} }
} }
pt := &ProtoTransaction{ pt := &ProtoTransaction{
@ -176,13 +231,15 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
} }
vout := make([]Vout, len(pt.Vout)) vout := make([]Vout, len(pt.Vout))
for i, pto := range pt.Vout { for i, pto := range pt.Vout {
var vs big.Int
vs.SetBytes(pto.ValueSat)
vout[i] = Vout{ vout[i] = Vout{
N: pto.N, N: pto.N,
ScriptPubKey: ScriptPubKey{ ScriptPubKey: ScriptPubKey{
Addresses: pto.Addresses, Addresses: pto.Addresses,
Hex: hex.EncodeToString(pto.ScriptPubKeyHex), Hex: hex.EncodeToString(pto.ScriptPubKeyHex),
}, },
Value: pto.Value, ValueSat: vs,
} }
if len(pto.Addresses) == 1 { if len(pto.Addresses) == 1 {
a, err := p.AddressFactory(pto.Addresses[0]) a, err := p.AddressFactory(pto.Addresses[0])

56
bchain/baseparser_test.go Normal file
View File

@ -0,0 +1,56 @@
package bchain
import (
"encoding/json"
"math/big"
"testing"
)
func NewBaseParser(adp int) *BaseParser {
return &BaseParser{
AmountDecimalPoint: adp,
}
}
var amounts = []struct {
a *big.Int
s string
adp int
alternative string
}{
{big.NewInt(123456789), "1.23456789", 8, "!"},
{big.NewInt(2), "0.00000002", 8, "!"},
{big.NewInt(300000000), "3", 8, "!"},
{big.NewInt(498700000), "4.987", 8, "!"},
{big.NewInt(567890), "0.00000000000056789", 18, "!"},
{big.NewInt(-100000000), "-1", 8, "!"},
{big.NewInt(-8), "-0.00000008", 8, "!"},
{big.NewInt(-89012345678), "-890.12345678", 8, "!"},
{big.NewInt(-12345), "-0.00012345", 8, "!"},
{big.NewInt(12345678), "0.123456789012", 8, "0.12345678"}, // test of truncation of too many decimal places
}
func TestBaseParser_AmountToDecimalString(t *testing.T) {
for _, tt := range amounts {
t.Run(tt.s, func(t *testing.T) {
if got := NewBaseParser(tt.adp).AmountToDecimalString(tt.a); got != tt.s && got != tt.alternative {
t.Errorf("BaseParser.AmountToDecimalString() = %v, want %v", got, tt.s)
}
})
}
}
func TestBaseParser_AmountToBigInt(t *testing.T) {
for _, tt := range amounts {
t.Run(tt.s, func(t *testing.T) {
got, err := NewBaseParser(tt.adp).AmountToBigInt(json.Number(tt.s))
if err != nil {
t.Errorf("BaseParser.AmountToBigInt() error = %v", err)
return
}
if got.Cmp(tt.a) != 0 {
t.Errorf("BaseParser.AmountToBigInt() = %v, want %v", got, tt.a)
}
})
}
}

View File

@ -49,6 +49,7 @@ func NewBCashParser(params *chaincfg.Params, c *btc.Configuration) (*BCashParser
BaseParser: &bchain.BaseParser{ BaseParser: &bchain.BaseParser{
AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) }, AddressFactory: func(addr string) (bchain.Address, error) { return newBCashAddress(addr, format) },
BlockAddressesToKeep: c.BlockAddressesToKeep, BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
}, },
Params: params, Params: params,
OutputScriptToAddressesFunc: outputScriptToAddresses, OutputScriptToAddressesFunc: outputScriptToAddresses,

View File

@ -7,6 +7,7 @@ import (
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -159,8 +160,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 0.00038812, ValueSat: *big.NewInt(38812),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87", Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
Addresses: []string{ Addresses: []string{
@ -190,8 +191,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: .1, ValueSat: *big.NewInt(10000000),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887", Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
Addresses: []string{ Addresses: []string{
@ -201,8 +202,8 @@ func init() {
Address: addr2, Address: addr2,
}, },
{ {
Value: 9.20081157, ValueSat: *big.NewInt(920081157),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87", Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
Addresses: []string{ Addresses: []string{

View File

@ -5,6 +5,7 @@ import (
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/big"
"github.com/cpacia/bchutil" "github.com/cpacia/bchutil"
"github.com/golang/glog" "github.com/golang/glog"
@ -132,7 +133,7 @@ func (b *BCashRPC) GetBlockFull(hash string) (*bchain.Block, error) {
} }
// EstimateSmartFee returns fee estimation. // EstimateSmartFee returns fee estimation.
func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) { func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
glog.V(1).Info("rpc: estimatesmartfee ", blocks) glog.V(1).Info("rpc: estimatesmartfee ", blocks)
res := btc.ResEstimateSmartFee{} res := btc.ResEstimateSmartFee{}
@ -141,13 +142,18 @@ func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, err
// conservative param is omitted // conservative param is omitted
err := b.Call(&req, &res) err := b.Call(&req, &res)
var r big.Int
if err != nil { if err != nil {
return 0, err return r, err
} }
if res.Error != nil { if res.Error != nil {
return 0, res.Error return r, res.Error
} }
return res.Result.Feerate, nil r, err = b.Parser.AmountToBigInt(res.Result.Feerate)
if err != nil {
return r, err
}
return r, nil
} }
func isErrBlockNotFound(err *bchain.RPCError) bool { func isErrBlockNotFound(err *bchain.RPCError) bool {

View File

@ -18,6 +18,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"reflect" "reflect"
"time" "time"
@ -175,12 +176,12 @@ func (c *blockChainWithMetrics) GetTransactionForMempool(txid string) (v *bchain
return c.b.GetTransactionForMempool(txid) return c.b.GetTransactionForMempool(txid)
} }
func (c *blockChainWithMetrics) EstimateSmartFee(blocks int, conservative bool) (v float64, err error) { func (c *blockChainWithMetrics) EstimateSmartFee(blocks int, conservative bool) (v big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EstimateSmartFee", s, err) }(time.Now()) defer func(s time.Time) { c.observeRPCLatency("EstimateSmartFee", s, err) }(time.Now())
return c.b.EstimateSmartFee(blocks, conservative) return c.b.EstimateSmartFee(blocks, conservative)
} }
func (c *blockChainWithMetrics) EstimateFee(blocks int) (v float64, err error) { func (c *blockChainWithMetrics) EstimateFee(blocks int) (v big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EstimateFee", s, err) }(time.Now()) defer func(s time.Time) { c.observeRPCLatency("EstimateFee", s, err) }(time.Now())
return c.b.EstimateFee(blocks) return c.b.EstimateFee(blocks)
} }

View File

@ -5,6 +5,7 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"math/big"
vlq "github.com/bsm/go-vlq" vlq "github.com/bsm/go-vlq"
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
@ -30,6 +31,7 @@ func NewBitcoinParser(params *chaincfg.Params, c *Configuration) *BitcoinParser
&bchain.BaseParser{ &bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress, AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: c.BlockAddressesToKeep, BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
}, },
params, params,
outputScriptToAddresses, outputScriptToAddresses,
@ -123,8 +125,10 @@ func (p *BitcoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.T
// missing: Asm, // missing: Asm,
// missing: Type, // missing: Type,
} }
var vs big.Int
vs.SetInt64(out.Value)
vout[i] = bchain.Vout{ vout[i] = bchain.Vout{
Value: float64(out.Value) / 1E8, ValueSat: vs,
N: uint32(i), N: uint32(i),
ScriptPubKey: s, ScriptPubKey: s,
} }

View File

@ -5,6 +5,7 @@ package btc
import ( import (
"blockbook/bchain" "blockbook/bchain"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -152,8 +153,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 0.00038812, ValueSat: *big.NewInt(38812),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87", Hex: "a9146144d57c8aff48492c9dfb914e120b20bad72d6f87",
Addresses: []string{ Addresses: []string{
@ -183,8 +184,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: .1, ValueSat: *big.NewInt(10000000),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887", Hex: "a914cd668d781ece600efa4b2404dc91fd26b8b8aed887",
Addresses: []string{ Addresses: []string{
@ -194,8 +195,8 @@ func init() {
Address: addr2, Address: addr2,
}, },
{ {
Value: 9.20081157, ValueSat: *big.NewInt(920081157),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87", Hex: "a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a87",
Addresses: []string{ Addresses: []string{

View File

@ -8,6 +8,7 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil" "io/ioutil"
"math/big"
"net" "net"
"net/http" "net/http"
"time" "time"
@ -36,19 +37,21 @@ type BitcoinRPC struct {
} }
type Configuration struct { type Configuration struct {
CoinName string `json:"coin_name"` CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"` CoinShortcut string `json:"coin_shortcut"`
RPCURL string `json:"rpc_url"` RPCURL string `json:"rpc_url"`
RPCUser string `json:"rpc_user"` RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"` RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"` RPCTimeout int `json:"rpc_timeout"`
Parse bool `json:"parse"` Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"` MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"` Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"` BlockAddressesToKeep int `json:"block_addresses_to_keep"`
MempoolWorkers int `json:"mempool_workers"` MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"` MempoolSubWorkers int `json:"mempool_sub_workers"`
AddressFormat string `json:"address_format"` AddressFormat string `json:"address_format"`
SupportsEstimateFee bool `json:"supports_estimate_fee"`
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
} }
// NewBitcoinRPC returns new BitcoinRPC instance. // NewBitcoinRPC returns new BitcoinRPC instance.
@ -70,6 +73,9 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
if c.MempoolSubWorkers < 1 { if c.MempoolSubWorkers < 1 {
c.MempoolSubWorkers = 1 c.MempoolSubWorkers = 1
} }
// btc supports both calls, other coins overriding BitcoinRPC can change this
c.SupportsEstimateFee = true
c.SupportsEstimateSmartFee = true
transport := &http.Transport{ transport := &http.Transport{
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial, Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
@ -302,8 +308,8 @@ type CmdEstimateSmartFee struct {
type ResEstimateSmartFee struct { type ResEstimateSmartFee struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result struct { Result struct {
Feerate float64 `json:"feerate"` Feerate json.Number `json:"feerate"`
Blocks int `json:"blocks"` Blocks int `json:"blocks"`
} `json:"result"` } `json:"result"`
} }
@ -318,7 +324,7 @@ type CmdEstimateFee struct {
type ResEstimateFee struct { type ResEstimateFee struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result float64 `json:"result"` Result json.Number `json:"result"`
} }
// sendrawtransaction // sendrawtransaction
@ -617,7 +623,12 @@ func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) {
} }
// EstimateSmartFee returns fee estimation. // EstimateSmartFee returns fee estimation.
func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) { func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
// use EstimateFee if EstimateSmartFee is not supported
if !b.ChainConfig.SupportsEstimateSmartFee && b.ChainConfig.SupportsEstimateFee {
return b.EstimateFee(blocks)
}
glog.V(1).Info("rpc: estimatesmartfee ", blocks) glog.V(1).Info("rpc: estimatesmartfee ", blocks)
res := ResEstimateSmartFee{} res := ResEstimateSmartFee{}
@ -630,17 +641,27 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, e
} }
err := b.Call(&req, &res) err := b.Call(&req, &res)
var r big.Int
if err != nil { if err != nil {
return 0, err return r, err
} }
if res.Error != nil { if res.Error != nil {
return 0, res.Error return r, res.Error
} }
return res.Result.Feerate, nil r, err = b.Parser.AmountToBigInt(res.Result.Feerate)
if err != nil {
return r, err
}
return r, nil
} }
// EstimateFee returns fee estimation. // EstimateFee returns fee estimation.
func (b *BitcoinRPC) EstimateFee(blocks int) (float64, error) { func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
// use EstimateSmartFee if EstimateFee is not supported
if !b.ChainConfig.SupportsEstimateFee && b.ChainConfig.SupportsEstimateSmartFee {
return b.EstimateSmartFee(blocks, true)
}
glog.V(1).Info("rpc: estimatefee ", blocks) glog.V(1).Info("rpc: estimatefee ", blocks)
res := ResEstimateFee{} res := ResEstimateFee{}
@ -648,13 +669,18 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (float64, error) {
req.Params.Blocks = blocks req.Params.Blocks = blocks
err := b.Call(&req, &res) err := b.Call(&req, &res)
var r big.Int
if err != nil { if err != nil {
return 0, err return r, err
} }
if res.Error != nil { if res.Error != nil {
return 0, res.Error return r, res.Error
} }
return res.Result, nil r, err = b.Parser.AmountToBigInt(res.Result)
if err != nil {
return r, err
}
return r, nil
} }
// SendRawTransaction sends raw transaction. // SendRawTransaction sends raw transaction.
@ -685,13 +711,20 @@ func (b *BitcoinRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error)
Params: []string{txid}, Params: []string{txid},
} }
err := b.Call(&req, &res) err := b.Call(&req, &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if res.Error != nil { if res.Error != nil {
return nil, res.Error return nil, res.Error
} }
res.Result.FeeSat, err = b.Parser.AmountToBigInt(res.Result.Fee)
if err != nil {
return nil, err
}
res.Result.ModifiedFeeSat, err = b.Parser.AmountToBigInt(res.Result.ModifiedFee)
if err != nil {
return nil, err
}
return res.Result, nil return res.Result, nil
} }

View File

@ -24,6 +24,7 @@ func NewDashRPC(config json.RawMessage, pushHandler func(bchain.NotificationType
b.(*btc.BitcoinRPC), b.(*btc.BitcoinRPC),
} }
s.RPCMarshaler = btc.JSONMarshalerV1{} s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil return s, nil
} }
@ -53,8 +54,3 @@ func (b *DashRPC) Initialize() error {
return nil return nil
} }
// EstimateSmartFee returns fee estimation.
func (b *DashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -9,6 +9,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
@ -111,8 +112,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 27478.75452951, ValueSat: *big.NewInt(2747875452951),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914eef21768a546590993e313c7f3dfadf6a6efa1e888ac", Hex: "76a914eef21768a546590993e313c7f3dfadf6a6efa1e888ac",
Addresses: []string{ Addresses: []string{
@ -122,8 +123,8 @@ func init() {
Address: addr1, Address: addr1,
}, },
{ {
Value: 74.20567469, ValueSat: *big.NewInt(7420567469),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac", Hex: "76a914e0fee2ea29dd9c6c759d8341bd0da4c4f738cced88ac",
Addresses: []string{ Addresses: []string{
@ -153,8 +154,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 59890867.89818935, ValueSat: *big.NewInt(5989086789818935),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac", Hex: "76a9149355c01ed20057eac9fe0bbf8b07d87e62fe712d88ac",
Addresses: []string{ Addresses: []string{
@ -164,8 +165,8 @@ func init() {
Address: addr3, Address: addr3,
}, },
{ {
Value: 9999998.90000000, ValueSat: *big.NewInt(999999890000000),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac", Hex: "76a9145b4f2511c94e4fcaa8f8835b2458f8cb6542ca7688ac",
Addresses: []string{ Addresses: []string{

View File

@ -24,6 +24,7 @@ func NewDogecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC), b.(*btc.BitcoinRPC),
} }
s.RPCMarshaler = btc.JSONMarshalerV1{} s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil return s, nil
} }
@ -69,8 +70,3 @@ func (b *DogecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
} }
return b.GetBlockWithoutHeader(hash, height) return b.GetBlockWithoutHeader(hash, height)
} }
// EstimateSmartFee returns fee estimation.
func (b *DogecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -22,7 +22,11 @@ type EthereumParser struct {
// NewEthereumParser returns new EthereumParser instance // NewEthereumParser returns new EthereumParser instance
func NewEthereumParser() *EthereumParser { func NewEthereumParser() *EthereumParser {
return &EthereumParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}} return &EthereumParser{&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: 0,
AmountDecimalPoint: 18,
}}
} }
type rpcTransaction struct { type rpcTransaction struct {
@ -86,6 +90,10 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
} }
tx.BlockHash = bh tx.BlockHash = bh
h := hex.EncodeToString(b) h := hex.EncodeToString(b)
vs, err := hexutil.DecodeBig(tx.Value)
if err != nil {
return nil, err
}
return &bchain.Tx{ return &bchain.Tx{
Blocktime: blocktime, Blocktime: blocktime,
Confirmations: confirmations, Confirmations: confirmations,
@ -105,8 +113,8 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
N: 0, // there is always up to one To address N: 0, // there is always up to one To address
// Value - cannot be set, it does not fit precisely to float64 ValueSat: *vs,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
// Hex // Hex
Addresses: ta, Addresses: ta,
@ -286,9 +294,3 @@ func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) {
func (p *EthereumParser) IsUTXOChain() bool { func (p *EthereumParser) IsUTXOChain() bool {
return false return false
} }
// KeepBlockAddresses returns number of blocks which are to be kept in blockaddresses column
// do not use the blockaddresses for eth
func (p *EthereumParser) KeepBlockAddresses() int {
return 0
}

View File

@ -5,6 +5,7 @@ package eth
import ( import (
"blockbook/bchain" "blockbook/bchain"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -63,6 +64,68 @@ func TestEthParser_GetAddrIDFromAddress(t *testing.T) {
} }
} }
var (
testTx1, testTx2 bchain.Tx
testTxPacked1 = "08aebf0a1205012a05f20018a0f73622081234567890abcdef2a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f"
testTxPacked2 = "08ece40212050430e234001888a4012201213220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915"
)
func init() {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a")
if err == nil {
addr2, err = bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f")
}
if err != nil {
panic(err)
}
testTx1 = bchain.Tx{
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307831323334353637383930616263646566222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1311768467294899695),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
Address: addr1,
},
},
}
testTx2 = bchain.Tx{
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a2230783231222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(33),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
Address: addr2,
},
},
}
}
func TestEthereumParser_PackTx(t *testing.T) { func TestEthereumParser_PackTx(t *testing.T) {
type args struct { type args struct {
tx *bchain.Tx tx *bchain.Tx
@ -77,61 +140,27 @@ func TestEthereumParser_PackTx(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "with 0x prefix", name: "1",
args: args{ args: args{
tx: &bchain.Tx{ tx: &testTx1,
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
},
},
},
height: 2870000, height: 2870000,
blockTime: 1521515026, blockTime: 1521515026,
}, },
want: "08aebf0a1205012a05f20018a0f7362a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f", want: testTxPacked1,
}, },
{ {
name: "without 0x prefix", name: "2",
args: args{ args: args{
tx: &bchain.Tx{ tx: &testTx2,
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a22307831626330313539643533306536303030222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
},
},
},
height: 2871048, height: 2871048,
blockTime: 1521533434, blockTime: 1521533434,
}, },
want: "08ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915", want: testTxPacked2,
}, },
} }
p := NewEthereumParser()
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser()
got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime) got, err := p.PackTx(tt.args.tx, tt.args.height, tt.args.blockTime)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("EthereumParser.PackTx() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("EthereumParser.PackTx() error = %v, wantErr %v", err, tt.wantErr)
@ -146,18 +175,6 @@ func TestEthereumParser_PackTx(t *testing.T) {
} }
func TestEthereumParser_UnpackTx(t *testing.T) { func TestEthereumParser_UnpackTx(t *testing.T) {
var (
addr1, addr2 bchain.Address
err error
)
addr1, err = bchain.NewBaseAddress("0x682b7903a11098cf770c7aef4aa02a85b3f3601a")
if err == nil {
addr2, err = bchain.NewBaseAddress("0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f")
}
if err != nil {
panic(err)
}
type args struct { type args struct {
hex string hex string
} }
@ -170,57 +187,21 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "1", name: "1",
args: args{hex: "08aebf0a1205012a05f20018a0f7362a24f025caaf00000000000000000000000000000000000000000000000000000000000002253220e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d38f095af014092f4c1d5054a14682b7903a11098cf770c7aef4aa02a85b3f3601a5214dacc9c61754a0c4616fc5323dc946e89eb272302580162011b6a201bd40a31122c03918df6d166d740a6a3a22f08a25934ceb1688c62977661c80c7220607fbc15c1f7995a4258f5a9bccc63b040362d1991d5efe1361c56222e4ca89f"}, args: args{hex: testTxPacked1},
want: &bchain.Tx{ want: &testTx1,
Blocktime: 1521515026,
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
Time: 1521515026,
Txid: "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
Vin: []bchain.Vin{
{
Addresses: []string{"0xdacc9c61754a0c4616fc5323dc946e89eb272302"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x682b7903a11098cf770c7aef4aa02a85b3f3601a"},
},
Address: addr1,
},
},
},
want1: 2870000, want1: 2870000,
}, },
{ {
name: "1", name: "2",
args: args{hex: "08ece40212050430e234001888a40122081bc0159d530e60003220cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b38889eaf0140fa83c3d5054a14555ee11fbddc0e49a9bab358a8941ad95ffdb48f52143e3a3d69dc66ba10737f531ed088954a9ec89d97580a6201296a20f7161c170d43573ad9c8d701cdaf714ff2a548a562b0dc639230d17889fcd40572203c4977fc90385a27efa0032e17b49fd575b2826cb56e3d1ecf21524f2a94f915"}, args: args{hex: testTxPacked2},
want: &bchain.Tx{ want: &testTx2,
Blocktime: 1521533434,
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a22307831626330313539643533306536303030222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22626c6f636b4e756d626572223a223078326263663038222c2266726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
Time: 1521533434,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
},
},
Vout: []bchain.Vout{
{
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
},
Address: addr2,
},
},
},
want1: 2871048, want1: 2871048,
}, },
} }
p := NewEthereumParser()
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := NewEthereumParser()
b, err := hex.DecodeString(tt.args.hex) b, err := hex.DecodeString(tt.args.hex)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -479,12 +479,12 @@ func (b *EthereumRPC) GetMempool() ([]string, error) {
} }
// EstimateFee returns fee estimation. // EstimateFee returns fee estimation.
func (b *EthereumRPC) EstimateFee(blocks int) (float64, error) { func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) {
return b.EstimateSmartFee(blocks, true) return b.EstimateSmartFee(blocks, true)
} }
// EstimateSmartFee returns fee estimation. // EstimateSmartFee returns fee estimation.
func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) { func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (big.Int, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel() defer cancel()
// TODO - what parameters of msg to use to get better estimate, maybe more data from the wallet are needed // TODO - what parameters of msg to use to get better estimate, maybe more data from the wallet are needed
@ -493,10 +493,12 @@ func (b *EthereumRPC) EstimateSmartFee(blocks int, conservative bool) (float64,
To: &a, To: &a,
} }
g, err := b.client.EstimateGas(ctx, msg) g, err := b.client.EstimateGas(ctx, msg)
var r big.Int
if err != nil { if err != nil {
return 0, err return r, err
} }
return float64(g), nil r.SetUint64(g)
return r, nil
} }
// SendRawTransaction sends raw transaction. // SendRawTransaction sends raw transaction.

View File

@ -6,6 +6,7 @@ import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -173,8 +174,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 12.52000000, ValueSat: *big.NewInt(1252000000),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9141ae882e788091732da6910595314447c9e38bd8d88ac", Hex: "76a9141ae882e788091732da6910595314447c9e38bd8d88ac",
Addresses: []string{ Addresses: []string{
@ -184,8 +185,8 @@ func init() {
Address: addr1, Address: addr1,
}, },
{ {
Value: 0.01000487, ValueSat: *big.NewInt(1000487),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac", Hex: "76a9146b474cbf0f6004329b630bdd4798f2c23d1751b688ac",
Addresses: []string{ Addresses: []string{

View File

@ -24,6 +24,7 @@ func NewLitecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC), b.(*btc.BitcoinRPC),
} }
s.RPCMarshaler = btc.JSONMarshalerV2{} s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil return s, nil
} }
@ -54,8 +55,3 @@ func (b *LitecoinRPC) Initialize() error {
return nil return nil
} }
// EstimateFee returns fee estimation.
func (b *LitecoinRPC) EstimateFee(blocks int) (float64, error) {
return b.EstimateSmartFee(blocks, true)
}

View File

@ -24,6 +24,7 @@ func NewNamecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC), b.(*btc.BitcoinRPC),
} }
s.RPCMarshaler = btc.JSONMarshalerV1{} s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil return s, nil
} }
@ -69,8 +70,3 @@ func (b *NamecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
} }
return b.GetBlockWithoutHeader(hash, height) return b.GetBlockWithoutHeader(hash, height)
} }
// EstimateSmartFee returns fee estimation.
func (b *NamecoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
return b.EstimateFee(blocks)
}

View File

@ -6,6 +6,7 @@ import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -105,8 +106,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 0.10781192, ValueSat: *big.NewInt(10781192),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91499b16da88a7e29b913b6131df2644d6d06cb331b88ac", Hex: "76a91499b16da88a7e29b913b6131df2644d6d06cb331b88ac",
Addresses: []string{ Addresses: []string{
@ -116,8 +117,8 @@ func init() {
Address: addr1, Address: addr1,
}, },
{ {
Value: 0.5000000, ValueSat: *big.NewInt(50000000),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "a91446eb90e002f137f05385896c882fe000cc2e967f87", Hex: "a91446eb90e002f137f05385896c882fe000cc2e967f87",
Addresses: []string{ Addresses: []string{

View File

@ -24,6 +24,7 @@ func NewVertcoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC), b.(*btc.BitcoinRPC),
} }
s.RPCMarshaler = btc.JSONMarshalerV2{} s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil return s, nil
} }
@ -54,8 +55,3 @@ func (b *VertcoinRPC) Initialize() error {
return nil return nil
} }
// EstimateFee returns fee estimation.
func (b *VertcoinRPC) EstimateFee(blocks int) (float64, error) {
return b.EstimateSmartFee(blocks, true)
}

View File

@ -20,12 +20,13 @@ type ZCashParser struct {
*bchain.BaseParser *bchain.BaseParser
} }
// NewZCAshParser returns new ZCAshParser instance // NewZCashParser returns new ZCashParser instance
func NewZCashParser(c *btc.Configuration) *ZCashParser { func NewZCashParser(c *btc.Configuration) *ZCashParser {
return &ZCashParser{ return &ZCashParser{
&bchain.BaseParser{ &bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress, AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: c.BlockAddressesToKeep, BlockAddressesToKeep: c.BlockAddressesToKeep,
AmountDecimalPoint: 8,
}, },
} }
} }

View File

@ -6,6 +6,7 @@ import (
"blockbook/bchain" "blockbook/bchain"
"blockbook/bchain/coins/btc" "blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big"
"reflect" "reflect"
"testing" "testing"
) )
@ -13,8 +14,8 @@ import (
var ( var (
testTx1, testTx2 bchain.Tx testTx1, testTx2 bchain.Tx
testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d505200028b0eb113299010a0012203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c1800226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a4b091e6c90cd3ebc664010001a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e76317951624441" testTxPacked1 = "0a20e64aac0c211ad210c90934f06b1cc932327329e41a9f70c6eb76f79ef798b7b812ab1002000000019c012650c99d0ef761e863dbb966babf2cb7a7a2b5d90b1461c09521c473d23d000000006b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c83ffffffff018eec1a3c040000001976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac000000000162b4fc6b0000000000000000000000006ffa88c89b74f0f82e24744296845a0d0113b132ff5dfc2af34e6418eb15206af53078c4dd475cf143cd9a427983f5993622464b53e3a37d2519a946492c3977e30f0866550b9097222993a439a39260ac5e7d36aef38c7fdd1df3035a2d5817a9c20526e38f52f822d4db9d2f0156c4119d786d6e3a060ca871df7fae9a5c3a9c921b38ddc6414b13d16aa807389c68016e54bd6a9eb3b23a6bc7bf152e6dba15e9ec36f95dab15ad8f4a92a9d0309bbd930ef24bb7247bf534065c1e2f5b42e2c80eb59f48b4da6ec522319e065f8c4e463f95cc7fcad8d7ee91608e3c0ffcaa44129ba2d2da45d9a413919eca41af29faaf806a3eeb823e5a6c51afb1ec709505d812c0306bd76061a0a62d207355ad44d1ffce2b9e1dfd0818f79bd0f8e4031116b71fee2488484f17818b80532865773166cd389929e8409bb94e3948bd2e0215ef96d4e29d094590fda0de50715c11ff47c03380bb1d31b14e5b4ad8a372ca0b03364ef85f086b8a8eb5c56c3b1aee33e2cfbf1b2be1a3fb41b14b2c432b5d04d54c058fa87a96ae1d65d61b79360d09acc1e25a883fd7ae9a2a734a03362903021401c243173e1050b5cdb459b9ffc07c95e920f026618952d3a800b2e47e03b902084aed7ee8466a65d34abdbbd292781564dcd9b7440029d48c2640ebc196d4b40217f2872c1d0c1c9c2abf1147d6a5a9501895bc92960bfa182ceeb76a658224f1022bc53c4c1cd6888d72a152dc1aec5ba8a1d750fb7e498bee844d3481e4b4cd210227f94f775744185c9f24571b7df0c1c694cb2d3e4e9b955ed0b1caad2b02b5702139c4fbba03f0e422b2f3e4fc822b4f58baf32e7cd217cdbdec8540cb13d6496f271959b72a05e130eeffbe5b9a7fcd2793347cd9c0ea695265669844c363190f690c52a600cf413c3f00bdc5e9d1539e0cc63f4ec2945e0d86e6304a6deb5651e73eac21add5a641dfc95ab56200ed40d81f76755aee4659334c17ed3841ca5a5ab22f923956be1d264be2b485a0de55404510ece5c73d6626798be688f9dc18b69846acfe897a357cc4afe31f57fea32896717f124290e68f36f849fa6ecf76e02087f8c19dbc566135d7fa2daca2d843b9cc5bc3897d35f1de7d174f6407658f4a3706c12cea53d880b4d8c4d45b3f0d210214f815be49a664021a4a44b4a63e06a41d76b46f9aa6bad248e8d1a974ae7bbae5ea8ac269447db91637a19346729083cad5aebd5ff43ea13d04783068e9136da321b1152c666d2995d0ca06b26541deac62f4ef91f0e4af445b18a5c2a17c96eada0b27f85bb26dfb8f16515114c6b9f88037e2b85b3b84b65822eb99c992d99d12dcf9c71e5b46a586016faf5758483a716566db95b42187c101df68ca0554824e1c23cf0302bea03ad0a146af57e91794a268b8c82d78211718c8b5fea286f5de72fc7dfffecddcc02413525c472cb26022641d4bec2b8b7e71a7beb9ee18b82632799498eeee9a351cb9431a8d1906d5164acdf351bd538c3e9d1da8a211fe1cd18c44e72d8cdf16ce3fc9551552c05d52846ea7ef619232102588395cc2bcce509a4e7f150262a76c15475496c923dfce6bfc05871467ee7c213b39ea365c010083e0b1ba8926d3a9e586d8b11c9bab2a47d888bc7cb1a226c0086a1530e295d0047547006f4c8f1c24cdd8e16bb3845749895dec95f03fcda97d3224f6875b1b7b1c819d2fd35dd30968a3c82bc480d10082caf9d9dda8f9ec649c136c7fa07978099d97eaf4abfdc9854c266979d3cfc868f60689b6e3098b6c52a21796fe7c259d9a0dadf1b6efa59297d4c8c902febe7acf826eed30d40d2ac5119be91b51f4839d94599872c9a93c3e2691294914034001d3a278cb4a84d4ae048c0201a97e4cf1341ee663a162f5b586355018b9e5e30624ccdbeacf7d0382afacaf45f08e84d30c50bcd4e55c3138377261deb4e8c2931cd3c51cee94a048ae4839517b6e6537a5c0148d3830a33fea719ef9b4fa437e4d5fecdb646397c19ee56a0973c362a81803895cdc67246352dc566689cb203f9ebda900a5537bbb75aa25ddf3d4ab87b88737a58d760e1d271f08265daae1fe056e71971a8b826e5b215a05b71f99315b167dd2ec78874189657acafac2b5eeb9a901913f55f7ab69e1f9b203504448d414e71098b932a2309db57257eb3fef9de2f2a5a69aa46747d7b827df838345d38b95772bdab8c178c45777b92e8773864964b8e12ae29dbc1b21bf6527589f6bec71ff1cbb9928477409811c2e8150c79c3f21027ee954863b716875d3e9adfc6fdb18cd57a49bb395ca5c42da56f3beb78aad3a7a487de34a870bca61f3cdec422061328c83c910ab32ea7403c354915b7ebee29e1fea5a75158197e4a68e103f017fd7de5a70148ee7ce59356b1a74f83492e14faaa6cd4870bcc004e6eb0114d3429b74ea98fe2851b4553467a7660074e69b040aa31220d0e405d9166dbaf15e3ae2d8ec3b049ed99d17e0743bb6a1a7c3890bbdb7117f7374ad7a59aa1ab47d10445b28f4bc033794a71f88a8bf024189e9d27f9dc5859a4296437585b215656f807aca9dad35747494a43b8a1cf38be2b18a13de32a262ab29f9ba271c4fbce1a470a8243ebf9e7fd37b09262314afbb9a7e180218a0f1c9d505200028b0eb113299010a0012203dd273c42195c061140bd9b5a2a7b72cbfba66b9db63e861f70e9dc95026019c1800226b483045022100f220f48c5267ef92a1e7a4d3b44fe9d97cce76eeba2785d45a0e2620b70e8d7302205640bc39e197ce19d95a98a3239af0f208ca289c067f80c97d8e411e61da5dee0121021721e83315fb5282f1d9d2a11892322df589bccd9cef45517b5fb3cfd3055c8328ffffffff0f3a490a05043c1aec8e10001a1976a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac222374315934794c31344143486141626a656d6b647057376e594e48576e76317951624441"
testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea113299010a0012206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c1800226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a4b09257b2170264d504010001a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a4b099a9999999999b93f10011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a6970646467" testTxPacked2 = "0a20bb47a9dd926de63e9d4f8dac58c3f63f4a079569ed3b80e932274a80f60e58b512e20101000000019cafb5c287980e6e5afb47339f6c1c81136d8255f5bd5226b36b01288494c46f000000006b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea22880feffffff0223a7a784010000001976a914826f87806ddd4643730be99b41c98acc379e83db88ac80969800000000001976a914e395634b7684289285926d4c64db395b783720ec88ac6e75040018e4b1c9d50520eeea1128f9ea113299010a0012206fc4948428016bb32652bdf555826d13811c6c9f3347fb5a6e0e9887c2b5af9c1800226b483045022100c92b2f3c54918fa26288530c63a58197ea4974e5b6d92db792dd9717e6d9183c02204e577254213675466a6adad3ae6e9384cf8269fb2dd9943b86fac0c0ad8e3f98012102c99dab469e63b232488b3e7acb9cfcab7e5755f61aad318d9e06b38e5ea2288028feffffff0f3a490a050184a7a72310001a1976a914826f87806ddd4643730be99b41c98acc379e83db88ac22237431566d4854547770457477766f6a786f644e32435351714c596931687a59336341713a470a0398968010011a1976a914e395634b7684289285926d4c64db395b783720ec88ac222374316563784d587070685554525158474c586e56684a367563714433445a6970646467"
) )
func init() { func init() {
@ -51,8 +52,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 181.88266638, ValueSat: *big.NewInt(18188266638),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac", Hex: "76a9149bb8229741305d8316ba3ca6a8d20740ce33c24188ac",
Addresses: []string{ Addresses: []string{
@ -82,8 +83,8 @@ func init() {
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
Value: 65.20547107, ValueSat: *big.NewInt(6520547107),
N: 0, N: 0,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914826f87806ddd4643730be99b41c98acc379e83db88ac", Hex: "76a914826f87806ddd4643730be99b41c98acc379e83db88ac",
Addresses: []string{ Addresses: []string{
@ -93,8 +94,8 @@ func init() {
Address: addr2, Address: addr2,
}, },
{ {
Value: .1, ValueSat: *big.NewInt(10000000),
N: 1, N: 1,
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914e395634b7684289285926d4c64db395b783720ec88ac", Hex: "76a914e395634b7684289285926d4c64db395b783720ec88ac",
Addresses: []string{ Addresses: []string{

View File

@ -22,6 +22,7 @@ func NewZCashRPC(config json.RawMessage, pushHandler func(bchain.NotificationTyp
BitcoinRPC: b.(*btc.BitcoinRPC), BitcoinRPC: b.(*btc.BitcoinRPC),
} }
z.RPCMarshaler = btc.JSONMarshalerV1{} z.RPCMarshaler = btc.JSONMarshalerV1{}
z.ChainConfig.SupportsEstimateSmartFee = false
return z, nil return z, nil
} }
@ -114,14 +115,6 @@ func (z *ZCashRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return z.GetTransaction(txid) return z.GetTransaction(txid)
} }
// EstimateSmartFee returns fee estimation.
func (z *ZCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, error) {
glog.V(1).Info("rpc: estimatesmartfee")
// return z.estimateFee(blocks)
return z.EstimateFee(blocks)
}
// GetMempoolEntry returns mempool data for given transaction // GetMempoolEntry returns mempool data for given transaction
func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) { func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
return nil, errors.New("GetMempoolEntry: not implemented") return nil, errors.New("GetMempoolEntry: not implemented")

View File

@ -1,8 +1,7 @@
// +build integration
package rpc package rpc
import ( import (
"blockbook/bchain"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -83,7 +82,7 @@ func LoadRPCConfig(coin string) (json.RawMessage, error) {
return json.RawMessage(fmt.Sprintf(t, coin, c.URL, c.User, c.Pass)), nil return json.RawMessage(fmt.Sprintf(t, coin, c.URL, c.User, c.Pass)), nil
} }
func LoadTestData(coin string) (*TestData, error) { func LoadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) {
b, err := readDataFile(".", "bchain/tests/rpc/testdata", coin+".json") b, err := readDataFile(".", "bchain/tests/rpc/testdata", coin+".json")
if err != nil { if err != nil {
return nil, err return nil, err
@ -93,5 +92,16 @@ func LoadTestData(coin string) (*TestData, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// convert amounts in test json to bit.Int and clear the temporary JsonValue
for _, tx := range v.TxDetails {
for i := range tx.Vout {
vout := &tx.Vout[i]
vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
vout.JsonValue = ""
}
}
return &v, nil return &v, nil
} }

View File

@ -1,5 +1,3 @@
// +build integration
package rpc package rpc
import ( import (
@ -9,6 +7,7 @@ import (
"net" "net"
"reflect" "reflect"
"testing" "testing"
"time"
"github.com/deckarep/golang-set" "github.com/deckarep/golang-set"
) )
@ -56,7 +55,7 @@ func NewTest(coin string, factory TestChainFactoryFunc) (*Test, error) {
return nil, err return nil, err
} }
} else { } else {
td, err = LoadTestData(coin) td, err = LoadTestData(coin, cli.GetChainParser())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -162,7 +161,7 @@ func (rt *Test) TestGetTransaction(t *testing.T) {
got.Confirmations = 0 got.Confirmations = 0
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransaction() got %v, want %v", got, want) t.Errorf("GetTransaction() got %+v, want %+v", got, want)
} }
} }
} }
@ -181,7 +180,7 @@ func (rt *Test) TestGetTransactionForMempool(t *testing.T) {
// transactions parsed from JSON may contain additional data // transactions parsed from JSON may contain additional data
got.Confirmations, got.Blocktime, got.Time = 0, 0, 0 got.Confirmations, got.Blocktime, got.Time = 0, 0, 0
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransactionForMempool() got %v, want %v", got, want) t.Errorf("GetTransactionForMempool() got %+v, want %+v", got, want)
} }
} }
} }
@ -329,8 +328,8 @@ func (rt *Test) TestGetMempoolEntry(t *testing.T) {
if e.Size <= 0 { if e.Size <= 0 {
t.Errorf("GetMempoolEntry() got zero or negative size %d", e.Size) t.Errorf("GetMempoolEntry() got zero or negative size %d", e.Size)
} }
if e.Fee <= 0 { if e.FeeSat.Sign() != 1 {
t.Errorf("GetMempoolEntry() got zero or negative fee %f", e.Fee) t.Errorf("GetMempoolEntry() got zero or negative fee %v", e.FeeSat.String())
} }
// done // done
@ -347,8 +346,11 @@ func (rt *Test) TestEstimateSmartFee(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if fee != -1 && fee < 0 { if fee.Sign() == -1 {
t.Errorf("EstimateSmartFee() returned unexpected fee rate: %f", fee) sf := rt.Client.GetChainParser().AmountToDecimalString(&fee)
if sf != "-1" {
t.Errorf("EstimateSmartFee() returned unexpected fee rate: %v", sf)
}
} }
} }
} }
@ -361,8 +363,11 @@ func (rt *Test) TestEstimateFee(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if fee != -1 && fee < 0 { if fee.Sign() == -1 {
t.Errorf("EstimateFee() returned unexpected fee rate: %f", fee) sf := rt.Client.GetChainParser().AmountToDecimalString(&fee)
if sf != "-1" {
t.Errorf("EstimateFee() returned unexpected fee rate: %v", sf)
}
} }
} }
} }
@ -385,6 +390,7 @@ func (rt *Test) TestGetBestBlockHash(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if hash != hh { if hash != hh {
time.Sleep(time.Millisecond * 100)
continue continue
} }

View File

@ -70,6 +70,7 @@
"blocktime":1531135480, "blocktime":1531135480,
"time":1531135480, "time":1531135480,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "fe496933e0a3582ef020bd35c38fe8244a80fa7c63e7607f6f9ccb0806f419e4", "txid": "fe496933e0a3582ef020bd35c38fe8244a80fa7c63e7607f6f9ccb0806f419e4",
@ -175,6 +176,7 @@
"blocktime":1531135480, "blocktime":1531135480,
"time":1531135480, "time":1531135480,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "52f37874fd5a2497c5d84619ab415ca17ebb1d49d559f0d468869f70537354b9", "txid": "52f37874fd5a2497c5d84619ab415ca17ebb1d49d559f0d468869f70537354b9",

View File

@ -12,6 +12,7 @@
"blocktime":1529571678, "blocktime":1529571678,
"time":1529571678, "time":1529571678,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"coinbase": "03fbf212055374617368", "coinbase": "03fbf212055374617368",
@ -34,6 +35,7 @@
"blocktime":1529571678, "blocktime":1529571678,
"time":1529571678, "time":1529571678,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "58ff2450a71b3c228d17d04ec8edcaae452bc97d5ccc4591e2741fd8b031d221", "txid": "58ff2450a71b3c228d17d04ec8edcaae452bc97d5ccc4591e2741fd8b031d221",

View File

@ -120,6 +120,7 @@
"blocktime": 1529915213, "blocktime": 1529915213,
"time": 1529915213, "time": 1529915213,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "83eff5bcc738d52e04528984cd5cf601b69e7df65b2b10ed2475c0072cdde14c", "txid": "83eff5bcc738d52e04528984cd5cf601b69e7df65b2b10ed2475c0072cdde14c",
@ -153,6 +154,7 @@
"blocktime": 1529915213, "blocktime": 1529915213,
"time": 1529915213, "time": 1529915213,
"locktime": 529149, "locktime": 529149,
"version": 2,
"vin": [ "vin": [
{ {
"txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7", "txid": "413988d546516707f3b20ca9876e026a9472bd814662e42f37f4e57a15713da7",

View File

@ -43,6 +43,7 @@
"blocktime": 1528788394, "blocktime": 1528788394,
"time": 1528788394, "time": 1528788394,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "4097f5265397047bffe219a1bca65ea5726402c3d9aeecd577fa1c274fc1b8a4", "txid": "4097f5265397047bffe219a1bca65ea5726402c3d9aeecd577fa1c274fc1b8a4",
@ -84,6 +85,7 @@
"blocktime": 1528788394, "blocktime": 1528788394,
"time": 1528788394, "time": 1528788394,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "80437ba4e81bde8b584258f5adad0d08dea60f1e38026344442ad59a4ef797c9", "txid": "80437ba4e81bde8b584258f5adad0d08dea60f1e38026344442ad59a4ef797c9",

View File

@ -27,6 +27,7 @@
"blocktime": 1530189699, "blocktime": 1530189699,
"time": 1530189699, "time": 1530189699,
"locktime": 0, "locktime": 0,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "a78824a88c089ea1344fe2a33c454024e6501e76cca2590515b390d85082bd23", "txid": "a78824a88c089ea1344fe2a33c454024e6501e76cca2590515b390d85082bd23",

View File

@ -31,6 +31,7 @@
"blocktime": 1528713762, "blocktime": 1528713762,
"time": 1528713762, "time": 1528713762,
"locktime": 139520, "locktime": 139520,
"version": 2,
"vin": [ "vin": [
{ {
"txid": "a41616c7585c98aeda98d6ff6766b15455e327c9472582b80289dab7597ad309", "txid": "a41616c7585c98aeda98d6ff6766b15455e327c9472582b80289dab7597ad309",
@ -64,6 +65,7 @@
"blocktime": 1528713762, "blocktime": 1528713762,
"time": 1528713762, "time": 1528713762,
"locktime": 139520, "locktime": 139520,
"version": 2,
"vin": [ "vin": [
{ {
"txid": "187ae015e41dff766f5a18ae705f59db950a6729a06fa5fd04630c362a9aee27", "txid": "187ae015e41dff766f5a18ae705f59db950a6729a06fa5fd04630c362a9aee27",

View File

@ -16,38 +16,21 @@
"0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0" "0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0"
], ],
"txDetails": { "txDetails": {
"0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d": { "0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0": {
"hex": "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d", "hex": "7b226e6f6e6365223a223078333632306136222c226761735072696365223a223078313261303566323030222c22676173223a22307835323038222c22746f223a22307831623137626331326166623635643563346238316139666632613037366234326131396661616136222c2276616c7565223a223078646530623662336137363430303030222c22696e707574223a223078222c2268617368223a22307837663064313430333239393431663132306235623366633735316533306164656238376232616562626663653561646364303231363630346133346236636330222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307838316237653038663635626466353634383630366338393939386139636338313634333937363437222c227472616e73616374696f6e496e646578223a22307862222c2276223a2230783162222c2272223a22307862666662323864633865373939383833366639356664616139396433626435666635346365663562313839636462383537333537666161326431616231393136222c2273223a22307833616462316365313264396664306538616662373064386639346534636538356137666639346465333966623636333139363638363435663464643138646432227d",
"txid": "0xe6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d", "txid": "0x7f0d140329941f120b5b3fc751e30adeb87b2aebbfce5adcd0216604a34b6cc0",
"blocktime": 1521515026, "blocktime": 1521515026,
"time": 1521515026, "time": 1521515026,
"vin": [ "vin": [
{ {
"addresses": ["0xdacc9c61754a0c4616fc5323dc946e89eb272302"] "addresses": ["0x81b7e08f65bdf5648606c89998a9cc8164397647"]
} }
], ],
"vout": [ "vout": [
{ {
"value": 1,
"scriptPubKey": { "scriptPubKey": {
"addresses": ["0x682b7903a11098cf770c7aef4aa02a85b3f3601a"] "addresses": ["0x1b17bc12afb65d5c4b81a9ff2a076b42a19faaa6"]
}
}
]
},
"0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3": {
"hex": "7b226e6f6e6365223a223078346566222c226761735072696365223a223078626134336237343030222c22676173223a2230783936653838222c22746f223a22307864303534323939633438326164356362333961336661383734373235663965326132306433343963222c2276616c7565223a22307830222c22696e707574223a2230783165626366613630303030303030303030303030303030303030303030303030633034383732383566313736663532306635636434363732306236383662366534366165636430373030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030313430303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030633461303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303530303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030353831303030303030303030303030303030303030303030303030633365313631343261306432366336616530623163656238643961373236653734653164613664663666656562653137653437306464626434656261663364623938333136386235316337623136363732613131623966306662383864623438323962653237346230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303034363336663665363230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303437323666373336313030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c2268617368223a22307831376565323335666330333539313535623235343139653065346336356439633530306466366537316538323838643665663032306430346363326632636233222c22626c6f636b4e756d626572223a223078326263616630222c2266726f6d223a22307838623461353365353739303739343466633261653539623837373066393331623639326232373062222c227472616e73616374696f6e496e646578223a22307830222c2276223a2230783163222c2272223a22307866633739633836353638323039313030323134616339353662373930653066383935656130343135313438643233613239353632356564393761633936333534222c2273223a223078373232303833616331643764663662626162393939383537616163323561336665646265386130633561326266376364653835393738363266373862313937227d",
"txid": "0x17ee235fc0359155b25419e0e4c65d9c500df6e71e8288d6ef020d04cc2f2cb3",
"blocktime": 1521515026,
"time": 1521515026,
"vin": [
{
"addresses": ["0x8b4a53e57907944fc2ae59b8770f931b692b270b"]
}
],
"vout": [
{
"scriptPubKey": {
"addresses": ["0xd054299c482ad5cb39a3fa874725f9e2a20d349c"]
} }
} }
] ]

View File

@ -39,6 +39,7 @@
"blocktime": 1530003649, "blocktime": 1530003649,
"time": 1530003649, "time": 1530003649,
"locktime": 404678, "locktime": 404678,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "223a4ff7a613173ffbc67f9efffe1b0c3fd5ba2471935a44cc81814038272901", "txid": "223a4ff7a613173ffbc67f9efffe1b0c3fd5ba2471935a44cc81814038272901",

View File

@ -14,6 +14,7 @@
"blocktime": 1529932850, "blocktime": 1529932850,
"time": 1529932850, "time": 1529932850,
"locktime": 952232, "locktime": 952232,
"version": 2,
"vin": [ "vin": [
{ {
"txid": "8ec70404bdd16559c589736863ee9d9f69431ba4e6b2ab3b1641b3f9f3821624", "txid": "8ec70404bdd16559c589736863ee9d9f69431ba4e6b2ab3b1641b3f9f3821624",
@ -47,6 +48,7 @@
"blocktime": 1529932850, "blocktime": 1529932850,
"time": 1529932850, "time": 1529932850,
"locktime": 952233, "locktime": 952233,
"version": 1,
"vin": [ "vin": [
{ {
"txid": "5765db7dd62cddada154b2c95505e298818a308cc1ce1e28d6e8565301cb8d74", "txid": "5765db7dd62cddada154b2c95505e298818a308cc1ce1e28d6e8565301cb8d74",

View File

@ -22,6 +22,7 @@
"blocktime": 1530264033, "blocktime": 1530264033,
"time": 1530264033, "time": 1530264033,
"locktime": 349399, "locktime": 349399,
"version": 3,
"vin": [ "vin": [
{ {
"txid": "9c9faac29b0fa1b0e683727f2973bfcb87e4baadd07e9c997b431a31713cb30c", "txid": "9c9faac29b0fa1b0e683727f2973bfcb87e4baadd07e9c997b431a31713cb30c",
@ -57,6 +58,7 @@
"blocktime": 1530264033, "blocktime": 1530264033,
"time": 1530264033, "time": 1530264033,
"locktime": 0, "locktime": 0,
"version": 3,
"vin": [ "vin": [
{ {
"txid": "4440c8e9d5b57da7ca0fb3f62ec13f392267aeb797c123bb01e850adf8573dd0", "txid": "4440c8e9d5b57da7ca0fb3f62ec13f392267aeb797c123bb01e850adf8573dd0",

View File

@ -13,6 +13,7 @@
"blocktime": 1528781777, "blocktime": 1528781777,
"time": 1528781777, "time": 1528781777,
"locktime": 251028, "locktime": 251028,
"version": 3,
"vin": [ "vin": [
{ {
"txid": "19a1d013b898239e9a2943faa07f8716b9be168bc8e001daf3625f535fde1a60", "txid": "19a1d013b898239e9a2943faa07f8716b9be168bc8e001daf3625f535fde1a60",
@ -48,6 +49,7 @@
"blocktime": 1528781777, "blocktime": 1528781777,
"time": 1528781777, "time": 1528781777,
"locktime": 251090, "locktime": 251090,
"version": 3,
"vin": [ "vin": [
{ {
"txid": "9acab5f13cf94074e75f5686b59fccd938f54b5f20ddddfcb6077c679a13c0ea", "txid": "9acab5f13cf94074e75f5686b59fccd938f54b5f20ddddfcb6077c679a13c0ea",

View File

@ -148,7 +148,7 @@ func (m *ProtoTransaction_VinType) GetAddresses() []string {
} }
type ProtoTransaction_VoutType struct { type ProtoTransaction_VoutType struct {
Value float64 `protobuf:"fixed64,1,opt,name=Value" json:"Value,omitempty"` ValueSat []byte `protobuf:"bytes,1,opt,name=ValueSat,proto3" json:"ValueSat,omitempty"`
N uint32 `protobuf:"varint,2,opt,name=N" json:"N,omitempty"` N uint32 `protobuf:"varint,2,opt,name=N" json:"N,omitempty"`
ScriptPubKeyHex []byte `protobuf:"bytes,3,opt,name=ScriptPubKeyHex,proto3" json:"ScriptPubKeyHex,omitempty"` ScriptPubKeyHex []byte `protobuf:"bytes,3,opt,name=ScriptPubKeyHex,proto3" json:"ScriptPubKeyHex,omitempty"`
Addresses []string `protobuf:"bytes,4,rep,name=Addresses" json:"Addresses,omitempty"` Addresses []string `protobuf:"bytes,4,rep,name=Addresses" json:"Addresses,omitempty"`
@ -159,11 +159,11 @@ func (m *ProtoTransaction_VoutType) String() string { return proto.Co
func (*ProtoTransaction_VoutType) ProtoMessage() {} func (*ProtoTransaction_VoutType) ProtoMessage() {}
func (*ProtoTransaction_VoutType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 1} } func (*ProtoTransaction_VoutType) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 1} }
func (m *ProtoTransaction_VoutType) GetValue() float64 { func (m *ProtoTransaction_VoutType) GetValueSat() []byte {
if m != nil { if m != nil {
return m.Value return m.ValueSat
} }
return 0 return nil
} }
func (m *ProtoTransaction_VoutType) GetN() uint32 { func (m *ProtoTransaction_VoutType) GetN() uint32 {
@ -196,26 +196,26 @@ func init() {
func init() { proto.RegisterFile("tx.proto", fileDescriptor0) } func init() { proto.RegisterFile("tx.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 330 bytes of a gzipped FileDescriptorProto // 331 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x4e, 0xc2, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x52, 0xc1, 0x6e, 0xea, 0x30,
0x10, 0x86, 0xb3, 0xb4, 0x14, 0x18, 0x21, 0x92, 0x89, 0x31, 0x0d, 0xf1, 0x50, 0x39, 0xf5, 0xd4, 0x10, 0x94, 0x89, 0x5f, 0x80, 0x7d, 0xa0, 0x87, 0xf6, 0xf0, 0x14, 0xa1, 0x1e, 0x52, 0x4e, 0x39,
0x03, 0xc6, 0x07, 0x50, 0x2f, 0x24, 0x1a, 0x42, 0x16, 0xd2, 0x7b, 0x5b, 0x36, 0xb0, 0x11, 0x77, 0xe5, 0x40, 0xd5, 0x0f, 0x68, 0x7b, 0x41, 0x6a, 0x85, 0x90, 0x83, 0x72, 0x4f, 0x82, 0x05, 0x56,
0xb1, 0xdd, 0x1a, 0x78, 0x16, 0x1f, 0xc1, 0x97, 0x34, 0x3b, 0x2d, 0x45, 0x48, 0xbc, 0xed, 0xff, 0xa9, 0x4d, 0x13, 0x47, 0x02, 0xa9, 0x3f, 0xd3, 0x73, 0x7f, 0xb2, 0xf2, 0x12, 0x42, 0x41, 0xea,
0xef, 0x4c, 0xff, 0x6f, 0xff, 0x14, 0xba, 0x66, 0x1f, 0xed, 0x72, 0x6d, 0x34, 0x7a, 0x69, 0xb6, 0x6d, 0x67, 0xbc, 0xe3, 0x19, 0x4f, 0x02, 0x3d, 0xbb, 0x8f, 0x77, 0xa5, 0xb1, 0x06, 0xfd, 0xbc,
0x49, 0xa4, 0x1a, 0x7f, 0xbb, 0x30, 0x9c, 0x5b, 0x67, 0x99, 0x27, 0xaa, 0x48, 0x32, 0x23, 0xb5, 0xd8, 0x64, 0x4a, 0x4f, 0x3e, 0x39, 0x8c, 0x16, 0x8e, 0x59, 0x96, 0x99, 0xae, 0xb2, 0xc2, 0x2a,
0x42, 0x04, 0x77, 0xb9, 0x97, 0x2b, 0x9f, 0x05, 0x2c, 0xec, 0x73, 0x3a, 0xe3, 0x10, 0x9c, 0xa9, 0xa3, 0x11, 0x81, 0x2f, 0xf7, 0x6a, 0x15, 0xb0, 0x90, 0x45, 0x03, 0x41, 0x33, 0x8e, 0xc0, 0x9b,
0xd8, 0xfb, 0x2d, 0xb2, 0xec, 0x11, 0xef, 0xa0, 0xf7, 0xbc, 0xd5, 0xd9, 0xbb, 0x91, 0x1f, 0xc2, 0xc9, 0x7d, 0xd0, 0x21, 0xca, 0x8d, 0x78, 0x03, 0xfd, 0xc7, 0xad, 0x29, 0x5e, 0xad, 0x7a, 0x93,
0x77, 0x02, 0x16, 0xba, 0xfc, 0x64, 0xe0, 0x08, 0xba, 0x6f, 0xc7, 0x4b, 0x37, 0x60, 0xe1, 0x80, 0x81, 0x17, 0xb2, 0x88, 0x8b, 0x33, 0x81, 0x63, 0xe8, 0xbd, 0x9c, 0x0e, 0x79, 0xc8, 0xa2, 0xa1,
0x37, 0x1a, 0x6f, 0xc1, 0x9b, 0x0a, 0xb9, 0xde, 0x18, 0xbf, 0x4d, 0x37, 0xb5, 0xc2, 0x09, 0x38, 0x68, 0x31, 0xfe, 0x07, 0x7f, 0x26, 0xd5, 0x7a, 0x63, 0x83, 0x3f, 0x74, 0xd2, 0x20, 0x9c, 0x82,
0xb1, 0x54, 0xbe, 0x17, 0x38, 0xe1, 0xd5, 0x24, 0x88, 0x2a, 0xc4, 0xe8, 0x12, 0x2f, 0x8a, 0xa5, 0x97, 0x2a, 0x1d, 0xf8, 0xa1, 0x17, 0xfd, 0x9d, 0x86, 0xf1, 0x31, 0x62, 0x7c, 0x1d, 0x2f, 0x4e,
0x5a, 0x1e, 0x76, 0x82, 0xdb, 0x61, 0x7c, 0x04, 0x37, 0xd6, 0xa5, 0xf1, 0x3b, 0xb4, 0x74, 0xff, 0x95, 0x5e, 0x1e, 0x76, 0x52, 0xb8, 0x65, 0xbc, 0x07, 0x9e, 0x9a, 0xda, 0x06, 0x5d, 0x12, 0xdd,
0xff, 0x92, 0x2e, 0x0d, 0x6d, 0xd1, 0xf8, 0xe8, 0x87, 0x41, 0xa7, 0xfe, 0x8e, 0x45, 0x7d, 0xd1, 0xfe, 0x2e, 0x32, 0xb5, 0x25, 0x15, 0xad, 0x8f, 0xbf, 0x18, 0x74, 0x9b, 0x7b, 0x5c, 0xd4, 0x27,
0x52, 0xa5, 0x49, 0x21, 0xe8, 0xc9, 0x3d, 0xde, 0xe8, 0xa6, 0x8a, 0xd6, 0x9f, 0x2a, 0xb0, 0x8e, 0xa3, 0x74, 0x9e, 0x55, 0x92, 0x9e, 0xdc, 0x17, 0x2d, 0x6e, 0xab, 0xe8, 0xfc, 0xa8, 0x02, 0x1b,
0x74, 0x08, 0x9e, 0xce, 0x38, 0x86, 0xfe, 0x22, 0xcb, 0xe5, 0xce, 0x2c, 0xe4, 0xda, 0xf6, 0xe4, 0x4b, 0x8f, 0xc2, 0xd3, 0x8c, 0x13, 0x18, 0x24, 0x45, 0xa9, 0x76, 0x36, 0x51, 0x6b, 0xd7, 0x13,
0xd2, 0xfc, 0x99, 0x67, 0x73, 0x16, 0xe2, 0xb3, 0x14, 0x2a, 0x13, 0xf5, 0xc3, 0x1b, 0x6d, 0xcb, 0xa7, 0xfd, 0x0b, 0xce, 0xf9, 0x24, 0xf2, 0xbd, 0x96, 0xba, 0x90, 0xcd, 0xc3, 0x5b, 0xec, 0xca,
0x7c, 0x5a, 0xad, 0x72, 0x51, 0x14, 0xa2, 0xa0, 0x02, 0x7a, 0xfc, 0x64, 0x8c, 0xbe, 0xa0, 0x7b, 0x7c, 0x58, 0xad, 0x4a, 0x59, 0x55, 0xb2, 0xa2, 0x02, 0xfa, 0xe2, 0x4c, 0x8c, 0x3f, 0xa0, 0x77,
0xe4, 0xc7, 0x1b, 0x68, 0xc7, 0xc9, 0xb6, 0xac, 0x50, 0x19, 0xaf, 0x04, 0xf6, 0x81, 0xcd, 0x08, 0xca, 0xef, 0x6e, 0x49, 0xb3, 0x6d, 0x2d, 0x93, 0xcc, 0x36, 0x1f, 0xa8, 0xc5, 0x38, 0x00, 0x36,
0x72, 0xc0, 0xd9, 0x0c, 0x43, 0xb8, 0xae, 0x92, 0xe7, 0x65, 0xfa, 0x2a, 0x0e, 0x16, 0xc8, 0x21, 0xa7, 0xa8, 0x43, 0xc1, 0xe6, 0x18, 0xc1, 0xbf, 0xa3, 0xff, 0xa2, 0xce, 0x9f, 0xe5, 0xc1, 0xc5,
0xa0, 0x4b, 0xfb, 0x3c, 0xd7, 0xbd, 0xc8, 0x4d, 0x3d, 0xfa, 0x59, 0x1e, 0x7e, 0x03, 0x00, 0x00, 0xf2, 0x48, 0x70, 0x4d, 0x5f, 0xba, 0xf3, 0x2b, 0xf7, 0xdc, 0xa7, 0x5f, 0xe6, 0xee, 0x3b, 0x00,
0xff, 0xff, 0x64, 0x1d, 0x38, 0xac, 0x38, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0x95, 0x9e, 0x00, 0x7a, 0x3e, 0x02, 0x00, 0x00,
} }

View File

@ -11,7 +11,7 @@ syntax = "proto3";
repeated string Addresses = 6; repeated string Addresses = 6;
} }
message VoutType { message VoutType {
double Value = 1; bytes ValueSat = 1;
uint32 N = 2; uint32 N = 2;
bytes ScriptPubKeyHex = 3; bytes ScriptPubKeyHex = 3;
repeated string Addresses = 4; repeated string Addresses = 4;

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/big"
) )
// errors with specific meaning returned by blockchain rpc // errors with specific meaning returned by blockchain rpc
@ -49,7 +50,8 @@ type Address interface {
} }
type Vout struct { type Vout struct {
Value float64 `json:"value"` ValueSat big.Int
JsonValue json.Number `json:"value"`
N uint32 `json:"n"` N uint32 `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"` ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Address Address Address Address
@ -89,18 +91,20 @@ type BlockHeader struct {
} }
type MempoolEntry struct { type MempoolEntry struct {
Size uint32 `json:"size"` Size uint32 `json:"size"`
Fee float64 `json:"fee"` FeeSat big.Int
ModifiedFee float64 `json:"modifiedfee"` Fee json.Number `json:"fee"`
Time float64 `json:"time"` ModifiedFeeSat big.Int
Height uint32 `json:"height"` ModifiedFee json.Number `json:"modifiedfee"`
DescendantCount uint32 `json:"descendantcount"` Time uint64 `json:"time"`
DescendantSize uint32 `json:"descendantsize"` Height uint32 `json:"height"`
DescendantFees uint32 `json:"descendantfees"` DescendantCount uint32 `json:"descendantcount"`
AncestorCount uint32 `json:"ancestorcount"` DescendantSize uint32 `json:"descendantsize"`
AncestorSize uint32 `json:"ancestorsize"` DescendantFees uint32 `json:"descendantfees"`
AncestorFees uint32 `json:"ancestorfees"` AncestorCount uint32 `json:"ancestorcount"`
Depends []string `json:"depends"` AncestorSize uint32 `json:"ancestorsize"`
AncestorFees uint32 `json:"ancestorfees"`
Depends []string `json:"depends"`
} }
type RPCError struct { type RPCError struct {
@ -132,8 +136,8 @@ type BlockChain interface {
GetMempool() ([]string, error) GetMempool() ([]string, error)
GetTransaction(txid string) (*Tx, error) GetTransaction(txid string) (*Tx, error)
GetTransactionForMempool(txid string) (*Tx, error) GetTransactionForMempool(txid string) (*Tx, error)
EstimateSmartFee(blocks int, conservative bool) (float64, error) EstimateSmartFee(blocks int, conservative bool) (big.Int, error)
EstimateFee(blocks int) (float64, error) EstimateFee(blocks int) (big.Int, error)
SendRawTransaction(tx string) (string, error) SendRawTransaction(tx string) (string, error)
// mempool // mempool
ResyncMempool(onNewTxAddr func(txid string, addr string)) (int, error) ResyncMempool(onNewTxAddr func(txid string, addr string)) (int, error)
@ -145,7 +149,7 @@ type BlockChain interface {
// BlockChainParser defines common interface to parsing and conversions of block chain data // BlockChainParser defines common interface to parsing and conversions of block chain data
type BlockChainParser interface { type BlockChainParser interface {
// self description // chain configuration description
// UTXO chains need "inputs" column in db, that map transactions to transactions that spend them // UTXO chains need "inputs" column in db, that map transactions to transactions that spend them
// non UTXO chains have mapping of address to input and output transactions directly in "outputs" column in db // non UTXO chains have mapping of address to input and output transactions directly in "outputs" column in db
IsUTXOChain() bool IsUTXOChain() bool
@ -153,6 +157,11 @@ type BlockChainParser interface {
// and used in case of fork // and used in case of fork
// if 0 the blockaddresses column is not used at all (usually non UTXO chains) // if 0 the blockaddresses column is not used at all (usually non UTXO chains)
KeepBlockAddresses() int KeepBlockAddresses() int
// AmountToDecimalString converts amount in big.Int to string with decimal point in the correct place
AmountToDecimalString(a *big.Int) string
// AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding
AmountToBigInt(n json.Number) (big.Int, error)
// address id conversions // address id conversions
GetAddrIDFromVout(output *Vout) ([]byte, error) GetAddrIDFromVout(output *Vout) ([]byte, error)
GetAddrIDFromAddress(address string) ([]byte, error) GetAddrIDFromAddress(address string) ([]byte, error)

View File

@ -175,7 +175,11 @@ func main() {
} }
index.SetInternalState(internalState) index.SetInternalState(internalState)
if internalState.DbState != common.DbStateClosed { if internalState.DbState != common.DbStateClosed {
glog.Warning("internalState: database in not closed state ", internalState.DbState, ", possibly previous ungraceful shutdown") if internalState.DbState == common.DbStateInconsistent {
glog.Error("internalState: database is in inconsistent state and cannot be used")
return
}
glog.Warning("internalState: database was left in open state, possibly previous ungraceful shutdown")
} }
if *computeColumnStats { if *computeColumnStats {

View File

@ -11,6 +11,8 @@ const (
DbStateClosed = uint32(iota) DbStateClosed = uint32(iota)
// DbStateOpen means db is open or application died without closing the db // DbStateOpen means db is open or application died without closing the db
DbStateOpen DbStateOpen
// DbStateInconsistent means db is in inconsistent state and cannot be used
DbStateInconsistent
) )
// InternalStateColumn contains the data of a db column // InternalStateColumn contains the data of a db column

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -213,17 +213,26 @@ func (w *SyncWorker) ConnectBlocksParallel(lower, higher uint32) error {
writeBlockDone := make(chan struct{}) writeBlockDone := make(chan struct{})
writeBlockWorker := func() { writeBlockWorker := func() {
defer close(writeBlockDone) defer close(writeBlockDone)
bc, err := w.db.InitBulkConnect()
if err != nil {
glog.Error("sync: InitBulkConnect error ", err)
}
lastBlock := lower - 1 lastBlock := lower - 1
keep := uint32(w.chain.GetChainParser().KeepBlockAddresses())
for b := range bch { for b := range bch {
if lastBlock+1 != b.Height { if lastBlock+1 != b.Height {
glog.Error("writeBlockWorker skipped block, last connected block", lastBlock, ", new block ", b.Height) glog.Error("writeBlockWorker skipped block, last connected block", lastBlock, ", new block ", b.Height)
} }
err := w.db.ConnectBlock(b) err := bc.ConnectBlock(b, b.Height+keep > higher)
if err != nil { if err != nil {
glog.Error("writeBlockWorker ", b.Height, " ", b.Hash, " error ", err) glog.Error("writeBlockWorker ", b.Height, " ", b.Hash, " error ", err)
} }
lastBlock = b.Height lastBlock = b.Height
} }
err = bc.Close()
if err != nil {
glog.Error("sync: bulkconnect.Close error ", err)
}
glog.Info("WriteBlock exiting...") glog.Info("WriteBlock exiting...")
} }
getBlockWorker := func(i int) { getBlockWorker := func(i int) {
@ -276,6 +285,7 @@ func (w *SyncWorker) ConnectBlocksParallel(lower, higher uint32) error {
} }
go writeBlockWorker() go writeBlockWorker()
var hash string var hash string
start := time.Now()
ConnectLoop: ConnectLoop:
for h := lower; h <= higher; { for h := lower; h <= higher; {
select { select {
@ -292,7 +302,8 @@ ConnectLoop:
} }
hch <- hashHeight{hash, h} hch <- hashHeight{hash, h}
if h > 0 && h%1000 == 0 { if h > 0 && h%1000 == 0 {
glog.Info("connecting block ", h, " ", hash) glog.Info("connecting block ", h, " ", hash, ", elapsed ", time.Since(start))
start = time.Now()
} }
h++ h++
} }
@ -346,25 +357,23 @@ func (w *SyncWorker) getBlockChain(out chan blockResult, done chan struct{}) {
} }
// DisconnectBlocks removes all data belonging to blocks in range lower-higher, // DisconnectBlocks removes all data belonging to blocks in range lower-higher,
// using block data from blockchain, if they are available,
// otherwise doing full scan
func (w *SyncWorker) DisconnectBlocks(lower uint32, higher uint32, hashes []string) error { func (w *SyncWorker) DisconnectBlocks(lower uint32, higher uint32, hashes []string) error {
glog.Infof("sync: disconnecting blocks %d-%d", lower, higher) glog.Infof("sync: disconnecting blocks %d-%d", lower, higher)
// if the chain uses Block to Addresses mapping, always use DisconnectBlockRange // if the chain is UTXO, always use DisconnectBlockRange
if w.chain.GetChainParser().KeepBlockAddresses() > 0 { if w.chain.GetChainParser().IsUTXOChain() {
return w.db.DisconnectBlockRange(lower, higher) return w.db.DisconnectBlockRangeUTXO(lower, higher)
} }
blocks := make([]*bchain.Block, len(hashes)) blocks := make([]*bchain.Block, len(hashes))
var err error var err error
// get all blocks first to see if we can avoid full scan // try to get all blocks first to see if we can avoid full scan
for i, hash := range hashes { for i, hash := range hashes {
blocks[i], err = w.chain.GetBlock(hash, 0) blocks[i], err = w.chain.GetBlock(hash, 0)
if err != nil { if err != nil {
// cannot get a block, we must do full range scan // cannot get a block, we must do full range scan
return w.db.DisconnectBlockRange(lower, higher) return w.db.DisconnectBlockRangeNonUTXO(lower, higher)
} }
} }
// then disconnect one after another // got all blocks to be disconnected, disconnect them one after another
for i, block := range blocks { for i, block := range blocks {
glog.Info("Disconnecting block ", (int(higher) - i), " ", block.Hash) glog.Info("Disconnecting block ", (int(higher) - i), " ", block.Hash)
if err = w.db.DisconnectBlock(block); err != nil { if err = w.db.DisconnectBlock(block); err != nil {

View File

@ -113,8 +113,10 @@ func formatUnixTime(ut int64) string {
return time.Unix(ut, 0).Format(time.RFC1123) return time.Unix(ut, 0).Format(time.RFC1123)
} }
func formatAmount(a float64) string { // for now return the string as it is
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%0.8f", a), "0"), ".") // in future could be used to do coin specific formatting
func formatAmount(a string) string {
return a
} }
// Run starts the server // Run starts the server

View File

@ -7,6 +7,7 @@ import (
"blockbook/db" "blockbook/db"
"encoding/json" "encoding/json"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time" "time"
@ -236,11 +237,11 @@ type txInputs struct {
// ScriptAsm *string `json:"scriptAsm"` // ScriptAsm *string `json:"scriptAsm"`
Sequence int64 `json:"sequence"` Sequence int64 `json:"sequence"`
Address *string `json:"address"` Address *string `json:"address"`
Satoshis int64 `json:"satoshis"` Satoshis string `json:"satoshis"`
} }
type txOutputs struct { type txOutputs struct {
Satoshis int64 `json:"satoshis"` Satoshis string `json:"satoshis"`
Script *string `json:"script"` Script *string `json:"script"`
// ScriptAsm *string `json:"scriptAsm"` // ScriptAsm *string `json:"scriptAsm"`
SpentTxID *string `json:"spentTxId,omitempty"` SpentTxID *string `json:"spentTxId,omitempty"`
@ -267,7 +268,7 @@ type resTx struct {
type addressHistoryItem struct { type addressHistoryItem struct {
Addresses map[string]addressHistoryIndexes `json:"addresses"` Addresses map[string]addressHistoryIndexes `json:"addresses"`
Satoshis int64 `json:"satoshis"` Satoshis string `json:"satoshis"`
Confirmations int `json:"confirmations"` Confirmations int `json:"confirmations"`
Tx resTx `json:"tx"` Tx resTx `json:"tx"`
} }
@ -329,7 +330,7 @@ func (s *SocketIoServer) getAddressHistory(addr []string, opts *addrOpts) (res r
for _, vout := range tx.Vout { for _, vout := range tx.Vout {
aoh := vout.ScriptPubKey.Hex aoh := vout.ScriptPubKey.Hex
ao := txOutputs{ ao := txOutputs{
Satoshis: int64(vout.Value*1E8 + 0.5), Satoshis: vout.ValueSat.String(),
Script: &aoh, Script: &aoh,
} }
if vout.Address != nil { if vout.Address != nil {
@ -452,6 +453,7 @@ func unmarshalEstimateSmartFee(params []byte) (blocks int, conservative bool, er
} }
type resultEstimateSmartFee struct { type resultEstimateSmartFee struct {
// for compatibility reasons use float64
Result float64 `json:"result"` Result float64 `json:"result"`
} }
@ -460,7 +462,7 @@ func (s *SocketIoServer) estimateSmartFee(blocks int, conservative bool) (res re
if err != nil { if err != nil {
return return
} }
res.Result = fee res.Result, err = strconv.ParseFloat(s.chainParser.AmountToDecimalString(&fee), 64)
return return
} }
@ -479,6 +481,7 @@ func unmarshalEstimateFee(params []byte) (blocks int, err error) {
} }
type resultEstimateFee struct { type resultEstimateFee struct {
// for compatibility reasons use float64
Result float64 `json:"result"` Result float64 `json:"result"`
} }
@ -487,7 +490,7 @@ func (s *SocketIoServer) estimateFee(blocks int) (res resultEstimateFee, err err
if err != nil { if err != nil {
return return
} }
res.Result = fee res.Result, err = strconv.ParseFloat(s.chainParser.AmountToDecimalString(&fee), 64)
return return
} }
@ -588,7 +591,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
a := vout.Address.String() a := vout.Address.String()
ai.Address = &a ai.Address = &a
} }
ai.Satoshis = int64(vout.Value*1E8 + 0.5) ai.Satoshis = vout.ValueSat.String()
} }
} }
hi = append(hi, ai) hi = append(hi, ai)
@ -596,7 +599,7 @@ func (s *SocketIoServer) getDetailedTransaction(txid string) (res resultGetDetai
for _, vout := range tx.Vout { for _, vout := range tx.Vout {
aos := vout.ScriptPubKey.Hex aos := vout.ScriptPubKey.Hex
ao := txOutputs{ ao := txOutputs{
Satoshis: int64(vout.Value*1E8 + 0.5), Satoshis: vout.ValueSat.String(),
Script: &aos, Script: &aos,
} }
if vout.Address != nil { if vout.Address != nil {