@ -132,6 +132,24 @@ namespace rct {
return proof ;
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 )
bool verBulletproof ( const Bulletproof & proof )
{
{
try { return bulletproof_VERIFY ( proof ) ; }
try { return bulletproof_VERIFY ( proof ) ; }
@ -146,6 +164,20 @@ namespace rct {
catch ( . . . ) { return false ; }
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 ; }
}
BulletproofPlus proveRangeBulletproofPlus ( keyV & C , keyV & masks , const std : : vector < uint64_t > & amounts , epee : : span < const key > sk , hw : : device & hwdev )
BulletproofPlus proveRangeBulletproofPlus ( keyV & C , keyV & masks , const std : : vector < uint64_t > & amounts , epee : : span < const key > sk , hw : : device & hwdev )
{
{
CHECK_AND_ASSERT_THROW_MES ( amounts . size ( ) = = sk . size ( ) , " Invalid amounts/sk sizes " ) ;
CHECK_AND_ASSERT_THROW_MES ( amounts . size ( ) = = sk . size ( ) , " Invalid amounts/sk sizes " ) ;
@ -616,7 +648,7 @@ namespace rct {
hashes . push_back ( hash2rct ( h ) ) ;
hashes . push_back ( hash2rct ( h ) ) ;
keyV kv ;
keyV kv ;
if ( rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeBulletproof2 | | rv . type = = RCTType CLSAG)
if ( rv . type = = RCTTypeBulletproof | | rv . type = = RCTTypeBulletproof2 | | rv . type = = RCTType SimpleBulletproof | | rv . type = = RCTTypeFullBulletproof | | rv . type = = RCTType CLSAG)
{
{
kv . reserve ( ( 6 * 2 + 9 ) * rv . p . bulletproofs . size ( ) ) ;
kv . reserve ( ( 6 * 2 + 9 ) * rv . p . bulletproofs . size ( ) ) ;
for ( const auto & p : rv . p . bulletproofs )
for ( const auto & p : rv . p . bulletproofs )
@ -1044,6 +1076,7 @@ namespace rct {
// Note: For txn fees, the last index in the amounts vector should contain that
// 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
// 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 , unsigned int index , ctkeyV & outSk , const RCTConfig & rct_config , 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 , unsigned int index , ctkeyV & outSk , const RCTConfig & rct_config , hw : : device & hwdev ) {
const bool bulletproof = rct_config . 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 ( 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 ( amount_keys . size ( ) = = destinations . size ( ) , " Different number of amount_keys/destinations " ) ;
CHECK_AND_ASSERT_THROW_MES ( index < mixRing . size ( ) , " Bad index into mixRing " ) ;
CHECK_AND_ASSERT_THROW_MES ( index < mixRing . size ( ) , " Bad index into mixRing " ) ;
@ -1053,9 +1086,10 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES ( inSk . size ( ) < 2 , " genRct is not suitable for 2+ rings " ) ;
CHECK_AND_ASSERT_THROW_MES ( inSk . size ( ) < 2 , " genRct is not suitable for 2+ rings " ) ;
rctSig rv ;
rctSig rv ;
rv . type = RCTTypeFull;
rv . type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
rv . message = message ;
rv . message = message ;
rv . outPk . resize ( destinations . size ( ) ) ;
rv . outPk . resize ( destinations . size ( ) ) ;
if ( ! bulletproof )
rv . p . rangeSigs . resize ( destinations . size ( ) ) ;
rv . p . rangeSigs . resize ( destinations . size ( ) ) ;
rv . ecdhInfo . resize ( destinations . size ( ) ) ;
rv . ecdhInfo . resize ( destinations . size ( ) ) ;
@ -1066,10 +1100,45 @@ namespace rct {
//add destination to sig
//add destination to sig
rv . outPk [ i ] . dest = copy ( destinations [ i ] ) ;
rv . outPk [ i ] . dest = copy ( destinations [ i ] ) ;
//compute range proof
//compute range proof
if ( ! bulletproof )
{
rv . p . rangeSigs [ i ] = proveRange ( rv . outPk [ i ] . mask , outSk [ i ] . mask , amounts [ i ] ) ;
rv . p . rangeSigs [ i ] = proveRange ( rv . outPk [ i ] . mask , outSk [ i ] . mask , amounts [ i ] ) ;
# ifdef DBG
# ifdef DBG
CHECK_AND_ASSERT_THROW_MES ( verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) , " verRange failed on newly created proof " ) ;
CHECK_AND_ASSERT_THROW_MES ( verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) , " verRange failed on newly created proof " ) ;
# endif
# 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 ( rct_config . 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
//mask amount and mask
rv . ecdhInfo [ i ] . mask = copy ( outSk [ i ] . mask ) ;
rv . ecdhInfo [ i ] . mask = copy ( outSk [ i ] . mask ) ;
rv . ecdhInfo [ i ] . amount = d2h ( amounts [ i ] ) ;
rv . ecdhInfo [ i ] . amount = d2h ( amounts [ i ] ) ;
@ -1311,6 +1380,98 @@ namespace rct {
return genRctSimple ( message , inSk , destinations , inamounts , outamounts , txnFee , mixRing , amount_keys , index , outSk , rct_config , hwdev ) ;
return genRctSimple ( message , inSk , destinations , inamounts , outamounts , txnFee , mixRing , amount_keys , index , outSk , rct_config , 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 < unsigned int > & index , ctkeyV & outSk , const RCTConfig & rct_config , hw : : device & hwdev ) {
const bool bulletproof = rct_config . 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 " ) ;
}
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 ] , rv . type = = RCTTypeSimpleBulletproof ) ;
}
//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 ) ;
for ( i = 0 ; i < inamounts . size ( ) ; i + + ) {
rv . p . MGs [ i ] = proveRctMGSimple ( full_message , rv . mixRing [ i ] , inSk [ i ] , a [ i ] , pseudoOuts [ i ] , 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 , xmr_amount txnFee , unsigned int mixin , const RCTConfig & rct_config , 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 , index , outSk , rct_config , hwdev ) ;
}
//RingCT protocol
//RingCT protocol
//genRct:
//genRct:
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
// creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
@ -1323,10 +1484,13 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
// must know the destination private key to find the correct amount, else will return a random number
bool verRct ( const rctSig & rv , bool semantics ) {
bool verRct ( const rctSig & rv , bool semantics ) {
PERF_TIMER ( verRct ) ;
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 ( 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 " ) ;
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 . 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 " ) ;
CHECK_AND_ASSERT_MES ( rv . p . MGs . size ( ) = = 1 , false , " full rctSig has not one MG " ) ;
}
}
@ -1341,10 +1505,23 @@ namespace rct {
if ( semantics ) {
if ( semantics ) {
tools : : threadpool & tpool = tools : : threadpool : : getInstanceForCompute ( ) ;
tools : : threadpool & tpool = tools : : threadpool : : getInstanceForCompute ( ) ;
tools : : threadpool : : waiter waiter ( tpool ) ;
tools : : threadpool : : waiter waiter ( tpool ) ;
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? " ) ;
DP ( " range proofs verified? " ) ;
if ( rct : : is_rct_new_bulletproof ( rv . type ) )
{
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 + + )
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 ] ) ; } ) ;
tpool . submit ( & waiter , [ & , i ] { results [ i ] = verRange ( rv . outPk [ i ] . mask , rv . p . rangeSigs [ i ] ) ; } ) ;
}
if ( ! waiter . wait ( ) )
if ( ! waiter . wait ( ) )
return false ;
return false ;
@ -1598,7 +1775,7 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment
// 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
// 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 ) {
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 ( 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 " ) ;
CHECK_AND_ASSERT_THROW_MES ( rv . outPk . size ( ) = = rv . ecdhInfo . size ( ) , " Mismatched sizes of rv.outPk and rv.ecdhInfo " ) ;