SmartCoin to know SmartTransaction
MODIFY
WalletWasabi.Tests/Common.cs
WalletWasabi.Tests/Common.cs
using NBitcoin;
+using System;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
+using WalletWasabi.Blockchain.Analysis.Clustering;
+using WalletWasabi.Blockchain.Keys;
+using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -34,6 +38,18 @@ namespace WalletWasabi.Tests
return stx;
}
+ public static SmartCoin GetRandomSmartCoin(string label, HdPubKey pubKey, decimal amount, bool confirmed = true, int anonymitySet = 1)
+ {
+ var height = confirmed ? new Height(new Random().Next(0, 200)) : Height.Mempool;
+ SmartLabel slabel = label;
+ pubKey.SetLabel(slabel);
+ pubKey.SetKeyState(KeyState.Used);
+ var tx = Transaction.Create(Network.Main);
+ tx.Outputs.Add(new TxOut(Money.Coins(amount), pubKey.P2wpkhScript));
+ var stx = new SmartTransaction(tx, height);
+ return new SmartCoin(stx, 0, pubKey, anonymitySet, slabel);
+ }
+
public static string GetWorkDir([CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "")
{
return Path.Combine(DataDir, EnvironmentHelpers.ExtractFileName(callerFilePath), callerMemberName);

MODIFY
WalletWasabi.Tests/RegressionTests/CoinJoinTests.cs
WalletWasabi.Tests/RegressionTests/CoinJoinTests.cs
@@ -16,6 +16,7 @@ using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Blocks;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Blockchain.TransactionOutputs;
+using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.CoinJoin.Client.Clients;
using WalletWasabi.CoinJoin.Client.Clients.Queuing;
using WalletWasabi.CoinJoin.Client.Rounds;
@@ -263,7 +264,7 @@ namespace WalletWasabi.Tests.RegressionTests
ClientRoundRegistration first = null;
var randomKey = KeyManager.CreateNew(out _, "").GenerateNewKey(SmartLabel.Empty, KeyState.Clean, false);
var second = new ClientRoundRegistration(aliceClient,
- new[] { new SmartCoin(new Coin(uint256.One, 1, Money.Zero, randomKey.P2wpkhScript), randomKey, new[] { new OutPoint(uint256.One, 1) }, Height.Unknown, true, 2) },
+ new[] { Tests.Common.GetRandomSmartCoin(SmartLabel.Empty, randomKey, 0m, false, 2) },
BitcoinAddress.Create("12Rty3c8j3QiZSwLVaBtch6XUMZaja3RC7", Network.Main));
first = second;
second = null;
@@ -1109,9 +1110,10 @@ namespace WalletWasabi.Tests.RegressionTests
key.SetKeyState(KeyState.Used);
var tx = await rpc.GetRawTransactionAsync(txId);
var height = await rpc.GetBlockCountAsync();
+ var stx = new SmartTransaction(tx, height + 1);
var bechCoin = tx.Outputs.GetCoins(bech.ScriptPubKey).Single();
- var smartCoin = new SmartCoin(bechCoin, key, tx.Inputs.ToOutPoints().ToArray(), height + 1, replaceable: false, anonymitySet: tx.GetAnonymitySet(bechCoin.Outpoint.N));
+ var smartCoin = new SmartCoin(stx, bechCoin.Outpoint.N, key, anonymitySet: tx.GetAnonymitySet(bechCoin.Outpoint.N));
var chaumianClient = new CoinJoinClient(synchronizer, rpc.Network, keyManager);
@@ -1214,15 +1216,20 @@ namespace WalletWasabi.Tests.RegressionTests
var tx4 = await rpc.GetRawTransactionAsync(txId4);
await rpc.GenerateAsync(1);
var height = await rpc.GetBlockCountAsync();
+ var stx1 = new SmartTransaction(tx1, height);
+ var stx2 = new SmartTransaction(tx2, height);
+ var stx3 = new SmartTransaction(tx3, height);
+ var stx4 = new SmartTransaction(tx4, height);
+
var bech1Coin = tx1.Outputs.GetCoins(bech1.ScriptPubKey).Single();
var bech2Coin = tx2.Outputs.GetCoins(bech2.ScriptPubKey).Single();
var bech3Coin = tx3.Outputs.GetCoins(bech3.ScriptPubKey).Single();
var bech4Coin = tx4.Outputs.GetCoins(bech4.ScriptPubKey).Single();
- var smartCoin1 = new SmartCoin(bech1Coin, key1, tx1.Inputs.ToOutPoints().ToArray(), height, replaceable: false, anonymitySet: tx1.GetAnonymitySet(bech1Coin.Outpoint.N));
- var smartCoin2 = new SmartCoin(bech2Coin, key2, tx2.Inputs.ToOutPoints().ToArray(), height, replaceable: false, anonymitySet: tx2.GetAnonymitySet(bech2Coin.Outpoint.N));
- var smartCoin3 = new SmartCoin(bech3Coin, key3, tx3.Inputs.ToOutPoints().ToArray(), height, replaceable: false, anonymitySet: tx3.GetAnonymitySet(bech3Coin.Outpoint.N));
- var smartCoin4 = new SmartCoin(bech4Coin, key4, tx4.Inputs.ToOutPoints().ToArray(), height, replaceable: false, anonymitySet: tx4.GetAnonymitySet(bech4Coin.Outpoint.N));
+ var smartCoin1 = new SmartCoin(stx1, bech1Coin.Outpoint.N, key1, anonymitySet: tx1.GetAnonymitySet(bech1Coin.Outpoint.N));
+ var smartCoin2 = new SmartCoin(stx2, bech2Coin.Outpoint.N, key2, anonymitySet: tx2.GetAnonymitySet(bech2Coin.Outpoint.N));
+ var smartCoin3 = new SmartCoin(stx3, bech3Coin.Outpoint.N, key3, anonymitySet: tx3.GetAnonymitySet(bech3Coin.Outpoint.N));
+ var smartCoin4 = new SmartCoin(stx4, bech4Coin.Outpoint.N, key4, anonymitySet: tx4.GetAnonymitySet(bech4Coin.Outpoint.N));
var chaumianClient1 = new CoinJoinClient(synchronizer, rpc.Network, keyManager);
var chaumianClient2 = new CoinJoinClient(synchronizer, rpc.Network, keyManager);
@@ -1246,7 +1253,10 @@ namespace WalletWasabi.Tests.RegressionTests
var randomKey = keyManager.GenerateNewKey(SmartLabel.Empty, KeyState.Clean, isInternal: false);
// Make sure it does not throw.
- await chaumianClient1.DequeueCoinsFromMixAsync(new SmartCoin(new Coin(network.Consensus.ConsensusFactory.CreateTransaction().GetHash(), 1, Money.Parse("3"), randomKey.P2wpkhScript), randomKey, new[] { new OutPoint(uint256.One, 0) }, Height.Mempool, replaceable: false, anonymitySet: 1), DequeueReason.UserRequested);
+ var randomTx = network.Consensus.ConsensusFactory.CreateTransaction();
+ randomTx.Outputs.Add(new TxOut(Money.Coins(3m), randomKey.P2wpkhScript));
+ var randomStx = new SmartTransaction(randomTx, Height.Mempool);
+ await chaumianClient1.DequeueCoinsFromMixAsync(new SmartCoin(randomStx, 0, randomKey, anonymitySet: 1), DequeueReason.UserRequested);
Assert.True(2 == (await chaumianClient1.QueueCoinsToMixAsync(password, smartCoin1, smartCoin2)).Count());
await chaumianClient1.DequeueCoinsFromMixAsync(smartCoin1, DequeueReason.UserRequested);

MODIFY
WalletWasabi.Tests/UnitTests/SmartCoinTests.cs
WalletWasabi.Tests/UnitTests/SmartCoinTests.cs
@@ -3,6 +3,7 @@ using System.Linq;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Blockchain.TransactionOutputs;
+using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Models;
using Xunit;
@@ -17,28 +18,22 @@ namespace WalletWasabi.Tests.UnitTests
var txId = tx.GetHash();
var index = 0U;
var km = KeyManager.CreateNew(out _, "");
- var hdpk = km.GenerateNewKey(SmartLabel.Empty, KeyState.Clean, false);
- tx.Outputs[0].ScriptPubKey = hdpk.P2wpkhScript;
- var output = tx.Outputs[0];
- var scriptPubKey = output.ScriptPubKey;
- var amount = output.Value;
- var spentOutputs = tx.Inputs.ToOutPoints().ToArray();
+ var hdpk1 = km.GenerateNewKey(SmartLabel.Empty, KeyState.Clean, false);
+ var hdpk2 = km.GenerateNewKey(SmartLabel.Empty, KeyState.Clean, false);
+ tx.Outputs[0].ScriptPubKey = hdpk1.P2wpkhScript;
+ tx.Outputs[1].ScriptPubKey = hdpk2.P2wpkhScript;
var height = Height.Mempool;
+ var stx = new SmartTransaction(tx, height);
var label = "foo";
- var coin = new SmartCoin(new Coin(txId, index, amount, scriptPubKey), hdpk, spentOutputs, height, tx.RBF, tx.GetAnonymitySet(index), label, txId);
+ var coin = new SmartCoin(stx, index, hdpk1, tx.GetAnonymitySet(index), label, txId);
// If the txId or the index differ, equality should think it's a different coin.
- var differentCoin = new SmartCoin(new Coin(txId, index + 1, amount, scriptPubKey), hdpk, spentOutputs, height, tx.RBF, tx.GetAnonymitySet(index + 1), label, txId);
- var differentOutput = tx.Outputs[1];
- var differentSpentOutputs = new[]
- {
- new OutPoint(txId, 0)
- };
+ var differentCoin = new SmartCoin(stx, index + 1, hdpk2, tx.GetAnonymitySet(index + 1), label, txId);
// If the txId and the index are the same, equality should think it's the same coin.
- var sameCoin = new SmartCoin(new Coin(txId, index, differentOutput.Value, differentOutput.ScriptPubKey), hdpk, differentSpentOutputs, Height.Unknown, tx.RBF, tx.GetAnonymitySet(index), "boo");
+ var sameCoin = new SmartCoin(stx, index, hdpk1, tx.GetAnonymitySet(index), "boo");
Assert.Equal(coin, sameCoin);
Assert.NotEqual(coin, differentCoin);

MODIFY
WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs
WalletWasabi.Tests/UnitTests/Transactions/PayjoinTests.cs
@@ -33,20 +33,6 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
return (password, KeyManager.CreateNewWatchOnly(keyManager.ExtPubKey));
}
- private static SmartCoin Coin(string label, HdPubKey pubKey, decimal amount, bool confirmed = true, int anonymitySet = 1)
- {
- var randomIndex = new Func<int>(() => new Random().Next(0, 200));
- var height = confirmed ? new Height(randomIndex()) : Height.Mempool;
- SmartLabel slabel = label;
- var spentOutput = new[]
- {
- new OutPoint(RandomUtils.GetUInt256(), (uint)randomIndex())
- };
- pubKey.SetLabel(slabel);
- pubKey.SetKeyState(KeyState.Used);
- return new SmartCoin(new Coin(RandomUtils.GetUInt256(), (uint)randomIndex(), Money.Coins(amount), pubKey.P2wpkhScript), pubKey, spentOutput, height, false, anonymitySet, slabel);
- }
-
public static PSBT GenerateRandomTransaction()
{
var key = new Key();
@@ -449,7 +435,7 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
keyManager.AssertCleanKeysIndexed();
var keys = keyManager.GetKeys().Take(10).ToArray();
- var scoins = coins.Select(x => Coin(x.Label, keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray();
+ var scoins = coins.Select(x => Common.GetRandomSmartCoin(x.Label, keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray();
foreach (var coin in scoins)
{
foreach (var sameLabelCoin in scoins.Where(c => !c.Label.IsEmpty && c.Label == coin.Label))

MODIFY
WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs
WalletWasabi.Tests/UnitTests/Transactions/TransactionFactoryTests.cs
@@ -182,16 +182,16 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
HdPubKey NewKey() => keyManager.GenerateNewKey("", KeyState.Used, true, false);
var scoins = new[]
{
- Coin("Pablo", NewKey(), 0.9m),
- Coin("Daniel", NewKey(), 0.9m),
- Coin("Adolf", NewKey(), 0.9m),
- Coin("Maria", NewKey(), 0.9m),
- Coin("Ding", NewKey(), 0.9m),
- Coin("Joseph", NewKey(), 0.9m),
- Coin("Eve", NewKey(), 0.9m),
- Coin("Julio", NewKey(), 0.9m),
- Coin("Donald, Jean, Lee, Onur", NewKey(), 0.9m),
- Coin("Satoshi", NewKey(), 0.9m)
+ Common.GetRandomSmartCoin("Pablo", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Daniel", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Adolf", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Maria", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Ding", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Joseph", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Eve", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Julio", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Donald, Jean, Lee, Onur", NewKey(), 0.9m),
+ Common.GetRandomSmartCoin("Satoshi", NewKey(), 0.9m)
};
var coinsByLabel = scoins.ToDictionary(x => x.Label);
@@ -583,20 +583,6 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
return (password, KeyManager.CreateNewWatchOnly(keyManager.ExtPubKey));
}
- private static SmartCoin Coin(string label, HdPubKey pubKey, decimal amount, bool confirmed = true, int anonymitySet = 1)
- {
- var randomIndex = new Func<int>(() => new Random().Next(0, 200));
- var height = confirmed ? new Height(randomIndex()) : Height.Mempool;
- SmartLabel slabel = label;
- var spentOutput = new[]
- {
- new OutPoint(RandomUtils.GetUInt256(), (uint)randomIndex())
- };
- pubKey.SetLabel(slabel);
- pubKey.SetKeyState(KeyState.Used);
- return new SmartCoin(new Coin(RandomUtils.GetUInt256(), (uint)randomIndex(), Money.Coins(amount), pubKey.P2wpkhScript), pubKey, spentOutput, height, false, anonymitySet, slabel);
- }
-
private TransactionFactory CreateTransactionFactory(
IEnumerable<(string Label, int KeyIndex, decimal Amount, bool Confirmed, int AnonymitySet)> coins,
bool allowUnconfirmed = true,
@@ -607,7 +593,7 @@ namespace WalletWasabi.Tests.UnitTests.Transactions
keyManager.AssertCleanKeysIndexed();
var keys = keyManager.GetKeys().Take(10).ToArray();
- var scoins = coins.Select(x => Coin(x.Label, keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray();
+ var scoins = coins.Select(x => Common.GetRandomSmartCoin(x.Label, keys[x.KeyIndex], x.Amount, x.Confirmed, x.AnonymitySet)).ToArray();
foreach (var coin in scoins)
{
foreach (var sameLabelCoin in scoins.Where(c => !c.Label.IsEmpty && c.Label == coin.Label))

MODIFY
WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs
WalletWasabi/Blockchain/TransactionOutputs/SmartCoin.cs
using NBitcoin;
using System;
+using System.Linq;
using WalletWasabi.Bases;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Keys;
+using WalletWasabi.Blockchain.Transactions;
using WalletWasabi.Helpers;
using WalletWasabi.Models;
@@ -34,13 +36,14 @@ namespace WalletWasabi.Blockchain.TransactionOutputs
#region Constructors
- public SmartCoin(Coin coin, HdPubKey pubKey, OutPoint[] spentOutputs, Height height, bool replaceable, int anonymitySet, SmartLabel label = null, uint256 spenderTransactionId = null, bool coinJoinInProgress = false, DateTimeOffset? bannedUntilUtc = null, bool spentAccordingToBackend = false)
+ public SmartCoin(SmartTransaction transaction, uint outputIndex, HdPubKey pubKey, int anonymitySet, SmartLabel label = null, uint256 spenderTransactionId = null, bool coinJoinInProgress = false, DateTimeOffset? bannedUntilUtc = null, bool spentAccordingToBackend = false)
{
- Coin = coin;
+ Transaction = transaction;
+ Coin = new Coin(transaction.Transaction, outputIndex);
HashCode = (TransactionId, Index).GetHashCode();
- Height = height;
- SpentOutputs = Guard.NotNullOrEmpty(nameof(spentOutputs), spentOutputs);
- WasReplaceable = replaceable;
+ Height = transaction.Height;
+ SpentOutputs = Transaction.Transaction.Inputs.ToOutPoints().ToArray();
+ WasReplaceable = Transaction.IsRBF;
AnonymitySet = Guard.InRangeAndNotNull(nameof(anonymitySet), anonymitySet, 1, int.MaxValue);
SpenderTransactionId = spenderTransactionId;
@@ -62,6 +65,8 @@ namespace WalletWasabi.Blockchain.TransactionOutputs
#region Properties
+ public SmartTransaction Transaction { get; }
+
public Coin Coin { get; }
public Script ScriptPubKey => Coin.ScriptPubKey;

MODIFY
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessor.cs
WalletWasabi/Blockchain/TransactionProcessing/TransactionProcessor.cs
@@ -201,8 +201,7 @@ namespace WalletWasabi.Blockchain.TransactionProcessing
anonset += spentOwnCoins.Min(x => x.AnonymitySet) - 1;
}
- var c = new Coin(txId, i, output.Value, output.ScriptPubKey);
- SmartCoin newCoin = new SmartCoin(c, foundKey, tx.Transaction.Inputs.ToOutPoints().ToArray(), tx.Height, tx.IsRBF, anonset, foundKey.Label, spenderTransactionId: null, false); // Do not inherit locked status from key, that's different.
+ SmartCoin newCoin = new SmartCoin(tx, i, foundKey, anonset, foundKey.Label, spenderTransactionId: null, false); // Do not inherit locked status from key, that's different.
result.ReceivedCoins.Add(newCoin);
// If we did not have it.

MODIFY
WalletWasabi/Blockchain/Transactions/TransactionFactory.cs
WalletWasabi/Blockchain/Transactions/TransactionFactory.cs
@@ -271,20 +271,21 @@ namespace WalletWasabi.Blockchain.Transactions
var label = SmartLabel.Merge(payments.Requests.Select(x => x.Label).Concat(spentCoins.Select(x => x.Label)));
var outerWalletOutputs = new List<Coin>();
var innerWalletOutputs = new List<SmartCoin>();
+ var smartTransaction = new SmartTransaction(tx, Height.Unknown, label: SmartLabel.Merge(payments.Requests.Select(x => x.Label)));
for (var i = 0U; i < tx.Outputs.Count; i++)
{
TxOut output = tx.Outputs[i];
- var coin = new Coin(tx.GetHash(), i, output.Value, output.ScriptPubKey);
var foundKey = KeyManager.GetKeyForScriptPubKey(output.ScriptPubKey);
if (foundKey is { })
{
var anonset = tx.GetAnonymitySet(i) + spentCoins.Min(x => x.AnonymitySet) - 1; // Minus 1, because count own only once.
- var smartCoin = new SmartCoin(coin, foundKey, tx.Inputs.ToOutPoints().ToArray(), Height.Unknown, tx.RBF, anonset);
+ var smartCoin = new SmartCoin(smartTransaction, i, foundKey, anonset);
label = SmartLabel.Merge(label, smartCoin.Label); // foundKey's label is already added to the coinlabel.
innerWalletOutputs.Add(smartCoin);
}
else
{
+ var coin = new Coin(tx.GetHash(), i, output.Value, output.ScriptPubKey);
outerWalletOutputs.Add(coin);
}
}
@@ -309,7 +310,6 @@ namespace WalletWasabi.Blockchain.Transactions
Logger.LogInfo($"Transaction is successfully built: {tx.GetHash()}.");
var sign = !KeyManager.IsWatchOnly;
var spendsUnconfirmed = spentCoins.Any(c => !c.Confirmed);
- SmartTransaction smartTransaction = new SmartTransaction(tx, Height.Unknown, label: SmartLabel.Merge(payments.Requests.Select(x => x.Label)));
return new BuildTransactionResult(smartTransaction, psbt, spendsUnconfirmed, sign, fee, feePc, outerWalletOutputs, innerWalletOutputs, spentCoins);
}

SmartCoin to know SmartTransaction