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