public static PreEvaluationBlock <T> MineNext <T>( Block <T> previousBlock, HashAlgorithmGetter hashAlgorithmGetter, IReadOnlyList <Transaction <T> > txs = null, byte[] nonce = null, long difficulty = 1, PublicKey miner = null, TimeSpan?blockInterval = null, int protocolVersion = Block <T> .CurrentProtocolVersion ) where T : IAction, new() { var content = new BlockContent <T> { Index = previousBlock.Index + 1, Difficulty = difficulty, TotalDifficulty = previousBlock.TotalDifficulty + difficulty, Miner = miner?.ToAddress() ?? previousBlock.Miner, PublicKey = protocolVersion < 2 ? null : miner ?? previousBlock.PublicKey, PreviousHash = previousBlock.Hash, Timestamp = previousBlock.Timestamp.Add(blockInterval ?? TimeSpan.FromSeconds(15)), Transactions = txs ?? Array.Empty <Transaction <T> >(), ProtocolVersion = protocolVersion, }; HashAlgorithmType hashAlgorithm = hashAlgorithmGetter(previousBlock.Index + 1); var preEval = nonce is byte[] nonceBytes ? new PreEvaluationBlock <T>(content, hashAlgorithm, new Nonce(nonceBytes)) : content.Mine(hashAlgorithm); preEval.ValidateTimestamp(); return(preEval); }
public void Evaluate() { Address address = _contents.Tx0InBlock1.Signer; var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer) 123, 0); var policy = new BlockPolicy <Arithmetic>( blockAction: blockAction, blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000), minimumDifficulty: 2, difficultyStability: 1 ); var stagePolicy = new VolatileStagePolicy <Arithmetic>(); PreEvaluationBlock <Arithmetic> preEvalGenesis = _contents.Genesis.Mine(policy.GetHashAlgorithm(0)); using (var fx = new DefaultStoreFixture()) { Block <Arithmetic> genesis = preEvalGenesis.Evaluate(_contents.GenesisKey, blockAction, fx.StateStore); AssertPreEvaluationBlocksEqual(preEvalGenesis, genesis); _output.WriteLine("#1: {0}", genesis); var blockChain = new BlockChain <Arithmetic>( policy, stagePolicy, fx.Store, fx.StateStore, genesis ); AssertBencodexEqual((Bencodex.Types.Integer) 123, blockChain.GetState(address)); HashDigest <SHA256> identicalGenesisStateRootHash = preEvalGenesis.DetermineStateRootHash(blockChain); AssertBytesEqual(genesis.StateRootHash, identicalGenesisStateRootHash); BlockContent <Arithmetic> content1 = _contents.Block1; content1.PreviousHash = genesis.Hash; content1.Difficulty = 2; content1.Transactions = new[] { _contents.Tx0InBlock1 }; PreEvaluationBlock <Arithmetic> preEval1 = content1.Mine(policy.GetHashAlgorithm(1)); Block <Arithmetic> block1 = preEval1.Evaluate(_contents.Block1Key, blockChain); AssertPreEvaluationBlocksEqual(preEval1, block1); _output.WriteLine("#1: {0}", block1); HashDigest <SHA256> identicalBlock1StateRootHash = preEval1.DetermineStateRootHash(blockChain); AssertBytesEqual(block1.StateRootHash, identicalBlock1StateRootHash); blockChain.Append(block1); AssertBencodexEqual((Bencodex.Types.Integer) 158, blockChain.GetState(address)); } }
public async Task <Block <T> > MineBlock( PrivateKey miner, DateTimeOffset timestamp, bool append, long maxBlockBytes, int maxTransactions, int maxTransactionsPerSigner, IComparer <Transaction <T> > txPriority = null, CancellationToken cancellationToken = default(CancellationToken)) { using var cts = new CancellationTokenSource(); using CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token); void WatchTip(object target, (Block <T> OldTip, Block <T> NewTip) tip) { try { cts.Cancel(); } catch (ObjectDisposedException) { // Ignore if mining was already finished. } } long index = Count; long difficulty = Policy.GetNextBlockDifficulty(this); BlockHash?prevHash = index > 0 ? Store.IndexBlockHash(Id, index - 1) : null; int sessionId = new System.Random().Next(); int processId = Process.GetCurrentProcess().Id; _logger.Debug( "{SessionId}/{ProcessId}: Starting to mine block #{Index} with " + "difficulty {Difficulty} and previous hash {PreviousHash}...", sessionId, processId, index, difficulty, prevHash); HashAlgorithmType hashAlgorithm = Policy.GetHashAlgorithm(index); var metadata = new BlockMetadata { Index = index, Difficulty = difficulty, TotalDifficulty = Tip.TotalDifficulty + difficulty, PublicKey = miner.PublicKey, PreviousHash = prevHash, Timestamp = timestamp, }; var transactionsToMine = GatherTransactionsToMine( metadata, maxBlockBytes: maxBlockBytes, maxTransactions: maxTransactions, maxTransactionsPerSigner: maxTransactionsPerSigner, txPriority: txPriority ); if (transactionsToMine.Count < Policy.GetMinTransactionsPerBlock(index)) { cts.Cancel(); throw new OperationCanceledException( $"Mining canceled due to insufficient number of gathered transactions " + $"to mine for the requirement of {Policy.GetMinTransactionsPerBlock(index)} " + $"given by the policy: {transactionsToMine.Count}"); } _logger.Verbose( "{SessionId}/{ProcessId}: Mined block #{Index} will include " + "{TxCount} transactions.", sessionId, processId, index, transactionsToMine.Count); var blockContent = new BlockContent <T>(metadata) { Transactions = transactionsToMine }; PreEvaluationBlock <T> preEval; TipChanged += WatchTip; try { preEval = await Task.Run( () => blockContent.Mine(hashAlgorithm, cancellationTokenSource.Token), cancellationTokenSource.Token ); } catch (OperationCanceledException) { if (cts.IsCancellationRequested) { throw new OperationCanceledException( "Mining canceled due to change of tip index."); } throw new OperationCanceledException(cancellationToken); } finally { TipChanged -= WatchTip; } (Block <T> block, IReadOnlyList <ActionEvaluation> actionEvaluations) = preEval.EvaluateActions(miner, this); IEnumerable <TxExecution> txExecutions = MakeTxExecutions(block, actionEvaluations); UpdateTxExecutions(txExecutions); _logger.Debug( "{SessionId}/{ProcessId}: Mined block #{Index} {Hash} " + "with difficulty {Difficulty} and previous hash {PreviousHash}.", sessionId, processId, block.Index, block.Hash, block.Difficulty, block.PreviousHash); if (append) { Append( block, evaluateActions: true, renderBlocks: true, renderActions: true, actionEvaluations: actionEvaluations); } return(block); }