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