public void TestPushData1()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;
            byte[] script;

            script = new byte[] {BitcoinScript.OP_PUSHDATA1};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA1, 0};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA1}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {2}));

            script = new byte[] {BitcoinScript.OP_PUSHDATA1, 1};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA1, 1, 1};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA1}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {3}));

            script = new byte[256];
            script[0] = BitcoinScript.OP_PUSHDATA1;
            script[1] = 255;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[257];
            script[0] = BitcoinScript.OP_PUSHDATA1;
            script[1] = 255;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA1}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {257}));

            script = new byte[12];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA1;
            script[2] = 10;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[13];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA1;
            script[2] = 10;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_PUSHDATA1}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0, 1}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {1, 12}));
        }
        public void TestEmpty()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;

            Assert.True(parser.TryParse(new byte[0], out commands));
            Assert.That(commands, Is.Empty);
        }
        public void TestPushData()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;
            byte[] script;

            script = new byte[] {BitcoinScript.OP_PUSHDATA_LEN_1};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA_LEN_1, 1};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA_LEN_1}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {2}));

            script = new byte[75];
            script[0] = BitcoinScript.OP_PUSHDATA_LEN_75;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[76];
            script[0] = BitcoinScript.OP_PUSHDATA_LEN_75;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA_LEN_75}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {76}));

            script = new byte[11];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA_LEN_1 + 9;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[12];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA_LEN_1 + 9;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_PUSHDATA_LEN_1 + 9}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0, 1}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {1, 11}));
        }
        private UnspentOutputsUpdate PrepareUnspentOutputsUpdate(StoredBlock block, BlockMessage blockMessage)
        {
            ScriptParser parser = new ScriptParser();

            ulong inputsSum = GetBlockReward(block);
            ulong outputsSum = 0;

            UnspentOutputsUpdate update = new UnspentOutputsUpdate(storage);

            for (int transactionNumber = 0; transactionNumber < blockMessage.Transactions.Length; transactionNumber++)
            {
                Tx transaction = blockMessage.Transactions[transactionNumber];

                ulong transactionInputsSum = 0;
                ulong transactionOutputsSum = 0;

                byte[] transactionHash = CryptoUtils.DoubleSha256(BitcoinStreamWriter.GetBytes(transaction.Write));

                List<UnspentOutput> unspentOutputs = update.FindUnspentOutputs(transactionHash);
                if (unspentOutputs.Any())
                {
                    //todo: use network settings
                    if (block.Height == 91842 || block.Height == 91880)
                    {
                        // this blocks are exceptions from BIP-30
                        foreach (UnspentOutput unspentOutput in unspentOutputs)
                        {
                            update.Spend(unspentOutput.TransactionHash, unspentOutput.OutputNumber, block);
                        }
                    }
                    else
                    {
                        throw new BitcoinProtocolViolationException(
                            $"The transaction '{BitConverter.ToString(transactionHash)}'" +
                            $" in block '{BitConverter.ToString(block.Hash)}'" +
                            $" has same hash as an existing unspent transaction (see BIP-30).");
                    }
                }

                //todo: check transaction hash against genesis block transaction hash
                if (transactionNumber != 0)
                {
                    foreach (TxIn input in transaction.Inputs)
                    {
                        UnspentOutput output = update.FindUnspentOutput(input.PreviousOutput.Hash, input.PreviousOutput.Index);
                        if (output == null)
                        {
                            throw new BitcoinProtocolViolationException(
                                $"The input of the transaction '{BitConverter.ToString(transactionHash)}'" +
                                $" in block '{BitConverter.ToString(block.Hash)}'" +
                                $" has been already spent or did not exist.");
                        }
                        //todo: check for overflow
                        transactionInputsSum += output.Sum;

                        List<ScriptCommand> inputCommands;
                        if (!parser.TryParse(input.SignatureScript, out inputCommands))
                        {
                            throw new BitcoinProtocolViolationException(
                                $"The transaction '{BitConverter.ToString(transactionHash)}'" +
                                $" in block '{BitConverter.ToString(block.Hash)}'" +
                                $" has an invalid signature script.");
                        }
                        if (inputCommands.Any(c => !IsValidSignatureCommand(c.Code)))
                        {
                            throw new BitcoinProtocolViolationException(
                                $"The transaction '{BitConverter.ToString(transactionHash)}'" +
                                $" in block '{BitConverter.ToString(block.Hash)}'" +
                                $" has forbidden commands in the signature script.");
                        }

                        //todo: check signature for the output
                        update.Spend(output.TransactionHash, output.OutputNumber, block);
                    }
                }

                for (int outputNumber = 0; outputNumber < transaction.Outputs.Length; outputNumber++)
                {
                    TxOut output = transaction.Outputs[outputNumber];
                    //todo: check for overflow
                    transactionOutputsSum += output.Value;

                    List<ScriptCommand> commands;
                    if (!parser.TryParse(output.PubkeyScript, out commands))
                    {
                        //todo: how Bitcoin Core works in this scenario?
                        throw new BitcoinProtocolViolationException(
                            $"The output of transaction '{BitConverter.ToString(transactionHash)}'" +
                            $" in block '{BitConverter.ToString(block.Hash)}'" +
                            $" has an invalid pubkey script script.");
                    }

                    UnspentOutput unspentOutput = UnspentOutput.Create(block, transaction, outputNumber);
                    update.Add(unspentOutput);
                }

                if (transactionNumber != 0 && transactionInputsSum < transactionOutputsSum)
                {
                    // for coinbase transaction output sum is checked later as part of total block inputs, outputs and reward sums equation
                    throw new BitcoinProtocolViolationException(
                        $"The sum of the inputs in the transaction '{BitConverter.ToString(transactionHash)}'" +
                        $" in block '{BitConverter.ToString(block.Hash)}'" +
                        $" is less than the sum of the outputs.");
                }

                //todo: check for overflow
                inputsSum += transactionInputsSum;
                //todo: check for overflow
                outputsSum += transactionOutputsSum;
            }

            if (inputsSum != outputsSum)
            {
                throw new BitcoinProtocolViolationException(
                    $"The sum of the inputs and the reward" +
                    $" in the block '{BitConverter.ToString(block.Hash)}'" +
                    $" does not match the sum of the outputs.");
            }

            return update;
        }
        public void TestSimple()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;

            Assert.True(parser.TryParse(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_VERIFY}, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_VERIFY}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0, 1}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {1, 1}));
        }
        public void TestPushData4()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;
            byte[] script;

            script = new byte[] {BitcoinScript.OP_PUSHDATA4, 0, 0, 0};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA4, 0, 0, 0, 0};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA4}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {5}));

            script = new byte[] {BitcoinScript.OP_PUSHDATA4, 0, 0, 0, 1};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA4, 0, 0, 0, 1, 1};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA4}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {6}));

            script = new byte[0x01020384];
            script[0] = BitcoinScript.OP_PUSHDATA4;
            script[1] = 0x01;
            script[2] = 0x02;
            script[3] = 0x03;
            script[4] = 0x80;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[0x01020385];
            script[0] = BitcoinScript.OP_PUSHDATA4;
            script[1] = 0x01;
            script[2] = 0x02;
            script[3] = 0x03;
            script[4] = 0x80;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA4}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {0x01020385}));

            script = new byte[15];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA4;
            script[2] = 0;
            script[3] = 0;
            script[4] = 0;
            script[5] = 10;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[16];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA4;
            script[2] = 0;
            script[3] = 0;
            script[4] = 0;
            script[5] = 10;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_PUSHDATA4}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0, 1}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {1, 15}));

            script = new byte[0x1000];
            script[0] = BitcoinScript.OP_PUSHDATA4;
            script[1] = 0xFF;
            script[2] = 0xFF;
            script[3] = 0xFF;
            script[4] = 0xFF;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);
        }
        public void TestPushData2()
        {
            ScriptParser parser = new ScriptParser();
            List<ScriptCommand> commands;
            byte[] script;

            script = new byte[] {BitcoinScript.OP_PUSHDATA2, 0};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA2, 0, 0};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA2}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {3}));

            script = new byte[] {BitcoinScript.OP_PUSHDATA2, 0, 1};
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[] {BitcoinScript.OP_PUSHDATA2, 0, 1, 1};
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA2}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {4}));

            script = new byte[0xFF82];
            script[0] = BitcoinScript.OP_PUSHDATA2;
            script[1] = 0xFF;
            script[2] = 0x80;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[0xFF83];
            script[0] = BitcoinScript.OP_PUSHDATA2;
            script[1] = 0xFF;
            script[2] = 0x80;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_PUSHDATA2}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {0xFF83}));

            script = new byte[13];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA2;
            script[2] = 0;
            script[3] = 10;
            Assert.False(parser.TryParse(script, out commands));
            Assert.That(commands, Is.Null);

            script = new byte[14];
            script[0] = BitcoinScript.OP_TRUE;
            script[1] = BitcoinScript.OP_PUSHDATA2;
            script[2] = 0;
            script[3] = 10;
            Assert.True(parser.TryParse(script, out commands));
            Assert.That(commands.Select(c => c.Code), Is.EqualTo(new byte[] {BitcoinScript.OP_TRUE, BitcoinScript.OP_PUSHDATA2}));
            Assert.That(commands.Select(c => c.Offset), Is.EqualTo(new int[] {0, 1}));
            Assert.That(commands.Select(c => c.Length), Is.EqualTo(new int[] {1, 13}));
        }