/// <summary> /// Use journal data to recover data in layered storages. /// </summary> public void RecoverFromJournal() { int offset = 0; // extract count and first size from the very first pack var pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity)); byte count = pack.WriteCount; int size = pack.SizeInBytes; // iterate through packs in journal by exact slicing for (var i = 0; i < count; ++i) { pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity - offset)); Debug.Assert(pack.LayerIndex <= _layerMmf.Length); if (pack.LayerIndex == _layerMmf.Length) { _bottomLayer.Set(pack.SectorIndex, pack.Span); } else { pack.Span.CopyTo(GetSpan(pack.LayerIndex, pack.SectorIndex)); _layerMmf[pack.LayerIndex].FlushLastSpan(); } size = pack.SizeInBytes; offset += size; } }
/// <summary> /// Returns true if journal is coherent to corresponding layered /// storages, false otherwise. /// </summary> public bool CompareWithJournal() { int offset = 0; // extract count and first size from the very first pack var pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity)); byte count = pack.WriteCount; int size = pack.SizeInBytes; // iterate through packs in journal by exact slicing for (var i = 0; i < count; ++i) { pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity - offset)); try { var storedPack = Read(pack.LayerIndex, pack.SectorIndex); if (!pack.Span.SequenceEqual(storedPack.Span)) { return(false); } size = pack.SizeInBytes; offset += size; } catch (SozuCorruptedDataException) { return(false); } } return(true); }
private Span <byte> GetSpan(int layerIndex, uint sectorIndex) { if (layerIndex >= _layerMmf.Length) { if (_bottomLayer == null) { throw new SozuDataOverflowException("Bottom layer is not defined, can't overflow."); } if (!_bottomLayer.TryGet(sectorIndex, out var span)) { // In case where key sectorIndex doesn't exist, // we initialize a empty array as value for this key. // We can see this as lazy sector allocation (only // for the bottom layer). Span <byte> mem = stackalloc byte[DefaultBottomLayerSectorSize]; var pack = new CoinPack(mem) { SectorIndex = sectorIndex, LayerIndex = (byte)layerIndex, Version = CoinPack.CurrentVersion, }; _bottomLayer.Set(sectorIndex, mem); _bottomLayer.TryGet(sectorIndex, out span); } return(span); } return(_layerMmf[layerIndex] .GetSpan(sectorIndex * _sectorSizeInBytes[layerIndex], _sectorSizeInBytes[layerIndex])); }
/// <summary> /// Returns false if journal data integrity is broken, either color /// code is broken or writecount is broken. /// </summary> public bool CheckJournalIntegrity() { int offset = 0; // check the very first pack var pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity)); byte count = pack.WriteCount; byte lastCount = count; byte color = pack.WriteColor; int size = pack.SizeInBytes; offset += size; // check next packs for (var i = 1; i < count; ++i) { pack = new CoinPack(_journalMmf.GetSpan(offset, Constants.CoinStoreJournalCapacity - offset)); if (pack.WriteColor != color || pack.WriteCount + 1 != lastCount) { return(false); } lastCount = pack.WriteCount; size = pack.SizeInBytes; offset += size; } Debug.Assert(lastCount == 1); // all colors are the same, and write count decreased 1 each time, until it reaches 1 // we consider that journal data integrity is preserved. return(true); }
public void Add(CoinPack pack) { if (_count > Capacity) { throw new InvalidOperationException("Exceeded capacity"); } switch (_count) { case 0: _pack0 = pack; break; case 1: _pack1 = pack; break; case 2: _pack2 = pack; break; case 3: _pack3 = pack; break; } _count++; }
public ShortCoinPackCollection(CoinPack pack0) { _pack0 = pack0; _pack1 = CoinPack.Empty; _pack2 = CoinPack.Empty; _pack3 = CoinPack.Empty; _count = 1; }
public CoinPack Read(int layerIndex, uint sectorIndex) { var span = GetSpan(layerIndex, sectorIndex); var pack = new CoinPack(span); if (pack.Version == 0) { throw new SozuCorruptedDataException("Sector not initialized."); } if (pack.SectorIndex != sectorIndex || pack.LayerIndex != layerIndex) { throw new SozuCorruptedDataException("SectorIndex mismatch or LayerIndex mismatch."); } return(pack); }
public CoinPack this[int index] { get { if (index >= _count) { throw new ArgumentOutOfRangeException(nameof(index)); } switch (index) { case 0: return(_pack0); case 1: return(_pack1); case 2: return(_pack2); case 3: return(_pack3); default: throw new NotSupportedException(); } } set { if (index >= _count) { throw new ArgumentOutOfRangeException(nameof(index)); } switch (index) { case 0: _pack0 = value; break; case 1: _pack1 = value; break; case 2: _pack2 = value; break; case 3: _pack3 = value; break; default: throw new NotSupportedException(); } } }
public void Initialize() { for (var layerIndex = 0; layerIndex < _layerMmf.Length; layerIndex++) { var sectorSize = _sectorSizeInBytes[layerIndex]; var layer = _layerMmf[layerIndex]; // If last sector is already initialized, then skip the whole layer. var version = new CoinPack(layer.GetSpan(layer.FileLength - sectorSize, sectorSize)).Version; if (version == CoinPack.CurrentVersion) { continue; } if (version != 0) { throw new NotSupportedException($"Unknown CoinPack version: {version}."); } _log?.Log(LogSeverity.Info, $"Initialize {layer.FileName} over {layer.FileLength} bytes."); for (long offset = 0; offset < layer.FileLength; offset += sectorSize) { var span = layer.GetSpan(offset, sectorSize); var coinpack = new CoinPack(span); // If sector is not initialized, then initialize. if (coinpack.Version != 0) { continue; } coinpack.Version = CoinPack.CurrentVersion; coinpack.SectorIndex = (uint)(offset / sectorSize); coinpack.LayerIndex = (byte)layerIndex; } layer.Flush(); } }
public CoinChangeStatus AddProduction( ulong outpointHash, ref Outpoint outpoint, bool isCoinBase, Payload payload, BlockAlias context, ILineage lineage) { try { var sectorIndex = (uint)(outpointHash % (uint)_store.SectorCount); var sig = OutpointSig.From(outpointHash); var evt = new CoinEvent(context, CoinEventKind.Production); // Oversized payload, insert in the last layer directly. if (payload.SizeInBytes > PayloadOversizeInBytes) { // HACK: [vermorel] edge-case, is-it possible to have an oversized coinbase? return(AddOversizedProduction(sectorIndex, sig, ref outpoint, isCoinBase, payload, evt, lineage)); } // Layer 0, the only layer to have outpoint signatures. var pack0 = _store.Read(layerIndex: 0, sectorIndex); var extendedPack = CoinPack.Empty; // The coin already exists on another chain. if (pack0.TryGet(ref outpoint, out var coin, out var coinOffset)) { if (coin.HasEvent(evt)) { return(CoinChangeStatus.Success); } if (!lineage.IsAddConsistent(coin.Events, evt)) { return(CoinChangeStatus.InvalidContext); } // HACK: [vermorel] edge-case, is-it possible to have the same coinbase on multiple chains? extendedPack = CoinPack.WithExtraCoinEvent(pack0, coin, coinOffset, evt, _pool); } // A probabilistic match exists, checking deeper layers. else if (pack0.PositiveMatch(sig)) { for (var layerIndex = 1; layerIndex < _store.LayerCount; layerIndex++) { // Check deeper layers var packN = _store.Read(layerIndex: layerIndex, sectorIndex); if (packN.TryGet(ref outpoint, out coin, out coinOffset)) { if (coin.HasEvent(evt)) { return(CoinChangeStatus.Success); } if (!lineage.IsAddConsistent(coin.Events, evt)) { return(CoinChangeStatus.InvalidContext); } extendedPack = CoinPack.WithExtraCoinEvent(packN, coin, coinOffset, evt, _pool); break; } // False-positive match, adding the coin as new in layer 0. if (layerIndex == _store.LayerCount - 1) { var newCoin = new Coin(ref outpoint, isCoinBase, payload, evt, _pool); extendedPack = CoinPack.WithExtraCoin(pack0, newCoin, _pool); } } // 'for' loop above should not exit without assigned a value to 'extendedPack'. Debug.Assert(!extendedPack.IsEmpty); } else // The coin does not exist on any chain. { var newCoin = new Coin(ref outpoint, isCoinBase, payload, evt, _pool); extendedPack = CoinPack.WithExtraCoin(pack0, newCoin, _pool); } // Check of overflow var packs = extendedPack.SizeInBytes < _store.SectorSizeInBytes[extendedPack.LayerIndex] ? new ShortCoinPackCollection(extendedPack) : GetOverflow(extendedPack, lineage, _hash); // Persist the pack(s), several coin packs in case of overflow. _store.Write(packs); return(CoinChangeStatus.Success); }