public static bool IsSwapTransaction(RVN_RPC RPC, dynamic DecodedTransaction, int Block, out RC_Swap Swap) { /* * * A lot of this code is simmilar/duplicated from ListingEntry.cs * Primarily to differentiate between listing submissions and detected on-chain transactions * */ Swap = null; //if (!(DecodedTransaction.vin.Count >= 1)) return false; var vin_asm = DecodedTransaction.vin?[0]?.scriptSig?.asm?.ToString(); if (vin_asm == null) { return(false); } if (!vin_asm.Contains(Constants.SINGLE_ANYONECANPAY)) { return(false); } //Ins/Outs of the person who setup the swap var swap_setup_vin = DecodedTransaction.vin[0]; var swap_setup_vout = DecodedTransaction.vout[0]; //Ins/Outs of the person who executed the swap var swap_result_vin = new List <dynamic>(); // DecodedTransaction.vin.Skip(1).ToList(); var swap_result_vout = new List <dynamic>(); // DecodedTransaction.vout.Skip(1).ToList(); for (int vin_idx = 1; vin_idx < DecodedTransaction.vin.Count; vin_idx++) { var tx_vin = DecodedTransaction.vin[vin_idx]; if (tx_vin.scriptSig?.asm?.ToString()?.Contains(Constants.SINGLE_ANYONECANPAY)) { //If we see [SINGLE|ANYONECANPAY] in anything other than the first vin, this is a complex swap. skip for now. //return false; //This happens sometimes :shrug: } swap_result_vin.Add(tx_vin); } for (int vout_idx = 1; vout_idx < DecodedTransaction.vout.Count; vout_idx++) { swap_result_vout.Add(DecodedTransaction.vout[vout_idx]); } dynamic swap_setup_src = RPC.GetRawTransaction(swap_setup_vin.txid.ToString()); dynamic swap_setup_src_vout = swap_setup_src.vout[(int)swap_setup_vin.vout]; string txid = DecodedTransaction.txid.ToString(); var in_type = (swap_setup_src_vout?.scriptPubKey?.asset != null) ? "asset" : "rvn"; var out_Type = (swap_setup_vout?.scriptPubKey?.asset != null) ? "asset" : "rvn"; if (in_type == "asset" && out_Type == "asset") { Swap = new RC_Swap() { TXID = txid, Block = Block, OrderType = SwapType.Trade, InType = swap_setup_src_vout.scriptPubKey.asset.name, InQuantity = swap_setup_src_vout.scriptPubKey.asset.amount, OutType = swap_setup_vout.scriptPubKey.asset.name, OutQuantity = swap_setup_vout.scriptPubKey.asset.amount }; return(true); } else if (out_Type == "asset") { Swap = new RC_Swap() { TXID = txid, Block = Block, OrderType = SwapType.Buy, InType = "rvn", InQuantity = swap_setup_src_vout.value, OutType = swap_setup_vout.scriptPubKey.asset.name, OutQuantity = swap_setup_vout.scriptPubKey.asset.amount }; return(true); } else if (in_type == "asset") { Swap = new RC_Swap() { TXID = txid, Block = Block, OrderType = SwapType.Sell, InType = swap_setup_src_vout.scriptPubKey.asset.name, InQuantity = swap_setup_src_vout.scriptPubKey.asset.amount, OutType = "rvn", OutQuantity = swap_setup_vout.value }; return(true); } return(false); }
public static bool TryParse(RVN_RPC rpc, RTDbContext db, ListingHex listing, out ListingEntry Value, out string Error, bool ValidateSignature = true) { Value = null; Error = null; if (!Regex.IsMatch(listing.Hex, @"^[0-9a-fA-F]*$")) { Error = "Invalid input format."; return(false); } JObject decodedTX = null; try { decodedTX = rpc.DecodeRawTransaction(listing.Hex); } catch { } if (decodedTX == null) { Error = "Unable to decode transaction."; return(false); } if (ValidateSignature) { //Don't do anything special, if we sign and it says completed, that its properly validated to the owner of the UTXO var sign_test = rpc.SignRawTransaction(listing.Hex); if ((sign_test?.Value <bool>("complete") ?? false) == false) { Error = "Signature is invalid"; return(false); } } dynamic parsed_tx = decodedTX; if (!parsed_tx.vin[0]?.scriptSig?.asm?.ToString()?.Contains(Constants.SINGLE_ANYONECANPAY)) { Error = "Transaction is not signed with SINGLE|ANYONECANPAY. Not a valid swap."; return(false); } var utxo = $"{parsed_tx.vin[0].txid}-{parsed_tx.vin[0].vout}"; //Must be fully txindexed to be able to use GetRawTransaction arbitrarily //var src_transaction = (dynamic)Utils.FullExternalTXDecode((string)parsed_tx.vin[0].txid); var src_transaction = (dynamic)rpc.GetRawTransaction((string)parsed_tx.vin[0].txid); var src_vout = src_transaction.vout[(int)parsed_tx.vin[0].vout]; var in_type = (src_vout?.scriptPubKey?.asset != null) ? "asset" : "rvn"; var out_Type = (parsed_tx.vout[0]?.scriptPubKey?.asset != null) ? "asset" : "rvn"; SwapType?type = null; if (in_type == "asset" && out_Type == "asset") { type = SwapType.Trade; } else if (out_Type == "asset") { type = SwapType.Buy; } else if (in_type == "asset") { type = SwapType.Sell; } if (type == null) { Error = "Swap does not contain a recognized Input/Output transaction type combination"; return(false); } var existing = db.Listings.SingleOrDefault(l => l.UTXO == utxo); if (existing == null) { Value = new ListingEntry() { UTXO = utxo }; } else { Value = existing; } Value.SubmitTime = DateTime.UtcNow; Value.B64SignedPartial = Convert.ToBase64String(Utils.StringToByteArray(listing.Hex)); Value.OrderType = type.Value; switch (Value.OrderType) { case SwapType.Buy: //For a buy order, the quantity is the amount being requested Value.InType = "rvn"; Value.InQuantity = src_vout.value; Value.OutType = parsed_tx.vout[0].scriptPubKey.asset.name; Value.OutQuantity = parsed_tx.vout[0].scriptPubKey.asset.amount; Value.UnitPrice = Value.InQuantity / Value.OutQuantity; break; case SwapType.Sell: Value.InType = src_vout.scriptPubKey.asset.name; Value.InQuantity = src_vout.scriptPubKey.asset.amount; Value.OutType = "rvn"; Value.OutQuantity = parsed_tx.vout[0].value; Value.UnitPrice = Value.OutQuantity / Value.InQuantity; break; case SwapType.Trade: Value.InType = src_vout.scriptPubKey.asset.name; Value.InQuantity = src_vout.scriptPubKey.asset.amount; Value.OutType = parsed_tx.vout[0].scriptPubKey.asset.name; Value.OutQuantity = parsed_tx.vout[0].scriptPubKey.asset.amount; Value.UnitPrice = Value.InQuantity / Value.OutQuantity; break; } return(true); }