예제 #1
0
        public AddBlockResult Insert(Block block)
        {
            if (!CanAcceptNewBlocks)
            {
                return(AddBlockResult.CannotAccept);
            }

            if (block.Number == 0)
            {
                throw new InvalidOperationException("Genesis block should not be inserted.");
            }

            using (MemoryStream stream = Rlp.BorrowStream())
            {
                Rlp.Encode(stream, block);
                byte[] newRlp = stream.ToArray();

                _blockDb.Set(block.Hash, newRlp);
            }

            long expectedNumber = (LowestInsertedBody?.Number - 1 ?? LongConverter.FromString(_syncConfig.PivotNumber ?? "0"));

            if (block.Number != expectedNumber)
            {
                throw new InvalidOperationException($"Trying to insert out of order block {block.Number} when expected number was {expectedNumber}");
            }

            if (block.Number < (LowestInsertedBody?.Number ?? long.MaxValue))
            {
                LowestInsertedBody = block;
            }

            return(AddBlockResult.Added);
        }
 public void Can_do_roundtrip_null_memory_stream()
 {
     using (MemoryStream stream = Rlp.BorrowStream())
     {
         Rlp.Encode(stream, (Block)null);
         Block decoded = Rlp.Decode <Block>(stream.ToArray());
         Assert.IsNull(decoded);
     }
 }
예제 #3
0
 private static Keccak GetTruncatedHash(BlockHeader header)
 {
     using (MemoryStream stream = Rlp.BorrowStream())
     {
         Rlp.Encode(stream, header, RlpBehaviors.ForSealing);
         Keccak headerHashed = Keccak.Compute(stream.ToArray()); // sic! Keccak here not Keccak512
         return(headerHashed);
     }
 }
예제 #4
0
 public static Keccak CalculateHash(BlockHeader header)
 {
     using (MemoryStream stream = Rlp.BorrowStream())
     {
         Rlp.Encode(stream, header);
         byte[] buffer = _rlpBuffer.Value;
         stream.Seek(0, SeekOrigin.Begin);
         stream.Read(buffer, 0, (int)stream.Length);
         Keccak newOne = Keccak.Compute(buffer.AsSpan().Slice(0, (int)stream.Length));
         return(newOne);
     }
 }
예제 #5
0
        public AddBlockResult Insert(BlockHeader header)
        {
            if (!CanAcceptNewBlocks)
            {
                return(AddBlockResult.CannotAccept);
            }

            if (header.Number == 0)
            {
                throw new InvalidOperationException("Genesis block should not be inserted.");
            }

            // validate hash here
            using (MemoryStream stream = Rlp.BorrowStream())
            {
                Rlp.Encode(stream, header);
                byte[] newRlp = stream.ToArray();

                _headerDb.Set(header.Hash, newRlp);
            }

            BlockInfo blockInfo = new BlockInfo(header.Hash, header.TotalDifficulty ?? 0);

            try
            {
                _blockInfoLock.EnterWriteLock();
                ChainLevelInfo chainLevel = new ChainLevelInfo(false, new[] { blockInfo });
                PersistLevel(header.Number, chainLevel);
            }
            finally
            {
                _blockInfoLock.ExitWriteLock();
            }

            if (header.Number < (LowestInsertedHeader?.Number ?? long.MaxValue))
            {
                LowestInsertedHeader = header;
            }

            if (header.Number > BestKnownNumber)
            {
                BestKnownNumber = header.Number;
            }

            return(AddBlockResult.Added);
        }
예제 #6
0
        public byte[] Serialize(BlockHeadersMessage message)
        {
            int contentLength = 0;

            for (int i = 0; i < message.BlockHeaders.Length; i++)
            {
                contentLength += _headerDecoder.GetLength(message.BlockHeaders[i], RlpBehaviors.None);
            }

            using (MemoryStream stream = Rlp.BorrowStream())
            {
                Rlp.StartSequence(stream, contentLength);
                for (int i = 0; i < message.BlockHeaders.Length; i++)
                {
                    _headerDecoder.Encode(stream, message.BlockHeaders[i]);
                }

                return(stream.ToArray());
            }
        }
예제 #7
0
        private AddBlockResult Suggest(Block block, BlockHeader header)
        {
#if DEBUG
            /* this is just to make sure that we do not fall into this trap when creating tests */
            if (header.StateRoot == null && !header.IsGenesis)
            {
                throw new InvalidDataException($"State root is null in {header.ToString(BlockHeader.Format.Short)}");
            }
#endif

            if (!CanAcceptNewBlocks)
            {
                return(AddBlockResult.CannotAccept);
            }

            if (_invalidBlocks.ContainsKey(header.Number) && _invalidBlocks[header.Number].Contains(header.Hash))
            {
                return(AddBlockResult.InvalidBlock);
            }

            if (header.Number == 0)
            {
                if (BestSuggested != null)
                {
                    throw new InvalidOperationException("Genesis block should be added only once");
                }
            }
            else if (IsKnownBlock(header.Number, header.Hash))
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Block {header.Hash} already known.");
                }

                return(AddBlockResult.AlreadyKnown);
            }
            else if (!IsKnownBlock(header.Number - 1, header.ParentHash))
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Could not find parent ({header.ParentHash}) of block {header.Hash}");
                }

                return(AddBlockResult.UnknownParent);
            }

            SetTotalDifficulty(header);
            if (block != null)
            {
                using (MemoryStream stream = Rlp.BorrowStream())
                {
                    Rlp.Encode(stream, block);
                    byte[] newRlp = stream.ToArray();
                    _blockDb.Set(block.Hash, newRlp);
                }
            }

            using (MemoryStream stream = Rlp.BorrowStream())
            {
                Rlp.Encode(stream, header);
                byte[] newRlp = stream.ToArray();
                _headerDb.Set(header.Hash, newRlp);
            }

            BlockInfo blockInfo = new BlockInfo(header.Hash, header.TotalDifficulty ?? 0);

            try
            {
                _blockInfoLock.EnterWriteLock();
                UpdateOrCreateLevel(header.Number, blockInfo);
            }
            finally
            {
                _blockInfoLock.ExitWriteLock();
            }


            if (header.IsGenesis || header.TotalDifficulty > (BestSuggested?.TotalDifficulty ?? 0))
            {
                BestSuggested = header;
                if (block != null)
                {
                    BestSuggestedFullBlock = block.Header;
                    NewBestSuggestedBlock?.Invoke(this, new BlockEventArgs(block));
                }
            }

            return(AddBlockResult.Added);
        }
예제 #8
0
        public AddBlockResult SuggestBlock(Block block)
        {
#if DEBUG
            /* this is just to make sure that we do not fall into this trap when creating tests */
            if (block.StateRoot == null && !block.IsGenesis)
            {
                throw new InvalidDataException($"State root is null in {block.ToString(Block.Format.Short)}");
            }
#endif

            if (!CanAcceptNewBlocks)
            {
                return(AddBlockResult.CannotAccept);
            }

            if (_invalidBlocks.ContainsKey(block.Number) && _invalidBlocks[block.Number].Contains(block.Hash))
            {
                return(AddBlockResult.InvalidBlock);
            }

            if (block.Number == 0)
            {
                if (BestSuggested != null)
                {
                    throw new InvalidOperationException("Genesis block should be added only once"); // TODO: make sure it cannot happen
                }
            }
            else if (IsKnownBlock(block.Number, block.Hash))
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Block {block.Hash} already known.");
                }

                return(AddBlockResult.AlreadyKnown);
            }
            else if (!IsKnownBlock(block.Number - 1, block.Header.ParentHash))
            {
                if (_logger.IsTrace)
                {
                    _logger.Trace($"Could not find parent ({block.Header.ParentHash}) of block {block.Hash}");
                }

                return(AddBlockResult.UnknownParent);
            }

            using (MemoryStream stream = Rlp.BorrowStream())
            {
                Rlp.Encode(stream, block);
                byte[] newRlp = stream.ToArray();
                _blockDb.Set(block.Hash, newRlp);
            }

            // TODO: when reviewing the entire data chain need to look at the transactional storing of level and block
            SetTotalDifficulty(block);
            SetTotalTransactions(block);
            BlockInfo blockInfo = new BlockInfo(block.Hash, block.TotalDifficulty.Value, block.TotalTransactions.Value);

            try
            {
                _blockInfoLock.EnterWriteLock();
                UpdateOrCreateLevel(block.Number, blockInfo);
            }
            finally
            {
                _blockInfoLock.ExitWriteLock();
            }


            if (block.IsGenesis || block.TotalDifficulty > (BestSuggested?.TotalDifficulty ?? 0))
            {
                BestSuggested = block.Header;
                NewBestSuggestedBlock?.Invoke(this, new BlockEventArgs(block));
            }

            return(AddBlockResult.Added);
        }