/// <summary> /// Verifies transaction's signature. /// </summary> /// <param name="coin">UTXO that is spent in the transaction.</param> /// <param name="txTo">Transaction.</param> /// <param name="txToInN">Index of the transaction's input.</param> /// <param name="flagScriptVerify">Script verification flags.</param> /// <returns><c>true</c> if signature is valid.</returns> private bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify) { TxIn input = txTo.Inputs[txToInN]; if (input.PrevOut.N >= coin.Outputs.Length) { return(false); } if (input.PrevOut.Hash != coin.TransactionId) { return(false); } TxOut output = coin.Outputs[input.PrevOut.N]; var txData = new PrecomputedTransactionData(txTo); var checker = new TransactionChecker(txTo, txToInN, output.Value, txData); var ctx = new ScriptEvaluationContext(this.chain.Network) { ScriptVerify = flagScriptVerify }; bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker); return(res); }
/// <summary> /// Verifies transaction's signature. /// </summary> /// <param name="coin">UTXO that is spent in the transaction.</param> /// <param name="txTo">Transaction.</param> /// <param name="txToInN">Index of the transaction's input.</param> /// <param name="flagScriptVerify">Script verification flags.</param> /// <returns><c>true</c> if signature is valid.</returns> private bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify) { this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4},{5}:{6})", nameof(coin), coin.TransactionId, coin.Height, nameof(txToInN), txToInN, nameof(flagScriptVerify), flagScriptVerify); TxIn input = txTo.Inputs[txToInN]; if (input.PrevOut.N >= coin.Outputs.Length) { return(false); } if (input.PrevOut.Hash != coin.TransactionId) { return(false); } TxOut output = coin.Outputs[input.PrevOut.N]; var txData = new PrecomputedTransactionData(txTo); var checker = new TransactionChecker(txTo, txToInN, output.Value, txData); var ctx = new ScriptEvaluationContext(this.chain.Network) { ScriptVerify = flagScriptVerify }; bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker); this.logger.LogTrace("(-):{0}", res); return(res); }
static void CheckTransaction(Transaction tx, SegWitCoin kernelCoin) { var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { TxIn input = tx.Inputs[inputIndex]; TxOut spentOut = new TxOut(kernelCoin.UtxoValue, kernelCoin.SegWitAddress.GetScriptPubKey()); var checker = new TransactionChecker(tx, inputIndex, spentOut, txData); var ctx = new ScriptEvaluationContext { ScriptVerify = ScriptVerify.Mandatory | ScriptVerify.DerSig | ScriptVerify.CheckLockTimeVerify | ScriptVerify.Witness /* todo | ScriptVerify.CheckColdStakeVerify*/ }; bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, spentOut.ScriptPubKey, checker); if (verifyScriptResult == false) { throw new InvalidOperationException( $"Verify script for transaction '{tx.GetHash()}' input {inputIndex} failed, ScriptSig = '{input.ScriptSig}', ScriptPubKey = '{spentOut.ScriptPubKey}', script evaluation error = '{ctx.Error}'"); } } }
/// <inheritdoc/> public bool VerifySignature(UnspentOutputs coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify) { TxIn input = txTo.Inputs[txToInN]; if (input.PrevOut.N >= coin.Outputs.Length) { this.logger.LogTrace("(-)[OUTPUT_INCORRECT_LENGTH]"); return(false); } if (input.PrevOut.Hash != coin.TransactionId) { this.logger.LogTrace("(-)[INCORRECT_TX]"); return(false); } TxOut output = coin.Outputs[input.PrevOut.N]; if (output == null) { this.logger.LogTrace("(-)[OUTPUT_NOT_FOUND]"); return(false); } var txData = new PrecomputedTransactionData(txTo); var checker = new TransactionChecker(txTo, txToInN, output.Value, txData); var ctx = new ScriptEvaluationContext(this.chain.Network) { ScriptVerify = flagScriptVerify }; bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker); return(res); }
/// <summary> /// Validates transaction inputs against transaction data for a specific script verify flag. /// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts) /// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it /// instead of being performed inline. /// </summary> /// <param name="ruleContext">Current mempool rule context.</param> /// <param name="context">Current validation context.</param> /// <param name="scriptVerify">Script verify flag.</param> /// <param name="txData">Transaction data.</param> /// <returns>Whether inputs are valid.</returns> private bool CheckInputs(MempoolValidationContext context, ScriptVerify scriptVerify, PrecomputedTransactionData txData) { Transaction tx = context.Transaction; if (!context.Transaction.IsCoinBase) { this.consensusRuleEngine.GetRule <CoinViewRule>().CheckInputs(context.Transaction, context.View.Set, this.chainIndexer.Height + 1); for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { TxIn input = tx.Inputs[iInput]; int iiIntput = iInput; TxOut txout = context.View.GetOutputFor(input); var checker = new TransactionChecker(tx, iiIntput, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.network); ctx.ScriptVerify = scriptVerify; if (ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)) { this.logger.LogTrace("(-)[SCRIPT_VERIFIED]:true"); return(true); } else { //TODO: //if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) //{ // // Check whether the failure was caused by a // // non-mandatory script verification check, such as // // non-standard DER encodings or non-null dummy // // arguments; if so, don't trigger DoS protection to // // avoid splitting the network between upgraded and // // non-upgraded nodes. // CScriptCheck check2(*coins, tx, i, // flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata); // if (check2()) // return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); //} //// Failures of other flags indicate a transaction that is //// invalid in new blocks, e.g. a invalid P2SH. We DoS ban //// such nodes as they are not following the protocol. That //// said during an upgrade careful thought should be taken //// as to the correct behavior - we may want to continue //// peering with non-upgraded nodes even after soft-fork //// super-majority signaling has occurred. this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(MempoolErrors.MandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw(); } } } return(true); }
public TransactionChecker(Transaction tx, int index, Money amount, PrecomputedTransactionData precomputedTransactionData) { if (tx == null) { throw new ArgumentNullException(nameof(tx)); } Transaction = tx; Index = index; Amount = amount; PrecomputedTransactionData = precomputedTransactionData; }
public override void CheckTransaction(MempoolValidationContext context) { var scriptVerifyFlags = ScriptVerify.Standard; if (!this.settings.RequireStandard) { // TODO: implement -promiscuousmempoolflags // scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. var txdata = new PrecomputedTransactionData(context.Transaction); if (!this.CheckInputs(context, scriptVerifyFlags, txdata)) { // TODO: Implement Witness Code //// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we //// need to turn both off, and compare against just turning off CLEANSTACK //// to see if the failure is specifically due to witness validation. //if (!tx.HasWitness() && CheckInputs(Trx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) && // !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) //{ // // Only the witness is missing, so the transaction itself may be fine. // state.SetCorruptionPossible(); //} this.logger.LogTrace("(-)[FAIL_INPUTS_PREV_TXS]"); context.State.Fail(new MempoolError()).Throw(); } // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For // instance the STRICTENC flag was incorrectly allowing certain // CHECKSIG NOT scripts to pass, even though they were invalid. // // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. if (!this.CheckInputs(context, ScriptVerify.P2SH, txdata)) { this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(new MempoolError(), $"CheckInputs: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags {context.TransactionHash}").Throw(); } }
/// <summary> /// Verify that an input may be validly spent as part of the given transaction in the given block. /// </summary> /// <param name="tx">Transaction to check.</param> /// <param name="inputIndexCopy">Index of the input to check.</param> /// <param name="txout">Output the input is spending.</param> /// <param name="txData">Transaction data for the transaction being checked.</param> /// <param name="input">Input to check.</param> /// <param name="flags">Deployment flags</param> /// <returns>Whether the input is valid.</returns> protected virtual bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags) { var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.Parent.Network); ctx.ScriptVerify = flags.ScriptFlags; var verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker); if (verifyScriptResult == false) { this.Logger.LogDebug( "Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'", tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error); } return(verifyScriptResult); }
/// <inheritdoc/> public bool VerifySignature(UnspentOutput coin, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify) { Guard.NotNull(coin, nameof(coin)); Guard.NotNull(txTo, nameof(txTo)); if (txToInN < 0 || txToInN >= txTo.Inputs.Count) { return(false); } TxIn input = txTo.Inputs[txToInN]; //if (input.PrevOut.N >= coin.Outputs.Length) //{ // this.logger.LogTrace("(-)[OUTPUT_INCORRECT_LENGTH]"); // return false; //} //if (input.PrevOut.Hash != coin.TransactionId) if (input.PrevOut.Hash != coin.OutPoint.Hash) { this.logger.LogTrace("(-)[INCORRECT_TX]"); return(false); } TxOut output = coin.Coins.TxOut;//.Outputs[input.PrevOut.N]; if (output == null) { this.logger.LogTrace("(-)[OUTPUT_NOT_FOUND]"); return(false); } var txData = new PrecomputedTransactionData(txTo); var checker = new TransactionChecker(txTo, txToInN, output.Value, txData); var ctx = new ScriptEvaluationContext(this.chainIndexer.Network) { ScriptVerify = flagScriptVerify }; bool res = ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker); return(res); }
/// <seealso>https://github.com/bitcoin/bitcoin/blob/febf3a856bcfb8fef2cb4ddcb8d1e0cab8a22580/src/validation.cpp#L770</seealso> public override void CheckTransaction(MempoolValidationContext context) { // TODO: How should the RequireStandard setting interact with this? var scriptVerifyFlags = ScriptVerify.Standard; // Check against previous transactions. // This is done last to help prevent CPU exhaustion denial-of-service attacks. var txdata = new PrecomputedTransactionData(context.Transaction); if (!CheckInputs(context, scriptVerifyFlags, txdata)) { // SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we // need to turn both off, and compare against just turning off CLEANSTACK // to see if the failure is specifically due to witness validation. if (!context.Transaction.HasWitness && CheckInputs(context, scriptVerifyFlags & ~(ScriptVerify.Witness | ScriptVerify.CleanStack), txdata) && !CheckInputs(context, scriptVerifyFlags & ~ScriptVerify.CleanStack, txdata)) { // Only the witness is missing, so the transaction itself may be fine. this.logger.LogTrace("(-)[FAIL_WITNESS_MUTATED]"); context.State.Fail(MempoolErrors.WitnessMutated).Throw(); } this.logger.LogTrace("(-)[FAIL_INPUTS_PREV_TXS]"); context.State.Fail(new MempoolError()).Throw(); } // Check again against just the consensus-critical mandatory script verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For instance the STRICTENC flag was incorrectly allowing certain // CHECKSIG NOT scripts to pass, even though they were invalid. // // There is a similar check during block creation to prevent creating invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. var flags = this.nodeDeployments.GetFlags(this.chainIndexer.Tip); if (!CheckInputs(context, flags.ScriptFlags, txdata)) { this.logger.LogTrace("(-)[FAIL_SCRIPT_VERIFY]"); context.State.Fail(new MempoolError(), $"CheckInputs: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags {context.TransactionHash}") .Throw(); } }
private bool VerifySignature(UnspentOutputs txFrom, Transaction txTo, int txToInN, ScriptVerify flagScriptVerify) { var input = txTo.Inputs[txToInN]; if (input.PrevOut.N >= txFrom._Outputs.Length) { return(false); } if (input.PrevOut.Hash != txFrom.TransactionId) { return(false); } var output = txFrom._Outputs[input.PrevOut.N]; var txData = new PrecomputedTransactionData(txTo); var checker = new TransactionChecker(txTo, txToInN, output.Value, txData); var ctx = new ScriptEvaluationContext { ScriptVerify = flagScriptVerify }; return(ctx.VerifyScript(input.ScriptSig, output.ScriptPubKey, checker)); }
public bool CheckInput(Func <Transaction, int, TxOut, PrecomputedTransactionData, TxIn, DeploymentFlags, bool> baseCheckInput, Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags) { if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall()) { return(input.ScriptSig.IsSmartContractSpend()); } return(baseCheckInput(tx, inputIndexCopy, txout, txData, input, flags)); }
/// <inheritdoc/> protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags) { return(this.logic.CheckInput(base.CheckInput, tx, inputIndexCopy, txout, txData, input, flags)); }
/// <inheritdoc /> public override async Task RunAsync(RuleContext context) { Block block = context.ValidationContext.BlockToValidate; ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate; DeploymentFlags flags = context.Flags; UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet; long sigOpsCost = 0; Money fees = Money.Zero; var inputsToCheck = new List <(Transaction tx, int inputIndexCopy, TxOut txOut, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags)>(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { if (!tx.IsCoinBase && !view.HaveInputs(tx)) { this.Logger.LogTrace("Transaction '{0}' has not inputs", tx.GetHash()); this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } if (!this.IsTxFinal(tx, context)) { this.Logger.LogTrace("Transaction '{0}' is not final", tx.GetHash()); this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost) { this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]"); ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase) { this.CheckInputs(tx, view, index.Height); if (!tx.IsCoinStake) { fees += view.GetValueIn(tx) - tx.TotalOut; } var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { TxIn input = tx.Inputs[inputIndex]; inputsToCheck.Add(( tx: tx, inputIndexCopy: inputIndex, txOut: view.GetOutputFor(input), txData, input: input, flags )); } } } this.UpdateCoinView(context, tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); // Start the Parallel loop on a thread so its result can be awaited rather than blocking Task <ParallelLoopResult> checkInputsInParallel = Task.Run(() => { return(Parallel.ForEach(inputsToCheck, (input, state) => { if (state.ShouldExitCurrentIteration) { return; } if (!this.CheckInput(input.tx, input.inputIndexCopy, input.txOut, input.txData, input.input, input.flags)) { state.Stop(); } })); }); ParallelLoopResult loopResult = await checkInputsInParallel.ConfigureAwait(false); if (!loopResult.IsCompleted) { this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } }
/// <inheritdoc /> public async override Task RunAsync(RuleContext context) { this.Logger.LogTrace("()"); this.blockTxsProcessed = new List <Transaction>(); NBitcoin.Block block = context.ValidationContext.Block; ChainedHeader index = context.ValidationContext.ChainedHeader; DeploymentFlags flags = context.Flags; UnspentOutputSet view = ((UtxoRuleContext)context).UnspentOutputSet; this.Parent.PerformanceCounter.AddProcessedBlocks(1); // Start state from previous block's root this.ContractCoinviewRule.OriginalStateRoot.SyncToRoot(((SmartContractBlockHeader)context.ConsensusTip.Header).HashStateRoot.ToBytes()); IContractState trackedState = this.ContractCoinviewRule.OriginalStateRoot.StartTracking(); this.receipts = new List <Receipt>(); this.refundCounter = 1; long sigOpsCost = 0; Money fees = Money.Zero; var checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { this.Parent.PerformanceCounter.AddProcessedTransactions(1); Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { if (!this.IsProtocolTransaction(tx)) { if (!view.HaveInputs(tx)) { this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } var prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final. // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set. for (int j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost) { ConsensusErrors.BadBlockSigOps.Throw(); } if (!this.IsProtocolTransaction(tx)) { this.CheckInputs(tx, view, index.Height); fees += view.GetValueIn(tx) - tx.TotalOut; var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { this.Parent.PerformanceCounter.AddProcessedInputs(1); TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall()) { return(input.ScriptSig.IsSmartContractSpend()); } var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.Parent.Network); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); }); checkInput.Start(); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); this.blockTxsProcessed.Add(tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); foreach (Task <bool> checkInput in checkInputs) { if (await checkInput.ConfigureAwait(false)) { continue; } this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } if (new uint256(this.ContractCoinviewRule.OriginalStateRoot.Root) != ((SmartContractBlockHeader)block.Header).HashStateRoot) { SmartContractConsensusErrors.UnequalStateRoots.Throw(); } ValidateAndStoreReceipts(((SmartContractBlockHeader)block.Header).ReceiptRoot); this.ContractCoinviewRule.OriginalStateRoot.Commit(); this.Logger.LogTrace("(-)"); }
/// <inheritdoc /> public override async Task RunAsync(RuleContext context) { Block block = context.ValidationContext.BlockToValidate; ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate; DeploymentFlags flags = context.Flags; UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet; long sigOpsCost = 0; Money fees = Money.Zero; var checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { if (!this.IsProtocolTransaction(tx)) { if (!view.HaveInputs(tx)) { this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } var prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final. // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set. for (int j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost) { this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]"); ConsensusErrors.BadBlockSigOps.Throw(); } if (!this.IsProtocolTransaction(tx)) { this.CheckInputs(tx, view, index.Height); fees += view.GetValueIn(tx) - tx.TotalOut; var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => this.CheckInput(tx, inputIndexCopy, txout, txData, input, flags)); checkInput.Start(); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); foreach (Task <bool> checkInput in checkInputs) { if (await checkInput.ConfigureAwait(false)) { continue; } this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (sigversion == HashVersion.WitnessV0) { if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney) { throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput)); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(this.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(this.Version); // PoS Time sss.ReadWrite(this.Time); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(spentOutput.Value.Satoshi); sss.ReadWrite(Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(LockTime); // Sighash type sss.ReadWrite((uint)nHashType); return(GetHash(sss)); } bool fAnyoneCanPay = (nHashType & SigHash.AnyoneCanPay) != 0; bool fHashSingle = ((byte)nHashType & 0x1f) == (byte)SigHash.Single; bool fHashNone = ((byte)nHashType & 0x1f) == (byte)SigHash.None; if (nIn >= Inputs.Count) { return(uint256.One); } if (fHashSingle) { if (nIn >= Outputs.Count) { return(uint256.One); } } var stream = CreateHashWriter(sigversion); stream.ReadWrite(Version); stream.ReadWrite(Time); uint nInputs = (uint)(fAnyoneCanPay ? 1 : Inputs.Count); stream.ReadWriteAsVarInt(ref nInputs); for (int nInput = 0; nInput < nInputs; nInput++) { if (fAnyoneCanPay) { nInput = nIn; } stream.ReadWrite(Inputs[nInput].PrevOut); if (nInput != nIn) { stream.ReadWrite(Script.Empty); } else { WriteScriptCode(stream, scriptCode); } if (nInput != nIn && (fHashSingle || fHashNone)) { stream.ReadWrite((uint)0); } else { stream.ReadWrite(Inputs[nInput].Sequence); } } uint nOutputs = (uint)(fHashNone ? 0 : (fHashSingle ? nIn + 1 : Outputs.Count)); stream.ReadWriteAsVarInt(ref nOutputs); for (int nOutput = 0; nOutput < nOutputs; nOutput++) { if (fHashSingle && nOutput != nIn) { this.Outputs.CreateNewTxOut().ReadWrite(stream); } else { Outputs[nOutput].ReadWrite(stream); } } stream.ReadWriteStruct(LockTime); stream.ReadWrite((uint)nHashType); return(GetHash(stream)); }
/* * if (nIn >= txTo.vin.size()) * { * printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); * return 1; * } * CTransaction txTmp(txTo); * * // In case concatenating two scripts ends up with two codeseparators, * // or an extra one at the end, this prevents all those possible incompatibilities. * scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); * * // Blank out other inputs' signatures * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * txTmp.vin[i].scriptSig = CScript(); * txTmp.vin[nIn].scriptSig = scriptCode; * * // Blank out some of the outputs * if ((nHashType & 0x1f) == SIGHASH_NONE) * { * // Wildcard payee * txTmp.vout.clear(); * * // Let the others update at will * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * if (i != nIn) * txTmp.vin[i].nSequence = 0; * } * else if ((nHashType & 0x1f) == SIGHASH_SINGLE) * { * // Only lock-in the txout payee at same index as txin * unsigned int nOut = nIn; * if (nOut >= txTmp.vout.size()) * { * printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); * return 1; * } * txTmp.vout.resize(nOut+1); * for (unsigned int i = 0; i < nOut; i++) * txTmp.vout[i].SetNull(); * * // Let the others update at will * for (unsigned int i = 0; i < txTmp.vin.size(); i++) * if (i != nIn) * txTmp.vin[i].nSequence = 0; * } * * // Blank out other inputs completely, not recommended for open transactions * if (nHashType & SIGHASH_ANYONECANPAY) * { * txTmp.vin[0] = txTmp.vin[nIn]; * txTmp.vin.resize(1); * } * * // Serialize and hash * CHashWriter ss(SER_GETHASH, 0); * ss << txTmp << nHashType; * return ss.GetHash(); */ public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (nIn >= Inputs.Count) { //Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n"); return(uint256.One); } var scriptCopy = new Script(scriptCode.ToBytes()); scriptCopy = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE //if (hashType == SigHash.Single) //{ // if (nIn >= Outputs.Count) // { // //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); // return uint256.One; // } //} var txCopy = (SolarCoinTransaction)GetConsensusFactory().CreateTransaction(); txCopy.FromBytes(this.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { if (nIn >= Outputs.Count) { //Utils.log("ERROR: SignatureHash() : nOut=" + nIn + " out of range\n"); return(uint256.One); } //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < nIn; i++) { //if (i == nIn) // continue; txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. //txCopy.Inputs[0].ScriptSig = scriptCopy; } var previousType = txCopy.NType; txCopy.NType = (uint)PrimaryActions.SER_GETHASH; //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); var txHash = GetHash(stream); txCopy.NType = previousType; return(txHash); }
public virtual void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { Block block = context.BlockResult.Block; ChainedBlock index = context.BlockResult.ChainedBlock; ConsensusFlags flags = context.Flags; UnspentOutputSet view = context.Set; this.PerformanceCounter.AddProcessedBlocks(1); taskScheduler = taskScheduler ?? TaskScheduler.Default; if (flags.EnforceBIP30) { foreach (var tx in block.Transactions) { var coins = view.AccessCoins(tx.GetHash()); if (coins != null && !coins.IsPrunable) { ConsensusErrors.BadTransactionBIP30.Throw(); } } } long nSigOpsCost = 0; Money nFees = Money.Zero; List <Task <bool> > checkInputs = new List <Task <bool> >(); for (int i = 0; i < block.Transactions.Count; i++) { this.PerformanceCounter.AddProcessedTransactions(1); var tx = block.Transactions[i]; if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { int[] prevheights; if (!view.HaveInputs(tx)) { ConsensusErrors.BadTransactionMissingInput.Throw(); } prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set for (var j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSigOpCost counts 3 types of sigops: // * legacy (always) // * p2sh (when P2SH enabled in flags and excludes coinbase) // * witness (when witness enabled in flags and excludes coinbase) nSigOpsCost += this.GetTransactionSigOpCost(tx, view, flags); if (nSigOpsCost > this.consensusOptions.MAX_BLOCK_SIGOPS_COST) { ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { this.CheckInputs(tx, view, index.Height); nFees += view.GetValueIn(tx) - tx.TotalOut; int ii = i; var localTx = tx; PrecomputedTransactionData txData = new PrecomputedTransactionData(tx); for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { this.PerformanceCounter.AddProcessedInputs(1); var input = tx.Inputs[iInput]; int iiIntput = iInput; var txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { if (this.UseConsensusLib) { Script.BitcoinConsensusError error; return(Script.VerifyScriptConsensus(txout.ScriptPubKey, tx, (uint)iiIntput, flags.ScriptFlags, out error)); } else { var checker = new TransactionChecker(tx, iiIntput, txout.Value, txData); var ctx = new ScriptEvaluationContext(); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); } }); checkInput.Start(taskScheduler); checkInputs.Add(checkInput); } } this.UpdateCoinView(context, tx); } this.CheckBlockReward(context, nFees, index, block); var passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { ConsensusErrors.BadTransactionScriptError.Throw(); } }
/// <summary> /// Validates transaction inputs against transaction data for a specific set of script verify flags. /// Check whether all inputs of this transaction are valid (no double spends, scripts & signatures, amounts) /// This does not modify the UTXO set. /// </summary> /// <seealso>https://github.com/bitcoin/bitcoin/blob/febf3a856bcfb8fef2cb4ddcb8d1e0cab8a22580/src/validation.cpp#L1259</seealso> /// <param name="ruleContext">Current mempool rule context.</param> /// <param name="context">Current validation context.</param> /// <param name="scriptVerify">Script verify flag.</param> /// <param name="txData">Transaction data.</param> /// <returns>Whether inputs are valid.</returns> private bool CheckInputs(MempoolValidationContext context, ScriptVerify scriptVerify, PrecomputedTransactionData txData) { Transaction tx = context.Transaction; if (tx.IsCoinBase) { return(true); } // TODO: The original code does not appear to do these checks here. Reevaluate if this needs to be done, or perhaps moved to another rule/method. this.consensusRuleEngine.GetRule <CoinViewRule>().CheckInputs(context.Transaction, context.View.Set, this.chainIndexer.Height + 1); // TODO: Original code has the concept of a script execution cache. This might be worth looking into for performance improvements. Signature checks are expensive. for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { TxIn input = tx.Inputs[iInput]; int iiInput = iInput; TxOut txout = context.View.GetOutputFor(input); var checker = new TransactionChecker(tx, iiInput, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.network) { ScriptVerify = scriptVerify }; if (!ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)) { if ((scriptVerify & ScriptVerify.StandardNotMandatory) == ScriptVerify.StandardNotMandatory) { // Check whether the failure was caused by a non-mandatory script verification check, such as // non-standard DER encodings or non-null dummy arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and non-upgraded nodes. // TODO: Investigate whether the checker and context can be reused instead of recreated. Probably not. checker = new TransactionChecker(tx, iiInput, txout.Value, txData); ctx = new ScriptEvaluationContext(this.network) { ScriptVerify = (scriptVerify & ~ScriptVerify.StandardNotMandatory) }; if (ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)) { this.logger.LogTrace("(-)[FAIL_NON_MANDATORY_SCRIPT_VERIFY]"); this.logger.LogDebug("Failed non-mandatory script verify: Transaction = {0}, scriptVerify = {1}, ctx.scriptVerify = {2}", tx.ToHex(), scriptVerify, ctx.ScriptVerify); // TODO: Check what this actually means in Core's logic. If it is on testnet/regtest and RequireStandard is false, is the transaction still rejected? context.State.Fail(MempoolErrors.NonMandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw(); } } // Failures of other flags indicate a transaction that is invalid in new blocks, e.g. an invalid P2SH. We DoS ban // such nodes as they are not following the protocol. That said, during an upgrade careful thought should be taken // as to the correct behavior - we may want to continue peering with non-upgraded nodes even after soft-fork // super-majority signaling has occurred. // Further comment from Bitcoin Core: // MANDATORY flag failures correspond to // ValidationInvalidReason::CONSENSUS. Because CONSENSUS // failures are the most serious case of validation // failures, we may need to consider using // RECENT_CONSENSUS_CHANGE for any script failure that // could be due to non-upgraded nodes which we may want to // support, to avoid splitting the network (but this // depends on the details of how net_processing handles // such errors). this.logger.LogTrace("(-)[FAIL_MANDATORY_SCRIPT_VERIFY]"); context.State.Fail(MempoolErrors.MandatoryScriptVerifyFlagFailed, ctx.Error.ToString()).Throw(); } } return(true); }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, Money amount, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { if (nIn >= Inputs.Count && (uint)nIn != NOT_AN_INPUT) // second condition is always true, NBitcoin restricts us to transparent txs only, left to match the original code { throw new InvalidOperationException("Input index is out of range"); } if (fOverwintered && (nVersionGroupId == OVERWINTER_VERSION_GROUP_ID || nVersionGroupId == SAPLING_VERSION_GROUP_ID)) { uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; uint256 hashJoinSplits = uint256.Zero; uint256 hashShieldedSpends = uint256.Zero; uint256 hashShieldedOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { using (var ss = new BLAKE2bWriter(ZCASH_OUTPUTS_HASH_PERSONALIZATION)) { ss.ReadWrite(Outputs[nIn]); hashOutputs = ss.GetHash(); } } if (vjoinsplit.Any()) { hashJoinSplits = GetJoinSplitsHash(); } if (vShieldedSpend.Any()) { hashShieldedSpends = GetShieldedSpendsHash(); } if (vShieldedOutput.Any()) { hashShieldedOutputs = GetShieldedOutputsHash(); } var branchId = nBranchId.HasValue ? nBranchId.Value : Version == OVERWINTER_VERSION ? OVERWINTER_BRANCH_ID : Version == SAPLING_VERSION ? SAPLING_BRANCH_ID : 0; var branchIdData = BitConverter.IsLittleEndian ? BitConverter.GetBytes(branchId) : BitConverter.GetBytes(branchId).Reverse().ToArray(); var personal = Encoding.ASCII.GetBytes("ZcashSigHash") .Concat(branchIdData) .ToArray(); using (var ss = new BLAKE2bWriter(personal)) { // Version var nVersion = Version; ss.ReadWriteVersionEncoded(ref nVersion, ref fOverwintered); ss.ReadWrite(nVersionGroupId); // Input prevouts/nSequence (none/all, depending on flags) ss.ReadWrite(hashPrevouts); ss.ReadWrite(hashSequence); // Outputs (none/one/all, depending on flags) ss.ReadWrite(hashOutputs); // JoinSplits ss.ReadWrite(hashJoinSplits); if (nVersionGroupId == SAPLING_VERSION_GROUP_ID) { // Spend descriptions ss.ReadWrite(hashShieldedSpends); // Output descriptions ss.ReadWrite(hashShieldedOutputs); } // Locktime ss.ReadWriteStruct(LockTime); // Expiry height ss.ReadWrite(nExpiryHeight); if (nVersionGroupId == SAPLING_VERSION_GROUP_ID) { // Sapling value balance ss.ReadWrite(valueBalance); } // Sighash type ss.ReadWrite((uint)nHashType); // If this hash is for a transparent input signature // (i.e. not for txTo.joinSplitSig): if ((uint)nIn != NOT_AN_INPUT) // always true, NBitcoin restricts us to transparent txs only, left to match the original code { // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contained in hashSequence. ss.ReadWrite(Inputs[nIn].PrevOut); ss.ReadWrite(scriptCode); ss.ReadWrite(amount.Satoshi); ss.ReadWrite(Inputs[nIn].Sequence.Value); } return(ss.GetHash()); } } else { // Check for invalid use of SIGHASH_SINGLE if (((uint)nHashType & 0x1f) == (uint)SigHash.Single) { if (nIn >= Outputs.Count) { throw new InvalidOperationException("No matching output for SIGHASH_SINGLE"); } } var scriptCopy = new Script(scriptCode.ToOps().Where(op => op.Code != OpcodeType.OP_CODESEPARATOR)); var txCopy = new ZcashTransaction(this.ToHex()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (nHashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (nHashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } // clean JS signature // see https://github.com/zcash/zcash/blob/e868f8247faea8cc74aef69262d93bdeacc82c53/src/script/interpreter.cpp#L1053 txCopy.joinSplitSig = new byte[64]; //Serialize TxCopy, append 4 byte hashtypecode using (var hs = CreateSignatureHashStream()) { BitcoinStream stream = new BitcoinStream(hs, true); stream.Type = SerializationType.Hash; stream.TransactionOptions = sigversion == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness; txCopy.ReadWrite(stream); stream.ReadWrite((uint)nHashType); return(hs.GetHash()); } } }
/// <inheritdoc /> public virtual void ExecuteBlock(RuleContext context, TaskScheduler taskScheduler = null) { this.logger.LogTrace("()"); Block block = context.BlockValidationContext.Block; ChainedBlock index = context.BlockValidationContext.ChainedBlock; DeploymentFlags flags = context.Flags; UnspentOutputSet view = context.Set; this.PerformanceCounter.AddProcessedBlocks(1); taskScheduler = taskScheduler ?? TaskScheduler.Default; long sigOpsCost = 0; Money fees = Money.Zero; var checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { this.PerformanceCounter.AddProcessedTransactions(1); Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { int[] prevheights; if (!view.HaveInputs(tx)) { this.logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final. // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set. for (int j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { this.logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost) { ConsensusErrors.BadBlockSigOps.Throw(); } // TODO: Simplify this condition. if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { this.CheckInputs(tx, view, index.Height); fees += view.GetValueIn(tx) - tx.TotalOut; var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { this.PerformanceCounter.AddProcessedInputs(1); TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); }); checkInput.Start(taskScheduler); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); bool passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { this.logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } this.logger.LogTrace("(-)"); }
/// <inheritdoc /> public override async Task RunAsync(RuleContext context) { this.Logger.LogTrace("()"); Block block = context.BlockValidationContext.Block; ChainedHeader index = context.BlockValidationContext.ChainedHeader; DeploymentFlags flags = context.Flags; UnspentOutputSet view = context.Set; this.Parent.PerformanceCounter.AddProcessedBlocks(1); long sigOpsCost = 0; Money fees = Money.Zero; var checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { this.Parent.PerformanceCounter.AddProcessedTransactions(1); Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { // TODO: Simplify this condition. if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { if (!view.HaveInputs(tx)) { this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } var prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final. // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set. for (int j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.PowConsensusOptions.MaxBlockSigopsCost) { this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]"); ConsensusErrors.BadBlockSigOps.Throw(); } // TODO: Simplify this condition. if (!tx.IsCoinBase && (!context.IsPoS || (context.IsPoS && !tx.IsCoinStake))) { this.CheckInputs(tx, view, index.Height); fees += view.GetValueIn(tx) - tx.TotalOut; var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { this.Parent.PerformanceCounter.AddProcessedInputs(1); TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.Parent.Network); ctx.ScriptVerify = flags.ScriptFlags; bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker); if (verifyScriptResult == false) { this.Logger.LogTrace("Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'", tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error); } return(verifyScriptResult); }); checkInput.Start(); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); foreach (Task <bool> checkInput in checkInputs) { if (await checkInput.ConfigureAwait(false)) { continue; } this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } this.Logger.LogTrace("(-)"); }
/// <inheritdoc /> public override async Task RunAsync(RuleContext context) { Block block = context.ValidationContext.BlockToValidate; ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate; DeploymentFlags flags = context.Flags; UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet; long sigOpsCost = 0; Money fees = Money.Zero; var checkInputs = new List <Task <bool> >(); for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++) { Transaction tx = block.Transactions[txIndex]; if (!context.SkipValidation) { if (!tx.IsCoinBase && !view.HaveInputs(tx)) { this.Logger.LogTrace("Transaction '{0}' has not inputs", tx.GetHash()); this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]"); ConsensusErrors.BadTransactionMissingInput.Throw(); } if (!this.IsTxFinal(tx, context)) { this.Logger.LogTrace("Transaction '{0}' is not final", tx.GetHash()); this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]"); ConsensusErrors.BadTransactionNonFinal.Throw(); } // GetTransactionSignatureOperationCost counts 3 types of sigops: // * legacy (always), // * p2sh (when P2SH enabled in flags and excludes coinbase), // * witness (when witness enabled in flags and excludes coinbase). sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags); if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost) { this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]"); ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase) { this.CheckInputs(tx, view, index.Height); if (!tx.IsCoinStake) { fees += view.GetValueIn(tx) - tx.TotalOut; } var txData = new PrecomputedTransactionData(tx); for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++) { TxIn input = tx.Inputs[inputIndex]; int inputIndexCopy = inputIndex; TxOut txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => this.CheckInput(tx, inputIndexCopy, txout, txData, input, flags)); checkInput.Start(); checkInputs.Add(checkInput); } } } this.UpdateCoinView(context, tx); } if (!context.SkipValidation) { this.CheckBlockReward(context, fees, index.Height, block); foreach (Task <bool> checkInput in checkInputs) { if (await checkInput.ConfigureAwait(false)) { continue; } this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]"); ConsensusErrors.BadTransactionScriptError.Throw(); } } else { this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height); } }
/// <inheritdoc/> protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags) { if (txout.ScriptPubKey.IsSmartContractExec() || txout.ScriptPubKey.IsSmartContractInternalCall()) { return(input.ScriptSig.IsSmartContractSpend()); } var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData); var ctx = new ScriptEvaluationContext(this.Parent.Network); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); }
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData) { uint nForkHashType = (uint)nHashType; if (UsesForkId(nHashType)) { nForkHashType |= ForkId << 8; } if ((SupportSegwit && sigversion == HashVersion.Witness) || UsesForkId(nHashType)) { if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney) { throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput)); } uint256 hashPrevouts = uint256.Zero; uint256 hashSequence = uint256.Zero; uint256 hashOutputs = uint256.Zero; if ((nHashType & SigHash.AnyoneCanPay) == 0) { hashPrevouts = precomputedTransactionData == null? GetHashPrevouts() : precomputedTransactionData.HashPrevouts; } if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashSequence = precomputedTransactionData == null? GetHashSequence() : precomputedTransactionData.HashSequence; } if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None) { hashOutputs = precomputedTransactionData == null? GetHashOutputs() : precomputedTransactionData.HashOutputs; } else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count) { BitcoinStream ss = CreateHashWriter(sigversion); ss.ReadWrite(this.Outputs[nIn]); hashOutputs = GetHash(ss); } BitcoinStream sss = CreateHashWriter(sigversion); // Version sss.ReadWrite(this.Version); // Input prevouts/nSequence (none/all, depending on flags) sss.ReadWrite(hashPrevouts); sss.ReadWrite(hashSequence); // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. sss.ReadWrite(Inputs[nIn].PrevOut); sss.ReadWrite(scriptCode); sss.ReadWrite(spentOutput.Value.Satoshi); sss.ReadWrite((uint)Inputs[nIn].Sequence); // Outputs (none/one/all, depending on flags) sss.ReadWrite(hashOutputs); // Locktime sss.ReadWriteStruct(LockTime); // Sighash type sss.ReadWrite((uint)nForkHashType); return(GetHash(sss)); } if (nIn >= Inputs.Count) { return(uint256.One); } var hashType = nHashType & (SigHash)31; // Check for invalid use of SIGHASH_SINGLE if (hashType == SigHash.Single) { if (nIn >= Outputs.Count) { return(uint256.One); } } var scriptCopy = scriptCode.Clone(); scriptCode = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR); var txCopy = GetConsensusFactory().CreateTransaction(); txCopy.FromBytes(this.ToBytes()); //Set all TxIn script to empty string foreach (var txin in txCopy.Inputs) { txin.ScriptSig = new Script(); } //Copy subscript into the txin script you are checking txCopy.Inputs[nIn].ScriptSig = scriptCopy; if (hashType == SigHash.None) { //The output of txCopy is set to a vector of zero size. txCopy.Outputs.Clear(); //All other inputs aside from the current input in txCopy have their nSequence index set to zero foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } else if (hashType == SigHash.Single) { //The output of txCopy is resized to the size of the current input index+1. txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1)); //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1. for (var i = 0; i < txCopy.Outputs.Count; i++) { if (i == nIn) { continue; } txCopy.Outputs[i] = new TxOut(); } //All other txCopy inputs aside from the current input are set to have an nSequence index of zero. foreach (var input in txCopy.Inputs.Where((x, i) => i != nIn)) { input.Sequence = 0; } } if ((nHashType & SigHash.AnyoneCanPay) != 0) { //The txCopy input vector is resized to a length of one. var script = txCopy.Inputs[nIn]; txCopy.Inputs.Clear(); txCopy.Inputs.Add(script); //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector. txCopy.Inputs[0].ScriptSig = scriptCopy; } //Serialize TxCopy, append 4 byte hashtypecode var stream = CreateHashWriter(sigversion); txCopy.ReadWrite(stream); stream.ReadWrite((uint)nForkHashType); return(GetHash(stream)); }
protected override bool CheckInput(Transaction tx, int inputIndexCopy, TxOut txout, PrecomputedTransactionData txData, TxIn input, DeploymentFlags flags) { if (txout.ScriptPubKey.IsScriptType(ScriptType.P2SH)) { // federation output is p2sh IList <Op> ops = input.ScriptSig.ToOps(); if (ops.Count > 5 && ops[0].PushData.Length == 0) // first op is zero, federation is at least 3 participants, last is redeem script { Script redeemScript = new Script(ops.Last().PushData); foreach (Op innerOp in redeemScript.ToOps()) { if (innerOp.Code == OpcodeType.OP_NOP9) { // federation overrides OP_NOP9 to push fed pub keys to the stack and the // needed signatures to satisfy the federation multisig, to avoid changing // the script engine for now we ignore such outputs and consider them valid // (as was with the opcode OP_NOP9) a malicious node may cause blockcore nodes // to accept invalid blocks however as long as blockcore nodes are minority nodes // its an acceptable risk, users not part of a federation should not really be effected. return(true); } } } } this.AllowSpend(txout, tx); return(base.CheckInput(tx, inputIndexCopy, txout, txData, input, flags)); }