Пример #1
0
        public PeerChainStateQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <ListGraphType <StringGraphType> > >(
                name: "state",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;

                if (service is null)
                {
                    Log.Error($"{nameof(NineChroniclesNodeService)} is null.");
                    return(null);
                }

                var swarm       = service.Swarm;
                var chain       = swarm.BlockChain;
                var chainStates = new List <string>
                {
                    $"{swarm.AsPeer.Address}, {chain.Tip.Index}, {chain.Tip.TotalDifficulty}"
                };

                var peerChainState = swarm.GetPeerChainStateAsync(
                    TimeSpan.FromSeconds(5), default)
                                     .Result
                                     .Select(
                    state => $"{state.Peer.Address}, {state.TipIndex}, {state.TotalDifficulty}");

                chainStates.AddRange(peerChainState);

                return(chainStates);
            }
                );
        }
Пример #2
0
        public GraphQLTestBase(ITestOutputHelper output)
        {
            Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().CreateLogger();

            _output = output;

            var store        = new DefaultStore(null);
            var genesisBlock = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock();

            var blockPolicy = new BlockPolicy <PolymorphicAction <ActionBase> >(blockAction: new RewardGold());
            var blockChain  = new BlockChain <PolymorphicAction <ActionBase> >(
                blockPolicy,
                store,
                store,
                genesisBlock,
                renderers: new IRenderer <PolymorphicAction <ActionBase> >[] { new BlockRenderer(), new ActionRenderer() }
                );

            var tempKeyStorePath = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());
            var keyStore         = new Web3KeyStore(tempKeyStorePath);

            StandaloneContextFx = new StandaloneContext
            {
                BlockChain = blockChain,
                KeyStore   = keyStore,
            };

            Schema = new StandaloneSchema(new TestServiceProvider(StandaloneContextFx));
            Schema.Subscription.As <StandaloneSubscription>().RegisterTipChangedSubscription();

            DocumentExecutor = new DocumentExecuter();
        }
Пример #3
0
        public GraphQLControllerTest()
        {
            var store      = new DefaultStore(null);
            var stateStore = new TrieStateStore(
                new DefaultKeyValueStore(null),
                new DefaultKeyValueStore(null));
            var genesisBlock = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock();

            var blockchain = new BlockChain <PolymorphicAction <ActionBase> >(
                new BlockPolicy <PolymorphicAction <ActionBase> >(),
                new VolatileStagePolicy <PolymorphicAction <ActionBase> >(),
                store,
                stateStore,
                genesisBlock);

            _standaloneContext = new StandaloneContext
            {
                BlockChain = blockchain,
                Store      = store,
            };
            _configuration                   = new ConfigurationBuilder().AddInMemoryCollection().Build();
            _httpContextAccessor             = new HttpContextAccessor();
            _httpContextAccessor.HttpContext = new DefaultHttpContext();

            _controller = new GraphQLController(_standaloneContext, _httpContextAccessor, _configuration);
        }
Пример #4
0
 public StandaloneMutation(
     StandaloneContext standaloneContext,
     NineChroniclesNodeService nodeService,
     IConfiguration configuration
     )
 {
     if (configuration[GraphQLService.SecretTokenKey] is { })
Пример #5
0
 public TestServiceProvider(StandaloneContext standaloneContext)
 {
     Query             = new StandaloneQuery(standaloneContext);
     Mutation          = new StandaloneMutation(standaloneContext);
     Subscription      = new StandaloneSubscription(standaloneContext);
     StandaloneContext = standaloneContext;
 }
Пример #6
0
        public GraphQLTestBase(ITestOutputHelper output)
        {
            Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().CreateLogger();

            _output = output;

            var goldCurrency = new Currency("NCG", 2, minter: null);

            var fixturePath  = Path.Combine("..", "..", "..", "..", "Lib9c", ".Lib9c.Tests", "Data", "TableCSV");
            var sheets       = TableSheetsImporter.ImportSheets(fixturePath);
            var blockAction  = new RewardGold();
            var genesisBlock = BlockChain <NCAction> .MakeGenesisBlock(
                new NCAction[]
            {
                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(AdminAddress, 10000),
                    activatedAccountsState: new ActivatedAccountsState(),
                    goldCurrencyState: new GoldCurrencyState(goldCurrency),
                    goldDistributions: new GoldDistribution[] { },
                    tableSheets: sheets,
                    pendingActivationStates: new PendingActivationState[] { }
                    ),
            }, blockAction : blockAction);

            var ncService        = ServiceBuilder.CreateNineChroniclesNodeService(genesisBlock, new PrivateKey());
            var tempKeyStorePath = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());
            var keyStore         = new Web3KeyStore(tempKeyStorePath);

            StandaloneContextFx = new StandaloneContext
            {
                KeyStore = keyStore,
            };
            ncService.ConfigureContext(StandaloneContextFx);

            var configurationBuilder = new ConfigurationBuilder();
            var configuration        = configurationBuilder.Build();

            var services = new ServiceCollection();

            services.AddSingleton(StandaloneContextFx);
            services.AddSingleton <IConfiguration>(configuration);
            services.AddGraphTypes();
            services.AddLibplanetExplorer <NCAction>();
            services.AddSingleton <StateQuery>();
            services.AddSingleton(ncService);
            ServiceProvider serviceProvider = services.BuildServiceProvider();

            Schema = new StandaloneSchema(serviceProvider);

            DocumentExecutor = new DocumentExecuter();
        }
Пример #7
0
        public ActivationStatusQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "activated",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;

                if (service is null)
                {
                    return(false);
                }

                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.");
                    }

                    Address userAddress      = privateKey.ToAddress();
                    Address activatedAddress = userAddress.Derive(ActivationKey.DeriveKey);

                    if (blockChain.GetState(activatedAddress) is Bencodex.Types.Boolean)
                    {
                        return(true);
                    }

                    // Preserve previous check code due to migration period.
                    // TODO: Remove this code after v100061+
                    IValue state = blockChain.GetState(ActivatedAccountsState.Address);

                    if (state is Bencodex.Types.Dictionary asDict)
                    {
                        var activatedAccountsState = new ActivatedAccountsState(asDict);
                        var activatedAccounts      = activatedAccountsState.Accounts;
                        return(activatedAccounts.Count == 0 ||
                               activatedAccounts.Contains(userAddress));
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    var msg = "Unexpected exception occurred during ActivationStatusQuery: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }
            }
                );
        }
Пример #8
0
        public StandaloneMutation(StandaloneContext standaloneContext)
        {
            StandaloneContext = standaloneContext;

            Field <KeyStoreMutation>(
                name: "keyStore",
                resolve: context => standaloneContext.KeyStore);

            Field <ActivationStatusMutation>(
                name: "activationStatus",
                resolve: context => standaloneContext.NineChroniclesNodeService);
        }
Пример #9
0
        public ActivationStatusQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "activated",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;

                if (service is null)
                {
                    return(false);
                }

                try
                {
                    PrivateKey privateKey = service.PrivateKey;
                    Address address       = privateKey.ToAddress();
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    IValue state = blockChain.GetState(ActivatedAccountsState.Address);

                    if (state is Bencodex.Types.Dictionary asDict)
                    {
                        var activatedAccountsState = new ActivatedAccountsState(asDict);
                        var activatedAccounts      = activatedAccountsState.Accounts;
                        return(activatedAccounts.Count == 0 ||
                               activatedAccounts.Contains(address));
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    var msg = "Unexpected exception occurred during ActivationStatusQuery: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }
            }
                );
        }
Пример #10
0
        public PeerChainStateQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <ListGraphType <StringGraphType> > >(
                name: "state",
                description: "Summary of other peers connected to this node. It consists of address, chain height, and total difficulty.",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;
                if (service is null)
                {
                    Log.Error($"{nameof(NineChroniclesNodeService)} is null.");
                    return(null);
                }

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

                var chainStates = new List <string>
                {
                    $"{swarm.AsPeer.Address}, {chain.Tip.Index}, {chain.Tip.TotalDifficulty}"
                };

                var peerChainState = swarm.GetPeerChainStateAsync(
                    TimeSpan.FromSeconds(5), default)
                                     .Result
                                     .Select(
                    state => $"{state.Peer.Address}, {state.TipIndex}, {state.TotalDifficulty}");

                chainStates.AddRange(peerChainState);

                return(chainStates);
            }
                );
        }
Пример #11
0
 public GraphQLController(StandaloneContext standaloneContext, IHttpContextAccessor httpContextAccessor, IConfiguration configuration)
 {
     _httpContextAccessor = httpContextAccessor;
     _configuration       = configuration;
     StandaloneContext    = standaloneContext;
 }
Пример #12
0
        public StandaloneQuery(StandaloneContext standaloneContext)
        {
            Field <ByteStringType>(
                name: "state",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "The address of state to fetch from the chain."
            },
                    new QueryArgument <ByteStringType> {
                Name = "hash", Description = "The hash of the block used to fetch state from chain."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                var address            = context.GetArgument <Address>("address");
                var blockHashByteArray = context.GetArgument <byte[]>("hash");
                var blockHash          = blockHashByteArray is null
                        ? blockChain.Tip.Hash
                        : new HashDigest <SHA256>(blockHashByteArray);

                var state = blockChain.GetState(address, blockHash);

                return(new Codec().Encode(state));
            }
                );

            Field <KeyStoreType>(
                name: "keyStore",
                resolve: context => standaloneContext.KeyStore
                );

            Field <NonNullGraphType <NodeStatusType> >(
                name: "nodeStatus",
                resolve: context => new NodeStatusType
            {
                BootstrapEnded = standaloneContext.BootstrapEnded,
                PreloadEnded   = standaloneContext.PreloadEnded,
            }
                );

            Field <NonNullGraphType <ValidationQuery> >(
                name: "validation",
                description: "The validation method provider for Libplanet types.",
                resolve: context => new ValidationQuery(standaloneContext));

            Field <NonNullGraphType <ActivationStatusQuery> >(
                name: "activationStatus",
                description: "Check if the provided address is activated.",
                resolve: context => new ActivationStatusQuery(standaloneContext));

            Field <NonNullGraphType <PeerChainStateQuery> >(
                name: "peerChainState",
                description: "Get the peer's block chain state",
                resolve: context => new PeerChainStateQuery(standaloneContext));
        }
Пример #13
0
        public ValidationQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "metadata",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "raw",
                Description = "The raw value of json metadata."
            }),
                resolve: context =>
            {
                var raw = context.GetArgument <string>("raw");
                try
                {
                    var remoteIndex = JsonDocument.Parse(raw).RootElement.GetProperty("Index").GetInt32();
                    Log.Debug("Remote: {index1}, Local: {index2}",
                              remoteIndex, standaloneContext.BlockChain.Tip?.Index ?? -1);
                    var ret = remoteIndex > (standaloneContext.BlockChain.Tip?.Index ?? -1);
                    return(ret);
                }
                catch (JsonException je)
                {
                    Log.Warning(je, "Given metadata is invalid. (raw: {raw})", raw);
                    return(false);
                }
                catch (Exception e)
                {
                    Log.Warning(e, "Unexpected exception occurred. (raw: {raw})", raw);
                    return(false);
                }
            }
                );

            Field <NonNullGraphType <BooleanGraphType> >(
                name: "privateKey",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <ByteStringType> >
            {
                Name        = "hex",
                Description = "The raw value of private-key, presented as hexadecimal."
            }),
                resolve: context =>
            {
                try
                {
                    var rawPrivateKey = context.GetArgument <byte[]>("hex");
                    var _             = new PrivateKey(rawPrivateKey);
                    return(true);
                }
                catch (ArgumentException)
                {
                    return(false);
                }
            }
                );

            Field <NonNullGraphType <BooleanGraphType> >(
                name: "publicKey",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <ByteStringType> >
            {
                Name        = "hex",
                Description = "The raw value of public-key, presented as hexadecimal."
            }),
                resolve: context =>
            {
                try
                {
                    var rawPublicKey = context.GetArgument <byte[]>("hex");
                    var _            = new PublicKey(rawPublicKey);
                    return(true);
                }
                catch (ArgumentException)
                {
                    return(false);
                }
                catch (FormatException)
                {
                    return(false);
                }
            }
                );
        }
        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 StandaloneQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <StateQuery <NCAction> > >(name: "stateQuery", resolve: _ => standaloneContext.BlockChain);
            Field <ByteStringType>(
                name: "state",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "The address of state to fetch from the chain."
            },
                    new QueryArgument <ByteStringType> {
                Name = "hash", Description = "The hash of the block used to fetch state from chain."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                var address            = context.GetArgument <Address>("address");
                var blockHashByteArray = context.GetArgument <byte[]>("hash");
                var blockHash          = blockHashByteArray is null
                        ? blockChain.Tip.Hash
                        : new HashDigest <SHA256>(blockHashByteArray);

                var state = blockChain.GetState(address, blockHash);

                return(new Codec().Encode(state));
            }
                );

            Field <KeyStoreType>(
                name: "keyStore",
                resolve: context => standaloneContext.KeyStore
                );

            Field <NonNullGraphType <NodeStatusType> >(
                name: "nodeStatus",
                resolve: context => new NodeStatusType
            {
                BootstrapEnded = standaloneContext.BootstrapEnded,
                PreloadEnded   = standaloneContext.PreloadEnded,
                IsMining       = standaloneContext.IsMining,
                BlockChain     = standaloneContext.BlockChain,
                Store          = standaloneContext.Store,
            }
                );

            Field <NonNullGraphType <ValidationQuery> >(
                name: "validation",
                description: "The validation method provider for Libplanet types.",
                resolve: context => new ValidationQuery(standaloneContext));

            Field <NonNullGraphType <ActivationStatusQuery> >(
                name: "activationStatus",
                description: "Check if the provided address is activated.",
                resolve: context => new ActivationStatusQuery(standaloneContext));

            Field <NonNullGraphType <PeerChainStateQuery> >(
                name: "peerChainState",
                description: "Get the peer's block chain state",
                resolve: context => new PeerChainStateQuery(standaloneContext));

            Field <NonNullGraphType <StringGraphType> >(
                name: "goldBalance",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "Target address to query"
            },
                    new QueryArgument <ByteStringType> {
                Name = "hash", Description = "Offset block hash for query."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                Address address           = context.GetArgument <Address>("address");
                byte[] blockHashByteArray = context.GetArgument <byte[]>("hash");
                var blockHash             = blockHashByteArray is null
                        ? blockChain.Tip.Hash
                        : new HashDigest <SHA256>(blockHashByteArray);
                Currency currency = new GoldCurrencyState(
                    (Dictionary)blockChain.GetState(GoldCurrencyState.Address)
                    ).Currency;

                return(blockChain.GetBalance(
                           address,
                           currency,
                           blockHash
                           ).GetQuantityString());
            }
                );

            Field <NonNullGraphType <LongGraphType> >(
                name: "nextTxNonce",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "Target address to query"
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                Address address = context.GetArgument <Address>("address");
                return(blockChain.GetNextTxNonce(address));
            }
                );
        }
Пример #16
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
            )
        {
#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
        }
Пример #17
0
        public async Task Run(
            [Option("app-protocol-version", new[] { 'V' }, Description = "App protocol version token")]
            string appProtocolVersionToken,
            [Option('G')]
            string genesisBlockPath,
            bool noMiner = false,
            [Option('H')]
            string?host = null,
            [Option('P')]
            ushort?port = null,
            [Option("swarm-private-key",
                    Description = "The private key used for signing messages and to specify your node. " +
                                  "If you leave this null, a randomly generated value will be used.")]
            string?swarmPrivateKeyString = null,
            [Option('D')]
            int minimumDifficulty = 5000000,
            [Option("miner-private-key",
                    Description = "The private key used for mining blocks. " +
                                  "Must not be null if you want to turn on mining with libplanet-node.")]
            string?minerPrivateKeyString = null,
            string?storeType             = null,
            string?storePath             = null,
            [Option("ice-server", new [] { 'I', })]
            string[]?iceServerStrings = null,
            [Option("peer")]
            string[]?peerStrings = null,
            [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("graphql-secret-token-path", Description = "The path to write GraphQL secret token. " +
                                                               "If you want to protect this headless application, " +
                                                               "you should use this option and take it into headers.")]
            string?graphQLSecretTokenPath = null,
            [Option(Description = "Run without CORS policy.")]
            bool noCors = 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 = "Log action renders besides block renders.  --rpc-server implies this.")]
            bool logActionRenders = false,
            [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,
            [Option(Description = "Run as an authorized miner, which mines only blocks that should be authorized.")]
            bool authorizedMiner = false,
            [Option(Description = "The lifetime of each transaction, which uses minute as its unit.  60 (m) by default.")]
            int txLifeTime = 60,
            [Option(Description = "The grace period for new messages, which uses second as its unit.  60 (s) by default.")]
            int messageTimeout = 60,
            [Option(Description = "The grace period for tip update, which uses second as its unit.  60 (s) by default.")]
            int tipTimeout = 60,
            [Option(Description =
                        "A number that determines how far behind the demand the tip of the chain " +
                        "will publish `NodeException` to GraphQL subscriptions.  1150 blocks by default.")]
            int demandBuffer = 1150,
            [Option("static-peer",
                    Description = "A list of peers that the node will continue to maintain.")]
            string[]?staticPeerStrings = null,
            [Option("miner-count", Description = "The number of miner task(thread).")]
            int minerCount = 1,
            [Option(Description = "Run node without preloading.")]
            bool skipPreload = false,
            [Option(Description = "Minimum number of peers to broadcast message.  10 by default.")]
            int minimumBroadcastTarget = 10,
            [Option(Description =
                        "Number of the peers can be stored in each bucket.  16 by default.")]
            int bucketSize = 16,
            [Option(Description =
                        "Determines behavior when the chain's tip is stale. \"reboot\" and \"preload\" " +
                        "is available and \"reboot\" option is selected by default.")]
            string chainTipStaleBehaviorType = "reboot"
            )
        {
#if SENTRY || !DEBUG
            try
            {
#endif

            // Setup logger.
            var configurationBuilder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
            var configuration        = configurationBuilder.Build();
            var loggerConf           = new LoggerConfiguration()
                                       .ReadFrom.Configuration(configuration)
                                       .Destructure.UsingAttributes();
#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);
            }

            // Clean-up previous temporary log files.
            if (Directory.Exists("_logs"))
            {
                Directory.Delete("_logs", true);
            }

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

                var guid = LoadAWSSinkGuid();
                if (guid is null)
                {
                    guid = Guid.NewGuid();
                    StoreAWSSinkGuid(guid.Value);
                }

                loggerConf = loggerConf.WriteTo.AmazonS3(
                    new AmazonS3Client(credentials, regionEndpoint),
                    "_logs/log.json",
                    "9c-headless-logs",
                    formatter: new CompactJsonFormatter(),
                    rollingInterval: Serilog.Sinks.AmazonS3.RollingInterval.Hour,
                    batchingPeriod: TimeSpan.FromMinutes(10),
                    batchSizeLimit: 10000,
                    bucketPath: guid.ToString()
                    );
            }

            Log.Logger = loggerConf.CreateLogger();

            if (!noMiner && minerPrivateKeyString is null)
            {
                throw new CommandExitedException(
                          "--miner-private-key must be present to turn on mining at libplanet node.",
                          -1
                          );
            }

            try
            {
                IHostBuilder hostBuilder = Host.CreateDefaultBuilder();

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

                if (graphQLServer)
                {
                    string?secretToken = null;
                    if (graphQLSecretTokenPath is { })
                    {
                        var buffer = new byte[40];
                        new SecureRandom().NextBytes(buffer);
                        secretToken = Convert.ToBase64String(buffer);
                        await File.WriteAllTextAsync(graphQLSecretTokenPath, secretToken);
                    }
                    var graphQLNodeServiceProperties = new GraphQLNodeServiceProperties
                    {
                        GraphQLServer     = graphQLServer,
                        GraphQLListenHost = graphQLHost,
                        GraphQLListenPort = graphQLPort,
                        SecretToken       = secretToken,
                        NoCors            = noCors,
                    };

                    var graphQLService = new GraphQLService(graphQLNodeServiceProperties);
                    hostBuilder = graphQLService.Configure(hostBuilder);
                }

                var properties = NineChroniclesNodeServiceProperties
                                 .GenerateLibplanetNodeServiceProperties(
                    appProtocolVersionToken,
                    genesisBlockPath,
                    host,
                    port,
                    swarmPrivateKeyString,
                    minimumDifficulty,
                    storeType,
                    storePath,
                    100,
                    iceServerStrings,
                    peerStrings,
                    trustedAppProtocolVersionSigners,
                    noMiner,
                    workers: workers,
                    confirmations: confirmations,
                    maximumTransactions: maximumTransactions,
                    messageTimeout: messageTimeout,
                    tipTimeout: tipTimeout,
                    demandBuffer: demandBuffer,
                    staticPeerStrings: staticPeerStrings,
                    preload: !skipPreload,
                    minimumBroadcastTarget: minimumBroadcastTarget,
                    bucketSize: bucketSize,
                    chainTipStaleBehaviorType: chainTipStaleBehaviorType
                    );

                if (rpcServer)
                {
                    properties.Render           = true;
                    properties.LogActionRenders = true;
                }

                if (logActionRenders)
                {
                    properties.LogActionRenders = true;
                }

                var minerPrivateKey = string.IsNullOrEmpty(minerPrivateKeyString)
                    ? null
                    : new PrivateKey(ByteUtil.ParseHex(minerPrivateKeyString));
                var nineChroniclesProperties = new NineChroniclesNodeServiceProperties()
                {
                    MinerPrivateKey = minerPrivateKey,
                    Libplanet       = properties,
                    Dev             = isDev,
                    StrictRender    = strictRendering,
                    BlockInterval   = blockInterval,
                    ReorgInterval   = reorgInterval,
                    AuthorizedMiner = authorizedMiner,
                    TxLifeTime      = TimeSpan.FromMinutes(txLifeTime),
                    MinerCount      = minerCount,
                };
                hostBuilder.ConfigureServices(services =>
                {
                    services.AddSingleton(_ => standaloneContext);
                });
                hostBuilder.UseNineChroniclesNode(nineChroniclesProperties, standaloneContext);
                if (rpcServer)
                {
                    hostBuilder.UseNineChroniclesRPC(
                        NineChroniclesNodeServiceProperties
                        .GenerateRpcNodeServiceProperties(rpcListenHost, rpcListenPort)
                        );
                }

                await hostBuilder.RunConsoleAsync(Context.CancellationToken);
            }
Пример #18
0
        public static async Task Main()
        {
            // Get configuration
            var configurationBuilder = new ConfigurationBuilder()
                                       .AddJsonFile("appsettings.json")
                                       .AddEnvironmentVariables("NC_");
            IConfiguration config         = configurationBuilder.Build();
            var            headlessConfig = new Configuration();

            config.Bind(headlessConfig);

            var loggerConf = new LoggerConfiguration()
                             .ReadFrom.Configuration(config);

            Log.Logger = loggerConf.CreateLogger();

            // FIXME: quick and dirty workaround.
            // Please remove it after fixing Libplanet.Net.Swarm<T> and NetMQTransport...
            if (string.IsNullOrEmpty(headlessConfig.Host))
            {
                headlessConfig.Host = null;
            }

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

            IHostBuilder hostBuilder = Host.CreateDefaultBuilder();

            CancellationTokenSource source = new CancellationTokenSource();
            CancellationToken       token  = source.Token;

            hostBuilder.ConfigureWebHostDefaults(builder =>
            {
                builder.UseStartup <GraphQL.GraphQLStartup>();
                builder.UseUrls($"http://{headlessConfig.GraphQLHost}:{headlessConfig.GraphQLPort}/");
            });

            var properties = NineChroniclesNodeServiceProperties
                             .GenerateLibplanetNodeServiceProperties(
                headlessConfig.AppProtocolVersionToken,
                headlessConfig.GenesisBlockPath,
                headlessConfig.Host,
                headlessConfig.Port,
                headlessConfig.SwarmPrivateKeyString,
                headlessConfig.StoreType,
                headlessConfig.StorePath,
                100,
                headlessConfig.IceServerStrings,
                headlessConfig.PeerStrings,
                headlessConfig.TrustedAppProtocolVersionSigners,
                noMiner: true,
                workers: headlessConfig.Workers,
                confirmations: headlessConfig.Confirmations,
                messageTimeout: headlessConfig.MessageTimeout,
                tipTimeout: headlessConfig.TipTimeout,
                demandBuffer: headlessConfig.DemandBuffer,
                minimumBroadcastTarget: headlessConfig.MinimumBroadcastTarget,
                bucketSize: headlessConfig.BucketSize,
                staticPeerStrings: headlessConfig.StaticPeerStrings,
                render: true,
                preload: false,
                transportType: "netmq");

            var nineChroniclesProperties = new NineChroniclesNodeServiceProperties()
            {
                MinerPrivateKey  = null,
                Libplanet        = properties,
                TxQuotaPerSigner = 10,
            };

            if (headlessConfig.LogActionRenders)
            {
                properties.LogActionRenders = true;
            }

            hostBuilder.UseNineChroniclesNode(nineChroniclesProperties, context);

            // ConfigureServices must come before Configure for now
            hostBuilder = hostBuilder
                          .ConfigureServices((ctx, services) =>
            {
                services.AddDbContextFactory <NineChroniclesContext>(
                    options => options.UseMySQL(headlessConfig.MySqlConnectionString)
                    );
                services.AddHostedService <RenderSubscriber>();
                services.AddSingleton <MySqlStore>();
                services.Configure <Configuration>(config);
            });

            await hostBuilder.RunConsoleAsync(token);
        }
        public ValidationQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "metadata",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "raw",
                Description = "The raw value of json metadata."
            }),
                resolve: context =>
            {
                var raw = context.GetArgument <string>("raw");
                try
                {
                    Log.Debug($"Validating received raw: {raw}");
                    // FIXME: Thread.Sleep is temporary. Should be removed.
                    var timeSpent           = 0;
                    const int retryInterval = 1000;
                    const int grace         = 100 * 1000;
                    while (standaloneContext.BlockChain is null)
                    {
                        Log.Debug(
                            "Blockchain instance is null. Sleep {interval}ms...",
                            retryInterval);
                        Thread.Sleep(retryInterval);
                        timeSpent += retryInterval;

                        if (timeSpent < grace)
                        {
                            continue;
                        }

                        var msg = $"Blockchain instance is not initialized until {grace}ms.";
                        Log.Debug(msg);
                        throw new BlockChainInitializeException(msg);
                    }

                    Log.Debug("Time until blockchain online: {time}ms", timeSpent);

                    var remoteIndex = JsonDocument.Parse(raw).RootElement.GetProperty("Index").GetInt32();
                    Log.Debug("Remote: {index1}, Local: {index2}",
                              remoteIndex, standaloneContext.BlockChain.Tip.Index);
                    var ret = remoteIndex > standaloneContext.BlockChain.Tip.Index;
                    return(ret);
                }
                catch (JsonException je)
                {
                    Log.Warning(je, "Given metadata is invalid. (raw: {raw})", raw);
                    return(false);
                }
                catch (Exception e)
                {
                    var msg = $"Exception occurred while validating metadata. (raw: {raw})";
                    Log.Warning(e, msg + " {e}", e);
                    throw new ExecutionError(msg, e);
                }
            }
                );

            Field <NonNullGraphType <BooleanGraphType> >(
                name: "privateKey",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <ByteStringType> >
            {
                Name        = "hex",
                Description = "The raw value of private-key, presented as hexadecimal."
            }),
                resolve: context =>
            {
                try
                {
                    var rawPrivateKey = context.GetArgument <byte[]>("hex");
                    var _             = new PrivateKey(rawPrivateKey);
                    return(true);
                }
                catch (ArgumentException)
                {
                    return(false);
                }
            }
                );

            Field <NonNullGraphType <BooleanGraphType> >(
                name: "publicKey",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <ByteStringType> >
            {
                Name        = "hex",
                Description = "The raw value of public-key, presented as hexadecimal."
            }),
                resolve: context =>
            {
                try
                {
                    var rawPublicKey = context.GetArgument <byte[]>("hex");
                    var _            = new PublicKey(rawPublicKey);
                    return(true);
                }
                catch (ArgumentException)
                {
                    return(false);
                }
                catch (FormatException)
                {
                    return(false);
                }
            }
                );
        }
        public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration configuration)
        {
            bool useSecretToken = configuration[GraphQLService.SecretTokenKey] is { };

            Field <NonNullGraphType <StateQuery> >(name: "stateQuery", arguments: new QueryArguments(
                                                       new QueryArgument <ByteStringType>
            {
                Name        = "hash",
                Description = "Offset block hash for query.",
            }),
                                                   resolve: context =>
            {
                BlockHash?blockHash = context.GetArgument <byte[]>("hash") switch
                {
                    byte[] bytes => new BlockHash(bytes),
                    null => null,
                };

                return(standaloneContext.BlockChain?.ToAccountStateGetter(blockHash),
                       standaloneContext.BlockChain?.ToAccountBalanceGetter(blockHash));
            }
                                                   );

            Field <ByteStringType>(
                name: "state",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "The address of state to fetch from the chain."
            },
                    new QueryArgument <ByteStringType> {
                Name = "hash", Description = "The hash of the block used to fetch state from chain."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                var address            = context.GetArgument <Address>("address");
                var blockHashByteArray = context.GetArgument <byte[]>("hash");
                var blockHash          = blockHashByteArray is null
                        ? blockChain.Tip.Hash
                        : new BlockHash(blockHashByteArray);

                var state = blockChain.GetState(address, blockHash);

                return(new Codec().Encode(state));
            }
                );

            Field <NonNullGraphType <ListGraphType <NonNullGraphType <TransferNCGHistoryType> > > >(
                "transferNCGHistories",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <ByteStringType> >
            {
                Name = "blockHash"
            },
                    new QueryArgument <AddressType>
            {
                Name = "recipient"
            }
                    ), resolve: context =>
            {
                BlockHash blockHash = new BlockHash(context.GetArgument <byte[]>("blockHash"));

                if (!(standaloneContext.Store is { } store))
                {
                    throw new InvalidOperationException();
                }

                if (!(store.GetBlock <NCAction>(blockHash) is { } block))
                {
                    throw new ArgumentException("blockHash");
                }

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

                var txs = block.Transactions.Where(tx =>
                                                   tx.Actions.Count == 1 &&
                                                   tx.Actions.First().InnerAction is TransferAsset transferAsset &&
                                                   (!recipient.HasValue || transferAsset.Recipient == recipient) &&
                                                   transferAsset.Amount.Currency.Ticker == "NCG" &&
                                                   store.GetTxExecution(blockHash, tx.Id) is TxSuccess);

                TransferNCGHistory ToTransferNCGHistory(TxSuccess txSuccess)
                {
                    var rawTransferNcgHistories = txSuccess.FungibleAssetsDelta.Select(pair =>
                                                                                       (pair.Key, pair.Value.Values.First(fav => fav.Currency.Ticker == "NCG")))
                                                  .ToArray();
                    var((senderAddress, _), (recipientAddress, amount)) =
                        rawTransferNcgHistories[0].Item2.RawValue > rawTransferNcgHistories[1].Item2.RawValue
                                ? (rawTransferNcgHistories[1], rawTransferNcgHistories[0])
                                : (rawTransferNcgHistories[0], rawTransferNcgHistories[1]);
                    return(new TransferNCGHistory(
                               txSuccess.BlockHash,
                               txSuccess.TxId,
                               senderAddress,
                               recipientAddress,
                               amount));
                }

                var histories = txs.Select(tx =>
                                           ToTransferNCGHistory((TxSuccess)store.GetTxExecution(blockHash, tx.Id)));

                return(histories);
            });
 public GraphQLController(StandaloneContext standaloneContext)
 {
     StandaloneContext = standaloneContext;
 }
Пример #22
0
        public NodeStatusType(StandaloneContext context)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "bootstrapEnded",
                description: "Whether the current libplanet node has ended bootstrapping.",
                resolve: _ => context.BootstrapEnded
                );
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "preloadEnded",
                description: "Whether the current libplanet node has ended preloading.",
                resolve: _ => context.PreloadEnded
                );
            Field <NonNullGraphType <BlockHeaderType> >(
                name: "tip",
                description: "Block header of the tip block from the current canonical chain.",
                resolve: _ => context.BlockChain is { } blockChain
                    ? BlockHeaderType.FromBlock(blockChain.Tip)
                    : null
                );
            Field <NonNullGraphType <ListGraphType <BlockHeaderType> > >(
                name: "topmostBlocks",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <IntGraphType> >
            {
                Name        = "limit",
                Description = "The number of blocks to get."
            },
                    new QueryArgument <AddressType>
            {
                Name        = "miner",
                Description = "List only blocks mined by the given address.  " +
                              "(List everything if omitted.)",
                DefaultValue = null,
            }
                    ),
                description: "The topmost blocks from the current node.",
                resolve: fieldContext =>
            {
                if (context.BlockChain is null)
                {
                    throw new InvalidOperationException($"{nameof(context.BlockChain)} is null.");
                }

                IEnumerable <Block <NCAction> > blocks =
                    GetTopmostBlocks(context.BlockChain);
                if (fieldContext.GetArgument <Address?>("miner") is { } miner)
                {
                    blocks = blocks.Where(b => b.Miner.Equals(miner));
                }

                return(blocks
                       .Take(fieldContext.GetArgument <int>("limit"))
                       .Select(BlockHeaderType.FromBlock));
            });
            Field <ListGraphType <TxIdType> >(
                name: "stagedTxIds",
                arguments: new QueryArguments(
                    new QueryArgument <AddressType>
            {
                Name        = "address",
                Description = "Target address to query"
            }
                    ),
                description: "Ids of staged transactions from the current node.",
                resolve: fieldContext =>
            {
                if (context.BlockChain is null)
                {
                    throw new InvalidOperationException($"{nameof(context.BlockChain)} is null.");
                }

                if (!fieldContext.HasArgument("address"))
                {
                    return(context.BlockChain.GetStagedTransactionIds());
                }
                else
                {
                    Address address = fieldContext.GetArgument <Address>("address");
                    IImmutableSet <TxId> stagedTransactionIds = context.BlockChain.GetStagedTransactionIds();

                    return(stagedTransactionIds.Where(txId =>
                                                      context.BlockChain.GetTransaction(txId).Signer.Equals(address)));
                }
            }
                );
            Field <NonNullGraphType <BlockHeaderType> >(
                name: "genesis",
                description: "Block header of the genesis block from the current chain.",
                resolve: fieldContext =>
                context.BlockChain is { } blockChain
                        ? BlockHeaderType.FromBlock(blockChain.Genesis)
                        : null
                );
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "isMining",
                description: "Whether the current node is mining.",
                resolve: _ => context.IsMining
                );
        }
Пример #23
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 TransactionHeadlessQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <LongGraphType> >(
                name: "nextTxNonce",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <AddressType> > {
                Name = "address", Description = "Target address to query"
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                Address address = context.GetArgument <Address>("address");
                return(blockChain.GetNextTxNonce(address));
            }
                );

            Field <TransactionType <NCAction> >(
                name: "getTx",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <TxIdType> >
            {
                Name = "txId", Description = "transaction id."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                var txId = context.GetArgument <TxId>("txId");
                return(blockChain.GetTransaction(txId));
            }
                );

            Field <NonNullGraphType <StringGraphType> >(
                name: "createUnsignedTx",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "publicKey",
                Description = "The base64-encoded public key for Transaction.",
            },
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "plainValue",
                Description = "The base64-encoded plain value of action for Transaction.",
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                string plainValueString = context.GetArgument <string>("plainValue");
                var plainValue          = new Bencodex.Codec().Decode(System.Convert.FromBase64String(plainValueString));
#pragma warning disable 612
                var action = new NCAction();
#pragma warning restore 612
                action.LoadPlainValue(plainValue);

                var publicKey  = new PublicKey(Convert.FromBase64String(context.GetArgument <string>("publicKey")));
                Address signer = publicKey.ToAddress();
                long nonce     = blockChain.GetNextTxNonce(signer);
                Transaction <NCAction> unsignedTransaction =
                    Transaction <NCAction> .CreateUnsigned(nonce, publicKey, blockChain.Genesis.Hash, new[] { action });
                return(Convert.ToBase64String(unsignedTransaction.Serialize(false)));
            });

            Field <NonNullGraphType <StringGraphType> >(
                name: "attachSignature",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "unsignedTransaction",
                Description = "The base64-encoded unsigned transaction to attach the given signature."
            },
                    new QueryArgument <NonNullGraphType <StringGraphType> >
            {
                Name        = "signature",
                Description = "The base64-encoded signature of the given unsigned transaction."
            }
                    ),
                resolve: context =>
            {
                byte[] signature = Convert.FromBase64String(context.GetArgument <string>("signature"));
                Transaction <NCAction> unsignedTransaction =
                    Transaction <NCAction> .Deserialize(
                        Convert.FromBase64String(context.GetArgument <string>("unsignedTransaction")),
                        false);
                Transaction <NCAction> signedTransaction = new Transaction <NCAction>(
                    unsignedTransaction.Nonce,
                    unsignedTransaction.Signer,
                    unsignedTransaction.PublicKey,
                    unsignedTransaction.GenesisHash,
                    unsignedTransaction.UpdatedAddresses,
                    unsignedTransaction.Timestamp,
                    unsignedTransaction.Actions,
                    signature);

                return(Convert.ToBase64String(signedTransaction.Serialize(true)));
            });

            Field <NonNullGraphType <TxResultType> >(
                name: "transactionResult",
                arguments: new QueryArguments(
                    new QueryArgument <NonNullGraphType <TxIdType> >
            {
                Name = "txId", Description = "transaction id."
            }
                    ),
                resolve: context =>
            {
                if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
                }

                if (!(standaloneContext.Store is IStore store))
                {
                    throw new ExecutionError(
                        $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.Store)} was not set yet!");
                }

                TxId txId = context.GetArgument <TxId>("txId");
                if (!(store.GetFirstTxIdBlockHashIndex(txId) is { } txExecutedBlockHash))
                {
                    return(blockChain.GetStagedTransactionIds().Contains(txId)
                            ? new TxResult(TxStatus.STAGING, null, null)
                            : new TxResult(TxStatus.INVALID, null, null));
                }

                try
                {
                    TxExecution execution = blockChain.GetTxExecution(txExecutedBlockHash, txId);
                    Block <PolymorphicAction <ActionBase> > txExecutedBlock = blockChain[txExecutedBlockHash];
                    return(execution switch
                    {
                        TxSuccess txSuccess => new TxResult(TxStatus.SUCCESS, txExecutedBlock.Index,
                                                            txExecutedBlock.Hash.ToString()),
                        TxFailure txFailure => new TxResult(TxStatus.FAILURE, txExecutedBlock.Index,
                                                            txExecutedBlock.Hash.ToString()),
                        _ => throw new NotImplementedException(
                            $"{nameof(execution)} is not expected concrete class.")
                    });
                }
                catch (Exception)
                {
                    return(new TxResult(TxStatus.INVALID, null, null));
                }
            }