1// Copyright (c) 2014 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package btcjson_test
6
7import (
8	"bytes"
9	"encoding/json"
10	"fmt"
11	"reflect"
12	"testing"
13
14	"github.com/btcsuite/btcd/btcjson"
15	"github.com/btcsuite/btcd/wire"
16)
17
18// TestChainSvrCmds tests all of the chain server commands marshal and unmarshal
19// into valid results include handling of optional fields being omitted in the
20// marshalled command, while optional fields with defaults have the default
21// assigned on unmarshalled commands.
22func TestChainSvrCmds(t *testing.T) {
23	t.Parallel()
24
25	testID := int(1)
26	tests := []struct {
27		name         string
28		newCmd       func() (interface{}, error)
29		staticCmd    func() interface{}
30		marshalled   string
31		unmarshalled interface{}
32	}{
33		{
34			name: "addnode",
35			newCmd: func() (interface{}, error) {
36				return btcjson.NewCmd("addnode", "127.0.0.1", btcjson.ANRemove)
37			},
38			staticCmd: func() interface{} {
39				return btcjson.NewAddNodeCmd("127.0.0.1", btcjson.ANRemove)
40			},
41			marshalled:   `{"jsonrpc":"1.0","method":"addnode","params":["127.0.0.1","remove"],"id":1}`,
42			unmarshalled: &btcjson.AddNodeCmd{Addr: "127.0.0.1", SubCmd: btcjson.ANRemove},
43		},
44		{
45			name: "createrawtransaction",
46			newCmd: func() (interface{}, error) {
47				return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`,
48					`{"456":0.0123}`)
49			},
50			staticCmd: func() interface{} {
51				txInputs := []btcjson.TransactionInput{
52					{Txid: "123", Vout: 1},
53				}
54				amounts := map[string]float64{"456": .0123}
55				return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, nil)
56			},
57			marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123}],"id":1}`,
58			unmarshalled: &btcjson.CreateRawTransactionCmd{
59				Inputs:  []btcjson.TransactionInput{{Txid: "123", Vout: 1}},
60				Amounts: map[string]float64{"456": .0123},
61			},
62		},
63		{
64			name: "createrawtransaction optional",
65			newCmd: func() (interface{}, error) {
66				return btcjson.NewCmd("createrawtransaction", `[{"txid":"123","vout":1}]`,
67					`{"456":0.0123}`, int64(12312333333))
68			},
69			staticCmd: func() interface{} {
70				txInputs := []btcjson.TransactionInput{
71					{Txid: "123", Vout: 1},
72				}
73				amounts := map[string]float64{"456": .0123}
74				return btcjson.NewCreateRawTransactionCmd(txInputs, amounts, btcjson.Int64(12312333333))
75			},
76			marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[{"txid":"123","vout":1}],{"456":0.0123},12312333333],"id":1}`,
77			unmarshalled: &btcjson.CreateRawTransactionCmd{
78				Inputs:   []btcjson.TransactionInput{{Txid: "123", Vout: 1}},
79				Amounts:  map[string]float64{"456": .0123},
80				LockTime: btcjson.Int64(12312333333),
81			},
82		},
83
84		{
85			name: "decoderawtransaction",
86			newCmd: func() (interface{}, error) {
87				return btcjson.NewCmd("decoderawtransaction", "123")
88			},
89			staticCmd: func() interface{} {
90				return btcjson.NewDecodeRawTransactionCmd("123")
91			},
92			marshalled:   `{"jsonrpc":"1.0","method":"decoderawtransaction","params":["123"],"id":1}`,
93			unmarshalled: &btcjson.DecodeRawTransactionCmd{HexTx: "123"},
94		},
95		{
96			name: "decodescript",
97			newCmd: func() (interface{}, error) {
98				return btcjson.NewCmd("decodescript", "00")
99			},
100			staticCmd: func() interface{} {
101				return btcjson.NewDecodeScriptCmd("00")
102			},
103			marshalled:   `{"jsonrpc":"1.0","method":"decodescript","params":["00"],"id":1}`,
104			unmarshalled: &btcjson.DecodeScriptCmd{HexScript: "00"},
105		},
106		{
107			name: "getaddednodeinfo",
108			newCmd: func() (interface{}, error) {
109				return btcjson.NewCmd("getaddednodeinfo", true)
110			},
111			staticCmd: func() interface{} {
112				return btcjson.NewGetAddedNodeInfoCmd(true, nil)
113			},
114			marshalled:   `{"jsonrpc":"1.0","method":"getaddednodeinfo","params":[true],"id":1}`,
115			unmarshalled: &btcjson.GetAddedNodeInfoCmd{DNS: true, Node: nil},
116		},
117		{
118			name: "getaddednodeinfo optional",
119			newCmd: func() (interface{}, error) {
120				return btcjson.NewCmd("getaddednodeinfo", true, "127.0.0.1")
121			},
122			staticCmd: func() interface{} {
123				return btcjson.NewGetAddedNodeInfoCmd(true, btcjson.String("127.0.0.1"))
124			},
125			marshalled: `{"jsonrpc":"1.0","method":"getaddednodeinfo","params":[true,"127.0.0.1"],"id":1}`,
126			unmarshalled: &btcjson.GetAddedNodeInfoCmd{
127				DNS:  true,
128				Node: btcjson.String("127.0.0.1"),
129			},
130		},
131		{
132			name: "getbestblockhash",
133			newCmd: func() (interface{}, error) {
134				return btcjson.NewCmd("getbestblockhash")
135			},
136			staticCmd: func() interface{} {
137				return btcjson.NewGetBestBlockHashCmd()
138			},
139			marshalled:   `{"jsonrpc":"1.0","method":"getbestblockhash","params":[],"id":1}`,
140			unmarshalled: &btcjson.GetBestBlockHashCmd{},
141		},
142		{
143			name: "getblock",
144			newCmd: func() (interface{}, error) {
145				return btcjson.NewCmd("getblock", "123", btcjson.Int(0))
146			},
147			staticCmd: func() interface{} {
148				return btcjson.NewGetBlockCmd("123", btcjson.Int(0))
149			},
150			marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",0],"id":1}`,
151			unmarshalled: &btcjson.GetBlockCmd{
152				Hash:      "123",
153				Verbosity: btcjson.Int(0),
154			},
155		},
156		{
157			name: "getblock required optional1",
158			newCmd: func() (interface{}, error) {
159				return btcjson.NewCmd("getblock", "123", btcjson.Int(1))
160			},
161			staticCmd: func() interface{} {
162				return btcjson.NewGetBlockCmd("123", btcjson.Int(1))
163			},
164			marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",1],"id":1}`,
165			unmarshalled: &btcjson.GetBlockCmd{
166				Hash:      "123",
167				Verbosity: btcjson.Int(1),
168			},
169		},
170		{
171			name: "getblock required optional2",
172			newCmd: func() (interface{}, error) {
173				return btcjson.NewCmd("getblock", "123", btcjson.Int(2))
174			},
175			staticCmd: func() interface{} {
176				return btcjson.NewGetBlockCmd("123", btcjson.Int(2))
177			},
178			marshalled: `{"jsonrpc":"1.0","method":"getblock","params":["123",2],"id":1}`,
179			unmarshalled: &btcjson.GetBlockCmd{
180				Hash:      "123",
181				Verbosity: btcjson.Int(2),
182			},
183		},
184		{
185			name: "getblockchaininfo",
186			newCmd: func() (interface{}, error) {
187				return btcjson.NewCmd("getblockchaininfo")
188			},
189			staticCmd: func() interface{} {
190				return btcjson.NewGetBlockChainInfoCmd()
191			},
192			marshalled:   `{"jsonrpc":"1.0","method":"getblockchaininfo","params":[],"id":1}`,
193			unmarshalled: &btcjson.GetBlockChainInfoCmd{},
194		},
195		{
196			name: "getblockcount",
197			newCmd: func() (interface{}, error) {
198				return btcjson.NewCmd("getblockcount")
199			},
200			staticCmd: func() interface{} {
201				return btcjson.NewGetBlockCountCmd()
202			},
203			marshalled:   `{"jsonrpc":"1.0","method":"getblockcount","params":[],"id":1}`,
204			unmarshalled: &btcjson.GetBlockCountCmd{},
205		},
206		{
207			name: "getblockhash",
208			newCmd: func() (interface{}, error) {
209				return btcjson.NewCmd("getblockhash", 123)
210			},
211			staticCmd: func() interface{} {
212				return btcjson.NewGetBlockHashCmd(123)
213			},
214			marshalled:   `{"jsonrpc":"1.0","method":"getblockhash","params":[123],"id":1}`,
215			unmarshalled: &btcjson.GetBlockHashCmd{Index: 123},
216		},
217		{
218			name: "getblockheader",
219			newCmd: func() (interface{}, error) {
220				return btcjson.NewCmd("getblockheader", "123")
221			},
222			staticCmd: func() interface{} {
223				return btcjson.NewGetBlockHeaderCmd("123", nil)
224			},
225			marshalled: `{"jsonrpc":"1.0","method":"getblockheader","params":["123"],"id":1}`,
226			unmarshalled: &btcjson.GetBlockHeaderCmd{
227				Hash:    "123",
228				Verbose: btcjson.Bool(true),
229			},
230		},
231		{
232			name: "getblockstats height",
233			newCmd: func() (interface{}, error) {
234				return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: 123})
235			},
236			staticCmd: func() interface{} {
237				return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: 123}, nil)
238			},
239			marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":[123],"id":1}`,
240			unmarshalled: &btcjson.GetBlockStatsCmd{
241				HashOrHeight: btcjson.HashOrHeight{Value: 123},
242			},
243		},
244		{
245			name: "getblockstats hash",
246			newCmd: func() (interface{}, error) {
247				return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: "deadbeef"})
248			},
249			staticCmd: func() interface{} {
250				return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: "deadbeef"}, nil)
251			},
252			marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":["deadbeef"],"id":1}`,
253			unmarshalled: &btcjson.GetBlockStatsCmd{
254				HashOrHeight: btcjson.HashOrHeight{Value: "deadbeef"},
255			},
256		},
257		{
258			name: "getblockstats height optional stats",
259			newCmd: func() (interface{}, error) {
260				return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: 123}, []string{"avgfee", "maxfee"})
261			},
262			staticCmd: func() interface{} {
263				return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: 123}, &[]string{"avgfee", "maxfee"})
264			},
265			marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":[123,["avgfee","maxfee"]],"id":1}`,
266			unmarshalled: &btcjson.GetBlockStatsCmd{
267				HashOrHeight: btcjson.HashOrHeight{Value: 123},
268				Stats:        &[]string{"avgfee", "maxfee"},
269			},
270		},
271		{
272			name: "getblockstats hash optional stats",
273			newCmd: func() (interface{}, error) {
274				return btcjson.NewCmd("getblockstats", btcjson.HashOrHeight{Value: "deadbeef"}, []string{"avgfee", "maxfee"})
275			},
276			staticCmd: func() interface{} {
277				return btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: "deadbeef"}, &[]string{"avgfee", "maxfee"})
278			},
279			marshalled: `{"jsonrpc":"1.0","method":"getblockstats","params":["deadbeef",["avgfee","maxfee"]],"id":1}`,
280			unmarshalled: &btcjson.GetBlockStatsCmd{
281				HashOrHeight: btcjson.HashOrHeight{Value: "deadbeef"},
282				Stats:        &[]string{"avgfee", "maxfee"},
283			},
284		},
285		{
286			name: "getblocktemplate",
287			newCmd: func() (interface{}, error) {
288				return btcjson.NewCmd("getblocktemplate")
289			},
290			staticCmd: func() interface{} {
291				return btcjson.NewGetBlockTemplateCmd(nil)
292			},
293			marshalled:   `{"jsonrpc":"1.0","method":"getblocktemplate","params":[],"id":1}`,
294			unmarshalled: &btcjson.GetBlockTemplateCmd{Request: nil},
295		},
296		{
297			name: "getblocktemplate optional - template request",
298			newCmd: func() (interface{}, error) {
299				return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"]}`)
300			},
301			staticCmd: func() interface{} {
302				template := btcjson.TemplateRequest{
303					Mode:         "template",
304					Capabilities: []string{"longpoll", "coinbasetxn"},
305				}
306				return btcjson.NewGetBlockTemplateCmd(&template)
307			},
308			marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"]}],"id":1}`,
309			unmarshalled: &btcjson.GetBlockTemplateCmd{
310				Request: &btcjson.TemplateRequest{
311					Mode:         "template",
312					Capabilities: []string{"longpoll", "coinbasetxn"},
313				},
314			},
315		},
316		{
317			name: "getblocktemplate optional - template request with tweaks",
318			newCmd: func() (interface{}, error) {
319				return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":500,"sizelimit":100000000,"maxversion":2}`)
320			},
321			staticCmd: func() interface{} {
322				template := btcjson.TemplateRequest{
323					Mode:         "template",
324					Capabilities: []string{"longpoll", "coinbasetxn"},
325					SigOpLimit:   500,
326					SizeLimit:    100000000,
327					MaxVersion:   2,
328				}
329				return btcjson.NewGetBlockTemplateCmd(&template)
330			},
331			marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":500,"sizelimit":100000000,"maxversion":2}],"id":1}`,
332			unmarshalled: &btcjson.GetBlockTemplateCmd{
333				Request: &btcjson.TemplateRequest{
334					Mode:         "template",
335					Capabilities: []string{"longpoll", "coinbasetxn"},
336					SigOpLimit:   int64(500),
337					SizeLimit:    int64(100000000),
338					MaxVersion:   2,
339				},
340			},
341		},
342		{
343			name: "getblocktemplate optional - template request with tweaks 2",
344			newCmd: func() (interface{}, error) {
345				return btcjson.NewCmd("getblocktemplate", `{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":true,"sizelimit":100000000,"maxversion":2}`)
346			},
347			staticCmd: func() interface{} {
348				template := btcjson.TemplateRequest{
349					Mode:         "template",
350					Capabilities: []string{"longpoll", "coinbasetxn"},
351					SigOpLimit:   true,
352					SizeLimit:    100000000,
353					MaxVersion:   2,
354				}
355				return btcjson.NewGetBlockTemplateCmd(&template)
356			},
357			marshalled: `{"jsonrpc":"1.0","method":"getblocktemplate","params":[{"mode":"template","capabilities":["longpoll","coinbasetxn"],"sigoplimit":true,"sizelimit":100000000,"maxversion":2}],"id":1}`,
358			unmarshalled: &btcjson.GetBlockTemplateCmd{
359				Request: &btcjson.TemplateRequest{
360					Mode:         "template",
361					Capabilities: []string{"longpoll", "coinbasetxn"},
362					SigOpLimit:   true,
363					SizeLimit:    int64(100000000),
364					MaxVersion:   2,
365				},
366			},
367		},
368		{
369			name: "getcfilter",
370			newCmd: func() (interface{}, error) {
371				return btcjson.NewCmd("getcfilter", "123",
372					wire.GCSFilterRegular)
373			},
374			staticCmd: func() interface{} {
375				return btcjson.NewGetCFilterCmd("123",
376					wire.GCSFilterRegular)
377			},
378			marshalled: `{"jsonrpc":"1.0","method":"getcfilter","params":["123",0],"id":1}`,
379			unmarshalled: &btcjson.GetCFilterCmd{
380				Hash:       "123",
381				FilterType: wire.GCSFilterRegular,
382			},
383		},
384		{
385			name: "getcfilterheader",
386			newCmd: func() (interface{}, error) {
387				return btcjson.NewCmd("getcfilterheader", "123",
388					wire.GCSFilterRegular)
389			},
390			staticCmd: func() interface{} {
391				return btcjson.NewGetCFilterHeaderCmd("123",
392					wire.GCSFilterRegular)
393			},
394			marshalled: `{"jsonrpc":"1.0","method":"getcfilterheader","params":["123",0],"id":1}`,
395			unmarshalled: &btcjson.GetCFilterHeaderCmd{
396				Hash:       "123",
397				FilterType: wire.GCSFilterRegular,
398			},
399		},
400		{
401			name: "getchaintips",
402			newCmd: func() (interface{}, error) {
403				return btcjson.NewCmd("getchaintips")
404			},
405			staticCmd: func() interface{} {
406				return btcjson.NewGetChainTipsCmd()
407			},
408			marshalled:   `{"jsonrpc":"1.0","method":"getchaintips","params":[],"id":1}`,
409			unmarshalled: &btcjson.GetChainTipsCmd{},
410		},
411		{
412			name: "getconnectioncount",
413			newCmd: func() (interface{}, error) {
414				return btcjson.NewCmd("getconnectioncount")
415			},
416			staticCmd: func() interface{} {
417				return btcjson.NewGetConnectionCountCmd()
418			},
419			marshalled:   `{"jsonrpc":"1.0","method":"getconnectioncount","params":[],"id":1}`,
420			unmarshalled: &btcjson.GetConnectionCountCmd{},
421		},
422		{
423			name: "getdifficulty",
424			newCmd: func() (interface{}, error) {
425				return btcjson.NewCmd("getdifficulty")
426			},
427			staticCmd: func() interface{} {
428				return btcjson.NewGetDifficultyCmd()
429			},
430			marshalled:   `{"jsonrpc":"1.0","method":"getdifficulty","params":[],"id":1}`,
431			unmarshalled: &btcjson.GetDifficultyCmd{},
432		},
433		{
434			name: "getgenerate",
435			newCmd: func() (interface{}, error) {
436				return btcjson.NewCmd("getgenerate")
437			},
438			staticCmd: func() interface{} {
439				return btcjson.NewGetGenerateCmd()
440			},
441			marshalled:   `{"jsonrpc":"1.0","method":"getgenerate","params":[],"id":1}`,
442			unmarshalled: &btcjson.GetGenerateCmd{},
443		},
444		{
445			name: "gethashespersec",
446			newCmd: func() (interface{}, error) {
447				return btcjson.NewCmd("gethashespersec")
448			},
449			staticCmd: func() interface{} {
450				return btcjson.NewGetHashesPerSecCmd()
451			},
452			marshalled:   `{"jsonrpc":"1.0","method":"gethashespersec","params":[],"id":1}`,
453			unmarshalled: &btcjson.GetHashesPerSecCmd{},
454		},
455		{
456			name: "getinfo",
457			newCmd: func() (interface{}, error) {
458				return btcjson.NewCmd("getinfo")
459			},
460			staticCmd: func() interface{} {
461				return btcjson.NewGetInfoCmd()
462			},
463			marshalled:   `{"jsonrpc":"1.0","method":"getinfo","params":[],"id":1}`,
464			unmarshalled: &btcjson.GetInfoCmd{},
465		},
466		{
467			name: "getmempoolentry",
468			newCmd: func() (interface{}, error) {
469				return btcjson.NewCmd("getmempoolentry", "txhash")
470			},
471			staticCmd: func() interface{} {
472				return btcjson.NewGetMempoolEntryCmd("txhash")
473			},
474			marshalled: `{"jsonrpc":"1.0","method":"getmempoolentry","params":["txhash"],"id":1}`,
475			unmarshalled: &btcjson.GetMempoolEntryCmd{
476				TxID: "txhash",
477			},
478		},
479		{
480			name: "getmempoolinfo",
481			newCmd: func() (interface{}, error) {
482				return btcjson.NewCmd("getmempoolinfo")
483			},
484			staticCmd: func() interface{} {
485				return btcjson.NewGetMempoolInfoCmd()
486			},
487			marshalled:   `{"jsonrpc":"1.0","method":"getmempoolinfo","params":[],"id":1}`,
488			unmarshalled: &btcjson.GetMempoolInfoCmd{},
489		},
490		{
491			name: "getmininginfo",
492			newCmd: func() (interface{}, error) {
493				return btcjson.NewCmd("getmininginfo")
494			},
495			staticCmd: func() interface{} {
496				return btcjson.NewGetMiningInfoCmd()
497			},
498			marshalled:   `{"jsonrpc":"1.0","method":"getmininginfo","params":[],"id":1}`,
499			unmarshalled: &btcjson.GetMiningInfoCmd{},
500		},
501		{
502			name: "getnetworkinfo",
503			newCmd: func() (interface{}, error) {
504				return btcjson.NewCmd("getnetworkinfo")
505			},
506			staticCmd: func() interface{} {
507				return btcjson.NewGetNetworkInfoCmd()
508			},
509			marshalled:   `{"jsonrpc":"1.0","method":"getnetworkinfo","params":[],"id":1}`,
510			unmarshalled: &btcjson.GetNetworkInfoCmd{},
511		},
512		{
513			name: "getnettotals",
514			newCmd: func() (interface{}, error) {
515				return btcjson.NewCmd("getnettotals")
516			},
517			staticCmd: func() interface{} {
518				return btcjson.NewGetNetTotalsCmd()
519			},
520			marshalled:   `{"jsonrpc":"1.0","method":"getnettotals","params":[],"id":1}`,
521			unmarshalled: &btcjson.GetNetTotalsCmd{},
522		},
523		{
524			name: "getnetworkhashps",
525			newCmd: func() (interface{}, error) {
526				return btcjson.NewCmd("getnetworkhashps")
527			},
528			staticCmd: func() interface{} {
529				return btcjson.NewGetNetworkHashPSCmd(nil, nil)
530			},
531			marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[],"id":1}`,
532			unmarshalled: &btcjson.GetNetworkHashPSCmd{
533				Blocks: btcjson.Int(120),
534				Height: btcjson.Int(-1),
535			},
536		},
537		{
538			name: "getnetworkhashps optional1",
539			newCmd: func() (interface{}, error) {
540				return btcjson.NewCmd("getnetworkhashps", 200)
541			},
542			staticCmd: func() interface{} {
543				return btcjson.NewGetNetworkHashPSCmd(btcjson.Int(200), nil)
544			},
545			marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[200],"id":1}`,
546			unmarshalled: &btcjson.GetNetworkHashPSCmd{
547				Blocks: btcjson.Int(200),
548				Height: btcjson.Int(-1),
549			},
550		},
551		{
552			name: "getnetworkhashps optional2",
553			newCmd: func() (interface{}, error) {
554				return btcjson.NewCmd("getnetworkhashps", 200, 123)
555			},
556			staticCmd: func() interface{} {
557				return btcjson.NewGetNetworkHashPSCmd(btcjson.Int(200), btcjson.Int(123))
558			},
559			marshalled: `{"jsonrpc":"1.0","method":"getnetworkhashps","params":[200,123],"id":1}`,
560			unmarshalled: &btcjson.GetNetworkHashPSCmd{
561				Blocks: btcjson.Int(200),
562				Height: btcjson.Int(123),
563			},
564		},
565		{
566			name: "getpeerinfo",
567			newCmd: func() (interface{}, error) {
568				return btcjson.NewCmd("getpeerinfo")
569			},
570			staticCmd: func() interface{} {
571				return btcjson.NewGetPeerInfoCmd()
572			},
573			marshalled:   `{"jsonrpc":"1.0","method":"getpeerinfo","params":[],"id":1}`,
574			unmarshalled: &btcjson.GetPeerInfoCmd{},
575		},
576		{
577			name: "getrawmempool",
578			newCmd: func() (interface{}, error) {
579				return btcjson.NewCmd("getrawmempool")
580			},
581			staticCmd: func() interface{} {
582				return btcjson.NewGetRawMempoolCmd(nil)
583			},
584			marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[],"id":1}`,
585			unmarshalled: &btcjson.GetRawMempoolCmd{
586				Verbose: btcjson.Bool(false),
587			},
588		},
589		{
590			name: "getrawmempool optional",
591			newCmd: func() (interface{}, error) {
592				return btcjson.NewCmd("getrawmempool", false)
593			},
594			staticCmd: func() interface{} {
595				return btcjson.NewGetRawMempoolCmd(btcjson.Bool(false))
596			},
597			marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[false],"id":1}`,
598			unmarshalled: &btcjson.GetRawMempoolCmd{
599				Verbose: btcjson.Bool(false),
600			},
601		},
602		{
603			name: "getrawtransaction",
604			newCmd: func() (interface{}, error) {
605				return btcjson.NewCmd("getrawtransaction", "123")
606			},
607			staticCmd: func() interface{} {
608				return btcjson.NewGetRawTransactionCmd("123", nil)
609			},
610			marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123"],"id":1}`,
611			unmarshalled: &btcjson.GetRawTransactionCmd{
612				Txid:    "123",
613				Verbose: btcjson.Int(0),
614			},
615		},
616		{
617			name: "getrawtransaction optional",
618			newCmd: func() (interface{}, error) {
619				return btcjson.NewCmd("getrawtransaction", "123", 1)
620			},
621			staticCmd: func() interface{} {
622				return btcjson.NewGetRawTransactionCmd("123", btcjson.Int(1))
623			},
624			marshalled: `{"jsonrpc":"1.0","method":"getrawtransaction","params":["123",1],"id":1}`,
625			unmarshalled: &btcjson.GetRawTransactionCmd{
626				Txid:    "123",
627				Verbose: btcjson.Int(1),
628			},
629		},
630		{
631			name: "gettxout",
632			newCmd: func() (interface{}, error) {
633				return btcjson.NewCmd("gettxout", "123", 1)
634			},
635			staticCmd: func() interface{} {
636				return btcjson.NewGetTxOutCmd("123", 1, nil)
637			},
638			marshalled: `{"jsonrpc":"1.0","method":"gettxout","params":["123",1],"id":1}`,
639			unmarshalled: &btcjson.GetTxOutCmd{
640				Txid:           "123",
641				Vout:           1,
642				IncludeMempool: btcjson.Bool(true),
643			},
644		},
645		{
646			name: "gettxout optional",
647			newCmd: func() (interface{}, error) {
648				return btcjson.NewCmd("gettxout", "123", 1, true)
649			},
650			staticCmd: func() interface{} {
651				return btcjson.NewGetTxOutCmd("123", 1, btcjson.Bool(true))
652			},
653			marshalled: `{"jsonrpc":"1.0","method":"gettxout","params":["123",1,true],"id":1}`,
654			unmarshalled: &btcjson.GetTxOutCmd{
655				Txid:           "123",
656				Vout:           1,
657				IncludeMempool: btcjson.Bool(true),
658			},
659		},
660		{
661			name: "gettxoutproof",
662			newCmd: func() (interface{}, error) {
663				return btcjson.NewCmd("gettxoutproof", []string{"123", "456"})
664			},
665			staticCmd: func() interface{} {
666				return btcjson.NewGetTxOutProofCmd([]string{"123", "456"}, nil)
667			},
668			marshalled: `{"jsonrpc":"1.0","method":"gettxoutproof","params":[["123","456"]],"id":1}`,
669			unmarshalled: &btcjson.GetTxOutProofCmd{
670				TxIDs: []string{"123", "456"},
671			},
672		},
673		{
674			name: "gettxoutproof optional",
675			newCmd: func() (interface{}, error) {
676				return btcjson.NewCmd("gettxoutproof", []string{"123", "456"},
677					btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"))
678			},
679			staticCmd: func() interface{} {
680				return btcjson.NewGetTxOutProofCmd([]string{"123", "456"},
681					btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"))
682			},
683			marshalled: `{"jsonrpc":"1.0","method":"gettxoutproof","params":[["123","456"],` +
684				`"000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"],"id":1}`,
685			unmarshalled: &btcjson.GetTxOutProofCmd{
686				TxIDs:     []string{"123", "456"},
687				BlockHash: btcjson.String("000000000000034a7dedef4a161fa058a2d67a173a90155f3a2fe6fc132e0ebf"),
688			},
689		},
690		{
691			name: "gettxoutsetinfo",
692			newCmd: func() (interface{}, error) {
693				return btcjson.NewCmd("gettxoutsetinfo")
694			},
695			staticCmd: func() interface{} {
696				return btcjson.NewGetTxOutSetInfoCmd()
697			},
698			marshalled:   `{"jsonrpc":"1.0","method":"gettxoutsetinfo","params":[],"id":1}`,
699			unmarshalled: &btcjson.GetTxOutSetInfoCmd{},
700		},
701		{
702			name: "getwork",
703			newCmd: func() (interface{}, error) {
704				return btcjson.NewCmd("getwork")
705			},
706			staticCmd: func() interface{} {
707				return btcjson.NewGetWorkCmd(nil)
708			},
709			marshalled: `{"jsonrpc":"1.0","method":"getwork","params":[],"id":1}`,
710			unmarshalled: &btcjson.GetWorkCmd{
711				Data: nil,
712			},
713		},
714		{
715			name: "getwork optional",
716			newCmd: func() (interface{}, error) {
717				return btcjson.NewCmd("getwork", "00112233")
718			},
719			staticCmd: func() interface{} {
720				return btcjson.NewGetWorkCmd(btcjson.String("00112233"))
721			},
722			marshalled: `{"jsonrpc":"1.0","method":"getwork","params":["00112233"],"id":1}`,
723			unmarshalled: &btcjson.GetWorkCmd{
724				Data: btcjson.String("00112233"),
725			},
726		},
727		{
728			name: "help",
729			newCmd: func() (interface{}, error) {
730				return btcjson.NewCmd("help")
731			},
732			staticCmd: func() interface{} {
733				return btcjson.NewHelpCmd(nil)
734			},
735			marshalled: `{"jsonrpc":"1.0","method":"help","params":[],"id":1}`,
736			unmarshalled: &btcjson.HelpCmd{
737				Command: nil,
738			},
739		},
740		{
741			name: "help optional",
742			newCmd: func() (interface{}, error) {
743				return btcjson.NewCmd("help", "getblock")
744			},
745			staticCmd: func() interface{} {
746				return btcjson.NewHelpCmd(btcjson.String("getblock"))
747			},
748			marshalled: `{"jsonrpc":"1.0","method":"help","params":["getblock"],"id":1}`,
749			unmarshalled: &btcjson.HelpCmd{
750				Command: btcjson.String("getblock"),
751			},
752		},
753		{
754			name: "invalidateblock",
755			newCmd: func() (interface{}, error) {
756				return btcjson.NewCmd("invalidateblock", "123")
757			},
758			staticCmd: func() interface{} {
759				return btcjson.NewInvalidateBlockCmd("123")
760			},
761			marshalled: `{"jsonrpc":"1.0","method":"invalidateblock","params":["123"],"id":1}`,
762			unmarshalled: &btcjson.InvalidateBlockCmd{
763				BlockHash: "123",
764			},
765		},
766		{
767			name: "ping",
768			newCmd: func() (interface{}, error) {
769				return btcjson.NewCmd("ping")
770			},
771			staticCmd: func() interface{} {
772				return btcjson.NewPingCmd()
773			},
774			marshalled:   `{"jsonrpc":"1.0","method":"ping","params":[],"id":1}`,
775			unmarshalled: &btcjson.PingCmd{},
776		},
777		{
778			name: "preciousblock",
779			newCmd: func() (interface{}, error) {
780				return btcjson.NewCmd("preciousblock", "0123")
781			},
782			staticCmd: func() interface{} {
783				return btcjson.NewPreciousBlockCmd("0123")
784			},
785			marshalled: `{"jsonrpc":"1.0","method":"preciousblock","params":["0123"],"id":1}`,
786			unmarshalled: &btcjson.PreciousBlockCmd{
787				BlockHash: "0123",
788			},
789		},
790		{
791			name: "reconsiderblock",
792			newCmd: func() (interface{}, error) {
793				return btcjson.NewCmd("reconsiderblock", "123")
794			},
795			staticCmd: func() interface{} {
796				return btcjson.NewReconsiderBlockCmd("123")
797			},
798			marshalled: `{"jsonrpc":"1.0","method":"reconsiderblock","params":["123"],"id":1}`,
799			unmarshalled: &btcjson.ReconsiderBlockCmd{
800				BlockHash: "123",
801			},
802		},
803		{
804			name: "searchrawtransactions",
805			newCmd: func() (interface{}, error) {
806				return btcjson.NewCmd("searchrawtransactions", "1Address")
807			},
808			staticCmd: func() interface{} {
809				return btcjson.NewSearchRawTransactionsCmd("1Address", nil, nil, nil, nil, nil, nil)
810			},
811			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address"],"id":1}`,
812			unmarshalled: &btcjson.SearchRawTransactionsCmd{
813				Address:     "1Address",
814				Verbose:     btcjson.Int(1),
815				Skip:        btcjson.Int(0),
816				Count:       btcjson.Int(100),
817				VinExtra:    btcjson.Int(0),
818				Reverse:     btcjson.Bool(false),
819				FilterAddrs: nil,
820			},
821		},
822		{
823			name: "searchrawtransactions",
824			newCmd: func() (interface{}, error) {
825				return btcjson.NewCmd("searchrawtransactions", "1Address", 0)
826			},
827			staticCmd: func() interface{} {
828				return btcjson.NewSearchRawTransactionsCmd("1Address",
829					btcjson.Int(0), nil, nil, nil, nil, nil)
830			},
831			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0],"id":1}`,
832			unmarshalled: &btcjson.SearchRawTransactionsCmd{
833				Address:     "1Address",
834				Verbose:     btcjson.Int(0),
835				Skip:        btcjson.Int(0),
836				Count:       btcjson.Int(100),
837				VinExtra:    btcjson.Int(0),
838				Reverse:     btcjson.Bool(false),
839				FilterAddrs: nil,
840			},
841		},
842		{
843			name: "searchrawtransactions",
844			newCmd: func() (interface{}, error) {
845				return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5)
846			},
847			staticCmd: func() interface{} {
848				return btcjson.NewSearchRawTransactionsCmd("1Address",
849					btcjson.Int(0), btcjson.Int(5), nil, nil, nil, nil)
850			},
851			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5],"id":1}`,
852			unmarshalled: &btcjson.SearchRawTransactionsCmd{
853				Address:     "1Address",
854				Verbose:     btcjson.Int(0),
855				Skip:        btcjson.Int(5),
856				Count:       btcjson.Int(100),
857				VinExtra:    btcjson.Int(0),
858				Reverse:     btcjson.Bool(false),
859				FilterAddrs: nil,
860			},
861		},
862		{
863			name: "searchrawtransactions",
864			newCmd: func() (interface{}, error) {
865				return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10)
866			},
867			staticCmd: func() interface{} {
868				return btcjson.NewSearchRawTransactionsCmd("1Address",
869					btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), nil, nil, nil)
870			},
871			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10],"id":1}`,
872			unmarshalled: &btcjson.SearchRawTransactionsCmd{
873				Address:     "1Address",
874				Verbose:     btcjson.Int(0),
875				Skip:        btcjson.Int(5),
876				Count:       btcjson.Int(10),
877				VinExtra:    btcjson.Int(0),
878				Reverse:     btcjson.Bool(false),
879				FilterAddrs: nil,
880			},
881		},
882		{
883			name: "searchrawtransactions",
884			newCmd: func() (interface{}, error) {
885				return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1)
886			},
887			staticCmd: func() interface{} {
888				return btcjson.NewSearchRawTransactionsCmd("1Address",
889					btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), nil, nil)
890			},
891			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1],"id":1}`,
892			unmarshalled: &btcjson.SearchRawTransactionsCmd{
893				Address:     "1Address",
894				Verbose:     btcjson.Int(0),
895				Skip:        btcjson.Int(5),
896				Count:       btcjson.Int(10),
897				VinExtra:    btcjson.Int(1),
898				Reverse:     btcjson.Bool(false),
899				FilterAddrs: nil,
900			},
901		},
902		{
903			name: "searchrawtransactions",
904			newCmd: func() (interface{}, error) {
905				return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1, true)
906			},
907			staticCmd: func() interface{} {
908				return btcjson.NewSearchRawTransactionsCmd("1Address",
909					btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), btcjson.Bool(true), nil)
910			},
911			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1,true],"id":1}`,
912			unmarshalled: &btcjson.SearchRawTransactionsCmd{
913				Address:     "1Address",
914				Verbose:     btcjson.Int(0),
915				Skip:        btcjson.Int(5),
916				Count:       btcjson.Int(10),
917				VinExtra:    btcjson.Int(1),
918				Reverse:     btcjson.Bool(true),
919				FilterAddrs: nil,
920			},
921		},
922		{
923			name: "searchrawtransactions",
924			newCmd: func() (interface{}, error) {
925				return btcjson.NewCmd("searchrawtransactions", "1Address", 0, 5, 10, 1, true, []string{"1Address"})
926			},
927			staticCmd: func() interface{} {
928				return btcjson.NewSearchRawTransactionsCmd("1Address",
929					btcjson.Int(0), btcjson.Int(5), btcjson.Int(10), btcjson.Int(1), btcjson.Bool(true), &[]string{"1Address"})
930			},
931			marshalled: `{"jsonrpc":"1.0","method":"searchrawtransactions","params":["1Address",0,5,10,1,true,["1Address"]],"id":1}`,
932			unmarshalled: &btcjson.SearchRawTransactionsCmd{
933				Address:     "1Address",
934				Verbose:     btcjson.Int(0),
935				Skip:        btcjson.Int(5),
936				Count:       btcjson.Int(10),
937				VinExtra:    btcjson.Int(1),
938				Reverse:     btcjson.Bool(true),
939				FilterAddrs: &[]string{"1Address"},
940			},
941		},
942		{
943			name: "sendrawtransaction",
944			newCmd: func() (interface{}, error) {
945				return btcjson.NewCmd("sendrawtransaction", "1122")
946			},
947			staticCmd: func() interface{} {
948				return btcjson.NewSendRawTransactionCmd("1122", nil)
949			},
950			marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122"],"id":1}`,
951			unmarshalled: &btcjson.SendRawTransactionCmd{
952				HexTx:         "1122",
953				AllowHighFees: btcjson.Bool(false),
954			},
955		},
956		{
957			name: "sendrawtransaction optional",
958			newCmd: func() (interface{}, error) {
959				return btcjson.NewCmd("sendrawtransaction", "1122", false)
960			},
961			staticCmd: func() interface{} {
962				return btcjson.NewSendRawTransactionCmd("1122", btcjson.Bool(false))
963			},
964			marshalled: `{"jsonrpc":"1.0","method":"sendrawtransaction","params":["1122",false],"id":1}`,
965			unmarshalled: &btcjson.SendRawTransactionCmd{
966				HexTx:         "1122",
967				AllowHighFees: btcjson.Bool(false),
968			},
969		},
970		{
971			name: "setgenerate",
972			newCmd: func() (interface{}, error) {
973				return btcjson.NewCmd("setgenerate", true)
974			},
975			staticCmd: func() interface{} {
976				return btcjson.NewSetGenerateCmd(true, nil)
977			},
978			marshalled: `{"jsonrpc":"1.0","method":"setgenerate","params":[true],"id":1}`,
979			unmarshalled: &btcjson.SetGenerateCmd{
980				Generate:     true,
981				GenProcLimit: btcjson.Int(-1),
982			},
983		},
984		{
985			name: "setgenerate optional",
986			newCmd: func() (interface{}, error) {
987				return btcjson.NewCmd("setgenerate", true, 6)
988			},
989			staticCmd: func() interface{} {
990				return btcjson.NewSetGenerateCmd(true, btcjson.Int(6))
991			},
992			marshalled: `{"jsonrpc":"1.0","method":"setgenerate","params":[true,6],"id":1}`,
993			unmarshalled: &btcjson.SetGenerateCmd{
994				Generate:     true,
995				GenProcLimit: btcjson.Int(6),
996			},
997		},
998		{
999			name: "stop",
1000			newCmd: func() (interface{}, error) {
1001				return btcjson.NewCmd("stop")
1002			},
1003			staticCmd: func() interface{} {
1004				return btcjson.NewStopCmd()
1005			},
1006			marshalled:   `{"jsonrpc":"1.0","method":"stop","params":[],"id":1}`,
1007			unmarshalled: &btcjson.StopCmd{},
1008		},
1009		{
1010			name: "submitblock",
1011			newCmd: func() (interface{}, error) {
1012				return btcjson.NewCmd("submitblock", "112233")
1013			},
1014			staticCmd: func() interface{} {
1015				return btcjson.NewSubmitBlockCmd("112233", nil)
1016			},
1017			marshalled: `{"jsonrpc":"1.0","method":"submitblock","params":["112233"],"id":1}`,
1018			unmarshalled: &btcjson.SubmitBlockCmd{
1019				HexBlock: "112233",
1020				Options:  nil,
1021			},
1022		},
1023		{
1024			name: "submitblock optional",
1025			newCmd: func() (interface{}, error) {
1026				return btcjson.NewCmd("submitblock", "112233", `{"workid":"12345"}`)
1027			},
1028			staticCmd: func() interface{} {
1029				options := btcjson.SubmitBlockOptions{
1030					WorkID: "12345",
1031				}
1032				return btcjson.NewSubmitBlockCmd("112233", &options)
1033			},
1034			marshalled: `{"jsonrpc":"1.0","method":"submitblock","params":["112233",{"workid":"12345"}],"id":1}`,
1035			unmarshalled: &btcjson.SubmitBlockCmd{
1036				HexBlock: "112233",
1037				Options: &btcjson.SubmitBlockOptions{
1038					WorkID: "12345",
1039				},
1040			},
1041		},
1042		{
1043			name: "uptime",
1044			newCmd: func() (interface{}, error) {
1045				return btcjson.NewCmd("uptime")
1046			},
1047			staticCmd: func() interface{} {
1048				return btcjson.NewUptimeCmd()
1049			},
1050			marshalled:   `{"jsonrpc":"1.0","method":"uptime","params":[],"id":1}`,
1051			unmarshalled: &btcjson.UptimeCmd{},
1052		},
1053		{
1054			name: "validateaddress",
1055			newCmd: func() (interface{}, error) {
1056				return btcjson.NewCmd("validateaddress", "1Address")
1057			},
1058			staticCmd: func() interface{} {
1059				return btcjson.NewValidateAddressCmd("1Address")
1060			},
1061			marshalled: `{"jsonrpc":"1.0","method":"validateaddress","params":["1Address"],"id":1}`,
1062			unmarshalled: &btcjson.ValidateAddressCmd{
1063				Address: "1Address",
1064			},
1065		},
1066		{
1067			name: "verifychain",
1068			newCmd: func() (interface{}, error) {
1069				return btcjson.NewCmd("verifychain")
1070			},
1071			staticCmd: func() interface{} {
1072				return btcjson.NewVerifyChainCmd(nil, nil)
1073			},
1074			marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[],"id":1}`,
1075			unmarshalled: &btcjson.VerifyChainCmd{
1076				CheckLevel: btcjson.Int32(3),
1077				CheckDepth: btcjson.Int32(288),
1078			},
1079		},
1080		{
1081			name: "verifychain optional1",
1082			newCmd: func() (interface{}, error) {
1083				return btcjson.NewCmd("verifychain", 2)
1084			},
1085			staticCmd: func() interface{} {
1086				return btcjson.NewVerifyChainCmd(btcjson.Int32(2), nil)
1087			},
1088			marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[2],"id":1}`,
1089			unmarshalled: &btcjson.VerifyChainCmd{
1090				CheckLevel: btcjson.Int32(2),
1091				CheckDepth: btcjson.Int32(288),
1092			},
1093		},
1094		{
1095			name: "verifychain optional2",
1096			newCmd: func() (interface{}, error) {
1097				return btcjson.NewCmd("verifychain", 2, 500)
1098			},
1099			staticCmd: func() interface{} {
1100				return btcjson.NewVerifyChainCmd(btcjson.Int32(2), btcjson.Int32(500))
1101			},
1102			marshalled: `{"jsonrpc":"1.0","method":"verifychain","params":[2,500],"id":1}`,
1103			unmarshalled: &btcjson.VerifyChainCmd{
1104				CheckLevel: btcjson.Int32(2),
1105				CheckDepth: btcjson.Int32(500),
1106			},
1107		},
1108		{
1109			name: "verifymessage",
1110			newCmd: func() (interface{}, error) {
1111				return btcjson.NewCmd("verifymessage", "1Address", "301234", "test")
1112			},
1113			staticCmd: func() interface{} {
1114				return btcjson.NewVerifyMessageCmd("1Address", "301234", "test")
1115			},
1116			marshalled: `{"jsonrpc":"1.0","method":"verifymessage","params":["1Address","301234","test"],"id":1}`,
1117			unmarshalled: &btcjson.VerifyMessageCmd{
1118				Address:   "1Address",
1119				Signature: "301234",
1120				Message:   "test",
1121			},
1122		},
1123		{
1124			name: "verifytxoutproof",
1125			newCmd: func() (interface{}, error) {
1126				return btcjson.NewCmd("verifytxoutproof", "test")
1127			},
1128			staticCmd: func() interface{} {
1129				return btcjson.NewVerifyTxOutProofCmd("test")
1130			},
1131			marshalled: `{"jsonrpc":"1.0","method":"verifytxoutproof","params":["test"],"id":1}`,
1132			unmarshalled: &btcjson.VerifyTxOutProofCmd{
1133				Proof: "test",
1134			},
1135		},
1136	}
1137
1138	t.Logf("Running %d tests", len(tests))
1139	for i, test := range tests {
1140		// Marshal the command as created by the new static command
1141		// creation function.
1142		marshalled, err := btcjson.MarshalCmd(testID, test.staticCmd())
1143		if err != nil {
1144			t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i,
1145				test.name, err)
1146			continue
1147		}
1148
1149		if !bytes.Equal(marshalled, []byte(test.marshalled)) {
1150			t.Errorf("Test #%d (%s) unexpected marshalled data - "+
1151				"got %s, want %s", i, test.name, marshalled,
1152				test.marshalled)
1153			t.Errorf("\n%s\n%s", marshalled, test.marshalled)
1154			continue
1155		}
1156
1157		// Ensure the command is created without error via the generic
1158		// new command creation function.
1159		cmd, err := test.newCmd()
1160		if err != nil {
1161			t.Errorf("Test #%d (%s) unexpected NewCmd error: %v ",
1162				i, test.name, err)
1163		}
1164
1165		// Marshal the command as created by the generic new command
1166		// creation function.
1167		marshalled, err = btcjson.MarshalCmd(testID, cmd)
1168		if err != nil {
1169			t.Errorf("MarshalCmd #%d (%s) unexpected error: %v", i,
1170				test.name, err)
1171			continue
1172		}
1173
1174		if !bytes.Equal(marshalled, []byte(test.marshalled)) {
1175			t.Errorf("Test #%d (%s) unexpected marshalled data - "+
1176				"got %s, want %s", i, test.name, marshalled,
1177				test.marshalled)
1178			continue
1179		}
1180
1181		var request btcjson.Request
1182		if err := json.Unmarshal(marshalled, &request); err != nil {
1183			t.Errorf("Test #%d (%s) unexpected error while "+
1184				"unmarshalling JSON-RPC request: %v", i,
1185				test.name, err)
1186			continue
1187		}
1188
1189		cmd, err = btcjson.UnmarshalCmd(&request)
1190		if err != nil {
1191			t.Errorf("UnmarshalCmd #%d (%s) unexpected error: %v", i,
1192				test.name, err)
1193			continue
1194		}
1195
1196		if !reflect.DeepEqual(cmd, test.unmarshalled) {
1197			t.Errorf("Test #%d (%s) unexpected unmarshalled command "+
1198				"- got %s, want %s", i, test.name,
1199				fmt.Sprintf("(%T) %+[1]v", cmd),
1200				fmt.Sprintf("(%T) %+[1]v\n", test.unmarshalled))
1201			continue
1202		}
1203	}
1204}
1205
1206// TestChainSvrCmdErrors ensures any errors that occur in the command during
1207// custom mashal and unmarshal are as expected.
1208func TestChainSvrCmdErrors(t *testing.T) {
1209	t.Parallel()
1210
1211	tests := []struct {
1212		name       string
1213		result     interface{}
1214		marshalled string
1215		err        error
1216	}{
1217		{
1218			name:       "template request with invalid type",
1219			result:     &btcjson.TemplateRequest{},
1220			marshalled: `{"mode":1}`,
1221			err:        &json.UnmarshalTypeError{},
1222		},
1223		{
1224			name:       "invalid template request sigoplimit field",
1225			result:     &btcjson.TemplateRequest{},
1226			marshalled: `{"sigoplimit":"invalid"}`,
1227			err:        btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
1228		},
1229		{
1230			name:       "invalid template request sizelimit field",
1231			result:     &btcjson.TemplateRequest{},
1232			marshalled: `{"sizelimit":"invalid"}`,
1233			err:        btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
1234		},
1235	}
1236
1237	t.Logf("Running %d tests", len(tests))
1238	for i, test := range tests {
1239		err := json.Unmarshal([]byte(test.marshalled), &test.result)
1240		if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
1241			t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
1242				"want %T", i, test.name, err, err, test.err)
1243			continue
1244		}
1245
1246		if terr, ok := test.err.(btcjson.Error); ok {
1247			gotErrorCode := err.(btcjson.Error).ErrorCode
1248			if gotErrorCode != terr.ErrorCode {
1249				t.Errorf("Test #%d (%s) mismatched error code "+
1250					"- got %v (%v), want %v", i, test.name,
1251					gotErrorCode, terr, terr.ErrorCode)
1252				continue
1253			}
1254		}
1255	}
1256}
1257