Skip to content

Commit

Permalink
btcjson+rpcserverhelp: restore bitcoind compatibility
Browse files Browse the repository at this point in the history
The PR btcsuite#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.
  • Loading branch information
guggero committed Nov 12, 2020
1 parent 65d2b7a commit 9e8bb3e
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 19 deletions.
68 changes: 61 additions & 7 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"reflect"

"github.com/btcsuite/btcd/wire"
)
Expand Down Expand Up @@ -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
Expand All @@ -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,
},
}
}

Expand All @@ -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,
},
}
}

Expand Down
34 changes: 27 additions & 7 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
},
},
},
{
Expand Down
10 changes: 5 additions & 5 deletions rpcserverhelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down

0 comments on commit 9e8bb3e

Please sign in to comment.