@@ -1337,13 +1337,83 @@ CAmount CalculateDeniabilizationFeeEstimate(const CTxDestination& shared_destina
1337
1337
return deniabilizationFee;
1338
1338
}
1339
1339
1340
- util::Result<CreatedTransactionResult> CreateDeniabilizationTransaction (
1341
- CWallet& wallet,
1342
- const std::set<COutPoint>& inputs,
1343
- unsigned int confirm_target,
1344
- unsigned int deniabilization_cycles,
1345
- bool sign,
1346
- bool & insufficient_amount)
1340
+ std::pair<unsigned int , bool > CalculateDeniabilizationCycles (CWallet& wallet, const COutPoint& outpoint)
1341
+ {
1342
+ LOCK (wallet.cs_wallet );
1343
+ auto walletTx = wallet.GetWalletTx (outpoint.hash );
1344
+ if (!walletTx) {
1345
+ return std::make_pair (0 , false );
1346
+ }
1347
+ auto tx = walletTx->tx ;
1348
+
1349
+ if (tx->IsCoinBase ()) {
1350
+ // this is a block reward tx, so we tag it as such
1351
+ return std::make_pair (0 , true );
1352
+ }
1353
+
1354
+ // an deniabilized coin is one we sent to ourselves
1355
+ // all txIn should belong to our wallet
1356
+ if (tx->vin .empty ()) {
1357
+ return std::make_pair (0 , false );
1358
+ }
1359
+ for (const auto & txIn : tx->vin ) {
1360
+ if (InputIsMine (wallet, txIn) == ISMINE_NO) {
1361
+ return std::make_pair (0 , false );
1362
+ }
1363
+ }
1364
+
1365
+ // all txOut should belong to our wallet
1366
+ Assert (outpoint.n < tx->vout .size ());
1367
+ uint n = 0 ;
1368
+ for (const auto & txOut : tx->vout ) {
1369
+ if (wallet.IsMine (txOut) == ISMINE_NO) {
1370
+ Assert (n != outpoint.n );
1371
+ return std::make_pair (0 , false );
1372
+ }
1373
+ n++;
1374
+ }
1375
+
1376
+ uint uniqueTxOutCount = 0 ;
1377
+ for (const auto & txOut : tx->vout ) {
1378
+ // check if it's a valid destination
1379
+ CTxDestination txOutDestination;
1380
+ if (!ExtractDestination (txOut.scriptPubKey , txOutDestination)) {
1381
+ continue ;
1382
+ }
1383
+
1384
+ // don't count outputs that match any input addresses (eg it's change output)
1385
+ bool matchesInput = false ;
1386
+ for (const auto & txIn : tx->vin ) {
1387
+ auto prevWalletTx = wallet.GetWalletTx (txIn.prevout .hash );
1388
+ if (prevWalletTx && prevWalletTx->tx ->vout [txIn.prevout .n ].scriptPubKey == txOut.scriptPubKey ) {
1389
+ matchesInput = true ;
1390
+ break ;
1391
+ }
1392
+ }
1393
+ if (matchesInput) {
1394
+ continue ;
1395
+ }
1396
+
1397
+ uniqueTxOutCount++;
1398
+ }
1399
+
1400
+ // we consider two or more unique outputs an "deniabilization" of the coin
1401
+ uint deniabilizationCycles = uniqueTxOutCount >= 2 ? 1 : 0 ;
1402
+
1403
+ // all txIn and txOut are from our wallet
1404
+ // however if we have multiple txIn this was either an initial deniabilization of multiple UTXOs or the user manually merged deniabilized UTXOs
1405
+ // in either case we don't need to recurse into parent transactions and we can return the calculated cycles
1406
+ if (tx->vin .size () > 1 ) {
1407
+ return std::make_pair (deniabilizationCycles, false );
1408
+ }
1409
+
1410
+ const auto & txIn = tx->vin [0 ];
1411
+ // now recursively calculate the deniabilization cycles of the input
1412
+ auto inputStats = CalculateDeniabilizationCycles (wallet, txIn.prevout );
1413
+ return std::make_pair (inputStats.first + deniabilizationCycles, inputStats.second );
1414
+ };
1415
+
1416
+ util::Result<CreatedTransactionResult> CreateDeniabilizationTransaction (CWallet& wallet, const std::set<COutPoint>& inputs, unsigned int confirm_target, unsigned int deniabilization_cycles, bool sign, bool & insufficient_amount)
1347
1417
{
1348
1418
if (inputs.empty ()) {
1349
1419
return util::Error{_ (" Inputs must not be empty" )};
0 commit comments