rippled
JSONRPC_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2014 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/misc/LoadFeeTrack.h>
21 #include <ripple/app/misc/TxQ.h>
22 #include <ripple/basics/contract.h>
23 #include <ripple/beast/unit_test.h>
24 #include <ripple/core/ConfigSections.h>
25 #include <ripple/json/json_reader.h>
26 #include <ripple/protocol/ErrorCodes.h>
27 #include <ripple/protocol/Feature.h>
28 #include <ripple/rpc/impl/TransactionSign.h>
29 #include <test/jtx.h>
30 #include <test/jtx/envconfig.h>
31 
32 namespace ripple {
33 
34 namespace RPC {
35 
37 {
38  char const* const description;
39  int const line;
40  char const* const json;
41  // The JSON is applied to four different interfaces:
42  // 1. sign,
43  // 2. submit,
44  // 3. sign_for, and
45  // 4. submit_multisigned.
46  // The JSON is not valid for all of these interfaces, but it should
47  // crash none of them, and should provide reliable error messages.
48  //
49  // The expMsg array contains the expected error string for the above cases.
51 
52  constexpr TxnTestData(
53  char const* description_,
54  int line_,
55  char const* json_,
56  std::array<char const* const, 4> const& expMsg_)
57  : description(description_), line(line_), json(json_), expMsg{expMsg_}
58  {
59  }
60 
61  TxnTestData() = delete;
62  TxnTestData(TxnTestData const&) = delete;
63  TxnTestData(TxnTestData&&) = delete;
65  operator=(TxnTestData const&) = delete;
67  operator=(TxnTestData&&) = delete;
68 };
69 
70 static constexpr TxnTestData txnTestArray[] = {
71 
72  {"Minimal payment.",
73  __LINE__,
74  R"({
75  "command": "doesnt_matter",
76  "secret": "masterpassphrase",
77  "tx_json": {
78  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
79  "Amount": "1000000000",
80  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
81  "TransactionType": "Payment"
82  }
83 })",
84  {{"",
85  "",
86  "Missing field 'account'.",
87  "Missing field 'tx_json.Sequence'."}}},
88 
89  {"Pass in Fee with minimal payment.",
90  __LINE__,
91  R"({
92  "command": "doesnt_matter",
93  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
94  "secret": "masterpassphrase",
95  "tx_json": {
96  "Fee": 10,
97  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
98  "Amount": "1000000000",
99  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
100  "TransactionType": "Payment"
101  }
102 })",
103  {{"",
104  "",
105  "Missing field 'tx_json.Sequence'.",
106  "Missing field 'tx_json.Sequence'."}}},
107 
108  {"Pass in Sequence.",
109  __LINE__,
110  R"({
111  "command": "doesnt_matter",
112  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
113  "secret": "masterpassphrase",
114  "tx_json": {
115  "Sequence": 0,
116  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
117  "Amount": "1000000000",
118  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
119  "TransactionType": "Payment"
120  }
121 })",
122  {{"",
123  "",
124  "Missing field 'tx_json.Fee'.",
125  "Missing field 'tx_json.SigningPubKey'."}}},
126 
127  {"Pass in Sequence and Fee with minimal payment.",
128  __LINE__,
129  R"({
130  "command": "doesnt_matter",
131  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
132  "secret": "masterpassphrase",
133  "tx_json": {
134  "Sequence": 0,
135  "Fee": 10,
136  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
137  "Amount": "1000000000",
138  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
139  "TransactionType": "Payment"
140  }
141 })",
142  {{"",
143  "",
144  "A Signer may not be the transaction's Account "
145  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
146  "Missing field 'tx_json.SigningPubKey'."}}},
147 
148  {"Add 'fee_mult_max' field.",
149  __LINE__,
150  R"({
151  "command": "doesnt_matter",
152  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
153  "secret": "masterpassphrase",
154  "fee_mult_max": 7,
155  "tx_json": {
156  "Sequence": 0,
157  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
158  "Amount": "1000000000",
159  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
160  "TransactionType": "Payment"
161  }
162 })",
163  {{"",
164  "",
165  "Missing field 'tx_json.Fee'.",
166  "Missing field 'tx_json.SigningPubKey'."}}},
167 
168  {"Add 'fee_mult_max' and 'fee_div_max' field.",
169  __LINE__,
170  R"({
171  "command": "doesnt_matter",
172  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
173  "secret": "masterpassphrase",
174  "fee_mult_max": 7,
175  "fee_div_max": 4,
176  "tx_json": {
177  "Sequence": 0,
178  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
179  "Amount": "1000000000",
180  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
181  "TransactionType": "Payment"
182  }
183 })",
184  {{"",
185  "",
186  "Missing field 'tx_json.Fee'.",
187  "Missing field 'tx_json.SigningPubKey'."}}},
188 
189  {"fee_mult_max is ignored if 'Fee' is present.",
190  __LINE__,
191  R"({
192  "command": "doesnt_matter",
193  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
194  "secret": "masterpassphrase",
195  "fee_mult_max": 0,
196  "tx_json": {
197  "Sequence": 0,
198  "Fee": 10,
199  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
200  "Amount": "1000000000",
201  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
202  "TransactionType": "Payment"
203  }
204 })",
205  {{"",
206  "",
207  "A Signer may not be the transaction's Account "
208  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
209  "Missing field 'tx_json.SigningPubKey'."}}},
210 
211  {"fee_div_max is ignored if 'Fee' is present.",
212  __LINE__,
213  R"({
214  "command": "doesnt_matter",
215  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
216  "secret": "masterpassphrase",
217  "fee_mult_max": 100,
218  "fee_div_max": 1000,
219  "tx_json": {
220  "Sequence": 0,
221  "Fee": 10,
222  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
223  "Amount": "1000000000",
224  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
225  "TransactionType": "Payment"
226  }
227 })",
228  {{"",
229  "",
230  "A Signer may not be the transaction's Account "
231  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
232  "Missing field 'tx_json.SigningPubKey'."}}},
233 
234  {"Invalid 'fee_mult_max' field.",
235  __LINE__,
236  R"({
237  "command": "doesnt_matter",
238  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
239  "secret": "masterpassphrase",
240  "fee_mult_max": "NotAFeeMultiplier",
241  "tx_json": {
242  "Sequence": 0,
243  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
244  "Amount": "1000000000",
245  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
246  "TransactionType": "Payment"
247  }
248 })",
249  {{"Invalid field 'fee_mult_max', not a positive integer.",
250  "Invalid field 'fee_mult_max', not a positive integer.",
251  "Missing field 'tx_json.Fee'.",
252  "Missing field 'tx_json.SigningPubKey'."}}},
253 
254  {"Invalid 'fee_div_max' field.",
255  __LINE__,
256  R"({
257  "command": "doesnt_matter",
258  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
259  "secret": "masterpassphrase",
260  "fee_mult_max": 5,
261  "fee_div_max": "NotAFeeMultiplier",
262  "tx_json": {
263  "Sequence": 0,
264  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
265  "Amount": "1000000000",
266  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
267  "TransactionType": "Payment"
268  }
269 })",
270  {{"Invalid field 'fee_div_max', not a positive integer.",
271  "Invalid field 'fee_div_max', not a positive integer.",
272  "Missing field 'tx_json.Fee'.",
273  "Missing field 'tx_json.SigningPubKey'."}}},
274 
275  {"Invalid value for 'fee_mult_max' field.",
276  __LINE__,
277  R"({
278  "command": "doesnt_matter",
279  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
280  "secret": "masterpassphrase",
281  "fee_mult_max": 0,
282  "tx_json": {
283  "Sequence": 0,
284  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
285  "Amount": "1000000000",
286  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
287  "TransactionType": "Payment"
288  }
289 })",
290  {{"Fee of 10 exceeds the requested tx limit of 0",
291  "Fee of 10 exceeds the requested tx limit of 0",
292  "Missing field 'tx_json.Fee'.",
293  "Missing field 'tx_json.SigningPubKey'."}}},
294 
295  {"Invalid value for 'fee_div_max' field.",
296  __LINE__,
297  R"({
298  "command": "doesnt_matter",
299  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
300  "secret": "masterpassphrase",
301  "fee_mult_max": 4,
302  "fee_div_max": 7,
303  "tx_json": {
304  "Sequence": 0,
305  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
306  "Amount": "1000000000",
307  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
308  "TransactionType": "Payment"
309  }
310 })",
311  {{"Fee of 10 exceeds the requested tx limit of 5",
312  "Fee of 10 exceeds the requested tx limit of 5",
313  "Missing field 'tx_json.Fee'.",
314  "Missing field 'tx_json.SigningPubKey'."}}},
315 
316  {"Invalid zero value for 'fee_div_max' field.",
317  __LINE__,
318  R"({
319  "command": "doesnt_matter",
320  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
321  "secret": "masterpassphrase",
322  "fee_mult_max": 4,
323  "fee_div_max": 0,
324  "tx_json": {
325  "Sequence": 0,
326  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
327  "Amount": "1000000000",
328  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
329  "TransactionType": "Payment"
330  }
331 })",
332  {{"Invalid field 'fee_div_max', not a positive integer.",
333  "Invalid field 'fee_div_max', not a positive integer.",
334  "Missing field 'tx_json.Fee'.",
335  "Missing field 'tx_json.SigningPubKey'."}}},
336 
337  {"Missing 'Amount'.",
338  __LINE__,
339  R"({
340  "command": "doesnt_matter",
341  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
342  "secret": "masterpassphrase",
343  "tx_json": {
344  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
345  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
346  "TransactionType": "Payment"
347  }
348 })",
349  {{"Missing field 'tx_json.Amount'.",
350  "Missing field 'tx_json.Amount'.",
351  "Missing field 'tx_json.Sequence'.",
352  "Missing field 'tx_json.Sequence'."}}},
353 
354  {"Invalid 'Amount'.",
355  __LINE__,
356  R"({
357  "command": "doesnt_matter",
358  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
359  "secret": "masterpassphrase",
360  "tx_json": {
361  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
362  "Amount": "NotAnAmount",
363  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
364  "TransactionType": "Payment"
365  }
366 })",
367  {{"Invalid field 'tx_json.Amount'.",
368  "Invalid field 'tx_json.Amount'.",
369  "Missing field 'tx_json.Sequence'.",
370  "Missing field 'tx_json.Sequence'."}}},
371 
372  {"Missing 'Destination'.",
373  __LINE__,
374  R"({
375  "command": "doesnt_matter",
376  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
377  "secret": "masterpassphrase",
378  "tx_json": {
379  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
380  "Amount": "1000000000",
381  "TransactionType": "Payment"
382  }
383 })",
384  {{"Missing field 'tx_json.Destination'.",
385  "Missing field 'tx_json.Destination'.",
386  "Missing field 'tx_json.Sequence'.",
387  "Missing field 'tx_json.Sequence'."}}},
388 
389  {"Invalid 'Destination'.",
390  __LINE__,
391  R"({
392  "command": "doesnt_matter",
393  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
394  "secret": "masterpassphrase",
395  "tx_json": {
396  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
397  "Amount": "1000000000",
398  "Destination": "NotADestination",
399  "TransactionType": "Payment"
400  }
401 })",
402  {{"Invalid field 'tx_json.Destination'.",
403  "Invalid field 'tx_json.Destination'.",
404  "Missing field 'tx_json.Sequence'.",
405  "Missing field 'tx_json.Sequence'."}}},
406 
407  {"Cannot create XRP to XRP paths.",
408  __LINE__,
409  R"({
410  "command": "doesnt_matter",
411  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
412  "secret": "masterpassphrase",
413  "build_path": 1,
414  "tx_json": {
415  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
416  "Amount": "1000000000",
417  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
418  "TransactionType": "Payment"
419  }
420 })",
421  {{"Cannot build XRP to XRP paths.",
422  "Cannot build XRP to XRP paths.",
423  "Missing field 'tx_json.Sequence'.",
424  "Missing field 'tx_json.Sequence'."}}},
425 
426  {"Successful 'build_path'.",
427  __LINE__,
428  R"({
429  "command": "doesnt_matter",
430  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
431  "secret": "masterpassphrase",
432  "build_path": 1,
433  "tx_json": {
434  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
435  "Amount": {
436  "value": "10",
437  "currency": "USD",
438  "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
439  },
440  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
441  "TransactionType": "Payment"
442  }
443 })",
444  {{"",
445  "",
446  "Missing field 'tx_json.Sequence'.",
447  "Missing field 'tx_json.Sequence'."}}},
448 
449  {"Not valid to include both 'Paths' and 'build_path'.",
450  __LINE__,
451  R"({
452  "command": "doesnt_matter",
453  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
454  "secret": "masterpassphrase",
455  "build_path": 1,
456  "tx_json": {
457  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
458  "Amount": {
459  "value": "10",
460  "currency": "USD",
461  "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
462  },
463  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
464  "Paths": "",
465  "TransactionType": "Payment"
466  }
467 })",
468  {{"Cannot specify both 'tx_json.Paths' and 'build_path'",
469  "Cannot specify both 'tx_json.Paths' and 'build_path'",
470  "Missing field 'tx_json.Sequence'.",
471  "Missing field 'tx_json.Sequence'."}}},
472 
473  {"Successful 'SendMax'.",
474  __LINE__,
475  R"({
476  "command": "doesnt_matter",
477  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
478  "secret": "masterpassphrase",
479  "build_path": 1,
480  "tx_json": {
481  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
482  "Amount": {
483  "value": "10",
484  "currency": "USD",
485  "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
486  },
487  "SendMax": {
488  "value": "5",
489  "currency": "USD",
490  "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
491  },
492  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
493  "TransactionType": "Payment"
494  }
495 })",
496  {{"",
497  "",
498  "Missing field 'tx_json.Sequence'.",
499  "Missing field 'tx_json.Sequence'."}}},
500 
501  {"'Amount' may not be XRP for pathfinding, but 'SendMax' may be XRP.",
502  __LINE__,
503  R"({
504  "command": "doesnt_matter",
505  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
506  "secret": "masterpassphrase",
507  "build_path": 1,
508  "tx_json": {
509  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
510  "Amount": {
511  "value": "10",
512  "currency": "USD",
513  "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
514  },
515  "SendMax": 10000,
516  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
517  "TransactionType": "Payment"
518  }
519 })",
520  {{"",
521  "",
522  "Missing field 'tx_json.Sequence'.",
523  "Missing field 'tx_json.Sequence'."}}},
524 
525  {"'secret' must be present.",
526  __LINE__,
527  R"({
528  "command": "doesnt_matter",
529  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
530  "tx_json": {
531  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
532  "Amount": "1000000000",
533  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
534  "TransactionType": "Payment"
535  }
536 })",
537  {{"Missing field 'secret'.",
538  "Missing field 'secret'.",
539  "Missing field 'tx_json.Sequence'.",
540  "Missing field 'tx_json.Sequence'."}}},
541 
542  {"'secret' must be non-empty.",
543  __LINE__,
544  R"({
545  "command": "doesnt_matter",
546  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
547  "secret": "",
548  "tx_json": {
549  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
550  "Amount": "1000000000",
551  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
552  "TransactionType": "Payment"
553  }
554 })",
555  {{"Invalid field 'secret'.",
556  "Invalid field 'secret'.",
557  "Missing field 'tx_json.Sequence'.",
558  "Missing field 'tx_json.Sequence'."}}},
559 
560  {"Use 'seed' instead of 'secret'.",
561  __LINE__,
562  R"({
563  "command": "doesnt_matter",
564  "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
565  "key_type": "ed25519",
566  "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd",
567  "tx_json": {
568  "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
569  "Amount": "1000000000",
570  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
571  "TransactionType": "Payment"
572  }
573 })",
574  {{"",
575  "",
576  "Missing field 'tx_json.Sequence'.",
577  "Missing field 'tx_json.Sequence'."}}},
578 
579  {"Malformed 'seed'.",
580  __LINE__,
581  R"({
582  "command": "doesnt_matter",
583  "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
584  "key_type": "ed25519",
585  "seed": "not a seed",
586  "tx_json": {
587  "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
588  "Amount": "1000000000",
589  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
590  "TransactionType": "Payment"
591  }
592 })",
593  {{"Disallowed seed.",
594  "Disallowed seed.",
595  "Missing field 'tx_json.Sequence'.",
596  "Missing field 'tx_json.Sequence'."}}},
597 
598  {"'tx_json' must be present.",
599  __LINE__,
600  R"({
601  "command": "doesnt_matter",
602  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
603  "secret": "masterpassphrase",
604  "rx_json": {
605  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
606  "Amount": "1000000000",
607  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
608  "TransactionType": "Payment"
609  }
610 })",
611  {{"Missing field 'tx_json'.",
612  "Missing field 'tx_json'.",
613  "Missing field 'tx_json'.",
614  "Missing field 'tx_json'."}}},
615 
616  {"'TransactionType' must be present.",
617  __LINE__,
618  R"({
619  "command": "doesnt_matter",
620  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
621  "secret": "masterpassphrase",
622  "tx_json": {
623  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
624  "Amount": "1000000000",
625  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
626  }
627 })",
628  {{"Missing field 'tx_json.TransactionType'.",
629  "Missing field 'tx_json.TransactionType'.",
630  "Missing field 'tx_json.Sequence'.",
631  "Missing field 'tx_json.Sequence'."}}},
632 
633  {"The 'TransactionType' must be a pre-established transaction type.",
634  __LINE__,
635  R"({
636  "command": "doesnt_matter",
637  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
638  "secret": "masterpassphrase",
639  "tx_json": {
640  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
641  "Amount": "1000000000",
642  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
643  "TransactionType": "tt"
644  }
645 })",
646  {{"Field 'tx_json.TransactionType' has invalid data.",
647  "Field 'tx_json.TransactionType' has invalid data.",
648  "Missing field 'tx_json.Sequence'.",
649  "Missing field 'tx_json.Sequence'."}}},
650 
651  {"The 'TransactionType' may be represented with an integer.",
652  __LINE__,
653  R"({
654  "command": "doesnt_matter",
655  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
656  "secret": "masterpassphrase",
657  "tx_json": {
658  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
659  "Amount": "1000000000",
660  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
661  "TransactionType": 0
662  }
663 })",
664  {{"",
665  "",
666  "Missing field 'tx_json.Sequence'.",
667  "Missing field 'tx_json.Sequence'."}}},
668 
669  {"'Account' must be present.",
670  __LINE__,
671  R"({
672  "command": "doesnt_matter",
673  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
674  "secret": "masterpassphrase",
675  "tx_json": {
676  "Amount": "1000000000",
677  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
678  "TransactionType": "Payment"
679  }
680 })",
681  {{"Missing field 'tx_json.Account'.",
682  "Missing field 'tx_json.Account'.",
683  "Missing field 'tx_json.Sequence'.",
684  "Missing field 'tx_json.Sequence'."}}},
685 
686  {"'Account' must be well formed.",
687  __LINE__,
688  R"({
689  "command": "doesnt_matter",
690  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
691  "secret": "masterpassphrase",
692  "tx_json": {
693  "Account": "NotAnAccount",
694  "Amount": "1000000000",
695  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
696  "TransactionType": "Payment"
697  }
698 })",
699  {{"Invalid field 'tx_json.Account'.",
700  "Invalid field 'tx_json.Account'.",
701  "Missing field 'tx_json.Sequence'.",
702  "Missing field 'tx_json.Sequence'."}}},
703 
704  {"The 'offline' tag may be added to the transaction.",
705  __LINE__,
706  R"({
707  "command": "doesnt_matter",
708  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
709  "secret": "masterpassphrase",
710  "offline": 0,
711  "tx_json": {
712  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
713  "Amount": "1000000000",
714  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
715  "TransactionType": "Payment"
716  }
717 })",
718  {{"",
719  "",
720  "Missing field 'tx_json.Sequence'.",
721  "Missing field 'tx_json.Sequence'."}}},
722 
723  {"If 'offline' is true then a 'Sequence' field must be supplied.",
724  __LINE__,
725  R"({
726  "command": "doesnt_matter",
727  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
728  "secret": "masterpassphrase",
729  "offline": 1,
730  "tx_json": {
731  "Fee": 10,
732  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
733  "Amount": "1000000000",
734  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
735  "TransactionType": "Payment"
736  }
737 })",
738  {{"Missing field 'tx_json.Sequence'.",
739  "Missing field 'tx_json.Sequence'.",
740  "Missing field 'tx_json.Sequence'.",
741  "Missing field 'tx_json.Sequence'."}}},
742 
743  {"If 'offline' is true then a 'Fee' field must be supplied.",
744  __LINE__,
745  R"({
746  "command": "doesnt_matter",
747  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
748  "secret": "masterpassphrase",
749  "offline": 1,
750  "tx_json": {
751  "Sequence": 0,
752  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
753  "Amount": "1000000000",
754  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
755  "TransactionType": "Payment"
756  }
757 })",
758  {{"Missing field 'tx_json.Fee'.",
759  "Missing field 'tx_json.Fee'.",
760  "Missing field 'tx_json.Fee'.",
761  "Missing field 'tx_json.SigningPubKey'."}}},
762 
763  {"Valid transaction if 'offline' is true.",
764  __LINE__,
765  R"({
766  "command": "doesnt_matter",
767  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
768  "secret": "masterpassphrase",
769  "offline": 1,
770  "tx_json": {
771  "Sequence": 0,
772  "Fee": 10,
773  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
774  "Amount": "1000000000",
775  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
776  "TransactionType": "Payment"
777  }
778 })",
779  {{"",
780  "",
781  "A Signer may not be the transaction's Account "
782  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
783  "Missing field 'tx_json.SigningPubKey'."}}},
784 
785  {"'offline' and 'build_path' are mutually exclusive.",
786  __LINE__,
787  R"({
788  "command": "doesnt_matter",
789  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
790  "secret": "masterpassphrase",
791  "offline": 1,
792  "build_path": 1,
793  "tx_json": {
794  "Sequence": 0,
795  "Fee": 10,
796  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
797  "Amount": "1000000000",
798  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
799  "TransactionType": "Payment"
800  }
801 })",
802  {{"Field 'build_path' not allowed in this context.",
803  "Field 'build_path' not allowed in this context.",
804  "Field 'build_path' not allowed in this context.",
805  "Missing field 'tx_json.SigningPubKey'."}}},
806 
807  {"A 'Flags' field may be specified.",
808  __LINE__,
809  R"({
810  "command": "doesnt_matter",
811  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
812  "secret": "masterpassphrase",
813  "tx_json": {
814  "Flags": 0,
815  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
816  "Amount": "1000000000",
817  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
818  "TransactionType": "Payment"
819  }
820 })",
821  {{"",
822  "",
823  "Missing field 'tx_json.Sequence'.",
824  "Missing field 'tx_json.Sequence'."}}},
825 
826  {"The 'Flags' field must be numeric.",
827  __LINE__,
828  R"({
829  "command": "doesnt_matter",
830  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
831  "secret": "masterpassphrase",
832  "tx_json": {
833  "Flags": "NotGoodFlags",
834  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
835  "Amount": "1000000000",
836  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
837  "TransactionType": "Payment"
838  }
839 })",
840  {{"Field 'tx_json.Flags' has invalid data.",
841  "Field 'tx_json.Flags' has invalid data.",
842  "Missing field 'tx_json.Sequence'.",
843  "Missing field 'tx_json.Sequence'."}}},
844 
845  {"It's okay to add a 'debug_signing' field.",
846  __LINE__,
847  R"({
848  "command": "doesnt_matter",
849  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
850  "secret": "masterpassphrase",
851  "debug_signing": 0,
852  "tx_json": {
853  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
854  "Amount": "1000000000",
855  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
856  "TransactionType": "Payment"
857  }
858 })",
859  {{"",
860  "",
861  "Missing field 'tx_json.Sequence'.",
862  "Missing field 'tx_json.Sequence'."}}},
863 
864  {"Single-sign a multisigned transaction.",
865  __LINE__,
866  R"({
867  "command": "doesnt_matter",
868  "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
869  "secret": "a",
870  "tx_json": {
871  "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
872  "Amount" : "1000000000",
873  "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
874  "Fee" : "50",
875  "Sequence" : 0,
876  "Signers" : [
877  {
878  "Signer" : {
879  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
880  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
881  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
882  }
883  }
884  ],
885  "SigningPubKey" : "",
886  "TransactionType" : "Payment"
887  }
888 })",
889  {{"Already multisigned.",
890  "Already multisigned.",
891  "Secret does not match account.",
892  ""}}},
893 
894  {"Minimal sign_for.",
895  __LINE__,
896  R"({
897  "command": "doesnt_matter",
898  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
899  "secret": "masterpassphrase",
900  "tx_json": {
901  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
902  "Amount": "1000000000",
903  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
904  "Fee": 50,
905  "Sequence": 0,
906  "SigningPubKey": "",
907  "TransactionType": "Payment"
908  }
909 })",
910  {{"Secret does not match account.",
911  "Secret does not match account.",
912  "",
913  "Missing field 'tx_json.Signers'."}}},
914 
915  {"Minimal offline sign_for.",
916  __LINE__,
917  R"({
918  "command": "doesnt_matter",
919  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
920  "secret": "masterpassphrase",
921  "offline": 1,
922  "tx_json": {
923  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
924  "Amount": "1000000000",
925  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
926  "Fee": 50,
927  "Sequence": 0,
928  "SigningPubKey": "",
929  "TransactionType": "Payment"
930  }
931 })",
932  {{"", "", "", "Missing field 'tx_json.Signers'."}}},
933 
934  {"Offline sign_for using 'seed' instead of 'secret'.",
935  __LINE__,
936  R"({
937  "command": "doesnt_matter",
938  "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
939  "key_type": "ed25519",
940  "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd",
941  "offline": 1,
942  "tx_json": {
943  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
944  "Amount": "1000000000",
945  "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
946  "Fee": 50,
947  "Sequence": 0,
948  "SigningPubKey": "",
949  "TransactionType": "Payment"
950  }
951 })",
952  {{"", "", "", "Missing field 'tx_json.Signers'."}}},
953 
954  {"Malformed seed in sign_for.",
955  __LINE__,
956  R"({
957  "command": "doesnt_matter",
958  "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
959  "key_type": "ed25519",
960  "seed": "sh1yJfwoi98zCygwjUzuHmJDeVKd",
961  "offline": 1,
962  "tx_json": {
963  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
964  "Amount": "1000000000",
965  "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
966  "Fee": 50,
967  "Sequence": 0,
968  "SigningPubKey": "",
969  "TransactionType": "Payment"
970  }
971 })",
972  {{"Disallowed seed.",
973  "Disallowed seed.",
974  "Disallowed seed.",
975  "Missing field 'tx_json.Signers'."}}},
976 
977  {"Missing 'Account' in sign_for.",
978  __LINE__,
979  R"({
980  "command": "doesnt_matter",
981  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
982  "secret": "masterpassphrase",
983  "tx_json": {
984  "Amount": "1000000000",
985  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
986  "Fee": 50,
987  "Sequence": 0,
988  "SigningPubKey": "",
989  "TransactionType": "Payment"
990  }
991 })",
992  {{"Missing field 'tx_json.Account'.",
993  "Missing field 'tx_json.Account'.",
994  "Missing field 'tx_json.Account'.",
995  "Missing field 'tx_json.Account'."}}},
996 
997  {"Missing 'Amount' in sign_for.",
998  __LINE__,
999  R"({
1000  "command": "doesnt_matter",
1001  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1002  "secret": "masterpassphrase",
1003  "tx_json": {
1004  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1005  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1006  "Fee": 50,
1007  "Sequence": 0,
1008  "SigningPubKey": "",
1009  "TransactionType": "Payment"
1010  }
1011 })",
1012  {{"Missing field 'tx_json.Amount'.",
1013  "Missing field 'tx_json.Amount'.",
1014  "Missing field 'tx_json.Amount'.",
1015  "Missing field 'tx_json.Amount'."}}},
1016 
1017  {"Missing 'Destination' in sign_for.",
1018  __LINE__,
1019  R"({
1020  "command": "doesnt_matter",
1021  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1022  "secret": "masterpassphrase",
1023  "tx_json": {
1024  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1025  "Amount": "1000000000",
1026  "Fee": 50,
1027  "Sequence": 0,
1028  "SigningPubKey": "",
1029  "TransactionType": "Payment"
1030  }
1031 })",
1032  {{"Missing field 'tx_json.Destination'.",
1033  "Missing field 'tx_json.Destination'.",
1034  "Missing field 'tx_json.Destination'.",
1035  "Missing field 'tx_json.Destination'."}}},
1036 
1037  {"Missing 'Fee' in sign_for.",
1038  __LINE__,
1039  R"({
1040  "command": "doesnt_matter",
1041  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1042  "secret": "masterpassphrase",
1043  "tx_json": {
1044  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1045  "Amount": "1000000000",
1046  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1047  "Sequence": 0,
1048  "SigningPubKey": "",
1049  "TransactionType": "Payment"
1050  }
1051 })",
1052  {{"Secret does not match account.",
1053  "Secret does not match account.",
1054  "Missing field 'tx_json.Fee'.",
1055  "Missing field 'tx_json.Fee'."}}},
1056 
1057  {"Missing 'Sequence' in sign_for.",
1058  __LINE__,
1059  R"({
1060  "command": "doesnt_matter",
1061  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1062  "secret": "masterpassphrase",
1063  "tx_json": {
1064  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1065  "Amount": "1000000000",
1066  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1067  "Fee": 50,
1068  "SigningPubKey": "",
1069  "TransactionType": "Payment"
1070  }
1071 })",
1072  {{"Secret does not match account.",
1073  "Secret does not match account.",
1074  "Missing field 'tx_json.Sequence'.",
1075  "Missing field 'tx_json.Sequence'."}}},
1076 
1077  {"Missing 'SigningPubKey' in sign_for is automatically filled in.",
1078  __LINE__,
1079  R"({
1080  "command": "doesnt_matter",
1081  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1082  "secret": "masterpassphrase",
1083  "tx_json": {
1084  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1085  "Amount": "1000000000",
1086  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1087  "Fee": 50,
1088  "Sequence": 0,
1089  "TransactionType": "Payment"
1090  }
1091 })",
1092  {{"Secret does not match account.",
1093  "Secret does not match account.",
1094  "",
1095  "Missing field 'tx_json.SigningPubKey'."}}},
1096 
1097  {"In sign_for, an account may not sign for itself.",
1098  __LINE__,
1099  R"({
1100  "command": "doesnt_matter",
1101  "account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1102  "secret": "a",
1103  "tx_json": {
1104  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1105  "Amount": "1000000000",
1106  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1107  "Fee": 50,
1108  "Sequence": 0,
1109  "TransactionType": "Payment"
1110  }
1111 })",
1112  {{"",
1113  "",
1114  "A Signer may not be the transaction's Account "
1115  "(rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA).",
1116  "Missing field 'tx_json.SigningPubKey'."}}},
1117 
1118  {"Cannot put duplicate accounts in Signers array",
1119  __LINE__,
1120  R"({
1121  "command": "doesnt_matter",
1122  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1123  "secret": "masterpassphrase",
1124  "tx_json": {
1125  "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1126  "Amount" : "1000000000",
1127  "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1128  "Fee" : "50",
1129  "Sequence" : 0,
1130  "Signers" : [
1131  {
1132  "Signer" : {
1133  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1134  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1135  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1136  }
1137  }
1138  ],
1139  "SigningPubKey" : "",
1140  "TransactionType" : "Payment"
1141  }
1142 })",
1143  {{"Already multisigned.",
1144  "Already multisigned.",
1145  "Duplicate Signers:Signer:Account entries "
1146  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh) are not allowed.",
1147  ""}}},
1148 
1149  {"Correctly append to pre-established Signers array",
1150  __LINE__,
1151  R"({
1152  "command": "doesnt_matter",
1153  "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1154  "secret": "c",
1155  "tx_json": {
1156  "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1157  "Amount" : "1000000000",
1158  "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1159  "Fee" : "50",
1160  "Sequence" : 0,
1161  "Signers" : [
1162  {
1163  "Signer" : {
1164  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1165  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1166  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1167  }
1168  }
1169  ],
1170  "SigningPubKey" : "",
1171  "TransactionType" : "Payment"
1172  }
1173 })",
1174  {{"Already multisigned.", "Already multisigned.", "", ""}}},
1175 
1176  {"Append to pre-established Signers array with bad signature",
1177  __LINE__,
1178  R"({
1179  "command": "doesnt_matter",
1180  "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1181  "secret": "c",
1182  "tx_json": {
1183  "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1184  "Amount" : "1000000000",
1185  "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1186  "Fee" : "50",
1187  "Sequence" : 0,
1188  "Signers" : [
1189  {
1190  "Signer" : {
1191  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1192  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1193  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ACB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1194  }
1195  }
1196  ],
1197  "SigningPubKey" : "",
1198  "TransactionType" : "Payment"
1199  }
1200 })",
1201  {{"Already multisigned.",
1202  "Already multisigned.",
1203  "Invalid signature.",
1204  "Invalid signature."}}},
1205 
1206  {"Non-empty 'SigningPubKey' in sign_for.",
1207  __LINE__,
1208  R"({
1209  "command": "doesnt_matter",
1210  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1211  "secret": "masterpassphrase",
1212  "tx_json": {
1213  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1214  "Amount": "1000000000",
1215  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1216  "Fee": 50,
1217  "Sequence": 0,
1218  "SigningPubKey": "1",
1219  "TransactionType": "Payment"
1220  }
1221 })",
1222  {{"Secret does not match account.",
1223  "Secret does not match account.",
1224  "When multi-signing 'tx_json.SigningPubKey' must be empty.",
1225  "When multi-signing 'tx_json.SigningPubKey' must be empty."}}},
1226 
1227  {"Missing 'TransactionType' in sign_for.",
1228  __LINE__,
1229  R"({
1230  "command": "doesnt_matter",
1231  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1232  "secret": "masterpassphrase",
1233  "tx_json": {
1234  "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1235  "Amount": "1000000000",
1236  "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1237  "Fee": 50,
1238  "Sequence": 0,
1239  "SigningPubKey": "",
1240  }
1241 })",
1242  {{"Missing field 'tx_json.TransactionType'.",
1243  "Missing field 'tx_json.TransactionType'.",
1244  "Missing field 'tx_json.TransactionType'.",
1245  "Missing field 'tx_json.TransactionType'."}}},
1246 
1247  {"TxnSignature in sign_for.",
1248  __LINE__,
1249  R"({
1250  "command": "doesnt_matter",
1251  "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1252  "secret": "c",
1253  "tx_json": {
1254  "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1255  "Amount" : "1000000000",
1256  "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1257  "Fee" : "50",
1258  "Sequence" : 0,
1259  "Signers" : [
1260  {
1261  "Signer" : {
1262  "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1263  "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1264  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1265  }
1266  }
1267  ],
1268  "SigningPubKey" : "",
1269  "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998",
1270  "TransactionType" : "Payment"
1271  }
1272 })",
1273  {{"Already multisigned.",
1274  "Already multisigned.",
1275  "Already single-signed.",
1276  "Signing of transaction is malformed."}}},
1277 
1278  {"Invalid field 'tx_json': string instead of object",
1279  __LINE__,
1280  R"({
1281  "command": "doesnt_matter",
1282  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1283  "secret": "masterpassphrase",
1284  "tx_json": ""
1285 })",
1286  {{"Invalid field 'tx_json', not object.",
1287  "Invalid field 'tx_json', not object.",
1288  "Invalid field 'tx_json', not object.",
1289  "Invalid field 'tx_json', not object."}}},
1290 
1291  {"Invalid field 'tx_json': integer instead of object",
1292  __LINE__,
1293  R"({
1294  "command": "doesnt_matter",
1295  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1296  "secret": "masterpassphrase",
1297  "tx_json": 20160331
1298 })",
1299  {{"Invalid field 'tx_json', not object.",
1300  "Invalid field 'tx_json', not object.",
1301  "Invalid field 'tx_json', not object.",
1302  "Invalid field 'tx_json', not object."}}},
1303 
1304  {"Invalid field 'tx_json': array instead of object",
1305  __LINE__,
1306  R"({
1307  "command": "doesnt_matter",
1308  "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1309  "secret": "masterpassphrase",
1310  "tx_json": [ "hello", "world" ]
1311 })",
1312  {{"Invalid field 'tx_json', not object.",
1313  "Invalid field 'tx_json', not object.",
1314  "Invalid field 'tx_json', not object.",
1315  "Invalid field 'tx_json', not object."}}},
1316 
1317  {"Minimal submit_multisigned.",
1318  __LINE__,
1319  R"({
1320  "command": "submit_multisigned",
1321  "tx_json": {
1322  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1323  "Amount": "1000000000",
1324  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1325  "Fee": 50,
1326  "Sequence": 0,
1327  "Signers" : [
1328  {
1329  "Signer" : {
1330  "Account" : "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1331  "SigningPubKey" : "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8",
1332  "TxnSignature" : "3045022100909D01399AFFAD1E30D250CE61F93975B7F61E47B5244D78C3E86D9806535D95022012E389E0ACB016334052B7FE07FA6CEFDC8BE82CB410FA841D5049641C89DC8F"
1333  }
1334  }
1335  ],
1336  "SigningPubKey": "",
1337  "TransactionType": "Payment"
1338  }
1339 })",
1340  {{"Missing field 'secret'.",
1341  "Missing field 'secret'.",
1342  "Missing field 'account'.",
1343  ""}}},
1344 
1345  {"Minimal submit_multisigned with bad signature.",
1346  __LINE__,
1347  R"({
1348  "command": "submit_multisigned",
1349  "tx_json": {
1350  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1351  "Amount": "1000000000",
1352  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1353  "Fee": 50,
1354  "Sequence": 0,
1355  "Signers": [
1356  {
1357  "Signer": {
1358  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1359  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1360  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1361  }
1362  }
1363  ],
1364  "SigningPubKey": "",
1365  "TransactionType": "Payment"
1366  }
1367 })",
1368  {{"Missing field 'secret'.",
1369  "Missing field 'secret'.",
1370  "Missing field 'account'.",
1371  "Invalid signature."}}},
1372 
1373  {"Missing tx_json in submit_multisigned.",
1374  __LINE__,
1375  R"({
1376  "command": "submit_multisigned",
1377  "Signers": [
1378  {
1379  "Signer": {
1380  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1381  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1382  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1383  }
1384  }
1385  ]
1386 })",
1387  {{"Missing field 'secret'.",
1388  "Missing field 'secret'.",
1389  "Missing field 'account'.",
1390  "Missing field 'tx_json'."}}},
1391 
1392  {"Missing sequence in submit_multisigned.",
1393  __LINE__,
1394  R"({
1395  "command": "submit_multisigned",
1396  "tx_json": {
1397  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1398  "Amount": "1000000000",
1399  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1400  "Fee": 50,
1401  "Signers": [
1402  {
1403  "Signer": {
1404  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1405  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1406  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1407  }
1408  }
1409  ],
1410  "SigningPubKey": "",
1411  "TransactionType": "Payment"
1412  }
1413 })",
1414  {{"Missing field 'secret'.",
1415  "Missing field 'secret'.",
1416  "Missing field 'account'.",
1417  "Missing field 'tx_json.Sequence'."}}},
1418 
1419  {"Missing SigningPubKey in submit_multisigned.",
1420  __LINE__,
1421  R"({
1422  "command": "submit_multisigned",
1423  "tx_json": {
1424  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1425  "Amount": "1000000000",
1426  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1427  "Fee": 50,
1428  "Signers": [
1429  {
1430  "Signer": {
1431  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1432  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1433  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1434  }
1435  }
1436  ],
1437  "Sequence": 0,
1438  "TransactionType": "Payment"
1439  }
1440 })",
1441  {{"Missing field 'secret'.",
1442  "Missing field 'secret'.",
1443  "Missing field 'account'.",
1444  "Missing field 'tx_json.SigningPubKey'."}}},
1445 
1446  {"Non-empty SigningPubKey in submit_multisigned.",
1447  __LINE__,
1448  R"({
1449  "command": "submit_multisigned",
1450  "tx_json": {
1451  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1452  "Amount": "1000000000",
1453  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1454  "Fee": 50,
1455  "Sequence": 0,
1456  "Signers": [
1457  {
1458  "Signer": {
1459  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1460  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1461  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1462  }
1463  }
1464  ],
1465  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8",
1466  "TransactionType": "Payment"
1467  }
1468 })",
1469  {{"Missing field 'secret'.",
1470  "Missing field 'secret'.",
1471  "Missing field 'account'.",
1472  "When multi-signing 'tx_json.SigningPubKey' must be empty."}}},
1473 
1474  {"Missing TransactionType in submit_multisigned.",
1475  __LINE__,
1476  R"({
1477  "command": "submit_multisigned",
1478  "tx_json": {
1479  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1480  "Amount": "1000000000",
1481  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1482  "Fee": 50,
1483  "Signers": [
1484  {
1485  "Signer": {
1486  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1487  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1488  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1489  }
1490  }
1491  ],
1492  "Sequence": 0,
1493  "SigningPubKey": "",
1494  }
1495 })",
1496  {{"Missing field 'secret'.",
1497  "Missing field 'secret'.",
1498  "Missing field 'account'.",
1499  "Missing field 'tx_json.TransactionType'."}}},
1500 
1501  {"Missing Account in submit_multisigned.",
1502  __LINE__,
1503  R"({
1504  "command": "submit_multisigned",
1505  "tx_json": {
1506  "Amount": "1000000000",
1507  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1508  "Fee": 50,
1509  "Sequence": 0,
1510  "Signers": [
1511  {
1512  "Signer": {
1513  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1514  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1515  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1516  }
1517  }
1518  ],
1519  "SigningPubKey": "",
1520  "TransactionType": "Payment"
1521  }
1522 })",
1523  {{"Missing field 'secret'.",
1524  "Missing field 'secret'.",
1525  "Missing field 'account'.",
1526  "Missing field 'tx_json.Account'."}}},
1527 
1528  {"Malformed Account in submit_multisigned.",
1529  __LINE__,
1530  R"({
1531  "command": "submit_multisigned",
1532  "tx_json": {
1533  "Account": "NotAnAccount",
1534  "Amount": "1000000000",
1535  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1536  "Fee": 50,
1537  "Sequence": 0,
1538  "Signers": [
1539  {
1540  "Signer": {
1541  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1542  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1543  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1544  }
1545  }
1546  ],
1547  "SigningPubKey": "",
1548  "TransactionType": "Payment"
1549  }
1550 })",
1551  {{"Missing field 'secret'.",
1552  "Missing field 'secret'.",
1553  "Missing field 'account'.",
1554  "Invalid field 'tx_json.Account'."}}},
1555 
1556  {"Account not in ledger in submit_multisigned.",
1557  __LINE__,
1558  R"({
1559  "command": "submit_multisigned",
1560  "tx_json": {
1561  "Account": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
1562  "Amount": "1000000000",
1563  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1564  "Fee": 50,
1565  "Sequence": 0,
1566  "Signers": [
1567  {
1568  "Signer": {
1569  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1570  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1571  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1572  }
1573  }
1574  ],
1575  "SigningPubKey": "",
1576  "TransactionType": "Payment"
1577  }
1578 })",
1579  {{"Missing field 'secret'.",
1580  "Missing field 'secret'.",
1581  "Missing field 'account'.",
1582  "Source account not found."}}},
1583 
1584  {"Missing Fee in submit_multisigned.",
1585  __LINE__,
1586  R"({
1587  "command": "submit_multisigned",
1588  "tx_json": {
1589  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1590  "Amount": "1000000000",
1591  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1592  "Sequence": 0,
1593  "Signers": [
1594  {
1595  "Signer": {
1596  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1597  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1598  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1599  }
1600  }
1601  ],
1602  "SigningPubKey": "",
1603  "TransactionType": "Payment"
1604  }
1605 })",
1606  {{"Missing field 'secret'.",
1607  "Missing field 'secret'.",
1608  "Missing field 'account'.",
1609  "Missing field 'tx_json.Fee'."}}},
1610 
1611  {"Non-numeric Fee in submit_multisigned.",
1612  __LINE__,
1613  R"({
1614  "command": "submit_multisigned",
1615  "tx_json": {
1616  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1617  "Amount": "1000000000",
1618  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1619  "Fee": 50.1,
1620  "Sequence": 0,
1621  "Signers": [
1622  {
1623  "Signer": {
1624  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1625  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1626  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1627  }
1628  }
1629  ],
1630  "SigningPubKey": "",
1631  "TransactionType": "Payment"
1632  }
1633 })",
1634  {{"Missing field 'secret'.",
1635  "Missing field 'secret'.",
1636  "Missing field 'account'.",
1637  "Field 'tx_json.Fee' has invalid data."}}},
1638 
1639  {"Missing Amount in submit_multisigned Payment.",
1640  __LINE__,
1641  R"({
1642  "command": "submit_multisigned",
1643  "tx_json": {
1644  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1645  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1646  "Fee": 50000000,
1647  "Sequence": 0,
1648  "Signers": [
1649  {
1650  "Signer": {
1651  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1652  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1653  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1654  }
1655  }
1656  ],
1657  "SigningPubKey": "",
1658  "TransactionType": "Payment"
1659  }
1660 })",
1661  {{"Missing field 'secret'.",
1662  "Missing field 'secret'.",
1663  "Missing field 'account'.",
1664  "Missing field 'tx_json.Amount'."}}},
1665 
1666  {"Invalid Amount in submit_multisigned Payment.",
1667  __LINE__,
1668  R"({
1669  "command": "submit_multisigned",
1670  "tx_json": {
1671  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1672  "Amount": "NotANumber",
1673  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1674  "Fee": 50,
1675  "Sequence": 0,
1676  "Signers": [
1677  {
1678  "Signer": {
1679  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1680  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1681  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1682  }
1683  }
1684  ],
1685  "SigningPubKey": "",
1686  "TransactionType": "Payment"
1687  }
1688 })",
1689  {{"Missing field 'secret'.",
1690  "Missing field 'secret'.",
1691  "Missing field 'account'.",
1692  "Invalid field 'tx_json.Amount'."}}},
1693 
1694  {"No build_path in submit_multisigned.",
1695  __LINE__,
1696  R"({
1697  "command": "submit_multisigned",
1698  "build_path": 1,
1699  "tx_json": {
1700  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1701  "Amount": "1000000000",
1702  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1703  "Fee": 50,
1704  "Sequence": 0,
1705  "Signers": [
1706  {
1707  "Signer": {
1708  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1709  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1710  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1711  }
1712  }
1713  ],
1714  "SigningPubKey": "",
1715  "TransactionType": "Payment"
1716  }
1717 })",
1718  {{"Missing field 'secret'.",
1719  "Missing field 'secret'.",
1720  "Missing field 'account'.",
1721  "Field 'build_path' not allowed in this context."}}},
1722 
1723  {"Missing Destination in submit_multisigned Payment.",
1724  __LINE__,
1725  R"({
1726  "command": "submit_multisigned",
1727  "tx_json": {
1728  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1729  "Amount": "1000000000",
1730  "Fee": 50,
1731  "Sequence": 0,
1732  "Signers": [
1733  {
1734  "Signer": {
1735  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1736  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1737  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1738  }
1739  }
1740  ],
1741  "SigningPubKey": "",
1742  "TransactionType": "Payment"
1743  }
1744 })",
1745  {{"Missing field 'secret'.",
1746  "Missing field 'secret'.",
1747  "Missing field 'account'.",
1748  "Missing field 'tx_json.Destination'."}}},
1749 
1750  {"Malformed Destination in submit_multisigned Payment.",
1751  __LINE__,
1752  R"({
1753  "command": "submit_multisigned",
1754  "tx_json": {
1755  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1756  "Amount": "1000000000",
1757  "Destination": "NotADestination",
1758  "Fee": 50,
1759  "Sequence": 0,
1760  "Signers": [
1761  {
1762  "Signer": {
1763  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1764  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1765  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1766  }
1767  }
1768  ],
1769  "SigningPubKey": "",
1770  "TransactionType": "Payment"
1771  }
1772 })",
1773  {{"Missing field 'secret'.",
1774  "Missing field 'secret'.",
1775  "Missing field 'account'.",
1776  "Invalid field 'tx_json.Destination'."}}},
1777 
1778  {"Missing Signers field in submit_multisigned.",
1779  __LINE__,
1780  R"({
1781  "command": "submit_multisigned",
1782  "tx_json": {
1783  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1784  "Amount": "1000000000",
1785  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1786  "Fee": 50,
1787  "Sequence": 0,
1788  "SigningPubKey": "",
1789  "TransactionType": "Payment"
1790  }
1791 })",
1792  {{"Missing field 'secret'.",
1793  "Missing field 'secret'.",
1794  "Missing field 'account'.",
1795  "Missing field 'tx_json.Signers'."}}},
1796 
1797  {"Signers not an array in submit_multisigned.",
1798  __LINE__,
1799  R"({
1800  "command": "submit_multisigned",
1801  "tx_json": {
1802  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1803  "Amount": "1000000000",
1804  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1805  "Fee": 50,
1806  "Sequence": 0,
1807  "Signers": {
1808  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1809  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1810  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1811  },
1812  "SigningPubKey": "",
1813  "TransactionType": "Payment"
1814  }
1815 })",
1816  {{"Missing field 'secret'.",
1817  "Missing field 'secret'.",
1818  "Missing field 'account'.",
1819  "Field 'tx_json.Signers' is not a JSON array."}}},
1820 
1821  {"Empty Signers array in submit_multisigned.",
1822  __LINE__,
1823  R"({
1824  "command": "submit_multisigned",
1825  "tx_json": {
1826  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1827  "Amount": "1000000000",
1828  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1829  "Fee": 50,
1830  "Sequence": 0,
1831  "Signers": [
1832  ],
1833  "SigningPubKey": "",
1834  "TransactionType": "Payment"
1835  }
1836 })",
1837  {{"Missing field 'secret'.",
1838  "Missing field 'secret'.",
1839  "Missing field 'account'.",
1840  "tx_json.Signers array may not be empty."}}},
1841 
1842  {"Duplicate Signer in submit_multisigned.",
1843  __LINE__,
1844  R"({
1845  "command": "submit_multisigned",
1846  "tx_json": {
1847  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1848  "Amount": "1000000000",
1849  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1850  "Fee": 50,
1851  "Sequence": 0,
1852  "Signers": [
1853  {
1854  "Signer": {
1855  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1856  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1857  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1858  }
1859  },
1860  {
1861  "Signer": {
1862  "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1863  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1864  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1865  }
1866  }
1867  ],
1868  "SigningPubKey": "",
1869  "TransactionType": "Payment"
1870  }
1871 })",
1872  {{"Missing field 'secret'.",
1873  "Missing field 'secret'.",
1874  "Missing field 'account'.",
1875  "Duplicate Signers:Signer:Account entries "
1876  "(rPcNzota6B8YBokhYtcTNqQVCngtbnWfux) are not allowed."}}},
1877 
1878  {"Signer is tx_json Account in submit_multisigned.",
1879  __LINE__,
1880  R"({
1881  "command": "submit_multisigned",
1882  "tx_json": {
1883  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1884  "Amount": "1000000000",
1885  "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1886  "Fee": 50,
1887  "Sequence": 0,
1888  "Signers": [
1889  {
1890  "Signer": {
1891  "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1892  "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1893  "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1894  }
1895  }
1896  ],
1897  "SigningPubKey": "",
1898  "TransactionType": "Payment"
1899  }
1900 })",
1901  {{"Missing field 'secret'.",
1902  "Missing field 'secret'.",
1903  "Missing field 'account'.",
1904  "A Signer may not be the transaction's Account "
1905  "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh)."}}},
1906 
1907 };
1908 
1909 class JSONRPC_test : public beast::unit_test::suite
1910 {
1911 public:
1912  void
1914  {
1915  test::jtx::Env env(*this);
1916  Json::Value const result{
1917  env.rpc("bad_command", R"({"MakingThisUp": 0})")};
1918 
1919  BEAST_EXPECT(result[jss::result][jss::error] == "unknownCmd");
1920  BEAST_EXPECT(
1921  result[jss::result][jss::request][jss::command] == "bad_command");
1922  }
1923 
1924  void
1926  {
1927  test::jtx::Env env(*this);
1928  auto ledger = env.current();
1929  auto const& feeTrack = env.app().getFeeTrack();
1930 
1931  {
1932  Json::Value req;
1933  Json::Reader().parse(
1934  "{ \"fee_mult_max\" : 1, \"tx_json\" : { } } ", req);
1935  Json::Value result = checkFee(
1936  req,
1937  Role::ADMIN,
1938  true,
1939  env.app().config(),
1940  feeTrack,
1941  env.app().getTxQ(),
1942  env.app());
1943 
1944  BEAST_EXPECT(!RPC::contains_error(result));
1945  BEAST_EXPECT(
1946  req[jss::tx_json].isMember(jss::Fee) &&
1947  req[jss::tx_json][jss::Fee] == 10);
1948  }
1949 
1950  {
1951  Json::Value req;
1952  Json::Reader().parse(
1953  "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 2, "
1954  "\"tx_json\" : { } } ",
1955  req);
1956  Json::Value result = checkFee(
1957  req,
1958  Role::ADMIN,
1959  true,
1960  env.app().config(),
1961  feeTrack,
1962  env.app().getTxQ(),
1963  env.app());
1964 
1965  BEAST_EXPECT(!RPC::contains_error(result));
1966  BEAST_EXPECT(
1967  req[jss::tx_json].isMember(jss::Fee) &&
1968  req[jss::tx_json][jss::Fee] == 10);
1969  }
1970 
1971  {
1972  Json::Value req;
1973  Json::Reader().parse(
1974  "{ \"fee_mult_max\" : 0, \"tx_json\" : { } } ", req);
1975  Json::Value result = checkFee(
1976  req,
1977  Role::ADMIN,
1978  true,
1979  env.app().config(),
1980  feeTrack,
1981  env.app().getTxQ(),
1982  env.app());
1983 
1984  BEAST_EXPECT(RPC::contains_error(result));
1985  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
1986  }
1987 
1988  {
1989  // 3/6 = 1/2, but use the bigger number make sure
1990  // we're dividing.
1991  Json::Value req;
1992  Json::Reader().parse(
1993  "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 6, "
1994  "\"tx_json\" : { } } ",
1995  req);
1996  Json::Value result = checkFee(
1997  req,
1998  Role::ADMIN,
1999  true,
2000  env.app().config(),
2001  feeTrack,
2002  env.app().getTxQ(),
2003  env.app());
2004 
2005  BEAST_EXPECT(RPC::contains_error(result));
2006  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2007  }
2008 
2009  {
2010  Json::Value req;
2011  Json::Reader().parse(
2012  "{ \"fee_mult_max\" : 0, \"fee_div_max\" : 2, "
2013  "\"tx_json\" : { } } ",
2014  req);
2015  Json::Value result = checkFee(
2016  req,
2017  Role::ADMIN,
2018  true,
2019  env.app().config(),
2020  feeTrack,
2021  env.app().getTxQ(),
2022  env.app());
2023 
2024  BEAST_EXPECT(RPC::contains_error(result));
2025  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2026  }
2027 
2028  {
2029  Json::Value req;
2030  Json::Reader().parse(
2031  "{ \"fee_mult_max\" : 10, \"fee_div_max\" : 0, "
2032  "\"tx_json\" : { } } ",
2033  req);
2034  Json::Value result = checkFee(
2035  req,
2036  Role::ADMIN,
2037  true,
2038  env.app().config(),
2039  feeTrack,
2040  env.app().getTxQ(),
2041  env.app());
2042 
2043  BEAST_EXPECT(RPC::contains_error(result));
2044  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2045  }
2046  }
2047 
2048  void
2050  {
2051  using namespace test::jtx;
2052  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
2053  cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue");
2054  cfg->section("transaction_queue")
2055  .set("minimum_txn_in_ledger_standalone", "3");
2056  return cfg;
2057  })};
2058  LoadFeeTrack const& feeTrackOuter = env.app().getFeeTrack();
2059 
2060  {
2061  // high mult, no tx
2062  Json::Value req;
2063  Json::Reader().parse(
2064  R"({
2065  "fee_mult_max" : 1000,
2066  "tx_json" : { }
2067  })",
2068  req);
2069  Json::Value result = checkFee(
2070  req,
2071  Role::ADMIN,
2072  true,
2073  env.app().config(),
2074  feeTrackOuter,
2075  env.app().getTxQ(),
2076  env.app());
2077 
2078  BEAST_EXPECT(!RPC::contains_error(result));
2079  BEAST_EXPECT(
2080  req[jss::tx_json].isMember(jss::Fee) &&
2081  req[jss::tx_json][jss::Fee] == 10);
2082  }
2083 
2084  {
2085  // low mult, no tx
2086  Json::Value req;
2087  Json::Reader().parse(
2088  R"({
2089  "fee_mult_max" : 5,
2090  "tx_json" : { }
2091  })",
2092  req);
2093  Json::Value result = checkFee(
2094  req,
2095  Role::ADMIN,
2096  true,
2097  env.app().config(),
2098  feeTrackOuter,
2099  env.app().getTxQ(),
2100  env.app());
2101 
2102  BEAST_EXPECT(!RPC::contains_error(result));
2103  BEAST_EXPECT(
2104  req[jss::tx_json].isMember(jss::Fee) &&
2105  req[jss::tx_json][jss::Fee] == 10);
2106  }
2107 
2108  // put 4 transactions into the open ledger
2109  for (auto i = 0; i < 4; ++i)
2110  {
2111  env(noop(env.master));
2112  }
2113 
2114  {
2115  // high mult, 4 txs
2116  Json::Value req;
2117  Json::Reader().parse(
2118  R"({
2119  "fee_mult_max" : 1000,
2120  "tx_json" : { }
2121  })",
2122  req);
2123  Json::Value result = checkFee(
2124  req,
2125  Role::ADMIN,
2126  true,
2127  env.app().config(),
2128  feeTrackOuter,
2129  env.app().getTxQ(),
2130  env.app());
2131 
2132  BEAST_EXPECT(!RPC::contains_error(result));
2133  BEAST_EXPECT(
2134  req[jss::tx_json].isMember(jss::Fee) &&
2135  req[jss::tx_json][jss::Fee] == 8889);
2136  }
2137 
2138  {
2139  // low mult, 4 tx
2140  Json::Value req;
2141  Json::Reader().parse(
2142  R"({
2143  "fee_mult_max" : 5,
2144  "tx_json" : { }
2145  })",
2146  req);
2147  Json::Value result = checkFee(
2148  req,
2149  Role::ADMIN,
2150  true,
2151  env.app().config(),
2152  feeTrackOuter,
2153  env.app().getTxQ(),
2154  env.app());
2155 
2156  BEAST_EXPECT(RPC::contains_error(result));
2157  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2158  }
2159 
2160  {
2161  // different low mult, 4 tx
2162  Json::Value req;
2163  Json::Reader().parse(
2164  R"({
2165  "fee_mult_max" : 1000,
2166  "fee_div_max" : 3,
2167  "tx_json" : { }
2168  })",
2169  req);
2170  Json::Value result = checkFee(
2171  req,
2172  Role::ADMIN,
2173  true,
2174  env.app().config(),
2175  feeTrackOuter,
2176  env.app().getTxQ(),
2177  env.app());
2178 
2179  BEAST_EXPECT(RPC::contains_error(result));
2180  BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2181  }
2182 
2183  {
2184  // high mult, 4 tx
2185  Json::Value req;
2186  Json::Reader().parse(
2187  R"({
2188  "fee_mult_max" : 8000,
2189  "fee_div_max" : 3,
2190  "tx_json" : { }
2191  })",
2192  req);
2193  Json::Value result = checkFee(
2194  req,
2195  Role::ADMIN,
2196  true,
2197  env.app().config(),
2198  feeTrackOuter,
2199  env.app().getTxQ(),
2200  env.app());
2201 
2202  BEAST_EXPECT(!RPC::contains_error(result));
2203  BEAST_EXPECT(
2204  req[jss::tx_json].isMember(jss::Fee) &&
2205  req[jss::tx_json][jss::Fee] == 8889);
2206  }
2207 
2208  {
2209  // negative mult
2210  Json::Value req;
2211  Json::Reader().parse(
2212  R"({
2213  "fee_mult_max" : -5,
2214  "tx_json" : { }
2215  })",
2216  req);
2217  Json::Value result = checkFee(
2218  req,
2219  Role::ADMIN,
2220  true,
2221  env.app().config(),
2222  feeTrackOuter,
2223  env.app().getTxQ(),
2224  env.app());
2225 
2226  BEAST_EXPECT(RPC::contains_error(result));
2227  }
2228 
2229  {
2230  // negative div
2231  Json::Value req;
2232  Json::Reader().parse(
2233  R"({
2234  "fee_div_max" : -2,
2235  "tx_json" : { }
2236  })",
2237  req);
2238  Json::Value result = checkFee(
2239  req,
2240  Role::ADMIN,
2241  true,
2242  env.app().config(),
2243  feeTrackOuter,
2244  env.app().getTxQ(),
2245  env.app());
2246 
2247  BEAST_EXPECT(RPC::contains_error(result));
2248  }
2249 
2250  {
2251  // negative mult & div
2252  Json::Value req;
2253  Json::Reader().parse(
2254  R"({
2255  "fee_mult_max" : -2,
2256  "fee_div_max" : -3,
2257  "tx_json" : { }
2258  })",
2259  req);
2260  Json::Value result = checkFee(
2261  req,
2262  Role::ADMIN,
2263  true,
2264  env.app().config(),
2265  feeTrackOuter,
2266  env.app().getTxQ(),
2267  env.app());
2268 
2269  BEAST_EXPECT(RPC::contains_error(result));
2270  }
2271 
2272  env.close();
2273 
2274  {
2275  // Call "sign" with nothing in the open ledger
2276  Json::Value toSign;
2277  toSign[jss::tx_json] = noop(env.master);
2278  toSign[jss::secret] = "masterpassphrase";
2279  auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2280  auto result = rpcResult[jss::result];
2281 
2282  BEAST_EXPECT(!RPC::contains_error(result));
2283  BEAST_EXPECT(
2284  result[jss::tx_json].isMember(jss::Fee) &&
2285  result[jss::tx_json][jss::Fee] == "10");
2286  BEAST_EXPECT(
2287  result[jss::tx_json].isMember(jss::Sequence) &&
2288  result[jss::tx_json][jss::Sequence].isConvertibleTo(
2289  Json::ValueType::uintValue));
2290  }
2291 
2292  {
2293  // Call "sign" with enough transactions in the open ledger
2294  // to escalate the fee.
2295  for (;;)
2296  {
2297  auto metrics = env.app().getTxQ().getMetrics(*env.current());
2298  if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2299  break;
2300  env(noop(env.master));
2301  }
2302 
2303  Json::Value toSign;
2304  toSign[jss::tx_json] = noop(env.master);
2305  toSign[jss::secret] = "masterpassphrase";
2306  toSign[jss::fee_mult_max] = 900;
2307  auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2308  auto result = rpcResult[jss::result];
2309 
2310  BEAST_EXPECT(!RPC::contains_error(result));
2311  BEAST_EXPECT(
2312  result[jss::tx_json].isMember(jss::Fee) &&
2313  result[jss::tx_json][jss::Fee] == "7813");
2314  BEAST_EXPECT(
2315  result[jss::tx_json].isMember(jss::Sequence) &&
2316  result[jss::tx_json][jss::Sequence].isConvertibleTo(
2317  Json::ValueType::uintValue));
2318 
2319  env.close();
2320  }
2321 
2322  {
2323  // Call "sign" with higher server load
2324  {
2325  auto& feeTrack = env.app().getFeeTrack();
2326  BEAST_EXPECT(feeTrack.getLoadFactor() == 256);
2327  for (int i = 0; i < 8; ++i)
2328  feeTrack.raiseLocalFee();
2329  BEAST_EXPECT(feeTrack.getLoadFactor() == 1220);
2330  }
2331 
2332  Json::Value toSign;
2333  toSign[jss::tx_json] = noop(env.master);
2334  toSign[jss::secret] = "masterpassphrase";
2335  auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2336  auto result = rpcResult[jss::result];
2337 
2338  BEAST_EXPECT(!RPC::contains_error(result));
2339  BEAST_EXPECT(
2340  result[jss::tx_json].isMember(jss::Fee) &&
2341  result[jss::tx_json][jss::Fee] == "47");
2342  BEAST_EXPECT(
2343  result[jss::tx_json].isMember(jss::Sequence) &&
2344  result[jss::tx_json][jss::Sequence].isConvertibleTo(
2345  Json::ValueType::uintValue));
2346  }
2347 
2348  {
2349  // Call "sign" with higher server load and
2350  // enough transactions to escalate the fee
2351  BEAST_EXPECT(feeTrackOuter.getLoadFactor() == 1220);
2352 
2353  for (;;)
2354  {
2355  auto metrics = env.app().getTxQ().getMetrics(*env.current());
2356  if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2357  break;
2358  env(noop(env.master), fee(47));
2359  }
2360 
2361  Env_ss envs(env);
2362 
2363  Json::Value toSign;
2364  toSign[jss::tx_json] = noop(env.master);
2365  toSign[jss::secret] = "masterpassphrase";
2366  // Max fee = 7000 drops
2367  toSign[jss::fee_mult_max] = 700;
2368  auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2369  auto result = rpcResult[jss::result];
2370 
2371  BEAST_EXPECT(!RPC::contains_error(result));
2372  BEAST_EXPECT(
2373  result[jss::tx_json].isMember(jss::Fee) &&
2374  result[jss::tx_json][jss::Fee] == "6806");
2375  BEAST_EXPECT(
2376  result[jss::tx_json].isMember(jss::Sequence) &&
2377  result[jss::tx_json][jss::Sequence].isConvertibleTo(
2378  Json::ValueType::uintValue));
2379  }
2380  }
2381 
2382  // A function that can be called as though it would process a transaction.
2383  static void
2386  bool,
2387  bool,
2389  {
2390  ;
2391  }
2392 
2393  void
2395  {
2396  using namespace std::chrono_literals;
2397  // Use jtx to set up a ledger so the tests will do the right thing.
2398  test::jtx::Account const a{"a"}; // rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA
2399  test::jtx::Account const g{"g"}; // rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4
2400  auto const USD = g["USD"];
2401 
2402  // Account: rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi
2403  // seed: sh1yJfwoi98zCygwijUzuHmJDeVKd
2404  test::jtx::Account const ed{"ed", KeyType::ed25519};
2405  // master is rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh.
2406  // "b" (not in the ledger) is rDg53Haik2475DJx8bjMDSDPj4VX7htaMd.
2407  // "c" (phantom signer) is rPcNzota6B8YBokhYtcTNqQVCngtbnWfux.
2408 
2409  test::jtx::Env env(*this);
2410  env.fund(test::jtx::XRP(100000), a, ed, g);
2411  env.close();
2412 
2413  env(trust(a, USD(1000)));
2414  env(trust(env.master, USD(1000)));
2415  env(pay(g, a, USD(50)));
2416  env(pay(g, env.master, USD(50)));
2417  env.close();
2418 
2420 
2421  // A list of all the functions we want to test.
2422  using signFunc = Json::Value (*)(
2423  Json::Value params,
2424  NetworkOPs::FailHard failType,
2425  Role role,
2426  std::chrono::seconds validatedLedgerAge,
2427  Application & app);
2428 
2429  using submitFunc = Json::Value (*)(
2430  Json::Value params,
2431  NetworkOPs::FailHard failType,
2432  Role role,
2433  std::chrono::seconds validatedLedgerAge,
2434  Application & app,
2435  ProcessTransactionFn const& processTransaction);
2436 
2437  using TestStuff =
2439 
2440  static TestStuff const testFuncs[] = {
2441  TestStuff{transactionSign, nullptr, "sign", 0},
2442  TestStuff{nullptr, transactionSubmit, "submit", 1},
2443  TestStuff{transactionSignFor, nullptr, "sign_for", 2},
2444  TestStuff{
2445  nullptr,
2447  "submit_multisigned",
2448  3}};
2449 
2450  for (auto testFunc : testFuncs)
2451  {
2452  // For each JSON test.
2453  for (auto const& txnTest : txnTestArray)
2454  {
2455  Json::Value req;
2456  Json::Reader().parse(txnTest.json, req);
2457  if (RPC::contains_error(req))
2458  Throw<std::runtime_error>(
2459  "Internal JSONRPC_test error. Bad test JSON.");
2460 
2461  static Role const testedRoles[] = {
2463 
2464  for (Role testRole : testedRoles)
2465  {
2466  Json::Value result;
2467  auto const signFn = get<0>(testFunc);
2468  if (signFn != nullptr)
2469  {
2470  assert(get<1>(testFunc) == nullptr);
2471  result = signFn(
2472  req,
2474  testRole,
2475  1s,
2476  env.app());
2477  }
2478  else
2479  {
2480  auto const submitFn = get<1>(testFunc);
2481  assert(submitFn != nullptr);
2482  result = submitFn(
2483  req,
2485  testRole,
2486  1s,
2487  env.app(),
2488  processTxn);
2489  }
2490 
2491  std::string errStr;
2492  if (RPC::contains_error(result))
2493  errStr = result["error_message"].asString();
2494 
2495  if (errStr == txnTest.expMsg[get<3>(testFunc)])
2496  {
2497  pass();
2498  }
2499  else
2500  {
2501  std::ostringstream description;
2502  description << txnTest.description << " Called "
2503  << get<2>(testFunc) << "(). Got \'"
2504  << errStr << "\'";
2505  fail(description.str(), __FILE__, txnTest.line);
2506  }
2507  }
2508  }
2509  }
2510  }
2511 
2512  void
2513  run() override
2514  {
2516  testAutoFillFees();
2519  }
2520 };
2521 
2522 BEAST_DEFINE_TESTSUITE(JSONRPC, ripple_app, ripple);
2523 
2524 } // namespace RPC
2525 } // namespace ripple
ripple::RPC::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountLinesRPC, app, ripple)
ripple::RPC::ProcessTransactionFn
std::function< void(std::shared_ptr< Transaction > &transaction, bool bUnlimited, bool bLocal, NetworkOPs::FailHard failType)> ProcessTransactionFn
Definition: TransactionSign.h:79
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
std::string
STL class.
std::shared_ptr< Transaction >
ripple::RPC::transactionSignFor
Json::Value transactionSignFor(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
Definition: TransactionSign.cpp:928
ripple::RPC::TxnTestData::description
char const *const description
Definition: JSONRPC_test.cpp:38
ripple::test::jtx::trust
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:30
std::chrono::seconds
ripple::RPC::JSONRPC_test::testTransactionRPC
void testTransactionRPC()
Definition: JSONRPC_test.cpp:2364
std::tuple
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::RPC::transactionSign
Json::Value transactionSign(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
Definition: TransactionSign.cpp:762
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::RPC::JSONRPC_test::testAutoFillFees
void testAutoFillFees()
Definition: JSONRPC_test.cpp:1925
ripple::KeyType::ed25519
@ ed25519
ripple::RPC::checkFee
Json::Value checkFee(Json::Value &request, Role const role, bool doAutoFill, Config const &config, LoadFeeTrack const &feeTrack, TxQ const &txQ, Application const &app)
Fill in the fee on behalf of the client.
Definition: TransactionSign.cpp:666
ripple::RPC::transactionSubmit
Json::Value transactionSubmit(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
Definition: TransactionSign.cpp:799
ripple::Role::ADMIN
@ ADMIN
ripple::RPC::contains_error
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:194
ripple::Role::USER
@ USER
ripple::Application::config
virtual Config & config()=0
ripple::RPC::TxnTestData::TxnTestData
TxnTestData()=delete
ripple::RPC::JSONRPC_test::run
void run() override
Definition: JSONRPC_test.cpp:2483
ripple::Application::getTxQ
virtual TxQ & getTxQ()=0
ripple::LoadFeeTrack
Manages the current fee schedule.
Definition: LoadFeeTrack.h:44
ripple::RPC::TxnTestData::TxnTestData
constexpr TxnTestData(char const *description_, int line_, char const *json_, std::array< char const *const, 4 > const &expMsg_)
Definition: JSONRPC_test.cpp:52
ripple::RPC::TxnTestData::line
const int line
Definition: JSONRPC_test.cpp:39
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
std::array
STL class.
ripple::Role::FORBID
@ FORBID
ripple::RPC::TxnTestData::expMsg
const std::array< char const *const, 4 > expMsg
Definition: JSONRPC_test.cpp:50
ripple::NetworkOPs::FailHard
FailHard
Definition: NetworkOPs.h:91
ripple::RPC::JSONRPC_test::fakeProcessTransaction
static void fakeProcessTransaction(std::shared_ptr< Transaction > &, bool, bool, NetworkOPs::FailHard)
Definition: JSONRPC_test.cpp:2354
std::ostringstream
STL class.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Role::GUEST
@ GUEST
ripple::LoadFeeTrack::getLoadFactor
std::uint32_t getLoadFactor() const
Definition: LoadFeeTrack.h:95
ripple::test::jtx::pay
Json::Value pay(Account const &account, Account const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
ripple::RPC::TxnTestData::json
char const *const json
Definition: JSONRPC_test.cpp:40
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
ripple::RPC::TxnTestData
Definition: JSONRPC_test.cpp:36
ripple::RPC::txnTestArray
static constexpr TxnTestData txnTestArray[]
Definition: JSONRPC_test.cpp:70
std::ostringstream::str
T str(T... args)
ripple::NetworkOPs::FailHard::yes
@ yes
ripple::RPC::JSONRPC_test
Definition: JSONRPC_test.cpp:1909
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::RPC::TxnTestData::operator=
TxnTestData & operator=(TxnTestData const &)=delete
ripple::RPC::JSONRPC_test::testAutoFillEscalatedFees
void testAutoFillEscalatedFees()
Definition: JSONRPC_test.cpp:2049
std::unique_ptr
STL class.
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:300
ripple::Role
Role
Indicates the level of administrative permission to grant.
Definition: Role.h:43
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:687
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::RPC::JSONRPC_test::testBadRpcCommand
void testBadRpcCommand()
Definition: JSONRPC_test.cpp:1913
ripple::RPC::transactionSubmitMultiSigned
Json::Value transactionSubmitMultiSigned(Json::Value jvRequest, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
Definition: TransactionSign.cpp:1035
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469