Use big.Int for all amounts

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

View File

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

View File

@ -3,6 +3,8 @@ package bchain
import (
"encoding/hex"
"encoding/json"
"math/big"
"strings"
"github.com/gogo/protobuf/proto"
"github.com/juju/errors"
@ -14,6 +16,7 @@ type AddressFactoryFunc func(string) (Address, error)
type BaseParser struct {
AddressFactory AddressFactoryFunc
BlockAddressesToKeep int
AmountDecimalPoint int
}
// 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")
}
// 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) {
var tx Tx
err := json.Unmarshal(msg, &tx)
@ -44,13 +87,18 @@ func (p *BaseParser) ParseTxFromJson(msg json.RawMessage) (*Tx, error) {
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 {
a, err := p.AddressFactory(vout.ScriptPubKey.Addresses[0])
if err != nil {
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,
N: vo.N,
ScriptPubKeyHex: hex,
Value: vo.Value,
ValueSat: vo.ValueSat.Bytes(),
}
}
pt := &ProtoTransaction{
@ -176,13 +224,15 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
}
vout := make([]Vout, len(pt.Vout))
for i, pto := range pt.Vout {
var vs big.Int
vs.SetBytes(pto.ValueSat)
vout[i] = Vout{
N: pto.N,
ScriptPubKey: ScriptPubKey{
Addresses: pto.Addresses,
Hex: hex.EncodeToString(pto.ScriptPubKeyHex),
},
Value: pto.Value,
ValueSat: vs,
}
if len(pto.Addresses) == 1 {
a, err := p.AddressFactory(pto.Addresses[0])

View File

@ -5,6 +5,7 @@ import (
"blockbook/bchain/coins/btc"
"encoding/hex"
"encoding/json"
"math/big"
"github.com/cpacia/bchutil"
"github.com/golang/glog"
@ -132,7 +133,7 @@ func (b *BCashRPC) GetBlockFull(hash string) (*bchain.Block, error) {
}
// 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)
res := btc.ResEstimateSmartFee{}
@ -141,13 +142,18 @@ func (b *BCashRPC) EstimateSmartFee(blocks int, conservative bool) (float64, err
// conservative param is omitted
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
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 {

View File

@ -17,6 +17,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"reflect"
"time"
@ -172,12 +173,12 @@ func (c *blockChainWithMetrics) GetTransactionForMempool(txid string) (v *bchain
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())
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())
return c.b.EstimateFee(blocks)
}

View File

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

View File

@ -8,6 +8,7 @@ import (
"encoding/json"
"io"
"io/ioutil"
"math/big"
"net"
"net/http"
"time"
@ -36,18 +37,20 @@ type BitcoinRPC struct {
}
type Configuration struct {
CoinName string `json:"coin_name"`
RPCURL string `json:"rpcURL"`
RPCUser string `json:"rpcUser"`
RPCPass string `json:"rpcPass"`
RPCTimeout int `json:"rpcTimeout"`
Parse bool `json:"parse"`
ZeroMQBinding string `json:"zeroMQBinding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"blockAddressesToKeep"`
MempoolWorkers int `json:"mempoolWorkers"`
MempoolSubWorkers int `json:"mempoolSubWorkers"`
AddressFormat string `json:"addressFormat"`
CoinName string `json:"coin_name"`
RPCURL string `json:"rpcURL"`
RPCUser string `json:"rpcUser"`
RPCPass string `json:"rpcPass"`
RPCTimeout int `json:"rpcTimeout"`
Parse bool `json:"parse"`
ZeroMQBinding string `json:"zeroMQBinding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"blockAddressesToKeep"`
MempoolWorkers int `json:"mempoolWorkers"`
MempoolSubWorkers int `json:"mempoolSubWorkers"`
AddressFormat string `json:"addressFormat"`
SupportsEstimateFee bool `json:"supportsEstimateFee"`
SupportsEstimateSmartFee bool `json:"supportsEstimateSmartFee"`
}
// NewBitcoinRPC returns new BitcoinRPC instance.
@ -69,6 +72,9 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
if 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{
Dial: (&net.Dialer{KeepAlive: 600 * time.Second}).Dial,
@ -301,8 +307,8 @@ type CmdEstimateSmartFee struct {
type ResEstimateSmartFee struct {
Error *bchain.RPCError `json:"error"`
Result struct {
Feerate float64 `json:"feerate"`
Blocks int `json:"blocks"`
Feerate json.Number `json:"feerate"`
Blocks int `json:"blocks"`
} `json:"result"`
}
@ -317,7 +323,7 @@ type CmdEstimateFee struct {
type ResEstimateFee struct {
Error *bchain.RPCError `json:"error"`
Result float64 `json:"result"`
Result json.Number `json:"result"`
}
// sendrawtransaction
@ -616,7 +622,12 @@ func (b *BitcoinRPC) GetMempoolTransactions(address string) ([]string, error) {
}
// 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)
res := ResEstimateSmartFee{}
@ -629,17 +640,27 @@ func (b *BitcoinRPC) EstimateSmartFee(blocks int, conservative bool) (float64, e
}
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
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.
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)
res := ResEstimateFee{}
@ -647,13 +668,18 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (float64, error) {
req.Params.Blocks = blocks
err := b.Call(&req, &res)
var r big.Int
if err != nil {
return 0, err
return r, err
}
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.

View File

@ -24,6 +24,7 @@ func NewDashRPC(config json.RawMessage, pushHandler func(bchain.NotificationType
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -53,8 +54,3 @@ func (b *DashRPC) Initialize() error {
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),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -69,8 +70,3 @@ func (b *DogecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
}
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
func NewEthereumParser() *EthereumParser {
return &EthereumParser{&bchain.BaseParser{AddressFactory: bchain.NewBaseAddress}}
return &EthereumParser{&bchain.BaseParser{
AddressFactory: bchain.NewBaseAddress,
BlockAddressesToKeep: 0,
AmountDecimalPoint: 18,
}}
}
type rpcTransaction struct {
@ -86,6 +90,10 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
}
tx.BlockHash = bh
h := hex.EncodeToString(b)
vs, err := hexutil.DecodeBig(tx.Value)
if err != nil {
return nil, err
}
return &bchain.Tx{
Blocktime: blocktime,
Confirmations: confirmations,
@ -105,8 +113,8 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
},
Vout: []bchain.Vout{
{
N: 0, // there is always up to one To address
// Value - cannot be set, it does not fit precisely to float64
N: 0, // there is always up to one To address
ValueSat: *vs,
ScriptPubKey: bchain.ScriptPubKey{
// Hex
Addresses: ta,
@ -286,9 +294,3 @@ func (p *EthereumParser) UnpackBlockHash(buf []byte) (string, error) {
func (p *EthereumParser) IsUTXOChain() bool {
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.
func (b *EthereumRPC) EstimateFee(blocks int) (float64, error) {
func (b *EthereumRPC) EstimateFee(blocks int) (big.Int, error) {
return b.EstimateSmartFee(blocks, true)
}
// 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)
defer cancel()
// 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,
}
g, err := b.client.EstimateGas(ctx, msg)
var r big.Int
if err != nil {
return 0, err
return r, err
}
return float64(g), nil
r.SetUint64(g)
return r, nil
}
// SendRawTransaction sends raw transaction.

View File

@ -24,6 +24,7 @@ func NewLitecoinRPC(config json.RawMessage, pushHandler func(bchain.Notification
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
@ -54,8 +55,3 @@ func (b *LitecoinRPC) Initialize() error {
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),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
@ -69,8 +70,3 @@ func (b *NamecoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
}
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),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
@ -54,8 +55,3 @@ func (b *VertcoinRPC) Initialize() error {
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),
}
z.RPCMarshaler = btc.JSONMarshalerV1{}
z.ChainConfig.SupportsEstimateSmartFee = false
return z, nil
}
@ -114,14 +115,6 @@ func (z *ZCashRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
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
func (z *ZCashRPC) GetMempoolEntry(txid string) (*bchain.MempoolEntry, error) {
return nil, errors.New("GetMempoolEntry: not implemented")

View File

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

View File

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

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
)
// errors with specific meaning returned by blockchain rpc
@ -49,7 +50,8 @@ type Address interface {
}
type Vout struct {
Value float64 `json:"value"`
ValueSat big.Int
JsonValue json.Number `json:"value"`
N uint32 `json:"n"`
ScriptPubKey ScriptPubKey `json:"scriptPubKey"`
Address Address
@ -90,9 +92,9 @@ type BlockHeader struct {
type MempoolEntry struct {
Size uint32 `json:"size"`
Fee float64 `json:"fee"`
ModifiedFee float64 `json:"modifiedfee"`
Time float64 `json:"time"`
Fee big.Int `json:"fee"`
ModifiedFee big.Int `json:"modifiedfee"`
Time uint64 `json:"time"`
Height uint32 `json:"height"`
DescendantCount uint32 `json:"descendantcount"`
DescendantSize uint32 `json:"descendantsize"`
@ -132,8 +134,8 @@ type BlockChain interface {
GetMempool() ([]string, error)
GetTransaction(txid string) (*Tx, error)
GetTransactionForMempool(txid string) (*Tx, error)
EstimateSmartFee(blocks int, conservative bool) (float64, error)
EstimateFee(blocks int) (float64, error)
EstimateSmartFee(blocks int, conservative bool) (big.Int, error)
EstimateFee(blocks int) (big.Int, error)
SendRawTransaction(tx string) (string, error)
// mempool
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
type BlockChainParser interface {
// self description
// chain configuration description
// 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
IsUTXOChain() bool
@ -153,6 +155,11 @@ type BlockChainParser interface {
// and used in case of fork
// if 0 the blockaddresses column is not used at all (usually non UTXO chains)
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
GetAddrIDFromVout(output *Vout) ([]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)
}
func formatAmount(a float64) string {
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%0.8f", a), "0"), ".")
// for now return the string as it is
// in future could be used to do coin specific formatting
func formatAmount(a string) string {
return a
}
// Run starts the server

View File

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