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 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 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})); }