예제 #1
0
        public bool VerifyScript(UInt256 blockHash, int txIndex, byte[] scriptPubKey, Transaction tx, int inputIndex, byte[] script)
        {
            if (logger.IsTraceEnabled)
                logger.Trace(
@"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Verifying script for block {0}, transaction {1}, input {2}
{3}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
                    , blockHash, txIndex, inputIndex, script.ToArray().ToHexDataString());

            Stack stack, altStack;
            if (
                ExecuteOps(scriptPubKey, tx, inputIndex, script, out stack, out altStack)
                && stack.Count == 1 && altStack.Count == 0)
            {
                var success = stack.PeekBool(); //TODO Pop? does it matter?

                // Additional validation for spend-to-script-hash transactions:
                //TODO

                return success;
            }
            else
            {
                return false;
            }
        }
예제 #2
0
        public void TestCalculateTransactionHash()
        {
            var expectedHash = UInt256.ParseHex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
            var tx = new Transaction
            (
                version: 1,
                inputs: ImmutableArray.Create(
                    new TxInput
                    (
                        previousTxOutputKey: new TxOutputKey(txHash: UInt256.Zero, txOutputIndex: 4294967295),
                        scriptSignature: "04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".HexToByteArray().ToImmutableArray(),
                        sequence: 4294967295
                    )),
                outputs: ImmutableArray.Create(
                    new TxOutput
                    (
                        value: (UInt64)(50L * 100.MILLION()),
                        scriptPublicKey: "4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".HexToByteArray().ToImmutableArray()
                    )),
                lockTime: 0
            );

            Assert.AreEqual(expectedHash, DataCalculator.CalculateTransactionHash(tx));
            Assert.AreEqual(expectedHash, DataCalculator.CalculateTransactionHash(tx.Version, tx.Inputs, tx.Outputs, tx.LockTime));
        }
예제 #3
0
        public Transaction CreateCoinbaseTransaction(ECPublicKeyParameters publicKey, byte[] coinbase)
        {
            var tx = new Transaction
            (
                version: 1,
                inputs: ImmutableArray.Create
                (
                    new TxInput
                    (
                        previousTxOutputKey: new TxOutputKey
                        (
                            txHash: UInt256.Zero,
                            txOutputIndex: 0
                        ),
                        scriptSignature: coinbase.ToImmutableArray(),
                        sequence: 0
                    )
                ),
                outputs: ImmutableArray.Create
                (
                    new TxOutput
                    (
                        value: 50L * (100 * 1000 * 1000),
                        scriptPublicKey: CreatePublicKeyScript(publicKey).ToImmutableArray()
                    )
                ),
                lockTime: 0
            );

            return tx;
        }
예제 #4
0
 public override void ValidationTransactionScript(ChainedHeader chainedHeader, Transaction tx, int txIndex, TxInput txInput, int txInputIndex, TxOutput prevTxOutput)
 {
     if (ValidationTransactionScriptAction == null)
         base.ValidationTransactionScript(chainedHeader, tx, txIndex, txInput, txInputIndex, prevTxOutput);
     else
         ValidationTransactionScriptAction(chainedHeader, tx, txIndex, txInput, txInputIndex, prevTxOutput);
 }
예제 #5
0
 public LoadingTx(int txIndex, Transaction transaction, ChainedHeader chainedHeader, ImmutableArray<TxLookupKey> prevOutputTxKeys)
 {
     Transaction = transaction;
     TxIndex = txIndex;
     ChainedHeader = chainedHeader;
     PrevOutputTxKeys = prevOutputTxKeys;
     InputTxes = new CompletionArray<DecodedTx>(txIndex != 0 ? transaction.Inputs.Length : 0);
 }
예제 #6
0
        public static DecodedBlockTx Create(int txIndex, Transaction tx)
        {
            if (tx == null)
                throw new ArgumentNullException(nameof(tx));

            var decodedTx = DataEncoder.EncodeTransaction(tx);

            return new DecodedBlockTx(txIndex, decodedTx);
        }
예제 #7
0
        public byte[] TxSignature(byte[] scriptPubKey, Transaction tx, int inputIndex, byte hashType)
        {
            ///TODO
            Debug.Assert(inputIndex < tx.Inputs.Length);

            // Blank out other inputs' signatures
            var empty = ImmutableArray.Create<byte>();
            var newInputs = ImmutableArray.CreateBuilder<TxInput>(tx.Inputs.Length);
            for (var i = 0; i < tx.Inputs.Length; i++)
            {
                var oldInput = tx.Inputs[i];
                var newInput = oldInput.With(scriptSignature: i == inputIndex ? scriptPubKey.ToImmutableArray() : empty);
                newInputs.Add(newInput);
            }

            //// Blank out some of the outputs
            //if ((hashType & 0x1F) == (int)ScriptHashType.SIGHASH_NONE)
            //{
            //    //TODO
            //    Debug.Assert(false);

            //    // Wildcard payee

            //    // Let the others update at will
            //}
            //else if ((hashType & 0x1F) == (int)ScriptHashType.SIGHASH_SINGLE)
            //{
            //    //TODO
            //    Debug.Assert(false);

            //    // Only lock-in the txout payee at same index as txin

            //    // Let the others update at will
            //}

            //// Blank out other inputs completely, not recommended for open transactions
            //if ((hashType & 0x80) == (int)ScriptHashType.SIGHASH_ANYONECANPAY)
            //{
            //    //TODO
            //    Debug.Assert(false);
            //}

            // create simplified transaction
            var newTx = tx.With(Inputs: newInputs.ToImmutable());

            // return wire-encoded simplified transaction with the 4-byte hashType tacked onto the end
            using (var stream = new MemoryStream())
            using (var writer = new BinaryWriter(stream))
            {
                writer.WriteBytes(DataEncoder.EncodeTransaction(newTx));
                writer.WriteUInt32(hashType);

                return stream.ToArray();
            }
        }
예제 #8
0
        public void TestReadOneLoadingTx()
        {
            var coreStorageMock = new Mock<ICoreStorage>();

            // create a fake transaction with 4 inputs
            var prevTxCount = 4;
            var txIndex = 1;
            var chainedHeader = RandomData.RandomChainedHeader();

            // create previous transactions for 4 inputs
            var prevTxes = new Transaction[prevTxCount];
            var inputs = new TxInput[prevTxCount];
            for (var i = 0; i < prevTxCount; i++)
            {
                var prevTx = RandomData.RandomTransaction();
                var prevBlockTx = (BlockTx)BlockTx.Create(i, prevTx);

                prevTxes[i] = prevTx;
                inputs[i] = new TxInput(prevTx.Hash, 0, ImmutableArray.Create<byte>(), 0);

                // mock retrieval of the previous transaction
                coreStorageMock.Setup(coreStorage => coreStorage.TryGetTransaction(UInt256.Zero, i, out prevBlockTx)).Returns(true);
            }

            // create a loading tx with the 4 inputs referencing block hash 0
            var tx = RandomData.RandomTransaction(new RandomDataOptions { TxOutputCount = 1 })
                .CreateWith(Inputs: inputs.ToImmutableArray()).Transaction;
            var prevOutputTxKeys = ImmutableArray.CreateRange(
                Enumerable.Range(0, prevTxCount).Select(x => new TxLookupKey(UInt256.Zero, x)));
            var loadingTx = new LoadingTx(txIndex, tx, chainedHeader, prevOutputTxKeys);

            // begin queuing transactions to load
            var loadingTxes = new BufferBlock<LoadingTx>();
            loadingTxes.Post(loadingTx);
            loadingTxes.Complete();

            // begin transaction loading
            var txLoader = TxLoader.LoadTxes(coreStorageMock.Object, loadingTxes);

            // verify the loaded transaction
            var loadedTxesBuffer = new BufferBlock<LoadedTx>();
            txLoader.LinkTo(loadedTxesBuffer, new DataflowLinkOptions { PropagateCompletion = true });
            txLoader.Completion.Wait();

            IList<LoadedTx> actualLoadedTxes;
            Assert.IsTrue(loadedTxesBuffer.TryReceiveAll(out actualLoadedTxes));

            var actualLoadedTx = actualLoadedTxes.Single();

            Assert.AreEqual(loadingTx.TxIndex, actualLoadedTx.TxIndex);
            Assert.AreEqual(loadingTx.Transaction, actualLoadedTx.Transaction);
            CollectionAssert.AreEqual(prevTxes.Select(x => x.Hash).ToArray(), actualLoadedTx.InputTxes.Select(x => x.Hash).ToArray());
        }
예제 #9
0
 /// <summary>
 /// Initializes a new instance of <see cref="LoadedTx"/> with the specified transaction and each input's previous transaction.
 /// </summary>
 /// <param name="transaction">The transaction.</param>
 /// <param name="txIndex">The index of the transaction.</param>
 /// <param name="inputTxes">The array of transactions corresponding to each input's previous transaction.</param>
 public LoadedTx(Transaction transaction, int txIndex, ImmutableArray<DecodedTx> inputTxes)
 {
     Transaction = transaction;
     TxIndex = txIndex;
     InputTxes = inputTxes;
     if (TxIndex == 0)
     {
         if (inputTxes.Length != 0)
             throw new InvalidOperationException($"Coinbase InputTxes.Length: {inputTxes.Length}");
     }
     else
     {
         if (inputTxes.Length != transaction.Inputs.Length)
             throw new InvalidOperationException($"Transaction.Inputs.Length: {transaction.Inputs.Length}, InputTxes.Length: {inputTxes.Length}");
     }
 }
예제 #10
0
        private void Mint(IChainStateCursor chainStateCursor, Transaction tx, int txIndex, ChainedHeader chainedHeader)
        {
            // add transaction to the utxo
            var unspentTx = new UnspentTx(tx.Hash, chainedHeader.Height, txIndex, tx.Version, tx.IsCoinbase, tx.Outputs.Length, OutputState.Unspent);
            if (!chainStateCursor.TryAddUnspentTx(unspentTx))
            {
                // duplicate transaction
                logger.Warn($"Duplicate transaction at block {chainedHeader.Height:N0}, {chainedHeader.Hash}, coinbase");
                throw new ValidationException(chainedHeader.Hash);
            }

            // add transaction outputs to the utxo
            for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++)
            {
                var output = tx.Outputs[outputIndex];
                if (!chainStateCursor.TryAddUnspentTxOutput(new TxOutputKey(tx.Hash, (uint)outputIndex), output))
                    throw new ValidationException(chainedHeader.Hash);
            }
        }
예제 #11
0
        public byte[] CreatePrivateKeyScript(Transaction tx, int inputIndex, byte hashType, ECPrivateKeyParameters privateKey, ECPublicKeyParameters publicKey)
        {
            //TODO
            var scriptEngine = new ScriptEngine();

            var publicAddress = CreatePublicAddress(publicKey);
            var publicKeyScript = CreatePublicKeyScript(publicAddress);
            var txSignature = scriptEngine.TxSignature(publicKeyScript, tx, inputIndex, hashType);
            var txSignatureHash = SHA256Static.ComputeDoubleHash(txSignature);

            //Debug.WriteLine("Signing Tx:       {0}".Format2(txSignature.ToHexDataString()));
            //Debug.WriteLine("Signing Tx  Hash: {0}".Format2(txSignatureHash.ToHexDataString()));

            var signer = new ECDsaSigner();
            signer.Init(forSigning: true, parameters: privateKey);
            var signature = signer.GenerateSignature(txSignatureHash);
            var r = signature[0];
            var s = signature[1];

            byte[] sigEncoded;
            using (var stream = new MemoryStream())
            {
                using (var asn1Stream = new Asn1OutputStream(stream))
                {
                    asn1Stream.WriteObject(new DerSequence(new DerInteger(r), new DerInteger(s)));
                }

                sigEncoded = stream.ToArray().Concat(hashType);
            }

            //Debug.WriteLine("Sig R:       {0}".Format2(r.ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Sig S:       {0}".Format2(s.ToHexNumberStringUnsigned()));
            //Debug.WriteLine("Sig Encoded: {0}".Format2(sigEncoded.ToHexDataString()));

            using (var privateKeyScript = new ScriptBuilder())
            {
                privateKeyScript.WritePushData(sigEncoded);
                privateKeyScript.WritePushData(publicAddress);
                //Debug.WriteLine("Private Script: {0}".Format2(privateKeyScript.GetScript().ToHexDataString()));

                return privateKeyScript.GetScript();
            }
        }
예제 #12
0
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out Transaction transaction)
        {
            int blockId;

            if (!this.TryGetBlockId(blockHash, out blockId))
            {
                transaction = default(Transaction);
                return(false);
            }

            using (var handle = this.cursorCache.TakeItem())
            {
                var cursor = handle.Item;

                using (var jetTx = cursor.jetSession.BeginTransaction())
                {
                    Api.JetSetCurrentIndex(cursor.jetSession, cursor.blocksTableId, "IX_BlockIdTxIndex");
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, blockId, MakeKeyGrbit.NewKey);
                    Api.MakeKey(cursor.jetSession, cursor.blocksTableId, txIndex, MakeKeyGrbit.None);
                    if (Api.TrySeek(cursor.jetSession, cursor.blocksTableId, SeekGrbit.SeekEQ))
                    {
                        var txBytes = Api.RetrieveColumn(cursor.jetSession, cursor.blocksTableId, cursor.blockTxBytesColumnId);
                        if (txBytes != null)
                        {
                            transaction = DataEncoder.DecodeTransaction(txBytes);
                            return(true);
                        }
                        else
                        {
                            transaction = default(Transaction);
                            return(false);
                        }
                    }
                    else
                    {
                        transaction = default(Transaction);
                        return(false);
                    }
                }
            }
        }
예제 #13
0
        private void TestTransactionSignature(byte[][] expectedSignatures, Transaction tx, IDictionary<UInt256, Transaction> txLookup)
        {
            var scriptEngine = new ScriptEngine();
            for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
            {
                var input = tx.Inputs[inputIndex];
                var prevOutput = txLookup[input.PreviousTxOutputKey.TxHash].Outputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()];

                var hashType = GetHashTypeFromScriptSig(input.ScriptSignature.ToArray());

                var actual = scriptEngine.TxSignature(prevOutput.ScriptPublicKey.ToArray(), tx, inputIndex, hashType);
                CollectionAssert.AreEqual(expectedSignatures[inputIndex].ToList(), actual.ToList());
            }
        }
예제 #14
0
        public static DecodedTx EncodeTransaction(Transaction tx)
        {
            using (var stream = new MemoryStream())
            using (var writer = new BinaryWriter(stream))
            {
                EncodeTransaction(writer, tx);

                var txBytes = stream.ToArray();

                return new DecodedTx(txBytes.ToImmutableArray(), tx);
            }
        }
예제 #15
0
        public static DecodedTx EncodeTransaction(UInt32 Version, ImmutableArray<TxInput> Inputs, ImmutableArray<TxOutput> Outputs, UInt32 LockTime)
        {
            using (var stream = new MemoryStream())
            using (var writer = new BinaryWriter(stream))
            {
                writer.WriteUInt32(Version);
                writer.WriteList(Inputs, input => EncodeTxInput(writer, input));
                writer.WriteList(Outputs, output => EncodeTxOutput(writer, output));
                writer.WriteUInt32(LockTime);

                var txBytes = stream.ToArray();
                var txHash = new UInt256(SHA256Static.ComputeDoubleHash(txBytes));
                var tx = new Transaction(Version, Inputs, Outputs, LockTime, txHash);

                return new DecodedTx(txBytes.ToImmutableArray(), tx);
            }
        }
예제 #16
0
 public static void EncodeTransaction(BinaryWriter writer, Transaction tx)
 {
     writer.WriteUInt32(tx.Version);
     writer.WriteList(tx.Inputs, input => EncodeTxInput(writer, input));
     writer.WriteList(tx.Outputs, output => EncodeTxOutput(writer, output));
     writer.WriteUInt32(tx.LockTime);
 }
예제 #17
0
 //TODO only used by tests
 public BlockTx(int txIndex, Transaction tx)
     : this(txIndex, 0, tx.Hash, false, tx)
 { }
예제 #18
0
 public static void GetTransaction(MainnetBlockProvider blockProvider, int blockIndex, int txIndex, out Block block, out Transaction tx)
 {
     block = blockProvider.GetBlock(blockIndex);
     tx = block.Transactions[txIndex];
 }
예제 #19
0
        public bool VerifySignature(byte[] scriptPubKey, Transaction tx, byte[] sig, byte[] pubKey, int inputIndex, out byte hashType, out byte[] txSignature, out byte[] txSignatureHash)
        {
            // get the 1-byte hashType off the end of sig
            hashType = sig[sig.Length - 1];

            // get the DER encoded portion of sig, which is everything except the last byte (the last byte being hashType)
            var sigDER = sig.Take(sig.Length - 1).ToArray();

            // get the simplified/signing version of the transaction
            txSignature = TxSignature(scriptPubKey, tx, inputIndex, hashType);

            // get the hash of the simplified/signing version of the transaction
            txSignatureHash = SHA256Static.ComputeDoubleHash(txSignature);

            if (this.ignoreSignatures)
            {
                return true;
            }
            else
            {
            #if SECP256K1_DLL
                // verify that signature is valid for pubKey and the simplified/signing transaction's hash
                return Signatures.Verify(txSignatureHash, sigDER, pubKey) == Signatures.VerifyResult.Verified;
            #else
                return true;
            #endif
            }
        }
예제 #20
0
 public BlockTx(int index, int depth, UInt256 hash, bool pruned, Transaction transaction)
     : base(index, depth, hash, pruned)
 {
     Transaction = transaction;
 }
예제 #21
0
        public static void GetFirstMultiInputTransaction(MainnetBlockProvider blockProvider, out Block block, out Transaction tx, out IDictionary<UInt256, Transaction> txLookup)
        {
            txLookup = new Dictionary<UInt256, Transaction>();

            // prior outputs for first transaction
            GetTransaction(blockProvider, 360, 0, out block, out tx);
            txLookup.Add(tx.Hash, tx);

            GetTransaction(blockProvider, 187, 1, out block, out tx);
            txLookup.Add(tx.Hash, tx);

            GetTransaction(blockProvider, 248, 1, out block, out tx);
            txLookup.Add(tx.Hash, tx);

            // first transaction
            // do this last so its output is what is returned
            GetTransaction(blockProvider, 496, 1, out block, out tx);
            txLookup.Add(tx.Hash, tx);
        }
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out Transaction transaction)
        {
            ImmutableSortedDictionary<int, BlockTx> blockTxes;
            BlockTx blockTx;

            if (this.allBlockTxes.TryGetValue(blockHash, out blockTxes)
                && blockTxes.TryGetValue(txIndex, out blockTx))
            {
                transaction = blockTx.Transaction;
                return true;
            }
            else
            {
                transaction = default(Transaction);
                return false;
            }
        }
 public bool TryGetTransaction(UInt256 blockHash, int txIndex, out Transaction transaction)
 {
     return GetStorage(blockHash).TryGetTransaction(blockHash, txIndex, out transaction);
 }
예제 #24
0
        private void TestTransactionVerifyScript(Transaction tx, IDictionary<UInt256, Transaction> txLookup)
        {
            var scriptEngine = new ScriptEngine();

            for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
            {
                var input = tx.Inputs[inputIndex];
                var prevOutput = txLookup[input.PreviousTxOutputKey.TxHash].Outputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()];

                var script = GetScriptFromInputPrevOutput(input, prevOutput);

                var result = scriptEngine.VerifyScript(UInt256.Zero /*blockHash*/, -1 /*txIndex*/, prevOutput.ScriptPublicKey.ToArray(), tx, inputIndex, script);

                Assert.IsTrue(result);
            }
        }
예제 #25
0
        private bool ExecuteOps(byte[] scriptPubKey, Transaction tx, int inputIndex, byte[] script, out Stack stack, out Stack altStack)
        {
            stack = new Stack();
            altStack = new Stack();

            using (var scriptStream = new MemoryStream(script))
            using (var opReader = new BinaryReader(scriptStream))
            {
                while (opReader.BaseStream.Position < script.Length)
                {
                    var opByte = opReader.ReadByte();
                    var op = (ScriptOp)Enum.ToObject(typeof(ScriptOp), opByte);

                    if (logger.IsTraceEnabled)
                        logger.Trace("Executing {0} with stack count: {1}", OpName(opByte), stack.Count);

                    switch (op)
                    {
                        // Constants
                        case ScriptOp.OP_PUSHDATA1:
                            {
                                if (opReader.BaseStream.Position + 1 >= script.Length)
                                    return false;

                                var length = opReader.ReadByte();
                                stack.PushBytes(opReader.ReadBytes(length));
                            }
                            break;

                        case ScriptOp.OP_PUSHDATA2:
                            {
                                if (opReader.BaseStream.Position + 2 >= script.Length)
                                    return false;

                                var length = opReader.ReadUInt16();
                                stack.PushBytes(opReader.ReadBytes(length));
                            }
                            break;

                        case ScriptOp.OP_PUSHDATA4:
                            {
                                if (opReader.BaseStream.Position + 4 >= script.Length)
                                    return false;

                                var length = opReader.ReadUInt32();
                                stack.PushBytes(opReader.ReadBytes(length.ToIntChecked()));
                            }
                            break;

                        // Flow control
                        case ScriptOp.OP_NOP:
                            {
                            }
                            break;

                        // Stack
                        case ScriptOp.OP_DROP:
                            {
                                if (stack.Count < 1)
                                    return false;

                                var value = stack.PopBytes();

                                if (logger.IsTraceEnabled)
                                    logger.Trace("{0} dropped {1}", OpName(opByte), value);
                            }
                            break;

                        case ScriptOp.OP_DUP:
                            {
                                if (stack.Count < 1)
                                    return false;

                                var value = stack.PeekBytes();
                                stack.PushBytes(value);

                                if (logger.IsTraceEnabled)
                                    logger.Trace("{0} duplicated {2}", OpName(opByte), value);
                            }
                            break;

                        // Splice

                        // Bitwise logic
                        case ScriptOp.OP_EQUAL:
                        case ScriptOp.OP_EQUALVERIFY:
                            {
                                if (stack.Count < 2)
                                    return false;

                                var value1 = stack.PopBytes();
                                var value2 = stack.PopBytes();

                                var result = value1.SequenceEqual(value2);
                                stack.PushBool(result);

                                if (logger.IsTraceEnabled)
                                    logger.Trace(
            @"{0} compared values:
            value1: {1}
            value2: {2}
            result: {3}", OpName(opByte), value1, value2, result);

                                if (op == ScriptOp.OP_EQUALVERIFY)
                                {
                                    if (result)
                                        stack.PopBool();
                                    else
                                        return false;
                                }
                            }
                            break;

                        // Arithmetic
                        // Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output.

                        // Crypto
                        case ScriptOp.OP_SHA256:
                            {
                                if (stack.Count < 1)
                                    return false;

                                var value = stack.PopBytes().ToArray();

                                var hash = SHA256Static.ComputeHash(value);
                                stack.PushBytes(hash);

                                if (logger.IsTraceEnabled)
                                    logger.Trace(
            @"{0} hashed value:
            value:  {1}
            hash:   {2}", OpName(opByte), value, hash);
                            }
                            break;

                        case ScriptOp.OP_HASH160:
                            {
                                if (stack.Count < 1)
                                    return false;

                                var value = stack.PopBytes().ToArray();

                                var hash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(value));
                                stack.PushBytes(hash);

                                if (logger.IsTraceEnabled)
                                    logger.Trace(
            @"{0} hashed value:
            value:  {1}
            hash:   {2}", OpName(opByte), value, hash);
                            }
                            break;

                        case ScriptOp.OP_CHECKSIG:
                        case ScriptOp.OP_CHECKSIGVERIFY:
                            {
                                if (stack.Count < 2)
                                    return false;

                                var pubKey = stack.PopBytes().ToArray();
                                var sig = stack.PopBytes().ToArray();

                                var startTime = DateTime.UtcNow;

                                byte hashType; byte[] txSignature, txSignatureHash;
                                var result = VerifySignature(scriptPubKey, tx, sig, pubKey, inputIndex, out hashType, out txSignature, out txSignatureHash);
                                stack.PushBool(result);

                                var finishTime = DateTime.UtcNow;

                                if (logger.IsTraceEnabled)
                                    logger.Trace(
            @"{0} executed in {9} ms:
            tx:                 {1}
            inputIndex:         {2}
            pubKey:             {3}
            sig:                {4}
            hashType:           {5}
            txSignature:        {6}
            txSignatureHash:    {7}
            result:             {8}", OpName(opByte), new byte[0] /*tx.ToRawBytes()*/, inputIndex, pubKey, sig, hashType, txSignature, txSignatureHash, result, (finishTime - startTime).TotalMilliseconds.ToString("0"));

                                if (op == ScriptOp.OP_CHECKSIGVERIFY)
                                {
                                    if (result)
                                        stack.PopBool();
                                    else
                                        return false;
                                }
                            }
                            break;

                        // Pseudo-words
                        // These words are used internally for assisting with transaction matching. They are invalid if used in actual scripts.

                        // Reserved words
                        // Any opcode not assigned is also reserved. Using an unassigned opcode makes the transaction invalid.

                        default:
                            //OP_PUSHBYTES1-75
                            if (opByte >= (int)ScriptOp.OP_PUSHBYTES1 && opByte <= (int)ScriptOp.OP_PUSHBYTES75)
                            {
                                stack.PushBytes(opReader.ReadBytes(opByte));

                                if (logger.IsTraceEnabled)
                                    logger.Trace("{0} loaded {1} bytes onto the stack: {2}", OpName(opByte), opByte, stack.PeekBytes());
                            }
                            // Unknown op
                            else
                            {
                                var message = $"Invalid operation in tx {tx.Hash} input {inputIndex}: {new[] { opByte }.ToHexNumberString()} {OpName(opByte)}";
                                //logger.Warn(message);
                                throw new Exception(message);
                            }
                            break;
                    }

                    if (logger.IsTraceEnabled)
                        logger.Trace(new string('-', 80));
                }
            }

            // TODO verify no if/else blocks left over

            // TODO not entirely sure what default return should be
            return true;
        }
예제 #26
0
        private void TestTransactionVerifySignature(byte[] expectedHashTypes, byte[][] expectedSignatures, byte[][] expectedSignatureHashes, Transaction tx, IDictionary<UInt256, Transaction> txLookup)
        {
            var scriptEngine = new ScriptEngine();

            for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++)
            {
                var input = tx.Inputs[inputIndex];
                var prevOutput = txLookup[input.PreviousTxOutputKey.TxHash].Outputs[input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked()];

                var scriptSigBytes = input.ScriptSignature.ToArray();
                var hashType = GetHashTypeFromScriptSig(scriptSigBytes);
                var sig = GetSigFromScriptSig(scriptSigBytes);
                var pubKey = GetPubKeyFromScripts(scriptSigBytes, prevOutput.ScriptPublicKey.ToArray());

                byte[] txSignature, txSignatureHash;
                var result = scriptEngine.VerifySignature(prevOutput.ScriptPublicKey.ToArray(), tx, sig.ToArray(), pubKey.ToArray(), inputIndex, out hashType, out txSignature, out txSignatureHash);

                Assert.AreEqual(expectedHashTypes[inputIndex], hashType);
                CollectionAssert.AreEqual(expectedSignatures[inputIndex].ToList(), txSignature.ToList());
                CollectionAssert.AreEqual(expectedSignatureHashes[inputIndex].ToList(), txSignatureHash.ToList());
                Assert.IsTrue(result);
            }
        }
예제 #27
0
        public static void GetFirstHash160Transaction(MainnetBlockProvider blockProvider, out Block block, out Transaction tx, out IDictionary<UInt256, Transaction> txLookup)
        {
            txLookup = new Dictionary<UInt256, Transaction>();

            // prior outputs for first OP_HASH160 transaction
            GetTransaction(blockProvider, 2676, 0, out block, out tx);
            txLookup.Add(tx.Hash, tx);

            GetTransaction(blockProvider, 2812, 1, out block, out tx);
            txLookup.Add(tx.Hash, tx);

            // first OP_HASH160 transaction
            // do this last so its output is what is returned
            GetTransaction(blockProvider, 2812, 2, out block, out tx);
            txLookup.Add(tx.Hash, tx);
        }
예제 #28
0
        public bool TryGetTransaction(UInt256 blockHash, int txIndex, out Transaction transaction)
        {
            using (var txn = this.jetInstance.BeginTransaction(TransactionBeginFlags.ReadOnly))
            {
                byte[] blockTxBytes;
                if (txn.TryGet(blocksTableId, DbEncoder.EncodeBlockHashTxIndex(blockHash, txIndex), out blockTxBytes))
                {
                    var blockTx = DataEncoder.DecodeBlockTx(blockTxBytes);

                    transaction = blockTx.Transaction;
                    return transaction != null;
                }
                else
                {
                    transaction = default(Transaction);
                    return false;
                }
            }
        }
예제 #29
0
 /// <summary>
 /// Initializes a new instance of <see cref="LoadedTx"/> with the specified transaction and each input's previous transaction.
 /// </summary>
 /// <param name="transaction">The transaction.</param>
 /// <param name="txIndex">The index of the transaction.</param>
 /// <param name="inputTxes">The array of transactions corresponding to each input's previous transaction.</param>
 public LoadedTx(Transaction transaction, int txIndex, ImmutableArray<Transaction> inputTxes)
 {
     Transaction = transaction;
     TxIndex = txIndex;
     InputTxes = inputTxes;
 }
예제 #30
0
 public EncodedTx(ImmutableArray<byte> txBytes, Transaction transaction)
 {
     Hash = transaction.Hash;
     TxBytes = txBytes;
     lazyTx = new Lazy<Transaction>(() => transaction).Force();
 }
예제 #31
0
        public DecodedTx CreateSpendTransaction(Transaction prevTx, int prevInputIndex, byte hashType, UInt64 value, ECPrivateKeyParameters fromPrivateKey, ECPublicKeyParameters fromPublicKey, ECPublicKeyParameters toPublicKey)
        {
            var tx = Transaction.Create
            (
                version: 1,
                inputs: ImmutableArray.Create
                (
                    new TxInput
                    (
                        prevTxHash: prevTx.Hash,
                        prevTxOutputIndex: (UInt32)prevInputIndex,
                        scriptSignature: ImmutableArray.Create<byte>(),
                        sequence: 0
                    )
                ),
                outputs: ImmutableArray.Create
                (
                    new TxOutput
                    (
                        value: value,
                        scriptPublicKey: CreatePublicKeyScript(toPublicKey).ToImmutableArray()
                    )
                ),
                lockTime: 0
            );

            // sign the transaction
            var scriptSignature = CreatePrivateKeyScript(tx.Transaction, 0, hashType, fromPrivateKey, fromPublicKey).ToImmutableArray();

            // add the signature script to the transaction
            tx = tx.Transaction.CreateWith(Inputs: ImmutableArray.Create(tx.Transaction.Inputs[0].With(scriptSignature: scriptSignature)));

            return tx;
        }