// IBlockTxesStorage.TryGetTransaction private void TestTryGetTransaction(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockTxesStorage = storageManager.BlockTxesStorage; // create a block var block = CreateFakeBlock(); // add block transactions blockTxesStorage.TryAddBlockTransactions(block.Hash, block.BlockTxes); // verify missing transactions BlockTx transaction; Assert.IsFalse(blockTxesStorage.TryGetTransaction(UInt256.Zero, 0, out transaction)); Assert.IsFalse(blockTxesStorage.TryGetTransaction(block.Hash, -1, out transaction)); Assert.IsFalse(blockTxesStorage.TryGetTransaction(block.Hash, block.Transactions.Length, out transaction)); // verify transactions for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { Assert.IsTrue(blockTxesStorage.TryGetTransaction(block.Hash, txIndex, out transaction)); Assert.AreEqual(block.Transactions[txIndex].Hash, transaction.Hash); Assert.AreEqual(transaction.Hash, new UInt256(SHA256Static.ComputeDoubleHash(transaction.TxBytes.ToArray()))); } } }
// IBlockTxesStorage.ReadBlockTransactions private void TestReadBlockTransactions(ITestStorageProvider provider) { using (var storageManager = provider.OpenStorageManager()) { var blockTxesStorage = storageManager.BlockTxesStorage; // create a block var expectedBlock = CreateFakeBlock(); var expectedBlockTxHashes = expectedBlock.Transactions.Select(x => x.Hash).ToList(); // add block transactions blockTxesStorage.TryAddBlockTransactions(expectedBlock.Hash, expectedBlock.BlockTxes); // retrieve block transactions IEnumerator <BlockTx> rawActualBlockTxes; Assert.IsTrue(blockTxesStorage.TryReadBlockTransactions(expectedBlock.Hash, out rawActualBlockTxes)); var actualBlockTxes = rawActualBlockTxes.UsingAsEnumerable().ToList(); var actualBlockTxHashes = actualBlockTxes.Select(x => x.Hash).ToList(); // verify all retrieved transactions match their hashes Assert.IsTrue(actualBlockTxes.All(x => x.Hash == new UInt256(SHA256Static.ComputeDoubleHash(x.TxBytes.ToArray())))); // verify retrieved block transactions match stored block transactions CollectionAssert.AreEqual(expectedBlockTxHashes, actualBlockTxHashes); } }
public void TestUInt256Sha256() { var expected = SHA256Static.ComputeDoubleHash(UInt256.ParseHex(TestData.HEX_STRING_64).ToByteArray()); var actual = new UInt256(expected).ToByteArray(); CollectionAssert.AreEqual(expected, actual); }
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 } }
public static T Pair <T>(T left, T right) where T : IMerkleTreeNode <T> { if (left.Depth != right.Depth) { throw new InvalidOperationException(); } if (!left.Pruned) { throw new ArgumentException("left"); } if (!right.Pruned) { throw new ArgumentException("right"); } var expectedIndex = left.Index + (1 << left.Depth); if (right.Index != expectedIndex) { throw new InvalidOperationException(); } var pairHashBytes = new byte[64]; left.Hash.ToByteArray(pairHashBytes, 0); right.Hash.ToByteArray(pairHashBytes, 32); var pairHash = new UInt256(SHA256Static.ComputeDoubleHash(pairHashBytes)); return(left.AsPruned(left.Index, left.Depth + 1, pairHash)); }
public static UInt256 PairHashes(UInt256 left, UInt256 right) { var bytes = new byte[64]; left.ToByteArray(bytes, 0); right.ToByteArray(bytes, 32); return(new UInt256(SHA256Static.ComputeDoubleHash(bytes))); }
public PublicKeyHashAddress(ImmutableArray <byte> publicKeyHashBytes) { this.publicKeyHashBytes = publicKeyHashBytes; var outputScript = new PayToPublicKeyHashBuilder().CreateOutputFromPublicKeyHash(publicKeyHashBytes.ToArray()); this.outputScriptHash = new UInt256(SHA256Static.ComputeHash(outputScript)); }
private UInt256 AddressToOutputScriptHash(string address) { var addressBytes = Base58Encoding.DecodeWithCheckSum(address); var publicKeyHash = addressBytes.Skip(1).ToArray(); var outputScript = new PayToPublicKeyHashBuilder().CreateOutputFromPublicKeyHash(publicKeyHash); var outputScriptHash = new UInt256(SHA256Static.ComputeHash(outputScript)); return(outputScriptHash); }
private UInt256 PublicKeyHashToOutputScriptHash(byte[] publicKeyHash) { if (publicKeyHash.Length != 20) { throw new ArgumentException("publicKeyHash"); } var outputScript = new PayToPublicKeyHashBuilder().CreateOutputFromPublicKeyHash(publicKeyHash); var outputScriptHash = new UInt256(SHA256Static.ComputeHash(outputScript)); return(outputScriptHash); }
public BlockHeader MineBlockHeader(BlockHeader blockHeader, UInt256 hashTarget) { var blockHeaderBytes = DataEncoder.EncodeBlockHeader(blockHeader); var hashTargetBytes = hashTarget.ToByteArray(); var start = 0; var finish = UInt32.MaxValue; var total = 0L; var nonceIndex = 76; var minedNonce = (UInt32?)null; var stopwatch = Stopwatch.StartNew(); Parallel.For( start, finish, () => new LocalMinerState(blockHeaderBytes), (nonceLong, loopState, localState) => { localState.total++; var nonce = (UInt32)nonceLong; var nonceBytes = Bits.GetBytes(nonce); Buffer.BlockCopy(nonceBytes, 0, localState.headerBytes, nonceIndex, 4); var headerBytes = localState.headerBytes; var hashBytes = SHA256Static.ComputeDoubleHash(headerBytes); if (BytesCompareLE(hashBytes, hashTargetBytes) < 0) { minedNonce = nonce; loopState.Stop(); } return(localState); }, localState => { Interlocked.Add(ref total, localState.total); }); stopwatch.Stop(); var hashRate = total / stopwatch.Elapsed.TotalSeconds; if (minedNonce == null) { throw new InvalidOperationException(); } var minedHeader = blockHeader.With(Nonce: minedNonce); logger.Debug($"Found block in {stopwatch.Elapsed.TotalMilliseconds:N3}ms at Nonce {minedNonce}, Hash Rate: {hashRate / 1.MILLION()} mHash/s, Total Hash Attempts: {total:N0}, Found Hash: {minedHeader.Hash}"); return(minedHeader); }
private static byte[] GetCheckSum(byte[] data) { //Contract.Requires<ArgumentNullException>(data != null); //Contract.Ensures(Contract.Result<byte[]>() != null); byte[] hash1 = SHA256Static.ComputeHash(data); byte[] hash2 = SHA256Static.ComputeHash(hash1); var result = new byte[CheckSumSizeInBytes]; Buffer.BlockCopy(hash2, 0, result, 0, result.Length); return(result); }
public static BlockHeader DecodeBlockHeader(UInt256 blockHash, byte[] buffer, ref int offset) { var initialOffset = offset; var version = DecodeUInt32(buffer, ref offset); var previousBlock = DecodeUInt256(buffer, ref offset); var merkleRoot = DecodeUInt256(buffer, ref offset); var time = DateTimeOffset.FromUnixTimeSeconds(DecodeUInt32(buffer, ref offset)); var bits = DecodeUInt32(buffer, ref offset); var nonce = DecodeUInt32(buffer, ref offset); blockHash = blockHash ?? new UInt256(SHA256Static.ComputeDoubleHash(buffer, initialOffset, 80)); return(new BlockHeader(version, previousBlock, merkleRoot, time, bits, nonce, blockHash)); }
public static UInt256 ComputeHash(IChainState chainState) { using (var sha256 = new SHA256Managed()) { // add each unspent tx to hash foreach (var unspentTx in chainState.ReadUnspentTransactions()) { var unspentTxBytes = DataEncoder.EncodeUnspentTx(unspentTx); sha256.TransformBlock(unspentTxBytes, 0, unspentTxBytes.Length, unspentTxBytes, 0); } // finalize hash sha256.TransformFinalBlock(new byte[0], 0, 0); // hash again to return double-hashed utxo committment return(new UInt256(SHA256Static.ComputeHash(sha256.Hash))); } }
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)); } }
public byte[] CreatePublicKeyScript(byte[] publicKey) { var publicKeyHash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(publicKey)); using (var publicKeyScript = new ScriptBuilder()) { publicKeyScript.WriteOp(ScriptOp.OP_DUP); publicKeyScript.WriteOp(ScriptOp.OP_HASH160); publicKeyScript.WritePushData(publicKeyHash); publicKeyScript.WriteOp(ScriptOp.OP_EQUALVERIFY); publicKeyScript.WriteOp(ScriptOp.OP_CHECKSIG); //Debug.WriteLine("Public Script: {0}".Format2(publicKeyScript.GetScript().ToHexDataString())); return(publicKeyScript.GetScript()); } }
private async Task ScanBlock(ICoreStorage coreStorage, IChainState chainState, ChainedHeader scanBlock, bool forward, CancellationToken cancelToken = default(CancellationToken)) { var replayTxes = BlockReplayer.ReplayBlock(coreStorage, chainState, scanBlock.Hash, forward, cancelToken); var txScanner = new ActionBlock <ValidatableTx>( validatableTx => { var tx = validatableTx.Transaction; var txIndex = validatableTx.Index; if (!validatableTx.IsCoinbase) { for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; var prevOutput = validatableTx.PrevTxOutputs[inputIndex]; var prevOutputScriptHash = new UInt256(SHA256Static.ComputeHash(prevOutput.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = forward ? EnumWalletEntryType.Spend : EnumWalletEntryType.UnSpend; ScanForEntry(chainPosition, entryType, (TxOutput)prevOutput, prevOutputScriptHash); } } for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; var outputScriptHash = new UInt256(SHA256Static.ComputeHash(output.ScriptPublicKey)); var chainPosition = ChainPosition.Fake(); var entryType = validatableTx.IsCoinbase ? (forward ? EnumWalletEntryType.Mine : EnumWalletEntryType.UnMine) : (forward ? EnumWalletEntryType.Receive : EnumWalletEntryType.UnReceieve); ScanForEntry(chainPosition, entryType, output, outputScriptHash); } }); replayTxes.LinkTo(txScanner, new DataflowLinkOptions { PropagateCompletion = true }); await txScanner.Completion; }
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()); } }
public static Transaction DecodeTransaction(UInt256 txHash, byte[] buffer, ref int offset) { var initialOffset = offset; // read version var version = DecodeUInt32(buffer, ref offset); // read inputs var inputs = DecodeTxInputList(buffer, ref offset); // read outputs var outputs = DecodeTxOutputList(buffer, ref offset); // read lockTime var lockTime = DecodeUInt32(buffer, ref offset); txHash = txHash ?? new UInt256(SHA256Static.ComputeDoubleHash(buffer, initialOffset, offset - initialOffset)); return(new Transaction(version, inputs, outputs, lockTime, txHash)); }
public void ReplayBlockExample() { // create example core daemon BlockProvider embeddedBlocks; IStorageManager storageManager; using (var coreDaemon = CreateExampleDaemon(out embeddedBlocks, out storageManager, maxHeight: 999)) using (embeddedBlocks) using (storageManager) { // start a chain at the genesis block to represent the processed progress var processedChain = Chain.CreateForGenesisBlock(coreDaemon.ChainParams.GenesisChainedHeader).ToBuilder(); // a dictionary of public key script hashes can be created for any addresses of interest, allowing for quick checking var scriptHashesOfInterest = new HashSet <UInt256>(); // retrieve a chainstate to replay blocks with using (var chainState = coreDaemon.GetChainState()) { // enumerate the steps needed to take the currently processed chain towards the current chainstate foreach (var pathElement in processedChain.NavigateTowards(chainState.Chain)) { // retrieve the next block to replay and whether to replay forwards, or backwards for a re-org var replayForward = pathElement.Item1 > 0; var replayBlock = pathElement.Item2; // begin replaying the transactions in the replay block // if this is a re-org, the transactions will be replayed in reverse block order var replayTxes = BlockReplayer.ReplayBlock(coreDaemon.CoreStorage, chainState, replayBlock.Hash, replayForward); // prepare the tx scanner var txScanner = new ActionBlock <ValidatableTx>( validatableTx => { // the transaction being replayed var tx = validatableTx.Transaction; // the previous tx outputs for each of the replay transaction's inputs var prevTxOutputs = validatableTx.PrevTxOutputs; // scan the replay transaction's inputs if (!validatableTx.IsCoinbase) { for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; var inputPrevTxOutput = validatableTx.PrevTxOutputs[inputIndex]; // check if the input's previous transaction output is of interest var inputPrevTxOutputPublicScriptHash = new UInt256(SHA256Static.ComputeHash(inputPrevTxOutput.ScriptPublicKey)); if (scriptHashesOfInterest.Contains(inputPrevTxOutputPublicScriptHash)) { if (replayForward) { /* An output for an address of interest is being spent. */ } else { /* An output for an address of interest is being "unspent", on re-org. */ } } } } // scan the replay transaction's outputs for (var outputIndex = 0; outputIndex < tx.Outputs.Length; outputIndex++) { var output = tx.Outputs[outputIndex]; // check if the output is of interest var outputPublicScriptHash = new UInt256(SHA256Static.ComputeHash(output.ScriptPublicKey)); if (scriptHashesOfInterest.Contains(outputPublicScriptHash)) { if (replayForward) { /* An output for an address of interest is being minted. */ } else { /* An output for an address of interest is being "unminted", on re-org. */ } } } }); // hook up and wait for the tx scanner replayTxes.LinkTo(txScanner, new DataflowLinkOptions { PropagateCompletion = true }); txScanner.Completion.Wait(); // a wallet would now commit its progress /* * walletDatabase.CurrentBlock = replayBlock.Hash; * walletDatabase.Commit(); */ // TODO: after successfully committing, a wallet would notify CoreDaemon of its current progress // TODO: CoreDaemon will use this information in order to determine how far in the current chainstate it is safe to prune // TODO: with this in place, if a wallet suffers a failure to commit it can just replay the block // TODO: wallets can also remain disconnected from CoreDaemon, and just replay blocks to catch up when they are reconnected // update the processed chain so that the next step towards the current chainstate can be taken if (replayForward) { processedChain.AddBlock(replayBlock); } else { processedChain.RemoveBlock(replayBlock); } } } logger.Info("Processed chain height: {0:N0}", processedChain.Height); } }
public static UInt256 CalculateBlockHash(UInt32 Version, UInt256 PreviousBlock, UInt256 MerkleRoot, DateTimeOffset Time, UInt32 Bits, UInt32 Nonce) { return(new UInt256(SHA256Static.ComputeDoubleHash(DataEncoder.EncodeBlockHeader(Version, PreviousBlock, MerkleRoot, Time, Bits, Nonce)))); }
public static UInt32 CalculatePayloadChecksum(byte[] payload) { return(Bits.ToUInt32(SHA256Static.ComputeDoubleHash(payload))); }
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.ReadExactly(length)); } break; case ScriptOp.OP_PUSHDATA2: { if (opReader.BaseStream.Position + 2 >= script.Length) { return(false); } var length = opReader.ReadUInt16(); stack.PushBytes(opReader.ReadExactly(length)); } break; case ScriptOp.OP_PUSHDATA4: { if (opReader.BaseStream.Position + 4 >= script.Length) { return(false); } var length = opReader.ReadUInt32(); stack.PushBytes(opReader.ReadExactly(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.ReadExactly(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); }
public byte[] CreateOutputFromPublicKey(byte[] publicKey) { var publicKeyHash = RIPEMD160Static.ComputeHash(SHA256Static.ComputeHash(publicKey)); return(CreateOutputFromPublicKeyHash(publicKeyHash)); }
public static UInt256 CalculateBlockHash(BlockHeader blockHeader) { return(new UInt256(SHA256Static.ComputeDoubleHash(DataEncoder.EncodeBlockHeader(blockHeader)))); }