diff --git a/api/types.go b/api/types.go index 776f9c43..e9725775 100644 --- a/api/types.go +++ b/api/types.go @@ -370,19 +370,19 @@ type Blocks struct { // BlockInfo contains extended block header data and a list of block txids type BlockInfo struct { - Hash string `json:"hash"` - Prev string `json:"previousBlockHash,omitempty"` - Next string `json:"nextBlockHash,omitempty"` - Height uint32 `json:"height"` - Confirmations int `json:"confirmations"` - Size int `json:"size"` - Time int64 `json:"time,omitempty"` - Version json.Number `json:"version"` - MerkleRoot string `json:"merkleRoot"` - Nonce string `json:"nonce"` - Bits string `json:"bits"` - Difficulty string `json:"difficulty"` - Txids []string `json:"tx,omitempty"` + Hash string `json:"hash"` + Prev string `json:"previousBlockHash,omitempty"` + Next string `json:"nextBlockHash,omitempty"` + Height uint32 `json:"height"` + Confirmations int `json:"confirmations"` + Size int `json:"size"` + Time int64 `json:"time,omitempty"` + Version common.JSONNumber `json:"version"` + MerkleRoot string `json:"merkleRoot"` + Nonce string `json:"nonce"` + Bits string `json:"bits"` + Difficulty string `json:"difficulty"` + Txids []string `json:"tx,omitempty"` } // Block contains information about block diff --git a/bchain/baseparser.go b/bchain/baseparser.go index d3596b82..b16c1f9a 100644 --- a/bchain/baseparser.go +++ b/bchain/baseparser.go @@ -9,6 +9,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/golang/glog" "github.com/juju/errors" + "github.com/trezor/blockbook/common" ) // BaseParser implements data parsing/handling functionality base for all other parsers @@ -39,9 +40,9 @@ func (p *BaseParser) GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescri const zeros = "0000000000000000000000000000000000000000" -// AmountToBigInt converts amount in json.Number (string) to big.Int +// AmountToBigInt converts amount in common.JSONNumber (string) to big.Int // it uses string operations to avoid problems with rounding -func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) { +func (p *BaseParser) AmountToBigInt(n common.JSONNumber) (big.Int, error) { var r big.Int s := string(n) i := strings.IndexByte(s, '.') diff --git a/bchain/baseparser_test.go b/bchain/baseparser_test.go index 668ed267..adbbab2a 100644 --- a/bchain/baseparser_test.go +++ b/bchain/baseparser_test.go @@ -1,9 +1,12 @@ +// +build unittest + package bchain import ( - "encoding/json" "math/big" "testing" + + "github.com/trezor/blockbook/common" ) func NewBaseParser(adp int) *BaseParser { @@ -44,7 +47,7 @@ func TestBaseParser_AmountToDecimalString(t *testing.T) { 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)) + got, err := NewBaseParser(tt.adp).AmountToBigInt(common.JSONNumber(tt.s)) if err != nil { t.Errorf("BaseParser.AmountToBigInt() error = %v", err) return diff --git a/bchain/coins/btc/bitcoinrpc.go b/bchain/coins/btc/bitcoinrpc.go index f09b102d..04446601 100644 --- a/bchain/coins/btc/bitcoinrpc.go +++ b/bchain/coins/btc/bitcoinrpc.go @@ -17,6 +17,7 @@ import ( "github.com/juju/errors" "github.com/martinboehm/btcd/wire" "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" ) // BitcoinRPC is an interface to JSON-RPC bitcoind service. @@ -237,13 +238,13 @@ type CmdGetBlockChainInfo struct { type ResGetBlockChainInfo struct { Error *bchain.RPCError `json:"error"` Result struct { - Chain string `json:"chain"` - Blocks int `json:"blocks"` - Headers int `json:"headers"` - Bestblockhash string `json:"bestblockhash"` - Difficulty json.Number `json:"difficulty"` - SizeOnDisk int64 `json:"size_on_disk"` - Warnings string `json:"warnings"` + Chain string `json:"chain"` + Blocks int `json:"blocks"` + Headers int `json:"headers"` + Bestblockhash string `json:"bestblockhash"` + Difficulty common.JSONNumber `json:"difficulty"` + SizeOnDisk int64 `json:"size_on_disk"` + Warnings string `json:"warnings"` } `json:"result"` } @@ -256,11 +257,11 @@ type CmdGetNetworkInfo struct { type ResGetNetworkInfo struct { Error *bchain.RPCError `json:"error"` Result struct { - Version json.Number `json:"version"` - Subversion json.Number `json:"subversion"` - ProtocolVersion json.Number `json:"protocolversion"` - Timeoffset float64 `json:"timeoffset"` - Warnings string `json:"warnings"` + Version common.JSONNumber `json:"version"` + Subversion common.JSONNumber `json:"subversion"` + ProtocolVersion common.JSONNumber `json:"protocolversion"` + Timeoffset float64 `json:"timeoffset"` + Warnings string `json:"warnings"` } `json:"result"` } @@ -358,8 +359,8 @@ type CmdEstimateSmartFee struct { type ResEstimateSmartFee struct { Error *bchain.RPCError `json:"error"` Result struct { - Feerate json.Number `json:"feerate"` - Blocks int `json:"blocks"` + Feerate common.JSONNumber `json:"feerate"` + Blocks int `json:"blocks"` } `json:"result"` } @@ -373,8 +374,8 @@ type CmdEstimateFee struct { } type ResEstimateFee struct { - Error *bchain.RPCError `json:"error"` - Result json.Number `json:"result"` + Error *bchain.RPCError `json:"error"` + Result common.JSONNumber `json:"result"` } // sendrawtransaction diff --git a/bchain/coins/dash/dashparser_test.go b/bchain/coins/dash/dashparser_test.go index 4e1d58d4..f88bf817 100644 --- a/bchain/coins/dash/dashparser_test.go +++ b/bchain/coins/dash/dashparser_test.go @@ -1,4 +1,4 @@ -// build unittest +// +build unittest package dash diff --git a/bchain/coins/dcr/decredrpc.go b/bchain/coins/dcr/decredrpc.go index abd12d4e..1bdbcca9 100644 --- a/bchain/coins/dcr/decredrpc.go +++ b/bchain/coins/dcr/decredrpc.go @@ -20,6 +20,7 @@ import ( "github.com/juju/errors" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/btc" + "github.com/trezor/blockbook/common" ) // voteBitYes defines the vote bit set when a given block validates the previous @@ -167,61 +168,61 @@ type GetBlockHashResult struct { type GetBlockResult struct { Error Error `json:"error"` Result struct { - Hash string `json:"hash"` - Confirmations int64 `json:"confirmations"` - Size int32 `json:"size"` - Height uint32 `json:"height"` - Version json.Number `json:"version"` - MerkleRoot string `json:"merkleroot"` - StakeRoot string `json:"stakeroot"` - RawTx []RawTx `json:"rawtx"` - Tx []string `json:"tx,omitempty"` - STx []string `json:"stx,omitempty"` - Time int64 `json:"time"` - Nonce json.Number `json:"nonce"` - VoteBits uint16 `json:"votebits"` - FinalState string `json:"finalstate"` - Voters uint16 `json:"voters"` - FreshStake uint8 `json:"freshstake"` - Revocations uint8 `json:"revocations"` - PoolSize uint32 `json:"poolsize"` - Bits string `json:"bits"` - SBits float64 `json:"sbits"` - ExtraData string `json:"extradata"` - StakeVersion uint32 `json:"stakeversion"` - Difficulty float64 `json:"difficulty"` - ChainWork string `json:"chainwork"` - PreviousHash string `json:"previousblockhash"` - NextHash string `json:"nextblockhash,omitempty"` + Hash string `json:"hash"` + Confirmations int64 `json:"confirmations"` + Size int32 `json:"size"` + Height uint32 `json:"height"` + Version common.JSONNumber `json:"version"` + MerkleRoot string `json:"merkleroot"` + StakeRoot string `json:"stakeroot"` + RawTx []RawTx `json:"rawtx"` + Tx []string `json:"tx,omitempty"` + STx []string `json:"stx,omitempty"` + Time int64 `json:"time"` + Nonce common.JSONNumber `json:"nonce"` + VoteBits uint16 `json:"votebits"` + FinalState string `json:"finalstate"` + Voters uint16 `json:"voters"` + FreshStake uint8 `json:"freshstake"` + Revocations uint8 `json:"revocations"` + PoolSize uint32 `json:"poolsize"` + Bits string `json:"bits"` + SBits float64 `json:"sbits"` + ExtraData string `json:"extradata"` + StakeVersion uint32 `json:"stakeversion"` + Difficulty float64 `json:"difficulty"` + ChainWork string `json:"chainwork"` + PreviousHash string `json:"previousblockhash"` + NextHash string `json:"nextblockhash,omitempty"` } `json:"result"` } type GetBlockHeaderResult struct { Error Error `json:"error"` Result struct { - Hash string `json:"hash"` - Confirmations int64 `json:"confirmations"` - Version json.Number `json:"version"` - MerkleRoot string `json:"merkleroot"` - StakeRoot string `json:"stakeroot"` - VoteBits uint16 `json:"votebits"` - FinalState string `json:"finalstate"` - Voters uint16 `json:"voters"` - FreshStake uint8 `json:"freshstake"` - Revocations uint8 `json:"revocations"` - PoolSize uint32 `json:"poolsize"` - Bits string `json:"bits"` - SBits float64 `json:"sbits"` - Height uint32 `json:"height"` - Size uint32 `json:"size"` - Time int64 `json:"time"` - Nonce uint32 `json:"nonce"` - ExtraData string `json:"extradata"` - StakeVersion uint32 `json:"stakeversion"` - Difficulty float64 `json:"difficulty"` - ChainWork string `json:"chainwork"` - PreviousHash string `json:"previousblockhash,omitempty"` - NextHash string `json:"nextblockhash,omitempty"` + Hash string `json:"hash"` + Confirmations int64 `json:"confirmations"` + Version common.JSONNumber `json:"version"` + MerkleRoot string `json:"merkleroot"` + StakeRoot string `json:"stakeroot"` + VoteBits uint16 `json:"votebits"` + FinalState string `json:"finalstate"` + Voters uint16 `json:"voters"` + FreshStake uint8 `json:"freshstake"` + Revocations uint8 `json:"revocations"` + PoolSize uint32 `json:"poolsize"` + Bits string `json:"bits"` + SBits float64 `json:"sbits"` + Height uint32 `json:"height"` + Size uint32 `json:"size"` + Time int64 `json:"time"` + Nonce uint32 `json:"nonce"` + ExtraData string `json:"extradata"` + StakeVersion uint32 `json:"stakeversion"` + Difficulty float64 `json:"difficulty"` + ChainWork string `json:"chainwork"` + PreviousHash string `json:"previousblockhash,omitempty"` + NextHash string `json:"nextblockhash,omitempty"` } `json:"result"` } @@ -296,8 +297,8 @@ type EstimateSmartFeeResult struct { } type EstimateFeeResult struct { - Error Error `json:"error"` - Result json.Number `json:"result"` + Error Error `json:"error"` + Result common.JSONNumber `json:"result"` } type SendRawTransactionResult struct { @@ -636,7 +637,7 @@ func (d *DecredRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { Version: block.Result.Version, Nonce: block.Result.Nonce, Bits: block.Result.Bits, - Difficulty: json.Number(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)), + Difficulty: common.JSONNumber(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)), Txids: block.Result.Tx, } diff --git a/bchain/coins/eth/ethrpc.go b/bchain/coins/eth/ethrpc.go index 8674a4b5..a2568aad 100644 --- a/bchain/coins/eth/ethrpc.go +++ b/bchain/coins/eth/ethrpc.go @@ -17,6 +17,7 @@ import ( "github.com/golang/glog" "github.com/juju/errors" "github.com/trezor/blockbook/bchain" + "github.com/trezor/blockbook/common" ) // EthereumNet type specifies the type of ethereum network @@ -569,8 +570,8 @@ func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) { } return &bchain.BlockInfo{ BlockHeader: *bch, - Difficulty: json.Number(head.Difficulty), - Nonce: json.Number(head.Nonce), + Difficulty: common.JSONNumber(head.Difficulty), + Nonce: common.JSONNumber(head.Nonce), Txids: txs.Transactions, }, nil } diff --git a/bchain/coins/nuls/nulsparser_test.go b/bchain/coins/nuls/nulsparser_test.go index 03017dbc..869318aa 100644 --- a/bchain/coins/nuls/nulsparser_test.go +++ b/bchain/coins/nuls/nulsparser_test.go @@ -1,8 +1,9 @@ +// +build unittest + package nuls import ( "encoding/hex" - "encoding/json" "math/big" "reflect" "testing" @@ -11,6 +12,7 @@ import ( "github.com/martinboehm/btcutil/hdkeychain" "github.com/trezor/blockbook/bchain" "github.com/trezor/blockbook/bchain/coins/btc" + "github.com/trezor/blockbook/common" ) var ( @@ -41,7 +43,7 @@ func init() { { ValueSat: *big.NewInt(399999000000), N: 0, - JsonValue: json.Number("0"), + JsonValue: common.JSONNumber("0"), ScriptPubKey: bchain.ScriptPubKey{ Hex: "Nse4zpZHsUuU7h5ymv28pcGbwHju3joV", Addresses: []string{ @@ -73,7 +75,7 @@ func init() { { ValueSat: *big.NewInt(400000000000), N: 0, - JsonValue: json.Number("0"), + JsonValue: common.JSONNumber("0"), ScriptPubKey: bchain.ScriptPubKey{ Hex: "Nse4ikjE88g2BgsNwsswTdkSwiSrKjjS", Addresses: []string{ @@ -84,7 +86,7 @@ func init() { { ValueSat: *big.NewInt(7286565570000), N: 1, - JsonValue: json.Number("0"), + JsonValue: common.JSONNumber("0"), ScriptPubKey: bchain.ScriptPubKey{ Hex: "Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi", Addresses: []string{ diff --git a/bchain/types.go b/bchain/types.go index 05523bf5..0df7f506 100644 --- a/bchain/types.go +++ b/bchain/types.go @@ -7,6 +7,8 @@ import ( "errors" "fmt" "math/big" + + "github.com/trezor/blockbook/common" ) // ChainType is type of the blockchain @@ -68,9 +70,9 @@ type ScriptPubKey struct { // Vout contains data about tx output type Vout struct { ValueSat big.Int - JsonValue json.Number `json:"value"` - N uint32 `json:"n"` - ScriptPubKey ScriptPubKey `json:"scriptPubKey"` + JsonValue common.JSONNumber `json:"value"` + N uint32 `json:"n"` + ScriptPubKey ScriptPubKey `json:"scriptPubKey"` } // Tx is blockchain transaction @@ -110,30 +112,30 @@ type BlockHeader struct { // BlockInfo contains extended block header data and a list of block txids type BlockInfo struct { BlockHeader - Version json.Number `json:"version"` - MerkleRoot string `json:"merkleroot"` - Nonce json.Number `json:"nonce"` - Bits string `json:"bits"` - Difficulty json.Number `json:"difficulty"` - Txids []string `json:"tx,omitempty"` + Version common.JSONNumber `json:"version"` + MerkleRoot string `json:"merkleroot"` + Nonce common.JSONNumber `json:"nonce"` + Bits string `json:"bits"` + Difficulty common.JSONNumber `json:"difficulty"` + Txids []string `json:"tx,omitempty"` } // MempoolEntry is used to get data about mempool entry type MempoolEntry struct { Size uint32 `json:"size"` FeeSat big.Int - Fee json.Number `json:"fee"` + Fee common.JSONNumber `json:"fee"` ModifiedFeeSat big.Int - ModifiedFee json.Number `json:"modifiedfee"` - Time uint64 `json:"time"` - Height uint32 `json:"height"` - DescendantCount uint32 `json:"descendantcount"` - DescendantSize uint32 `json:"descendantsize"` - DescendantFees uint32 `json:"descendantfees"` - AncestorCount uint32 `json:"ancestorcount"` - AncestorSize uint32 `json:"ancestorsize"` - AncestorFees uint32 `json:"ancestorfees"` - Depends []string `json:"depends"` + ModifiedFee common.JSONNumber `json:"modifiedfee"` + Time uint64 `json:"time"` + Height uint32 `json:"height"` + DescendantCount uint32 `json:"descendantcount"` + DescendantSize uint32 `json:"descendantsize"` + DescendantFees uint32 `json:"descendantfees"` + AncestorCount uint32 `json:"ancestorcount"` + AncestorSize uint32 `json:"ancestorsize"` + AncestorFees uint32 `json:"ancestorfees"` + Depends []string `json:"depends"` } // ChainInfo is used to get information about blockchain @@ -267,9 +269,9 @@ type BlockChainParser interface { MinimumCoinbaseConfirmations() 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 + // AmountToBigInt converts amount in common.JSONNumber (string) to big.Int // it uses string operations to avoid problems with rounding - AmountToBigInt(n json.Number) (big.Int, error) + AmountToBigInt(n common.JSONNumber) (big.Int, error) // address descriptor conversions GetAddrDescFromVout(output *Vout) (AddressDescriptor, error) GetAddrDescFromAddress(address string) (AddressDescriptor, error) diff --git a/common/jsonnumber.go b/common/jsonnumber.go index 85ddcbea..d209fbe2 100644 --- a/common/jsonnumber.go +++ b/common/jsonnumber.go @@ -3,8 +3,6 @@ package common import ( "encoding/json" "strconv" - - "github.com/golang/glog" ) // JSONNumber is used instead of json.Number after upgrade to go 1.14 @@ -36,7 +34,11 @@ func (c JSONNumber) String() string { // MarshalJSON marsalls JSONNumber to []byte // if possible, return a number without quotes, otherwise string value in quotes +// empty string is treated as number 0 func (c JSONNumber) MarshalJSON() ([]byte, error) { + if len(c) == 0 { + return []byte("0"), nil + } if f, err := c.Float64(); err == nil { return json.Marshal(f) } @@ -53,6 +55,5 @@ func (c *JSONNumber) UnmarshalJSON(d []byte) error { } else { *c = JSONNumber(s) } - glog.Info("JSONNumber ", s, ", ", *c) return nil } diff --git a/common/jsonnumber_test.go b/common/jsonnumber_test.go index 10945675..81a63786 100644 --- a/common/jsonnumber_test.go +++ b/common/jsonnumber_test.go @@ -1,3 +1,5 @@ +// +build unittest + package common import ( @@ -16,8 +18,8 @@ func TestJSONNumber_MarshalJSON(t *testing.T) { {"1", JSONNumber("1"), []byte("1"), false}, {"2", JSONNumber("12341234.43214123"), []byte("12341234.43214123"), false}, {"3", JSONNumber("123E55"), []byte("1.23e+57"), false}, - {"NaN", JSONNumber("dsfafdasf"), []byte("\"dsfafdasf\""), false}, - {"empty", JSONNumber(""), []byte("\"\""), false}, + {"NaN", JSONNumber("dsfafdasf"), []byte(`"dsfafdasf"`), false}, + {"empty", JSONNumber(""), []byte("0"), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -36,17 +38,28 @@ func TestJSONNumber_MarshalJSON(t *testing.T) { func TestJSONNumber_UnmarshalJSON(t *testing.T) { tests := []struct { name string - c *JSONNumber d []byte + want JSONNumber wantErr bool }{ - // TODO: Add test cases. + {"0", []byte("0"), JSONNumber("0"), false}, + {"1", []byte("1"), JSONNumber("1"), false}, + {"1 quotes", []byte(`"1"`), JSONNumber("1"), false}, + {"2", []byte("12341234.43214123"), JSONNumber("12341234.43214123"), false}, + {"3", []byte("1.23e+57"), JSONNumber("1.23e+57"), false}, + {"NaN", []byte(`"dsfafdasf"`), JSONNumber("dsfafdasf"), false}, + {"empty", []byte(`""`), JSONNumber(""), false}, + {"really empty", []byte(""), JSONNumber(""), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := tt.c.UnmarshalJSON(tt.d); (err != nil) != tt.wantErr { + var got JSONNumber + if err := got.UnmarshalJSON(tt.d); (err != nil) != tt.wantErr { t.Errorf("JSONNumber.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("JSONNumber.UnmarshalJSON() = %v, want %v", got, tt.want) + } }) } }