Exemple #1
0
        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());
        }
Exemple #4
0
 public uint256 GetSignatureHash(ICoin coin, SigHash sigHash = SigHash.All)
 {
     return(Script.SignatureHash(coin.GetScriptCode(), Transaction, (int)Index, sigHash, coin.TxOut.Value, coin.GetHashVersion()));
 }
Exemple #5
0
 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));
				}
			}
		}