@@ -583,6 +583,138 @@ BOOST_FIXTURE_TEST_CASE( account_history_pagination, cli_fixture )
583
583
}
584
584
}
585
585
586
+
587
+ // /////////////////////
588
+ // Create a multi-sig account and verify that only when all signatures are
589
+ // signed, the transaction could be broadcast
590
+ // /////////////////////
591
+ BOOST_AUTO_TEST_CASE ( cli_multisig_transaction )
592
+ {
593
+ using namespace graphene ::chain;
594
+ using namespace graphene ::app;
595
+ std::shared_ptr<graphene::app::application> app1;
596
+ try {
597
+ fc::temp_directory app_dir ( graphene::utilities::temp_directory_path () );
598
+
599
+ int server_port_number = 0 ;
600
+ app1 = start_application (app_dir, server_port_number);
601
+
602
+ // connect to the server
603
+ client_connection con (app1, app_dir, server_port_number);
604
+
605
+ BOOST_TEST_MESSAGE (" Setting wallet password" );
606
+ con.wallet_api_ptr ->set_password (" supersecret" );
607
+ con.wallet_api_ptr ->unlock (" supersecret" );
608
+
609
+ // import Nathan account
610
+ BOOST_TEST_MESSAGE (" Importing nathan key" );
611
+ std::vector<std::string> nathan_keys{" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" };
612
+ BOOST_CHECK_EQUAL (nathan_keys[0 ], " 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" );
613
+ BOOST_CHECK (con.wallet_api_ptr ->import_key (" nathan" , nathan_keys[0 ]));
614
+
615
+ BOOST_TEST_MESSAGE (" Importing nathan's balance" );
616
+ std::vector<signed_transaction> import_txs = con.wallet_api_ptr ->import_balance (" nathan" , nathan_keys, true );
617
+ account_object nathan_acct_before_upgrade = con.wallet_api_ptr ->get_account (" nathan" );
618
+
619
+ // upgrade nathan
620
+ BOOST_TEST_MESSAGE (" Upgrading Nathan to LTM" );
621
+ signed_transaction upgrade_tx = con.wallet_api_ptr ->upgrade_account (" nathan" , true );
622
+ account_object nathan_acct_after_upgrade = con.wallet_api_ptr ->get_account (" nathan" );
623
+
624
+ // verify that the upgrade was successful
625
+ BOOST_CHECK_PREDICATE ( std::not_equal_to<uint32_t >(), (nathan_acct_before_upgrade.membership_expiration_date .sec_since_epoch ())(nathan_acct_after_upgrade.membership_expiration_date .sec_since_epoch ()) );
626
+ BOOST_CHECK (nathan_acct_after_upgrade.is_lifetime_member ());
627
+
628
+ // create a new multisig account
629
+ graphene::wallet::brain_key_info bki1 = con.wallet_api_ptr ->suggest_brain_key ();
630
+ graphene::wallet::brain_key_info bki2 = con.wallet_api_ptr ->suggest_brain_key ();
631
+ graphene::wallet::brain_key_info bki3 = con.wallet_api_ptr ->suggest_brain_key ();
632
+ graphene::wallet::brain_key_info bki4 = con.wallet_api_ptr ->suggest_brain_key ();
633
+ BOOST_CHECK (!bki1.brain_priv_key .empty ());
634
+ BOOST_CHECK (!bki2.brain_priv_key .empty ());
635
+ BOOST_CHECK (!bki3.brain_priv_key .empty ());
636
+ BOOST_CHECK (!bki4.brain_priv_key .empty ());
637
+
638
+ signed_transaction create_multisig_acct_tx;
639
+ account_create_operation account_create_op;
640
+
641
+ account_create_op.referrer = nathan_acct_after_upgrade.id ;
642
+ account_create_op.referrer_percent = nathan_acct_after_upgrade.referrer_rewards_percentage ;
643
+ account_create_op.registrar = nathan_acct_after_upgrade.id ;
644
+ account_create_op.name = " cifer.test" ;
645
+ account_create_op.owner = authority (1 , bki1.pub_key , 1 );
646
+ account_create_op.active = authority (2 , bki2.pub_key , 1 , bki3.pub_key , 1 );
647
+ account_create_op.options .memo_key = bki4.pub_key ;
648
+ account_create_op.fee = asset (1000000 ); // should be enough for creating account
649
+
650
+ create_multisig_acct_tx.operations .push_back (account_create_op);
651
+ con.wallet_api_ptr ->sign_transaction (create_multisig_acct_tx, true );
652
+
653
+ // attempt to give cifer.test some bitsahres
654
+ BOOST_TEST_MESSAGE (" Transferring bitshares from Nathan to cifer.test" );
655
+ signed_transaction transfer_tx1 = con.wallet_api_ptr ->transfer (" nathan" , " cifer.test" , " 10000" , " 1.3.0" , " Here are some BTS for your new account" , true );
656
+
657
+ // transfer bts from cifer.test to nathan
658
+ BOOST_TEST_MESSAGE (" Transferring bitshares from cifer.test to nathan" );
659
+ auto dyn_props = app1->chain_database ()->get_dynamic_global_properties ();
660
+ account_object cifer_test = con.wallet_api_ptr ->get_account (" cifer.test" );
661
+
662
+ // construct a transfer transaction
663
+ signed_transaction transfer_tx2;
664
+ transfer_operation xfer_op;
665
+ xfer_op.from = cifer_test.id ;
666
+ xfer_op.to = nathan_acct_after_upgrade.id ;
667
+ xfer_op.amount = asset (100000000 );
668
+ xfer_op.fee = asset (3000000 ); // should be enough for transfer
669
+ transfer_tx2.operations .push_back (xfer_op);
670
+
671
+ // case1: sign a transaction without TaPoS and expiration fields
672
+ // expect: return a transaction with TaPoS and expiration filled
673
+ transfer_tx2 =
674
+ con.wallet_api_ptr ->add_transaction_signature ( transfer_tx2, false );
675
+ BOOST_CHECK ( ( transfer_tx2.ref_block_num != 0 &&
676
+ transfer_tx2.ref_block_prefix != 0 ) ||
677
+ ( transfer_tx2.expiration != fc::time_point_sec () ) );
678
+
679
+ // case2: broadcast without signature
680
+ // expect: exception with missing active authority
681
+ BOOST_CHECK_THROW (con.wallet_api_ptr ->broadcast_transaction (transfer_tx2), fc::exception );
682
+
683
+ // case3:
684
+ // import one of the private keys for this new account in the wallet file,
685
+ // sign and broadcast with partial signatures
686
+ //
687
+ // expect: exception with missing active authority
688
+ BOOST_CHECK (con.wallet_api_ptr ->import_key (" cifer.test" , bki2.wif_priv_key ));
689
+ BOOST_CHECK_THROW (con.wallet_api_ptr ->add_transaction_signature (transfer_tx2, true ), fc::exception );
690
+
691
+ // case4: sign again as signature exists
692
+ // expect: num of signatures not increase
693
+ transfer_tx2 = con.wallet_api_ptr ->add_transaction_signature (transfer_tx2, false );
694
+ BOOST_CHECK_EQUAL (transfer_tx2.signatures .size (), 1 );
695
+
696
+ // case5:
697
+ // import another private key, sign and broadcast without full signatures
698
+ //
699
+ // expect: transaction broadcast successfully
700
+ BOOST_CHECK (con.wallet_api_ptr ->import_key (" cifer.test" , bki3.wif_priv_key ));
701
+ con.wallet_api_ptr ->add_transaction_signature (transfer_tx2, true );
702
+ auto balances = con.wallet_api_ptr ->list_account_balances ( " cifer.test" );
703
+ for (auto b : balances) {
704
+ if (b.asset_id == asset_id_type ()) {
705
+ BOOST_ASSERT (b == asset (900000000 - 3000000 ));
706
+ }
707
+ }
708
+
709
+ // wait for everything to finish up
710
+ fc::usleep (fc::seconds (1 ));
711
+ } catch ( fc::exception & e ) {
712
+ edump ((e.to_detail_string ()));
713
+ throw ;
714
+ }
715
+ app1->shutdown ();
716
+ }
717
+
586
718
graphene::wallet::plain_keys decrypt_keys ( const std::string& password, const vector<char >& cipher_keys )
587
719
{
588
720
auto pw = fc::sha512::hash ( password.c_str (), password.size () );
0 commit comments