public async Task CreatesRoundIfNoneInputRegistrationAsync() { 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 = CreateArena(cfg, mockRpc); Assert.Empty(arena.Rounds); await arena.StartAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds); round.SetPhase(Phase.ConnectionConfirmation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(2, arena.Rounds.Count); 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 AsyncNotifyTest() { /* * IClientMessageHandler<AmpMessage> handler, * IRpcClient<AmpMessage> rpcClient, * ISerializer serializer, * ILogger<DefaultCallInvoker> logger */ var handler = new DefaultClientMessageHandler(); var client = new MockRpcClient(handler); var serializer = new JsonSerializer(); var logger = NullLogger <DefaultCallInvoker> .Instance; var invoker = new DefaultCallInvoker(handler, client, serializer, logger); var req = new FooReq { FooWord = "hello dotbpe" }; var result = await invoker.AsyncNotify("FooService.Foo", "default", 100, 1, req); Assert.NotNull(result); Assert.Equal(0, result.Code); Assert.NotNull(client.ReceiveMessage); Assert.Equal(100, client.ReceiveMessage.ServiceId); Assert.Equal(1, client.ReceiveMessage.MessageId); Assert.Equal("FooService.Foo", client.ReceiveMessage.FriendlyServiceName); }
public async Task TryOptimizeFeesTestAsync() { var rpc = new MockRpcClient(); rpc.Network = Network.Main; rpc.OnEstimateSmartFeeAsync = (confTarget, estMode) => Task.FromResult(new EstimateSmartFeeResponse { Blocks = 1, FeeRate = new FeeRate(10m) }); var roundConfig = new CoordinatorRoundConfig(); var utxoReferee = new UtxoReferee(Network.Main, "./", rpc, roundConfig); var confirmationTarget = 12; var round = new CoordinatorRound(rpc, utxoReferee, roundConfig, adjustedConfirmationTarget: confirmationTarget, confirmationTarget, roundConfig.ConfirmationTargetReductionRate, TimeSpan.FromSeconds(roundConfig.InputRegistrationTimeout));
private BlockNotifier CreateNotifier(ConcurrentChain chain) { var rpc = new MockRpcClient(); rpc.OnGetBestBlockHashAsync = () => Task.FromResult(chain.Tip.HashBlock); rpc.OnGetBlockAsync = (blockHash) => { var block = rpc.Network.Consensus.ConsensusFactory.CreateBlock(); block.Header = chain.GetBlock(blockHash).Header; return(Task.FromResult(block)); }; rpc.OnGetBlockHeaderAsync = (blockHash) => Task.FromResult(chain.GetBlock(blockHash).Header); var notifier = new BlockNotifier(TimeSpan.FromMilliseconds(100), rpc); return(notifier); }
public async Task UnsynchronizedBitcoinNodeAsync() { var rpc = new MockRpcClient { OnGetBlockchainInfoAsync = () => Task.FromResult(new BlockchainInfo { Headers = 0, Blocks = 0, InitialBlockDownload = false }), }; var blockNotifier = new BlockNotifier(TimeSpan.MaxValue, rpc); var indexer = new IndexBuilderService(rpc, blockNotifier, "."); indexer.Synchronize(); await Task.Delay(TimeSpan.FromSeconds(1)); //// Assert.False(indexer.IsRunning); // <------------ ERROR: it should have stopped but there is a bug for RegTest Assert.Throws <ArgumentOutOfRangeException>(() => indexer.GetLastFilter()); // There are no filters }
public async Task InitializesRoundAsync() { 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); Assert.Single(arena.Rounds); await arena.StopAsync(CancellationToken.None); }
public async Task TimeoutSufficientPeersAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 1, TransactionSigningTimeout = TimeSpan.Zero, OutputRegistrationTimeout = TimeSpan.Zero }; var mockRpc = new MockRpcClient(); mockRpc.OnSendRawTransactionAsync = _ => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); using Key key2 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroWeightCredentialRequests); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroWeightCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); var alice1 = round.Alices.Single(x => x.Id == irres1.AliceId); var alice2 = round.Alices.Single(x => x.Id == irres2.AliceId); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient weightClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.weightClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.weightClient.HandleResponse(ccresp1.RealWeightCredentials !, ccreq1.weightValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.weightClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.weightClient.HandleResponse(ccresp2.RealWeightCredentials !, ccreq2.weightValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. foreach (var orreq in WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps)) { var orresp = await arena.RegisterOutputAsync(orreq); } // Make sure not all alices signed. var alice3 = WabiSabiFactory.CreateAlice(); alice3.ConfirmedConnection = true; round.Alices.Add(alice3); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Coinjoin.Clone(); var coin1 = alice1.Coins.First(); var coin2 = alice2.Coins.First(); var idx1 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin1.Outpoint)); var idx2 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin2.Outpoint)); signedCoinJoin.Sign(key1.GetBitcoinSecret(Network.Main), coin1); var txsigreq1 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx1, signedCoinJoin.Inputs[idx1].WitScript) }); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), coin2); var txsigreq2 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx2, signedCoinJoin.Inputs[idx2].WitScript) }); await arena.SignTransactionAsync(txsigreq1); await arena.SignTransactionAsync(txsigreq2); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round.Id, arena.Rounds.Keys); Assert.Single(arena.Rounds.Where(x => x.Value.IsBlameRound)); var badOutpoint = alice3.Coins.Select(x => x.Outpoint).First(); Assert.Contains(badOutpoint, arena.Prison.GetInmates().Select(x => x.Utxo)); var blameRound = arena.Rounds.Single(x => x.Value.IsBlameRound).Value; Assert.True(blameRound.IsBlameRound); Assert.NotNull(blameRound.BlameOf); Assert.Equal(round.Id, blameRound.BlameOf?.Id); var whitelist = blameRound.BlameWhitelist; Assert.Contains(alice1.Coins.Select(x => x.Outpoint).First(), whitelist); Assert.Contains(alice2.Coins.Select(x => x.Outpoint).First(), whitelist); Assert.DoesNotContain(badOutpoint, whitelist); await arena.StopAsync(CancellationToken.None); } }
public async Task AlicesSpentAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; var mockRpc = new MockRpcClient(); mockRpc.OnSendRawTransactionAsync = _ => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); mockRpc.OnGetTxOutAsync ??= (_, _, _) => null; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); using Key key2 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroWeightCredentialRequests); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroWeightCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); var alice1 = round.Alices.Single(x => x.Id == irres1.AliceId); var alice2 = round.Alices.Single(x => x.Id == irres2.AliceId); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient weightClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.weightClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.weightClient.HandleResponse(ccresp1.RealWeightCredentials !, ccreq1.weightValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.weightClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.weightClient.HandleResponse(ccresp2.RealWeightCredentials !, ccreq2.weightValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. foreach (var orreq in WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps)) { var orresp = await arena.RegisterOutputAsync(orreq); } await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Coinjoin.Clone(); var coin1 = alice1.Coins.First(); var coin2 = alice2.Coins.First(); var idx1 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin1.Outpoint)); var idx2 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin2.Outpoint)); signedCoinJoin.Sign(key1.GetBitcoinSecret(Network.Main), coin1); var txsigreq1 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx1, signedCoinJoin.Inputs[idx1].WitScript) }); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), coin2); var txsigreq2 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx2, signedCoinJoin.Inputs[idx2].WitScript) }); await arena.SignTransactionAsync(txsigreq1); await arena.SignTransactionAsync(txsigreq2); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round.Id, arena.Rounds.Keys); // There should be no inmate, because we aren't punishing spenders with banning // as there's no reason to ban already spent UTXOs, // the cost of spending the UTXO is the punishment instead. Assert.Empty(arena.Prison.GetInmates()); await arena.StopAsync(CancellationToken.None); }