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