예제 #1
0
    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);
    }
예제 #2
0
        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);
    }
예제 #6
0
        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
        }
예제 #7
0
        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);
        }