示例#1
0
        protected LibplanetNodeService <T> CreateLibplanetNodeService <T>(
            Block <T> genesisBlock,
            AppProtocolVersion appProtocolVersion,
            PublicKey appProtocolVersionSigner,
            Progress <PreloadState> preloadProgress = null,
            IEnumerable <Peer> peers = null)
            where T : IAction, new()
        {
            var properties = new LibplanetNodeServiceProperties <T>
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = appProtocolVersion,
                GenesisBlock         = genesisBlock,
                StoreStatesCacheSize = 2,
                PrivateKey           = new PrivateKey(),
                StorePath            = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()),
                Port = null,
                MinimumDifficulty = 1024,
                NoMiner           = true,
                Render            = false,
                Peers             = peers ?? ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = ImmutableHashSet <PublicKey> .Empty.Add(appProtocolVersionSigner),
            };

            return(new LibplanetNodeService <T>(
                       properties,
                       blockPolicy: new BlockPolicy <T>(),
                       renderers: new[] { new DummyRenderer <T>() },
                       minerLoopAction: (chain, swarm, privateKey, _) => Task.CompletedTask,
                       preloadProgress: preloadProgress,
                       exceptionHandlerAction: (code, msg) => throw new Exception($"{code}, {msg}"),
                       preloadStatusHandlerAction: isPreloadStart => { }
                       ));
        }
示例#2
0
        protected LibplanetNodeService <T> CreateLibplanetNodeService <T>(
            Block <T> genesisBlock,
            AppProtocolVersion appProtocolVersion,
            PublicKey appProtocolVersionSigner,
            Progress <PreloadState> preloadProgress = null,
            IEnumerable <Peer> peers = null)
            where T : IAction, new()
        {
            var properties = new LibplanetNodeServiceProperties <T>
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = appProtocolVersion,
                GenesisBlock         = genesisBlock,
                StoreStatesCacheSize = 2,
                PrivateKey           = new PrivateKey(),
                Port = null,
                MinimumDifficulty = 1024,
                NoMiner           = true,
                Render            = false,
                Peers             = peers ?? ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = ImmutableHashSet <PublicKey> .Empty.Add(appProtocolVersionSigner),
            };

            return(new LibplanetNodeService <T>(
                       properties,
                       new BlockPolicy <T>(),
                       async(chain, swarm, privateKey, cancellationToken) => { },
                       preloadProgress));
        }
示例#3
0
 public BlockChainService(
     BlockChain <NCAction> blockChain,
     Swarm <NCAction> swarm,
     RpcContext context,
     LibplanetNodeServiceProperties <NCAction> libplanetNodeServiceProperties
     )
 {
     _blockChain      = blockChain;
     _delayedRenderer = blockChain.GetDelayedRenderer();
     _swarm           = swarm;
     _context         = context;
     _codec           = new Codec();
     _libplanetNodeServiceProperties = libplanetNodeServiceProperties;
 }
示例#4
0
 public BlockChainService(
     BlockChain <NineChroniclesActionType> blockChain,
     Swarm <NineChroniclesActionType> swarm,
     RpcContext context,
     LibplanetNodeServiceProperties <NineChroniclesActionType> libplanetNodeServiceProperties
     )
 {
     _blockChain      = blockChain;
     _delayedRenderer = blockChain.Renderers
                        .OfType <DelayedRenderer <NineChroniclesActionType> >()
                        .FirstOrDefault();
     _swarm   = swarm;
     _context = context;
     _codec   = new Codec();
     _libplanetNodeServiceProperties = libplanetNodeServiceProperties;
 }
        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 async Task ActivationStatus(bool existsActivatedAccounts)
        {
            var adminPrivateKey   = new PrivateKey();
            var adminAddress      = adminPrivateKey.ToAddress();
            var activatedAccounts = ImmutableHashSet <Address> .Empty;

            if (existsActivatedAccounts)
            {
                activatedAccounts = new[] { adminAddress }.ToImmutableHashSet();
            }

            Block <PolymorphicAction <ActionBase> > genesis =
                BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(
                    new PolymorphicAction <ActionBase>[]
            {
                new InitializeStates(
                    rankingState: new RankingState(),
                    shopState: new ShopState(),
                    gameConfigState: new GameConfigState(),
                    redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                         .Add("address", RedeemCodeState.Address.Serialize())
                                                         .Add("map", Bencodex.Types.Dictionary.Empty)
                                                         ),
                    adminAddressState: new AdminState(adminAddress, 1500000),
                    activatedAccountsState: new ActivatedAccountsState(activatedAccounts),
                    goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)),
                    goldDistributions: new GoldDistribution[0],
                    tableSheets: _sheets,
                    pendingActivationStates: new PendingActivationState[] { }
                    ),
            }
                    );

            var apvPrivateKey  = new PrivateKey();
            var apv            = AppProtocolVersion.Sign(apvPrivateKey, 0);
            var userPrivateKey = new PrivateKey();
            var properties     = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> >
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = apv,
                GenesisBlock         = genesis,
                StorePath            = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()),
                StoreStatesCacheSize = 2,
                SwarmPrivateKey      = new PrivateKey(),
                Port = null,
                MinimumDifficulty = 4096,
                NoMiner           = true,
                Render            = false,
                Peers             = ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = null,
                StaticPeers = ImmutableHashSet <Peer> .Empty
            };

            var service = new NineChroniclesNodeService(userPrivateKey, properties, null);

            StandaloneContextFx.NineChroniclesNodeService = service;
            StandaloneContextFx.BlockChain = service.Swarm?.BlockChain;

            var blockChain = StandaloneContextFx.BlockChain !;

            var queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }");

            var result = (bool)queryResult.Data
                         .As <Dictionary <string, object> >()["activationStatus"]
                         .As <Dictionary <string, object> >()["activated"];

            // ActivatedAccounts가 비어있을때는 true이고 하나라도 있을경우 false
            Assert.Equal(!existsActivatedAccounts, result);

            var nonce      = new byte[] { 0x00, 0x01, 0x02, 0x03 };
            var privateKey = new PrivateKey();

            (ActivationKey activationKey, PendingActivationState pendingActivation) =
                ActivationKey.Create(privateKey, nonce);
            PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation);

            blockChain.MakeTransaction(adminPrivateKey, new[] { action });
            await blockChain.MineBlock(adminAddress);

            action = activationKey.CreateActivateAccount(nonce);
            blockChain.MakeTransaction(userPrivateKey, new[] { action });
            await blockChain.MineBlock(adminAddress);

            queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }");

            result = (bool)queryResult.Data
                     .As <Dictionary <string, object> >()["activationStatus"]
                     .As <Dictionary <string, object> >()["activated"];

            // ActivatedAccounts에 Address가 추가 되었기 때문에 true
            Assert.True(result);
        }
示例#7
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(
            LibplanetNodeServiceProperties <NineChroniclesActionType> properties,
            RpcNodeServiceProperties?rpcNodeServiceProperties,
            Progress <PreloadState> preloadProgress = null,
            bool ignoreBootstrapFailure             = false
            )
        {
            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);
            }

            // BlockPolicy shared through Lib9c.
            IBlockPolicy <PolymorphicAction <ActionBase> > blockPolicy = BlockPolicy.GetPolicy(
                properties.MinimumDifficulty
                );

            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.");
                            var block = await miner.MineBlockAsync(cancellationToken);

                            const int txCountThreshold = 10;
                            var       txCount          = block?.Transactions.Count() ?? 0;
                            if (!(block is null) && txCount >= txCountThreshold)
                            {
                                Log.Error($"Block {block.Index}({block.Hash}) transaction count is {txCount}.");
                            }
                        }
                        else
                        {
                            await Task.Delay(1000, cancellationToken);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex, "Exception occurred.");
                    }
                }
            }

            NodeService = new LibplanetNodeService <NineChroniclesActionType>(
                Properties,
                blockPolicy,
                minerLoopAction,
                preloadProgress,
                ignoreBootstrapFailure
                );

            // FIXME: Agent.cs와 중복된 코드입니다.
            if (BlockPolicy.ActivatedAccounts is null)
            {
                var rawState = NodeService?.BlockChain?.GetState(ActivatedAccountsState.Address);
                BlockPolicy.UpdateActivationSet(rawState);
            }
        }
        public async Task ActivationKeyNonce_Throw_ExecutionError(string code, string msg)
        {
            var adminPrivateKey         = new PrivateKey();
            var adminAddress            = adminPrivateKey.ToAddress();
            var activatedAccounts       = ImmutableHashSet <Address> .Empty;
            var pendingActivationStates = new List <PendingActivationState>();

            Block <PolymorphicAction <ActionBase> > genesis =
                BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(
                    HashAlgorithmType.Of <SHA256>(),
                    new PolymorphicAction <ActionBase>[]
            {
                new InitializeStates(
                    rankingState: new RankingState(),
                    shopState: new ShopState(),
                    gameConfigState: new GameConfigState(),
                    redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                         .Add("address", RedeemCodeState.Address.Serialize())
                                                         .Add("map", Bencodex.Types.Dictionary.Empty)
                                                         ),
                    adminAddressState: new AdminState(adminAddress, 1500000),
                    activatedAccountsState: new ActivatedAccountsState(activatedAccounts),
                    goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)),
                    goldDistributions: new GoldDistribution[0],
                    tableSheets: _sheets,
                    pendingActivationStates: pendingActivationStates.ToArray()
                    ),
            }
                    );

            var apvPrivateKey = new PrivateKey();
            var apv           = AppProtocolVersion.Sign(apvPrivateKey, 0);

            var userPrivateKey = new PrivateKey();
            var properties     = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> >
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = apv,
                GenesisBlock         = genesis,
                StorePath            = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()),
                StoreStatesCacheSize = 2,
                SwarmPrivateKey      = new PrivateKey(),
                Port    = null,
                NoMiner = true,
                Render  = false,
                Peers   = ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = null,
                StaticPeers = ImmutableHashSet <BoundPeer> .Empty
            };
            var blockPolicy = NineChroniclesNodeService.GetTestBlockPolicy();

            var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, NetworkType.Test);

            StandaloneContextFx.NineChroniclesNodeService = service;
            StandaloneContextFx.BlockChain = service.Swarm?.BlockChain;

            var query       = $"query {{ activationKeyNonce(invitationCode: \"{code}\") }}";
            var queryResult = await ExecuteQueryAsync(query);

            Assert.Single(queryResult.Errors);
            Assert.Equal(msg, queryResult.Errors.First().Message);
        }
        public async Task ActivateAccount()
        {
            var adminPrivateKey = new PrivateKey();
            var adminAddress = adminPrivateKey.ToAddress();
            var activateAccounts = new[] { adminAddress }.ToImmutableHashSet();

            Block <PolymorphicAction <ActionBase> > genesis =
                BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(
                    new PolymorphicAction <ActionBase>[]
            {
                new InitializeStates()
                {
                    RankingState     = new RankingState(),
                    ShopState        = new ShopState(),
                    TableSheetsState = new TableSheetsState(),
                    GameConfigState  = new GameConfigState(),
                    RedeemCodeState  = new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                           .Add("address", RedeemCodeState.Address.Serialize())
                                                           .Add("map", Bencodex.Types.Dictionary.Empty)
                                                           ),
                    AdminAddressState      = new AdminState(adminAddress, 1500000),
                    ActivatedAccountsState = new ActivatedAccountsState(activateAccounts),
                    GoldCurrencyState      = new GoldCurrencyState(new Currency("NCG", minter: null)),
                    GoldDistributions      = new GoldDistribution[0],
                },
            }
                    );

            var apvPrivateKey = new PrivateKey();
            var apv            = AppProtocolVersion.Sign(apvPrivateKey, 0);
            var userPrivateKey = new PrivateKey();
            var properties     = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> >
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = apv,
                GenesisBlock         = genesis,
                StorePath            = null,
                StoreStatesCacheSize = 2,
                PrivateKey           = userPrivateKey,
                Port = null,
                MinimumDifficulty = 4096,
                NoMiner           = true,
                Render            = false,
                Peers             = ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = null,
            };

            var service = new NineChroniclesNodeService(properties, null);

            service.PrivateKey = userPrivateKey;
            StandaloneContextFx.NineChroniclesNodeService = service;
            StandaloneContextFx.BlockChain = service.Swarm.BlockChain;

            var blockChain = StandaloneContextFx.BlockChain;

            var nonce      = new byte[] { 0x00, 0x01, 0x02, 0x03 };
            var privateKey = new PrivateKey();

            (ActivationKey activationKey, PendingActivationState pendingActivation) =
                ActivationKey.Create(privateKey, nonce);
            PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation);

            blockChain.MakeTransaction(adminPrivateKey, new[] { action });
            await blockChain.MineBlock(adminAddress);

            var encodedActivationKey = activationKey.Encode();
            var queryResult          = await ExecuteQueryAsync(
                $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}");

            await blockChain.MineBlock(adminAddress);

            var result = (bool)queryResult.Data
                         .As <Dictionary <string, object> >()["activationStatus"]
                         .As <Dictionary <string, object> >()["activateAccount"];

            Assert.True(result);

            var state = (Bencodex.Types.Dictionary)blockChain.GetState(
                ActivatedAccountsState.Address);
            var activatedAccountsState = new ActivatedAccountsState(state);
            var userAddress            = userPrivateKey.ToAddress();

            Assert.True(activatedAccountsState.Accounts.Contains(userAddress));
        }
        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.");
        }