public async Task InputCanBeNotedAsync() { using Key key = new(); var outpoint = BitcoinFactory.CreateOutPoint(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); Prison prison = new(); using Arena arena = await ArenaBuilder.From(cfg, prison).CreateAndStartAsync(round); 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, ownershipProof, CancellationToken.None)); Assert.NotEqual(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessFromPreviousCoinJoinAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiFactory.CreateCoin(key); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin); var coinJoinIdsStore = new InMemoryCoinJoinIdStore(); coinJoinIdsStore.Add(coin.Outpoint.Hash); using Arena arena = await ArenaBuilder.From(cfg).With(rpc).With(coinJoinIdsStore).CreateAndStartAsync(round); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id); var(resp, _) = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); var myAlice = Assert.Single(round.Alices); Assert.True(myAlice.IsPayingZeroCoordinationFee); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessWithAliceUpdateIntraRoundAsync() { WabiSabiConfig cfg = new(); 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. var preAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key), round); round.Alices.Add(preAlice); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin); using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); 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); }
public async Task InputImmatureAsync() { 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>(), It.IsAny <CancellationToken>())); foreach (var i in Enumerable.Range(1, 100)) { rpcCfg = rpcCfg.ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse { Confirmations = i, IsCoinBase = true }); } using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var req = WabiSabiFactory.CreateInputRegistrationRequest(round: round); foreach (var i in Enumerable.Range(1, 100)) { var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputImmature, ex.ErrorCode); } await arena.StopAsync(CancellationToken.None); }
public async Task InputUnconfirmedAsync() { 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>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse { Confirmations = 0 }); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputUnconfirmed, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationFullAsync() { 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 ArenaBuilder.From(cfg).CreateAndStartAsync(round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WrongPhaseException>( async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); Assert.Equal(Phase.InputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationTimedoutAsync() { 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); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin); using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); arena.Rounds.Add(round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WrongPhaseException>( async() => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); Assert.Equal(Phase.InputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task AliceTimesoutAsync() { // Alice times out when its deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiFactory.CreateCoin(key); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, rpc, round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var aliceClient = new AliceClient(round.Id, arenaClient, coin, round.FeeRate, key.GetBitcoinSecret(round.Network)); await aliceClient.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); var alice = Assert.Single(round.Alices); alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Empty(round.Alices); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationTimeoutCanBeModifiedRuntimeAsync() { WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.FromHours(1) }; using Key key = new(); var coin = WabiSabiFactory.CreateCoin(key); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id); round.InputRegistrationTimeFrame = round.InputRegistrationTimeFrame with { Duration = TimeSpan.Zero }; var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); Assert.Equal(Phase.InputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); // Register Alices. var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network)); var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network)); await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. await aliceClient1.ConfirmConnectionAsync(TimeSpan.FromSeconds(1), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await aliceClient2.ConfirmConnectionAsync(TimeSpan.FromSeconds(1), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); return(round, arenaClient, new[]
public async Task RoundNotFoundAsync() { using Key key = new(); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(new(), WabiSabiFactory.CreatePreconfiguredRpcClient()); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task AliceTimesoutAsync() { // Alice times out when its deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); var smartCoin = BitcoinFactory.CreateSmartCoin(key, 10m); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(smartCoin.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(CancellationToken.None); // Register Alices. var keyChain = new KeyChain(km, new Kitchen("")); using CancellationTokenSource cancellationTokenSource = new(); var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, keyChain, roundStateUpdater, cancellationTokenSource.Token); while (round.Alices.Count == 0) { await Task.Delay(10); } var alice = Assert.Single(round.Alices); alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Empty(round.Alices); cancellationTokenSource.Cancel(); try { await task; throw new InvalidOperationException("The operation should throw!"); } catch (Exception exc) { Assert.True(exc is OperationCanceledException or WabiSabiProtocolException); } await roundStateUpdater.StopAsync(CancellationToken.None); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network)); var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network)); await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. await aliceClient1.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await aliceClient2.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient1.RealAmountCredentials, aliceClient1.RealVsizeCredentials, CancellationToken.None).ConfigureAwait(false); await bobClient.RegisterOutputAsync( coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient2.RealAmountCredentials, aliceClient2.RealVsizeCredentials, CancellationToken.None).ConfigureAwait(false); return(round, aliceClient1, aliceClient2); }
CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead; var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(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) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = task1.Result; var aliceClient2 = task2.Result; // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( destKey1.PubKey.WitHash.ScriptPubKey, aliceClient1.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient1.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); await bobClient.RegisterOutputAsync( destKey1.PubKey.WitHash.ScriptPubKey, aliceClient2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient2.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, aliceClient1, aliceClient2); }
public async Task RoundNotFoundAsync() { using Key key = new(); using Arena arena = await ArenaBuilder.Default.CreateAndStartAsync(); var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(uint256.Zero, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); // Register Alices. var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network)); var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network)); await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena)); await aliceClient1.ConfirmConnectionAsync( TimeSpan.FromSeconds(1), new long[] { coin1.EffectiveValue(round.FeeRate) }, new long[] { round.MaxVsizeAllocationPerAlice - coin1.ScriptPubKey.EstimateInputVsize() }, roundStateUpdater, CancellationToken.None).ConfigureAwait(false); await aliceClient2.ConfirmConnectionAsync( TimeSpan.FromSeconds(1), new long[] { coin2.EffectiveValue(round.FeeRate) }, new long[] { round.MaxVsizeAllocationPerAlice - coin2.ScriptPubKey.EstimateInputVsize() }, roundStateUpdater, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); return(round, arenaClient, new[] { aliceClient1, aliceClient2 }); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiFactory.CreateCoin(key); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round); 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); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); await arena.StopAsync(CancellationToken.None); }
public async Task AliceTimesoutAsync() { // Alice times out when its deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); var smartCoin = BitcoinFactory.CreateSmartCoin(key, 10m); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(smartCoin.Coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, rpc, round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); 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), identificationKey, roundStateUpdater, cancellationTokenSource.Token); while (round.Alices.Count == 0) { await Task.Delay(10); } var alice = Assert.Single(round.Alices); alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Empty(round.Alices); cancellationTokenSource.Cancel(); await Assert.ThrowsAsync <OperationCanceledException>(async() => await task); await roundStateUpdater.StopAsync(CancellationToken.None); await arena.StopAsync(CancellationToken.None); }
public async Task InputCantBeNotedAsync() { using Key key = new(); var outpoint = BitcoinFactory.CreateOutPoint(); WabiSabiConfig cfg = new() { AllowNotedInputRegistration = false }; var round = WabiSabiFactory.CreateRound(cfg); 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)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead; using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(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) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = task1.Result; var aliceClient2 = task2.Result; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, arenaClient, new[] { aliceClient1, aliceClient2 }); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, IKeyChain keyChain, SmartCoin coin1, SmartCoin coin2) { // Get the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); // Refresh the Arena States because of vsize manipulation. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(CancellationToken.None); var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, keyChain, roundStateUpdater, CancellationToken.None); var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, keyChain, roundStateUpdater, CancellationToken.None); while (Phase.ConnectionConfirmation != round.Phase) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = await task1; var aliceClient2 = await task2; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, arenaClient, new[] { aliceClient1, aliceClient2 }); }
public async Task InputSpentAsync() { using Key key = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var mockRpc = new Mock <IRPCClient>(); mockRpc.Setup(rpc => rpc.GetTxOutAsync(It.IsAny <uint256>(), It.IsAny <int>(), It.IsAny <bool>())) .ReturnsAsync((NBitcoin.RPC.GetTxOutResponse?)null); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc, round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), key, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); // Register Alices. var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network)); var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network)); await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena)); await aliceClient1.ConfirmConnectionAsync( TimeSpan.FromMilliseconds(100), new long[] { coin1.EffectiveValue(round.FeeRate) }, new long[] { round.MaxVsizeAllocationPerAlice - coin1.ScriptPubKey.EstimateInputVsize() }, roundStateUpdater, CancellationToken.None).ConfigureAwait(false); await aliceClient2.ConfirmConnectionAsync( TimeSpan.FromMilliseconds(100), new long[] { coin2.EffectiveValue(round.FeeRate) }, new long[] { round.MaxVsizeAllocationPerAlice - coin2.ScriptPubKey.EstimateInputVsize() }, roundStateUpdater, CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey1 = new Key(); using var destKey2 = new Key(); await bobClient.RegisterOutputAsync( coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient1.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient1.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); await bobClient.RegisterOutputAsync( coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()), destKey1.PubKey.WitHash.ScriptPubKey, aliceClient2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber), aliceClient2.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber), CancellationToken.None).ConfigureAwait(false); return(round, aliceClient1, aliceClient2); }
public async Task ReissueExactDeltaAmountAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); // Step 1. Create credentials var(amClient, vsClient, amIssuer, vsIssuer, amZeroCredentials, vsZeroCredentials) = WabiSabiFactory.CreateWabiSabiClientsAndIssuers(round); var amountsToRequest = new[] { alice.CalculateRemainingAmountCredentials(round.Parameters.MiningFeeRate, round.Parameters.CoordinationFeeRate).Satoshi }; var(amCredentialRequest, amValid) = amClient.CreateRequest( amountsToRequest, amZeroCredentials, // FIXME doesn't make much sense CancellationToken.None); var startingVsizeCredentialAmount = 100L; // any number is okay here for this test var(vsCredentialRequest, weValid) = vsClient.CreateRequest( new[] { startingVsizeCredentialAmount }, vsZeroCredentials, // FIXME doesn't make much sense CancellationToken.None); var amResp = amIssuer.HandleRequest(amCredentialRequest); var weResp = vsIssuer.HandleRequest(vsCredentialRequest); var amountCredentialsToPresent = amClient.HandleResponse(amResp, amValid).ToArray(); var vsizeCredentialsToPresent = vsClient.HandleResponse(weResp, weValid).ToArray(); // Step 2. var invalidVsizesToRequest = vsizeCredentialsToPresent.Select(x => 2 * x.Value); // we request the double than what we have. var(realVsizeCredentialRequest, realVsizeCredentialResponseValidation) = vsClient.CreateRequest( invalidVsizesToRequest, vsizeCredentialsToPresent, CancellationToken.None); var(realAmountCredentialRequest, realAmountCredentialResponseValidation) = amClient.CreateRequest( amountsToRequest, amountCredentialsToPresent, CancellationToken.None); var zeroAmountCredentialRequestData = amClient.CreateRequestForZeroAmount(); var zeroVsizeCredentialRequestData = vsClient.CreateRequestForZeroAmount(); // hit Arena directly to verify it prevents requesting more vsize credentials than what are presented. // we have to bypass the ArenaClient because it also prevents this invalid requests and breaks the circuit // early, not allowing to hit Arena. var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.ReissuanceAsync( new ReissueCredentialRequest( round.Id, realAmountCredentialRequest, realVsizeCredentialRequest, zeroAmountCredentialRequestData.CredentialsRequest, zeroVsizeCredentialRequestData.CredentialsRequest), CancellationToken.None).ConfigureAwait(false)); Assert.Equal(WabiSabiProtocolErrorCode.DeltaNotZero, ex.ErrorCode); // Step 2a. Now we verify the client also implements the same verifications. var arenaClient = WabiSabiFactory.CreateArenaClient(arena); await Assert.ThrowsAsync <InvalidOperationException>(async() => await arenaClient.ReissueCredentialAsync( round.Id, amountsToRequest, invalidVsizesToRequest, // we request the double than what we can amountCredentialsToPresent, vsizeCredentialsToPresent, CancellationToken.None)); await arena.StopAsync(CancellationToken.None); }