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 @@
-
-
-