Skip to content

Commit

Permalink
Merge pull request #1700 from longfin/rc-v100321-3
Browse files Browse the repository at this point in the history
Release v100321-3
  • Loading branch information
longfin authored Nov 17, 2022
2 parents cf6652c + b018534 commit 7c396f7
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 54 deletions.
26 changes: 26 additions & 0 deletions NineChronicles.Headless/GraphTypes/NodeStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using NCAction = Libplanet.Action.PolymorphicAction<Nekoyume.Action.ActionBase>;

namespace NineChronicles.Headless.GraphTypes
{
public class NodeStatusType : ObjectGraphType<NodeStatusType>
{
private static readonly string _productVersion =
Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Unknown";

private static readonly string _informationalVersion =
Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion ?? "Unknown";

public bool BootstrapEnded { get; set; }

public bool PreloadEnded { get; set; }
Expand Down Expand Up @@ -129,6 +137,24 @@ context.BlockChain is { } blockChain
Field<AppProtocolVersionType>(
"appProtocolVersion",
resolve: _ => context.NineChroniclesNodeService?.Swarm.AppProtocolVersion);

Field<ListGraphType<AddressType>>(
name: "subscriberAddresses",
description: "A list of subscribers' address",
resolve: _ => context.AgentAddresses.Keys
);

Field<StringGraphType>(
name: "productVersion",
description: "A version of NineChronicles.Headless",
resolve: _ => _productVersion
);

Field<StringGraphType>(
name: "informationalVersion",
description: "A informational version (a.k.a. version suffix) of NineChronicles.Headless",
resolve: _ => _informationalVersion
);
}

private IEnumerable<Block<T>> GetTopmostBlocks<T>(BlockChain<T> blockChain, int offset)
Expand Down
143 changes: 89 additions & 54 deletions NineChronicles.Headless/GraphTypes/StandaloneSubscription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
using Libplanet.Headless;
using Libplanet.Net;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Bencodex.Types;
Expand All @@ -22,9 +22,8 @@
using Nekoyume.Model.State;
using Nekoyume.TableData;
using NineChronicles.Headless.GraphTypes.States;
using Serilog;
using Libplanet.Blockchain;
using Libplanet.Blockchain.Renderers;
using Serilog;

namespace NineChronicles.Headless.GraphTypes
{
Expand Down Expand Up @@ -102,6 +101,10 @@ public PreloadStateType()
}
}

private const int _blockRenderDegreeOfParallelism = 8;

private BlockHeader? _tipHeader;

private ISubject<TipChanged> _subject = new ReplaySubject<TipChanged>();

private StandaloneContext StandaloneContext { get; }
Expand Down Expand Up @@ -226,13 +229,20 @@ public StandaloneSubscription(StandaloneContext standaloneContext)
});

BlockRenderer blockRenderer = standaloneContext.NineChroniclesNodeService!.BlockRenderer;
blockRenderer.BlockSubject.Subscribe(RenderBlock);
blockRenderer.BlockSubject
.ObserveOn(NewThreadScheduler.Default)
.Subscribe(RenderBlock);

ActionRenderer actionRenderer = standaloneContext.NineChroniclesNodeService!.ActionRenderer;
actionRenderer.EveryRender<ActionBase>().Subscribe(RenderAction);
actionRenderer.EveryRender<MonsterCollect>().Subscribe(RenderMonsterCollectionStateSubject);
actionRenderer.EveryRender<CancelMonsterCollect>().Subscribe(RenderMonsterCollectionStateSubject);
actionRenderer.EveryRender<ClaimMonsterCollectionReward>().Subscribe(RenderMonsterCollectionStateSubject);
actionRenderer.EveryRender<MonsterCollect>()
.ObserveOn(NewThreadScheduler.Default)
.Subscribe(RenderMonsterCollectionStateSubject);
actionRenderer.EveryRender<CancelMonsterCollect>()
.ObserveOn(NewThreadScheduler.Default)
.Subscribe(RenderMonsterCollectionStateSubject);
actionRenderer.EveryRender<ClaimMonsterCollectionReward>()
.ObserveOn(NewThreadScheduler.Default)
.Subscribe(RenderMonsterCollectionStateSubject);
}

private IObservable<MonsterCollectionState> SubscribeMonsterCollectionState(IResolveEventStreamContext context)
Expand Down Expand Up @@ -279,73 +289,93 @@ private IObservable<TipChanged> SubscribeTipChanged(IResolveEventStreamContext c
}
private void RenderBlock((Block<PolymorphicAction<ActionBase>> OldTip, Block<PolymorphicAction<ActionBase>> NewTip) pair)
{
_subject.OnNext(new TipChanged
{
Index = pair.NewTip.Index,
Hash = pair.NewTip.Hash,
}
_tipHeader = pair.NewTip.Header;
_subject.OnNext(
new TipChanged
{
Index = pair.NewTip.Index,
Hash = pair.NewTip.Hash,
}
);

if (StandaloneContext.NineChroniclesNodeService is null)
{
throw new InvalidOperationException(
$"{nameof(StandaloneContext.NineChroniclesNodeService)} is null.");
}

var sw = new System.Diagnostics.Stopwatch();
sw.Start();
Log.Debug("StandaloneSubscription.RenderBlock started");

BlockChain<PolymorphicAction<ActionBase>> blockChain = StandaloneContext.NineChroniclesNodeService.BlockChain;
DelayedRenderer<PolymorphicAction<ActionBase>>? delayedRenderer = blockChain.GetDelayedRenderer();
BlockHash? offset = delayedRenderer?.Tip?.Hash;
Currency currency =
new GoldCurrencyState(
(Dictionary)blockChain.GetState(Addresses.GoldCurrency, offset)
(Dictionary)blockChain.GetState(Addresses.GoldCurrency, _tipHeader.Hash)
).Currency;
var rewardSheet = new MonsterCollectionRewardSheet();
var csv = blockChain.GetState(
Addresses.GetSheetAddress<MonsterCollectionRewardSheet>(),
offset
_tipHeader.Hash
).ToDotnetString();
rewardSheet.Set(csv);
foreach (var (address, subjects) in StandaloneContext.AgentAddresses)
{
FungibleAssetValue agentBalance = blockChain.GetBalance(address, currency, offset);
subjects.balanceSubject.OnNext(agentBalance.GetQuantityString(true));
if (blockChain.GetState(address, offset) is Dictionary rawAgent)
Log.Debug($"StandaloneSubscription.RenderBlock target addresses. (count: {StandaloneContext.AgentAddresses.Count})");
StandaloneContext.AgentAddresses
.AsParallel()
.WithDegreeOfParallelism(_blockRenderDegreeOfParallelism)
.ForAll(kv =>
{
AgentState agentState = new AgentState(rawAgent);
Address deriveAddress =
MonsterCollectionState.DeriveAddress(address, agentState.MonsterCollectionRound);
if (blockChain.GetState(deriveAddress, offset) is Dictionary collectDict &&
agentState.avatarAddresses.Any())
{
long tipIndex = blockChain.Tip.Index;
var monsterCollectionState = new MonsterCollectionState(collectDict);
List<MonsterCollectionRewardSheet.RewardInfo> rewards = monsterCollectionState.CalculateRewards(
rewardSheet,
tipIndex
);
Address address = kv.Key;
(ReplaySubject<MonsterCollectionStatus> statusSubject, _, ReplaySubject<string> balanceSubject) =
kv.Value;
RenderForAgent(
blockChain,
_tipHeader,
address,
currency,
statusSubject,
balanceSubject,
rewardSheet
);
});

var monsterCollectionStatus = new MonsterCollectionStatus(
agentBalance,
rewards,
tipIndex,
monsterCollectionState.IsLocked(tipIndex)
);
subjects.statusSubject.OnNext(monsterCollectionStatus);
}
}
}
sw.Stop();
Log.Debug($"StandaloneSubscription.RenderBlock ended. elapsed: {sw.Elapsed}");
}

private void RenderAction(ActionBase.ActionEvaluation<ActionBase> eval)
private void RenderForAgent(
BlockChain<PolymorphicAction<ActionBase>> blockChain,
BlockHeader tipHeader,
Address address,
Currency currency,
ReplaySubject<MonsterCollectionStatus> statusSubject,
ReplaySubject<string> balanceSubject,
MonsterCollectionRewardSheet rewardSheet)
{
if (StandaloneContext.NineChroniclesNodeService is null)
FungibleAssetValue agentBalance = blockChain.GetBalance(address, currency, tipHeader.Hash);
balanceSubject.OnNext(agentBalance.GetQuantityString(true));
if (blockChain.GetState(address, tipHeader.Hash) is Dictionary rawAgent)
{
throw new InvalidOperationException(
$"{nameof(StandaloneContext.NineChroniclesNodeService)} is null.");
}
AgentState agentState = new AgentState(rawAgent);
Address deriveAddress =
MonsterCollectionState.DeriveAddress(address, agentState.MonsterCollectionRound);
if (agentState.avatarAddresses.Any() &&
blockChain.GetState(deriveAddress, tipHeader.Hash) is Dictionary collectDict)
{
var monsterCollectionState = new MonsterCollectionState(collectDict);
List<MonsterCollectionRewardSheet.RewardInfo> rewards = monsterCollectionState.CalculateRewards(
rewardSheet,
tipHeader.Index
);

if (StandaloneContext.NineChroniclesNodeService.MinerPrivateKey is null)
{
Log.Information("PrivateKey is not set. please call SetPrivateKey() first");
var monsterCollectionStatus = new MonsterCollectionStatus(
agentBalance,
rewards,
tipHeader.Index,
monsterCollectionState.IsLocked(tipHeader.Index)
);
statusSubject.OnNext(monsterCollectionStatus);
}
}
}

Expand All @@ -358,11 +388,16 @@ private void RenderMonsterCollectionStateSubject<T>(ActionBase.ActionEvaluation<
$"{nameof(NineChroniclesNodeService)} is null.");
}

// Skip when error.
if (eval.Exception is { })
{
return;
}

foreach (var (address, subjects) in StandaloneContext.AgentAddresses)
{
if (eval.Signer.Equals(address) &&
eval.Exception is null &&
service.BlockChain.GetState(address) is Dictionary agentDict)
service.BlockChain.GetState(address, _tipHeader?.Hash) is Dictionary agentDict)
{
var agentState = new AgentState(agentDict);
Address deriveAddress = MonsterCollectionState.DeriveAddress(address, agentState.MonsterCollectionRound);
Expand Down

0 comments on commit 7c396f7

Please sign in to comment.