public void UpdateFromCoin(ICoin coin) { if (coin == null) { throw new ArgumentNullException(nameof(coin)); } if (IsFinalized()) { throw new InvalidOperationException("Impossible to modify the PSBTInput if it has been finalized"); } if (coin.Outpoint != PrevOut) { throw new ArgumentException("This coin does not match the input", nameof(coin)); } if (coin is ScriptCoin scriptCoin) { if (scriptCoin.RedeemType == RedeemType.P2SH) { redeem_script = scriptCoin.Redeem; } else if (scriptCoin.RedeemType == RedeemType.WitnessV0) { witness_script = scriptCoin.Redeem; if (scriptCoin.IsP2SH) { redeem_script = witness_script.WitHash.ScriptPubKey; } } } else { if (coin.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2SH) && redeem_script == null) { // Let's try to be smart by finding the redeemScript in the global tx if (Parent.Settings.IsSmart && redeem_script == null) { var redeemScript = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(originalScriptSig, coin.TxOut.ScriptPubKey)?.RedeemScript; if (redeemScript != null) { redeem_script = redeemScript; } } } if (witness_script == null) { // Let's try to be smart by finding the witness script in the global tx if (Parent.Settings.IsSmart && witness_script == null) { var witScriptId = PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(coin.TxOut.ScriptPubKey); if (witScriptId == null && redeem_script != null) { witScriptId = PayToWitScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(redeem_script); } if (witScriptId != null) { var redeemScript = PayToWitScriptHashTemplate.Instance.ExtractWitScriptParameters(originalWitScript, witScriptId); if (redeemScript != null) { witness_script = redeemScript; } } } } } if (coin.GetHashVersion() == HashVersion.Witness || witness_script != null) { witness_utxo = coin.TxOut; non_witness_utxo = null; } else { orphanTxOut = coin.TxOut; witness_utxo = null; } }
public TransactionPolicyError[] Check(Transaction transaction, ICoin[] spentCoins, int blockHeight = -1, uint256 blockHash = null) { if (transaction == null) { throw new ArgumentNullException("transaction"); } spentCoins = spentCoins ?? new ICoin[0]; var errors = new List <TransactionPolicyError>(); foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs()) { ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if (coin != null) { if (this.ScriptVerify != null) { var script = (blockHeight < 0) ? coin.TxOut.ScriptPubKey : new ScriptAtHeight(coin.TxOut.ScriptPubKey, blockHeight, blockHash); if (!input.VerifyScript(this.Network, script, coin.TxOut.Value, this.ScriptVerify.Value, out ScriptError error)) { errors.Add(new ScriptPolicyError(input, error, this.ScriptVerify.Value, coin.TxOut.ScriptPubKey)); } } } TxIn 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 (this.CheckMalleabilitySafe) { foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs()) { ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if (coin != null && coin.GetHashVersion(this.Network) != HashVersion.Witness) { errors.Add(new InputPolicyError("Malleable input detected", input)); } } } CheckPubKey(transaction, errors); int txSize = transaction.GetSerializedSize(); if (this.MaxTransactionSize != null) { if (txSize >= this.MaxTransactionSize.Value) { errors.Add(new TransactionSizePolicyError(txSize, this.MaxTransactionSize.Value)); } } Money fees = transaction.GetFee(spentCoins); if (fees != null) { if (this.CheckFee) { if (this.MaxTxFee != null) { Money max = this.MaxTxFee.GetFee(txSize); if (fees > max) { errors.Add(new FeeTooHighPolicyError(fees, max)); } } if (this.MinRelayTxFee != null) { if (this.MinRelayTxFee != null) { Money min = this.MinRelayTxFee.GetFee(txSize); if (fees < min) { errors.Add(new FeeTooLowPolicyError(fees, min)); } } } } } this.CheckMinRelayTxFee(transaction, errors); int 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(Transaction transaction, ICoin[] spentCoins) { if (transaction == null) { throw new ArgumentNullException("transaction"); } spentCoins = spentCoins ?? new ICoin[0]; var errors = new List <TransactionPolicyError>(); foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs()) { ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if (coin != null) { if (this.ScriptVerify != null) { ScriptError error; if (!VerifyScript(input, coin.TxOut.ScriptPubKey, coin.TxOut.Value, this.ScriptVerify.Value, out error)) { errors.Add(new ScriptPolicyError(input, error, this.ScriptVerify.Value, coin.TxOut.ScriptPubKey)); } } } TxIn 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 (this.CheckMalleabilitySafe) { foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs()) { ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut); if (coin != null && coin.GetHashVersion(this.network) != HashVersion.Witness) { errors.Add(new InputPolicyError("Malleable input detected", input)); } } } if (this.CheckScriptPubKey) { foreach (Coin txout in transaction.Outputs.AsCoins()) { ScriptTemplate template = StandardScripts.GetTemplateFromScriptPubKey(txout.ScriptPubKey); if (template == null) { errors.Add(new OutputPolicyError("Non-Standard scriptPubKey", (int)txout.Outpoint.N)); } } } int txSize = transaction.GetSerializedSize(); if (this.MaxTransactionSize != null) { if (txSize >= this.MaxTransactionSize.Value) { errors.Add(new TransactionSizePolicyError(txSize, this.MaxTransactionSize.Value)); } } Money fees = transaction.GetFee(spentCoins); if (fees != null) { if (this.CheckFee) { if (this.MaxTxFee != null) { Money max = this.MaxTxFee.GetFee(txSize); if (fees > max) { errors.Add(new FeeTooHighPolicyError(fees, max)); } } if (this.MinRelayTxFee != null) { if (this.MinRelayTxFee != null) { Money min = this.MinRelayTxFee.GetFee(txSize); if (fees < min) { errors.Add(new FeeTooLowPolicyError(fees, min)); } } } } } if (this.MinRelayTxFee != null) { foreach (TxOut output in transaction.Outputs) { byte[] bytes = output.ScriptPubKey.ToBytes(true); if (output.IsDust(this.MinRelayTxFee) && !IsOpReturn(bytes)) { errors.Add(new DustPolicyError(output.Value, output.GetDustThreshold(this.MinRelayTxFee))); } } } int 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 uint256 GetSignatureHash(ICoin coin, SigHash sigHash = SigHash.All) { return(Script.SignatureHash(coin.GetScriptCode(), Transaction, (int)Index, sigHash, coin.TxOut.Value, coin.GetHashVersion())); }
public HashVersion GetHashVersion() { return(innerCoin.GetHashVersion()); }
private void Sign(TransactionSigningContext ctx, ICoin coin, IndexedTxIn txIn) { var input = txIn.TxIn; if(coin is StealthCoin) { var stealthCoin = (StealthCoin)coin; var scanKey = FindKey(ctx, stealthCoin.Address.ScanPubKey.ScriptPubKey); if(scanKey == null) throw new KeyNotFoundException("Scan key for decrypting StealthCoin not found"); var spendKeys = stealthCoin.Address.SpendPubKeys.Select(p => FindKey(ctx, p.ScriptPubKey)).Where(p => p != null).ToArray(); ctx.AdditionalKeys.AddRange(stealthCoin.Uncover(spendKeys, scanKey)); var normalCoin = new Coin(coin.Outpoint, coin.TxOut); if(stealthCoin.Redeem != null) normalCoin = normalCoin.ToScriptCoin(stealthCoin.Redeem); coin = normalCoin; } var scriptSig = CreateScriptSig(ctx, coin, txIn); if(scriptSig == null) return; ScriptCoin scriptCoin = coin as ScriptCoin; Script signatures = null; if(coin.GetHashVersion() == HashVersion.Witness) { signatures = txIn.WitScript; if(scriptCoin != null) { if(scriptCoin.IsP2SH) txIn.ScriptSig = Script.Empty; if(scriptCoin.RedeemType == RedeemType.WitnessV0) signatures = RemoveRedeem(signatures); } } else { signatures = txIn.ScriptSig; if(scriptCoin != null && scriptCoin.RedeemType == RedeemType.P2SH) signatures = RemoveRedeem(signatures); } signatures = CombineScriptSigs(coin, scriptSig, signatures); if(coin.GetHashVersion() == HashVersion.Witness) { txIn.WitScript = signatures; if(scriptCoin != null) { if(scriptCoin.IsP2SH) txIn.ScriptSig = new Script(Op.GetPushOp(scriptCoin.GetP2SHRedeem().ToBytes(true))); if(scriptCoin.RedeemType == RedeemType.WitnessV0) txIn.WitScript = txIn.WitScript + new WitScript(Op.GetPushOp(scriptCoin.Redeem.ToBytes(true))); } } else { txIn.ScriptSig = signatures; if(scriptCoin != null && scriptCoin.RedeemType == RedeemType.P2SH) { txIn.ScriptSig = input.ScriptSig + Op.GetPushOp(scriptCoin.GetP2SHRedeem().ToBytes(true)); } } }