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