From 88be4c1a117cbabd4933c04c72856d76ee9b4171 Mon Sep 17 00:00:00 2001 From: area363 Date: Tue, 19 Dec 2023 21:53:09 +0900 Subject: [PATCH 1/3] update snapshot command to latest --- .../Commands/ChainCommand.cs | 267 +++++++++++------- 1 file changed, 169 insertions(+), 98 deletions(-) diff --git a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs index 11184493a..ffb5ecac7 100644 --- a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs @@ -17,12 +17,14 @@ using Libplanet.RocksDBStore; using Libplanet.Store; using Libplanet.Store.Trie; +using Microsoft.Extensions.Configuration; using Nekoyume.Action.Loader; using Nekoyume.Blockchain.Policy; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NineChronicles.Headless.Executable.IO; using NineChronicles.Headless.Executable.Store; +using Serilog; using Serilog.Core; using static NineChronicles.Headless.NCActionUtils; using CoconaUtils = Libplanet.Extensions.Cocona.Utils; @@ -380,39 +382,46 @@ public void Snapshot( string? storePath = null, [Argument("BLOCK-BEFORE", Description = "Number of blocks to truncate from the tip")] - int blockBefore = 10, + int blockBefore = 1, [Argument("SNAPSHOT-TYPE", Description = "Type of snapshot to take (full, partition, or all)")] SnapshotType snapshotType = SnapshotType.Partition) { try { - // If store changed epoch unit seconds, this will be changed too - const int blockEpochUnitSeconds = 86400; - const int txEpochUnitSeconds = 86400; + var snapshotStart = DateTimeOffset.Now; + _console.Out.WriteLine($"Create Snapshot-{snapshotType.ToString()} start."); + // If store changed epoch unit seconds, this will be changed too + const int epochUnitSeconds = 86400; string defaultStorePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "planetarium", "9c" ); - var metadataDirectory = Path.Combine(outputDirectory, "metadata"); + if (blockBefore < 0) + { + throw new CommandExitedException("The --block-before option must be greater than or equal to 0.", + -1); + } Directory.CreateDirectory(outputDirectory); Directory.CreateDirectory(Path.Combine(outputDirectory, "partition")); Directory.CreateDirectory(Path.Combine(outputDirectory, "state")); - Directory.CreateDirectory(metadataDirectory); + Directory.CreateDirectory(Path.Combine(outputDirectory, "metadata")); Directory.CreateDirectory(Path.Combine(outputDirectory, "full")); + Directory.CreateDirectory(Path.Combine(outputDirectory, "temp")); outputDirectory = string.IsNullOrEmpty(outputDirectory) ? Environment.CurrentDirectory : outputDirectory; + var metadataDirectory = Path.Combine(outputDirectory, "metadata"); + var tempDirectory = Path.Combine(outputDirectory, "temp"); int currentMetadataBlockEpoch = GetMetaDataEpoch(metadataDirectory, "BlockEpoch"); int currentMetadataTxEpoch = GetMetaDataEpoch(metadataDirectory, "TxEpoch"); int previousMetadataBlockEpoch = GetMetaDataEpoch(metadataDirectory, "PreviousBlockEpoch"); - int previousMetadataTxEpoch = GetMetaDataEpoch(metadataDirectory, "PreviousTxEpoch"); storePath = string.IsNullOrEmpty(storePath) ? defaultStorePath : storePath; if (!Directory.Exists(storePath)) @@ -428,29 +437,25 @@ public void Snapshot( var stateHashesPath = Path.Combine(storePath, "state_hashes"); var staleDirectories = - new[] { mainPath, statePath, stateRefPath, stateHashesPath }; -#pragma warning disable S3267 - foreach (var staleDirectory in staleDirectories) + new[] {mainPath, statePath, stateRefPath, stateHashesPath}; + staleDirectories.Where(Directory.Exists).ToList() + .ForEach(staleDirectory => Directory.Delete(staleDirectory, true)); + + + if (RocksDBStore.MigrateChainDBFromColumnFamilies(Path.Combine(storePath, "chain"))) { - if (Directory.Exists(staleDirectory)) - { - Directory.Delete(staleDirectory, true); - } + _console.Out.WriteLine("Successfully migrated IndexDB."); + } + else + { + _console.Out.WriteLine("Migration not required."); } -#pragma warning restore S3267 - - _console.Out.WriteLine(RocksDBStore.MigrateChainDBFromColumnFamilies(Path.Combine(storePath, "chain")) - ? "Successfully migrated IndexDB." - : "Migration not required."); - IStore store = new RocksDBStore( - storePath, - blockEpochUnitSeconds: blockEpochUnitSeconds, - txEpochUnitSeconds: txEpochUnitSeconds); + RocksDBStore store = new RocksDBStore(storePath); IKeyValueStore stateKeyValueStore = new RocksDBKeyValueStore(statesPath); IKeyValueStore newStateKeyValueStore = new RocksDBKeyValueStore(newStatesPath); TrieStateStore stateStore = new TrieStateStore(stateKeyValueStore); - TrieStateStore newStateStore = new TrieStateStore(newStateKeyValueStore); + var newStateStore = new TrieStateStore(newStateKeyValueStore); var canonicalChainId = store.GetCanonicalChainId(); if (!(canonicalChainId is { } chainId)) @@ -460,7 +465,7 @@ public void Snapshot( var genesisHash = store.IterateIndexes(chainId, 0, 1).First(); var tipHash = store.IndexBlockHash(chainId, -1) - ?? throw new CommandExitedException("The given chain seems empty.", -1); + ?? throw new CommandExitedException("The given chain seems empty.", -1); if (!(store.GetBlockIndex(tipHash) is { } tipIndex)) { throw new CommandExitedException( @@ -468,7 +473,72 @@ public void Snapshot( -1); } - Block tip = store.GetBlock(tipHash); + IStagePolicy stagePolicy = new VolatileStagePolicy(); + IBlockPolicy blockPolicy = + new BlockPolicy(); + var blockChainStates = new BlockChainStates(store, stateStore); + var actionEvaluator = new ActionEvaluator( + _ => blockPolicy.BlockAction, + stateStore, + new NCActionLoader() + ); + var originalChain = new BlockChain(blockPolicy, stagePolicy, store, stateStore, + store.GetBlock(genesisHash), blockChainStates, actionEvaluator); + var tip = store.GetBlock(tipHash); + + var potentialSnapshotTipIndex = tipIndex - blockBefore; + var potentialSnapshotTipHash = (BlockHash) store.IndexBlockHash(chainId, potentialSnapshotTipIndex)!; + var snapshotTip = store.GetBlock(potentialSnapshotTipHash); + + _console.Out.WriteLine( + "Original Store Tip: #{0}\n1. LastCommit: {1}\n2. BlockCommit in Chain: {2}\n3. BlockCommit in Store: {3}", + tip.Index, tip.LastCommit, originalChain.GetBlockCommit(tipHash), store.GetBlockCommit(tipHash)); + _console.Out.WriteLine( + "Potential Snapshot Tip: #{0}\n1. LastCommit: {1}\n2. BlockCommit in Chain: {2}\n3. BlockCommit in Store: {3}", + potentialSnapshotTipIndex, snapshotTip.LastCommit, + originalChain.GetBlockCommit(potentialSnapshotTipHash), + store.GetBlockCommit(potentialSnapshotTipHash)); + + var tipBlockCommit = store.GetBlockCommit(tipHash) ?? + originalChain.GetBlockCommit(tipHash); + var potentialSnapshotTipBlockCommit = store.GetBlockCommit(potentialSnapshotTipHash) ?? + originalChain.GetBlockCommit(potentialSnapshotTipHash); + + // Add tip and the snapshot tip's block commit to store to avoid block validation during preloading + if (potentialSnapshotTipBlockCommit != null) + { + _console.Out.WriteLine("Adding the tip(#{0}) and the snapshot tip(#{1})'s block commit to the store", + tipIndex, snapshotTip.Index); + store.PutBlockCommit(tipBlockCommit); + store.PutChainBlockCommit(chainId, tipBlockCommit); + store.PutBlockCommit(potentialSnapshotTipBlockCommit); + store.PutChainBlockCommit(chainId, potentialSnapshotTipBlockCommit); + } + else + { + _console.Out.WriteLine( + "There is no block commit associated with the potential snapshot tip: #{0}. Snapshot will automatically truncate 1 more block from the original chain tip.", + potentialSnapshotTipIndex); + blockBefore += 1; + potentialSnapshotTipBlockCommit = store + .GetBlock((BlockHash) store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit; + store.PutBlockCommit(tipBlockCommit); + store.PutChainBlockCommit(chainId, tipBlockCommit); + store.PutBlockCommit(potentialSnapshotTipBlockCommit); + store.PutChainBlockCommit(chainId, potentialSnapshotTipBlockCommit); + } + + var blockCommitBlock = store.GetBlock(tipHash); + + // Add last block commits to store from tip until --block-before + 5 for buffer + for (var i = 0; i < blockBefore + 5; i++) + { + _console.Out.WriteLine("Adding block #{0}'s block commit to the store", blockCommitBlock.Index - 1); + store.PutBlockCommit(blockCommitBlock.LastCommit); + store.PutChainBlockCommit(chainId, blockCommitBlock.LastCommit); + blockCommitBlock = store.GetBlock((BlockHash) blockCommitBlock.PreviousHash!); + } + var snapshotTipIndex = Math.Max(tipIndex - (blockBefore + 1), 0); BlockHash snapshotTipHash; @@ -487,7 +557,6 @@ public void Snapshot( } while (!stateStore.GetStateRoot(store.GetBlock(snapshotTipHash).StateRootHash).Recorded); var forkedId = Guid.NewGuid(); - Fork(chainId, forkedId, snapshotTipHash, tip, store); store.SetCanonicalChainId(forkedId); @@ -498,60 +567,69 @@ public void Snapshot( var snapshotTipDigest = store.GetBlockDigest(snapshotTipHash); var snapshotTipStateRootHash = store.GetStateRootHash(snapshotTipHash); + ImmutableHashSet> stateHashes = + ImmutableHashSet>.Empty.Add((HashDigest) snapshotTipStateRootHash!); - _console.Out.WriteLine("CopyStates Start."); - var start = DateTimeOffset.Now; - stateStore.CopyStates(ImmutableHashSet>.Empty -#pragma warning disable CS8629 - .Add((HashDigest)snapshotTipStateRootHash), newStateStore); -#pragma warning restore CS8629 - var end = DateTimeOffset.Now; - var stringData = $"CopyStates Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); - - var latestBlockEpoch = (int)(tip.Timestamp.ToUnixTimeSeconds() / blockEpochUnitSeconds); - var latestBlockWithTx = tip; - while (!latestBlockWithTx.Transactions.Any()) + // Get 2 block digest before snapshot tip using snapshot previous block hash. + BlockHash? previousBlockHash = snapshotTipDigest?.Hash; + int count = 0; + const int maxStateDepth = 2; + + while (previousBlockHash is { } pbh && + store.GetBlockDigest(pbh) is { } previousBlockDigest && + count < maxStateDepth) { - if (latestBlockWithTx.PreviousHash is { } newHash) - { - latestBlockWithTx = store.GetBlock(newHash); - } + stateHashes = stateHashes.Add(previousBlockDigest.StateRootHash); + previousBlockHash = previousBlockDigest.PreviousHash; + count++; } - var txTimeSecond = latestBlockWithTx.Transactions.Max(tx => tx.Timestamp.ToUnixTimeSeconds()); - var latestTxEpoch = (int)(txTimeSecond / txEpochUnitSeconds); + var newChain = new BlockChain(blockPolicy, stagePolicy, store, stateStore, + store.GetBlock(genesisHash), blockChainStates, actionEvaluator); + var newTip = newChain.Tip; + var latestEpoch = (int) (newTip.Timestamp.ToUnixTimeSeconds() / epochUnitSeconds); + _console.Out.WriteLine( + "Official Snapshot Tip: #{0}\n1. Timestamp: {1}\n2. Latest Epoch: {2}\n3. BlockCommit in Chain: {3}\n4. BlockCommit in Store: {4}", + newTip.Index, newTip.Timestamp.UtcDateTime, latestEpoch, newChain.GetBlockCommit(newTip.Hash), + store.GetBlockCommit(newTip.Hash)); + + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} CopyStates Start."); + var start = DateTimeOffset.Now; + stateStore.CopyStates(stateHashes, newStateStore); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} CopyStates Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); store.Dispose(); stateStore.Dispose(); + stateKeyValueStore.Dispose(); + newStateStore.Dispose(); newStateKeyValueStore.Dispose(); - _console.Out.WriteLine("Move States Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Move States Start."); start = DateTimeOffset.Now; Directory.Delete(statesPath, recursive: true); Directory.Move(newStatesPath, statesPath); - end = DateTimeOffset.Now; - stringData = $"Move States Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Move States Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min"); var partitionBaseFilename = GetPartitionBaseFileName( currentMetadataBlockEpoch, currentMetadataTxEpoch, - latestBlockEpoch); - var stateBaseFilename = $"state_latest"; + latestEpoch); + var stateBaseFilename = "state_latest"; var fullSnapshotDirectory = Path.Combine(outputDirectory, "full"); var genesisHashHex = ByteUtil.Hex(genesisHash.ToByteArray()); var snapshotTipHashHex = ByteUtil.Hex(snapshotTipHash.ToByteArray()); - var fullSnapshotFilename = $"{genesisHashHex}-snapshot-{snapshotTipHashHex}.zip"; + var fullSnapshotFilename = $"{genesisHashHex}-snapshot-{snapshotTipHashHex}-{snapshotTipIndex}.zip"; var fullSnapshotPath = Path.Combine(fullSnapshotDirectory, fullSnapshotFilename); var partitionSnapshotFilename = $"{partitionBaseFilename}.zip"; var partitionSnapshotPath = Path.Combine(outputDirectory, "partition", partitionSnapshotFilename); var stateSnapshotFilename = $"{stateBaseFilename}.zip"; var stateSnapshotPath = Path.Combine(outputDirectory, "state", stateSnapshotFilename); - string partitionDirectory = Path.Combine(Path.GetTempPath(), "snapshot"); - string stateDirectory = Path.Combine(Path.GetTempPath(), "state"); + string partitionDirectory = Path.Combine(tempDirectory, "snapshot"); + string stateDirectory = Path.Combine(tempDirectory, "state"); if (Directory.Exists(partitionDirectory)) { @@ -563,84 +641,73 @@ public void Snapshot( Directory.Delete(stateDirectory, true); } - _console.Out.WriteLine("Clean Store Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Clean Store Start."); start = DateTimeOffset.Now; CleanStore( partitionSnapshotPath, stateSnapshotPath, fullSnapshotPath, storePath); - end = DateTimeOffset.Now; - stringData = $"Clean Store Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Clean Store Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); - if (snapshotType is SnapshotType.Partition or SnapshotType.All) + if (snapshotType == SnapshotType.Partition || snapshotType == SnapshotType.All) { var storeBlockPath = Path.Combine(storePath, "block"); var storeTxPath = Path.Combine(storePath, "tx"); var partitionDirBlockPath = Path.Combine(partitionDirectory, "block"); var partitionDirTxPath = Path.Combine(partitionDirectory, "tx"); - _console.Out.WriteLine("Clone Partition Directory Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Clone Partition Directory Start."); start = DateTimeOffset.Now; CopyDirectory(storeBlockPath, partitionDirBlockPath, true); CopyDirectory(storeTxPath, partitionDirTxPath, true); - end = DateTimeOffset.Now; - stringData = $"Clone Partition Directory Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Clone Partition Directory Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); // get epoch limit for block & tx - var blockEpochLimit = GetEpochLimit( - latestBlockEpoch, + var epochLimit = GetEpochLimit( + latestEpoch, currentMetadataBlockEpoch, previousMetadataBlockEpoch); - var txEpochLimit = GetEpochLimit( - latestTxEpoch, - currentMetadataTxEpoch, - previousMetadataTxEpoch); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Clean Partition Store Start."); - _console.Out.WriteLine("Clean Partition Store Start."); - start = DateTimeOffset.Now; // clean epoch directories in block & tx - CleanEpoch(partitionDirBlockPath, blockEpochLimit); - CleanEpoch(partitionDirTxPath, txEpochLimit); - + start = DateTimeOffset.Now; + CleanEpoch(partitionDirBlockPath, epochLimit); + CleanEpoch(partitionDirTxPath, epochLimit); CleanPartitionStore(partitionDirectory); - end = DateTimeOffset.Now; - stringData = $"Clean Partition Store Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Clean Partition Store Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); - _console.Out.WriteLine("Clone State Directory Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Clone State Directory Start."); start = DateTimeOffset.Now; CopyStateStore(storePath, stateDirectory); - end = DateTimeOffset.Now; - stringData = $"Clone State Directory Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Clone State Directory Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); } - if (snapshotType is SnapshotType.Full or SnapshotType.All) + if (snapshotType == SnapshotType.Full || snapshotType == SnapshotType.All) { - _console.Out.WriteLine("Create Full ZipFile Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Create Full ZipFile Start."); start = DateTimeOffset.Now; ZipFile.CreateFromDirectory(storePath, fullSnapshotPath); - end = DateTimeOffset.Now; - stringData = $"Create Full ZipFile Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Create Full ZipFile Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); } - if (snapshotType is SnapshotType.Partition or SnapshotType.All) + if (snapshotType == SnapshotType.Partition || snapshotType == SnapshotType.All) { - _console.Out.WriteLine("Create Partition ZipFile Start."); + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Create Partition ZipFile Start."); start = DateTimeOffset.Now; ZipFile.CreateFromDirectory(partitionDirectory, partitionSnapshotPath); - end = DateTimeOffset.Now; - stringData = $"Create Partition ZipFile Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); - _console.Out.WriteLine("Create State ZipFile Start."); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Create Partition ZipFile Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); + + _console.Out.WriteLine($"Snapshot-{snapshotType.ToString()} Create State ZipFile Start."); start = DateTimeOffset.Now; ZipFile.CreateFromDirectory(stateDirectory, stateSnapshotPath); - end = DateTimeOffset.Now; - stringData = $"Create State ZipFile Done. Time Taken: {(end - start).Minutes} min"; - _console.Out.WriteLine(stringData); + _console.Out.WriteLine( + $"Snapshot-{snapshotType.ToString()} Create State ZipFile Done. Time Taken: {(DateTimeOffset.Now - start).TotalMinutes} min."); if (snapshotTipDigest is null) { @@ -653,7 +720,7 @@ public void Snapshot( currentMetadataBlockEpoch, currentMetadataTxEpoch, previousMetadataBlockEpoch, - latestBlockEpoch); + latestEpoch); var metadataFilename = $"{partitionBaseFilename}.json"; var metadataPath = Path.Combine(metadataDirectory, metadataFilename); @@ -666,10 +733,14 @@ public void Snapshot( Directory.Delete(partitionDirectory, true); Directory.Delete(stateDirectory, true); } + + _console.Out.WriteLine( + $"Create Snapshot-{snapshotType.ToString()} Complete. Time Taken: {(DateTimeOffset.Now - snapshotStart).TotalMinutes} min."); } catch (Exception ex) { - _console.Out.WriteLine(ex.Message); + _console.Error.WriteLine(ex.Message); + _console.Error.WriteLine(ex.StackTrace); } } From b6be5d89e900ad81eb845f23553a2d544e674457 Mon Sep 17 00:00:00 2001 From: area363 Date: Tue, 19 Dec 2023 21:57:16 +0900 Subject: [PATCH 2/3] fix lint --- .../Commands/ChainCommand.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs index ffb5ecac7..6d29bea5c 100644 --- a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs @@ -437,7 +437,7 @@ public void Snapshot( var stateHashesPath = Path.Combine(storePath, "state_hashes"); var staleDirectories = - new[] {mainPath, statePath, stateRefPath, stateHashesPath}; + new[] { mainPath, statePath, stateRefPath, stateHashesPath }; staleDirectories.Where(Directory.Exists).ToList() .ForEach(staleDirectory => Directory.Delete(staleDirectory, true)); @@ -487,7 +487,7 @@ public void Snapshot( var tip = store.GetBlock(tipHash); var potentialSnapshotTipIndex = tipIndex - blockBefore; - var potentialSnapshotTipHash = (BlockHash) store.IndexBlockHash(chainId, potentialSnapshotTipIndex)!; + var potentialSnapshotTipHash = (BlockHash)store.IndexBlockHash(chainId, potentialSnapshotTipIndex)!; var snapshotTip = store.GetBlock(potentialSnapshotTipHash); _console.Out.WriteLine( @@ -521,7 +521,7 @@ public void Snapshot( potentialSnapshotTipIndex); blockBefore += 1; potentialSnapshotTipBlockCommit = store - .GetBlock((BlockHash) store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit; + .GetBlock((BlockHash)store.IndexBlockHash(chainId, tip.Index - blockBefore + 1)!).LastCommit; store.PutBlockCommit(tipBlockCommit); store.PutChainBlockCommit(chainId, tipBlockCommit); store.PutBlockCommit(potentialSnapshotTipBlockCommit); @@ -536,7 +536,7 @@ public void Snapshot( _console.Out.WriteLine("Adding block #{0}'s block commit to the store", blockCommitBlock.Index - 1); store.PutBlockCommit(blockCommitBlock.LastCommit); store.PutChainBlockCommit(chainId, blockCommitBlock.LastCommit); - blockCommitBlock = store.GetBlock((BlockHash) blockCommitBlock.PreviousHash!); + blockCommitBlock = store.GetBlock((BlockHash)blockCommitBlock.PreviousHash!); } var snapshotTipIndex = Math.Max(tipIndex - (blockBefore + 1), 0); @@ -568,7 +568,7 @@ public void Snapshot( var snapshotTipDigest = store.GetBlockDigest(snapshotTipHash); var snapshotTipStateRootHash = store.GetStateRootHash(snapshotTipHash); ImmutableHashSet> stateHashes = - ImmutableHashSet>.Empty.Add((HashDigest) snapshotTipStateRootHash!); + ImmutableHashSet>.Empty.Add((HashDigest)snapshotTipStateRootHash!); // Get 2 block digest before snapshot tip using snapshot previous block hash. BlockHash? previousBlockHash = snapshotTipDigest?.Hash; @@ -587,7 +587,7 @@ public void Snapshot( var newChain = new BlockChain(blockPolicy, stagePolicy, store, stateStore, store.GetBlock(genesisHash), blockChainStates, actionEvaluator); var newTip = newChain.Tip; - var latestEpoch = (int) (newTip.Timestamp.ToUnixTimeSeconds() / epochUnitSeconds); + var latestEpoch = (int)(newTip.Timestamp.ToUnixTimeSeconds() / epochUnitSeconds); _console.Out.WriteLine( "Official Snapshot Tip: #{0}\n1. Timestamp: {1}\n2. Latest Epoch: {2}\n3. BlockCommit in Chain: {3}\n4. BlockCommit in Store: {4}", newTip.Index, newTip.Timestamp.UtcDateTime, latestEpoch, newChain.GetBlockCommit(newTip.Hash), From cf5bebc7fc8759d81e7c2ffa29ecce0715b64cf9 Mon Sep 17 00:00:00 2001 From: area363 Date: Tue, 19 Dec 2023 23:54:33 +0900 Subject: [PATCH 3/3] fix test --- .../Commands/ChainCommandTest.cs | 9 ++++---- .../Commands/ChainCommand.cs | 23 ++++++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs index a14f5c529..bc5a62233 100644 --- a/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs +++ b/NineChronicles.Headless.Executable.Tests/Commands/ChainCommandTest.cs @@ -306,7 +306,7 @@ public void Snapshot(StoreType storeType) }; Guid chainId = chain.Id; - for (var i = 0; i < 2; i++) + for (var i = 0; i < 5; i++) { chain.MakeTransaction(GenesisHelper.ValidatorKey, new ActionBase[] { action }); Block block = chain.ProposeBlock( @@ -334,9 +334,10 @@ public void Snapshot(StoreType storeType) IStore storeAfterSnapshot = storeType.CreateStore(_storePath); chainId = storeAfterSnapshot.GetCanonicalChainId() ?? new Guid(); var tipHashAfterSnapshot = storeAfterSnapshot.IndexBlockHash(chainId, -1); + var snapshotTipIndex = storeAfterSnapshot.GetBlockIndex((BlockHash)tipHashAfterSnapshot!); var expectedGenesisPartitionSnapshotPath = Path.Combine(outputDirectory, "partition", $"snapshot-{genesisBlockEpoch}-{genesisBlockEpoch}.zip"); var expectedGenesisMetadataPath = Path.Combine(outputDirectory, "metadata", $"snapshot-{genesisBlockEpoch}-{genesisBlockEpoch}.json"); - var expectedFullSnapshotPath = Path.Combine(outputDirectory, "full", $"{genesisHash}-snapshot-{tipHashAfterSnapshot}.zip"); + var expectedFullSnapshotPath = Path.Combine(outputDirectory, "full", $"{genesisHash}-snapshot-{tipHashAfterSnapshot}-{snapshotTipIndex}.zip"); storeAfterSnapshot.Dispose(); Assert.True(File.Exists(expectedGenesisPartitionSnapshotPath)); @@ -360,8 +361,8 @@ public void Snapshot(StoreType storeType) Assert.True(File.Exists(expectedPartitionSnapshotPath)); Assert.True(File.Exists(expectedStateSnapshotPath)); Assert.True(File.Exists(expectedMetadataPath)); - Assert.Equal(3, indexCountBeforeSnapshot); - Assert.Equal(2, indexCountAfterSnapshot); + Assert.Equal(6, indexCountBeforeSnapshot); + Assert.Equal(5, indexCountAfterSnapshot); Directory.Delete(outputDirectory, true); } diff --git a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs index 6d29bea5c..163aacf27 100644 --- a/NineChronicles.Headless.Executable/Commands/ChainCommand.cs +++ b/NineChronicles.Headless.Executable/Commands/ChainCommand.cs @@ -455,7 +455,7 @@ public void Snapshot( IKeyValueStore stateKeyValueStore = new RocksDBKeyValueStore(statesPath); IKeyValueStore newStateKeyValueStore = new RocksDBKeyValueStore(newStatesPath); TrieStateStore stateStore = new TrieStateStore(stateKeyValueStore); - var newStateStore = new TrieStateStore(newStateKeyValueStore); + TrieStateStore newStateStore = new TrieStateStore(newStateKeyValueStore); var canonicalChainId = store.GetCanonicalChainId(); if (!(canonicalChainId is { } chainId)) @@ -530,8 +530,9 @@ public void Snapshot( var blockCommitBlock = store.GetBlock(tipHash); - // Add last block commits to store from tip until --block-before + 5 for buffer - for (var i = 0; i < blockBefore + 5; i++) + // Add last block commits to store from tip until --block-before + 5 or tip if too short for buffer + var blockCommitRange = blockBefore + 5 < tip.Index ? blockBefore + 5 : tip.Index - 1; + for (var i = 0; i < blockCommitRange; i++) { _console.Out.WriteLine("Adding block #{0}'s block commit to the store", blockCommitBlock.Index - 1); store.PutBlockCommit(blockCommitBlock.LastCommit); @@ -570,10 +571,10 @@ public void Snapshot( ImmutableHashSet> stateHashes = ImmutableHashSet>.Empty.Add((HashDigest)snapshotTipStateRootHash!); - // Get 2 block digest before snapshot tip using snapshot previous block hash. + // Get 1 block digest before snapshot tip using snapshot previous block hash. BlockHash? previousBlockHash = snapshotTipDigest?.Hash; int count = 0; - const int maxStateDepth = 2; + const int maxStateDepth = 1; while (previousBlockHash is { } pbh && store.GetBlockDigest(pbh) is { } previousBlockDigest && @@ -888,16 +889,22 @@ private void CopyStateStore(string storePath, string stateDirectory) var storeTxBIndexPath = Path.Combine(storePath, "txbindex"); var storeStatesPath = Path.Combine(storePath, "states"); var storeChainPath = Path.Combine(storePath, "chain"); + var storeBlockCommitPath = Path.Combine(storePath, "blockcommit"); + var storeTxExecPath = Path.Combine(storePath, "txexec"); var stateDirBlockIndexPath = Path.Combine(stateDirectory, "block", "blockindex"); var stateDirTxIndexPath = Path.Combine(stateDirectory, "tx", "txindex"); var stateDirTxBIndexPath = Path.Combine(stateDirectory, "txbindex"); var stateDirStatesPath = Path.Combine(stateDirectory, "states"); var stateDirChainPath = Path.Combine(stateDirectory, "chain"); + var stateDirBlockCommitPath = Path.Combine(stateDirectory, "blockcommit"); + var stateDirTxExecPath = Path.Combine(stateDirectory, "txexec"); CopyDirectory(storeBlockIndexPath, stateDirBlockIndexPath, true); CopyDirectory(storeTxIndexPath, stateDirTxIndexPath, true); CopyDirectory(storeTxBIndexPath, stateDirTxBIndexPath, true); CopyDirectory(storeStatesPath, stateDirStatesPath, true); CopyDirectory(storeChainPath, stateDirChainPath, true); + CopyDirectory(storeBlockCommitPath, stateDirBlockCommitPath, true); + CopyDirectory(storeTxExecPath, stateDirTxExecPath, true); } private int GetMetaDataEpoch( @@ -915,7 +922,7 @@ private int GetMetaDataEpoch( } catch (InvalidOperationException e) { - Console.Error.WriteLine(e.Message); + _console.Error.WriteLine(e.Message); return 0; } } @@ -1016,6 +1023,10 @@ private void Fork( IStore store) { store.ForkBlockIndexes(src, dest, branchPointHash); + if (store.GetBlockCommit(branchPointHash) is { }) + { + store.PutChainBlockCommit(dest, store.GetBlockCommit(branchPointHash)); + } store.ForkTxNonces(src, dest); for (