@@ -388,3 +388,98 @@ BOOST_AUTO_TEST_CASE( cli_set_voting_proxy )
388
388
}
389
389
app1->shutdown ();
390
390
}
391
+
392
+ // /////////////////
393
+ // Test blind transactions and mantissa length of range proofs.
394
+ // /////////////////
395
+ BOOST_AUTO_TEST_CASE ( cli_confidential_tx_test )
396
+ {
397
+ using namespace graphene ::chain;
398
+ using namespace graphene ::app;
399
+ using namespace graphene ::wallet;
400
+ std::shared_ptr<graphene::app::application> app1;
401
+ try {
402
+
403
+ // ** Start a Graphene chain and API server:
404
+ fc::temp_directory app_dir ( graphene::utilities::temp_directory_path () );
405
+ int server_port_number;
406
+ app1 = start_application (app_dir, server_port_number);
407
+ unsigned int head_block = 0 ;
408
+
409
+ // ** Connect a Wallet to the API server, and generate three BLIND accounts:
410
+ client_connection con (app1, app_dir, server_port_number);
411
+ auto & W = *con.wallet_api_ptr ; // Wallet alias
412
+ BOOST_TEST_MESSAGE (" Setting wallet password" );
413
+ W.set_password (" supersecret" );
414
+ W.unlock (" supersecret" );
415
+ BOOST_TEST_MESSAGE (" Creating blind accounts" );
416
+ graphene::wallet::brain_key_info bki_nathan = W.suggest_brain_key ();
417
+ graphene::wallet::brain_key_info bki_alice = W.suggest_brain_key ();
418
+ graphene::wallet::brain_key_info bki_bob = W.suggest_brain_key ();
419
+ W.create_blind_account (" nathan" , bki_nathan.brain_priv_key );
420
+ W.create_blind_account (" alice" , bki_alice.brain_priv_key );
421
+ W.create_blind_account (" bob" , bki_bob.brain_priv_key );
422
+ BOOST_CHECK (W.get_blind_accounts ().size () == 3 );
423
+
424
+ // ** Block 1: Import Nathan account:
425
+ BOOST_TEST_MESSAGE (" Importing nathan key and balance" );
426
+ std::vector<std::string> nathan_keys{" 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" };
427
+ W.import_key (" nathan" , nathan_keys[0 ]);
428
+ W.import_balance (" nathan" , nathan_keys, true );
429
+ generate_block (app1); head_block++;
430
+
431
+ // ** Block 2: Nathan will blind 100M BTS:
432
+ BOOST_TEST_MESSAGE (" Blinding a large balance" );
433
+ W.transfer_to_blind (" nathan" , " BTS" , {{" nathan" ," 100000000" }}, true );
434
+ BOOST_CHECK ( W.get_blind_balances (" nathan" )[0 ].amount == 10000000000000 );
435
+ generate_block (app1); head_block++;
436
+
437
+ // ** Block 3: Nathan will send 1M BTS to alice and 10K BTS to bob. We
438
+ // then confirm that balances are received, and then analyze the range
439
+ // prooofs to make sure the mantissa length does not reveal approximate
440
+ // balance (issue #480).
441
+ std::map<std::string, share_type> to_list = {{" alice" ,100000000000 },
442
+ {" bob" , 1000000000 }};
443
+ vector<blind_confirmation> bconfs;
444
+ asset_object core_asset = W.get_asset (" 1.3.0" );
445
+ BOOST_TEST_MESSAGE (" Sending blind transactions to alice and bob" );
446
+ for (auto to : to_list) {
447
+ string amount = core_asset.amount_to_string (to.second );
448
+ bconfs.push_back (W.blind_transfer (" nathan" ,to.first ,amount,core_asset.symbol ,true ));
449
+ BOOST_CHECK ( W.get_blind_balances (to.first )[0 ].amount == to.second );
450
+ }
451
+ BOOST_TEST_MESSAGE (" Inspecting range proof mantissa lengths" );
452
+ vector<int > rp_mantissabits;
453
+ for (auto conf : bconfs) {
454
+ for (auto out : conf.trx .operations [0 ].get <blind_transfer_operation>().outputs ) {
455
+ rp_mantissabits.push_back (1 +out.range_proof [1 ]); // 2nd byte encodes mantissa length
456
+ }
457
+ }
458
+ // We are checking the mantissa length of the range proofs for several Pedersen
459
+ // commitments of varying magnitude. We don't want the mantissa lengths to give
460
+ // away magnitude. Deprecated wallet behavior was to use "just enough" mantissa
461
+ // bits to prove range, but this gives away value to within a factor of two. As a
462
+ // naive test, we assume that if all mantissa lengths are equal, then they are not
463
+ // revealing magnitude. However, future more-sophisticated wallet behavior
464
+ // *might* randomize mantissa length to achieve some space savings in the range
465
+ // proof. The following test will fail in that case and a more sophisticated test
466
+ // will be needed.
467
+ auto adjacent_unequal = std::adjacent_find (rp_mantissabits.begin (),
468
+ /* find unequal adjacent values */ rp_mantissabits.end (),
469
+ std::not_equal_to<int >());
470
+ BOOST_CHECK (adjacent_unequal == rp_mantissabits.end ());
471
+ generate_block (app1); head_block++;
472
+
473
+ // ** Check head block:
474
+ BOOST_TEST_MESSAGE (" Check that all expected blocks have processed" );
475
+ dynamic_global_property_object dgp = W.get_dynamic_global_properties ();
476
+ BOOST_CHECK (dgp.head_block_number == head_block);
477
+
478
+ // wait for everything to finish up
479
+ fc::usleep (fc::seconds (1 ));
480
+ } catch ( fc::exception & e ) {
481
+ edump ((e.to_detail_string ()));
482
+ throw ;
483
+ }
484
+ app1->shutdown ();
485
+ }
0 commit comments