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