示例#1
0
 public StandaloneMutation(
     StandaloneContext standaloneContext,
     NineChroniclesNodeService nodeService,
     IConfiguration configuration
     )
 {
     if (configuration[GraphQLService.SecretTokenKey] is { })
 public RenderSubscriber(
     NineChroniclesNodeService nodeService,
     MySqlStore mySqlStore
     )
 {
     _blockRenderer      = nodeService.BlockRenderer;
     _actionRenderer     = nodeService.ActionRenderer;
     _exceptionRenderer  = nodeService.ExceptionRenderer;
     _nodeStatusRenderer = nodeService.NodeStatusRenderer;
     MySqlStore          = mySqlStore;
 }
示例#3
0
        public async Task TransferGold()
        {
            NineChroniclesNodeService service = StandaloneContextFx.NineChroniclesNodeService !;
            Currency goldCurrency             = new GoldCurrencyState(
                (Dictionary)BlockChain.GetState(GoldCurrencyState.Address)
                ).Currency;

            Address senderAddress = service.MinerPrivateKey !.ToAddress();

            var store = service.Store;
            await BlockChain.MineBlock(service.MinerPrivateKey);

            await BlockChain.MineBlock(service.MinerPrivateKey);

            // 10 + 10 (mining rewards)
            Assert.Equal(
                20 * goldCurrency,
                BlockChain.GetBalance(senderAddress, goldCurrency)
                );

            var             recipientKey = new PrivateKey();
            Address         recipient    = recipientKey.ToAddress();
            var             query        = $"mutation {{ transferGold(recipient: \"{recipient}\", amount: \"17.5\") }}";
            ExecutionResult result       = await ExecuteQueryAsync(query);

            var stagedTxIds = BlockChain.GetStagedTransactionIds().ToImmutableList();

            Assert.Single(stagedTxIds);

            var expectedResult = new Dictionary <string, object>
            {
                ["transferGold"] = stagedTxIds.Single().ToString(),
            };

            Assert.Null(result.Errors);
            Assert.Equal(expectedResult, result.Data);

            await BlockChain.MineBlock(recipientKey);

            // 10 + 10 - 17.5(transfer)
            Assert.Equal(
                FungibleAssetValue.Parse(goldCurrency, "2.5"),
                BlockChain.GetBalance(senderAddress, goldCurrency)
                );

            // 0 + 17.5(transfer) + 10(mining reward)
            Assert.Equal(
                FungibleAssetValue.Parse(goldCurrency, "27.5"),
                BlockChain.GetBalance(recipient, goldCurrency)
                );
        }
        public async Task ActivateAccount()
        {
            var adminPrivateKey = new PrivateKey();
            var adminAddress = adminPrivateKey.ToAddress();
            var activateAccounts = new[] { adminAddress }.ToImmutableHashSet();

            Block <PolymorphicAction <ActionBase> > genesis =
                MakeGenesisBlock(adminAddress, new Currency("NCG", 2, minters: null), activateAccounts);
            NineChroniclesNodeService service = ServiceBuilder.CreateNineChroniclesNodeService(genesis);

            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);
            Address userAddress            = service.PrivateKey.ToAddress();

            Assert.True(activatedAccountsState.Accounts.Contains(userAddress));
        }
 private NineChroniclesNodeService MakeMineChroniclesNodeService(PrivateKey privateKey)
 {
     var goldCurrency      = new Currency("NCG", 2, minter: null);
     int minimumDifficulty = 4096;
     var blockAction       = NineChroniclesNodeService.GetBlockPolicy(minimumDifficulty,
                                                                      new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> >().MaximumTransactions).BlockAction;
     Block <PolymorphicAction <ActionBase> > genesis =
         BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(
             new PolymorphicAction <ActionBase>[]
     {
         new InitializeStates(
             rankingState: new RankingState(),
             shopState: new ShopState(),
             gameConfigState: new GameConfigState(_sheets[nameof(GameConfigSheet)]),
             redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                  .Add("address", RedeemCodeState.Address.Serialize())
                                                  .Add("map", Bencodex.Types.Dictionary.Empty)
                                                  ),
             adminAddressState: new AdminState(default, 0),
        private NineChroniclesNodeService MakeMineChroniclesNodeService(PrivateKey privateKey)
        {
            var goldCurrency = new Currency("NCG", 2, minter: null);

            var blockPolicy = NineChroniclesNodeService.GetTestBlockPolicy();
            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(_sheets[nameof(GameConfigSheet)]),
                    redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                         .Add("address", RedeemCodeState.Address.Serialize())
                                                         .Add("map", Bencodex.Types.Dictionary.Empty)
                                                         ),
                    adminAddressState: new AdminState(default, 0),
示例#7
0
        public ActivationStatusMutation(NineChroniclesNodeService service)
        {
            Field <NonNullGraphType <BooleanGraphType> >("activateAccount",
                                                         arguments: new QueryArguments(
                                                             new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "encodedActivationKey",
            }),
                                                         resolve: context =>
            {
                try
                {
                    string encodedActivationKey =
                        context.GetArgument <string>("encodedActivationKey");
                    // FIXME: Private key may not exists at this moment.
                    if (!(service.MinerPrivateKey is { } privateKey))
                    {
                        throw new InvalidOperationException($"{nameof(privateKey)} is null.");
                    }

                    ActivationKey activationKey = ActivationKey.Decode(encodedActivationKey);
                    if (!(service.Swarm?.BlockChain is { } blockChain))
                    {
                        throw new InvalidOperationException($"{nameof(blockChain)} is null.");
                    }

                    IValue state = blockChain.GetState(activationKey.PendingAddress);

                    if (!(state is Bencodex.Types.Dictionary asDict))
                    {
                        context.Errors.Add(new ExecutionError("The given key was already expired."));
                        return(false);
                    }

                    var pendingActivationState = new PendingActivationState(asDict);
                    ActivateAccount action     = activationKey.CreateActivateAccount(
                        pendingActivationState.Nonce);

                    var actions = new NCAction[] { action };
                    blockChain.MakeTransaction(privateKey, actions);
                }
        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);
        }
        public ActionMutation(NineChroniclesNodeService service)
        {
            Field <NonNullGraphType <TxIdType> >("createAvatar",
                                                 description: "Create new avatar.",
                                                 arguments: new QueryArguments(
                                                     new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "avatarName",
                Description = "Avatar name."
            },
                                                     new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "avatarIndex",
                Description = "The index of character slot. 0 ~ 2"
            },
                                                     new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "hairIndex",
                Description = "The index of character hair color. 0 ~ 8"
            },
                                                     new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "lensIndex",
                Description = "The index of character eye color. 0 ~ 8"
            },
                                                     new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "earIndex",
                Description = "The index of character ear color. 0 ~ 8"
            },
                                                     new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "tailIndex",
                Description = "The index of character tail color. 0 ~ 8"
            }
                                                     ),
                                                 resolve: context =>
            {
                try
                {
                    if (!(service.MinerPrivateKey is { } privateKey))
                    {
                        throw new InvalidOperationException($"{nameof(service.MinerPrivateKey)} is null.");
                    }

                    if (!(service.Swarm?.BlockChain is { } blockChain))
                    {
                        throw new InvalidOperationException($"{nameof(service.Swarm.BlockChain)} is null.");
                    }

                    var avatarName  = context.GetArgument <string>("avatarName");
                    var avatarIndex = context.GetArgument <int>("avatarIndex");
                    var hairIndex   = context.GetArgument <int>("hairIndex");
                    var lensIndex   = context.GetArgument <int>("lensIndex");
                    var earIndex    = context.GetArgument <int>("earIndex");
                    var tailIndex   = context.GetArgument <int>("tailIndex");
                    var action      = new CreateAvatar
                    {
                        index = avatarIndex,
                        hair  = hairIndex,
                        lens  = lensIndex,
                        ear   = earIndex,
                        tail  = tailIndex,
                        name  = avatarName,
                    };

                    var actions = new NCAction[] { action };
                    Transaction <NCAction> tx = blockChain.MakeTransaction(privateKey, actions);
                    return(tx.Id);
                }
        public async Task Run(
            bool noMiner = false,
            [Option("app-protocol-version", new[] { 'V' }, Description = "App protocol version token")]
            string appProtocolVersionToken = null,
            [Option('G')]
            string genesisBlockPath = null,
            [Option('H')]
            string host = null,
            [Option('P')]
            ushort?port = null,
            [Option('D')]
            int minimumDifficulty = 5000000,
            [Option("private-key")]
            string privateKeyString = null,
            string storeType        = null,
            string storePath        = null,
            [Option("ice-server", new [] { 'I', })]
            string[] iceServerStrings = null,
            [Option("peer")]
            string[] peerStrings = null,
            [Option("no-trusted-state-validators")]
            bool noTrustedStateValidators = false,
            [Option("trusted-app-protocol-version-signer", new[] { 'T' },
                    Description = "Trustworthy signers who claim new app protocol versions")]
            string[] trustedAppProtocolVersionSigners = null,
            bool rpcServer       = false,
            string rpcListenHost = "0.0.0.0",
            int?rpcListenPort    = null,
            [Option("graphql-server")]
            bool graphQLServer = false,
            [Option("graphql-host")]
            string graphQLHost = "0.0.0.0",
            [Option("graphql-port")]
            int?graphQLPort = null,
            [Option("libplanet-node")]
            bool libplanetNode = false
            )
        {
#if SENTRY || !DEBUG
            try
            {
#endif
            // Setup logger.
            var loggerConf = new LoggerConfiguration()
                             .WriteTo.Console()
                             .MinimumLevel.Debug();
#if SENTRY || !DEBUG
            loggerConf = loggerConf
                         .WriteTo.Sentry(o =>
            {
                o.InitializeSdk = false;
            });
#endif
            Log.Logger = loggerConf.CreateLogger();

            if (!graphQLServer && !libplanetNode)
            {
                throw new CommandExitedException(
                          "Either --graphql-server or --libplanet-node must be present.",
                          -1
                          );
            }

            var tasks = new List <Task>();
            try
            {
                IHostBuilder graphQLHostBuilder = Host.CreateDefaultBuilder();

                var standaloneContext = new StandaloneContext
                {
                    KeyStore = Web3KeyStore.DefaultKeyStore,
                };

                if (graphQLServer)
                {
                    var graphQLNodeServiceProperties = new GraphQLNodeServiceProperties
                    {
                        GraphQLServer     = graphQLServer,
                        GraphQLListenHost = graphQLHost,
                        GraphQLListenPort = graphQLPort,
                    };


                    var graphQLService = new GraphQLService(graphQLNodeServiceProperties);
                    graphQLHostBuilder =
                        graphQLService.Configure(graphQLHostBuilder, standaloneContext);
                    tasks.Add(graphQLHostBuilder.RunConsoleAsync(Context.CancellationToken));

                    await WaitForGraphQLService(graphQLNodeServiceProperties,
                                                Context.CancellationToken);
                }

                if (appProtocolVersionToken is null)
                {
                    throw new CommandExitedException(
                              "--app-protocol-version must be present.",
                              -1
                              );
                }

                if (genesisBlockPath is null)
                {
                    throw new CommandExitedException(
                              "--genesis-block-path must be present.",
                              -1
                              );
                }

                RpcNodeServiceProperties?rpcProperties = null;
                var properties = NineChroniclesNodeServiceProperties
                                 .GenerateLibplanetNodeServiceProperties(
                    appProtocolVersionToken,
                    genesisBlockPath,
                    host,
                    port,
                    minimumDifficulty,
                    privateKeyString,
                    storeType,
                    storePath,
                    100,
                    iceServerStrings,
                    peerStrings,
                    noTrustedStateValidators,
                    trustedAppProtocolVersionSigners,
                    noMiner);
                if (rpcServer)
                {
                    rpcProperties = NineChroniclesNodeServiceProperties
                                    .GenerateRpcNodeServiceProperties(rpcListenHost, rpcListenPort);
                    properties.Render = true;
                }

                var nineChroniclesProperties = new NineChroniclesNodeServiceProperties()
                {
                    Rpc       = rpcProperties,
                    Libplanet = properties
                };

                NineChroniclesNodeService nineChroniclesNodeService =
                    StandaloneServices.CreateHeadless(nineChroniclesProperties, standaloneContext);
                standaloneContext.NineChroniclesNodeService = nineChroniclesNodeService;

                if (libplanetNode)
                {
                    if (!properties.NoMiner)
                    {
                        nineChroniclesNodeService.PrivateKey = properties.PrivateKey;
                        nineChroniclesNodeService.StartMining();
                    }

                    IHostBuilder nineChroniclesNodeHostBuilder = Host.CreateDefaultBuilder();
                    nineChroniclesNodeHostBuilder =
                        nineChroniclesNodeService.Configure(nineChroniclesNodeHostBuilder);
                    tasks.Add(
                        nineChroniclesNodeHostBuilder.RunConsoleAsync(Context.CancellationToken));
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Unexpected exception occurred during Run. {e}", e);
            }
            finally
            {
                await Task.WhenAll(tasks);
            }

#if SENTRY || !DEBUG
        }

        catch (CommandExitedException)
        {
            throw;
        }
        catch (Exception exceptionToCapture)
        {
            SentrySdk.CaptureException(exceptionToCapture);
            throw;
        }
#endif
        }
示例#11
0
        public async Task Transfer(string?memo, bool error)
        {
            NineChroniclesNodeService service = StandaloneContextFx.NineChroniclesNodeService !;
            Currency goldCurrency             = new GoldCurrencyState(
                (Dictionary)BlockChain.GetState(GoldCurrencyState.Address)
                ).Currency;

            Address senderAddress = service.MinerPrivateKey !.ToAddress();
            var     store         = service.Store;
            await BlockChain.MineBlock(senderAddress);

            await BlockChain.MineBlock(senderAddress);

            // 10 + 10 (mining rewards)
            Assert.Equal(
                20 * goldCurrency,
                BlockChain.GetBalance(senderAddress, goldCurrency)
                );

            Address recipient = new PrivateKey().ToAddress();
            long    txNonce   = BlockChain.GetNextTxNonce(senderAddress);

            var args = $"recipient: \"{recipient}\", txNonce: {txNonce}, amount: \"17.5\"";

            if (!(memo is null))
            {
                args += $"memo: \"{memo}\"";
            }

            var             query  = $"mutation {{ transfer({args}) }}";
            ExecutionResult result = await ExecuteQueryAsync(query);

            if (error)
            {
                Assert.NotNull(result.Errors);
            }
            else
            {
                Assert.Null(result.Errors);

                var stagedTxIds = BlockChain.GetStagedTransactionIds().ToImmutableList();
                Assert.Single(stagedTxIds);
                string transferTxIdString = stagedTxIds.Single().ToString();
                TxId   transferTxId       = new TxId(ByteUtil.ParseHex(transferTxIdString));

                Transaction <NCAction>?tx = BlockChain.StagePolicy.Get(BlockChain, transferTxId, false);
                Assert.NotNull(tx);
                Assert.IsType <TransferAsset>(tx !.Actions.Single().InnerAction);
                TransferAsset transferAsset = (TransferAsset)tx.Actions.Single().InnerAction;
                Assert.Equal(memo, transferAsset.Memo);

                var expectedResult = new Dictionary <string, object>
                {
                    ["transfer"] = transferTxIdString,
                };

                Assert.Equal(expectedResult, result.Data);

                await BlockChain.MineBlock(recipient);

                // 10 + 10 - 17.5(transfer)
                Assert.Equal(
                    FungibleAssetValue.Parse(goldCurrency, "2.5"),
                    BlockChain.GetBalance(senderAddress, goldCurrency)
                    );

                // 0 + 17.5(transfer) + 10(mining reward)
                Assert.Equal(
                    FungibleAssetValue.Parse(goldCurrency, "27.5"),
                    BlockChain.GetBalance(recipient, goldCurrency)
                    );
            }
        }
示例#12
0
        public async Task Run(
            bool noMiner = false,
            [Option("app-protocol-version", new[] { 'V' }, Description = "App protocol version token")]
            string appProtocolVersionToken = null,
            [Option('G')]
            string genesisBlockPath = null,
            [Option('H')]
            string host = null,
            [Option('P')]
            ushort?port = null,
            [Option('D')]
            int minimumDifficulty = 5000000,
            [Option("private-key")]
            string privateKeyString = null,
            string storeType        = null,
            string storePath        = null,
            [Option("ice-server", new [] { 'I', })]
            string[] iceServerStrings = null,
            [Option("peer")]
            string[] peerStrings = null,
            [Option("no-trusted-state-validators")]
            bool noTrustedStateValidators = false,
            [Option("trusted-app-protocol-version-signer", new[] { 'T' },
                    Description = "Trustworthy signers who claim new app protocol versions")]
            string[] trustedAppProtocolVersionSigners = null,
            bool rpcServer       = false,
            string rpcListenHost = "0.0.0.0",
            int?rpcListenPort    = null,
            [Option("graphql-server")]
            bool graphQLServer = false,
            [Option("graphql-host")]
            string graphQLHost = "0.0.0.0",
            [Option("graphql-port")]
            int?graphQLPort = null,
            [Option("libplanet-node")]
            bool libplanetNode = false,
            [Option("workers", Description = "Number of workers to use in Swarm")]
            int workers = 5,
            [Option(
                 "confirmations",
                 Description =
                     "The number of required confirmations to recognize a block.  0 by default."
                 )]
            int confirmations = 0,
            [Option(
                 "max-transactions",
                 Description =
                     "The number of maximum transactions can be included in a single block. " +
                     "Unlimited if the value is less then or equal to 0.  100 by default."
                 )]
            int maximumTransactions = 100,
            [Option("strict-rendering", Description = "Flag to turn on validating action renderer.")]
            bool strictRendering = false,
            [Option("dev", Description = "Flag to turn on the dev mode.  false by default.")]
            bool isDev = false,
            [Option(
                 "dev.block-interval",
                 Description =
                     "The time interval between blocks. It's unit is milliseconds. Works only when dev mode is on.  10000 (ms) by default.")]
            int blockInterval = 10000,
            [Option(
                 "dev.reorg-interval",
                 Description =
                     "The size of reorg interval. Works only when dev mode is on.  0 by default.")]
            int reorgInterval = 0,
            [Option(Description = "The log minimum level during headless execution.  debug by default.")]
            string logMinimumLevel = "debug",
            [Option(Description = "The Cognito identity for AWS CloudWatch logging.")]
            string awsCognitoIdentity = null,
            [Option(Description = "The access key for AWS CloudWatch logging.")]
            string awsAccessKey = null,
            [Option(Description = "The secret key for AWS CloudWatch logging.")]
            string awsSecretKey = null,
            [Option(Description = "The AWS region for AWS CloudWatch (e.g., us-east-1, ap-northeast-2).")]
            string awsRegion = null
            )
        {
#if SENTRY || !DEBUG
            try
            {
#endif

            // Setup logger.
            var loggerConf = new LoggerConfiguration()
                             .WriteTo.Console(outputTemplate: LogTemplate)
                             .ConfigureMinimumLevel(logMinimumLevel);
#if SENTRY || !DEBUG
            loggerConf = loggerConf
                         .WriteTo.Sentry(o =>
            {
                o.InitializeSdk = false;
            });
#endif
            bool useBasicAwsCredentials = !(awsAccessKey is null) && !(awsSecretKey is null);
            bool useCognitoCredentials  = !(awsCognitoIdentity is null);
            if (useBasicAwsCredentials && useCognitoCredentials)
            {
                const string message =
                    "You must choose to use only one credential between basic credential " +
                    "(i.e., --aws-access-key, --aws-secret-key) and " +
                    "Cognito credential (i.e., --aws-cognito-identity).";
                throw new CommandExitedException(message, -1);
            }

            if (useBasicAwsCredentials ^ useCognitoCredentials && !(awsRegion is null))
            {
                var            regionEndpoint = RegionEndpoint.GetBySystemName(awsRegion);
                AWSCredentials credentials    = useCognitoCredentials
                    ? (AWSCredentials) new CognitoAWSCredentials(awsCognitoIdentity, regionEndpoint)
                    : (AWSCredentials) new BasicAWSCredentials(awsAccessKey, awsSecretKey);

                var guid = LoadAWSSinkGuid() ?? Guid.NewGuid();
                StoreAWSSinkGuid(guid);

                var awsSink = new AWSSink(
                    credentials,
                    regionEndpoint,
                    "9c-standalone-logs",
                    guid.ToString());
                loggerConf.WriteTo.Sink(awsSink);
            }

            Log.Logger = loggerConf.CreateLogger();

            if (!graphQLServer && !libplanetNode)
            {
                throw new CommandExitedException(
                          "Either --graphql-server or --libplanet-node must be present.",
                          -1
                          );
            }

            var tasks = new List <Task>();
            try
            {
                IHostBuilder graphQLHostBuilder = Host.CreateDefaultBuilder();

                var standaloneContext = new StandaloneContext
                {
                    KeyStore = Web3KeyStore.DefaultKeyStore,
                };

                if (graphQLServer)
                {
                    var graphQLNodeServiceProperties = new GraphQLNodeServiceProperties
                    {
                        GraphQLServer     = graphQLServer,
                        GraphQLListenHost = graphQLHost,
                        GraphQLListenPort = graphQLPort,
                    };


                    var graphQLService = new GraphQLService(graphQLNodeServiceProperties);
                    graphQLHostBuilder =
                        graphQLService.Configure(graphQLHostBuilder, standaloneContext);
                    tasks.Add(graphQLHostBuilder.RunConsoleAsync(Context.CancellationToken));

                    await WaitForGraphQLService(graphQLNodeServiceProperties,
                                                Context.CancellationToken);
                }

                if (appProtocolVersionToken is null)
                {
                    throw new CommandExitedException(
                              "--app-protocol-version must be present.",
                              -1
                              );
                }

                if (genesisBlockPath is null)
                {
                    throw new CommandExitedException(
                              "--genesis-block-path must be present.",
                              -1
                              );
                }

                RpcNodeServiceProperties?rpcProperties = null;
                var properties = NineChroniclesNodeServiceProperties
                                 .GenerateLibplanetNodeServiceProperties(
                    appProtocolVersionToken,
                    genesisBlockPath,
                    host,
                    port,
                    minimumDifficulty,
                    privateKeyString,
                    storeType,
                    storePath,
                    100,
                    iceServerStrings,
                    peerStrings,
                    noTrustedStateValidators,
                    trustedAppProtocolVersionSigners,
                    noMiner,
                    workers: workers,
                    confirmations: confirmations,
                    maximumTransactions: maximumTransactions);


                if (rpcServer)
                {
                    rpcProperties = NineChroniclesNodeServiceProperties
                                    .GenerateRpcNodeServiceProperties(rpcListenHost, rpcListenPort);
                    properties.Render = true;
                }

                var nineChroniclesProperties = new NineChroniclesNodeServiceProperties()
                {
                    Rpc       = rpcProperties,
                    Libplanet = properties
                };

                NineChroniclesNodeService nineChroniclesNodeService =
                    StandaloneServices.CreateHeadless(
                        nineChroniclesProperties,
                        standaloneContext,
                        strictRendering: strictRendering,
                        isDev: isDev,
                        blockInterval: blockInterval,
                        reorgInterval: reorgInterval);
                standaloneContext.NineChroniclesNodeService = nineChroniclesNodeService;

                if (libplanetNode)
                {
                    if (!properties.NoMiner)
                    {
                        nineChroniclesNodeService.PrivateKey = properties.PrivateKey;
                        nineChroniclesNodeService.StartMining();
                    }

                    IHostBuilder nineChroniclesNodeHostBuilder = Host.CreateDefaultBuilder();
                    nineChroniclesNodeHostBuilder =
                        nineChroniclesNodeService.Configure(nineChroniclesNodeHostBuilder);
                    tasks.Add(
                        nineChroniclesNodeHostBuilder.RunConsoleAsync(Context.CancellationToken));
                }

                await Task.WhenAll(tasks);
            }
            catch (TaskCanceledException)
            {
                Log.Information("Terminated by the cancellation.");
            }
            catch (Exception e)
            {
                Log.Error(e, "Unexpected exception occurred during Run. {e}", e);
            }

#if SENTRY || !DEBUG
        }

        catch (CommandExitedException)
        {
            throw;
        }
        catch (Exception exceptionToCapture)
        {
            SentrySdk.CaptureException(exceptionToCapture);
            throw;
        }
#endif
        }
        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 StandaloneMutation(StandaloneContext standaloneContext)
        {
            Field <KeyStoreMutation>(
                name: "keyStore",
                resolve: context => standaloneContext.KeyStore);

            Field <ActivationStatusMutation>(
                name: "activationStatus",
                resolve: context => standaloneContext.NineChroniclesNodeService);

            Field <ActionMutation>(
                name: "action",
                resolve: context => standaloneContext.NineChroniclesNodeService);

            Field <NonNullGraphType <BooleanGraphType> >(
                name: "stageTx",
                description: "Add a new transaction to staging",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "payload",
                Description = "Hex-encoded bytes for new transaction."
            }
                    ),
                resolve: context =>
            {
                try
                {
                    byte[] bytes = ByteUtil.ParseHex(context.GetArgument <string>("payload"));
                    Transaction <NCAction> tx         = Transaction <NCAction> .Deserialize(bytes);
                    NineChroniclesNodeService service = standaloneContext.NineChroniclesNodeService;
                    BlockChain <NCAction> blockChain  = service.Swarm.BlockChain;

                    if (blockChain.Policy.DoesTransactionFollowsPolicy(tx, blockChain))
                    {
                        blockChain.StageTransaction(tx);
                        return(true);
                    }
                    else
                    {
                        context.Errors.Add(new ExecutionError("The given transaction is invalid."));
                        return(false);
                    }
                }
                catch (Exception e)
                {
                    context.Errors.Add(new ExecutionError("An unexpected exception occurred.", e));
                    return(false);
                }
            }
                );

            Field <TxIdType>(
                name: "transferGold",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> >
            {
                Name = "recipient",
            },
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name = "amount"
            }
                    ),
                resolve: context =>
            {
                NineChroniclesNodeService service = standaloneContext.NineChroniclesNodeService;
                PrivateKey privateKey             = service.PrivateKey;
                if (privateKey is null)
                {
                    // FIXME We should cover this case on unittest.
                    var msg = "No private key was loaded.";
                    context.Errors.Add(new ExecutionError(msg));
                    Log.Error(msg);
                    return(null);
                }

                BlockChain <NCAction> blockChain = service.BlockChain;
                var currency = new GoldCurrencyState(
                    (Dictionary)blockChain.GetState(GoldCurrencyState.Address)
                    ).Currency;
                FungibleAssetValue amount =
                    FungibleAssetValue.Parse(currency, context.GetArgument <string>("amount"));

                Address recipient = context.GetArgument <Address>("recipient");

                Transaction <NCAction> tx = blockChain.MakeTransaction(
                    privateKey,
                    new NCAction[]
                {
                    new TransferAsset(
                        privateKey.ToAddress(),
                        recipient,
                        amount
                        ),
                }
                    );
                return(tx.Id);
            }
                );
        }
        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));
        }