Use `OwnershipIdentifier`
MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -37,8 +37,15 @@ namespace WalletWasabi.Tests.Helpers
public static OwnershipProof CreateOwnershipProof(Key key, uint256? roundHash = null)
=> OwnershipProof.GenerateCoinJoinInputProof(
key,
+ GetOwnershipIdentifier(key.PubKey.WitHash.ScriptPubKey),
new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundHash ?? BitcoinFactory.CreateUint256()));
+ public static OwnershipIdentifier GetOwnershipIdentifier(Script scriptPubKey)
+ {
+ using var identificationKey = Key.Parse("5KbdaBwc9Eit2LrmDp1WfZd815StNstwHanbRrPpGGN6wWJKyHe", Network.Main);
+ return new OwnershipIdentifier(identificationKey, scriptPubKey);
+ }
+
public static Round CreateRound(WabiSabiConfig cfg)
{
Round round = new(new RoundParameters(

MODIFY
WalletWasabi.Tests/UnitTests/Crypto/OwnershipProofTest.cs
WalletWasabi.Tests/UnitTests/Crypto/OwnershipProofTest.cs
@@ -14,8 +14,12 @@ namespace WalletWasabi.Tests.UnitTests.Crypto
{
// Trezor test vector
// [all all all all all all all all all all all all]/84'/0'/0'/1/0
+ var allMnemonic = new Mnemonic("all all all all all all all all all all all all");
+ var identificationMasterKey = Slip21Node.FromSeed(allMnemonic.DeriveSeed());
+ var identificationKey = identificationMasterKey.DeriveChild("SLIP-0019").DeriveChild("Ownership identification key").Key;
using var key = new Key(Encoders.Hex.DecodeData("3460814214450E864EC722FF1F84F96C41746CD6BBE2F1C09B33972761032E9F"));
- var ownershipIdentifier = new OwnershipIdentifier(Encoders.Hex.DecodeData("A122407EFC198211C81AF4450F40B235D54775EFD934D16B9E31C6CE9BAD5707"));
+
+ var ownershipIdentifier = new OwnershipIdentifier(identificationKey, key.PubKey.WitHash.ScriptPubKey);
var commitmentData = Array.Empty<byte>();
var ownershipProof = OwnershipProof.Generate(key, ownershipIdentifier, commitmentData, false, ScriptPubKeyType.Segwit);
var scriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/AliceTimeoutTests.cs
@@ -36,10 +36,11 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
await roundStateUpdater.StartAsync(CancellationToken.None);
// Register Alices.
+ using var identificationKey = new Key();
var esk = km.GetSecrets("", smartCoin.ScriptPubKey).Single();
using CancellationTokenSource cancellationTokenSource = new();
- var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network), roundStateUpdater, cancellationTokenSource.Token);
+ var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, cancellationTokenSource.Token);
while (round.Alices.Count == 0)
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepOutputRegistrationTests.cs
@@ -194,9 +194,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
await roundStateUpdater.StartAsync(CancellationToken.None);
-
- var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
- var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
+ var identificationKey = new Key();
+ var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);
+ var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);
while (Phase.ConnectionConfirmation != round.Phase)
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
@@ -227,8 +227,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PhaseStepping
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
await roundStateUpdater.StartAsync(CancellationToken.None);
- var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
- var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), roundStateUpdater, CancellationToken.None);
+ using var identificationKey = new Key();
+ var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);
+ var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);
while (Phase.OutputRegistration != round.Phase)
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputFailureTests.cs
@@ -29,10 +29,11 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
{
using Key key = new();
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(new(), WabiSabiFactory.CreatePreconfiguredRpcClient());
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -67,6 +68,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new() { MaxInputCountByRound = 3 };
var round = WabiSabiFactory.CreateRound(cfg);
using Key key = new();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(), round);
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
@@ -76,7 +79,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
Assert.Equal(Phase.InputRegistration, round.Phase);
@@ -89,6 +92,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero };
var round = WabiSabiFactory.CreateRound(cfg);
using Key key = new();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
var coin = WabiSabiFactory.CreateCoin(key);
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin));
@@ -98,7 +103,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
Assert.Equal(Phase.InputRegistration, round.Phase);
@@ -116,12 +121,13 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
var round = arena.Rounds.First();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
cfg.StandardInputRegistrationTimeout = TimeSpan.Zero;
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
Assert.Equal(Phase.InputRegistration, round.Phase);
@@ -139,10 +145,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
arena.Prison.Punish(outpoint, Punishment.Banned, uint256.One);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);
+
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode);
@@ -160,10 +168,12 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);
+
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None));
Assert.NotEqual(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -177,13 +187,14 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new() { AllowNotedInputRegistration = false };
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -195,6 +206,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Key key = new();
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
var mockRpc = new Mock<IRPCClient>();
mockRpc.Setup(rpc => rpc.GetTxOutAsync(It.IsAny<uint256>(), It.IsAny<int>(), It.IsAny<bool>()))
@@ -204,7 +216,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -216,6 +228,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Key key = new();
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
var mockRpc = new Mock<IRPCClient>();
mockRpc.Setup(rpc => rpc.GetTxOutAsync(It.IsAny<uint256>(), It.IsAny<int>(), It.IsAny<bool>()))
@@ -225,7 +238,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.InputUnconfirmed, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -237,6 +250,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Key key = new();
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient();
var rpcCfg = rpc.SetupSequence(rpc => rpc.GetTxOutAsync(It.IsAny<uint256>(), It.IsAny<int>(), It.IsAny<bool>()));
foreach (var i in Enumerable.Range(1, 100))
@@ -250,7 +265,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
foreach (var i in Enumerable.Range(1, 100))
{
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.InputImmature, ex.ErrorCode);
}
@@ -264,6 +279,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
using Key key = new();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
var mockRpc = new Mock<IRPCClient>();
mockRpc.Setup(rpc => rpc.GetTxOutAsync(It.IsAny<uint256>(), It.IsAny<int>(), It.IsAny<bool>()))
@@ -278,7 +294,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode);
@@ -296,8 +312,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
using Key nonOwnerKey = new();
+ var wrongOwnershipProof = WabiSabiFactory.CreateOwnershipProof(nonOwnerKey, round.Id);
+
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, nonOwnerKey, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, wrongOwnershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.WrongOwnershipProof, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -309,11 +327,13 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Key key = new();
WabiSabiConfig cfg = new() { MinRegistrableAmount = Money.Coins(2) };
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.NotEnoughFunds, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -325,11 +345,13 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Key key = new();
WabiSabiConfig cfg = new() { MaxRegistrableAmount = Money.Coins(0.9m) };
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(), round);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.TooMuchFunds, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -345,13 +367,15 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var coin = WabiSabiFactory.CreateCoin(key);
WabiSabiConfig cfg = new() { MaxInputCountByRound = 100_000 };
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
round.MaxVsizeAllocationPerAlice = 0;
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.TooMuchVsize, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -364,6 +388,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var coin = WabiSabiFactory.CreateCoin(key);
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
// Make sure an Alice have already been registered with the same input.
var anotherAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key), round);
@@ -374,7 +399,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);
@@ -388,6 +413,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
WabiSabiConfig cfg = new();
var round = WabiSabiFactory.CreateRound(cfg);
var anotherRound = WabiSabiFactory.CreateRound(cfg);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
// Make sure an Alice have already been registered with the same input.
var preAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key), round);
@@ -398,7 +424,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(
- async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None));
+ async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None));
Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PostRequests/RegisterInputSuccessTests.cs
@@ -34,7 +34,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9;
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
- var resp = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
+
+ var resp = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None);
AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp);
await arena.StopAsync(CancellationToken.None);
@@ -47,6 +49,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
var round = WabiSabiFactory.CreateRound(cfg);
using Key key = new();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
var coin = WabiSabiFactory.CreateCoin(key);
// Make sure an Alice have already been registered with the same input.
@@ -56,7 +59,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend.PostRequests
using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round);
var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
- var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, key, CancellationToken.None).ConfigureAwait(false));
+ var ex = await Assert.ThrowsAsync<WabiSabiProtocolException>(async () => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None).ConfigureAwait(false));
Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);
await arena.StopAsync(CancellationToken.None);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs
@@ -69,8 +69,9 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
roundState.CreateAmountCredentialClient(insecureRandom),
roundState.CreateVsizeCredentialClient(insecureRandom),
wabiSabiApi);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
- var inputRegistrationResponse = await aliceArenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None);
+ var inputRegistrationResponse = await aliceArenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None);
var aliceId = inputRegistrationResponse.Value;
var inputVsize = Constants.P2wpkhInputVirtualSize;

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/BobClientTests.cs
@@ -52,7 +52,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), wabiSabiApi);
await roundStateUpdater.StartAsync(CancellationToken.None);
- var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), aliceArenaClient, coin1, bitcoinSecret, roundStateUpdater, CancellationToken.None);
+ using var identificationKey = new Key();
+ var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), aliceArenaClient, coin1, bitcoinSecret, identificationKey, roundStateUpdater, CancellationToken.None);
do
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
@@ -46,9 +46,10 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
// means that the output is spent or simply doesn't even exist.
var nonExistingOutPoint = new OutPoint();
using var signingKey = new Key();
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(signingKey, round.Id);
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () =>
- await apiClient.RegisterInputAsync(round.Id, nonExistingOutPoint, signingKey, CancellationToken.None));
+ await apiClient.RegisterInputAsync(round.Id, nonExistingOutPoint, ownershipProof, CancellationToken.None));
var wex = Assert.IsType<WabiSabiProtocolException>(ex.InnerException);
Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, wex.ErrorCode);
@@ -370,7 +371,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
var rounds = await apiClient.GetStatusAsync(CancellationToken.None);
var round = rounds.First(x => x.CoinjoinState is ConstructionState);
- var response = await apiClient.RegisterInputAsync(round.Id, coinToRegister.Outpoint, signingKey, CancellationToken.None);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(signingKey, round.Id);
+ var response = await apiClient.RegisterInputAsync(round.Id, coinToRegister.Outpoint, ownershipProof, CancellationToken.None);
Assert.NotEqual(Guid.Empty, response.Value);
}
@@ -403,7 +405,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
RoundState[] rounds = await apiClient.GetStatusAsync(CancellationToken.None);
RoundState round = rounds.First(x => x.CoinjoinState is ConstructionState);
- ArenaResponse<Guid> response = await apiClient.RegisterInputAsync(round.Id, coinToRegister.Outpoint, signingKey, CancellationToken.None);
+ var ownershipProof = WabiSabiFactory.CreateOwnershipProof(signingKey, round.Id);
+ ArenaResponse<Guid> response = await apiClient.RegisterInputAsync(round.Id, coinToRegister.Outpoint, ownershipProof, CancellationToken.None);
Assert.NotEqual(Guid.Empty, response.Value);
}

MODIFY
WalletWasabi/Crypto/OwnershipIdentifier.cs
WalletWasabi/Crypto/OwnershipIdentifier.cs
using System;
using System.Linq;
using NBitcoin;
+using NBitcoin.Crypto;
namespace WalletWasabi.Crypto
{
@@ -9,6 +10,11 @@ namespace WalletWasabi.Crypto
public const int OwnershipIdLength = 32;
private byte[] _bytes;
+ public OwnershipIdentifier(Key identificationKey, Script scriptPubKey)
+ : this(Hashes.HMACSHA256(identificationKey.ToBytes(), scriptPubKey.ToBytes()))
+ {
+ }
+
public OwnershipIdentifier(byte[] bytes)
{
if (bytes.Length != OwnershipIdLength)

MODIFY
WalletWasabi/Crypto/OwnershipProof.cs
WalletWasabi/Crypto/OwnershipProof.cs
@@ -64,8 +64,8 @@ namespace WalletWasabi.Crypto
return _proofSignature.Verify(hash, scriptPubKey);
}
- public static OwnershipProof GenerateCoinJoinInputProof(Key key, CoinJoinInputCommitmentData coinJoinInputsCommitmentData) =>
- Generate(key, new OwnershipIdentifier(), coinJoinInputsCommitmentData.ToBytes(), true, ScriptPubKeyType.Segwit);
+ public static OwnershipProof GenerateCoinJoinInputProof(Key key, OwnershipIdentifier ownershipIdentifier, CoinJoinInputCommitmentData coinJoinInputsCommitmentData) =>
+ Generate(key, ownershipIdentifier, coinJoinInputsCommitmentData.ToBytes(), true, ScriptPubKeyType.Segwit);
public static bool VerifyCoinJoinInputProof(OwnershipProof ownershipProofBytes, Script scriptPubKey, CoinJoinInputCommitmentData coinJoinInputsCommitmentData) =>
ownershipProofBytes.VerifyOwnership(scriptPubKey, coinJoinInputsCommitmentData.ToBytes(), true);

MODIFY
WalletWasabi/WabiSabi/Client/AliceClient.cs
WalletWasabi/WabiSabi/Client/AliceClient.cs
@@ -52,13 +52,14 @@ namespace WalletWasabi.WabiSabi.Client
ArenaClient arenaClient,
SmartCoin coin,
BitcoinSecret bitcoinSecret,
+ Key identificationKey,
RoundStateUpdater roundStatusUpdater,
CancellationToken cancellationToken)
{
AliceClient? aliceClient = null;
try
{
- aliceClient = await RegisterInputAsync(roundState, arenaClient, coin, bitcoinSecret, cancellationToken).ConfigureAwait(false);
+ aliceClient = await RegisterInputAsync(roundState, arenaClient, coin, bitcoinSecret, identificationKey, cancellationToken).ConfigureAwait(false);
await aliceClient.ConfirmConnectionAsync(roundStatusUpdater, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -74,12 +75,18 @@ namespace WalletWasabi.WabiSabi.Client
return aliceClient;
}
- private static async Task<AliceClient> RegisterInputAsync(RoundState roundState, ArenaClient arenaClient, SmartCoin coin, BitcoinSecret bitcoinSecret, CancellationToken cancellationToken)
+ private static async Task<AliceClient> RegisterInputAsync(RoundState roundState, ArenaClient arenaClient, SmartCoin coin, BitcoinSecret bitcoinSecret, Key identificationKey, CancellationToken cancellationToken)
{
AliceClient? aliceClient;
try
{
- var response = await arenaClient.RegisterInputAsync(roundState.Id, coin.Coin.Outpoint, bitcoinSecret.PrivateKey, cancellationToken).ConfigureAwait(false);
+ var signingKey = bitcoinSecret.PrivateKey;
+ var ownershipProof = OwnershipProof.GenerateCoinJoinInputProof(
+ signingKey,
+ new OwnershipIdentifier(identificationKey, signingKey.PubKey.WitHash.ScriptPubKey),
+ new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundState.Id));
+
+ var response = await arenaClient.RegisterInputAsync(roundState.Id, coin.Coin.Outpoint, ownershipProof, cancellationToken).ConfigureAwait(false);
aliceClient = new(response.Value, roundState, arenaClient, coin, bitcoinSecret, response.IssuedAmountCredentials, response.IssuedVsizeCredentials);
coin.CoinJoinInProgress = true;

MODIFY
WalletWasabi/WabiSabi/Client/ArenaClient.cs
WalletWasabi/WabiSabi/Client/ArenaClient.cs
@@ -33,13 +33,9 @@ namespace WalletWasabi.WabiSabi.Client
public async Task<ArenaResponse<Guid>> RegisterInputAsync(
uint256 roundId,
OutPoint outPoint,
- Key key,
+ OwnershipProof ownershipProof,
CancellationToken cancellationToken)
{
- var ownershipProof = OwnershipProof.GenerateCoinJoinInputProof(
- key,
- new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundId));
-
var zeroAmountCredentialRequestData = AmountCredentialClient.CreateRequestForZeroAmount();
var zeroVsizeCredentialRequestData = VsizeCredentialClient.CreateRequestForZeroAmount();

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Blockchain.TransactionOutputs;
+using WalletWasabi.Crypto;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -180,7 +181,11 @@ namespace WalletWasabi.WabiSabi.Client
throw new InvalidOperationException("The key cannot generate the utxo scriptpubkey. This could happen if the wallet password is not the correct one.");
}
- return await AliceClient.CreateRegisterAndConfirmInputAsync(roundState, aliceArenaClient, coin, secret, RoundStatusUpdater, cancellationToken).ConfigureAwait(false);
+ var masterKey = Keymanager.GetMasterExtKey(Kitchen.SaltSoup()).PrivateKey;
+ var identificationMasterKey = Slip21Node.FromSeed(masterKey.ToBytes());
+ var identificationKey = identificationMasterKey.DeriveChild("SLIP-0019").DeriveChild("Ownership identification key").Key;
+
+ return await AliceClient.CreateRegisterAndConfirmInputAsync(roundState, aliceArenaClient, coin, secret, identificationKey, RoundStatusUpdater, cancellationToken).ConfigureAwait(false);
}
catch (HttpRequestException)
{

Use `OwnershipIdentifier`