예제 #1
0
        public NineChroniclesNodeService(
            LibplanetNodeServiceProperties <NineChroniclesActionType> properties,
            RpcNodeServiceProperties?rpcNodeServiceProperties,
            Progress <PreloadState> preloadProgress = null,
            bool ignoreBootstrapFailure             = false,
            bool strictRendering = false,
            bool isDev           = false,
            int blockInterval    = 10000,
            int reorgInterval    = 0
            )
        {
            Properties    = properties;
            RpcProperties = rpcNodeServiceProperties;

            try
            {
                Libplanet.Crypto.CryptoConfig.CryptoBackend = new Secp256K1CryptoBackend <SHA256>();
                Log.Debug("Secp256K1CryptoBackend initialized.");
            }
            catch (Exception e)
            {
                Log.Error("Secp256K1CryptoBackend initialize failed. Use default backend. {e}", e);
            }

            var blockPolicySource = new BlockPolicySource(Log.Logger, LogEventLevel.Debug);
            // BlockPolicy shared through Lib9c.
            IBlockPolicy <PolymorphicAction <ActionBase> > blockPolicy = null;
            // Policies for dev mode.
            IBlockPolicy <PolymorphicAction <ActionBase> > easyPolicy = null;
            IBlockPolicy <PolymorphicAction <ActionBase> > hardPolicy = null;

            if (isDev)
            {
                easyPolicy = new ReorgPolicy(new RewardGold(), 1);
                hardPolicy = new ReorgPolicy(new RewardGold(), 2);
            }
            else
            {
                blockPolicy = blockPolicySource.GetPolicy(properties.MinimumDifficulty, properties.MaximumTransactions);
            }

            BlockRenderer      = blockPolicySource.BlockRenderer;
            ActionRenderer     = blockPolicySource.ActionRenderer;
            ExceptionRenderer  = new ExceptionRenderer();
            NodeStatusRenderer = new NodeStatusRenderer();
            var renderers      = new List <IRenderer <NineChroniclesActionType> >();
            var strictRenderer = new StrictRenderer(onError: exc =>
                                                    ExceptionRenderer.RenderException(
                                                        RPCException.InvalidRenderException,
                                                        exc.Message.Split("\n")[0]
                                                        )
                                                    );

            if (Properties.Render)
            {
                renderers.Add(blockPolicySource.BlockRenderer);
                renderers.Add(blockPolicySource.LoggedActionRenderer);
            }
            else
            {
                renderers.Add(blockPolicySource.LoggedBlockRenderer);
            }

            if (strictRendering)
            {
                Log.Debug(
                    $"Strict rendering is on. Add {nameof(StrictRenderer)}.");
                renderers.Add(strictRenderer);
            }

            async Task minerLoopAction(
                BlockChain <NineChroniclesActionType> chain,
                Swarm <NineChroniclesActionType> swarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new Miner(chain, swarm, privateKey);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        if (swarm.Running)
                        {
                            Log.Debug("Start mining.");
                            await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken);
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            async Task devMinerLoopAction(
                Swarm <NineChroniclesActionType> mainSwarm,
                Swarm <NineChroniclesActionType> subSwarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        if (mainSwarm.Running)
                        {
                            Log.Debug("Start mining.");
                            await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken);

                            await Task.Delay(blockInterval, cancellationToken);
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            if (isDev)
            {
                NodeService = new DevLibplanetNodeService <NineChroniclesActionType>(
                    Properties,
                    easyPolicy,
                    hardPolicy,
                    renderers,
                    devMinerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); },
                    ignoreBootstrapFailure
                    );
            }
            else
            {
                NodeService = new LibplanetNodeService <NineChroniclesActionType>(
                    Properties,
                    blockPolicy,
                    renderers,
                    minerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted =>
                {
                    NodeStatusRenderer.PreloadStatus(isPreloadStarted);
                },
                    ignoreBootstrapFailure
                    );
            }

            strictRenderer.BlockChain = NodeService?.BlockChain ?? throw new Exception("BlockChain is null.");
            if (NodeService?.BlockChain?.GetState(AuthorizedMinersState.Address) is Dictionary ams &&
                blockPolicy is BlockPolicy bp)
            {
                bp.AuthorizedMinersState = new AuthorizedMinersState(ams);
            }
        }
        public NineChroniclesNodeService(
            PrivateKey?minerPrivateKey,
            LibplanetNodeServiceProperties <NCAction> properties,
            Progress <PreloadState>?preloadProgress = null,
            bool ignoreBootstrapFailure             = false,
            bool ignorePreloadFailure = false,
            bool strictRendering      = false,
            bool authorizedMiner      = false,
            bool isDev          = false,
            int blockInterval   = 10000,
            int reorgInterval   = 0,
            TimeSpan txLifeTime = default,
            int minerCount      = 1
            )
        {
            MinerPrivateKey = minerPrivateKey;
            Properties      = properties;

            LogEventLevel logLevel          = LogEventLevel.Debug;
            var           blockPolicySource = new BlockPolicySource(Log.Logger, logLevel);
            // BlockPolicy shared through Lib9c.
            IBlockPolicy <NCAction>?blockPolicy = null;
            // Policies for dev mode.
            IBlockPolicy <NCAction>?easyPolicy  = null;
            IBlockPolicy <NCAction>?hardPolicy  = null;
            IStagePolicy <NCAction> stagePolicy =
                txLifeTime == default
                    ? new VolatileStagePolicy <NCAction>()
                    : new VolatileStagePolicy <NCAction>(txLifeTime);

            if (isDev)
            {
                easyPolicy = new ReorgPolicy(new RewardGold(), 1);
                hardPolicy = new ReorgPolicy(new RewardGold(), 2);
            }
            else
            {
                blockPolicy = blockPolicySource.GetPolicy(properties.MinimumDifficulty, properties.MaximumTransactions);
            }

            BlockRenderer      = blockPolicySource.BlockRenderer;
            ActionRenderer     = blockPolicySource.ActionRenderer;
            ExceptionRenderer  = new ExceptionRenderer();
            NodeStatusRenderer = new NodeStatusRenderer();
            var renderers      = new List <IRenderer <NCAction> >();
            var strictRenderer = new StrictRenderer(onError: exc =>
                                                    ExceptionRenderer.RenderException(
                                                        RPCException.InvalidRenderException,
                                                        exc.Message.Split("\n")[0]
                                                        )
                                                    );

            if (Properties.Render)
            {
                renderers.Add(blockPolicySource.BlockRenderer);
                renderers.Add(blockPolicySource.LoggedActionRenderer);
            }
            else if (Properties.LogActionRenders)
            {
                renderers.Add(blockPolicySource.BlockRenderer);
                // The following "nullRenderer" does nothing.  It's just for filling
                // the LoggedActionRenderer<T>() constructor's parameter:
                IActionRenderer <NCAction> nullRenderer =
                    new AnonymousActionRenderer <NCAction>();
                renderers.Add(
                    new LoggedActionRenderer <NCAction>(
                        nullRenderer,
                        Log.Logger,
                        logLevel
                        )
                    );
            }
            else
            {
                renderers.Add(blockPolicySource.LoggedBlockRenderer);
            }

            if (strictRendering)
            {
                Log.Debug(
                    $"Strict rendering is on. Add {nameof(StrictRenderer)}.");
                renderers.Add(strictRenderer);
            }

            async Task minerLoopAction(
                BlockChain <NCAction> chain,
                Swarm <NCAction> swarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new Miner(chain, swarm, privateKey, authorizedMiner);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        long nextBlockIndex = chain.Tip.Index + 1;
                        bool authBlock      = blockPolicy is BlockPolicy bp
                                              // Copied from https://git.io/JLxNd
                                              && nextBlockIndex > 0 &&
                                              nextBlockIndex <= bp.AuthorizedMinersState?.ValidUntil &&
                                              nextBlockIndex % bp.AuthorizedMinersState?.Interval == 0;
                        if (swarm.Running && ((authorizedMiner && authBlock) || (!authorizedMiner && !authBlock)))
                        {
                            Log.Debug("Start mining.");

                            IEnumerable <Task <Block <NCAction> > > miners = Enumerable
                                                                             .Range(0, minerCount)
                                                                             .Select(_ => miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken));
                            await Task.WhenAll(miners);
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            async Task devMinerLoopAction(
                Swarm <NCAction> mainSwarm,
                Swarm <NCAction> subSwarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        if (mainSwarm.Running)
                        {
                            Log.Debug("Start mining.");
                            await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken);

                            await Task.Delay(blockInterval, cancellationToken);
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            if (isDev)
            {
                NodeService = new DevLibplanetNodeService <NCAction>(
                    Properties,
                    easyPolicy,
                    hardPolicy,
                    stagePolicy,
                    renderers,
                    devMinerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); },
                    ignoreBootstrapFailure
                    );
            }
            else
            {
                NodeService = new LibplanetNodeService <NCAction>(
                    Properties,
                    blockPolicy,
                    stagePolicy,
                    renderers,
                    minerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted =>
                {
                    NodeStatusRenderer.PreloadStatus(isPreloadStarted);
                },
                    ignoreBootstrapFailure,
                    ignorePreloadFailure
                    );
            }

            strictRenderer.BlockChain = NodeService.BlockChain ?? throw new Exception("BlockChain is null.");
            if (NodeService.BlockChain?.GetState(AuthorizedMinersState.Address) is Dictionary ams &&
                blockPolicy is BlockPolicy bp)
            {
                bp.AuthorizedMinersState = new AuthorizedMinersState(ams);
            }

            if (authorizedMiner && blockPolicy is BlockPolicy {
                AuthorizedMinersState : null
            })
        public NineChroniclesNodeService(
            PrivateKey?minerPrivateKey,
            LibplanetNodeServiceProperties <NCAction> properties,
            IBlockPolicy <NCAction> blockPolicy,
            NetworkType networkType,
            Progress <PreloadState>?preloadProgress = null,
            bool ignoreBootstrapFailure             = false,
            bool ignorePreloadFailure = false,
            bool strictRendering      = false,
            bool authorizedMiner      = false,
            bool isDev           = false,
            int blockInterval    = 10000,
            int reorgInterval    = 0,
            TimeSpan txLifeTime  = default,
            int minerCount       = 1,
            int txQuotaPerSigner = 10
            )
        {
            MinerPrivateKey = minerPrivateKey;
            Properties      = properties;

            LogEventLevel logLevel          = LogEventLevel.Debug;
            var           blockPolicySource = new BlockPolicySource(Log.Logger, logLevel);
            // Policies for dev mode.
            IBlockPolicy <NCAction>?easyPolicy  = null;
            IBlockPolicy <NCAction>?hardPolicy  = null;
            IStagePolicy <NCAction> stagePolicy = new StagePolicy(txLifeTime, txQuotaPerSigner);

            if (isDev)
            {
                easyPolicy = new ReorgPolicy(new RewardGold(), 1);
                hardPolicy = new ReorgPolicy(new RewardGold(), 2);
            }

            BlockRenderer      = blockPolicySource.BlockRenderer;
            ActionRenderer     = blockPolicySource.ActionRenderer;
            ExceptionRenderer  = new ExceptionRenderer();
            NodeStatusRenderer = new NodeStatusRenderer();
            var renderers      = new List <IRenderer <NCAction> >();
            var strictRenderer = new StrictRenderer(onError: exc =>
                                                    ExceptionRenderer.RenderException(
                                                        RPCException.InvalidRenderException,
                                                        exc.Message.Split("\n")[0]
                                                        )
                                                    );

            if (Properties.Render)
            {
                renderers.Add(blockPolicySource.BlockRenderer);
                renderers.Add(blockPolicySource.LoggedActionRenderer);
            }
            else if (Properties.LogActionRenders)
            {
                renderers.Add(blockPolicySource.BlockRenderer);
                // The following "nullRenderer" does nothing.  It's just for filling
                // the LoggedActionRenderer<T>() constructor's parameter:
                IActionRenderer <NCAction> nullRenderer =
                    new AnonymousActionRenderer <NCAction>();
                renderers.Add(
                    new LoggedActionRenderer <NCAction>(
                        nullRenderer,
                        Log.Logger,
                        logLevel
                        )
                    );
            }
            else
            {
                renderers.Add(blockPolicySource.LoggedBlockRenderer);
            }

            if (strictRendering)
            {
                Log.Debug(
                    $"Strict rendering is on. Add {nameof(StrictRenderer)}.");
                renderers.Add(strictRenderer);
            }

            async Task minerLoopAction(
                BlockChain <NCAction> chain,
                Swarm <NCAction> swarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new Miner(chain, swarm, privateKey, authorizedMiner);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        long nextBlockIndex = chain.Tip.Index + 1;

                        if (swarm.Running)
                        {
                            Log.Debug("Start mining.");

                            if (chain.Policy is BlockPolicy bp)
                            {
                                if (bp.IsAllowedToMine(privateKey.ToAddress(), chain.Count))
                                {
                                    IEnumerable <Task <Block <NCAction> > > miners = Enumerable
                                                                                     .Range(0, minerCount)
                                                                                     .Select(_ => miner.MineBlockAsync(cancellationToken));
                                    await Task.WhenAll(miners);
                                }
                                else
                                {
                                    Log.Debug(
                                        "Miner {MinerAddress} is not allowed to mine a block with index {Index} " +
                                        "under current policy.",
                                        privateKey.ToAddress(),
                                        chain.Count);
                                    await Task.Delay(1000, cancellationToken);
                                }
                            }
                            else
                            {
                                Log.Error(
                                    "No suitable policy was found for chain {ChainId}.",
                                    chain.Id);
                                await Task.Delay(1000, cancellationToken);
                            }
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            async Task devMinerLoopAction(
                Swarm <NCAction> mainSwarm,
                Swarm <NCAction> subSwarm,
                PrivateKey privateKey,
                CancellationToken cancellationToken)
            {
                var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval);

                Log.Debug("Miner called.");
                while (!cancellationToken.IsCancellationRequested)
                {
                    try
                    {
                        if (mainSwarm.Running)
                        {
                            Log.Debug("Start mining.");
                            await miner.MineBlockAsync(cancellationToken);

                            await Task.Delay(blockInterval, cancellationToken);
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            if (isDev)
            {
                NodeService = new DevLibplanetNodeService <NCAction>(
                    Properties,
                    easyPolicy,
                    hardPolicy,
                    stagePolicy,
                    renderers,
                    devMinerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); },
                    ignoreBootstrapFailure
                    );
            }
            else
            {
                NodeService = new LibplanetNodeService <NCAction>(
                    Properties,
                    blockPolicy,
                    stagePolicy,
                    renderers,
                    minerLoopAction,
                    preloadProgress,
                    (code, msg) =>
                {
                    ExceptionRenderer.RenderException(code, msg);
                    Log.Error(msg);
                },
                    isPreloadStarted =>
                {
                    NodeStatusRenderer.PreloadStatus(isPreloadStarted);
                },
                    ignoreBootstrapFailure,
                    ignorePreloadFailure
                    );
            }

            strictRenderer.BlockChain = NodeService.BlockChain ?? throw new Exception("BlockChain is null.");
        }