예제 #1
0
        /// <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;
            }
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        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]));
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        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++;
        }
예제 #6
0
 public ShortCoinPackCollection(CoinPack pack0)
 {
     _pack0 = pack0;
     _pack1 = CoinPack.Empty;
     _pack2 = CoinPack.Empty;
     _pack3 = CoinPack.Empty;
     _count = 1;
 }
예제 #7
0
        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);
        }
예제 #8
0
        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();
                }
            }
        }
예제 #9
0
        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();
            }
        }
예제 #10
0
        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);
            }