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