public TransactionPolicyError[] Check(Transaction transaction, ICoin[] spentCoins) { spentCoins = spentCoins ?? new ICoin[0]; List<TransactionPolicyError> errors = new List<TransactionPolicyError>(); if(transaction.Version > Transaction.CURRENT_VERSION || transaction.Version < 1) { errors.Add(new TransactionPolicyError("Invalid transaction version, expected " + Transaction.CURRENT_VERSION)); } var dups = transaction.Inputs.AsIndexedInputs().GroupBy(i => i.PrevOut); foreach(var dup in dups) { var duplicates = dup.ToArray(); if(duplicates.Length != 1) errors.Add(new DuplicateInputPolicyError(duplicates)); } foreach(var input in transaction.Inputs.AsIndexedInputs()) { var coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if(coin == null) { errors.Add(new CoinNotFoundPolicyError(input)); } } var fees = transaction.GetFee(spentCoins); if(fees != null) { if(fees < Money.Zero) errors.Add(new NotEnoughFundsPolicyError("Not enough funds in this transaction", -fees)); } return errors.ToArray(); }
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, 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)); } } 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(); }