@ -88,6 +88,24 @@ namespace rct {
return proof ;
}
Bulletproof proveRangeBulletproof_old ( key & C , key & mask , uint64_t amount )
{
mask = rct : : skGen ( ) ;
Bulletproof proof = bulletproof_PROVE_old ( amount , mask ) ;
CHECK_AND_ASSERT_THROW_MES ( proof . V . size ( ) = = 1 , " V has not exactly one element " ) ;
C = proof . V [ 0 ] ;
return proof ;
}
Bulletproof proveRangeBulletproof_old ( keyV & C , keyV & masks , const std : : vector < uint64_t > & amounts )
{
masks = rct : : skvGen ( amounts . size ( ) ) ;
Bulletproof proof = bulletproof_PROVE_old ( amounts , masks ) ;
CHECK_AND_ASSERT_THROW_MES ( proof . V . size ( ) = = amounts . size ( ) , " V does not have the expected size " ) ;
C = proof . V ;
return proof ;
}
bool verBulletproof ( const Bulletproof & proof )
{
try { return bulletproof_VERIFY ( proof ) ; }
@ -102,6 +120,20 @@ namespace rct {
catch ( . . . ) { return false ; }
}
bool verBulletproof_old ( const Bulletproof & proof )
{
try { return bulletproof_VERIFY_old ( proof ) ; }
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch ( . . . ) { return false ; }
}
bool verBulletproof_old ( const std : : vector < const Bulletproof * > & proofs )
{
try { return bulletproof_VERIFY_old ( proofs ) ; }
// we can get deep throws from ge_frombytes_vartime if input isn't valid
catch ( . . . ) { return false ; }
}
//Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean ( const key64 x , const key64 P1 , const key64 P2 , const bits indices ) {
key64 L [ 2 ] , alpha ;
@ -416,7 +448,7 @@ namespace rct {
hashes . push_back ( hash2rct ( h ) ) ;
keyV kv ;
if ( rv . type = = RCTTypeBulletproof )
if ( rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeSimpleBulletproof | | rv . type = = RCTTypeFullBulletproof )
{
kv . reserve ( ( 6 * 2 + 9 ) * rv . p . bulletproofs . size ( ) ) ;
for ( const auto & p : rv . p . bulletproofs )
@ -677,7 +709,8 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
// Note: For txn fees, the last index in the amounts vector should contain that
// Thus the amounts vector will be "one" longer than the destinations vectort
rctSig genRct ( const key & message , const ctkeyV & inSk , const keyV & destinations , const vector < xmr_amount > & amounts , const ctkeyM & mixRing , const keyV & amount_keys , const multisig_kLRki * kLRki , multisig_out * msout , unsigned int index , ctkeyV & outSk , hw : : device & hwdev ) {
rctSig genRct ( const key & message , const ctkeyV & inSk , const keyV & destinations , const vector < xmr_amount > & amounts , const ctkeyM & mixRing , const keyV & amount_keys , const multisig_kLRki * kLRki , multisig_out * msout , unsigned int index , ctkeyV & outSk , RangeProofType range_proof_type , hw : : device & hwdev ) {
const bool bulletproof = range_proof_type ! = RangeProofBorromean ;
CHECK_AND_ASSERT_THROW_MES ( amounts . size ( ) = = destinations . size ( ) | | amounts . size ( ) = = destinations . size ( ) + 1 , " Different number of amounts/destinations " ) ;
CHECK_AND_ASSERT_THROW_MES ( amount_keys . size ( ) = = destinations . size ( ) , " Different number of amount_keys/destinations " ) ;
CHECK_AND_ASSERT_THROW_MES ( index < mixRing . size ( ) , " Bad index into mixRing " ) ;
@ -687,9 +720,10 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES ( ( kLRki & & msout ) | | ( ! kLRki & & ! msout ) , " Only one of kLRki/msout is present " ) ;
rctSig rv ;
rv . type = RCTTypeFull;
rv . type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
rv . message = message ;
rv . outPk . resize ( destinations . size ( ) ) ;
if ( ! bulletproof )
rv . p . rangeSigs . resize ( destinations . size ( ) ) ;
rv . ecdhInfo . resize ( destinations . size ( ) ) ;
@ -699,11 +733,46 @@ namespace rct {
for ( i = 0 ; i < destinations . size ( ) ; i + + ) {
//add destination to sig
rv . outPk [ i ] . dest = copy ( destinations [ i ] ) ;
//compute range proof
//compute range proof (bulletproofs are done later)
if ( ! bulletproof )
{
rv . p . rangeSigs [ i ] = proveRange ( rv . outPk [ i ] . mask , outSk [ i ] . mask , amounts [ i ] ) ;
# ifdef DBG
CHECK_AND_ASSERT_THROW_MES ( verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) , " verRange failed on newly created proof " ) ;
# endif
}
}
rv . p . bulletproofs . clear ( ) ;
if ( bulletproof )
{
std : : vector < uint64_t > proof_amounts ;
size_t amounts_proved = 0 ;
while ( amounts_proved < amounts . size ( ) )
{
size_t batch_size = 1 ;
if ( range_proof_type = = RangeProofMultiOutputBulletproof )
while ( batch_size * 2 + amounts_proved < = amounts . size ( ) )
batch_size * = 2 ;
rct : : keyV C , masks ;
std : : vector < uint64_t > batch_amounts ( batch_size ) ;
for ( i = 0 ; i < batch_size ; + + i )
batch_amounts [ i ] = amounts [ i + amounts_proved ] ;
rv . p . bulletproofs . push_back ( proveRangeBulletproof_old ( C , masks , batch_amounts ) ) ;
# ifdef DBG
CHECK_AND_ASSERT_THROW_MES ( verBulletproof_old ( rv . p . bulletproofs . back ( ) ) , " verBulletproof failed on newly created proof " ) ;
# endif
for ( i = 0 ; i < batch_size ; + + i )
{
rv . outPk [ i + amounts_proved ] . mask = C [ i ] ;
outSk [ i + amounts_proved ] . mask = masks [ i ] ;
}
amounts_proved + = batch_size ;
}
}
for ( i = 0 ; i < outSk . size ( ) ; + + i )
{
//mask amount and mask
rv . ecdhInfo [ i ] . mask = copy ( outSk [ i ] . mask ) ;
rv . ecdhInfo [ i ] . amount = d2h ( amounts [ i ] ) ;
@ -733,10 +802,10 @@ namespace rct {
ctkeyM mixRing ;
ctkeyV outSk ;
tie ( mixRing , index ) = populateFromBlockchain ( inPk , mixin ) ;
return genRct ( message , inSk , destinations , amounts , mixRing , amount_keys , kLRki , msout , index , outSk , hwdev) ;
return genRct ( message , inSk , destinations , amounts , mixRing , amount_keys , kLRki , msout , index , outSk , RangeProofBorromean, hwdev) ;
}
//RCT simple
//RCT simple NEW
//for post-rct only
rctSig genRctSimple ( const key & message , const ctkeyV & inSk , const keyV & destinations , const vector < xmr_amount > & inamounts , const vector < xmr_amount > & outamounts , xmr_amount txnFee , const ctkeyM & mixRing , const keyV & amount_keys , const std : : vector < multisig_kLRki > * kLRki , multisig_out * msout , const std : : vector < unsigned int > & index , ctkeyV & outSk , RangeProofType range_proof_type , hw : : device & hwdev ) {
const bool bulletproof = range_proof_type ! = RangeProofBorromean ;
@ -889,6 +958,104 @@ namespace rct {
return genRctSimple ( message , inSk , destinations , inamounts , outamounts , txnFee , mixRing , amount_keys , kLRki , msout , index , outSk , RangeProofBorromean , hwdev ) ;
}
//RCT simple
//for post-rct only
rctSig genRctSimple_old ( const key & message , const ctkeyV & inSk , const keyV & destinations , const vector < xmr_amount > & inamounts , const vector < xmr_amount > & outamounts , xmr_amount txnFee , const ctkeyM & mixRing , const keyV & amount_keys , const std : : vector < multisig_kLRki > * kLRki , multisig_out * msout , const std : : vector < unsigned int > & index , ctkeyV & outSk , RangeProofType range_proof_type , hw : : device & hwdev ) {
const bool bulletproof = range_proof_type ! = RangeProofBorromean ;
CHECK_AND_ASSERT_THROW_MES ( inamounts . size ( ) > 0 , " Empty inamounts " ) ;
CHECK_AND_ASSERT_THROW_MES ( inamounts . size ( ) = = inSk . size ( ) , " Different number of inamounts/inSk " ) ;
CHECK_AND_ASSERT_THROW_MES ( outamounts . size ( ) = = destinations . size ( ) , " Different number of amounts/destinations " ) ;
CHECK_AND_ASSERT_THROW_MES ( amount_keys . size ( ) = = destinations . size ( ) , " Different number of amount_keys/destinations " ) ;
CHECK_AND_ASSERT_THROW_MES ( index . size ( ) = = inSk . size ( ) , " Different number of index/inSk " ) ;
CHECK_AND_ASSERT_THROW_MES ( mixRing . size ( ) = = inSk . size ( ) , " Different number of mixRing/inSk " ) ;
for ( size_t n = 0 ; n < mixRing . size ( ) ; + + n ) {
CHECK_AND_ASSERT_THROW_MES ( index [ n ] < mixRing [ n ] . size ( ) , " Bad index into mixRing " ) ;
}
CHECK_AND_ASSERT_THROW_MES ( ( kLRki & & msout ) | | ( ! kLRki & & ! msout ) , " Only one of kLRki/msout is present " ) ;
if ( kLRki & & msout ) {
CHECK_AND_ASSERT_THROW_MES ( kLRki - > size ( ) = = inamounts . size ( ) , " Mismatched kLRki/inamounts sizes " ) ;
}
rctSig rv ;
rv . type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple ;
rv . message = message ;
rv . outPk . resize ( destinations . size ( ) ) ;
if ( bulletproof )
rv . p . bulletproofs . resize ( destinations . size ( ) ) ;
else
rv . p . rangeSigs . resize ( destinations . size ( ) ) ;
rv . ecdhInfo . resize ( destinations . size ( ) ) ;
size_t i ;
keyV masks ( destinations . size ( ) ) ; //sk mask..
outSk . resize ( destinations . size ( ) ) ;
key sumout = zero ( ) ;
for ( i = 0 ; i < destinations . size ( ) ; i + + ) {
//add destination to sig
rv . outPk [ i ] . dest = copy ( destinations [ i ] ) ;
//compute range proof
if ( bulletproof )
rv . p . bulletproofs [ i ] = proveRangeBulletproof_old ( rv . outPk [ i ] . mask , outSk [ i ] . mask , outamounts [ i ] ) ;
else
rv . p . rangeSigs [ i ] = proveRange ( rv . outPk [ i ] . mask , outSk [ i ] . mask , outamounts [ i ] ) ;
# ifdef DBG
if ( bulletproof )
CHECK_AND_ASSERT_THROW_MES ( verBulletproof_old ( rv . p . bulletproofs [ i ] ) , " verBulletproof failed on newly created proof " ) ;
else
CHECK_AND_ASSERT_THROW_MES ( verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) , " verRange failed on newly created proof " ) ;
# endif
sc_add ( sumout . bytes , outSk [ i ] . mask . bytes , sumout . bytes ) ;
//mask amount and mask
rv . ecdhInfo [ i ] . mask = copy ( outSk [ i ] . mask ) ;
rv . ecdhInfo [ i ] . amount = d2h ( outamounts [ i ] ) ;
hwdev . ecdhEncode ( rv . ecdhInfo [ i ] , amount_keys [ i ] ) ;
}
//set txn fee
rv . txnFee = txnFee ;
// TODO: unused ??
// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv . mixRing = mixRing ;
keyV & pseudoOuts = bulletproof ? rv . p . pseudoOuts : rv . pseudoOuts ;
pseudoOuts . resize ( inamounts . size ( ) ) ;
rv . p . MGs . resize ( inamounts . size ( ) ) ;
key sumpouts = zero ( ) ; //sum pseudoOut masks
keyV a ( inamounts . size ( ) ) ;
for ( i = 0 ; i < inamounts . size ( ) - 1 ; i + + ) {
skGen ( a [ i ] ) ;
sc_add ( sumpouts . bytes , a [ i ] . bytes , sumpouts . bytes ) ;
genC ( pseudoOuts [ i ] , a [ i ] , inamounts [ i ] ) ;
}
rv . mixRing = mixRing ;
sc_sub ( a [ i ] . bytes , sumout . bytes , sumpouts . bytes ) ;
genC ( pseudoOuts [ i ] , a [ i ] , inamounts [ i ] ) ;
DP ( pseudoOuts [ i ] ) ;
key full_message = get_pre_mlsag_hash ( rv , hwdev ) ;
if ( msout )
msout - > c . resize ( inamounts . size ( ) ) ;
for ( i = 0 ; i < inamounts . size ( ) ; i + + ) {
rv . p . MGs [ i ] = proveRctMGSimple ( full_message , rv . mixRing [ i ] , inSk [ i ] , a [ i ] , pseudoOuts [ i ] , kLRki ? & ( * kLRki ) [ i ] : NULL , msout ? & msout - > c [ i ] : NULL , index [ i ] , hwdev ) ;
}
return rv ;
}
rctSig genRctSimple_old ( const key & message , const ctkeyV & inSk , const ctkeyV & inPk , const keyV & destinations , const vector < xmr_amount > & inamounts , const vector < xmr_amount > & outamounts , const keyV & amount_keys , const std : : vector < multisig_kLRki > * kLRki , multisig_out * msout , xmr_amount txnFee , unsigned int mixin , hw : : device & hwdev ) {
std : : vector < unsigned int > index ;
index . resize ( inPk . size ( ) ) ;
ctkeyM mixRing ;
ctkeyV outSk ;
mixRing . resize ( inPk . size ( ) ) ;
for ( size_t i = 0 ; i < inPk . size ( ) ; + + i ) {
mixRing [ i ] . resize ( mixin + 1 ) ;
index [ i ] = populateFromBlockchainSimple ( mixRing [ i ] , inPk [ i ] , mixin ) ;
}
return genRctSimple_old ( message , inSk , destinations , inamounts , outamounts , txnFee , mixRing , amount_keys , kLRki , msout , index , outSk , RangeProofBorromean , hwdev ) ;
}
//RingCT protocol
//genRct:
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
@ -901,10 +1068,13 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
bool verRct ( const rctSig & rv , bool semantics ) {
PERF_TIMER ( verRct ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull , false , " verRct called on non-full rctSig " ) ;
const bool bulletproof = is_rct_bulletproof ( rv . type ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull | | rv . type = = RCTTypeFullBulletproof , false , " verRct called on non-full rctSig " ) ;
if ( semantics )
{
if ( rv . type = = RCTTypeBulletproof )
CHECK_AND_ASSERT_MES ( rv . outPk . size ( ) = = rv . p . rangeSigs . size ( ) , false , " Mismatched sizes of outPk and rv.p.rangeSigs " ) ;
else
CHECK_AND_ASSERT_MES ( rv . outPk . size ( ) = = rv . ecdhInfo . size ( ) , false , " Mismatched sizes of outPk and rv.ecdhInfo " ) ;
CHECK_AND_ASSERT_MES ( rv . p . MGs . size ( ) = = 1 , false , " full rctSig has not one MG " ) ;
}
@ -919,8 +1089,19 @@ namespace rct {
if ( semantics ) {
tools : : threadpool & tpool = tools : : threadpool : : getInstance ( ) ;
tools : : threadpool : : waiter waiter ;
std : : deque < bool > results ( rv. outPk . size ( ) , false ) ;
std : : deque < bool > results ( bulletproof ? rv . p . bulletproofs . size ( ) : rv. outPk . size ( ) , false ) ;
DP ( " range proofs verified? " ) ;
if ( bulletproof & & rv . type = = RCTTypeBulletproof )
{
for ( size_t i = 0 ; i < rv . p . bulletproofs . size ( ) ; i + + )
tpool . submit ( & waiter , [ & , i ] { results [ i ] = verBulletproof ( rv . p . bulletproofs [ i ] ) ; } ) ;
}
else if ( bulletproof )
{
for ( size_t i = 0 ; i < rv . p . bulletproofs . size ( ) ; i + + )
tpool . submit ( & waiter , [ & , i ] { results [ i ] = verBulletproof_old ( rv . p . bulletproofs [ i ] ) ; } ) ;
}
else
for ( size_t i = 0 ; i < rv . outPk . size ( ) ; i + + )
tpool . submit ( & waiter , [ & , i ] { results [ i ] = verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) ; } ) ;
waiter . wait ( & tpool ) ;
@ -976,11 +1157,12 @@ namespace rct {
{
CHECK_AND_ASSERT_MES ( rvp , false , " rctSig pointer is NULL " ) ;
const rctSig & rv = * rvp ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof , false , " verRctSemanticsSimple called on non simple rctSig " ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeSimpleBulletproof , false , " verRctSemanticsSimple called on non simple rctSig " ) ;
const bool bulletproof = is_rct_bulletproof ( rv . type ) ;
if ( bulletproof )
{
if ( rv . type = = RCTTypeBulletproof )
CHECK_AND_ASSERT_MES ( rv . outPk . size ( ) = = n_bulletproof_amounts ( rv . p . bulletproofs ) , false , " Mismatched sizes of outPk and bulletproofs " ) ;
else if ( bulletproof )
{
CHECK_AND_ASSERT_MES ( rv . p . pseudoOuts . size ( ) = = rv . p . MGs . size ( ) , false , " Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs " ) ;
CHECK_AND_ASSERT_MES ( rv . pseudoOuts . empty ( ) , false , " rv.pseudoOuts is not empty " ) ;
}
@ -1034,12 +1216,23 @@ namespace rct {
offset + = rv . p . rangeSigs . size ( ) ;
}
}
for ( const rctSig * rvp : rvv )
{
const rctSig & rv = * rvp ;
if ( rv . type ! = RCTTypeBulletproof ) {
if ( ! proofs . empty ( ) & & ! verBulletproof_old ( proofs ) )
{
LOG_PRINT_L1 ( " Aggregate range proof verified failed " ) ;
return false ;
}
} else {
if ( ! proofs . empty ( ) & & ! verBulletproof ( proofs ) )
{
LOG_PRINT_L1 ( " Aggregate range proof verified failed " ) ;
return false ;
}
}
}
waiter . wait ( & tpool ) ;
for ( size_t i = 0 ; i < results . size ( ) ; + + i ) {
if ( ! results [ i ] ) {
@ -1075,7 +1268,7 @@ namespace rct {
{
PERF_TIMER ( verRctNonSemanticsSimple ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof , false , " verRctNonSemanticsSimple called on non simple rctSig " ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeSimpleBulletproof , false , " verRctNonSemanticsSimple called on non simple rctSig " ) ;
const bool bulletproof = is_rct_bulletproof ( rv . type ) ;
// semantics check is early, and mixRing/MGs aren't resolved yet
if ( bulletproof )
@ -1135,7 +1328,7 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct ( const rctSig & rv , const key & sk , unsigned int i , key & mask , hw : : device & hwdev ) {
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull , false , " decodeRct called on non-full rctSig " ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull | | rv . type = = RCTTypeFullBulletproof , false , " decodeRct called on non-full rctSig " ) ;
CHECK_AND_ASSERT_THROW_MES ( i < rv . ecdhInfo . size ( ) , " Bad index " ) ;
CHECK_AND_ASSERT_THROW_MES ( rv . outPk . size ( ) = = rv . ecdhInfo . size ( ) , " Mismatched sizes of rv.outPk and rv.ecdhInfo " ) ;
@ -1165,7 +1358,7 @@ namespace rct {
}
xmr_amount decodeRctSimple ( const rctSig & rv , const key & sk , unsigned int i , key & mask , hw : : device & hwdev ) {
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof , false , " decodeRct called on non simple rctSig " ) ;
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeSimpleBulletproof , false , " decodeRct called on non simple rctSig " ) ;
CHECK_AND_ASSERT_THROW_MES ( i < rv . ecdhInfo . size ( ) , " Bad index " ) ;
CHECK_AND_ASSERT_THROW_MES ( rv . outPk . size ( ) = = rv . ecdhInfo . size ( ) , " Mismatched sizes of rv.outPk and rv.ecdhInfo " ) ;
@ -1195,12 +1388,12 @@ namespace rct {
}
bool signMultisig ( rctSig & rv , const std : : vector < unsigned int > & indices , const keyV & k , const multisig_out & msout , const key & secret_key ) {
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull | | rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof ,
CHECK_AND_ASSERT_MES ( rv . type = = RCTTypeFull | | rv . type = = RCTTypeSimple | | rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeFullBulletproof | | rv . type = = RCTTypeSimpleBulletproof ,
false , " unsupported rct type " ) ;
CHECK_AND_ASSERT_MES ( indices . size ( ) = = k . size ( ) , false , " Mismatched k/indices sizes " ) ;
CHECK_AND_ASSERT_MES ( k . size ( ) = = rv . p . MGs . size ( ) , false , " Mismatched k/MGs size " ) ;
CHECK_AND_ASSERT_MES ( k . size ( ) = = msout . c . size ( ) , false , " Mismatched k/msout.c size " ) ;
if ( rv . type = = RCTTypeFull )
if ( rv . type = = RCTTypeFull | | rv . type = = RCTTypeFullBulletproof )
{
CHECK_AND_ASSERT_MES ( rv . p . MGs . size ( ) = = 1 , false , " MGs not a single element " ) ;
}