rippled
Loading...
Searching...
No Matches
MultiSign_test.cpp
1#include <test/jtx.h>
2
3#include <xrpld/core/ConfigSections.h>
4
5#include <xrpl/protocol/Feature.h>
6#include <xrpl/protocol/jss.h>
7
8namespace xrpl {
9namespace test {
10
12{
13 // Unfunded accounts to use for phantom signing.
46
47public:
48 void
50 {
51 testcase("No Reserve");
52
53 using namespace jtx;
54 Env env{*this, features};
55 Account const alice{"alice", KeyType::secp256k1};
56
57 // Pay alice enough to meet the initial reserve, but not enough to
58 // meet the reserve for a SignerListSet.
59 auto const fee = env.current()->fees().base;
60 env.fund(XRP(250) - drops(1), alice);
61 env.close();
62 env.require(owners(alice, 0));
63
64 {
65 // Attach a signer list to alice. Should fail.
66 Json::Value const signersList = signers(alice, 1, {{bogie, 1}});
67 env(signersList, ter(tecINSUFFICIENT_RESERVE));
68 env.close();
69 env.require(owners(alice, 0));
70
71 // Fund alice enough to set the signer list, then attach signers.
72 env(pay(env.master, alice, fee + drops(1)));
73 env.close();
74 env(signersList);
75 env.close();
76 env.require(owners(alice, 1));
77 }
78 {
79 // Pay alice enough to almost make the reserve for the biggest
80 // possible list.
81 env(pay(env.master, alice, fee - drops(1)));
82
83 // Replace with the biggest possible signer list. Should fail.
84 Json::Value const bigSigners = signers(
85 alice,
86 1,
87 {{bogie, 1},
88 {demon, 1},
89 {ghost, 1},
90 {haunt, 1},
91 {jinni, 1},
92 {phase, 1},
93 {shade, 1},
94 {spook, 1}});
95 env(bigSigners, ter(tecINSUFFICIENT_RESERVE));
96 env.close();
97 env.require(owners(alice, 1));
98
99 // Fund alice one more drop (plus the fee) and succeed.
100 env(pay(env.master, alice, fee + drops(1)));
101 env.close();
102 env(bigSigners);
103 env.close();
104 env.require(owners(alice, 1));
105 }
106 // Remove alice's signer list and get the owner count back.
107 env(signers(alice, jtx::none));
108 env.close();
109 env.require(owners(alice, 0));
110 }
111
112 void
114 {
115 testcase("SignerListSet");
116
117 using namespace jtx;
118 Env env{*this, features};
119 Account const alice{"alice", KeyType::ed25519};
120 env.fund(XRP(1000), alice);
121 env.close();
122
123 // Add alice as a multisigner for herself. Should fail.
124 env(signers(alice, 1, {{alice, 1}}), ter(temBAD_SIGNER));
125
126 // Add a signer with a weight of zero. Should fail.
127 env(signers(alice, 1, {{bogie, 0}}), ter(temBAD_WEIGHT));
128
129 // Add a signer where the weight is too big. Should fail since
130 // the weight field is only 16 bits. The jtx framework can't do
131 // this kind of test, so it's commented out.
132 // env(signers(alice, 1, { { bogie, 0x10000} }), ter
133 // (temBAD_WEIGHT));
134
135 // Add the same signer twice. Should fail.
136 env(signers(
137 alice,
138 1,
139 {{bogie, 1},
140 {demon, 1},
141 {ghost, 1},
142 {haunt, 1},
143 {jinni, 1},
144 {phase, 1},
145 {demon, 1},
146 {spook, 1}}),
148
149 // Set a quorum of zero. Should fail.
150 env(signers(alice, 0, {{bogie, 1}}), ter(temMALFORMED));
151
152 // Make a signer list where the quorum can't be met. Should fail.
153 env(signers(
154 alice,
155 9,
156 {{bogie, 1},
157 {demon, 1},
158 {ghost, 1},
159 {haunt, 1},
160 {jinni, 1},
161 {phase, 1},
162 {shade, 1},
163 {spook, 1}}),
165
166 // Make a signer list that's too big. Should fail.
167 Account const spare("spare", KeyType::secp256k1);
168 env(signers(
169 alice,
170 1,
172 {bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1}, {jinni, 1}, {phase, 1},
173 {shade, 1}, {spook, 1}, {spare, 1}, {acc10, 1}, {acc11, 1}, {acc12, 1},
174 {acc13, 1}, {acc14, 1}, {acc15, 1}, {acc16, 1}, {acc17, 1}, {acc18, 1},
175 {acc19, 1}, {acc20, 1}, {acc21, 1}, {acc22, 1}, {acc23, 1}, {acc24, 1},
176 {acc25, 1}, {acc26, 1}, {acc27, 1}, {acc28, 1}, {acc29, 1}, {acc30, 1},
177 {acc31, 1}, {acc32, 1}, {acc33, 1},
178 }),
180 env.close();
181 env.require(owners(alice, 0));
182 }
183
184 void
186 {
187 testcase("Phantom Signers");
188
189 using namespace jtx;
190 Env env{*this, features};
191 Account const alice{"alice", KeyType::ed25519};
192 env.fund(XRP(1000), alice);
193 env.close();
194
195 // Attach phantom signers to alice and use them for a transaction.
196 env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
197 env.close();
198 env.require(owners(alice, 1));
199
200 // This should work.
201 auto const baseFee = env.current()->fees().base;
202 std::uint32_t aliceSeq = env.seq(alice);
203 env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
204 env.close();
205 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
206
207 // Either signer alone should work.
208 aliceSeq = env.seq(alice);
209 env(noop(alice), msig(bogie), fee(2 * baseFee));
210 env.close();
211 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
212
213 aliceSeq = env.seq(alice);
214 env(noop(alice), msig(demon), fee(2 * baseFee));
215 env.close();
216 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
217
218 // Duplicate signers should fail.
219 aliceSeq = env.seq(alice);
220 env(noop(alice),
221 msig(demon, demon),
222 fee(3 * baseFee),
223 rpc("invalidTransaction", "fails local checks: Duplicate Signers not allowed."));
224 env.close();
225 BEAST_EXPECT(env.seq(alice) == aliceSeq);
226
227 // A non-signer should fail.
228 aliceSeq = env.seq(alice);
229 env(noop(alice), msig(bogie, spook), fee(3 * baseFee), ter(tefBAD_SIGNATURE));
230 env.close();
231 BEAST_EXPECT(env.seq(alice) == aliceSeq);
232
233 // Don't meet the quorum. Should fail.
234 env(signers(alice, 2, {{bogie, 1}, {demon, 1}}));
235 aliceSeq = env.seq(alice);
236 env(noop(alice), msig(bogie), fee(2 * baseFee), ter(tefBAD_QUORUM));
237 env.close();
238 BEAST_EXPECT(env.seq(alice) == aliceSeq);
239
240 // Meet the quorum. Should succeed.
241 aliceSeq = env.seq(alice);
242 env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
243 env.close();
244 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
245 }
246
247 void
249 {
250 testcase("Fee");
251
252 using namespace jtx;
253 Env env{*this, features};
254 Account const alice{"alice", KeyType::ed25519};
255 env.fund(XRP(1000), alice);
256 env.close();
257
258 // Attach maximum possible number of signers to alice.
259 env(signers(
260 alice,
261 1,
262 {{bogie, 1},
263 {demon, 1},
264 {ghost, 1},
265 {haunt, 1},
266 {jinni, 1},
267 {phase, 1},
268 {shade, 1},
269 {spook, 1}}));
270 env.close();
271 env.require(owners(alice, 1));
272
273 // This should work.
274 auto const baseFee = env.current()->fees().base;
275 std::uint32_t aliceSeq = env.seq(alice);
276 env(noop(alice), msig(bogie), fee(2 * baseFee));
277 env.close();
278
279 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
280
281 // This should fail because the fee is too small.
282 aliceSeq = env.seq(alice);
283 env(noop(alice), msig(bogie), fee((2 * baseFee) - 1), ter(telINSUF_FEE_P));
284 env.close();
285
286 BEAST_EXPECT(env.seq(alice) == aliceSeq);
287
288 // This should work.
289 aliceSeq = env.seq(alice);
290 env(noop(alice),
292 fee(9 * baseFee));
293 env.close();
294
295 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
296
297 // This should fail because the fee is too small.
298 aliceSeq = env.seq(alice);
299 env(noop(alice),
301 fee((9 * baseFee) - 1),
303 env.close();
304
305 BEAST_EXPECT(env.seq(alice) == aliceSeq);
306 }
307
308 void
310 {
311 testcase("Misordered Signers");
312
313 using namespace jtx;
314 Env env{*this, features};
315 Account const alice{"alice", KeyType::ed25519};
316 env.fund(XRP(1000), alice);
317 env.close();
318
319 // The signatures in a transaction must be submitted in sorted order.
320 // Make sure the transaction fails if they are not.
321 env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
322 env.close();
323 env.require(owners(alice, 1));
324
325 msig phantoms{bogie, demon};
326 std::reverse(phantoms.signers.begin(), phantoms.signers.end());
327 std::uint32_t const aliceSeq = env.seq(alice);
328 env(noop(alice),
329 phantoms,
330 rpc("invalidTransaction", "fails local checks: Unsorted Signers array."));
331 env.close();
332 BEAST_EXPECT(env.seq(alice) == aliceSeq);
333 }
334
335 void
337 {
338 testcase("Master Signers");
339
340 using namespace jtx;
341 Env env{*this, features};
342 Account const alice{"alice", KeyType::ed25519};
343 Account const becky{"becky", KeyType::secp256k1};
344 Account const cheri{"cheri", KeyType::ed25519};
345 env.fund(XRP(1000), alice, becky, cheri);
346 env.close();
347
348 // For a different situation, give alice a regular key but don't use it.
349 Account const alie{"alie", KeyType::secp256k1};
350 env(regkey(alice, alie));
351 env.close();
352 std::uint32_t aliceSeq = env.seq(alice);
353 env(noop(alice), sig(alice));
354 env(noop(alice), sig(alie));
355 env.close();
356 BEAST_EXPECT(env.seq(alice) == aliceSeq + 2);
357
358 // Attach signers to alice
359 env(signers(alice, 4, {{becky, 3}, {cheri, 4}}), sig(alice));
360 env.close();
361 env.require(owners(alice, 1));
362
363 // Attempt a multisigned transaction that meets the quorum.
364 auto const baseFee = env.current()->fees().base;
365 aliceSeq = env.seq(alice);
366 env(noop(alice), msig(cheri), fee(2 * baseFee));
367 env.close();
368 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
369
370 // If we don't meet the quorum the transaction should fail.
371 aliceSeq = env.seq(alice);
372 env(noop(alice), msig(becky), fee(2 * baseFee), ter(tefBAD_QUORUM));
373 env.close();
374 BEAST_EXPECT(env.seq(alice) == aliceSeq);
375
376 // Give becky and cheri regular keys.
377 Account const beck{"beck", KeyType::ed25519};
378 env(regkey(becky, beck));
379 Account const cher{"cher", KeyType::ed25519};
380 env(regkey(cheri, cher));
381 env.close();
382
383 // becky's and cheri's master keys should still work.
384 aliceSeq = env.seq(alice);
385 env(noop(alice), msig(becky, cheri), fee(3 * baseFee));
386 env.close();
387 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
388 }
389
390 void
392 {
393 testcase("Regular Signers");
394
395 using namespace jtx;
396 Env env{*this, features};
397 Account const alice{"alice", KeyType::secp256k1};
398 Account const becky{"becky", KeyType::ed25519};
399 Account const cheri{"cheri", KeyType::secp256k1};
400 env.fund(XRP(1000), alice, becky, cheri);
401 env.close();
402
403 // Attach signers to alice.
404 env(signers(alice, 1, {{becky, 1}, {cheri, 1}}), sig(alice));
405
406 // Give everyone regular keys.
407 Account const alie{"alie", KeyType::ed25519};
408 env(regkey(alice, alie));
409 Account const beck{"beck", KeyType::secp256k1};
410 env(regkey(becky, beck));
411 Account const cher{"cher", KeyType::ed25519};
412 env(regkey(cheri, cher));
413 env.close();
414
415 // Disable cheri's master key to mix things up.
416 env(fset(cheri, asfDisableMaster), sig(cheri));
417 env.close();
418
419 // Attempt a multisigned transaction that meets the quorum.
420 auto const baseFee = env.current()->fees().base;
421 std::uint32_t aliceSeq = env.seq(alice);
422 env(noop(alice), msig(Reg{cheri, cher}), fee(2 * baseFee));
423 env.close();
424 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
425
426 // cheri should not be able to multisign using her master key.
427 aliceSeq = env.seq(alice);
428 env(noop(alice), msig(cheri), fee(2 * baseFee), ter(tefMASTER_DISABLED));
429 env.close();
430 BEAST_EXPECT(env.seq(alice) == aliceSeq);
431
432 // becky should be able to multisign using either of her keys.
433 aliceSeq = env.seq(alice);
434 env(noop(alice), msig(becky), fee(2 * baseFee));
435 env.close();
436 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
437
438 aliceSeq = env.seq(alice);
439 env(noop(alice), msig(Reg{becky, beck}), fee(2 * baseFee));
440 env.close();
441 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
442
443 // Both becky and cheri should be able to sign using regular keys.
444 aliceSeq = env.seq(alice);
445 env(noop(alice), fee(3 * baseFee), msig(Reg{becky, beck}, Reg{cheri, cher}));
446 env.close();
447 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
448 }
449
450 void
452 {
453 testcase("Regular Signers Using submit_multisigned");
454
455 using namespace jtx;
456 Env env(
457 *this,
459 cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue");
460 return cfg;
461 }),
462 features);
463 Account const alice{"alice", KeyType::secp256k1};
464 Account const becky{"becky", KeyType::ed25519};
465 Account const cheri{"cheri", KeyType::secp256k1};
466 env.fund(XRP(1000), alice, becky, cheri);
467 env.close();
468
469 // Attach signers to alice.
470 env(signers(alice, 2, {{becky, 1}, {cheri, 1}}), sig(alice));
471
472 // Give everyone regular keys.
473 Account const beck{"beck", KeyType::secp256k1};
474 env(regkey(becky, beck));
475 Account const cher{"cher", KeyType::ed25519};
476 env(regkey(cheri, cher));
477 env.close();
478
479 // Disable cheri's master key to mix things up.
480 env(fset(cheri, asfDisableMaster), sig(cheri));
481 env.close();
482
483 auto const baseFee = env.current()->fees().base;
484 std::uint32_t aliceSeq = 0;
485
486 // these represent oft-repeated setup for input json below
487 auto setup_tx = [&]() -> Json::Value {
488 Json::Value jv;
489 jv[jss::tx_json][jss::Account] = alice.human();
490 jv[jss::tx_json][jss::TransactionType] = jss::AccountSet;
491 jv[jss::tx_json][jss::Fee] = (8 * baseFee).jsonClipped();
492 jv[jss::tx_json][jss::Sequence] = env.seq(alice);
493 jv[jss::tx_json][jss::SigningPubKey] = "";
494 return jv;
495 };
496 auto cheri_sign = [&](Json::Value& jv) {
497 jv[jss::account] = cheri.human();
498 jv[jss::key_type] = "ed25519";
499 jv[jss::passphrase] = cher.name();
500 };
501 auto becky_sign = [&](Json::Value& jv) {
502 jv[jss::account] = becky.human();
503 jv[jss::secret] = beck.name();
504 };
505
506 {
507 // Attempt a multisigned transaction that meets the quorum.
508 // using sign_for and submit_multisigned
509 aliceSeq = env.seq(alice);
510 Json::Value jv_one = setup_tx();
511 cheri_sign(jv_one);
512 auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result];
513 BEAST_EXPECT(jrr[jss::status] == "success");
514
515 // for the second sign_for, use the returned tx_json with
516 // first signer info
517 Json::Value jv_two;
518 jv_two[jss::tx_json] = jrr[jss::tx_json];
519 becky_sign(jv_two);
520 jrr = env.rpc("json", "sign_for", to_string(jv_two))[jss::result];
521 BEAST_EXPECT(jrr[jss::status] == "success");
522
523 Json::Value jv_submit;
524 jv_submit[jss::tx_json] = jrr[jss::tx_json];
525 jrr = env.rpc("json", "submit_multisigned", to_string(jv_submit))[jss::result];
526 BEAST_EXPECT(jrr[jss::status] == "success");
527 env.close();
528 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
529 }
530
531 {
532 // failure case -- SigningPubKey not empty
533 aliceSeq = env.seq(alice);
534 Json::Value jv_one = setup_tx();
535 jv_one[jss::tx_json][jss::SigningPubKey] = strHex(alice.pk().slice());
536 cheri_sign(jv_one);
537 auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result];
538 BEAST_EXPECT(jrr[jss::status] == "error");
539 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
540 BEAST_EXPECT(
541 jrr[jss::error_message] ==
542 "When multi-signing 'tx_json.SigningPubKey' must be empty.");
543 }
544
545 {
546 // failure case - bad fee
547 aliceSeq = env.seq(alice);
548 Json::Value jv_one = setup_tx();
549 jv_one[jss::tx_json][jss::Fee] = -1;
550 cheri_sign(jv_one);
551 auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result];
552 BEAST_EXPECT(jrr[jss::status] == "success");
553
554 // for the second sign_for, use the returned tx_json with
555 // first signer info
556 Json::Value jv_two;
557 jv_two[jss::tx_json] = jrr[jss::tx_json];
558 becky_sign(jv_two);
559 jrr = env.rpc("json", "sign_for", to_string(jv_two))[jss::result];
560 BEAST_EXPECT(jrr[jss::status] == "success");
561
562 Json::Value jv_submit;
563 jv_submit[jss::tx_json] = jrr[jss::tx_json];
564 jrr = env.rpc("json", "submit_multisigned", to_string(jv_submit))[jss::result];
565 BEAST_EXPECT(jrr[jss::status] == "error");
566 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
567 BEAST_EXPECT(
568 jrr[jss::error_message] == "Invalid Fee field. Fees must be greater than zero.");
569 }
570
571 {
572 // failure case - bad fee v2
573 aliceSeq = env.seq(alice);
574 Json::Value jv_one = setup_tx();
575 jv_one[jss::tx_json][jss::Fee] = alice["USD"](10).value().getFullText();
576 cheri_sign(jv_one);
577 auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result];
578 BEAST_EXPECT(jrr[jss::status] == "success");
579
580 // for the second sign_for, use the returned tx_json with
581 // first signer info
582 Json::Value jv_two;
583 jv_two[jss::tx_json] = jrr[jss::tx_json];
584 becky_sign(jv_two);
585 jrr = env.rpc("json", "sign_for", to_string(jv_two))[jss::result];
586 BEAST_EXPECT(jrr[jss::status] == "success");
587
588 Json::Value jv_submit;
589 jv_submit[jss::tx_json] = jrr[jss::tx_json];
590 jrr = env.rpc("json", "submit_multisigned", to_string(jv_submit))[jss::result];
591 BEAST_EXPECT(jrr[jss::status] == "error");
592 BEAST_EXPECT(jrr[jss::error] == "internal");
593 BEAST_EXPECT(jrr[jss::error_message] == "Internal error.");
594 }
595
596 {
597 // cheri should not be able to multisign using her master key.
598 aliceSeq = env.seq(alice);
599 Json::Value jv = setup_tx();
600 jv[jss::account] = cheri.human();
601 jv[jss::secret] = cheri.name();
602 auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
603 BEAST_EXPECT(jrr[jss::status] == "error");
604 BEAST_EXPECT(jrr[jss::error] == "masterDisabled");
605 env.close();
606 BEAST_EXPECT(env.seq(alice) == aliceSeq);
607 }
608
609 {
610 // Unlike cheri, becky should also be able to sign using her master
611 // key
612 aliceSeq = env.seq(alice);
613 Json::Value jv_one = setup_tx();
614 cheri_sign(jv_one);
615 auto jrr = env.rpc("json", "sign_for", to_string(jv_one))[jss::result];
616 BEAST_EXPECT(jrr[jss::status] == "success");
617
618 // for the second sign_for, use the returned tx_json with
619 // first signer info
620 Json::Value jv_two;
621 jv_two[jss::tx_json] = jrr[jss::tx_json];
622 jv_two[jss::account] = becky.human();
623 jv_two[jss::key_type] = "ed25519";
624 jv_two[jss::passphrase] = becky.name();
625 jrr = env.rpc("json", "sign_for", to_string(jv_two))[jss::result];
626 BEAST_EXPECT(jrr[jss::status] == "success");
627
628 Json::Value jv_submit;
629 jv_submit[jss::tx_json] = jrr[jss::tx_json];
630 jrr = env.rpc("json", "submit_multisigned", to_string(jv_submit))[jss::result];
631 BEAST_EXPECT(jrr[jss::status] == "success");
632 env.close();
633 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
634 }
635
636 {
637 // check for bad or bogus accounts in the tx
638 Json::Value jv = setup_tx();
639 jv[jss::tx_json][jss::Account] = "DEADBEEF";
640 cheri_sign(jv);
641 auto jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
642 BEAST_EXPECT(jrr[jss::status] == "error");
643 BEAST_EXPECT(jrr[jss::error] == "srcActMalformed");
644
645 Account const jimmy{"jimmy"};
646 jv[jss::tx_json][jss::Account] = jimmy.human();
647 jrr = env.rpc("json", "sign_for", to_string(jv))[jss::result];
648 BEAST_EXPECT(jrr[jss::status] == "error");
649 BEAST_EXPECT(jrr[jss::error] == "srcActNotFound");
650 }
651
652 {
653 aliceSeq = env.seq(alice);
654 Json::Value jv = setup_tx();
655 jv[jss::tx_json][sfSigners.fieldName] = Json::Value{Json::arrayValue};
656 becky_sign(jv);
657 auto jrr = env.rpc("json", "submit_multisigned", to_string(jv))[jss::result];
658 BEAST_EXPECT(jrr[jss::status] == "error");
659 BEAST_EXPECT(jrr[jss::error] == "invalidParams");
660 BEAST_EXPECT(jrr[jss::error_message] == "tx_json.Signers array may not be empty.");
661 env.close();
662 BEAST_EXPECT(env.seq(alice) == aliceSeq);
663 }
664 }
665
666 void
668 {
669 testcase("Heterogeneous Signers");
670
671 using namespace jtx;
672 Env env{*this, features};
673 Account const alice{"alice", KeyType::secp256k1};
674 Account const becky{"becky", KeyType::ed25519};
675 Account const cheri{"cheri", KeyType::secp256k1};
676 Account const daria{"daria", KeyType::ed25519};
677 env.fund(XRP(1000), alice, becky, cheri, daria);
678 env.close();
679
680 // alice uses a regular key with the master disabled.
681 Account const alie{"alie", KeyType::secp256k1};
682 env(regkey(alice, alie));
683 env(fset(alice, asfDisableMaster), sig(alice));
684
685 // becky is master only without a regular key.
686
687 // cheri has a regular key, but leaves the master key enabled.
688 Account const cher{"cher", KeyType::secp256k1};
689 env(regkey(cheri, cher));
690
691 // daria has a regular key and disables her master key.
692 Account const dari{"dari", KeyType::ed25519};
693 env(regkey(daria, dari));
694 env(fset(daria, asfDisableMaster), sig(daria));
695 env.close();
696
697 // Attach signers to alice.
698 env(signers(alice, 1, {{becky, 1}, {cheri, 1}, {daria, 1}, {jinni, 1}}), sig(alie));
699 env.close();
700 env.require(owners(alice, 1));
701
702 // Each type of signer should succeed individually.
703 auto const baseFee = env.current()->fees().base;
704 std::uint32_t aliceSeq = env.seq(alice);
705 env(noop(alice), msig(becky), fee(2 * baseFee));
706 env.close();
707 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
708
709 aliceSeq = env.seq(alice);
710 env(noop(alice), msig(cheri), fee(2 * baseFee));
711 env.close();
712 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
713
714 aliceSeq = env.seq(alice);
715 env(noop(alice), msig(Reg{cheri, cher}), fee(2 * baseFee));
716 env.close();
717 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
718
719 aliceSeq = env.seq(alice);
720 env(noop(alice), msig(Reg{daria, dari}), fee(2 * baseFee));
721 env.close();
722 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
723
724 aliceSeq = env.seq(alice);
725 env(noop(alice), msig(jinni), fee(2 * baseFee));
726 env.close();
727 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
728
729 // Should also work if all signers sign.
730 aliceSeq = env.seq(alice);
731 env(noop(alice), fee(5 * baseFee), msig(becky, Reg{cheri, cher}, Reg{daria, dari}, jinni));
732 env.close();
733 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
734
735 // Require all signers to sign.
736 env(signers(
737 alice,
738 0x3FFFC,
739 {{becky, 0xFFFF}, {cheri, 0xFFFF}, {daria, 0xFFFF}, {jinni, 0xFFFF}}),
740 sig(alie));
741 env.close();
742 env.require(owners(alice, 1));
743
744 aliceSeq = env.seq(alice);
745 env(noop(alice), fee(9 * baseFee), msig(becky, Reg{cheri, cher}, Reg{daria, dari}, jinni));
746 env.close();
747 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
748
749 // Try cheri with both key types.
750 aliceSeq = env.seq(alice);
751 env(noop(alice), fee(5 * baseFee), msig(becky, cheri, Reg{daria, dari}, jinni));
752 env.close();
753 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
754
755 // Makes sure the maximum allowed number of signers works.
756 env(signers(
757 alice,
758 0x7FFF8,
759 {{becky, 0xFFFF},
760 {cheri, 0xFFFF},
761 {daria, 0xFFFF},
762 {haunt, 0xFFFF},
763 {jinni, 0xFFFF},
764 {phase, 0xFFFF},
765 {shade, 0xFFFF},
766 {spook, 0xFFFF}}),
767 sig(alie));
768 env.close();
769 env.require(owners(alice, 1));
770
771 aliceSeq = env.seq(alice);
772 env(noop(alice),
773 fee(9 * baseFee),
774 msig(becky, Reg{cheri, cher}, Reg{daria, dari}, haunt, jinni, phase, shade, spook));
775 env.close();
776 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
777
778 // One signer short should fail.
779 aliceSeq = env.seq(alice);
780 env(noop(alice),
781 msig(becky, cheri, haunt, jinni, phase, shade, spook),
782 fee(8 * baseFee),
784 env.close();
785 BEAST_EXPECT(env.seq(alice) == aliceSeq);
786
787 // Remove alice's signer list and get the owner count back.
788 env(signers(alice, jtx::none), sig(alie));
789 env.close();
790 env.require(owners(alice, 0));
791 }
792
793 // We want to always leave an account signable. Make sure the that we
794 // disallow removing the last way a transaction may be signed.
795 void
797 {
798 testcase("Key Disable");
799
800 using namespace jtx;
801 Env env{*this, features};
802 Account const alice{"alice", KeyType::ed25519};
803 env.fund(XRP(1000), alice);
804 env.close();
805
806 // There are three negative tests we need to make:
807 // M0. A lone master key cannot be disabled.
808 // R0. A lone regular key cannot be removed.
809 // L0. A lone signer list cannot be removed.
810 //
811 // Additionally, there are 6 positive tests we need to make:
812 // M1. The master key can be disabled if there's a regular key.
813 // M2. The master key can be disabled if there's a signer list.
814 //
815 // R1. The regular key can be removed if there's a signer list.
816 // R2. The regular key can be removed if the master key is enabled.
817 //
818 // L1. The signer list can be removed if the master key is enabled.
819 // L2. The signer list can be removed if there's a regular key.
820
821 // Master key tests.
822 // M0: A lone master key cannot be disabled.
823 env(fset(alice, asfDisableMaster), sig(alice), ter(tecNO_ALTERNATIVE_KEY));
824
825 // Add a regular key.
826 Account const alie{"alie", KeyType::ed25519};
827 env(regkey(alice, alie));
828
829 // M1: The master key can be disabled if there's a regular key.
830 env(fset(alice, asfDisableMaster), sig(alice));
831
832 // R0: A lone regular key cannot be removed.
833 env(regkey(alice, disabled), sig(alie), ter(tecNO_ALTERNATIVE_KEY));
834
835 // Add a signer list.
836 env(signers(alice, 1, {{bogie, 1}}), sig(alie));
837
838 // R1: The regular key can be removed if there's a signer list.
839 env(regkey(alice, disabled), sig(alie));
840
841 // L0: A lone signer list cannot be removed.
842 auto const baseFee = env.current()->fees().base;
843 env(signers(alice, jtx::none), msig(bogie), fee(2 * baseFee), ter(tecNO_ALTERNATIVE_KEY));
844
845 // Enable the master key.
846 env(fclear(alice, asfDisableMaster), msig(bogie), fee(2 * baseFee));
847
848 // L1: The signer list can be removed if the master key is enabled.
849 env(signers(alice, jtx::none), msig(bogie), fee(2 * baseFee));
850
851 // Add a signer list.
852 env(signers(alice, 1, {{bogie, 1}}), sig(alice));
853
854 // M2: The master key can be disabled if there's a signer list.
855 env(fset(alice, asfDisableMaster), sig(alice));
856
857 // Add a regular key.
858 env(regkey(alice, alie), msig(bogie), fee(2 * baseFee));
859
860 // L2: The signer list can be removed if there's a regular key.
861 env(signers(alice, jtx::none), sig(alie));
862
863 // Enable the master key.
864 env(fclear(alice, asfDisableMaster), sig(alie));
865
866 // R2: The regular key can be removed if the master key is enabled.
867 env(regkey(alice, disabled), sig(alie));
868 }
869
870 // Verify that the first regular key can be made for free using the
871 // master key, but not when multisigning.
872 void
874 {
875 testcase("Regular Key");
876
877 using namespace jtx;
878 Env env{*this, features};
879 Account const alice{"alice", KeyType::secp256k1};
880 env.fund(XRP(1000), alice);
881 env.close();
882
883 // Give alice a regular key with a zero fee. Should succeed. Once.
884 Account const alie{"alie", KeyType::ed25519};
885 env(regkey(alice, alie), sig(alice), fee(0));
886
887 // Try it again and creating the regular key for free should fail.
888 Account const liss{"liss", KeyType::secp256k1};
889 env(regkey(alice, liss), sig(alice), fee(0), ter(telINSUF_FEE_P));
890
891 // But paying to create a regular key should succeed.
892 env(regkey(alice, liss), sig(alice));
893
894 // In contrast, trying to multisign for a regular key with a zero
895 // fee should always fail. Even the first time.
896 Account const becky{"becky", KeyType::ed25519};
897 env.fund(XRP(1000), becky);
898 env.close();
899
900 env(signers(becky, 1, {{alice, 1}}), sig(becky));
901 env(regkey(becky, alie), msig(alice), fee(0), ter(telINSUF_FEE_P));
902
903 // Using the master key to sign for a regular key for free should
904 // still work.
905 env(regkey(becky, alie), sig(becky), fee(0));
906 }
907
908 // See if every kind of transaction can be successfully multi-signed.
909 void
911 {
912 testcase("Transaction Types");
913
914 using namespace jtx;
915 Env env{*this, features};
916 Account const alice{"alice", KeyType::secp256k1};
917 Account const becky{"becky", KeyType::ed25519};
918 Account const zelda{"zelda", KeyType::secp256k1};
919 Account const gw{"gw"};
920 auto const USD = gw["USD"];
921 env.fund(XRP(1000), alice, becky, zelda, gw);
922 env.close();
923
924 // alice uses a regular key with the master disabled.
925 Account const alie{"alie", KeyType::secp256k1};
926 env(regkey(alice, alie));
927 env(fset(alice, asfDisableMaster), sig(alice));
928
929 // Attach signers to alice.
930 env(signers(alice, 2, {{becky, 1}, {bogie, 1}}), sig(alie));
931 env.close();
932 env.require(owners(alice, 1));
933
934 // Multisign a ttPAYMENT.
935 auto const baseFee = env.current()->fees().base;
936 std::uint32_t aliceSeq = env.seq(alice);
937 env(pay(alice, env.master, XRP(1)), msig(becky, bogie), fee(3 * baseFee));
938 env.close();
939 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
940
941 // Multisign a ttACCOUNT_SET.
942 aliceSeq = env.seq(alice);
943 env(noop(alice), msig(becky, bogie), fee(3 * baseFee));
944 env.close();
945 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
946
947 // Multisign a ttREGULAR_KEY_SET.
948 aliceSeq = env.seq(alice);
949 Account const ace{"ace", KeyType::secp256k1};
950 env(regkey(alice, ace), msig(becky, bogie), fee(3 * baseFee));
951 env.close();
952 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
953
954 // Multisign a ttTRUST_SET
955 env(trust("alice", USD(100)),
956 msig(becky, bogie),
957 fee(3 * baseFee),
958 require(lines("alice", 1)));
959 env.close();
960 env.require(owners(alice, 2));
961
962 // Multisign a ttOFFER_CREATE transaction.
963 env(pay(gw, alice, USD(50)));
964 env.close();
965 env.require(balance(alice, USD(50)));
966 env.require(balance(gw, alice["USD"](-50)));
967
968 std::uint32_t const offerSeq = env.seq(alice);
969 env(offer(alice, XRP(50), USD(50)), msig(becky, bogie), fee(3 * baseFee));
970 env.close();
971 env.require(owners(alice, 3));
972
973 // Now multisign a ttOFFER_CANCEL canceling the offer we just created.
974 {
975 aliceSeq = env.seq(alice);
976 env(offer_cancel(alice, offerSeq), seq(aliceSeq), msig(becky, bogie), fee(3 * baseFee));
977 env.close();
978 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
979 env.require(owners(alice, 2));
980 }
981
982 // Multisign a ttSIGNER_LIST_SET.
983 env(signers(alice, 3, {{becky, 1}, {bogie, 1}, {demon, 1}}),
984 msig(becky, bogie),
985 fee(3 * baseFee));
986 env.close();
987 env.require(owners(alice, 2));
988 }
989
990 void
992 {
993 testcase("Bad Signature Text");
994
995 // Verify that the text returned for signature failures is correct.
996 using namespace jtx;
997
998 Env env{*this, features};
999
1000 // lambda that submits an STTx and returns the resulting JSON.
1001 auto submitSTTx = [&env](STTx const& stx) {
1002 Json::Value jvResult;
1003 jvResult[jss::tx_blob] = strHex(stx.getSerializer().slice());
1004 return env.rpc("json", "submit", to_string(jvResult));
1005 };
1006
1007 Account const alice{"alice"};
1008 env.fund(XRP(1000), alice);
1009 env.close();
1010 env(signers(alice, 1, {{bogie, 1}, {demon, 1}}), sig(alice));
1011
1012 auto const baseFee = env.current()->fees().base;
1013 {
1014 // Single-sign, but leave an empty SigningPubKey.
1015 JTx const tx = env.jt(noop(alice), sig(alice));
1016 STTx local = *(tx.stx);
1017 local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey
1018 auto const info = submitSTTx(local);
1019 BEAST_EXPECT(
1020 info[jss::result][jss::error_exception] ==
1021 "fails local checks: Empty SigningPubKey.");
1022 }
1023 {
1024 // Single-sign, but invalidate the signature.
1025 JTx const tx = env.jt(noop(alice), sig(alice));
1026 STTx local = *(tx.stx);
1027 // Flip some bits in the signature.
1028 auto badSig = local.getFieldVL(sfTxnSignature);
1029 badSig[20] ^= 0xAA;
1030 local.setFieldVL(sfTxnSignature, badSig);
1031 // Signature should fail.
1032 auto const info = submitSTTx(local);
1033 BEAST_EXPECT(
1034 info[jss::result][jss::error_exception] ==
1035 "fails local checks: Invalid signature.");
1036 }
1037 {
1038 // Single-sign, but invalidate the sequence number.
1039 JTx const tx = env.jt(noop(alice), sig(alice));
1040 STTx local = *(tx.stx);
1041 // Flip some bits in the signature.
1042 auto seq = local.getFieldU32(sfSequence);
1043 local.setFieldU32(sfSequence, seq + 1);
1044 // Signature should fail.
1045 auto const info = submitSTTx(local);
1046 BEAST_EXPECT(
1047 info[jss::result][jss::error_exception] ==
1048 "fails local checks: Invalid signature.");
1049 }
1050 {
1051 // Multisign, but leave a nonempty sfSigningPubKey.
1052 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie));
1053 STTx local = *(tx.stx);
1054 local[sfSigningPubKey] = alice.pk(); // Insert sfSigningPubKey
1055 auto const info = submitSTTx(local);
1056 BEAST_EXPECT(
1057 info[jss::result][jss::error_exception] ==
1058 "fails local checks: Cannot both single- and multi-sign.");
1059 }
1060 {
1061 // Both multi- and single-sign with an empty SigningPubKey.
1062 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie));
1063 STTx local = *(tx.stx);
1064 local.sign(alice.pk(), alice.sk());
1065 local.setFieldVL(sfSigningPubKey, Blob()); // Empty SigningPubKey
1066 auto const info = submitSTTx(local);
1067 BEAST_EXPECT(
1068 info[jss::result][jss::error_exception] ==
1069 "fails local checks: Cannot both single- and multi-sign.");
1070 }
1071 {
1072 // Multisign but invalidate one of the signatures.
1073 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie));
1074 STTx local = *(tx.stx);
1075 // Flip some bits in the signature.
1076 auto& signer = local.peekFieldArray(sfSigners).back();
1077 auto badSig = signer.getFieldVL(sfTxnSignature);
1078 badSig[20] ^= 0xAA;
1079 signer.setFieldVL(sfTxnSignature, badSig);
1080 // Signature should fail.
1081 auto const info = submitSTTx(local);
1082 BEAST_EXPECT(
1083 info[jss::result][jss::error_exception].asString().find(
1084 "Invalid signature on account r") != std::string::npos);
1085 }
1086 {
1087 // Multisign with an empty signers array should fail.
1088 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie));
1089 STTx local = *(tx.stx);
1090 local.peekFieldArray(sfSigners).clear(); // Empty Signers array.
1091 auto const info = submitSTTx(local);
1092 BEAST_EXPECT(
1093 info[jss::result][jss::error_exception] ==
1094 "fails local checks: Invalid Signers array size.");
1095 }
1096 {
1097 JTx const tx = env.jt(
1098 noop(alice),
1099 fee(2 * baseFee),
1100
1101 msig(
1102 bogie,
1103 bogie,
1104 bogie,
1105 bogie,
1106 bogie,
1107 bogie,
1108 bogie,
1109 bogie,
1110 bogie,
1111 bogie,
1112 bogie,
1113 bogie,
1114 bogie,
1115 bogie,
1116 bogie,
1117 bogie,
1118 bogie,
1119 bogie,
1120 bogie,
1121 bogie,
1122 bogie,
1123 bogie,
1124 bogie,
1125 bogie,
1126 bogie,
1127 bogie,
1128 bogie,
1129 bogie,
1130 bogie,
1131 bogie,
1132 bogie,
1133 bogie,
1134 bogie));
1135 STTx const local = *(tx.stx);
1136 auto const info = submitSTTx(local);
1137 BEAST_EXPECT(
1138 info[jss::result][jss::error_exception] ==
1139 "fails local checks: Invalid Signers array size.");
1140 }
1141 {
1142 // The account owner may not multisign for themselves.
1143 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(alice));
1144 STTx const local = *(tx.stx);
1145 auto const info = submitSTTx(local);
1146 BEAST_EXPECT(
1147 info[jss::result][jss::error_exception] ==
1148 "fails local checks: Invalid multisigner.");
1149 }
1150 {
1151 // No duplicate multisignatures allowed.
1152 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, bogie));
1153 STTx const local = *(tx.stx);
1154 auto const info = submitSTTx(local);
1155 BEAST_EXPECT(
1156 info[jss::result][jss::error_exception] ==
1157 "fails local checks: Duplicate Signers not allowed.");
1158 }
1159 {
1160 // Multisignatures must be submitted in sorted order.
1161 JTx const tx = env.jt(noop(alice), fee(2 * baseFee), msig(bogie, demon));
1162 STTx local = *(tx.stx);
1163 // Unsort the Signers array.
1164 auto& signers = local.peekFieldArray(sfSigners);
1166 // Signature should fail.
1167 auto const info = submitSTTx(local);
1168 BEAST_EXPECT(
1169 info[jss::result][jss::error_exception] ==
1170 "fails local checks: Unsorted Signers array.");
1171 }
1172 }
1173
1174 void
1176 {
1177 testcase("No Multisigners");
1178
1179 using namespace jtx;
1180 Env env{*this, features};
1181 Account const alice{"alice", KeyType::ed25519};
1182 Account const becky{"becky", KeyType::secp256k1};
1183 env.fund(XRP(1000), alice, becky);
1184 env.close();
1185
1186 auto const baseFee = env.current()->fees().base;
1187 env(noop(alice), msig(becky, demon), fee(3 * baseFee), ter(tefNOT_MULTI_SIGNING));
1188 }
1189
1190 void
1192 {
1193 testcase("Multisigning multisigner");
1194
1195 // Set up a signer list where one of the signers has both the
1196 // master disabled and no regular key (because that signer is
1197 // exclusively multisigning). That signer should no longer be
1198 // able to successfully sign the signer list.
1199
1200 using namespace jtx;
1201 Env env{*this, features};
1202 Account const alice{"alice", KeyType::ed25519};
1203 Account const becky{"becky", KeyType::secp256k1};
1204 env.fund(XRP(1000), alice, becky);
1205 env.close();
1206
1207 // alice sets up a signer list with becky as a signer.
1208 env(signers(alice, 1, {{becky, 1}}));
1209 env.close();
1210
1211 // becky sets up her signer list.
1212 env(signers(becky, 1, {{bogie, 1}, {demon, 1}}));
1213 env.close();
1214
1215 // Because becky has not (yet) disabled her master key, she can
1216 // multisign a transaction for alice.
1217 auto const baseFee = env.current()->fees().base;
1218 env(noop(alice), msig(becky), fee(2 * baseFee));
1219 env.close();
1220
1221 // Now becky disables her master key.
1222 env(fset(becky, asfDisableMaster));
1223 env.close();
1224
1225 // Since becky's master key is disabled she can no longer
1226 // multisign for alice.
1227 env(noop(alice), msig(becky), fee(2 * baseFee), ter(tefMASTER_DISABLED));
1228 env.close();
1229
1230 // Becky cannot 2-level multisign for alice. 2-level multisigning
1231 // is not supported.
1232 env(noop(alice), msig(Reg{becky, bogie}), fee(2 * baseFee), ter(tefBAD_SIGNATURE));
1233 env.close();
1234
1235 // Verify that becky cannot sign with a regular key that she has
1236 // not yet enabled.
1237 Account const beck{"beck", KeyType::ed25519};
1238 env(noop(alice), msig(Reg{becky, beck}), fee(2 * baseFee), ter(tefBAD_SIGNATURE));
1239 env.close();
1240
1241 // Once becky gives herself the regular key, she can sign for alice
1242 // using that regular key.
1243 env(regkey(becky, beck), msig(demon), fee(2 * baseFee));
1244 env.close();
1245
1246 env(noop(alice), msig(Reg{becky, beck}), fee(2 * baseFee));
1247 env.close();
1248
1249 // The presence of becky's regular key does not influence whether she
1250 // can 2-level multisign; it still won't work.
1251 env(noop(alice), msig(Reg{becky, demon}), fee(2 * baseFee), ter(tefBAD_SIGNATURE));
1252 env.close();
1253 }
1254
1255 void
1257 {
1258 testcase("sign_for Hash");
1259
1260 // Make sure that the "hash" field returned by the "sign_for" RPC
1261 // command matches the hash returned when that command is sent
1262 // through "submit_multisigned". Make sure that hash also locates
1263 // the transaction in the ledger.
1264 using namespace jtx;
1265 Account const alice{"alice", KeyType::ed25519};
1266
1267 Env env(
1268 *this,
1270 cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue");
1271 return cfg;
1272 }),
1273 features);
1274 env.fund(XRP(1000), alice);
1275 env.close();
1276
1277 env(signers(alice, 2, {{bogie, 1}, {ghost, 1}}));
1278 env.close();
1279
1280 // Use sign_for to sign a transaction where alice pays 10 XRP to
1281 // masterpassphrase.
1282 auto const baseFee = env.current()->fees().base;
1283 Json::Value jvSig1;
1284 jvSig1[jss::account] = bogie.human();
1285 jvSig1[jss::secret] = bogie.name();
1286 jvSig1[jss::tx_json][jss::Account] = alice.human();
1287 jvSig1[jss::tx_json][jss::Amount] = 10000000;
1288 jvSig1[jss::tx_json][jss::Destination] = env.master.human();
1289 jvSig1[jss::tx_json][jss::Fee] = (3 * baseFee).jsonClipped();
1290 jvSig1[jss::tx_json][jss::Sequence] = env.seq(alice);
1291 jvSig1[jss::tx_json][jss::TransactionType] = jss::Payment;
1292
1293 Json::Value jvSig2 = env.rpc("json", "sign_for", to_string(jvSig1));
1294 BEAST_EXPECT(jvSig2[jss::result][jss::status].asString() == "success");
1295
1296 // Save the hash with one signature for use later.
1297 std::string const hash1 = jvSig2[jss::result][jss::tx_json][jss::hash].asString();
1298
1299 // Add the next signature and sign again.
1300 jvSig2[jss::result][jss::account] = ghost.human();
1301 jvSig2[jss::result][jss::secret] = ghost.name();
1302 Json::Value jvSubmit = env.rpc("json", "sign_for", to_string(jvSig2[jss::result]));
1303 BEAST_EXPECT(jvSubmit[jss::result][jss::status].asString() == "success");
1304
1305 // Save the hash with two signatures for use later.
1306 std::string const hash2 = jvSubmit[jss::result][jss::tx_json][jss::hash].asString();
1307 BEAST_EXPECT(hash1 != hash2);
1308
1309 // Submit the result of the two signatures.
1310 Json::Value jvResult =
1311 env.rpc("json", "submit_multisigned", to_string(jvSubmit[jss::result]));
1312 BEAST_EXPECT(jvResult[jss::result][jss::status].asString() == "success");
1313 BEAST_EXPECT(jvResult[jss::result][jss::engine_result].asString() == "tesSUCCESS");
1314
1315 // The hash from the submit should be the same as the hash from the
1316 // second signing.
1317 BEAST_EXPECT(hash2 == jvResult[jss::result][jss::tx_json][jss::hash].asString());
1318 env.close();
1319
1320 // The transaction we just submitted should now be available and
1321 // validated.
1322 Json::Value jvTx = env.rpc("tx", hash2);
1323 BEAST_EXPECT(jvTx[jss::result][jss::status].asString() == "success");
1324 BEAST_EXPECT(jvTx[jss::result][jss::validated].asString() == "true");
1325 BEAST_EXPECT(
1326 jvTx[jss::result][jss::meta][sfTransactionResult.jsonName].asString() == "tesSUCCESS");
1327 }
1328
1329 void
1331 {
1332 testcase("Signers With Tickets");
1333
1334 using namespace jtx;
1335 Env env{*this, features};
1336 Account const alice{"alice", KeyType::ed25519};
1337 env.fund(XRP(2000), alice);
1338 env.close();
1339
1340 // Create a few tickets that alice can use up.
1341 std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
1342 env(ticket::create(alice, 20));
1343 env.close();
1344 std::uint32_t const aliceSeq = env.seq(alice);
1345
1346 // Attach phantom signers to alice using a ticket.
1347 env(signers(alice, 1, {{bogie, 1}, {demon, 1}}), ticket::use(aliceTicketSeq++));
1348 env.close();
1349 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1350 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1351
1352 // This should work.
1353 auto const baseFee = env.current()->fees().base;
1354 env(noop(alice), msig(bogie, demon), fee(3 * baseFee), ticket::use(aliceTicketSeq++));
1355 env.close();
1356 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1357 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1358
1359 // Should also be able to remove the signer list using a ticket.
1360 env(signers(alice, jtx::none), ticket::use(aliceTicketSeq++));
1361 env.close();
1362 env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
1363 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1364 }
1365
1366 void
1368 {
1369 testcase("Signers With Tags");
1370
1371 using namespace jtx;
1372 Env env{*this, features};
1373 Account const alice{"alice", KeyType::ed25519};
1374 env.fund(XRP(1000), alice);
1375 env.close();
1376 uint8_t tag1[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03,
1377 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1378 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
1379
1380 uint8_t tag2[] = "hello world some ascii 32b long"; // including 1 byte for NUL
1381
1382 uint256 bogie_tag = xrpl::base_uint<256>::fromVoid(tag1);
1383 uint256 demon_tag = xrpl::base_uint<256>::fromVoid(tag2);
1384
1385 // Attach phantom signers to alice and use them for a transaction.
1386 env(signers(alice, 1, {{bogie, 1, bogie_tag}, {demon, 1, demon_tag}}));
1387 env.close();
1388 env.require(owners(alice, 1));
1389
1390 // This should work.
1391 auto const baseFee = env.current()->fees().base;
1392 std::uint32_t aliceSeq = env.seq(alice);
1393 env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
1394 env.close();
1395 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
1396
1397 // Either signer alone should work.
1398 aliceSeq = env.seq(alice);
1399 env(noop(alice), msig(bogie), fee(2 * baseFee));
1400 env.close();
1401 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
1402
1403 aliceSeq = env.seq(alice);
1404 env(noop(alice), msig(demon), fee(2 * baseFee));
1405 env.close();
1406 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
1407
1408 // Duplicate signers should fail.
1409 aliceSeq = env.seq(alice);
1410 env(noop(alice),
1411 msig(demon, demon),
1412 fee(3 * baseFee),
1413 rpc("invalidTransaction", "fails local checks: Duplicate Signers not allowed."));
1414 env.close();
1415 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1416
1417 // A non-signer should fail.
1418 aliceSeq = env.seq(alice);
1419 env(noop(alice), msig(bogie, spook), fee(3 * baseFee), ter(tefBAD_SIGNATURE));
1420 env.close();
1421 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1422
1423 // Don't meet the quorum. Should fail.
1424 env(signers(alice, 2, {{bogie, 1}, {demon, 1}}));
1425 aliceSeq = env.seq(alice);
1426 env(noop(alice), msig(bogie), fee(2 * baseFee), ter(tefBAD_QUORUM));
1427 env.close();
1428 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1429
1430 // Meet the quorum. Should succeed.
1431 aliceSeq = env.seq(alice);
1432 env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
1433 env.close();
1434 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
1435 }
1436
1437 void
1439 {
1440 using namespace test::jtx;
1441
1442 Env env{*this, features};
1443 Account const alice{"alice"};
1444
1445 env.fund(XRP(1000), alice);
1446 env.close();
1447
1448 bool const enabled = features[fixInvalidTxFlags];
1449 testcase(std::string("SignerListSet flag, fix ") + (enabled ? "enabled" : "disabled"));
1450
1451 ter const expected(enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
1452 env(signers(alice, 2, {{bogie, 1}, {ghost, 1}}), expected, txflags(tfPassive));
1453 env.close();
1454 }
1455
1456 void
1458 {
1459 testcase("SignerList Object");
1460
1461 // Verify that the SignerList object is created correctly.
1462 using namespace jtx;
1463 Env env{*this, features};
1464 Account const alice{"alice", KeyType::ed25519};
1465 env.fund(XRP(1000), alice);
1466 env.close();
1467
1468 // Attach phantom signers to alice.
1469 env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
1470 env.close();
1471
1472 // Verify that the SignerList object was created correctly.
1473 auto const& sle = env.le(keylet::signers(alice.id()));
1474 BEAST_EXPECT(sle);
1475 BEAST_EXPECT(sle->getFieldArray(sfSignerEntries).size() == 2);
1476 if (features[fixIncludeKeyletFields])
1477 {
1478 BEAST_EXPECT((*sle)[sfOwner] == alice.id());
1479 }
1480 else
1481 {
1482 BEAST_EXPECT(!sle->isFieldPresent(sfOwner));
1483 }
1484 }
1485
1486 void
1488 {
1489 testNoReserve(features);
1490 testSignerListSet(features);
1491 testPhantomSigners(features);
1492 testFee(features);
1493 testMisorderedSigners(features);
1494 testMasterSigners(features);
1495 testRegularSigners(features);
1497 testHeterogeneousSigners(features);
1498 testKeyDisable(features);
1499 testRegKey(features);
1500 testTxTypes(features);
1501 testBadSignatureText(features);
1502 testNoMultiSigners(features);
1504 testSignForHash(features);
1505 testSignersWithTickets(features);
1506 testSignersWithTags(features);
1507 }
1508
1509 void
1510 run() override
1511 {
1512 using namespace jtx;
1513 auto const all = testable_amendments();
1514
1515 testAll(all);
1516
1517 testSignerListSetFlags(all - fixInvalidTxFlags);
1519
1520 testSignerListObject(all - fixIncludeKeyletFields);
1522 }
1523};
1524
1525BEAST_DEFINE_TESTSUITE(MultiSign, app, xrpl);
1526
1527} // namespace test
1528} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
const_iterator begin() const
const_iterator end() const
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
void clear()
Definition STArray.h:237
STObject & back()
Definition STArray.h:170
Blob getFieldVL(SField const &field) const
Definition STObject.cpp:641
std::uint32_t getFieldU32(SField const &field) const
Definition STObject.cpp:593
void setFieldVL(SField const &field, Blob const &)
Definition STObject.cpp:777
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:735
STArray & peekFieldArray(SField const &field)
Definition STObject.cpp:473
void sign(PublicKey const &publicKey, SecretKey const &secretKey, std::optional< std::reference_wrapper< SField const > > signatureTarget={})
Definition STTx.cpp:231
static base_uint fromVoid(void const *data)
Definition base_uint.h:293
void testMultisigningMultisigner(FeatureBitset features)
void testRegKey(FeatureBitset features)
void testAll(FeatureBitset features)
void testPhantomSigners(FeatureBitset features)
void testBadSignatureText(FeatureBitset features)
void testSignForHash(FeatureBitset features)
void testTxTypes(FeatureBitset features)
void testRegularSigners(FeatureBitset features)
void testKeyDisable(FeatureBitset features)
void testMisorderedSigners(FeatureBitset features)
void testSignersWithTags(FeatureBitset features)
void testSignerListSet(FeatureBitset features)
void testSignerListSetFlags(FeatureBitset features)
void testRegularSignersUsingSubmitMulti(FeatureBitset features)
void testSignerListObject(FeatureBitset features)
void testHeterogeneousSigners(FeatureBitset features)
void testMasterSigners(FeatureBitset features)
void testFee(FeatureBitset features)
void run() override
Runs the suite.
void testNoMultiSigners(FeatureBitset features)
void testNoReserve(FeatureBitset features)
void testSignersWithTickets(FeatureBitset features)
Immutable cryptographic account descriptor.
Definition Account.h:19
std::string const & human() const
Returns the human readable public key.
Definition Account.h:94
std::string const & name() const
Return the name.
Definition Account.h:63
A transaction testing environment.
Definition Env.h:122
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:100
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:270
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:249
Account const & master
Definition Env.h:126
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:847
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:329
A balance matches.
Definition balance.h:19
Set the fee on a JTx.
Definition fee.h:17
Set a multisignature on a JTx.
Definition multisign.h:41
Match the number of items in the account's owner directory.
Definition owners.h:52
Check a set of conditions.
Definition require.h:46
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
Set the regular signature on a JTx.
Definition sig.h:15
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:15
Set a ticket sequence on a JTx.
Definition ticket.h:28
Set the flags on a JTx.
Definition txflags.h:11
@ arrayValue
array value (ordered list)
Definition json_value.h:25
Keylet signers(AccountID const &account) noexcept
A SignerList.
Definition Indexes.cpp:295
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition trust.cpp:13
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition multisign.cpp:15
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:95
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:101
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition offer.cpp:27
static none_t const none
Definition tags.h:14
FeatureBitset testable_amendments()
Definition Env.h:78
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition regkey.cpp:10
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition ticket.h:44
owner_count< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
Definition owners.h:67
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
static disabled_t const disabled
Definition tags.h:30
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ telINSUF_FEE_P
Definition TER.h:37
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
@ tefBAD_QUORUM
Definition TER.h:160
@ tefMASTER_DISABLED
Definition TER.h:157
@ tefBAD_SIGNATURE
Definition TER.h:159
@ tefNOT_MULTI_SIGNING
Definition TER.h:161
TERSubset< CanCvtToTER > TER
Definition TER.h:622
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:10
@ temINVALID_FLAG
Definition TER.h:91
@ temMALFORMED
Definition TER.h:67
@ temBAD_QUORUM
Definition TER.h:96
@ temBAD_SIGNER
Definition TER.h:95
@ temBAD_WEIGHT
Definition TER.h:97
@ tecNO_ALTERNATIVE_KEY
Definition TER.h:277
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
@ tesSUCCESS
Definition TER.h:225
T reverse(T... args)
Execution context for applying a JSON transaction.
Definition JTx.h:25
std::shared_ptr< STTx const > stx
Definition JTx.h:35
Set the sequence number on a JTx.
Definition seq.h:14
A signer in a SignerList.
Definition multisign.h:19