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 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 InputWhitelistedButBannedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); var alice = WabiSabiFactory.CreateAlice(key, round); var bannedCoin = alice.Coin.Outpoint; round.Alices.Add(alice); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(alice.Coin); Prison prison = new(); using Arena arena = await ArenaBuilder.From(cfg, mockRpc, prison).CreateAndStartAsync(round, blameRound); prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero); var req = WabiSabiFactory.CreateInputRegistrationRequest(key: key, round: blameRound, prevout: bannedCoin); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterInputAsync(req, CancellationToken.None)); Assert.Equal(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 AliceDeadlineUpdatedAsync() { // Alice's deadline is updated by connection confirmation. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); 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.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 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 WrongPhaseAsync() { WabiSabiConfig cfg = new(); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(); var round = arena.Rounds.First(); var alice = WabiSabiFactory.CreateAlice(round); var preDeadline = alice.Deadline; round.Alices.Add(alice); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation) { round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } Assert.Equal(preDeadline, alice.Deadline); Assert.False(alice.ConfirmedConnection); 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 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 ArenaBuilder.From(cfg).CreateAndStartAsync(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 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 ArenaBuilder.From(cfg).CreateAndStartAsync(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 WrongPhaseAsync() { WabiSabiConfig cfg = new(); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = arena.Rounds.First(); var req = new TransactionSignaturesRequest(round.Id, 0, WitScript.Empty); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.TransactionSigning) { round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.SignTransactionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, 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(); var req = new InputsRemovalRequest(round.Id, Guid.NewGuid()); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration) { round.SetPhase(phase); var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.RemoveInputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } 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); round.Alices.Add(alice); Assert.True(round.RemainingInputVsizeAllocation < initialRemaining); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); // There's no such alice yet, so success. var req = new InputsRemovalRequest(round.Id, Guid.NewGuid()); await arena.RemoveInputAsync(req, CancellationToken.None); // There was the alice we want to remove so success. req = new InputsRemovalRequest(round.Id, alice.Id); await arena.RemoveInputAsync(req, CancellationToken.None); // Ensure that removing an alice freed up the input vsize // allocation from the round Assert.Equal(initialRemaining, round.RemainingInputVsizeAllocation); 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 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 DoesntSwitchImmaturelyAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey = new Key(); await bobClient.RegisterOutputAsync( destKey.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); 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 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); }
private static Arena CreateArena(WabiSabiConfig cfg, IRPCClient rpc) { var arenaBuilder = ArenaBuilder.From(cfg).With(rpc); arenaBuilder.Period = TimeSpan.FromSeconds(1); return(arenaBuilder.Create()); }
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 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 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 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 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 RemoveInputAsyncTestAsync() { var config = new WabiSabiConfig(); var round = WabiSabiFactory.CreateRound(config); round.SetPhase(Phase.ConnectionConfirmation); var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1); var coin = fundingTx.WalletOutputs.First().Coin; var alice = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid(), false); round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(config).CreateAndStartAsync(round); using var memoryCache = new MemoryCache(new MemoryCacheOptions()); var idempotencyRequestCache = new IdempotencyRequestCache(memoryCache); using CoinJoinFeeRateStatStore coinJoinFeeRateStatStore = new(config, arena.Rpc); var wabiSabiApi = new WabiSabiController(idempotencyRequestCache, arena, coinJoinFeeRateStatStore); var apiClient = new ArenaClient(null !, null !, wabiSabiApi); round.SetPhase(Phase.InputRegistration); await apiClient.RemoveInputAsync(round.Id, alice.Id, CancellationToken.None); Assert.Empty(round.Alices); }
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 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 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 DiffTooSmallToBlameAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero, CoordinationFeeRate = CoordinationFeeRate.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // 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, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await bobClient.RegisterOutputAsync( destKey2.PubKey.WitHash.ScriptPubKey, amountCredentials2.Take(ProtocolConstants.CredentialNumber), vsizeCredentials2.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); // Add another input. The input must be able to pay for itself, but // the remaining amount after deducting the fees needs to be less // than the minimum. var txParams = round.Assert <ConstructionState>().Parameters; var extraAlice = WabiSabiFactory.CreateAlice(txParams.FeeRate.GetFee(Constants.P2wpkhInputVirtualSize) + txParams.AllowedOutputAmounts.Min - new Money(1L), round); round.Alices.Add(extraAlice); round.CoinjoinState = round.Assert <ConstructionState>().AddInput(extraAlice.Coin); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(3, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); Assert.DoesNotContain(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }
private int _agentDecisionInterval; // To replace with a call to DecisionRequester.DecisionPeriod if possible // (not possible at the moment as it's internal and we cannot call GetComponent on internals) internal void Awake() { _builder = new ArenaBuilder(gameObject, spawnedObjectsHolder, maxSpawnAttemptsForPrefabs, maxSpawnAttemptsForAgent); _environmentManager = GameObject.FindObjectOfType <EnvironmentManager>(); agent = transform.FindChildWithTag("agent").GetComponent <Agent>(); _agentDecisionInterval = transform.FindChildWithTag("agent").GetComponent <DecisionPeriod>().decisionPeriod; _fades = blackScreens.GetFades(); }
/// <summary> /// This function reloads the arena with a new difficulty. /// </summary> /// <param name="difficulty">The new arena's difficulty</param> public void loadNewArena(ArenaDifficulty difficulty) { collidables = new List<Entity>(); if (content == null) content = new ContentManager(SceneManager.Game.Services, "Content"); //populate the GameSave object GameSave.seed = Environment.TickCount; GameSave.level = controller.getLevel(); GameSave.partyHealth = PartyUtils.getPartyHealth(); GameSave.score = controller.getScore(); //SAVE SaveUtils.getInstance().saveGame(GameSave); controller.setGenerator(new Random(GameSave.seed)); // Set the new ambience colour ambientColour = controller.getAmbience(); // Generate the arena ArenaBuilder builder = new ArenaBuilder(controller.getLevelSize(), controller.getLevelSize(), content, SceneManager.GraphicsDevice.Viewport.AspectRatio, difficulty); baseArena = builder.buildArenaBase(); StartTile = builder.getStartTile(); // if (effect == null) effect = new BasicEffect(SceneManager.Game.GraphicsDevice);//null Hero = new Character(); camera = new Camera(effect, SceneManager.Game.Window.ClientBounds.Width, SceneManager.Game.Window.ClientBounds.Height, SceneManager.GraphicsDevice.Viewport.AspectRatio, Hero.getPOSITION()); //load model Hero.LoadModel(content, SceneManager.GraphicsDevice.Viewport.AspectRatio); potionsUsed = 0; MysteryBoxUsed = false; // Load the level start conversation currentConversation = DialogueUtils.makeConversation((ConversationType)controller.getLevel() - 1); currentConversation.load(content); //load score dispaly ScoreDisplay = new Scoring(); ScoreDisplay.load(content); // Debug arena printDebugArena(); }
/** * This function is called when a scene is made active. */ public override void loadScene(ContentManager content) { collidables = new List<Entity>(); if (content == null) content = new ContentManager(SceneManager.Game.Services, "Content"); // Load the shader HLSLeffect = content.Load<Effect>("Effects/Shader"); if (!loaded) { GameSave.seed = Environment.TickCount; GameSave.level = controller.getLevel(); GameSave.partyHealth = PartyUtils.getPartyHealth(); GameSave.score = controller.getScore(); //SAVE SaveUtils.getInstance().saveGame(GameSave); controller.setGenerator(new Random(GameSave.seed)); } // Generate the arena ArenaBuilder builder = new ArenaBuilder(controller.getLevelSize(), controller.getLevelSize(), content, SceneManager.GraphicsDevice.Viewport.AspectRatio, controller.getLevelDifficulty()); baseArena = builder.buildArenaBase(); StartTile = builder.getStartTile(); // if (effect == null) effect = new BasicEffect(SceneManager.Game.GraphicsDevice);//null FlashLightAngle = MIN_FLASH; Hero = new Character(); camera = new Camera(effect, SceneManager.Game.Window.ClientBounds.Width, SceneManager.Game.Window.ClientBounds.Height, SceneManager.GraphicsDevice.Viewport.AspectRatio, Hero.getPOSITION()); //load model Hero.LoadModel(content, SceneManager.GraphicsDevice.Viewport.AspectRatio); table = new ArenaTable(getStartTile(), content); skybox = new ArenaSkybox(getStartTile(), content); potionsUsed = 0; MysteryBoxUsed = false; //load CombatInfo at top left Vector2 start = new Vector2(10, 45); foreach (PlayerSprite ps in PartyUtils.getParty()) { ps.getCombatInfo().init(content, start); start.Y += 60; } pauseMenu.load(content, SceneManager.GraphicsDevice.Viewport); pauseMenu.center(); // Load the level start conversation currentConversation = DialogueUtils.makeConversation((ConversationType)controller.getLevel() - 1); currentConversation.load(content); //load score dispaly ScoreDisplay = new Scoring(); ScoreDisplay.load(content); }