Exemple #1
0
        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();
            }
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        /// <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
                );
        }
Exemple #4
0
        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));
        }
Exemple #5
0
        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
                          );
            }
        }
Exemple #6
0
        /// <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);
            }
        }
Exemple #7
0
        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
                          );
            }
        }
Exemple #8
0
        /// <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;
                }
            }
        }
Exemple #9
0
        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));
 }
Exemple #11
0
        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);
        }
Exemple #12
0
        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();
        }