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.")); } }
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); }
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); }
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); }
public IVariableSubPolicy <T> AddRange( ImmutableList <SpannedSubPolicy <T> > spannedSubPolicies) { IVariableSubPolicy <T> variableSubPolicy = this; foreach (SpannedSubPolicy <T> spannedSubPolicy in spannedSubPolicies) { variableSubPolicy = variableSubPolicy.Add(spannedSubPolicy); } return(variableSubPolicy); }
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); }
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); }
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); }
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}")); } }
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); }
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); }
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); }
// 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); } } }
/// <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 }