public ScriptCoinModel OpenChannel( [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))] ClassicTumblerParameters tumblerId, [FromBody] OpenChannelRequest request) { if (tumblerId == null) { throw new ArgumentNullException("tumblerId"); } var height = Services.BlockExplorerService.GetCurrentHeight(); var cycle = GetCycle(request.CycleStart); if (!cycle.IsInPhase(CyclePhase.TumblerChannelEstablishment, height)) { throw new ActionResultException(BadRequest("invalid-phase")); } var fee = Services.FeeService.GetFeeRate(); try { if (!Parameters.VoucherKey.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("nonce-already-used")); } var escrowKey = new Key(); var escrow = new EscrowScriptPubKeyParameters(); escrow.LockTime = cycle.GetTumblerLockTime(); escrow.Receiver = request.EscrowKey; escrow.Initiator = escrowKey.PubKey; Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Asked to open channel"); var txOut = new TxOut(Parameters.Denomination, escrow.ToScript().Hash); var tx = Services.WalletService.FundTransaction(txOut, fee); var correlation = escrow.GetCorrelation(); var escrowTumblerLabel = $"Cycle {cycle.Start} Tumbler Escrow"; Services.BlockExplorerService.Track(txOut.ScriptPubKey); Tracker.AddressCreated(cycle.Start, TransactionType.TumblerEscrow, txOut.ScriptPubKey, correlation); Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerEscrow, tx.GetHash(), correlation); Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Channel created " + tx.GetHash()); var coin = tx.Outputs.AsCoins().First(o => o.ScriptPubKey == txOut.ScriptPubKey && o.TxOut.Value == txOut.Value); var session = new PromiseServerSession(Parameters.CreatePromiseParamaters()); var redeem = Services.WalletService.GenerateAddress(); session.ConfigureEscrowedCoin(coin.ToScriptCoin(escrow.ToScript()), escrowKey, redeem.ScriptPubKey); Repository.Save(cycle.Start, session); Services.BroadcastService.Broadcast(tx); var redeemTx = session.CreateRedeemTransaction(fee); Tracker.AddressCreated(cycle.Start, TransactionType.TumblerRedeem, redeem.ScriptPubKey, correlation); Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.TumblerRedeem, correlation, redeemTx); return(new ScriptCoinModel(session.EscrowedCoin)); } catch (PuzzleException) { throw new ActionResultException(BadRequest("incorrect-voucher")); } catch (NotEnoughFundsException ex) { Logs.Tumbler.LogInformation(ex.Message); throw new ActionResultException(BadRequest("tumbler-insufficient-funds")); } }