예제 #1
0
        public BlockchainKey WriteBlockchain(Data.Blockchain blockchain)
        {
            var guid = Guid.NewGuid();
            var blockchainKey = new BlockchainKey(guid, blockchain.RootBlockHash);

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

                using (var trans = conn.BeginTransaction())
                {
                    // write out the metadata for the blockchain
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO BlockchainMetadata (Guid, RootBlockHash, TotalWork, IsComplete)
                            VALUES (@guid, @rootBlockHash, @totalWork, 0);";

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

                        cmd.ExecuteNonQuery();
                    }

                    // write out the block metadata comprising the blockchain
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO ChainedBlocks (Guid, RootBlockHash, BlockHash, PreviousBlockHash, Height, TotalWork)
                            VALUES (@guid, @rootBlockHash, @blockHash, @previousBlockHash, @height, @totalWork)";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();
                        cmd.Parameters.Add(new SqlParameter { ParameterName = "@blockHash", SqlDbType = SqlDbType.Binary, Size = 32 });
                        cmd.Parameters.Add(new SqlParameter { ParameterName = "@previousBlockHash", SqlDbType = SqlDbType.Binary, Size = 32 });
                        cmd.Parameters.Add(new SqlParameter { ParameterName = "@height", SqlDbType = SqlDbType.Int });
                        cmd.Parameters.Add(new SqlParameter { ParameterName = "@totalWork", SqlDbType = SqlDbType.Binary, Size = 64 });

                        foreach (var chainedBlock in blockchain.BlockList)
                        {
                            cmd.Parameters["@blockHash"].Value = chainedBlock.BlockHash.ToDbByteArray();
                            cmd.Parameters["@previousBlockHash"].Value = chainedBlock.PreviousBlockHash.ToDbByteArray();
                            cmd.Parameters["@height"].Value = chainedBlock.Height;
                            cmd.Parameters["@totalWork"].Value = chainedBlock.TotalWork.ToDbByteArray();

                            cmd.ExecuteNonQuery();
                        }
                    }

                    // write out the utxo
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            INSERT INTO UtxoData (Guid, RootBlockhash, UtxoChunkBytes)
                            VALUES (@guid, @rootBlockHash, @utxoChunkBytes)";

                        cmd.Parameters.SetValue("@guid", SqlDbType.Binary, 16).Value = blockchainKey.Guid.ToByteArray();
                        cmd.Parameters.SetValue("@rootBlockHash", SqlDbType.Binary, 32).Value = blockchainKey.RootBlockHash.ToDbByteArray();
                        cmd.Parameters.Add(new SqlParameter { ParameterName = "@utxoChunkBytes", SqlDbType = SqlDbType.VarBinary });

                        var chunkSize = 100000;
                        var currentOffset = 0;
                        var chunkBytes = new byte[4 + (36 * chunkSize)];

                        using (var utxoEnumerator = blockchain.Utxo.GetEnumerator())
                        {
                            // chunk outer loop
                            while (currentOffset < blockchain.Utxo.Count)
                            {
                                var chunkLength = Math.Min(chunkSize, blockchain.Utxo.Count - currentOffset);

                                var chunkStream = new MemoryStream(chunkBytes);
                                using (var chunkWriter = new BinaryWriter(chunkStream))
                                {
                                    chunkWriter.Write4Bytes((UInt32)chunkLength);

                                    // chunk inner loop
                                    for (var i = 0; i < chunkLength; i++)
                                    {
                                        // get the next output from the utxo
                                        if (!utxoEnumerator.MoveNext())
                                            throw new Exception();

                                        var output = utxoEnumerator.Current;
                                        //TODO chunkWriter.Write32Bytes(output.TxHash);
                                        //TODO chunkWriter.Write4Bytes((UInt32)output.TxOutputIndex);
                                    }

                                    cmd.Parameters["@utxoChunkBytes"].Size = chunkBytes.Length;
                                    cmd.Parameters["@utxoChunkBytes"].Value = chunkBytes;
                                }

                                // write the chunk
                                cmd.ExecuteNonQuery();

                                currentOffset += chunkLength;
                            }

                            // there should be no items left in utxo at this point
                            if (utxoEnumerator.MoveNext())
                                throw new Exception();
                        }
                    }

                    // mark write as complete
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.Transaction = trans;

                        cmd.CommandText = @"
                            UPDATE BlockchainMetadata SET IsComplete = 1
                            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();

                        cmd.ExecuteNonQuery();
                    }

                    trans.Commit();
                }
            }

            return blockchainKey;
        }
예제 #2
0
        public static byte[] EncodeBlockHeader(UInt32 Version, UInt256 PreviousBlock, UInt256 MerkleRoot, UInt32 Time, UInt32 Bits, UInt32 Nonce)
        {
            var stream = new MemoryStream();
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write4Bytes(Version);
                writer.Write32Bytes(PreviousBlock);
                writer.Write32Bytes(MerkleRoot);
                writer.Write4Bytes(Time);
                writer.Write4Bytes(Bits);
                writer.Write4Bytes(Nonce);

                return stream.ToArray();
            }
        }
예제 #3
0
        public static byte[] EncodeTransaction(UInt32 Version, ImmutableArray<TxInput> Inputs, ImmutableArray<TxOutput> Outputs, UInt32 LockTime)
        {
            var stream = new MemoryStream();
            using (var writer = new BinaryWriter(stream))
            {
                writer.Write4Bytes(Version);
                writer.WriteVarInt((UInt64)Inputs.Length);
                foreach (var input in Inputs)
                {
                    writer.Write32Bytes(input.PreviousTxOutputKey.TxHash);
                    writer.Write4Bytes(input.PreviousTxOutputKey.TxOutputIndex);
                    writer.WriteVarBytes(input.ScriptSignature.ToArray());
                    writer.Write4Bytes(input.Sequence);
                }
                writer.WriteVarInt((UInt64)Outputs.Length);
                foreach (var output in Outputs)
                {
                    writer.Write8Bytes(output.Value);
                    writer.WriteVarBytes(output.ScriptPublicKey.ToArray());
                }
                writer.Write4Bytes(LockTime);

                return stream.ToArray();
            }
        }
예제 #4
0
 public static void EncodeTxInput(Stream stream, TxInput txInput)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write32Bytes(txInput.PreviousTxOutputKey.TxHash);
         writer.Write4Bytes(txInput.PreviousTxOutputKey.TxOutputIndex);
         writer.WriteVarBytes(txInput.ScriptSignature.ToArray());
         writer.Write4Bytes(txInput.Sequence);
     }
 }
예제 #5
0
        public static void EncodeVersionPayload(Stream stream, VersionPayload versionPayload, bool withRelay)
        {
            using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
            {
                writer.Write4Bytes(versionPayload.ProtocolVersion);
                writer.Write8Bytes(versionPayload.ServicesBitfield);
                writer.Write8Bytes(versionPayload.UnixTime);
                EncodeNetworkAddress(stream, versionPayload.RemoteAddress);
                EncodeNetworkAddress(stream, versionPayload.LocalAddress);
                writer.Write8Bytes(versionPayload.Nonce);
                writer.WriteVarString(versionPayload.UserAgent);
                writer.Write4Bytes(versionPayload.StartBlockHeight);

                if (withRelay)
                    writer.WriteBool(versionPayload.Relay);
            }
        }
예제 #6
0
 public static void EncodeNetworkAddressWithTime(Stream stream, NetworkAddressWithTime networkAddress)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(networkAddress.Time);
         EncodeNetworkAddress(stream, networkAddress.NetworkAddress);
     }
 }
예제 #7
0
 public static void EncodeTransaction(Stream stream, Transaction tx)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(tx.Version);
         writer.EncodeList(tx.Inputs, input => EncodeTxInput(stream, input));
         writer.EncodeList(tx.Outputs, output => EncodeTxOutput(stream, output));
         writer.Write4Bytes(tx.LockTime);
     }
 }
예제 #8
0
 public static void EncodeMessage(Stream stream, Message message)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(message.Magic);
         writer.WriteFixedString(12, message.Command);
         writer.Write4Bytes(message.PayloadSize);
         writer.Write4Bytes(message.PayloadChecksum);
         writer.WriteBytes(message.PayloadSize.ToIntChecked(), message.Payload.ToArray());
     }
 }
예제 #9
0
 public static void EncodeInventoryVector(Stream stream, InventoryVector invVector)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(invVector.Type);
         writer.Write32Bytes(invVector.Hash);
     }
 }
예제 #10
0
 public static void EncodeGetBlocksPayload(Stream stream, GetBlocksPayload getBlocksPayload)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(getBlocksPayload.Version);
         writer.EncodeList(getBlocksPayload.BlockLocatorHashes, locatorHash => writer.Write32Bytes(locatorHash));
         writer.Write32Bytes(getBlocksPayload.HashStop);
     }
 }
예제 #11
0
 public static void EncodeBlockHeader(Stream stream, BlockHeader blockHeader)
 {
     using (var writer = new BinaryWriter(stream, Encoding.ASCII, leaveOpen: true))
     {
         writer.Write4Bytes(blockHeader.Version);
         writer.Write32Bytes(blockHeader.PreviousBlock);
         writer.Write32Bytes(blockHeader.MerkleRoot);
         writer.Write4Bytes(blockHeader.Time);
         writer.Write4Bytes(blockHeader.Bits);
         writer.Write4Bytes(blockHeader.Nonce);
     }
 }
예제 #12
0
        public byte[] TxSignature(ImmutableArray<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 = new 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 : empty);
                newInputs[i] = 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.ToImmutableArray());

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

                return stream.ToArray();
            }
        }