示例#1
0
 public void MarkSpent(TxIn[] txInList)
 {
     foreach (var tin in txInList)
     {
         IUtxo utxo = Find(tin);
         utxo.IsBlockSpent = true;
     }
 }
示例#2
0
        internal void Add(byte[] hash, IUtxo output)
        {
            if (hashes is null)
            {
                hashes   = new List <byte[]>(1);
                database = new List <IUtxo>(1);
            }

            hashes.Add(hash);
            database.Add(output);
        }
示例#3
0
 internal void Add(byte[] hash, IUtxo output)
 {
     if (!database.ContainsKey(hash))
     {
         database.Add(hash, new List <IUtxo>()
         {
             output
         });
     }
     else if (!database[hash].Contains(output))
     {
         database[hash].Add(output);
     }
     else
     {
         Assert.True(false, "Dictionary contains duplicates.");
     }
 }
示例#4
0
        internal void Add(byte[] hash, IUtxo output)
        {
            if (database is null)
            {
                database = new Dictionary <byte[], List <Utxo> >(new ByteArrayComparer());
            }


            if (database.ContainsKey(hash))
            {
                database[hash].Add(new Utxo(output.Index, output.Amount, output.PubScript));
            }
            else
            {
                database.Add(hash, new List <Utxo>()
                {
                    new Utxo(output.Index, output.Amount, output.PubScript)
                });
            }
        }
示例#5
0
        /// <inheritdoc/>
        public bool Verify(ITransaction tx, out string error)
        {
            // If a tx is already in memory pool it must have been verified and be valid.
            // The SigOpCount property must be set by the caller (mempool dependency).
            if (!isMempool && mempool.Contains(tx))
            {
                // TODO: get the tx object from mempool the passed tx (from block) doesn't have any properties set
                TotalSigOpCount += tx.SigOpCount;
                TotalFee        += utxoDb.MarkSpentAndGetFee(tx.TxInList);
                if (!AnySegWit)
                {
                    AnySegWit = tx.WitnessList != null;
                }
                error = null;
                return(true);
            }

            // TODO: these 2 checks should be performed during creation of tx (ctor or Deserialize)
            if (tx.TxInList.Length == 0 || tx.TxOutList.Length == 0)
            {
                error = "Invalid number of inputs or outputs.";
                return(false);
            }

            ulong toSpend = 0;

            for (int i = 0; i < tx.TxInList.Length; i++)
            {
                TxIn currentInput = tx.TxInList[i];
                // TODO: add a condition in UTXO for when it is a coinbase transaction (they are not spendable if haven't
                // reached maturity ie. 100 blocks -> thisHeight - spendingCoinbaseHeight >= 100)
                IUtxo prevOutput = utxoDb.Find(currentInput);
                if (prevOutput is null)
                {
                    // TODO: add a ToString() method to TxIn?
                    error = $"Input {currentInput.TxHash.ToBase16()}:{currentInput.Index} was not found.";
                    return(false);
                }
                toSpend += prevOutput.Amount;

                PubkeyScriptSpecialType pubType = prevOutput.PubScript.GetSpecialType(consensus, BlockHeight);

                if (pubType == PubkeyScriptSpecialType.None || pubType == PubkeyScriptSpecialType.P2PKH)
                {
                    // If the type is not witness there shouldn't be any witness item
                    if (tx.WitnessList != null && tx.WitnessList.Length != 0 && tx.WitnessList[i].Items.Length != 0)
                    {
                        error = $"Unexpected witness." +
                                $"{Environment.NewLine}TxId: {tx.GetTransactionId()}";
                        return(false);
                    }

                    if (!currentInput.SigScript.TryEvaluate(out IOperation[] signatureOps, out int signatureOpCount, out error))
                    {
                        error = $"Invalid transaction signature script." +
                                $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                $"{Environment.NewLine}More info: {error}";
                        return(false);
                    }

                    // P2PKH is not a special type so the signature can contain extra OPs (eg. PushData() OP_DROP <sig> <pub>).
                    // The optimization only works when the signature script is standard (Push<sig> Push<pub>).
                    // The optimization doesn't need to use OpData, evaluate PubkeyScript, convert PubkeyScript (FindAndDelete),
                    // run the operations, count Ops, count sigOps, check for stack item overflow,
                    // or check stack after execution.
                    if (pubType == PubkeyScriptSpecialType.P2PKH && signatureOps.Length == 2 &&
                        signatureOps[0] is PushDataOp sigPush && sigPush.data != null &&
                        signatureOps[1] is PushDataOp pubPush && pubPush.data != null)
                    {
                        if (!VerifyP2pkh(tx, i, sigPush, pubPush, prevOutput.PubScript.Data, out error))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        TotalSigOpCount += currentInput.SigScript.CountSigOps() * Constants.WitnessScaleFactor;

                        if (!prevOutput.PubScript.TryEvaluate(out IOperation[] pubOps, out int pubOpCount, out error))
                        {
                            error = $"Invalid input transaction pubkey script." +
                                    $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                    $"{Environment.NewLine}More info: {error}";
                            return(false);
                        }

                        var stack = new OpData()
                        {
                            Tx        = tx,
                            TxInIndex = i,

                            ForceLowS            = ForceLowS,
                            StrictNumberEncoding = StrictNumberEncoding,

                            IsBip65Enabled  = consensus.IsBip65Enabled(BlockHeight),
                            IsBip112Enabled = consensus.IsBip112Enabled(BlockHeight),
                            IsStrictDerSig  = consensus.IsStrictDerSig(BlockHeight),
                            IsBip147Enabled = consensus.IsBip147Enabled(BlockHeight),
                        };

                        // Note that checking OP count is below max is done during Evaluate() and op.Run()
                        stack.ExecutingScript = signatureOps;
                        stack.OpCount         = signatureOpCount;
                        foreach (var op in signatureOps)
                        {
                            if (!op.Run(stack, out error))
                            {
                                error = $"Script evaluation failed." +
                                        $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                        $"{Environment.NewLine}More info: {error}";
                                return(false);
                            }
                        }

                        stack.ExecutingScript = pubOps;
                        stack.OpCount         = pubOpCount;
                        foreach (var op in pubOps)
                        {
                            if (!op.Run(stack, out error))
                            {
                                error = $"Script evaluation failed." +
                                        $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                        $"{Environment.NewLine}More info: {error}";
                                return(false);
                            }
                        }

                        if (!CheckStack(stack, out error))
                        {
                            return(false);
                        }
                    }
                }
示例#6
0
 public MockUtxoDatabase(byte[] hash, IUtxo output)
 {
     Add(hash, output);
 }
示例#7
0
 public MockUtxoDatabase(string hashHex, IUtxo output) : this(Helper.HexToBytes(hashHex), output)
 {
 }
        /// <inheritdoc/>
        public bool Verify(ITransaction tx, out string error)
        {
            // If a tx is already in memory pool it must have been verified and be valid.
            // The SigOpCount property must be set by the caller (mempool dependency).
            if (mempool.Contains(tx))
            {
                TotalSigOpCount += tx.SigOpCount;
                TotalFee        += utxoDb.MarkSpentAndGetFee(tx.TxInList);
                if (!AnySegWit)
                {
                    AnySegWit = tx.WitnessList != null;
                }
                error = null;
                return(true);
            }

            if (tx.TxInList.Length == 0 || tx.TxOutList.Length == 0)
            {
                error = "Invalid number of inputs or outputs.";
                return(false);
            }

            ulong toSpend = 0;

            for (int i = 0; i < tx.TxInList.Length; i++)
            {
                TxIn  currentInput = tx.TxInList[i];
                IUtxo prevOutput   = utxoDb.Find(currentInput);
                if (prevOutput is null)
                {
                    // TODO: add a ToString() method to TxIn?
                    error = $"Input {currentInput.TxHash.ToBase16()}:{currentInput.Index} was not found.";
                    return(false);
                }
                toSpend += prevOutput.Amount;

                if (!prevOutput.PubScript.TryEvaluate(out IOperation[] pubOps, out int pubOpCount, out error))
                {
                    error = $"Invalid input transaction pubkey script." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }

                if (!currentInput.SigScript.TryEvaluate(out IOperation[] signatureOps, out int signatureOpCount, out error))
                {
                    error = $"Invalid transaction signature script." +
                            $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                            $"{Environment.NewLine}More info: {error}";
                    return(false);
                }

                OpData stack = new OpData()
                {
                    Tx        = tx,
                    TxInIndex = i,

                    ForceLowS            = ForceLowS,
                    StrictNumberEncoding = StrictNumberEncoding,

                    IsBip65Enabled  = consensus.IsBip65Enabled(BlockHeight),
                    IsBip112Enabled = consensus.IsBip112Enabled(BlockHeight),
                    IsStrictDerSig  = consensus.IsStrictDerSig(BlockHeight),
                    IsBip147Enabled = consensus.IsBip147Enabled(BlockHeight),
                };

                PubkeyScriptSpecialType pubType = prevOutput.PubScript.GetSpecialType(consensus, BlockHeight);

                // TODO: optimize for specific pubScrTypes
                if (pubType == PubkeyScriptSpecialType.None || pubType == PubkeyScriptSpecialType.P2PKH)
                {
                    TotalSigOpCount += (prevOutput.PubScript.CountSigOps() + currentInput.SigScript.CountSigOps())
                                       * Constants.WitnessScaleFactor;

                    if ((tx.WitnessList != null && tx.WitnessList.Length != 0) &&
                        (tx.WitnessList[i].Items.Length != 0))
                    {
                        error = $"Unexpected witness." +
                                $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                $"{Environment.NewLine}More info: {error}";
                        return(false);
                    }

                    // Note that checking OP count is below max is done during Evaluate() and op.Run()
                    stack.ExecutingScript = signatureOps;
                    stack.OpCount         = signatureOpCount;
                    foreach (var op in signatureOps)
                    {
                        if (!op.Run(stack, out error))
                        {
                            error = $"Script evaluation failed." +
                                    $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                    $"{Environment.NewLine}More info: {error}";
                            return(false);
                        }
                    }

                    stack.ExecutingScript = pubOps;
                    stack.OpCount         = pubOpCount;
                    foreach (var op in pubOps)
                    {
                        if (!op.Run(stack, out error))
                        {
                            error = $"Script evaluation failed." +
                                    $"{Environment.NewLine}TxId: {tx.GetTransactionId()}" +
                                    $"{Environment.NewLine}More info: {error}";
                            return(false);
                        }
                    }
                }
                else if (pubType == PubkeyScriptSpecialType.P2SH)
                {
                    if (signatureOps.Length == 0 || !(signatureOps[^ 1] is PushDataOp rdmPush))