@ -960,6 +960,13 @@ int64_t CWallet::NullifierCount()
return pcoinsTip - > getNullifiers ( ) . size ( ) ;
}
void CWallet : : AddToSifted ( const uint256 & wtxid )
{
CWalletTx & wtx = mapWallet . at ( wtxid ) ;
if ( ! wtx . mapSaplingNoteData . empty ( ) )
setSiftedSapling . emplace ( wtxid ) ;
}
void CWallet : : ClearNoteWitnessCache ( )
{
@ -1132,6 +1139,13 @@ int CWallet::VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessO
return nMinimumHeight ;
}
static SaplingWitness AppendSingleSaplingCommitment ( const SaplingWitness & witness , const uint256 & commitment )
{
SaplingWitness sw ( witness ) ;
sw . append ( commitment ) ;
return sw ;
}
void CWallet : : BuildWitnessCache ( const CBlockIndex * pindex , bool witnessOnly )
{
@ -1169,12 +1183,26 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
pcoinsTip - > GetSaplingAnchorAt ( saplingRoot , saplingTree ) ;
//Cycle through blocks and transactions building sapling tree until the commitment needed is reached
const CBlock * pblock ;
CBlock block ;
if ( ! ReadBlockFromDisk ( block , pblockindex , 1 ) ) {
throw std : : runtime_error (
strprintf ( " Cannot read block height %d (%s) from disk " , pindex - > GetHeight ( ) , pindex - > GetBlockHash ( ) . GetHex ( ) ) ) ;
}
else {
pblock = & block ;
}
std : : vector < uint256 > vSaplingCommitments ;
std : : vector < SaplingNoteData * > vSaplingNoteData ;
if ( setSiftedSapling . size ( ) ) {
for ( const CTransaction & tx : pblock - > vtx ) {
for ( const OutputDescription & outdesc : tx . vShieldedOutput ) {
vSaplingCommitments . emplace_back ( outdesc . cm ) ;
}
}
for ( std : : pair < const uint256 , CWalletTx > & wtxItem : mapWallet ) {
if ( wtxItem . second . mapSaplingNoteData . empty ( ) )
@ -1192,18 +1220,29 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
while ( nd - > witnesses . size ( ) > WITNESS_CACHE_SIZE ) {
nd - > witnesses . pop_back ( ) ;
}
for ( const CTransaction & tx : block . vtx ) {
for ( uint32_t i = 0 ; i < tx . vShieldedOutput . size ( ) ; i + + ) {
const uint256 & note_commitment = tx . vShieldedOutput [ i ] . cm ;
nd - > witnesses . front ( ) . append ( note_commitment ) ;
}
}
// for async append of commitments
vSaplingNoteData . push_back ( nd ) ;
nd - > witnessHeight = pblockindex - > GetHeight ( ) ;
}
}
}
// Parallelization with std::async
std : : vector < std : : future < SaplingWitness > > vSaplingWitnessFutures ;
for ( const auto & commitment : vSaplingCommitments ) {
for ( auto pnd : vSaplingNoteData ) {
vSaplingWitnessFutures . emplace_back ( std : : async ( std : : launch : : async , AppendSingleSaplingCommitment , pnd - > witnesses . front ( ) , commitment ) ) ;
}
assert ( vSaplingWitnessFutures . size ( ) = = vSaplingNoteData . size ( ) ) ;
for ( int i = 0 ; i < vSaplingWitnessFutures . size ( ) ; i + + ) {
vSaplingNoteData . at ( i ) - > witnesses . front ( ) = vSaplingWitnessFutures . at ( i ) . get ( ) ;
}
vSaplingWitnessFutures . resize ( 0 ) ;
}
}
if ( pblockindex = = pindex )
@ -1212,7 +1251,7 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly)
pblockindex = chainActive . Next ( pblockindex ) ;
}
}
}
bool CWallet : : EncryptWallet ( const SecureString & strWalletPassphrase )
@ -1417,7 +1456,7 @@ void CWallet::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) {
if ( mapSaplingFullViewingKeys . count ( nd . ivk ) ! = 0 ) {
SaplingFullViewingKey fvk = mapSaplingFullViewingKeys . at ( nd . ivk ) ;
OutputDescription output = wtx . vShieldedOutput [ op . n ] ;
auto optPlaintext = SaplingNotePlaintext : : decrypt ( output . encCiphertext , nd . ivk , output . ephemeralKey , output . cm ) ;
auto optPlaintext = SaplingNotePlaintext : : decrypt ( Params ( ) . GetConsensus ( ) , chainActive . Height ( ) , output . encCiphertext , nd . ivk , output . ephemeralKey , output . cm ) ;
if ( ! optPlaintext ) {
// An item in mapSaplingNoteData must have already been successfully decrypted,
// otherwise the item would not exist in the first place.
@ -1466,6 +1505,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
mapWallet [ hash ] . BindWallet ( this ) ;
UpdateNullifierNoteMapWithTx ( mapWallet [ hash ] ) ;
AddToSpends ( hash ) ;
AddToSifted ( hash ) ;
}
else
{
@ -1558,9 +1598,13 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
// Write to disk
if ( fInsertedNew | | fUpdated )
{
AddToSifted ( hash ) ;
if ( ! wtx . WriteToDisk ( pwalletdb ) )
return false ;
}
// Break debit/credit balance caches:
wtx . MarkDirty ( ) ;
@ -1608,7 +1652,7 @@ bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx)
* If fUpdate is true , existing transactions will be updated .
*/
bool CWallet : : AddToWalletIfInvolvingMe ( const CTransaction & tx , const CBlock * pblock , bool fUpdate )
bool CWallet : : AddToWalletIfInvolvingMe ( const CTransaction & tx , const CBlock * pblock , const int nHeight , bool fUpdate )
{
if ( fDebug )
fprintf ( stderr , " %s: tx=%s \n " , __func__ , tx . GetHash ( ) . ToString ( ) . c_str ( ) ) ;
@ -1619,7 +1663,8 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
return false ;
bool fExisted = mapWallet . count ( tx . GetHash ( ) ) ! = 0 ;
if ( fExisted & & ! fUpdate ) return false ;
auto saplingNoteDataAndAddressesToAdd = FindMySaplingNotes ( tx ) ;
std : : pair < mapSaplingNoteData_t , SaplingIncomingViewingKeyMap > saplingNoteDataAndAddressesToAdd ;
saplingNoteDataAndAddressesToAdd = fAsyncNoteDecryption ? FindMySaplingNotesAsync ( tx , nHeight ) : FindMySaplingNotes ( tx ) ;
auto saplingNoteData = saplingNoteDataAndAddressesToAdd . first ;
auto addressesToAdd = saplingNoteDataAndAddressesToAdd . second ;
for ( const auto & addressToAdd : addressesToAdd ) {
@ -1689,12 +1734,12 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
return false ;
}
void CWallet : : SyncTransaction ( const CTransaction & tx , const CBlock * pblock )
void CWallet : : SyncTransaction ( const CTransaction & tx , const CBlock * pblock , const int nHeight )
{
LOCK ( cs_wallet ) ;
if ( fDebug )
fprintf ( stderr , " %s: tx=%s \n " , __func__ , tx . GetHash ( ) . ToString ( ) . c_str ( ) ) ;
if ( ! AddToWalletIfInvolvingMe ( tx , pblock , true ) )
if ( ! AddToWalletIfInvolvingMe ( tx , pblock , nHeight , true ) )
return ; // Not one of ours
MarkAffectedTransactionsDirty ( tx ) ;
@ -1777,7 +1822,7 @@ std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySap
bool found = false ;
for ( auto it = mapSaplingFullViewingKeys . begin ( ) ; it ! = mapSaplingFullViewingKeys . end ( ) ; + + it ) {
SaplingIncomingViewingKey ivk = it - > first ;
auto result = SaplingNotePlaintext : : decrypt ( output . encCiphertext , ivk , output . ephemeralKey , output . cm ) ;
auto result = SaplingNotePlaintext : : decrypt ( Params ( ) . GetConsensus ( ) , chainActive . Height ( ) , output . encCiphertext , ivk , output . ephemeralKey , output . cm ) ;
if ( result ) {
auto address = ivk . address ( result . get ( ) . d ) ;
if ( address & & mapSaplingIncomingViewingKeys . count ( address . get ( ) ) = = 0 ) {
@ -1796,7 +1841,7 @@ std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySap
if ( ! found ) {
for ( auto it = mapSaplingIncomingViewingKeys . begin ( ) ; it ! = mapSaplingIncomingViewingKeys . end ( ) ; + + it ) {
SaplingIncomingViewingKey ivk = it - > second ;
auto result = SaplingNotePlaintext : : decrypt ( output . encCiphertext , ivk , output . ephemeralKey , output . cm ) ;
auto result = SaplingNotePlaintext : : decrypt ( Params ( ) . GetConsensus ( ) , chainActive . Height ( ) , output . encCiphertext , ivk , output . ephemeralKey , output . cm ) ;
if ( ! result ) {
continue ;
}
@ -1814,6 +1859,103 @@ std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySap
return std : : make_pair ( noteData , viewingKeysToAdd ) ;
}
static std : : pair < mapSaplingNoteData_t , SaplingIncomingViewingKeyMap > DecryptSaplingNoteWorker ( const Consensus : : Params & consensus_params , const SaplingIncomingViewingKey & ivk , const OutputDescription & outdesc , const int & height , const uint256 & hash , const uint32_t & i )
{
mapSaplingNoteData_t noteData ;
SaplingIncomingViewingKeyMap viewingKeysToAdd ;
auto result = SaplingNotePlaintext : : decrypt ( consensus_params , height , outdesc . encCiphertext , ivk , outdesc . ephemeralKey , outdesc . cm ) ;
if ( result )
{
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op { hash , i } ;
SaplingNoteData nd ;
nd . ivk = ivk ;
noteData . insert ( std : : make_pair ( op , nd ) ) ;
auto address = ivk . address ( result . value ( ) . d ) ;
if ( address )
{
viewingKeysToAdd . insert ( make_pair ( address . value ( ) , ivk ) ) ;
}
}
return std : : make_pair ( noteData , viewingKeysToAdd ) ;
}
/**
* Finds all output notes in the given transaction that have been sent to
* SaplingPaymentAddresses in this wallet .
*
* It should never be necessary to call this method with a CWalletTx , because
* the result of FindMySaplingNotes ( for the addresses available at the time ) will
* already have been cached in CWalletTx . mapSaplingNoteData .
*/
std : : pair < mapSaplingNoteData_t , SaplingIncomingViewingKeyMap > CWallet : : FindMySaplingNotesAsync ( const CTransaction & tx , int height ) const
{
mapSaplingNoteData_t noteData ;
SaplingIncomingViewingKeyMap viewingKeysToAdd ;
if ( tx . vShieldedOutput . empty ( ) )
{
return std : : make_pair ( noteData , viewingKeysToAdd ) ;
}
LOCK ( cs_KeyStore ) ;
std : : set < SaplingIncomingViewingKey > setSaplingIvkToTest ;
for ( const auto & [ ivk , fvk ] : mapSaplingFullViewingKeys )
{
setSaplingIvkToTest . insert ( ivk ) ;
}
for ( const auto & [ address , ivk ] : mapSaplingIncomingViewingKeys )
{
setSaplingIvkToTest . insert ( ivk ) ;
}
if ( setSaplingIvkToTest . empty ( ) )
{
return std : : make_pair ( noteData , viewingKeysToAdd ) ;
}
std : : vector < std : : future < std : : pair < mapSaplingNoteData_t , SaplingIncomingViewingKeyMap > > > vDecryptFutures ;
const Consensus : : Params consensus_params = Params ( ) . GetConsensus ( ) ;
uint256 hash = tx . GetHash ( ) ;
// Protocol Spec: 4.19 Block Chain Scanning (Sapling)
for ( uint32_t i = 0 ; i < tx . vShieldedOutput . size ( ) ; + + i )
{
const OutputDescription output = tx . vShieldedOutput [ i ] ;
for ( SaplingIncomingViewingKey ivk : setSaplingIvkToTest )
{
vDecryptFutures . emplace_back ( std : : async ( std : : launch : : async , DecryptSaplingNoteWorker , consensus_params , ivk , output , height , hash , i ) ) ;
}
}
for ( auto & fut : vDecryptFutures )
{
auto result_pair = fut . get ( ) ;
if ( ! result_pair . first . empty ( ) )
{
noteData . insert ( result_pair . first . begin ( ) , result_pair . first . end ( ) ) ;
for ( auto [ address , ivk ] : result_pair . second )
{
if ( mapSaplingIncomingViewingKeys . count ( address ) = = 0 )
{
viewingKeysToAdd [ address ] = ivk ;
}
}
}
}
return std : : make_pair ( noteData , viewingKeysToAdd ) ;
}
bool CWallet : : IsSaplingNullifierFromMe ( const uint256 & nullifier ) const
{
{
@ -2221,7 +2363,7 @@ void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData)
boost : : optional < std : : pair <
SaplingNotePlaintext ,
SaplingPaymentAddress > > CWalletTx : : DecryptSaplingNote ( SaplingOutPoint op ) const
SaplingPaymentAddress > > CWalletTx : : DecryptSaplingNote ( const Consensus : : Params & params , int height , SaplingOutPoint op ) const
{
// Check whether we can decrypt this SaplingOutPoint
if ( this - > mapSaplingNoteData . count ( op ) = = 0 ) {
@ -2232,6 +2374,8 @@ boost::optional<std::pair<
auto nd = this - > mapSaplingNoteData . at ( op ) ;
auto maybe_pt = SaplingNotePlaintext : : decrypt (
params ,
height ,
output . encCiphertext ,
nd . ivk ,
output . ephemeralKey ,
@ -2248,8 +2392,7 @@ boost::optional<std::pair<
boost : : optional < std : : pair <
SaplingNotePlaintext ,
SaplingPaymentAddress > > CWalletTx : : RecoverSaplingNote (
SaplingOutPoint op , std : : set < uint256 > & ovks ) const
SaplingPaymentAddress > > CWalletTx : : RecoverSaplingNote ( const Consensus : : Params & params , int height , SaplingOutPoint op , std : : set < uint256 > & ovks ) const
{
auto output = this - > vShieldedOutput [ op . n ] ;
@ -2265,6 +2408,8 @@ boost::optional<std::pair<
}
auto maybe_pt = SaplingNotePlaintext : : decrypt (
params ,
height ,
output . encCiphertext ,
output . ephemeralKey ,
outPt - > esk ,
@ -2780,7 +2925,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
BOOST_FOREACH ( CTransaction & tx , block . vtx )
{
if ( AddToWalletIfInvolvingMe ( tx , & block , fUpdate ) ) {
if ( AddToWalletIfInvolvingMe ( tx , & block , pindex - > GetHeight ( ) , fUpdate ) ) {
involvesMe = true ;
ret + + ;
}
@ -4918,6 +5063,8 @@ void CWallet::GetFilteredNotes(
SaplingNoteData nd = pair . second ;
auto maybe_pt = SaplingNotePlaintext : : decrypt (
Params ( ) . GetConsensus ( ) ,
chainActive . Height ( ) ,
wtx . vShieldedOutput [ op . n ] . encCiphertext ,
nd . ivk ,
wtx . vShieldedOutput [ op . n ] . ephemeralKey ,