diff --git a/Lib9c b/Lib9c index d5dbcbc8a..39ddae9bc 160000 --- a/Lib9c +++ b/Lib9c @@ -1 +1 @@ -Subproject commit d5dbcbc8a3b7d710f9a25584d9a71ff381962b2d +Subproject commit 39ddae9bc1b873c9a3248f9a090c7ba5d46a06fe diff --git a/NineChronicles.Headless.Executable/Configuration.cs b/NineChronicles.Headless.Executable/Configuration.cs index bc2fbbcf5..2543b2c23 100644 --- a/NineChronicles.Headless.Executable/Configuration.cs +++ b/NineChronicles.Headless.Executable/Configuration.cs @@ -91,10 +91,6 @@ public class Configuration public int? MaxTransactionPerBlock { get; set; } - public string SentryDsn { get; set; } = ""; - - public double SentryTraceSampleRate { get; set; } = 0.01; - public AccessControlServiceOptions? AccessControlService { get; set; } public int ArenaParticipantsSyncInterval { get; set; } = 1000; @@ -147,8 +143,6 @@ public void Overwrite( double? consensusTargetBlockIntervalMilliseconds, int? consensusProposeSecondBase, int? maxTransactionPerBlock, - string? sentryDsn, - double? sentryTraceSampleRate, int? arenaParticipantsSyncInterval, bool? remoteKeyValueService ) @@ -201,8 +195,6 @@ public void Overwrite( ConsensusTargetBlockIntervalMilliseconds = consensusTargetBlockIntervalMilliseconds ?? ConsensusTargetBlockIntervalMilliseconds; ConsensusProposeSecondBase = consensusProposeSecondBase ?? ConsensusProposeSecondBase; MaxTransactionPerBlock = maxTransactionPerBlock ?? MaxTransactionPerBlock; - SentryDsn = sentryDsn ?? SentryDsn; - SentryTraceSampleRate = sentryTraceSampleRate ?? SentryTraceSampleRate; ArenaParticipantsSyncInterval = arenaParticipantsSyncInterval ?? ArenaParticipantsSyncInterval; RemoteKeyValueService = remoteKeyValueService ?? RemoteKeyValueService; } diff --git a/NineChronicles.Headless.Executable/NineChronicles.Headless.Executable.csproj b/NineChronicles.Headless.Executable/NineChronicles.Headless.Executable.csproj index d58c7fbca..da4115593 100644 --- a/NineChronicles.Headless.Executable/NineChronicles.Headless.Executable.csproj +++ b/NineChronicles.Headless.Executable/NineChronicles.Headless.Executable.csproj @@ -6,7 +6,6 @@ 1.0.0 true ..\NineChronicles.Headless.Common.ruleset - true enable Debug;Release;DevEx AnyCPU @@ -30,10 +29,6 @@ - - - - diff --git a/NineChronicles.Headless.Executable/Program.cs b/NineChronicles.Headless.Executable/Program.cs index 00677e2fd..d44dd1486 100644 --- a/NineChronicles.Headless.Executable/Program.cs +++ b/NineChronicles.Headless.Executable/Program.cs @@ -12,7 +12,6 @@ using NineChronicles.Headless.Executable.IO; using NineChronicles.Headless.Properties; using Org.BouncyCastle.Security; -using Sentry; using Serilog; using Serilog.Formatting.Compact; using System; @@ -25,6 +24,7 @@ using System.Net; using System.Net.Http; using System.Reflection; +using System.Runtime; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -32,7 +32,6 @@ using Lib9c.DevExtensions.Action.Loader; using Libplanet.Action; using Libplanet.Action.Loader; -// import necessary for sentry exception filters using Libplanet.Types.Blocks; using Libplanet.Headless; using Libplanet.Headless.Hosting; @@ -216,10 +215,6 @@ public async Task Run( [Option("config", new[] { 'C' }, Description = "Absolute path of \"appsettings.json\" file to provide headless configurations.")] string? configPath = "appsettings.json", - [Option(Description = "Sentry DSN")] - string? sentryDsn = "", - [Option(Description = "Trace sample rate for sentry")] - double? sentryTraceSampleRate = null, [Option(Description = "arena participants list sync interval time")] int? arenaParticipantsSyncInterval = null, [Option(Description = "arena participants list sync enable")] @@ -229,10 +224,6 @@ public async Task Run( [Ignore] CancellationToken? cancellationToken = null ) { -#if SENTRY || ! DEBUG - try - { -#endif var configurationBuilder = new ConfigurationBuilder(); if (Uri.IsWellFormedUriString(configPath, UriKind.Absolute)) { @@ -312,40 +303,9 @@ public async Task Run( txLifeTime, messageTimeout, tipTimeout, demandBuffer, skipPreload, minimumBroadcastTarget, bucketSize, chainTipStaleBehaviorType, txQuotaPerSigner, maximumPollPeers, consensusPort, consensusPrivateKeyString, consensusSeedStrings, consensusTargetBlockIntervalMilliseconds, consensusProposeSecondBase, - maxTransactionPerBlock, sentryDsn, sentryTraceSampleRate, arenaParticipantsSyncInterval, remoteKeyValueService + maxTransactionPerBlock, arenaParticipantsSyncInterval, remoteKeyValueService ); -#if SENTRY || ! DEBUG - loggerConf = loggerConf - .WriteTo.Sentry(o => - { - o.InitializeSdk = false; - }); - - using var _ = SentrySdk.Init(o => - { - o.SendDefaultPii = true; - o.Dsn = headlessConfig.SentryDsn; - // TODO: We need to specify `o.Release` after deciding the version scheme. - // https://docs.sentry.io/workflow/releases/?platform=csharp - //o.Debug = true; - o.Release = Assembly.GetExecutingAssembly().GetCustomAttribute() - ?.InformationalVersion ?? "Unknown"; - o.SampleRate = 0.01f; - o.TracesSampleRate = headlessConfig.SentryTraceSampleRate; - o.AddExceptionFilterForType(); - o.AddExceptionFilterForType(); - o.AddExceptionFilterForType(); - o.AddExceptionFilterForType(); - }); - - // Set global tag - SentrySdk.ConfigureScope(scope => - { - scope.SetTag("host", headlessConfig.Host ?? "no-host"); - }); -#endif - // Clean-up previous temporary log files. if (Directory.Exists("_logs")) { @@ -354,6 +314,8 @@ public async Task Run( Log.Logger = loggerConf.CreateLogger(); + Log.Information("The {0} garbage collector is running.", GCSettings.IsServerGC ? "server" : "workstation"); + if (!headlessConfig.NoMiner && headlessConfig.MinerPrivateKeyString is null) { throw new CommandExitedException( @@ -480,7 +442,6 @@ IActionLoader MakeSingleActionLoader() hostBuilder.ConfigureServices(services => { services.AddSingleton(_ => standaloneContext); - services.AddSingleton>(); services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddService( serviceName: Assembly.GetEntryAssembly()?.GetName().Name ?? "NineChronicles.Headless", @@ -543,7 +504,6 @@ IActionLoader MakeSingleActionLoader() IPAddress.Loopback.ToString(), rpcProperties.RpcListenPort, context, - new ConcurrentDictionary(), arenaMemoryCache ); @@ -574,7 +534,6 @@ IActionLoader MakeSingleActionLoader() IPAddress.Loopback.ToString(), 0, context, - new ConcurrentDictionary(), arenaMemoryCache ); hostBuilder.UseNineChroniclesNode( @@ -625,23 +584,6 @@ IActionLoader MakeSingleActionLoader() { Log.Error(e, "Unexpected exception occurred during Run. {e}", e); } - -#if SENTRY || ! DEBUG - } - catch (CommandExitedException) - { - throw; - } - catch (Exception exceptionToCapture) - { - SentrySdk.CaptureException(exceptionToCapture); - throw; - } -#endif - } - - static void ConfigureSentryOptions(SentryOptions o) - { } } } diff --git a/NineChronicles.Headless.Tests/GraphQLStartupTest.cs b/NineChronicles.Headless.Tests/GraphQLStartupTest.cs index 09eb71a65..8e26a1ecb 100644 --- a/NineChronicles.Headless.Tests/GraphQLStartupTest.cs +++ b/NineChronicles.Headless.Tests/GraphQLStartupTest.cs @@ -27,7 +27,6 @@ public GraphQLStartupTest() "", 0, new RpcContext(), - new ConcurrentDictionary(), new StateMemoryCache() ); _startup = new GraphQLService.GraphQLStartup(_configuration, standaloneContext, publisher); diff --git a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs index 1ab0f3719..0734b01a8 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/ActionQueryTest.cs @@ -1539,5 +1539,56 @@ public async Task RetrieveAvatarAssets() Assert.Equal(avatarAddress, action.AvatarAddress); } + + [Theory] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task IssueToken(bool favExist, bool itemExist) + { + var avatarAddress = new PrivateKey().Address; + var fungibleAssetValues = favExist + ? "[{ticker: \"CRYSTAL\", decimalPlaces: 18, quantity: 100}]" + : "[]"; + var items = itemExist + ? "[{itemId: 500000, count: 100, tradable: true}, {itemId: 500000, count: 100, tradable: false}]" + : "[]"; + var query = $@"{{ + issueToken( + avatarAddress: ""{avatarAddress}"", + fungibleAssetValues: {fungibleAssetValues}, + items: {items} + ) + }}"; + + var queryResult = await ExecuteQueryAsync(query, standaloneContext: _standaloneContext); + var data = (Dictionary)((ExecutionNode)queryResult.Data!).ToValue()!; + var plainValue = _codec.Decode(ByteUtil.ParseHex((string)data["issueToken"])); + Assert.IsType(plainValue); + var actionBase = DeserializeNCAction(plainValue); + var action = Assert.IsType(actionBase); + + Assert.Equal(avatarAddress, action.AvatarAddress); + Assert.Equal(favExist, action.FungibleAssetValues.Any()); + Assert.Equal(itemExist, action.Items.Any()); + + if (favExist) + { + var fav = action.FungibleAssetValues.First(); + Assert.Equal(Currencies.Crystal * 100, fav); + } + + if (itemExist) + { + for (int i = 0; i < action.Items.Count; i++) + { + var (itemId, count, tradable) = action.Items[i]; + Assert.Equal(500000, itemId); + Assert.Equal(100, count); + Assert.Equal(i == 0, tradable); + } + } + } } } diff --git a/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs b/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs index b6ff30cbd..01e1d1889 100644 --- a/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs +++ b/NineChronicles.Headless.Tests/GraphTypes/GraphQLTestBase.cs @@ -113,7 +113,6 @@ public GraphQLTestBase(ITestOutputHelper output) "", 0, new RpcContext(), - new ConcurrentDictionary(), new StateMemoryCache() ); services.AddSingleton(publisher); diff --git a/NineChronicles.Headless/ActionEvaluationPublisher.cs b/NineChronicles.Headless/ActionEvaluationPublisher.cs index bba680fd1..cc3060f00 100644 --- a/NineChronicles.Headless/ActionEvaluationPublisher.cs +++ b/NineChronicles.Headless/ActionEvaluationPublisher.cs @@ -56,7 +56,6 @@ public class ActionEvaluationPublisher : BackgroundService private MemoryCache _memoryCache; private RpcContext _context; - private ConcurrentDictionary _sentryTraces; public ActionEvaluationPublisher( BlockRenderer blockRenderer, @@ -67,7 +66,6 @@ public ActionEvaluationPublisher( string host, int port, RpcContext context, - ConcurrentDictionary sentryTraces, StateMemoryCache cache) { _blockRenderer = blockRenderer; @@ -78,7 +76,6 @@ public ActionEvaluationPublisher( _host = host; _port = port; _context = context; - _sentryTraces = sentryTraces; var memoryCacheOptions = new MemoryCacheOptions(); var options = Options.Create(memoryCacheOptions); _cache = new MemoryCache(options); @@ -137,7 +134,7 @@ public async Task AddClient(Address clientAddress) }; GrpcChannel channel = GrpcChannel.ForAddress($"http://{_host}:{_port}", options); - Client client = await Client.CreateAsync(channel, _blockChainStates, clientAddress, _context, _sentryTraces); + Client client = await Client.CreateAsync(channel, _blockChainStates, clientAddress, _context); if (_clients.TryAdd(clientAddress, client)) { if (clientAddress == default) @@ -401,29 +398,24 @@ private sealed class Client : IAsyncDisposable public ImmutableHashSet
TargetAddresses { get; set; } - public readonly ConcurrentDictionary SentryTraces; - private Client( IActionEvaluationHub hub, IBlockChainStates blockChainStates, Address clientAddress, - RpcContext context, - ConcurrentDictionary sentryTraces) + RpcContext context) { _hub = hub; _blockChainStates = blockChainStates; _clientAddress = clientAddress; _context = context; TargetAddresses = ImmutableHashSet
.Empty; - SentryTraces = sentryTraces; } public static async Task CreateAsync( GrpcChannel channel, IBlockChainStates blockChainStates, Address clientAddress, - RpcContext context, - ConcurrentDictionary sentryTraces) + RpcContext context) { IActionEvaluationHub hub = await StreamingHubClient.ConnectAsync( channel, @@ -431,7 +423,7 @@ public static async Task CreateAsync( ); await hub.JoinAsync(clientAddress.ToHex()); - return new Client(hub, blockChainStates, clientAddress, context, sentryTraces); + return new Client(hub, blockChainStates, clientAddress, context); } public void Subscribe( @@ -530,13 +522,6 @@ await _hub.BroadcastRenderBlockAsync( // FIXME add logger as property Log.Error(e, "[{ClientAddress}] Skip broadcasting render due to the unexpected exception", _clientAddress); } - - if (ev.TxId is TxId txId && SentryTraces.TryRemove(txId.ToString() ?? "", out var sentryTrace)) - { - var span = sentryTrace.GetLastActiveSpan(); - span?.Finish(); - sentryTrace.Finish(); - } } ); diff --git a/NineChronicles.Headless/BlockChainService.cs b/NineChronicles.Headless/BlockChainService.cs index db7e565ad..eb4dcbc72 100644 --- a/NineChronicles.Headless/BlockChainService.cs +++ b/NineChronicles.Headless/BlockChainService.cs @@ -25,7 +25,6 @@ using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.Shared.Services; -using Sentry; using Serilog; using static NineChronicles.Headless.NCActionUtils; using NodeExceptionType = Libplanet.Headless.NodeExceptionType; @@ -42,7 +41,6 @@ public class BlockChainService : ServiceBase, IBlockChainSer private Codec _codec; private LibplanetNodeServiceProperties _libplanetNodeServiceProperties; private ActionEvaluationPublisher _publisher; - private ConcurrentDictionary _sentryTraces; private MemoryCache _memoryCache; public BlockChainService( @@ -51,7 +49,6 @@ public BlockChainService( RpcContext context, LibplanetNodeServiceProperties libplanetNodeServiceProperties, ActionEvaluationPublisher actionEvaluationPublisher, - ConcurrentDictionary sentryTraces, StateMemoryCache cache ) { @@ -61,7 +58,6 @@ StateMemoryCache cache _codec = new Codec(); _libplanetNodeServiceProperties = libplanetNodeServiceProperties; _publisher = actionEvaluationPublisher; - _sentryTraces = sentryTraces; _memoryCache = cache.SheetCache; } @@ -76,13 +72,6 @@ public UnaryResult PutTransaction(byte[] txBytes) ? $"{action}" : "NoAction"; var txId = tx.Id.ToString(); - var sentryTrace = SentrySdk.StartTransaction( - actionName, - "PutTransaction"); - sentryTrace.SetTag("TxId", txId); - var span = sentryTrace.StartChild( - "BroadcastTX", - $"Broadcast Transaction {txId}"); try { @@ -101,17 +90,11 @@ public UnaryResult PutTransaction(byte[] txBytes) Log.Debug("Skip StageTransaction({TxId}) reason: {Msg}", tx.Id, validationExc.Message); } - span.Finish(); - sentryTrace.StartChild( - "ExecuteAction", - $"Execute Action {actionName} from tx {txId}"); - _sentryTraces.TryAdd(txId, sentryTrace); return new UnaryResult(true); } catch (InvalidTxException ite) { Log.Error(ite, $"{nameof(InvalidTxException)} occurred during {nameof(PutTransaction)}(). {{e}}", ite); - sentryTrace.Finish(ite); return new UnaryResult(false); } } diff --git a/NineChronicles.Headless/GraphTypes/ActionQuery.cs b/NineChronicles.Headless/GraphTypes/ActionQuery.cs index 2afe6d606..4c2c6fc0d 100644 --- a/NineChronicles.Headless/GraphTypes/ActionQuery.cs +++ b/NineChronicles.Headless/GraphTypes/ActionQuery.cs @@ -571,6 +571,7 @@ public ActionQuery(StandaloneContext standaloneContext) RegisterSummon(); RegisterClaimItems(); RegisterRetrieveAvatarAssets(); + RegisterIssueToken(); Field>( name: "craftQuery", diff --git a/NineChronicles.Headless/GraphTypes/ActionQueryFields/IssueToken.cs b/NineChronicles.Headless/GraphTypes/ActionQueryFields/IssueToken.cs new file mode 100644 index 000000000..3ba1d3fc6 --- /dev/null +++ b/NineChronicles.Headless/GraphTypes/ActionQueryFields/IssueToken.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using GraphQL; +using GraphQL.Types; +using Libplanet.Crypto; +using Libplanet.Explorer.GraphTypes; +using Libplanet.Types.Assets; +using Nekoyume.Action; +using NineChronicles.Headless.GraphTypes.Input; + +namespace NineChronicles.Headless.GraphTypes +{ + public partial class ActionQuery + { + private void RegisterIssueToken() + { + Field>( + "issueToken", + arguments: new QueryArguments( + new QueryArgument>>> + { + Name = "fungibleAssetValues", + Description = "List of FungibleAssetValues for wrapping token" + }, + new QueryArgument>>> + { + Name = "items", + Description = "List of pair of item id, count for wrapping token" + }, + new QueryArgument> + { + Name = "avatarAddress", + Description = "Avatar address" + } + ), + resolve: context => + { + var fungibleAssetValues = context.GetArgument>("fungibleAssetValues"); + var items = context.GetArgument>("items"); + var avatarAddress = context.GetArgument
("avatarAddress"); + ActionBase action = new IssueToken + { + AvatarAddress = avatarAddress, + FungibleAssetValues = fungibleAssetValues, + Items = items, + }; + return Encode(context, action); + } + ); + } + } +} diff --git a/NineChronicles.Headless/GraphTypes/IssueTokenItemsInputType.cs b/NineChronicles.Headless/GraphTypes/IssueTokenItemsInputType.cs new file mode 100644 index 000000000..024fb5bd1 --- /dev/null +++ b/NineChronicles.Headless/GraphTypes/IssueTokenItemsInputType.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using GraphQL.Types; + +namespace NineChronicles.Headless.GraphTypes.Input +{ + public class IssueTokenItemsInputType : InputObjectGraphType<(int itemId, int count, bool tradable)> + { + public IssueTokenItemsInputType() + { + Name = "IssueTokenItemsInputType"; + + Field>( + name: "itemId", + description: "item ID"); + + Field>( + name: "count", + description: "Count"); + + Field>( + name: "tradable", + description: "item can be tradable"); + } + + public override object ParseDictionary(IDictionary value) + { + var itemId = (int)value["itemId"]!; + var count = (int)value["count"]!; + var tradable = (bool)value["tradable"]!; + return (itemId, count, tradable); + } + } +} diff --git a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs index 4147da88c..cc7ffa66d 100644 --- a/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs +++ b/NineChronicles.Headless/GraphTypes/StandaloneQuery.cs @@ -168,7 +168,6 @@ public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration confi } ); - Field>>>( name: "accountDiffs", description: "This field allows you to query the diffs based accountAddress between two blocks." + @@ -246,7 +245,6 @@ Binary GetAccountState(ITrie model, KeyBytes key) diff.SourceValue, diff.TargetValue)) .ToArray(); - return subDiff; } ); diff --git a/NineChronicles.Headless/NineChronicles.Headless.csproj b/NineChronicles.Headless/NineChronicles.Headless.csproj index 5c2a6bba5..8c1fe7d3b 100644 --- a/NineChronicles.Headless/NineChronicles.Headless.csproj +++ b/NineChronicles.Headless/NineChronicles.Headless.csproj @@ -45,9 +45,6 @@ - - -