public static bool TryVerifyPartyPaymentTx( IBitcoinBasedTransaction tx, Swap swap, ICurrencies currencies, byte[] secretHash, long refundLockTime, out Error error) { if (tx == null) { throw new ArgumentNullException(nameof(tx)); } if (swap == null) { throw new ArgumentNullException(nameof(swap)); } var currency = currencies.Get <BitcoinBasedCurrency>(swap.PurchasedCurrency); var partyRedeemScript = new Script(Convert.FromBase64String(swap.PartyRedeemScript)); var targetAddressHash = new BitcoinPubKeyAddress(swap.ToAddress, currency.Network) .Hash .ToBytes(); var hasSwapOutput = false; foreach (var txOutput in tx.Outputs) { try { var output = (BitcoinBasedTxOutput)txOutput; if (!output.IsPayToScriptHash(partyRedeemScript)) { continue; } // check address var outputTargetAddressHash = BitcoinBasedSwapTemplate.ExtractTargetPkhFromHtlcP2PkhSwapPayment( script: partyRedeemScript); if (!outputTargetAddressHash.SequenceEqual(targetAddressHash)) { continue; } var outputSecretHash = BitcoinBasedSwapTemplate.ExtractSecretHashFromHtlcP2PkhSwapPayment( script: partyRedeemScript); if (!outputSecretHash.SequenceEqual(secretHash)) { continue; } hasSwapOutput = true; // check swap output refund lock time var outputLockTime = BitcoinBasedSwapTemplate.ExtractLockTimeFromHtlcP2PkhSwapPayment( script: partyRedeemScript); var swapTimeUnix = (long)swap.TimeStamp.ToUniversalTime().ToUnixTime(); if (outputLockTime - swapTimeUnix < refundLockTime) { error = new Error( code: Errors.InvalidRefundLockTime, description: "Invalid refund time", swap: swap); return(false); } // check swap amount var side = swap.Symbol .OrderSideForBuyCurrency(currency.Name) .Opposite(); var requiredAmount = AmountHelper.QtyToAmount(side, swap.Qty, swap.Price, currency.DigitsMultiplier); var requiredAmountInSatoshi = currency.CoinToSatoshi(requiredAmount); if (output.Value < requiredAmountInSatoshi) { error = new Error( code: Errors.InvalidSwapPaymentTxAmount, description: "Invalid payment tx amount", swap: swap); return(false); } } catch (Exception e) { Log.Error(e, "Transaction verification error"); error = new Error( code: Errors.TransactionVerificationError, description: e.Message, swap: swap); return(false); } } if (!hasSwapOutput) { error = new Error( code: Errors.TransactionVerificationError, description: $"No swap outputs in tx @{tx.Id} for address {swap.ToAddress}", swap: swap); return(false); } // todo: check fee // todo: try to verify error = null; return(true); }