public async Task <uint256> WaitOfferAsync(OfferData offer, CancellationToken cancellation = default(CancellationToken)) { var scriptPubKey = offer.CreateScriptPubkey(); var chain = GetChain(offer.Initiator.Asset.Chain); return(await WaitConfirmationCoreAsync(chain, scriptPubKey, offer.Initiator.Asset.Amount, 0, cancellation)); }
public async Task <uint256> BroadcastOffer(OfferData offerData, bool watch) { var intitator = GetChain(offerData.Initiator.Asset.Chain); Transaction tx = new Transaction(); tx.AddOutput(offerData.CreateOffer()); if (watch) { await intitator.RPCClient.ImportAddressAsync(offerData.CreateScriptPubkey()).ConfigureAwait(false); } return(await FundAndBroadcast(intitator.RPCClient, tx).ConfigureAwait(false)); }
private async Task Take(TakeOptions o) { if (o.Offer == null) { throw new FormatException(); } var offer = OfferData.Parse(o.Offer); var counterOffer = offer.CreateCounterOffer(); if (Repository.GetPrivateKey(offer.Taker.PubKey) == null) { Console.WriteLine("This offer does not use our pubkey"); return; } //TODO need to check the timelocks if (!AutoAccept) { Console.WriteLine($"Do you accept to exchange {counterOffer.Initiator.Asset} against {counterOffer.Taker.Asset}? (yes to accept)"); while (true) { var line = Console.ReadLine(); if (line.Equals("yes", StringComparison.OrdinalIgnoreCase)) { break; } } } var swapper = CreateSwapper(); var waitingOffer = swapper.WaitOfferAsync(offer); var waitingCounterOfferTaken = swapper.WaitOfferTakenAsync(counterOffer); var txId = await swapper.BroadcastOffer(counterOffer, true).ConfigureAwait(false); Console.WriteLine("Counter offer broadcasted " + txId); Console.WriteLine("Waiting the offer to be broadcasted..."); txId = await waitingOffer.ConfigureAwait(false); Console.WriteLine("Offer broadcasted " + txId); Console.WriteLine("Waiting the counter offer to be taken..."); blocked.Set(); await waitingCounterOfferTaken.ConfigureAwait(false); blocked.Reset(); Console.WriteLine("Counter offer taken by the other party"); txId = await swapper.TakeOffer(offer).ConfigureAwait(false); Console.WriteLine("Offer taken"); Console.WriteLine("Exchange complete"); }
public async Task <uint256> TakeOffer(OfferData offer) { var taker = GetChain(offer.Taker.Asset.Chain); var initiator = GetChain(offer.Initiator.Asset.Chain); var preimage = _Repository.GetPreimage(offer.Hash); if (preimage == null) { throw new InvalidOperationException("Unknown preimage"); } var key = _Repository.GetPrivateKey(offer.Taker.PubKey); if (key == null) { throw new InvalidOperationException("Unknown pubkey"); } var offerOutpoint = _Repository.GetOffer(offer.CreateScriptPubkey()); if (offerOutpoint == null) { throw new InvalidOperationException("Unknown offer"); } var destination = await initiator.RPCClient.GetNewAddressAsync().ConfigureAwait(false); var tx = new Transaction(); tx.AddInput(new TxIn(offerOutpoint, offer.TakeOffer(new TransactionSignature(key.Sign(uint256.One), SigHash.All), preimage))); tx.AddOutput(new TxOut(offer.Initiator.Asset.Amount, destination)); var fee = (await GetFeeAsync(initiator).ConfigureAwait(false)).GetFee(tx.GetVirtualSize()); tx.Outputs[0].Value -= fee; var offerCoin = new Coin(offerOutpoint, new TxOut(offer.Initiator.Asset.Amount, offer.CreateScriptPubkey())).ToScriptCoin(offer.CreateRedeemScript()); var sig = tx.Inputs.AsIndexedInputs().First().Sign(key, offerCoin, SigHash.All); tx.Inputs[0].ScriptSig = offer.TakeOffer(sig, preimage); await initiator.RPCClient.SendRawTransactionAsync(tx).ConfigureAwait(false); return(tx.GetHash()); }
public async Task <bool> WaitOfferTakenAsync(OfferData offerData, CancellationToken cancellation = default(CancellationToken)) { var decodedTxById = new Dictionary <uint256, JObject>(); var initiator = GetChain(offerData.Initiator.Asset.Chain); var redeemHash = offerData.CreateRedeemScript().Hash; int offset = 0; int takeCount = 10; while (true) { cancellation.ThrowIfCancellationRequested(); var transactions = await initiator.RPCClient.SendCommandAsync(RPCOperations.listtransactions, "*", takeCount, offset, true).ConfigureAwait(false); offset += takeCount; //If we reach end of the list, start over if (transactions.Result == null || ((JArray)transactions.Result).Count() == 0) { offset = 0; await Task.Delay(1000, cancellation).ConfigureAwait(false); continue; } bool startOver = false; //Check if the preimage is present foreach (var tx in ((JArray)transactions.Result)) { int confirmation = 0; if (tx["confirmations"] != null) { confirmation = tx["confirmations"].Value <int>(); } //Old transaction, skip if (confirmation > 144) { startOver = true; continue; } //Coinbase we can't have the preimage var category = tx["category"]?.Value <string>(); if (category == "immature" || category == "generate") { continue; } var txId = new uint256(tx["txid"].Value <string>()); JObject txObj = null; if (!decodedTxById.TryGetValue(txId, out txObj)) { var getTransaction = await initiator.RPCClient.SendCommandAsync("gettransaction", txId.ToString(), true); var decodeRawTransaction = await initiator.RPCClient.SendCommandAsync("decoderawtransaction", getTransaction.Result["hex"]); txObj = (JObject)decodeRawTransaction.Result; decodedTxById.Add(txId, txObj); } foreach (var input in txObj["vin"]) { var scriptSig = input["scriptSig"]["asm"].Value <string>(); var sigParts = scriptSig.Split(' ').ToList(); // Add the hash in txin if segwit if (input["txinwitness"] is JArray scriptWitness) { sigParts.AddRange(scriptWitness.Select(c => c.Value <string>())); } foreach (var sigPart in sigParts) { if (!HexEncoder.IsWellFormed(sigPart)) { continue; } var preimage = new Preimage(Encoders.Hex.DecodeData(sigPart)); if (preimage.GetHash() == offerData.Hash) { _Repository.SavePreimage(preimage); return(true); } } } } //If we reach end of the list, start over if (startOver) { offset = 0; await Task.Delay(1000, cancellation).ConfigureAwait(false); continue; } } }
public Task <uint256> WaitOfferConfirmationAsync(OfferData offer, CancellationToken cancellation = default(CancellationToken)) { return(WaitConfirmationCoreAsync(GetChain(offer.Initiator.Asset.Chain), offer.CreateScriptPubkey(), offer.Initiator.Asset.Amount, 1, cancellation)); }