From 08b38480204a93dc12accd762da336a171bbc7e3 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Thu, 28 Jun 2018 13:57:14 -0400 Subject: [PATCH 1/4] Added test case for confidential transactions and range proof mantissa length produced by cli_wallet. --- tests/cli/main.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 53f702aea1..5695e0e801 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -388,3 +388,98 @@ BOOST_AUTO_TEST_CASE( cli_set_voting_proxy ) } app1->shutdown(); } + +/////////////////// +// Test blind transactions and mantissa length of range proofs. +/////////////////// +BOOST_AUTO_TEST_CASE( cli_confidential_tx_test ) +{ + using namespace graphene::chain; + using namespace graphene::app; + using namespace graphene::wallet; + std::shared_ptr app1; + try { + + // ** Start a Graphene chain and API server: + fc::temp_directory app_dir( graphene::utilities::temp_directory_path() ); + int server_port_number; + app1 = start_application(app_dir, server_port_number); + unsigned int head_block = 0; + + // ** Connect a Wallet to the API server, and generate three BLIND accounts: + client_connection con(app1, app_dir, server_port_number); + auto & W = *con.wallet_api_ptr; // Wallet alias + BOOST_TEST_MESSAGE("Setting wallet password"); + W.set_password("supersecret"); + W.unlock("supersecret"); + BOOST_TEST_MESSAGE("Creating blind accounts"); + graphene::wallet::brain_key_info bki_nathan = W.suggest_brain_key(); + graphene::wallet::brain_key_info bki_alice = W.suggest_brain_key(); + graphene::wallet::brain_key_info bki_bob = W.suggest_brain_key(); + W.create_blind_account("nathan", bki_nathan.brain_priv_key); + W.create_blind_account("alice", bki_alice.brain_priv_key); + W.create_blind_account("bob", bki_bob.brain_priv_key); + BOOST_CHECK(W.get_blind_accounts().size() == 3); + + // ** Block 1: Import Nathan account: + BOOST_TEST_MESSAGE("Importing nathan key and balance"); + std::vector nathan_keys{"5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"}; + W.import_key("nathan", nathan_keys[0]); + W.import_balance("nathan", nathan_keys, true); + generate_block(app1); head_block++; + + // ** Block 2: Nathan will blind 100M BTS: + BOOST_TEST_MESSAGE("Blinding a large balance"); + W.transfer_to_blind("nathan", "BTS", {{"nathan","100000000"}}, true); + BOOST_CHECK( W.get_blind_balances("nathan")[0].amount == 10000000000000 ); + generate_block(app1); head_block++; + + // ** Block 3: Nathan will send 1M BTS to alice and 10K BTS to bob. We + // then confirm that balances are received, and then analyze the range + // prooofs to make sure the mantissa length does not reveal approximate + // balance (issue #480). + std::map to_list = {{"alice",100000000000}, + {"bob", 1000000000}}; + vector bconfs; + asset_object core_asset = W.get_asset("1.3.0"); + BOOST_TEST_MESSAGE("Sending blind transactions to alice and bob"); + for (auto to : to_list) { + string amount = core_asset.amount_to_string(to.second); + bconfs.push_back(W.blind_transfer("nathan",to.first,amount,core_asset.symbol,true)); + BOOST_CHECK( W.get_blind_balances(to.first)[0].amount == to.second ); + } + BOOST_TEST_MESSAGE("Inspecting range proof mantissa lengths"); + vector rp_mantissabits; + for (auto conf : bconfs) { + for (auto out : conf.trx.operations[0].get().outputs) { + rp_mantissabits.push_back(1+out.range_proof[1]); // 2nd byte encodes mantissa length + } + } + // We are checking the mantissa length of the range proofs for several Pedersen + // commitments of varying magnitude. We don't want the mantissa lengths to give + // away magnitude. Deprecated wallet behavior was to use "just enough" mantissa + // bits to prove range, but this gives away value to within a factor of two. As a + // naive test, we assume that if all mantissa lengths are equal, then they are not + // revealing magnitude. However, future more-sophisticated wallet behavior + // *might* randomize mantissa length to achieve some space savings in the range + // proof. The following test will fail in that case and a more sophisticated test + // will be needed. + auto adjacent_unequal = std::adjacent_find(rp_mantissabits.begin(), + /* find unequal adjacent values */ rp_mantissabits.end(), + std::not_equal_to()); + BOOST_CHECK(adjacent_unequal == rp_mantissabits.end()); + generate_block(app1); head_block++; + + // ** Check head block: + BOOST_MESSAGE("Check that all expected blocks have processed"); + dynamic_global_property_object dgp = W.get_dynamic_global_properties(); + BOOST_CHECK(dgp.head_block_number == head_block); + + // wait for everything to finish up + fc::usleep(fc::seconds(1)); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } + app1->shutdown(); +} From e0a7399b90544d573a1e9c855ce1404537484918 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sun, 1 Jul 2018 22:21:15 -0400 Subject: [PATCH 2/4] Changed range proof mantissa min_bits used by cli_wallet from 0 (automatic) to 49 (the largest bit length that won't exceed GRAPHENE_MAX_SHARE_SUPPLY). --- libraries/wallet/wallet.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 1347a26e8f..cd3db6d94a 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -85,6 +85,10 @@ namespace fc { } #define BRAIN_KEY_WORD_COUNT 16 +#define RANGE_PROOF_MANTISSA 49 // Minimum mantissa bits to "hide" in the range proof. + // If this number is set too low, then for large value + // commitments the length of the range proof will hint + // strongly at the value amount that is being hidden. namespace graphene { namespace wallet { @@ -4274,12 +4278,12 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, if( blind_tr.outputs.size() > 1 ) { - to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, RANGE_PROOF_MANTISSA, amount.amount.value ); blind_output change_out; change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); - change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, 0, change.amount.value ); + change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, RANGE_PROOF_MANTISSA, change.amount.value ); blind_tr.outputs[1] = change_out; @@ -4387,7 +4391,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); if( to_amounts.size() > 1 ) - out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, 0, amount.amount.value ); + out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, RANGE_PROOF_MANTISSA, amount.amount.value ); blind_confirmation::output conf_output; From 0babe53db8b521178f53edb3c9bc6ca52ed14fa7 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Wed, 4 Jul 2018 19:01:15 -0400 Subject: [PATCH 3/4] Code style (line length) --- libraries/wallet/wallet.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index cd3db6d94a..ab6583a158 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -4278,12 +4278,14 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, if( blind_tr.outputs.size() > 1 ) { - to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, 0, RANGE_PROOF_MANTISSA, amount.amount.value ); + to_out.range_proof = fc::ecc::range_proof_sign( 0, to_out.commitment, blind_factor, nonce, + 0, RANGE_PROOF_MANTISSA, amount.amount.value ); blind_output change_out; change_out.owner = authority( 1, public_key_type( from_pub_key.child( from_child ) ), 1 ); change_out.commitment = fc::ecc::blind( change_blind_factor, change.amount.value ); - change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, 0, RANGE_PROOF_MANTISSA, change.amount.value ); + change_out.range_proof = fc::ecc::range_proof_sign( 0, change_out.commitment, change_blind_factor, from_nonce, + 0, RANGE_PROOF_MANTISSA, change.amount.value ); blind_tr.outputs[1] = change_out; @@ -4391,8 +4393,8 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name out.owner = authority( 1, public_key_type( to_pub_key.child( child ) ), 1 ); out.commitment = fc::ecc::blind( blind_factor, amount.amount.value ); if( to_amounts.size() > 1 ) - out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, 0, RANGE_PROOF_MANTISSA, amount.amount.value ); - + out.range_proof = fc::ecc::range_proof_sign( 0, out.commitment, blind_factor, nonce, + 0, RANGE_PROOF_MANTISSA, amount.amount.value ); blind_confirmation::output conf_output; conf_output.label = item.first; From 31a09cec00fe13252fa6ee985716a7e90de28ab4 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Tue, 17 Jul 2018 14:51:50 -0400 Subject: [PATCH 4/4] Replaced deprecated BOOST_MESSAGE with BOOST_TEST_MESSAGE. --- tests/cli/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index 5695e0e801..672a8b8600 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -471,7 +471,7 @@ BOOST_AUTO_TEST_CASE( cli_confidential_tx_test ) generate_block(app1); head_block++; // ** Check head block: - BOOST_MESSAGE("Check that all expected blocks have processed"); + BOOST_TEST_MESSAGE("Check that all expected blocks have processed"); dynamic_global_property_object dgp = W.get_dynamic_global_properties(); BOOST_CHECK(dgp.head_block_number == head_block);