public async Task SuccessInInputRegistrationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(round); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroVsizeCredentials); Assert.Null(resp.RealAmountCredentials); Assert.Null(resp.RealVsizeCredentials); Assert.NotEqual(preDeadline, alice.Deadline); Assert.True(minAliceDeadline <= alice.Deadline); Assert.False(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var initialRemaining = round.RemainingInputVsizeAllocation; var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); Assert.True(round.RemainingInputVsizeAllocation < initialRemaining); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); // There's no such alice yet, so success. var req = new InputsRemovalRequest(round.Id, Guid.NewGuid()); await handler.RemoveInputAsync(req); // There was the alice we want to remove so success. req = new InputsRemovalRequest(round.Id, alice.Id); await handler.RemoveInputAsync(req); // Ensure that removing an alice freed up the input weight // allocation from the round Assert.Equal(initialRemaining, round.RemainingInputVsizeAllocation); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDeadlineUpdatedAsync() { // Alice's deadline is updated by connection confirmation. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await handler.ConfirmConnectionAsync(req, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.NotEqual(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessInConnectionConfirmationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var resp = await handler.ConfirmConnectionAsync(req); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroWeightCredentials); Assert.NotNull(resp.RealAmountCredentials); Assert.NotNull(resp.RealWeightCredentials); Assert.Equal(preDeadline, alice.Deadline); Assert.True(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task AllConfirmedStepsAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); var a1 = WabiSabiFactory.CreateAlice(round); var a2 = WabiSabiFactory.CreateAlice(round); var a3 = WabiSabiFactory.CreateAlice(round); var a4 = WabiSabiFactory.CreateAlice(round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = true; a3.ConfirmedConnection = true; a4.ConfirmedConnection = true; round.Alices.Add(a1); round.Alices.Add(a2); round.Alices.Add(a3); round.Alices.Add(a4); round.SetPhase(Phase.ConnectionConfirmation); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); Alice alice = WabiSabiFactory.CreateAlice(key: key); round.Alices.Add(alice); var coinjoin = round.Coinjoin; coinjoin.Inputs.Add(alice.Coins.First().Outpoint); round.SetPhase(Phase.TransactionSigning); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var signedCoinJoin = coinjoin.Clone(); signedCoinJoin.Sign(key.GetBitcoinSecret(Network.Main), alice.Coins.First()); var req = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair(0, signedCoinJoin.Inputs[0].WitScript) }); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); await handler.SignTransactionAsync(req); Assert.True(round.Coinjoin.Inputs.First().HasWitScript()); await arena.StopAsync(CancellationToken.None); }
public async Task NotEnoughConfirmedTimedoutDestroysAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5, ConnectionConfirmationTimeout = TimeSpan.Zero }; var round = WabiSabiFactory.CreateRound(cfg); var a1 = WabiSabiFactory.CreateAlice(); var a2 = WabiSabiFactory.CreateAlice(); var a3 = WabiSabiFactory.CreateAlice(); var a4 = WabiSabiFactory.CreateAlice(); a1.ConfirmedConnetion = true; a2.ConfirmedConnetion = false; a3.ConfirmedConnetion = false; a4.ConfirmedConnetion = false; round.Alices.Add(a1); round.Alices.Add(a2); round.Alices.Add(a3); round.Alices.Add(a4); round.SetPhase(Phase.ConnectionConfirmation); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round.Id, arena.Rounds.Keys); Assert.Equal(3, arena.Prison.CountInmates().noted); Assert.Equal(0, arena.Prison.CountInmates().banned); await arena.StopAsync(CancellationToken.None); } }
public async Task InputRegistrationTimeoutCanBeModifiedRuntimeAsync() { WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.FromHours(1), MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); round.InputRegistrationTimeFrame = round.InputRegistrationTimeFrame with { Duration = TimeSpan.Zero }; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); await arena.StopAsync(CancellationToken.None); } }
public async Task DetectSpentTxoBeforeSteppingIntoConnectionConfirmationAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 3 }; var round = WabiSabiFactory.CreateRound(cfg); var offendingAlice = WabiSabiFactory.CreateAlice(round); // this Alice spent the coin after registration var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(); mockRpc.Setup(rpc => rpc.GetTxOutAsync(offendingAlice.Coin.Outpoint.Hash, (int)offendingAlice.Coin.Outpoint.N, true, It.IsAny <CancellationToken>())) .ReturnsAsync((GetTxOutResponse?)null); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(round); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(offendingAlice); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.InputRegistration, round.Phase); Assert.Equal(2, round.Alices.Count); // the offending alice was removed round.Alices.Add(WabiSabiFactory.CreateAlice(round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); Assert.Equal(3, round.Alices.Count); await arena.StopAsync(CancellationToken.None); }
public async Task BlameRoundTimedoutWithSufficientInputsAsync() { WabiSabiConfig cfg = new() { BlameInputRegistrationTimeout = TimeSpan.Zero, StandardInputRegistrationTimeout = TimeSpan.FromHours(1), // Test that this is disregarded. MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); var alice1 = WabiSabiFactory.CreateAlice(round); var alice2 = WabiSabiFactory.CreateAlice(round); var alice3 = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice1); round.Alices.Add(alice2); round.Alices.Add(alice3); var blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); blameRound.Alices.Add(alice1); blameRound.Alices.Add(alice2); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(blameRound); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, blameRound.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task BlameRoundTimedoutWithoutSufficientInputsAsync() { // This test also tests that the min input count multiplier is applied // against the max input count by round number and not against the // number of inputs awaited by the blame round itself. WabiSabiConfig cfg = new() { BlameInputRegistrationTimeout = TimeSpan.Zero, StandardInputRegistrationTimeout = TimeSpan.FromHours(1), // Test that this is disregarded. MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); var alice1 = WabiSabiFactory.CreateAlice(round); var alice2 = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice1); round.Alices.Add(alice2); var blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); blameRound.Alices.Add(alice1); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(blameRound); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.Ended, blameRound.Phase); Assert.DoesNotContain(blameRound, arena.GetActiveRounds()); 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 AliceDoesntTimeoutInConnectionConfirmationAsync() { // Alice does not time out when it's not input registration anymore, // even though the deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.Equal(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDoesntTimeoutIfMaxInputCountReachedAsync() { // Alice does not time out if input reg is full with alices, // even though the deadline is reached and still in input reg. WabiSabiConfig cfg = new() { MaxInputCountByRound = 3 }; var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); Assert.Equal(3, round.Alices.Count); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(3, round.Alices.Count); Assert.Equal(preDeadline, alice.Deadline); 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 InputWhitelistedButBannedAsync() { using Key key = new(); var alice = WabiSabiFactory.CreateAlice(key); var bannedCoin = alice.Coin.Outpoint; WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(alice); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(alice.Coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc, round, blameRound); arena.Prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero); await using ArenaRequestHandler handler = new(cfg, arena.Prison, arena, mockRpc.Object); var req = WabiSabiFactory.CreateInputRegistrationRequest(key: key, round: blameRound, prevout: bannedCoin); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead; round.Alices.Add(WabiSabiFactory.CreateAlice(round)); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.OutputRegistration) { var req = WabiSabiFactory.CreateOutputRegistrationRequest(round); round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } await arena.StopAsync(CancellationToken.None); }
public async Task IncorrectRequestedVsizeCredentialsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); Assert.Contains(alice, round.Alices); var incorrectVsizeCredentials = WabiSabiFactory.CreateRealCredentialRequests(round, null, 234).vsizeRequest; var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round) with { RealVsizeCredentialRequests = incorrectVsizeCredentials }; var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedVsizeCredentials, ex.ErrorCode); Assert.False(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task WrongCoinjoinSignatureAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key1 = new(); Alice alice1 = WabiSabiFactory.CreateAlice(key: key1); using Key key2 = new(); Alice alice2 = WabiSabiFactory.CreateAlice(key: key2); round.Alices.Add(alice1); round.Alices.Add(alice2); var coinjoin = round.Coinjoin; coinjoin.Inputs.Add(alice1.Coins.First().Outpoint); coinjoin.Inputs.Add(alice2.Coins.First().Outpoint); round.SetPhase(Phase.TransactionSigning); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); // Submit the signature for the second alice to the first alice's input. var signedCoinJoin = coinjoin.Clone(); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), alice2.Coins.First()); var req = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair(0, signedCoinJoin.Inputs[0].WitScript) }); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.SignTransactionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.WrongCoinjoinSignature, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessInConnectionConfirmationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroVsizeCredentials); Assert.NotNull(resp.RealAmountCredentials); Assert.NotNull(resp.RealVsizeCredentials); Assert.Equal(preDeadline, alice.Deadline); Assert.True(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task IncorrectRequestedAmountCredentialsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); req = new( req.RoundId, req.AliceId, req.ZeroAmountCredentialRequests, WabiSabiFactory.CreateRealCredentialRequests(round, Money.Coins(3), null).amountReq, req.ZeroWeightCredentialRequests, req.RealWeightCredentialRequests); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.ConfirmConnectionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials, ex.ErrorCode); Assert.False(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task AliceAlreadyRegisteredCrossRoundAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var anotherRound = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, anotherRound); using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); // Make sure an Alice have already been registered with the same input. var preAlice = WabiSabiFactory.CreateAlice(req.InputRoundSignaturePairs); anotherRound.Alices.Add(preAlice); anotherRound.SetPhase(Phase.ConnectionConfirmation); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); var alice = WabiSabiFactory.CreateAlice(); var preDeadline = alice.Deadline; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var round = arena.Rounds.First().Value; round.Alices.Add(alice); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation) { round.SetPhase(phase); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.ConfirmConnectionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } Assert.Equal(preDeadline, alice.Deadline); Assert.False(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); Round round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); // Refresh the Arena States because of vsize manipulation. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.OutputRegistration) { var req = WabiSabiFactory.CreateOutputRegistrationRequest(round); round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } await arena.StopAsync(CancellationToken.None); }
public async Task NotAllConfirmedStaysAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); var a1 = WabiSabiFactory.CreateAlice(round); var a2 = WabiSabiFactory.CreateAlice(round); var a3 = WabiSabiFactory.CreateAlice(round); var a4 = WabiSabiFactory.CreateAlice(round); a1.ConfirmedConnection = true; a2.ConfirmedConnection = true; a3.ConfirmedConnection = true; a4.ConfirmedConnection = false; round.Alices.Add(a1); round.Alices.Add(a2); round.Alices.Add(a3); round.Alices.Add(a4); round.SetPhase(Phase.ConnectionConfirmation); Prison prison = new(); using Arena arena = await ArenaBuilder.From(cfg, prison).CreateAndStartAsync(round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); Assert.Equal(0, prison.CountInmates().noted); Assert.Equal(0, prison.CountInmates().banned); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var round = arena.Rounds.First(); round.Alices.Add(WabiSabiFactory.CreateAlice()); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.OutputRegistration) { var req = WabiSabiFactory.CreateOutputRegistrationRequest(round); round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } await arena.StopAsync(CancellationToken.None); }
public async Task CreatesRoundIfInBlameInputRegistrationAsync() { WabiSabiConfig cfg = new(); var mockRpc = new MockRpcClient(); mockRpc.OnEstimateSmartFeeAsync = async(target, _) => await Task.FromResult(new EstimateSmartFeeResponse { Blocks = target, FeeRate = new FeeRate(10m) }); using Arena arena = new Arena(TimeSpan.FromSeconds(1), Network.Main, cfg, mockRpc, new Prison()); Assert.Empty(arena.Rounds); await arena.StartAsync(CancellationToken.None).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var round = Assert.Single(arena.Rounds).Value; round.SetPhase(Phase.ConnectionConfirmation); round.Alices.Add(WabiSabiFactory.CreateAlice()); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); Assert.Equal(Phase.InputRegistration, blameRound.Phase); arena.Rounds.Add(blameRound.Id, blameRound); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); Assert.Equal(3, arena.Rounds.Count); Assert.Equal(2, arena.Rounds.Where(x => x.Value.Phase == Phase.InputRegistration).Count()); await arena.StopAsync(CancellationToken.None); }
public async Task TimeoutSufficientPeersAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 1, TransactionSigningTimeout = TimeSpan.Zero, OutputRegistrationTimeout = TimeSpan.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>(), It.IsAny <CancellationToken>())) .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null)); Prison prison = new(); using Arena arena = await ArenaBuilder.From(cfg, mockRpc, prison).CreateAndStartAsync(); var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); // Make sure not all alices signed. var alice3 = WabiSabiFactory.CreateAlice(round); alice3.ConfirmedConnection = true; round.Alices.Add(alice3); round.CoinjoinState = round.Assert <ConstructionState>().AddInput(alice3.Coin); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Assert <SigningState>().CreateTransaction(); await aliceClient1.SignTransactionAsync(signedCoinJoin, keyChain, CancellationToken.None); await aliceClient2.SignTransactionAsync(signedCoinJoin, keyChain, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.Rounds.Where(x => x.Phase != Phase.Ended)); Assert.Single(arena.Rounds.Where(x => x is BlameRound)); var badOutpoint = alice3.Coin.Outpoint; Assert.Contains(badOutpoint, prison.GetInmates().Select(x => x.Utxo)); var onlyRound = arena.Rounds.Single(x => x is BlameRound); var blameRound = Assert.IsType <BlameRound>(onlyRound); Assert.NotNull(blameRound.BlameOf); Assert.Equal(round.Id, blameRound.BlameOf.Id); var whitelist = blameRound.BlameWhitelist; Assert.Contains(aliceClient1.SmartCoin.OutPoint, whitelist); Assert.Contains(aliceClient2.SmartCoin.OutPoint, whitelist); Assert.DoesNotContain(badOutpoint, whitelist); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDoesntTimeoutIfInputRegistrationTimedoutAsync() { // Alice does not time out if input registration timed out, // even though the deadline is reached and still in input reg. WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero }; var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.Equal(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationFullWeightAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); // TODO add configuration parameter round.InitialInputVsizeAllocation = (int)round.PerAliceVsizeAllocation; // Add an alice await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); round.Alices.Add(WabiSabiFactory.CreateAlice()); Assert.Equal(0, round.RemainingInputVsizeAllocation); // try to add an additional input var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchTotalWeight, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }