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