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