btcjson+rpcserverhelp: restore bitcoind compatibility

The PR #1594 introduced a change that made the order of parameters
relevant, if one of them is nil. This makes it harder to be backward
compatible with the same JSON message if an existing parameter in
bitcoind was re-purposed to have a different meaning.
This commit is contained in:
Oliver Gugger 2020-11-12 10:08:54 +01:00
parent 65d2b7a18c
commit 9e8bb3eddb
No known key found for this signature in database
GPG key ID: 8E4256593F177720
3 changed files with 93 additions and 19 deletions

View file

@ -11,6 +11,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"reflect"
"github.com/btcsuite/btcd/wire"
)
@ -819,11 +820,60 @@ func NewSearchRawTransactionsCmd(address string, verbose, skip, count *int, vinE
}
}
// AllowHighFeesOrMaxFeeRate defines a type that can either be the legacy
// allowhighfees boolean field or the new maxfeerate int field.
type AllowHighFeesOrMaxFeeRate struct {
Value interface{}
}
// String returns the string representation of this struct, used for printing
// the marshaled default value in the help text.
func (a AllowHighFeesOrMaxFeeRate) String() string {
b, _ := a.MarshalJSON()
return string(b)
}
// MarshalJSON implements the json.Marshaler interface
func (a AllowHighFeesOrMaxFeeRate) MarshalJSON() ([]byte, error) {
// The default value is false which only works with the legacy versions.
if a.Value == nil ||
(reflect.ValueOf(a.Value).Kind() == reflect.Ptr &&
reflect.ValueOf(a.Value).IsNil()) {
return json.Marshal(false)
}
return json.Marshal(a.Value)
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (a *AllowHighFeesOrMaxFeeRate) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
var unmarshalled interface{}
if err := json.Unmarshal(data, &unmarshalled); err != nil {
return err
}
switch v := unmarshalled.(type) {
case bool:
a.Value = Bool(v)
case float64:
a.Value = Int32(int32(v))
default:
return fmt.Errorf("invalid allowhighfees or maxfeerate value: "+
"%v", unmarshalled)
}
return nil
}
// SendRawTransactionCmd defines the sendrawtransaction JSON-RPC command.
type SendRawTransactionCmd struct {
HexTx string
AllowHighFees *bool `jsonrpcdefault:"false"`
MaxFeeRate *int32
HexTx string
FeeSetting *AllowHighFeesOrMaxFeeRate `jsonrpcdefault:"false"`
}
// NewSendRawTransactionCmd returns a new instance which can be used to issue a
@ -833,8 +883,10 @@ type SendRawTransactionCmd struct {
// for optional parameters will use the default value.
func NewSendRawTransactionCmd(hexTx string, allowHighFees *bool) *SendRawTransactionCmd {
return &SendRawTransactionCmd{
HexTx: hexTx,
AllowHighFees: allowHighFees,
HexTx: hexTx,
FeeSetting: &AllowHighFeesOrMaxFeeRate{
Value: allowHighFees,
},
}
}
@ -844,8 +896,10 @@ func NewSendRawTransactionCmd(hexTx string, allowHighFees *bool) *SendRawTransac
// A 0 maxFeeRate indicates that a maximum fee rate won't be enforced.
func NewBitcoindSendRawTransactionCmd(hexTx string, maxFeeRate int32) *SendRawTransactionCmd {
return &SendRawTransactionCmd{
HexTx: hexTx,
MaxFeeRate: &maxFeeRate,
HexTx: hexTx,
FeeSetting: &AllowHighFeesOrMaxFeeRate{
Value: &maxFeeRate,
},
}
}

View file

@ -1224,29 +1224,49 @@ func TestChainSvrCmds(t *testing.T) {
{
name: "sendrawtransaction",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("sendrawtransaction", "1122")
return btcjson.NewCmd("sendrawtransaction", "1122", &btcjson.AllowHighFeesOrMaxFeeRate{})
},
staticCmd: func() interface{} {
return btcjson.NewSendRawTransactionCmd("1122", nil)
},
marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122"],"id":1}`,
marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122",false],"id":1}`,
unmarshalled: &btcjson.SendRawTransactionCmd{
HexTx: "1122",
AllowHighFees: btcjson.Bool(false),
HexTx: "1122",
FeeSetting: &btcjson.AllowHighFeesOrMaxFeeRate{
Value: btcjson.Bool(false),
},
},
},
{
name: "sendrawtransaction optional",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("sendrawtransaction", "1122", false)
return btcjson.NewCmd("sendrawtransaction", "1122", &btcjson.AllowHighFeesOrMaxFeeRate{Value: btcjson.Bool(false)})
},
staticCmd: func() interface{} {
return btcjson.NewSendRawTransactionCmd("1122", btcjson.Bool(false))
},
marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122",false],"id":1}`,
unmarshalled: &btcjson.SendRawTransactionCmd{
HexTx: "1122",
AllowHighFees: btcjson.Bool(false),
HexTx: "1122",
FeeSetting: &btcjson.AllowHighFeesOrMaxFeeRate{
Value: btcjson.Bool(false),
},
},
},
{
name: "sendrawtransaction optional, bitcoind >= 0.19.0",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("sendrawtransaction", "1122", &btcjson.AllowHighFeesOrMaxFeeRate{Value: btcjson.Int32(1234)})
},
staticCmd: func() interface{} {
return btcjson.NewBitcoindSendRawTransactionCmd("1122", 1234)
},
marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122",1234],"id":1}`,
unmarshalled: &btcjson.SendRawTransactionCmd{
HexTx: "1122",
FeeSetting: &btcjson.AllowHighFeesOrMaxFeeRate{
Value: btcjson.Int32(1234),
},
},
},
{

View file

@ -561,11 +561,11 @@ var helpDescsEnUS = map[string]string{
"searchrawtransactions--result0": "Hex-encoded serialized transaction",
// SendRawTransactionCmd help.
"sendrawtransaction--synopsis": "Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.",
"sendrawtransaction-hextx": "Serialized, hex-encoded signed transaction",
"sendrawtransaction-allowhighfees": "Whether or not to allow insanely high fees (btcd does not yet implement this parameter, so it has no effect)",
"sendrawtransaction-maxfeerate": "Used by bitcoind on or after v0.19.0",
"sendrawtransaction--result0": "The hash of the transaction",
"sendrawtransaction--synopsis": "Submits the serialized, hex-encoded transaction to the local peer and relays it to the network.",
"sendrawtransaction-hextx": "Serialized, hex-encoded signed transaction",
"sendrawtransaction-feesetting": "Whether or not to allow insanely high fees in bitcoind < v0.19.0 or the max fee rate for bitcoind v0.19.0 and later (btcd does not yet implement this parameter, so it has no effect)",
"sendrawtransaction--result0": "The hash of the transaction",
"allowhighfeesormaxfeerate-value": "Either the boolean value for the allowhighfees parameter in bitcoind < v0.19.0 or the numerical value for the maxfeerate field in bitcoind v0.19.0 and later",
// SetGenerateCmd help.
"setgenerate--synopsis": "Set the server to generate coins (mine) or not.",