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