Exemple #1
0
        public async Task <uint160.MutableUint160> BeginOpenChannel(
            [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))]
            ClassicTumblerParameters tumblerId,
            [FromBody] OpenChannelRequest request)
        {
            if (tumblerId == null)
            {
                throw new ArgumentNullException("tumblerId");
            }
            var height = Services.BlockExplorerService.GetCurrentHeight();

            if (Repository.IsUsed(request.CycleStart, request.Nonce))
            {
                throw new ActionResultException(BadRequest("duplicate-query"));
            }
            var cycle = GetCycle(request.CycleStart);

            if (!cycle.IsInPhase(CyclePhase.TumblerChannelEstablishment, height))
            {
                throw new ActionResultException(BadRequest("invalid-phase"));
            }
            var fee = await Services.FeeService.GetFeeRateAsync();

            try
            {
                if (!Parameters.VoucherKey.PublicKey.Verify(request.Signature, NBitcoin.Utils.ToBytes((uint)request.CycleStart, true), request.Nonce))
                {
                    throw new ActionResultException(BadRequest("incorrect-voucher"));
                }
                if (!Repository.MarkUsedNonce(request.CycleStart, request.Nonce))
                {
                    throw new ActionResultException(BadRequest("duplicate-query"));
                }

                var escrowKey = new Key();

                var escrow = new EscrowScriptPubKeyParameters();
                escrow.LockTime  = cycle.GetTumblerLockTime();
                escrow.Receiver  = request.EscrowKey;
                escrow.Initiator = escrowKey.PubKey;
                var channelId = new uint160(RandomUtils.GetBytes(20));
                Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Asked to open channel");
                var txOut = new TxOut(Parameters.Denomination, escrow.ToScript().WitHash.ScriptPubKey.Hash);

                var unused = Services.WalletService.FundTransactionAsync(txOut, fee)
                             .ContinueWith(async(Task <Transaction> task) =>
                {
                    try
                    {
                        var tx          = await task.ConfigureAwait(false);
                        var correlation = new CorrelationId(channelId);
                        Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerEscrow, tx.GetHash(), correlation);

                        //Logging/Broadcast per funding TX one time
                        if (Repository.MarkUsedNonce(cycle.Start, new uint160(tx.GetHash().ToBytes().Take(20).ToArray())))
                        {
                            var bobCount = Parameters.CountEscrows(tx, Client.Identity.Bob);
                            Logs.Tumbler.LogInformation($"Cycle {cycle.Start} channel created {tx.GetHash()} with {bobCount} users");
                            await Services.BroadcastService.BroadcastAsync(tx).ConfigureAwait(false);
                        }

                        await Services.BlockExplorerService.TrackAsync(txOut.ScriptPubKey).ConfigureAwait(false);
                        Tracker.AddressCreated(cycle.Start, TransactionType.TumblerEscrow, txOut.ScriptPubKey, correlation);
                        var coin    = tx.Outputs.AsCoins().First(o => o.ScriptPubKey == txOut.ScriptPubKey && o.TxOut.Value == txOut.Value);
                        var session = new PromiseServerSession(Parameters.CreatePromiseParamaters());
                        var redeem  = await Services.WalletService.GenerateAddressAsync().ConfigureAwait(false);
                        session.ConfigureEscrowedCoin(channelId, coin.ToScriptCoin(escrow.ToScript()), escrowKey, redeem.ScriptPubKey);
                        var redeemTx = session.CreateRedeemTransaction(fee);
                        Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.TumblerRedeem, correlation, redeemTx);
                        Repository.Save(cycle.Start, session);
                        Tracker.AddressCreated(cycle.Start, TransactionType.TumblerRedeem, redeem.ScriptPubKey, correlation);
                    }
                    catch (Exception ex)
                    {
                        Logs.Tumbler.LogCritical(new EventId(), ex, "Error during escrow transaction callback");
                    }
                });
                return(channelId.AsBitcoinSerializable());
            }
            catch (NotEnoughFundsException ex)
            {
                Logs.Tumbler.LogInformation(ex.Message);
                throw new ActionResultException(BadRequest("tumbler-insufficient-funds"));
            }
        }