コード例 #1
0
 private static BlockPolicyViolationException ValidateMinerAuthorityRaw(
     Block <NCAction> block,
     IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy)
 {
     // For genesis block, any miner can mine.
     if (block.Index == 0)
     {
         return(null);
     }
     // If not an authorized mining block index, any miner can mine.
     else if (!authorizedMinersPolicy.IsTargetIndex(block.Index))
     {
         return(null);
     }
     // Otherwise, block's miner should be one of the authorized miners.
     else if (authorizedMinersPolicy.Getter(block.Index).Contains(block.Miner))
     {
         return(null);
     }
     else
     {
         return(new BlockPolicyViolationException(
                    $"The block #{block.Index} {block.Hash} is not mined by an authorized miner."));
     }
 }
コード例 #2
0
        private static BlockPolicyViolationException ValidateTxCountPerSignerPerBlockRaw(
            Block <NCAction> block,
            IVariableSubPolicy <int> maxTransactionsPerSignerPerBlockPolicy)
        {
            int maxTransactionsPerSignerPerBlock =
                maxTransactionsPerSignerPerBlockPolicy.Getter(block.Index);
            var groups = block.Transactions
                         .GroupBy(tx => tx.Signer)
                         .Where(group => group.Count() > maxTransactionsPerSignerPerBlock);
            var offendingGroup = groups.FirstOrDefault();

            if (!(offendingGroup is null))
            {
                int offendingGroupCount = offendingGroup.Count();
                return(new InvalidBlockTxCountPerSignerException(
                           $"Block #{block.Index} {block.Hash} includes too many " +
                           $"transactions from signer {offendingGroup.Key} where " +
                           $"the maximum number of transactions allowed by a single signer " +
                           $"per block is {maxTransactionsPerSignerPerBlock}: " +
                           $"{offendingGroupCount}",
                           offendingGroup.Key,
                           offendingGroupCount));
            }

            return(null);
        }
コード例 #3
0
        private static BlockPolicyViolationException ValidateTxCountPerBlockRaw(
            Block <NCAction> block,
            IVariableSubPolicy <int> minTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerBlockPolicy)
        {
            int minTransactionsPerBlock =
                minTransactionsPerBlockPolicy.Getter(block.Index);
            int maxTransactionsPerBlock =
                maxTransactionsPerBlockPolicy.Getter(block.Index);

            if (block.Transactions.Count < minTransactionsPerBlock)
            {
                return(new InvalidBlockTxCountException(
                           $"Block #{block.Index} {block.Hash} should include " +
                           $"at least {minTransactionsPerBlock} transaction(s): " +
                           $"{block.Transactions.Count}",
                           block.Transactions.Count));
            }
            else if (block.Transactions.Count > maxTransactionsPerBlock)
            {
                return(new InvalidBlockTxCountException(
                           $"Block #{block.Index} {block.Hash} should include " +
                           $"at most {maxTransactionsPerBlock} transaction(s): " +
                           $"{block.Transactions.Count}",
                           block.Transactions.Count));
            }

            return(null);
        }
コード例 #4
0
        internal static BlockPolicyViolationException ValidateNextBlockRaw(
            BlockChain <NCAction> blockChain,
            Block <NCAction> nextBlock,
            IVariableSubPolicy <HashAlgorithmType> hashAlgorithmTypePolicy,
            IVariableSubPolicy <int> maxBlockBytesPolicy,
            IVariableSubPolicy <int> minTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerSignerPerBlockPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > permissionedMinersPolicy)
        {
            if (ValidateHashAlgorithmTypeRaw(
                    nextBlock,
                    hashAlgorithmTypePolicy) is InvalidBlockHashAlgorithmTypeException ibhate)
            {
                return(ibhate);
            }
            else if (ValidateBlockBytesRaw(
                         nextBlock,
                         maxBlockBytesPolicy) is InvalidBlockBytesLengthException ibble)
            {
                return(ibble);
            }
            else if (ValidateTxCountPerBlockRaw(
                         nextBlock,
                         minTransactionsPerBlockPolicy,
                         maxTransactionsPerBlockPolicy) is InvalidBlockTxCountException ibtce)
            {
                return(ibtce);
            }
            else if (ValidateTxCountPerSignerPerBlockRaw(
                         nextBlock,
                         maxTransactionsPerSignerPerBlockPolicy) is InvalidBlockTxCountPerSignerException ibtcpse)
            {
                return(ibtcpse);
            }
            else
            {
                if (nextBlock.Index == 0)
                {
                    return(null);
                }
                else if (authorizedMinersPolicy.IsTargetIndex(nextBlock.Index))
                {
                    return(ValidateMinerAuthorityRaw(
                               nextBlock,
                               authorizedMinersPolicy));
                }
                else if (permissionedMinersPolicy.IsTargetIndex(nextBlock.Index))
                {
                    return(ValidateMinerPermissionRaw(
                               nextBlock,
                               permissionedMinersPolicy));
                }
            }

            return(null);
        }
コード例 #5
0
        public IVariableSubPolicy <T> AddRange(
            ImmutableList <SpannedSubPolicy <T> > spannedSubPolicies)
        {
            IVariableSubPolicy <T> variableSubPolicy = this;

            foreach (SpannedSubPolicy <T> spannedSubPolicy in spannedSubPolicies)
            {
                variableSubPolicy = variableSubPolicy.Add(spannedSubPolicy);
            }
            return(variableSubPolicy);
        }
コード例 #6
0
        private static InvalidBlockHashAlgorithmTypeException ValidateHashAlgorithmTypeRaw(
            Block <NCAction> block,
            IVariableSubPolicy <HashAlgorithmType> hashAlgorithmTypePolicy)
        {
            HashAlgorithmType hashAlgorithm = hashAlgorithmTypePolicy.Getter(block.Index);

            if (!block.HashAlgorithm.Equals(hashAlgorithm))
            {
                return(new InvalidBlockHashAlgorithmTypeException(
                           $"The hash algorithm type of block #{block.Index} {block.Hash} " +
                           $"does not match {hashAlgorithm}: {block.HashAlgorithm}",
                           hashAlgorithm));
            }

            return(null);
        }
コード例 #7
0
        private static InvalidBlockBytesLengthException ValidateBlockBytesRaw(
            Block <NCAction> block,
            IVariableSubPolicy <int> maxBlockBytesPolicy)
        {
            int maxBlockBytes = maxBlockBytesPolicy.Getter(block.Index);

            if (block.BytesLength > maxBlockBytes)
            {
                return(new InvalidBlockBytesLengthException(
                           $"The size of block #{block.Index} {block.Hash} is too large " +
                           $"where the maximum number of bytes allowed is {maxBlockBytes}: " +
                           $"{block.BytesLength}",
                           block.BytesLength));
            }

            return(null);
        }
コード例 #8
0
        private static InvalidBlockBytesLengthException ValidateBlockBytesRaw(
            Block <NCAction> block,
            IVariableSubPolicy <long> maxBlockBytesPolicy)
        {
            long maxBlockBytes = maxBlockBytesPolicy.Getter(block.Index);
            long blockBytes    = block.MarshalBlock().EncodingLength;

            if (blockBytes > maxBlockBytes)
            {
                return(new InvalidBlockBytesLengthException(
                           $"The size of block #{block.Index} {block.Hash} is too large " +
                           $"where the maximum number of bytes allowed is {maxBlockBytes}: " +
                           $"{blockBytes}",
                           blockBytes));
            }

            return(null);
        }
コード例 #9
0
 private static BlockPolicyViolationException ValidateMinerPermissionRaw(
     Block <NCAction> block,
     IVariableSubPolicy <ImmutableHashSet <Address> > permissionedMinersPolicy)
 {
     // If the set of permissioned miners is empty, any miner can mine.
     if (!permissionedMinersPolicy.IsTargetIndex(block.Index))
     {
         return(null);
     }
     else if (permissionedMinersPolicy.Getter(block.Index).Contains(block.Miner))
     {
         return(null);
     }
     else
     {
         return(new BlockPolicyViolationException(
                    $"Block #{block.Index} {block.Hash} is not mined by " +
                    $"a permissioned miner: {block.Miner}"));
     }
 }
コード例 #10
0
        internal static BlockPolicyViolationException ValidateNextBlockRaw(
            BlockChain <NCAction> blockChain,
            Block <NCAction> nextBlock,
            IVariableSubPolicy <int> minTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerSignerPerBlockPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > permissionedMinersPolicy)
        {
            // FIXME: Tx count validation should be done in libplanet, not here.
            // Should be removed once libplanet is updated.
            if (ValidateTxCountPerBlockRaw(
                    nextBlock,
                    minTransactionsPerBlockPolicy,
                    maxTransactionsPerBlockPolicy,
                    maxTransactionsPerSignerPerBlockPolicy) is BlockPolicyViolationException bpve)
            {
                return(bpve);
            }
            else
            {
                if (nextBlock.Index == 0)
                {
                    return(null);
                }
                else if (authorizedMinersPolicy.IsTargetIndex(nextBlock.Index))
                {
                    return(ValidateMinerAuthorityRaw(
                               nextBlock,
                               authorizedMinersPolicy));
                }
                else if (permissionedMinersPolicy.IsTargetIndex(nextBlock.Index))
                {
                    return(ValidateMinerPermissionRaw(
                               nextBlock,
                               permissionedMinersPolicy));
                }
            }

            return(null);
        }
コード例 #11
0
        private static BlockPolicyViolationException ValidateTxCountPerBlockRaw(
            Block <NCAction> block,
            IVariableSubPolicy <int> minTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerSignerPerBlockPolicy)
        {
            int minTransactionsPerBlock =
                minTransactionsPerBlockPolicy.Getter(block.Index);
            int maxTransactionsPerBlock =
                maxTransactionsPerBlockPolicy.Getter(block.Index);
            int maxTransactionsPerSignerPerBlock =
                maxTransactionsPerSignerPerBlockPolicy.Getter(block.Index);

            if (block.Transactions.Count < minTransactionsPerBlock)
            {
                return(new BlockPolicyViolationException(
                           $"Block #{block.Index} {block.Hash} should include " +
                           $"at least {minTransactionsPerBlock} transaction(s): " +
                           $"{block.Transactions.Count}"));
            }
            else if (block.Transactions.Count > maxTransactionsPerBlock)
            {
                return(new BlockPolicyViolationException(
                           $"Block #{block.Index} {block.Hash} should include " +
                           $"at most {maxTransactionsPerBlock} transaction(s): " +
                           $"{block.Transactions.Count}"));
            }
            else if (block.Transactions
                     .GroupBy(tx => tx.Signer)
                     .Any(group => group.Count() > maxTransactionsPerSignerPerBlock))
            {
                return(new BlockPolicyViolationException(
                           $"Block #{block.Index} {block.Hash} includes too many transactions " +
                           $"from a single signer where the maximum number of allowed by " +
                           $"a single signer per block is {maxTransactionsPerSignerPerBlock}."));
            }

            return(null);
        }
コード例 #12
0
        private static bool IsAllowedToMineRaw(
            Address miner,
            long index,
            IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > permissionedMinersPolicy)
        {
            // For genesis blocks, any miner is allowed to mine.
            if (index == 0)
            {
                return(true);
            }
            else if (authorizedMinersPolicy.IsTargetIndex(index))
            {
                return(authorizedMinersPolicy.Getter(index).Contains(miner));
            }
            else if (permissionedMinersPolicy.IsTargetIndex(index))
            {
                return(permissionedMinersPolicy.Getter(index).Contains(miner));
            }

            // If none of the conditions apply, any miner is allowed to mine.
            return(true);
        }
コード例 #13
0
        // FIXME: Although the intention is to use a slight variant of the algorithm provided,
        // this allows a wildly different implementation for special cases.
        internal static long GetNextBlockDifficultyRaw(
            BlockChain <NCAction> blockChain,
            TimeSpan targetBlockInterval,
            long difficultyStability,
            long minimumDifficulty,
            IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy,
            Func <BlockChain <NCAction>, long> defaultAlgorithm)
        {
            long index = blockChain.Count;
            Func <long, bool> isAuthorizedMiningIndex = authorizedMinersPolicy.IsTargetIndex;

            // FIXME: Uninstantiated blockChain can be passed as an argument.
            // Until this is fixed, it is crucial block index is checked first.
            // Authorized minor validity is only checked for certain indices.
            if (index < 0)
            {
                throw new InvalidBlockIndexException(
                          $"Value of {nameof(index)} must be non-negative: {index}");
            }
            else if (index <= 1)
            {
                return(index == 0 ? 0 : minimumDifficulty);
            }
            else if (isAuthorizedMiningIndex(index))
            {
                return(minimumDifficulty);
            }
            else
            {
                long prevIndex = !isAuthorizedMiningIndex(index - 1)
                    ? index - 1
                    : index - 2;
                long prevPrevIndex = !isAuthorizedMiningIndex(prevIndex - 1)
                    ? prevIndex - 1
                    : prevIndex - 2;

                // Arbitrary condition not strictly necessary, but already hardcoded.
                if (prevPrevIndex <= 1)
                {
                    return(minimumDifficulty);
                }
                // Blocks with index, prevIndex, and prevPrevIndex are all
                // non-authorized mining blocks.
                else if (prevPrevIndex == index - 2)
                {
                    return(defaultAlgorithm(blockChain));
                }
                // At least one of previous blocks involved is authorized mining block.
                // This can happen if two or more consecutive blocks are authorized mining blocks.
                else if (isAuthorizedMiningIndex(prevIndex) ||
                         isAuthorizedMiningIndex(prevPrevIndex))
                {
                    return(minimumDifficulty);
                }
                else
                {
                    Block <NCAction> prevBlock     = blockChain[prevIndex];
                    Block <NCAction> prevPrevBlock = blockChain[prevPrevIndex];
                    TimeSpan         prevTimeDiff  = prevBlock.Timestamp - prevPrevBlock.Timestamp;
                    const long       minimumAdjustmentMultiplier = -99;

                    long adjustmentMultiplier = Math.Max(
                        1 - ((long)prevTimeDiff.TotalMilliseconds /
                             (long)targetBlockInterval.TotalMilliseconds),
                        minimumAdjustmentMultiplier);
                    long difficultyAdjustment =
                        prevBlock.Difficulty / difficultyStability * adjustmentMultiplier;

                    long nextDifficulty = Math.Max(
                        prevBlock.Difficulty + difficultyAdjustment, minimumDifficulty);

                    return(nextDifficulty);
                }
            }
        }
コード例 #14
0
        /// <summary>
        /// Gets a <see cref="BlockPolicy"/> constructed from given parameters.
        /// </summary>
        /// <param name="minimumDifficulty">The minimum difficulty that a <see cref="Block{T}"/>
        /// can have.  This is ignored for genesis blocks.</param>
        /// <param name="minTransactionsPerBlockPolicy">Used for minimum number of transactions
        /// required per block.</param>
        /// <param name="maxTransactionsPerBlockPolicy">The maximum number of
        /// <see cref="Transaction{T}"/>s that a <see cref="Block{T}"/> can have.</param>
        /// <param name="maxTransactionsPerSignerPerBlockPolicy">The maximum number of
        /// <see cref="Transaction{T}"/>s from a single miner that a <see cref="Block{T}"/>
        /// can have.</param>
        /// <param name="authorizedMinersPolicy">Used for authorized mining.</param>
        /// <param name="permissionedMinersPolicy">Used for permissioned mining.</param>
        /// <returns>A <see cref="BlockPolicy"/> constructed from given parameters.</returns>
        internal IBlockPolicy <NCAction> GetPolicy(
            long minimumDifficulty,
            IVariableSubPolicy <int> maxBlockBytesPolicy,
            IVariableSubPolicy <int> minTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerBlockPolicy,
            IVariableSubPolicy <int> maxTransactionsPerSignerPerBlockPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > authorizedMinersPolicy,
            IVariableSubPolicy <ImmutableHashSet <Address> > permissionedMinersPolicy)
        {
#if UNITY_EDITOR
            return(new DebugPolicy());
#else
            maxBlockBytesPolicy = maxBlockBytesPolicy
                                  ?? MaxBlockBytesPolicy.Default;
            minTransactionsPerBlockPolicy = minTransactionsPerBlockPolicy
                                            ?? MinTransactionsPerBlockPolicy.Default;
            maxTransactionsPerBlockPolicy = maxTransactionsPerBlockPolicy
                                            ?? MaxTransactionsPerBlockPolicy.Default;
            maxTransactionsPerSignerPerBlockPolicy = maxTransactionsPerSignerPerBlockPolicy
                                                     ?? MaxTransactionsPerSignerPerBlockPolicy.Default;
            authorizedMinersPolicy = authorizedMinersPolicy
                                     ?? AuthorizedMinersPolicy.Default;
            permissionedMinersPolicy = permissionedMinersPolicy
                                       ?? PermissionedMinersPolicy.Default;

            // FIXME: Slight inconsistency due to pre-existing delegate.
            HashAlgorithmGetter getHashAlgorithmType =
                index => HashAlgorithmTypePolicy.Mainnet.Getter(index);

            // FIXME: Ad hoc solution to poorly defined tx validity.
            ImmutableHashSet <Address> allAuthorizedMiners =
                authorizedMinersPolicy.SpannedSubPolicies
                .Select(spannedSubPolicy => spannedSubPolicy.Value)
#pragma warning disable LAA1002
                .Aggregate(
                    authorizedMinersPolicy.DefaultValue,
                    (union, next) => union.Union(next));
#pragma warning restore LAA1002

            Func <BlockChain <NCAction>, Transaction <NCAction>, TxPolicyViolationException> validateNextBlockTx =
                (blockChain, transaction) => ValidateNextBlockTxRaw(
                    blockChain, transaction, allAuthorizedMiners);
            Func <BlockChain <NCAction>, Block <NCAction>, BlockPolicyViolationException> validateNextBlock =
                (blockChain, block) => ValidateNextBlockRaw(
                    blockChain,
                    block,
                    minTransactionsPerBlockPolicy,
                    maxTransactionsPerBlockPolicy,
                    maxTransactionsPerSignerPerBlockPolicy,
                    authorizedMinersPolicy,
                    permissionedMinersPolicy);
            Func <BlockChain <NCAction>, long> getNextBlockDifficulty = blockChain =>
                                                                        GetNextBlockDifficultyRaw(
                blockChain,
                BlockInterval,
                DifficultyStability,
                minimumDifficulty,
                authorizedMinersPolicy,
                defaultAlgorithm: chain => DifficultyAdjustment <NCAction> .BaseAlgorithm(
                    chain, BlockInterval, DifficultyStability, minimumDifficulty));
            Func <Address, long, bool> isAllowedToMine = (address, index) => IsAllowedToMineRaw(
                address,
                index,
                authorizedMinersPolicy,
                permissionedMinersPolicy);

            return(new BlockPolicy(
                       new RewardGold(),
                       blockInterval: BlockInterval,
                       difficultyStability: DifficultyStability,
                       minimumDifficulty: minimumDifficulty,
                       canonicalChainComparer: new TotalDifficultyComparer(),
                       hashAlgorithmGetter: getHashAlgorithmType,
                       validateNextBlockTx: validateNextBlockTx,
                       validateNextBlock: validateNextBlock,
                       getMaxBlockBytes: maxBlockBytesPolicy.Getter,
                       getMinTransactionsPerBlock: minTransactionsPerBlockPolicy.Getter,
                       getMaxTransactionsPerBlock: maxTransactionsPerBlockPolicy.Getter,
                       getMaxTransactionsPerSignerPerBlock: maxTransactionsPerSignerPerBlockPolicy.Getter,
                       getNextBlockDifficulty: getNextBlockDifficulty,
                       isAllowedToMine: isAllowedToMine));
#endif
        }