Use big.Int for all amounts

This commit is contained in:
Martin Boehm 2018-07-24 15:58:37 +02:00
parent 64b34c1dd8
commit b464f282a9
20 changed files with 261 additions and 187 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
@ -191,14 +193,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 +218,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,47 @@ 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 {
s := a.String()
if len(s) <= p.AmountDecimalPoint {
s = zeros[:p.AmountDecimalPoint-len(s)+1] + s
}
i := len(s) - p.AmountDecimalPoint
ad := strings.TrimRight(s[i:], "0")
if len(ad) > 0 {
s = s[:i] + "." + ad
} else {
s = s[:i]
}
return s
}
// 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 +87,18 @@ 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]
vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
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 +175,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 +224,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])

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

@ -17,6 +17,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"reflect" "reflect"
"time" "time"
@ -172,12 +173,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

@ -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,18 +37,20 @@ type BitcoinRPC struct {
} }
type Configuration struct { type Configuration struct {
CoinName string `json:"coin_name"` CoinName string `json:"coin_name"`
RPCURL string `json:"rpcURL"` RPCURL string `json:"rpcURL"`
RPCUser string `json:"rpcUser"` RPCUser string `json:"rpcUser"`
RPCPass string `json:"rpcPass"` RPCPass string `json:"rpcPass"`
RPCTimeout int `json:"rpcTimeout"` RPCTimeout int `json:"rpcTimeout"`
Parse bool `json:"parse"` Parse bool `json:"parse"`
ZeroMQBinding string `json:"zeroMQBinding"` ZeroMQBinding string `json:"zeroMQBinding"`
Subversion string `json:"subversion"` Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"blockAddressesToKeep"` BlockAddressesToKeep int `json:"blockAddressesToKeep"`
MempoolWorkers int `json:"mempoolWorkers"` MempoolWorkers int `json:"mempoolWorkers"`
MempoolSubWorkers int `json:"mempoolSubWorkers"` MempoolSubWorkers int `json:"mempoolSubWorkers"`
AddressFormat string `json:"addressFormat"` AddressFormat string `json:"addressFormat"`
SupportsEstimateFee bool `json:"supportsEstimateFee"`
SupportsEstimateSmartFee bool `json:"supportsEstimateSmartFee"`
} }
// NewBitcoinRPC returns new BitcoinRPC instance. // NewBitcoinRPC returns new BitcoinRPC instance.
@ -69,6 +72,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,
@ -301,8 +307,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"`
} }
@ -317,7 +323,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
@ -616,7 +622,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{}
@ -629,17 +640,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{}
@ -647,13 +668,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.

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

@ -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

@ -478,12 +478,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
@ -492,10 +492,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

@ -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

@ -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

@ -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

@ -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
@ -90,9 +92,9 @@ type BlockHeader struct {
type MempoolEntry struct { type MempoolEntry struct {
Size uint32 `json:"size"` Size uint32 `json:"size"`
Fee float64 `json:"fee"` Fee big.Int `json:"fee"`
ModifiedFee float64 `json:"modifiedfee"` ModifiedFee big.Int `json:"modifiedfee"`
Time float64 `json:"time"` Time uint64 `json:"time"`
Height uint32 `json:"height"` Height uint32 `json:"height"`
DescendantCount uint32 `json:"descendantcount"` DescendantCount uint32 `json:"descendantcount"`
DescendantSize uint32 `json:"descendantsize"` DescendantSize uint32 `json:"descendantsize"`
@ -132,8 +134,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 +147,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 +155,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

@ -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 {