internal IEnumerable <HashDigest <SHA256> > FindNextHashes( BlockLocator locator, HashDigest <SHA256>?stop = null, int count = 500) { try { _rwlock.EnterReadLock(); HashDigest <SHA256>?Next(HashDigest <SHA256> hash) { long nextIndex = Blocks[hash].Index + 1; return(Store.IndexBlockHash(Id.ToString(), nextIndex)); } HashDigest <SHA256>?tip = Store.IndexBlockHash( Id.ToString(), -1); if (tip is null) { yield break; } HashDigest <SHA256>?currentHash = FindBranchPoint(locator); while (currentHash != null && count > 0) { yield return(currentHash.Value); if (currentHash.Equals(stop) || currentHash.Equals(tip)) { break; } currentHash = Next(currentHash.Value); count--; } } finally { _rwlock.ExitReadLock(); } }
public void ExistsBlockState() { HashDigest <SHA256> randomBlockHash; do { randomBlockHash = new HashDigest <SHA256>(TestUtils.GetRandomBytes(HashDigest <SHA256> .Size)); }while (randomBlockHash.Equals(_fx.GenesisBlock.Hash)); Assert.False(_stateStore.ContainsBlockStates(randomBlockHash)); Assert.True(_stateStore.ContainsBlockStates(_fx.GenesisBlock.Hash)); }
/// <inheritdoc/> public override void ForkBlockIndexes( Guid sourceChainId, Guid destinationChainId, HashDigest <SHA256> branchPoint) { HashDigest <SHA256>?genesisHash = IterateIndexes(sourceChainId, 0, 1) .Cast <HashDigest <SHA256>?>() .FirstOrDefault(); if (genesisHash is null || branchPoint.Equals(genesisHash)) { return; } ColumnFamilyHandle cf = GetColumnFamily(_chainDb, destinationChainId); var writeBatch = new WriteBatch(); long index = 0; try { foreach (Iterator it in IterateDb(_chainDb, IndexKeyPrefix, sourceChainId)) { byte[] hashBytes = it.Value(); writeBatch.Put(it.Key(), hashBytes, cf); index += 1; if (writeBatch.Count() >= ForkWriteBatchSize) { _chainDb.Write(writeBatch); writeBatch.Dispose(); writeBatch = new WriteBatch(); } if (branchPoint.ToByteArray().SequenceEqual(hashBytes)) { break; } } } finally { _chainDb.Write(writeBatch); writeBatch.Dispose(); } _chainDb.Put( IndexCountKey, RocksDBStoreBitConverter.GetBytes(index), cf ); }
public void ExistsBlockState(bool secure) { var stateStore = MakeTrieStateStoreFixture(secure); HashDigest <SHA256> randomBlockHash; do { randomBlockHash = new HashDigest <SHA256>(TestUtils.GetRandomBytes(HashDigest <SHA256> .Size)); }while (randomBlockHash.Equals(_fx.GenesisBlock.Hash)); Assert.False(stateStore.ContainsBlockStates(randomBlockHash)); Assert.True(stateStore.ContainsBlockStates(_fx.GenesisBlock.Hash)); }
internal void Validate(DateTimeOffset currentTime) { Header.Validate(currentTime); HashDigest <SHA256>?calculatedTxHash = CalcualteTxHashes(Transactions.OrderBy(tx => tx.Id)); if (!calculatedTxHash.Equals(TxHash)) { throw new InvalidBlockTxHashException( $"Block #{Index} {Hash}'s TxHash doesn't match its content.", TxHash, calculatedTxHash ); } }
/// <inheritdoc/> public override void ForkBlockIndexes( Guid sourceChainId, Guid destinationChainId, HashDigest <SHA256> branchPoint) { LiteCollection <HashDoc> srcColl = IndexCollection(sourceChainId); LiteCollection <HashDoc> destColl = IndexCollection(destinationChainId); var genesisHash = IterateIndexes(sourceChainId, 0, 1).First(); destColl.InsertBulk(srcColl.FindAll() .TakeWhile(i => !i.Hash.Equals(branchPoint)).Skip(1)); if (!branchPoint.Equals(genesisHash)) { AppendIndex(destinationChainId, branchPoint); } }
internal void Validate(DateTimeOffset currentTime) { Header.Validate(currentTime); foreach (Transaction <T> tx in Transactions) { tx.Validate(); } if (ProtocolVersion > 0) { HashDigest <SHA256> expectedPreEvaluationHash = Hashcash.Hash(Header.SerializeForHash(includeStateRootHash: false)); if (!expectedPreEvaluationHash.Equals(PreEvaluationHash)) { string message = $"The expected pre evaluation hash of block {Hash} is " + $"{expectedPreEvaluationHash}, but its pre evaluation hash is " + $"{PreEvaluationHash}."; throw new InvalidBlockPreEvaluationHashException( PreEvaluationHash, expectedPreEvaluationHash, message); } } HashDigest <SHA256>?calculatedTxHash = CalcualteTxHashes(Transactions.OrderBy(tx => tx.Id)); if (!calculatedTxHash.Equals(TxHash)) { throw new InvalidBlockTxHashException( $"Block #{Index} {Hash}'s TxHash doesn't match its content.", TxHash, calculatedTxHash ); } }
/// <inheritdoc/> public override void ForkBlockIndexes( Guid sourceChainId, Guid destinationChainId, HashDigest <SHA256> branchPoint) { HashDigest <SHA256>?genesisHash = IterateIndexes(sourceChainId, 0, 1) .Cast <HashDigest <SHA256>?>() .FirstOrDefault(); if (genesisHash is null || branchPoint.Equals(genesisHash)) { return; } foreach (HashDigest <SHA256> hash in IterateIndexes(sourceChainId, 1, null)) { AppendIndex(destinationChainId, hash); if (hash.Equals(branchPoint)) { break; } } }
internal void Validate(DateTimeOffset currentTime) { if (ProtocolVersion < 0) { throw new InvalidBlockProtocolVersionException( ProtocolVersion, $"A block's protocol version cannot be less than zero: {ProtocolVersion}." ); } else if (ProtocolVersion > CurrentProtocolVersion) { string message = $"Unknown protocol version: {ProtocolVersion}; " + $"the highest known version is {CurrentProtocolVersion}."; throw new InvalidBlockProtocolVersionException(ProtocolVersion, message); } DateTimeOffset ts = DateTimeOffset.ParseExact( Timestamp, TimestampFormat, CultureInfo.InvariantCulture ); HashDigest <SHA256> hash = new HashDigest <SHA256>(Hash); if (currentTime + TimestampThreshold < ts) { throw new InvalidBlockTimestampException( $"The block #{Index} {hash}'s timestamp ({Timestamp}) is " + $"later than now ({currentTime}, threshold: {TimestampThreshold})." ); } if (Index < 0) { throw new InvalidBlockIndexException( $"Block #{Index} {hash}'s index must be 0 or more." ); } if (Difficulty > TotalDifficulty) { var msg = $"Block #{Index} {hash}'s difficulty ({Difficulty}) " + $"must be less than its TotalDifficulty ({TotalDifficulty})."; throw new InvalidBlockTotalDifficultyException( Difficulty, TotalDifficulty, msg ); } if (Index == 0) { if (Difficulty != 0) { throw new InvalidBlockDifficultyException( $"Difficulty must be 0 for the genesis block {hash}, " + $"but its difficulty is {Difficulty}." ); } if (TotalDifficulty != 0) { var msg = "Total difficulty must be 0 for the genesis block " + $"{hash}, but its total difficulty is " + $"{TotalDifficulty}."; throw new InvalidBlockTotalDifficultyException( Difficulty, TotalDifficulty, msg ); } if (!PreviousHash.IsEmpty) { throw new InvalidBlockPreviousHashException( $"Previous hash must be empty for the genesis block " + $"{hash}, but its value is {ByteUtil.Hex(PreviousHash)}." ); } } else { if (Difficulty < 1) { throw new InvalidBlockDifficultyException( $"Block #{Index} {hash}'s difficulty must be more than 0 " + $"(except of the genesis block), but its difficulty is {Difficulty}." ); } if (PreviousHash.IsEmpty) { throw new InvalidBlockPreviousHashException( $"Block #{Index} {hash}'s previous hash " + "must be present since it's not the genesis block." ); } } if (!new HashDigest <SHA256>(PreEvaluationHash.ToArray()).Satisfies(Difficulty)) { throw new InvalidBlockNonceException( $"Block #{Index} {hash}'s pre-evaluation hash " + $"({ByteUtil.Hex(PreEvaluationHash)}) with the nonce " + $"({ByteUtil.Hex(Nonce)}) does not satisfy its difficulty level {Difficulty}." ); } HashDigest <SHA256> calculatedHash = Hashcash.Hash(SerializeForHash()); if (!hash.Equals(calculatedHash)) { throw new InvalidBlockHashException( $"The block #{Index} {hash}'s isn't matched its content, " + $"caculcated: {calculatedHash}"); } }
protected bool Equals(JsonConvertibleHashDigest <T> other) { return(Value.Equals(other.Value)); }
internal BlockChain <T> Fork(HashDigest <SHA256> point) { if (!ContainsBlock(point)) { throw new ArgumentException( $"The block [{point}] doesn't exist.", nameof(point)); } Block <T> pointBlock = this[point]; if (!point.Equals(this[pointBlock.Index].Hash)) { throw new ArgumentException( $"The block [{point}] doesn't exist in the chain index.", nameof(point)); } var forked = new BlockChain <T>(Policy, Store, Guid.NewGuid()); Guid forkedId = forked.Id; _logger.Debug( "Trying to fork chain at {branchPoint}" + "(prevId: {prevChainId}) (forkedId: {forkedChainId})", point, Id, forkedId); try { _rwlock.EnterReadLock(); Store.ForkBlockIndexes(Id, forkedId, point); var signersToStrip = new Dictionary <Address, int>(); for ( Block <T> block = Tip; block.PreviousHash is HashDigest <SHA256> hash && !block.Hash.Equals(point); block = _blocks[hash]) { IEnumerable <(Address, int)> signers = block .Transactions .GroupBy(tx => tx.Signer) .Select(g => (g.Key, g.Count())); foreach ((Address address, int txCount) in signers) { int existingValue = 0; signersToStrip.TryGetValue(address, out existingValue); signersToStrip[address] = existingValue + txCount; } } Store.ForkStateReferences(Id, forked.Id, pointBlock); foreach (KeyValuePair <Address, long> pair in Store.ListTxNonces(Id)) { Address address = pair.Key; long existingNonce = pair.Value; long txNonce = existingNonce; int staleTxCount = 0; if (signersToStrip.TryGetValue(address, out staleTxCount)) { txNonce -= staleTxCount; } if (txNonce < 0) { throw new InvalidOperationException( $"A tx nonce for {address} in the store seems broken.\n" + $"Existing tx nonce: {existingNonce}\n" + $"# of stale transactions: {staleTxCount}\n" ); } // Note that at this point every address has tx nonce = 0 // it's merely "setting" rather than "increasing." Store.IncreaseTxNonce(forkedId, address, txNonce); } } finally { _rwlock.ExitReadLock(); } return(forked); }
public void Rebuild( [Option('v', Description = "Print more logs.")] bool verbose, [Option('s', Description = "Path to the chain store.")] string storePath, [Option('c', Description = "Optional chain ID. Default is the canonical chain ID.")] Guid?chainId = null, [Option( 't', Description = "Optional topmost block to execute last. Can be either a block " + "hash or block index. Tip by default.")] string topmost = null, [Option( new char[] { 'f', 'B' }, Description = "Optional bottommost block to execute first. Can be either a " + "block hash or block index. Genesis by default.")] string bottommost = null, [Option('b', Description = "Bypass the state root hash check.")] bool bypassStateRootHashCheck = false, [Option( 'm', Description = "Use the in-memory key-value state store and dump it to " + "the specified directory path in the end.", ValueName = "DIR")] string useMemoryKvStore = null ) { using Logger logger = Utils.ConfigureLogger(verbose); CancellationToken cancellationToken = GetInterruptSignalCancellationToken(); TextWriter stderr = Console.Error; ( BlockChain <NCAction> chain, IStore store, IKeyValueStore stateKvStore, IStateStore stateStore ) = Utils.GetBlockChain( logger, storePath, chainId, useMemoryKvStore is string p ? new MemoryKeyValueStore(p, stderr) : null ); Block <NCAction> bottom = Utils.ParseBlockOffset(chain, bottommost, 0); Block <NCAction> top = Utils.ParseBlockOffset(chain, topmost); stderr.WriteLine("It will execute all actions (tx actions & block actions)"); stderr.WriteLine( " ...from the block #{0} {1}", bottom.Index.ToString(CultureInfo.InvariantCulture).PadRight( top.Index.ToString(CultureInfo.InvariantCulture).Length), bottom.Hash); stderr.WriteLine(" ...to the block #{0} {1}.", top.Index, top.Hash); IBlockPolicy <NCAction> policy = chain.Policy; (Block <NCAction>, string)? invalidStateRootHashBlock = null; long totalBlocks = top.Index - bottom.Index + 1; long blocksExecuted = 0L; long txsExecuted = 0L; DateTimeOffset started = DateTimeOffset.Now; IEnumerable <BlockHash> blockHashes = store.IterateIndexes( chain.Id, (int)bottom.Index, (int)(top.Index - bottom.Index + 1L) ); foreach (BlockHash blockHash in blockHashes) { if (cancellationToken.IsCancellationRequested) { throw new CommandExitedException(1); } Block <NCAction> block = store.GetBlock <NCAction>(policy.GetHashAlgorithm, blockHash); var preEvalBlock = new PreEvaluationBlock <NCAction>( block, block.HashAlgorithm, block.Nonce, block.PreEvaluationHash ); stderr.WriteLine( "[{0}/{1}] Executing block #{2} {3}...", block.Index - bottom.Index + 1L, top.Index - bottom.Index + 1L, block.Index, block.Hash ); IImmutableDictionary <string, IValue> delta; HashDigest <SHA256> stateRootHash = block.Index < 1 ? preEvalBlock.DetermineStateRootHash(chain.Policy.BlockAction, stateStore, out delta) : preEvalBlock.DetermineStateRootHash( chain, StateCompleterSet <NCAction> .Reject, out delta); DateTimeOffset now = DateTimeOffset.Now; if (invalidStateRootHashBlock is null && !stateRootHash.Equals(block.StateRootHash)) { string blockDump = DumpBencodexToFile( block.MarshalBlock(), $"block_{block.Index}_{block.Hash}" ); string deltaDump = DumpBencodexToFile( new Dictionary( delta.Select(kv => new KeyValuePair <IKey, IValue>(new Text(kv.Key), kv.Value))), $"delta_{block.Index}_{block.Hash}" ); string message = $"Unexpected state root hash for block #{block.Index} {block.Hash}.\n" + $" Expected: {block.StateRootHash}\n Actual: {stateRootHash}\n" + $" Block file: {blockDump}\n Evaluated delta file: {deltaDump}\n"; if (!bypassStateRootHashCheck) { throw new CommandExitedException(message, 1); } stderr.WriteLine(message); invalidStateRootHashBlock = (block, message); } blocksExecuted++; txsExecuted += block.Transactions.Count; TimeSpan elapsed = now - started; if (blocksExecuted >= totalBlocks || block.Hash.Equals(top.Hash)) { stderr.WriteLine("Elapsed: {0:c}.", elapsed); break; } else { TimeSpan estimatedRemaining = elapsed / blocksExecuted * (totalBlocks - blocksExecuted); stderr.WriteLine( "Elapsed: {0:c}, estimated remaining: {1:c}.", elapsed, estimatedRemaining ); } } if (invalidStateRootHashBlock is { } b) { stderr.WriteLine( "Note that the state root hash check is bypassed, " + "but there was an invalid state root hash for block #{0} {1}. {2}", b.Item1.Index, b.Item1.Hash, b.Item2 ); } TimeSpan totalElapsed = DateTimeOffset.Now - started; stderr.WriteLine("Total elapsed: {0:c}", totalElapsed); stderr.WriteLine("Avg block execution time: {0:c}", totalElapsed / totalBlocks); stderr.WriteLine("Avg tx execution time: {0:c}", totalElapsed / txsExecuted); stateKvStore.Dispose(); stateStore.Dispose(); }