Example #1
0
 public static BlockHeader DecodeBlockHeader(Stream stream, UInt256? blockHash = null)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new BlockHeader
         (
             version: reader.Read4Bytes(),
             previousBlock: reader.Read32Bytes(),
             merkleRoot: reader.Read32Bytes(),
             time: reader.Read4Bytes(),
             bits: reader.Read4Bytes(),
             nonce: reader.Read4Bytes(),
             hash: blockHash
         );
     }
 }
Example #2
0
 public static GetBlocksPayload DecodeGetBlocksPayload(Stream stream)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new GetBlocksPayload
         (
             Version: reader.Read4Bytes(),
             BlockLocatorHashes: reader.DecodeList(() => reader.Read32Bytes()),
             HashStop: reader.Read32Bytes()
         );
     }
 }
Example #3
0
        public Data.Blockchain ReadBlockchain(BlockchainKey blockchainKey)
        {
            CheckDatabaseFolder();

            var blockListBuilder = ImmutableList.CreateBuilder<ChainedBlock>();
            var utxoBuilder = ImmutableDictionary.CreateBuilder<UInt256, UnspentTx>();

            var connString = @"Server=localhost; Database=BitSharp_Blockchains; Trusted_Connection=true;";
            using (var conn = new SqlConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT BlockHash, PreviousBlockHash, Height, TotalWork
                        FROM ChainedBlocks
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash
                        ORDER BY Height ASC";

                    cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var blockHash = reader.GetUInt256(0);
                            var previousBlockHash = reader.GetUInt256(1);
                            var height = reader.GetInt32(2);
                            var totalWork = reader.GetBigInteger(3);

                            blockListBuilder.Add(new ChainedBlock(blockHash, previousBlockHash, height, totalWork));
                        }
                    }
                }

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT UtxoChunkBytes
                        FROM UtxoData
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash";

                    cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var chunkBytes = reader.GetBytes(0);
                            var chunkStream = new MemoryStream(chunkBytes);
                            using (var chunkReader = new BinaryReader(chunkStream))
                            {
                                var chunkLength = chunkReader.Read4Bytes().ToIntChecked();

                                var outputs = new TxOutputKey[chunkLength];

                                for (var i = 0; i < chunkLength; i++)
                                {
                                    var prevTxHash = chunkReader.Read32Bytes();
                                    var prevTxOutputIndex = chunkReader.Read4Bytes();

                                    outputs[i] = new TxOutputKey(prevTxHash, prevTxOutputIndex);
                                }

                                //TODO utxoBuilder.UnionWith(outputs);
                            }
                        }
                    }
                }
            }

            return new Data.Blockchain(blockListBuilder.ToImmutable(), blockListBuilder.Select(x => x.BlockHash).ToImmutableHashSet(), utxoBuilder.ToImmutable());
        }
Example #4
0
 public static InventoryVector DecodeInventoryVector(Stream stream)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new InventoryVector
         (
             Type: reader.Read4Bytes(),
             Hash: reader.Read32Bytes()
         );
     }
 }
Example #5
0
        public static VersionPayload DecodeVersionPayload(Stream stream, int payloadLength)
        {
            using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
            {
                var position = stream.Position;

                var versionPayload = new VersionPayload
                (
                    ProtocolVersion: reader.Read4Bytes(),
                    ServicesBitfield: reader.Read8Bytes(),
                    UnixTime: reader.Read8Bytes(),
                    RemoteAddress: DecodeNetworkAddress(stream),
                    LocalAddress: DecodeNetworkAddress(stream),
                    Nonce: reader.Read8Bytes(),
                    UserAgent: reader.ReadVarString(),
                    StartBlockHeight: reader.Read4Bytes(),
                    Relay: false
                );

                var readCount = stream.Position - position;
                if (versionPayload.ProtocolVersion >= VersionPayload.RELAY_VERSION && payloadLength - readCount == 1)
                    versionPayload = versionPayload.With(Relay: reader.ReadBool());

                return versionPayload;
            }
        }
Example #6
0
 public static Transaction DecodeTransaction(Stream stream, UInt256? txHash = null)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new Transaction
         (
             version: reader.Read4Bytes(),
             inputs: reader.DecodeList(() => DecodeTxInput(stream)),
             outputs: reader.DecodeList(() => DecodeTxOutput(stream)),
             lockTime: reader.Read4Bytes(),
             hash: txHash
         );
     }
 }
Example #7
0
 public static TxInput DecodeTxInput(Stream stream)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new TxInput
         (
             previousTxOutputKey: new TxOutputKey
             (
                 txHash: reader.Read32Bytes(),
                 txOutputIndex: reader.Read4Bytes()
             ),
             scriptSignature: reader.ReadVarBytes().ToImmutableArray(),
             sequence: reader.Read4Bytes()
         );
     }
 }
Example #8
0
 public static NetworkAddressWithTime DecodeNetworkAddressWithTime(Stream stream)
 {
     using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
     {
         return new NetworkAddressWithTime
         (
             Time: reader.Read4Bytes(),
             NetworkAddress: DecodeNetworkAddress(stream)
         );
     }
 }
Example #9
0
        public static Message DecodeMessage(Stream stream)
        {
            using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
            {
                var magic = reader.Read4Bytes();
                var command = reader.ReadFixedString(12);
                var payloadSize = reader.Read4Bytes();
                var payloadChecksum = reader.Read4Bytes();
                var payload = reader.ReadBytes(payloadSize.ToIntChecked()).ToImmutableArray();

                return new Message
                (
                    Magic: magic,
                    Command: command,
                    PayloadSize: payloadSize,
                    PayloadChecksum: payloadChecksum,
                    Payload: payload
                );
            }
        }
Example #10
0
        public Data.Blockchain ReadBlockchain(BlockchainKey blockchainKey)
        {
            CheckDatabaseFolder();

            var blockListBuilder = ImmutableList.CreateBuilder<ChainedBlock>();
            var utxoBuilder = ImmutableHashSet.CreateBuilder<TxOutputKey>();

            var dbPath = GetDatabasePath(blockchainKey.Guid);
            var connString = @"ServerType=1; DataSource=localhost; Database={0}; Pooling=false; User=SYSDBA; Password=NA;".Format2(dbPath);
            using (var conn = new FbConnection(connString))
            {
                conn.Open();

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT BlockHash, PreviousBlockHash, Height, TotalWork
                        FROM ChainedBlocks
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash
                        ORDER BY Height ASC";

                    cmd.Parameters.SetValue("@guid", FbDbType.Char, FbCharset.Octets, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", FbDbType.Char, FbCharset.Octets, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var blockHash = reader.GetUInt256(0);
                            var previousBlockHash = reader.GetUInt256(1);
                            var height = reader.GetInt32(2);
                            var totalWork = reader.GetBigInteger(3);

                            blockListBuilder.Add(new ChainedBlock(blockHash, previousBlockHash, height, totalWork));
                        }
                    }
                }

                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = @"
                        SELECT UtxoChunkBytes
                        FROM UtxoData
                        WHERE Guid = @guid AND RootBlockHash = @rootBlockHash";

                    cmd.Parameters.SetValue("@guid", FbDbType.Char, FbCharset.Octets, 16).Value = blockchainKey.Guid.ToByteArray();
                    cmd.Parameters.SetValue("@rootBlockHash", FbDbType.Char, FbCharset.Octets, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();

                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            var chunkBytes = reader.GetBytes(0);
                            var chunkStream = new MemoryStream(chunkBytes);
                            using (var chunkReader = new BinaryReader(chunkStream))
                            {
                                var chunkLength = chunkReader.Read4Bytes().ToIntChecked();

                                var outputs = new TxOutputKey[chunkLength];

                                for (var i = 0; i < chunkLength; i++)
                                {
                                    var prevTxHash = chunkReader.Read32Bytes();
                                    var prevTxOutputIndex = chunkReader.Read4Bytes();

                                    outputs[i] = new TxOutputKey(prevTxHash, prevTxOutputIndex);
                                }

                                utxoBuilder.UnionWith(outputs);
                            }
                        }
                    }
                }
            }

            return new Data.Blockchain(blockListBuilder.ToImmutable(), blockListBuilder.Select(x => x.BlockHash).ToImmutableHashSet(), utxoBuilder.ToImmutable());
        }
Example #11
0
        private Message WireDecodeMessage(UInt32 magic, Stream stream)
        {
            byte[] payload;
            Message message;
            using (var reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
            {
                var command = reader.ReadFixedString(12);
                var payloadSize = reader.Read4Bytes();
                var payloadChecksum = reader.Read4Bytes();

                payload = reader.ReadBytes(payloadSize.ToIntChecked());

                if (!Messaging.VerifyPayloadChecksum(payloadChecksum, payload))
                    throw new Exception(string.Format("Checksum failed for {0}", command));

                message = new Message
                (
                    Magic: magic,
                    Command: command,
                    PayloadSize: payloadSize,
                    PayloadChecksum: payloadChecksum,
                    Payload: payload.ToImmutableArray()
                );
            }

            switch (message.Command)
            {
                case "addr":
                    {
                        var addressPayload = NetworkEncoder.DecodeAddressPayload(payload.ToMemoryStream());

                        var handler = this.OnReceivedAddresses;
                        if (handler != null)
                            handler(addressPayload.NetworkAddresses);
                    }
                    break;

                case "alert":
                    {
                        var alertPayload = NetworkEncoder.DecodeAlertPayload(payload.ToMemoryStream());
                    }
                    break;

                case "block":
                    {
                        var block = NetworkEncoder.DecodeBlock(payload.ToMemoryStream());

                        var handler = this.OnBlock;
                        if (handler != null)
                            handler(block);
                    }
                    break;

                case "getblocks":
                    {
                        var getBlocksPayload = NetworkEncoder.DecodeGetBlocksPayload(payload.ToMemoryStream());

                        var handler = this.OnGetBlocks;
                        if (handler != null)
                            handler(getBlocksPayload);
                    }
                    break;

                case "getheaders":
                    {
                        var getHeadersPayload = NetworkEncoder.DecodeGetBlocksPayload(payload.ToMemoryStream());

                        var handler = this.OnGetHeaders;
                        if (handler != null)
                            handler(getHeadersPayload);
                    }
                    break;

                case "headers":
                    {
                        var headerStream = payload.ToMemoryStream();
                        using (var reader = new BinaryReader(headerStream))
                        {
                            var headerCount = reader.ReadVarInt().ToIntChecked();

                            for (var i = 0; i < headerCount; i++)
                            {
                                var blockHeader = NetworkEncoder.DecodeBlockHeader(headerStream);
                                //TODO wiki says this is a byte and a var int, which is it?
                                var txCount = reader.ReadVarInt();

                                var handler = this.OnBlockHeader;
                                if (handler != null)
                                    handler(blockHeader);
                            }
                        }
                    }
                    break;

                case "inv":
                    {
                        var invPayload = NetworkEncoder.DecodeInventoryPayload(payload.ToMemoryStream());

                        var handler = this.OnInventoryVectors;
                        if (handler != null)
                            handler(invPayload.InventoryVectors);
                    }
                    break;

                case "notfound":
                    {
                        var invPayload = NetworkEncoder.DecodeInventoryPayload(payload.ToMemoryStream());

                        var handler = this.OnNotFound;
                        if (handler != null)
                            handler(invPayload.InventoryVectors);
                    }
                    break;

                case "ping":
                    {
                        var handler = this.OnPing;
                        if (handler != null)
                            handler(payload.ToImmutableArray());
                    }
                    break;

                case "tx":
                    {
                        var tx = NetworkEncoder.DecodeTransaction(payload.ToMemoryStream());

                        var handler = this.OnTransaction;
                        if (handler != null)
                            handler(tx);
                    }
                    break;

                case "version":
                    {
                        var versionPayload = NetworkEncoder.DecodeVersionPayload(payload.ToMemoryStream(), payload.Length);
                        //Debug.WriteLine(string.Format("{0}, {1}", versionPayload.RemoteAddress.ToIPEndPoint(), this.socket.RemoteEndPoint));

                        var handler = this.OnVersion;
                        if (handler != null)
                            handler(versionPayload);
                    }
                    break;

                case "verack":
                    {
                        var handler = this.OnVersionAcknowledged;
                        if (handler != null)
                            handler();
                    }
                    break;

                default:
                    {
                        Debug.WriteLine("Unhandled incoming message: {0}".Format2(message.Command));
                    }
                    break;
            }

            //TODO
            //if (payloadStream.Position != payloadStream.Length)
            //{
            //    var exMessage = string.Format("Wrong number of bytes read for {0}, parser error: read {1} bytes from a {2} byte payload", message.Command, payloadStream.Position, payloadStream.Length);
            //    Debug.WriteLine(exMessage);
            //    throw new Exception(exMessage);
            //}

            return message;
        }
Example #12
0
        private bool ExecuteOps(ImmutableArray<byte> scriptPubKey, Transaction tx, int inputIndex, byte[] script, out Stack stack, out Stack altStack)
        {
            stack = new Stack();
            altStack = new Stack();

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

                    //logger.LogTrace("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.Read2Bytes();
                                stack.PushBytes(opReader.ReadBytes(length));
                            }
                            break;

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

                                var length = opReader.Read4Bytes();
                                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();

                                //logger.LogTrace("{0} dropped {1}", OpName(opByte), value);
                            }
                            break;

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

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

                                //logger.LogTrace("{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);

                                //                            logger.LogTrace(
                                //@"{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 = Crypto.SingleSHA256(value);
                                stack.PushBytes(hash);

                                //                            logger.LogTrace(
                                //@"{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 = Crypto.SingleRIPEMD160(Crypto.SingleSHA256(value));
                                stack.PushBytes(hash);

                                //                            logger.LogTrace(
                                //@"{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; BigIntegerBouncy x, y, r, s;
                                var result = VerifySignature(scriptPubKey, tx, sig, pubKey, inputIndex, out hashType, out txSignature, out txSignatureHash, out x, out y, out r, out s);
                                stack.PushBool(result);

                                var finishTime = DateTime.UtcNow;
                                //                            logger.LogTrace(
                                //@"{0} executed in {13} ms:
                                //tx:                 {1}
                                //inputIndex:         {2}
                                //pubKey:             {3}
                                //sig:                {4}
                                //hashType:           {5}
                                //txSignature:        {6}
                                //txSignatureHash:    {7}
                                //x:                  {8}
                                //y:                  {9}
                                //r:                  {10}
                                //s:                  {11}
                                //result:             {12}", OpName(opByte), tx.ToRawBytes(), inputIndex, pubKey, sig, hashType, txSignature, txSignatureHash, x, y, r, s, 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));
                                //logger.LogTrace("{0} loaded {1} bytes onto the stack: {2}", OpName(opByte), opByte, stack.PeekBytes());
                            }
                            // Unknown op
                            else
                            {
                                var message = string.Format("Invalid operation in tx {0} input {1}: {2} {3}", tx.Hash.ToHexNumberString(), inputIndex, new[] { opByte }.ToHexNumberString(), OpName(opByte));
                                Debug.WriteLine(message);
                                throw new Exception(message);
                            }
                            break;
                    }

                    //logger.LogTrace(new string('-', 80));
                }
            }

            // TODO verify no if/else blocks left over

            // TODO not entirely sure what default return should be
            return true;
        }