public TransactionPolicyError[] Check(Transaction transaction, ICoin[] spentCoins) { if (transaction == null) { throw new ArgumentNullException("transaction"); } spentCoins = spentCoins ?? new ICoin[0]; List <TransactionPolicyError> errors = new List <TransactionPolicyError>(); foreach (var input in transaction.Inputs.AsIndexedInputs()) { var coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if (coin != null) { if (ScriptVerify != null) { ScriptError error; if (!VerifyScript(input, coin.TxOut.ScriptPubKey, coin.TxOut.Value, ScriptVerify.Value, out error)) { errors.Add(new ScriptPolicyError(input, error, ScriptVerify.Value, coin.TxOut.ScriptPubKey)); } } } var txin = input.TxIn; if (txin.ScriptSig.Length > MaxScriptSigLength) { errors.Add(new InputPolicyError("Max scriptSig length exceeded actual is " + txin.ScriptSig.Length + ", max is " + MaxScriptSigLength, input)); } if (!txin.ScriptSig.IsPushOnly) { errors.Add(new InputPolicyError("All operation should be push", input)); } if (!txin.ScriptSig.HasCanonicalPushes) { errors.Add(new InputPolicyError("All operation should be canonical push", input)); } } if (CheckScriptPubKey) { foreach (var txout in transaction.Outputs.AsCoins()) { var template = StandardScripts.GetTemplateFromScriptPubKey(txout.ScriptPubKey); if (template == null) { errors.Add(new OutputPolicyError("Non-Standard scriptPubKey", (int)txout.Outpoint.N)); } } } int txSize = transaction.GetSerializedSize(); if (MaxTransactionSize != null) { if (txSize >= MaxTransactionSize.Value) { errors.Add(new TransactionSizePolicyError(txSize, MaxTransactionSize.Value)); } } var fees = transaction.GetFee(spentCoins); if (fees != null) { if (CheckFee) { if (MaxTxFee != null) { var max = MaxTxFee.GetFee(txSize); if (fees > max) { errors.Add(new FeeTooHighPolicyError(fees, max)); } } if (MinRelayTxFee != null) { if (MinRelayTxFee != null) { var min = MinRelayTxFee.GetFee(txSize); if (fees < min) { errors.Add(new FeeTooLowPolicyError(fees, min)); } } } } } if (MinRelayTxFee != null) { foreach (var output in transaction.Outputs) { var bytes = output.ScriptPubKey.ToBytes(true); if (output.IsDust(MinRelayTxFee) && !IsOpReturn(bytes)) { errors.Add(new DustPolicyError(output.Value, output.GetDustThreshold(MinRelayTxFee))); } } } var opReturnCount = transaction.Outputs.Select(o => o.ScriptPubKey.ToBytes(true)).Count(b => IsOpReturn(b)); if (opReturnCount > 1) { errors.Add(new TransactionPolicyError("More than one op return detected")); } return(errors.ToArray()); }
public TransactionPolicyError[] Check(TransactionValidator validator) { if (validator == null) { throw new ArgumentNullException(nameof(validator)); } var transaction = validator.Transaction; List <TransactionPolicyError> errors = new List <TransactionPolicyError>(); foreach (var input in validator.Transaction.Inputs.AsIndexedInputs()) { if (this.ScriptVerify is NBitcoin.ScriptVerify) { ScriptError?error; if (!VerifyScript(validator, (int)input.Index, out error) && error is ScriptError err) { errors.Add(new ScriptPolicyError(input, err, validator.ScriptVerify, validator.SpentOutputs[input.Index].ScriptPubKey)); } } var txin = input.TxIn; if (txin.ScriptSig.Length > MaxScriptSigLength) { errors.Add(new InputPolicyError("Max scriptSig length exceeded actual is " + txin.ScriptSig.Length + ", max is " + MaxScriptSigLength, input)); } if (!txin.ScriptSig.IsPushOnly) { errors.Add(new InputPolicyError("All operation should be push", input)); } if (!txin.ScriptSig.HasCanonicalPushes) { errors.Add(new InputPolicyError("All operation should be canonical push", input)); } } if (CheckScriptPubKey) { foreach (var txout in transaction.Outputs.AsCoins()) { if (!Strategy.IsStandardOutput(txout.TxOut)) { errors.Add(new OutputPolicyError("Non-Standard scriptPubKey", (int)txout.Outpoint.N)); } } } int txSize = transaction.GetSerializedSize(); if (MaxTransactionSize != null) { if (txSize >= MaxTransactionSize.Value) { errors.Add(new TransactionSizePolicyError(txSize, MaxTransactionSize.Value)); } } var fees = transaction.GetFee(validator.SpentOutputs); if (fees != null) { var virtualSize = transaction.GetVirtualSize(); if (CheckFee) { if (MaxTxFee != null) { var max = MaxTxFee.GetFee(virtualSize); if (fees > max) { errors.Add(new FeeTooHighPolicyError(fees, max)); } } if (MinFee != null) { if (fees < MinFee) { errors.Add(new FeeTooLowPolicyError(fees, MinFee)); } } if (MinRelayTxFee != null) { if (MinRelayTxFee != null) { var min = MinRelayTxFee.GetFee(virtualSize); if (fees < min) { errors.Add(new FeeTooLowPolicyError(fees, min)); } } } } } if (CheckDust) { foreach (var output in transaction.Outputs) { var bytes = output.ScriptPubKey.ToBytes(true); if (output.IsDust() && !IsOpReturn(bytes)) { errors.Add(new DustPolicyError(output.Value, output.GetDustThreshold())); } } } var opReturnCount = transaction.Outputs.Select(o => o.ScriptPubKey.ToBytes(true)).Count(b => IsOpReturn(b)); if (opReturnCount > 1) { errors.Add(new TransactionPolicyError("More than one op return detected")); } return(errors.ToArray()); }