protected LibplanetNodeService <T> CreateLibplanetNodeService <T>( Block <T> genesisBlock, AppProtocolVersion appProtocolVersion, PublicKey appProtocolVersionSigner, Progress <PreloadState> preloadProgress = null, IEnumerable <Peer> peers = null) where T : IAction, new() { var properties = new LibplanetNodeServiceProperties <T> { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = appProtocolVersion, GenesisBlock = genesisBlock, StoreStatesCacheSize = 2, PrivateKey = new PrivateKey(), StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), Port = null, MinimumDifficulty = 1024, NoMiner = true, Render = false, Peers = peers ?? ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = ImmutableHashSet <PublicKey> .Empty.Add(appProtocolVersionSigner), }; return(new LibplanetNodeService <T>( properties, blockPolicy: new BlockPolicy <T>(), renderers: new[] { new DummyRenderer <T>() }, minerLoopAction: (chain, swarm, privateKey, _) => Task.CompletedTask, preloadProgress: preloadProgress, exceptionHandlerAction: (code, msg) => throw new Exception($"{code}, {msg}"), preloadStatusHandlerAction: isPreloadStart => { } )); }
protected LibplanetNodeService <T> CreateLibplanetNodeService <T>( Block <T> genesisBlock, AppProtocolVersion appProtocolVersion, PublicKey appProtocolVersionSigner, Progress <PreloadState> preloadProgress = null, IEnumerable <Peer> peers = null) where T : IAction, new() { var properties = new LibplanetNodeServiceProperties <T> { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = appProtocolVersion, GenesisBlock = genesisBlock, StoreStatesCacheSize = 2, PrivateKey = new PrivateKey(), Port = null, MinimumDifficulty = 1024, NoMiner = true, Render = false, Peers = peers ?? ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = ImmutableHashSet <PublicKey> .Empty.Add(appProtocolVersionSigner), }; return(new LibplanetNodeService <T>( properties, new BlockPolicy <T>(), async(chain, swarm, privateKey, cancellationToken) => { }, preloadProgress)); }
public BlockChainService( BlockChain <NCAction> blockChain, Swarm <NCAction> swarm, RpcContext context, LibplanetNodeServiceProperties <NCAction> libplanetNodeServiceProperties ) { _blockChain = blockChain; _delayedRenderer = blockChain.GetDelayedRenderer(); _swarm = swarm; _context = context; _codec = new Codec(); _libplanetNodeServiceProperties = libplanetNodeServiceProperties; }
public BlockChainService( BlockChain <NineChroniclesActionType> blockChain, Swarm <NineChroniclesActionType> swarm, RpcContext context, LibplanetNodeServiceProperties <NineChroniclesActionType> libplanetNodeServiceProperties ) { _blockChain = blockChain; _delayedRenderer = blockChain.Renderers .OfType <DelayedRenderer <NineChroniclesActionType> >() .FirstOrDefault(); _swarm = swarm; _context = context; _codec = new Codec(); _libplanetNodeServiceProperties = libplanetNodeServiceProperties; }
public NineChroniclesNodeService( PrivateKey?minerPrivateKey, LibplanetNodeServiceProperties <NCAction> properties, Progress <PreloadState>?preloadProgress = null, bool ignoreBootstrapFailure = false, bool ignorePreloadFailure = false, bool strictRendering = false, bool authorizedMiner = false, bool isDev = false, int blockInterval = 10000, int reorgInterval = 0, TimeSpan txLifeTime = default, int minerCount = 1 ) { MinerPrivateKey = minerPrivateKey; Properties = properties; LogEventLevel logLevel = LogEventLevel.Debug; var blockPolicySource = new BlockPolicySource(Log.Logger, logLevel); // BlockPolicy shared through Lib9c. IBlockPolicy <NCAction>?blockPolicy = null; // Policies for dev mode. IBlockPolicy <NCAction>?easyPolicy = null; IBlockPolicy <NCAction>?hardPolicy = null; IStagePolicy <NCAction> stagePolicy = txLifeTime == default ? new VolatileStagePolicy <NCAction>() : new VolatileStagePolicy <NCAction>(txLifeTime); if (isDev) { easyPolicy = new ReorgPolicy(new RewardGold(), 1); hardPolicy = new ReorgPolicy(new RewardGold(), 2); } else { blockPolicy = blockPolicySource.GetPolicy(properties.MinimumDifficulty, properties.MaximumTransactions); } BlockRenderer = blockPolicySource.BlockRenderer; ActionRenderer = blockPolicySource.ActionRenderer; ExceptionRenderer = new ExceptionRenderer(); NodeStatusRenderer = new NodeStatusRenderer(); var renderers = new List <IRenderer <NCAction> >(); var strictRenderer = new StrictRenderer(onError: exc => ExceptionRenderer.RenderException( RPCException.InvalidRenderException, exc.Message.Split("\n")[0] ) ); if (Properties.Render) { renderers.Add(blockPolicySource.BlockRenderer); renderers.Add(blockPolicySource.LoggedActionRenderer); } else if (Properties.LogActionRenders) { renderers.Add(blockPolicySource.BlockRenderer); // The following "nullRenderer" does nothing. It's just for filling // the LoggedActionRenderer<T>() constructor's parameter: IActionRenderer <NCAction> nullRenderer = new AnonymousActionRenderer <NCAction>(); renderers.Add( new LoggedActionRenderer <NCAction>( nullRenderer, Log.Logger, logLevel ) ); } else { renderers.Add(blockPolicySource.LoggedBlockRenderer); } if (strictRendering) { Log.Debug( $"Strict rendering is on. Add {nameof(StrictRenderer)}."); renderers.Add(strictRenderer); } async Task minerLoopAction( BlockChain <NCAction> chain, Swarm <NCAction> swarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new Miner(chain, swarm, privateKey, authorizedMiner); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { long nextBlockIndex = chain.Tip.Index + 1; bool authBlock = blockPolicy is BlockPolicy bp // Copied from https://git.io/JLxNd && nextBlockIndex > 0 && nextBlockIndex <= bp.AuthorizedMinersState?.ValidUntil && nextBlockIndex % bp.AuthorizedMinersState?.Interval == 0; if (swarm.Running && ((authorizedMiner && authBlock) || (!authorizedMiner && !authBlock))) { Log.Debug("Start mining."); IEnumerable <Task <Block <NCAction> > > miners = Enumerable .Range(0, minerCount) .Select(_ => miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken)); await Task.WhenAll(miners); } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } async Task devMinerLoopAction( Swarm <NCAction> mainSwarm, Swarm <NCAction> subSwarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { if (mainSwarm.Running) { Log.Debug("Start mining."); await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken); await Task.Delay(blockInterval, cancellationToken); } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } if (isDev) { NodeService = new DevLibplanetNodeService <NCAction>( Properties, easyPolicy, hardPolicy, stagePolicy, renderers, devMinerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure ); } else { NodeService = new LibplanetNodeService <NCAction>( Properties, blockPolicy, stagePolicy, renderers, minerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure, ignorePreloadFailure ); } strictRenderer.BlockChain = NodeService.BlockChain ?? throw new Exception("BlockChain is null."); if (NodeService.BlockChain?.GetState(AuthorizedMinersState.Address) is Dictionary ams && blockPolicy is BlockPolicy bp) { bp.AuthorizedMinersState = new AuthorizedMinersState(ams); } if (authorizedMiner && blockPolicy is BlockPolicy { AuthorizedMinersState : null })
public async Task ActivationStatus(bool existsActivatedAccounts) { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activatedAccounts = ImmutableHashSet <Address> .Empty; if (existsActivatedAccounts) { activatedAccounts = new[] { adminAddress }.ToImmutableHashSet(); } Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( new PolymorphicAction <ActionBase>[] { new InitializeStates( rankingState: new RankingState(), shopState: new ShopState(), gameConfigState: new GameConfigState(), redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), adminAddressState: new AdminState(adminAddress, 1500000), activatedAccountsState: new ActivatedAccountsState(activatedAccounts), goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)), goldDistributions: new GoldDistribution[0], tableSheets: _sheets, pendingActivationStates: new PendingActivationState[] { } ), } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), StoreStatesCacheSize = 2, SwarmPrivateKey = new PrivateKey(), Port = null, MinimumDifficulty = 4096, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, StaticPeers = ImmutableHashSet <Peer> .Empty }; var service = new NineChroniclesNodeService(userPrivateKey, properties, null); StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm?.BlockChain; var blockChain = StandaloneContextFx.BlockChain !; var queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }"); var result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activated"]; // ActivatedAccounts가 비어있을때는 true이고 하나라도 있을경우 false Assert.Equal(!existsActivatedAccounts, result); var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation); blockChain.MakeTransaction(adminPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); action = activationKey.CreateActivateAccount(nonce); blockChain.MakeTransaction(userPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }"); result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activated"]; // ActivatedAccounts에 Address가 추가 되었기 때문에 true Assert.True(result); }
public NineChroniclesNodeService( LibplanetNodeServiceProperties <NineChroniclesActionType> properties, RpcNodeServiceProperties?rpcNodeServiceProperties, Progress <PreloadState> preloadProgress = null, bool ignoreBootstrapFailure = false, bool strictRendering = false, bool isDev = false, int blockInterval = 10000, int reorgInterval = 0 ) { Properties = properties; RpcProperties = rpcNodeServiceProperties; try { Libplanet.Crypto.CryptoConfig.CryptoBackend = new Secp256K1CryptoBackend <SHA256>(); Log.Debug("Secp256K1CryptoBackend initialized."); } catch (Exception e) { Log.Error("Secp256K1CryptoBackend initialize failed. Use default backend. {e}", e); } var blockPolicySource = new BlockPolicySource(Log.Logger, LogEventLevel.Debug); // BlockPolicy shared through Lib9c. IBlockPolicy <PolymorphicAction <ActionBase> > blockPolicy = null; // Policies for dev mode. IBlockPolicy <PolymorphicAction <ActionBase> > easyPolicy = null; IBlockPolicy <PolymorphicAction <ActionBase> > hardPolicy = null; if (isDev) { easyPolicy = new ReorgPolicy(new RewardGold(), 1); hardPolicy = new ReorgPolicy(new RewardGold(), 2); } else { blockPolicy = blockPolicySource.GetPolicy(properties.MinimumDifficulty, properties.MaximumTransactions); } BlockRenderer = blockPolicySource.BlockRenderer; ActionRenderer = blockPolicySource.ActionRenderer; ExceptionRenderer = new ExceptionRenderer(); NodeStatusRenderer = new NodeStatusRenderer(); var renderers = new List <IRenderer <NineChroniclesActionType> >(); var strictRenderer = new StrictRenderer(onError: exc => ExceptionRenderer.RenderException( RPCException.InvalidRenderException, exc.Message.Split("\n")[0] ) ); if (Properties.Render) { renderers.Add(blockPolicySource.BlockRenderer); renderers.Add(blockPolicySource.LoggedActionRenderer); } else { renderers.Add(blockPolicySource.LoggedBlockRenderer); } if (strictRendering) { Log.Debug( $"Strict rendering is on. Add {nameof(StrictRenderer)}."); renderers.Add(strictRenderer); } async Task minerLoopAction( BlockChain <NineChroniclesActionType> chain, Swarm <NineChroniclesActionType> swarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new Miner(chain, swarm, privateKey); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { if (swarm.Running) { Log.Debug("Start mining."); await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken); } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } async Task devMinerLoopAction( Swarm <NineChroniclesActionType> mainSwarm, Swarm <NineChroniclesActionType> subSwarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { if (mainSwarm.Running) { Log.Debug("Start mining."); await miner.MineBlockAsync(properties.MaximumTransactions, cancellationToken); await Task.Delay(blockInterval, cancellationToken); } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } if (isDev) { NodeService = new DevLibplanetNodeService <NineChroniclesActionType>( Properties, easyPolicy, hardPolicy, renderers, devMinerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure ); } else { NodeService = new LibplanetNodeService <NineChroniclesActionType>( Properties, blockPolicy, renderers, minerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure ); } strictRenderer.BlockChain = NodeService?.BlockChain ?? throw new Exception("BlockChain is null."); if (NodeService?.BlockChain?.GetState(AuthorizedMinersState.Address) is Dictionary ams && blockPolicy is BlockPolicy bp) { bp.AuthorizedMinersState = new AuthorizedMinersState(ams); } }
public NineChroniclesNodeService( LibplanetNodeServiceProperties <NineChroniclesActionType> properties, RpcNodeServiceProperties?rpcNodeServiceProperties, Progress <PreloadState> preloadProgress = null, bool ignoreBootstrapFailure = false ) { Properties = properties; RpcProperties = rpcNodeServiceProperties; try { Libplanet.Crypto.CryptoConfig.CryptoBackend = new Secp256K1CryptoBackend <SHA256>(); Log.Debug("Secp256K1CryptoBackend initialized."); } catch (Exception e) { Log.Error("Secp256K1CryptoBackend initialize failed. Use default backend. {e}", e); } // BlockPolicy shared through Lib9c. IBlockPolicy <PolymorphicAction <ActionBase> > blockPolicy = BlockPolicy.GetPolicy( properties.MinimumDifficulty ); async Task minerLoopAction( BlockChain <NineChroniclesActionType> chain, Swarm <NineChroniclesActionType> swarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new Miner(chain, swarm, privateKey); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { if (swarm.Running) { Log.Debug("Start mining."); var block = await miner.MineBlockAsync(cancellationToken); const int txCountThreshold = 10; var txCount = block?.Transactions.Count() ?? 0; if (!(block is null) && txCount >= txCountThreshold) { Log.Error($"Block {block.Index}({block.Hash}) transaction count is {txCount}."); } } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } NodeService = new LibplanetNodeService <NineChroniclesActionType>( Properties, blockPolicy, minerLoopAction, preloadProgress, ignoreBootstrapFailure ); // FIXME: Agent.cs와 중복된 코드입니다. if (BlockPolicy.ActivatedAccounts is null) { var rawState = NodeService?.BlockChain?.GetState(ActivatedAccountsState.Address); BlockPolicy.UpdateActivationSet(rawState); } }
public async Task ActivationKeyNonce_Throw_ExecutionError(string code, string msg) { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activatedAccounts = ImmutableHashSet <Address> .Empty; var pendingActivationStates = new List <PendingActivationState>(); Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( HashAlgorithmType.Of <SHA256>(), new PolymorphicAction <ActionBase>[] { new InitializeStates( rankingState: new RankingState(), shopState: new ShopState(), gameConfigState: new GameConfigState(), redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), adminAddressState: new AdminState(adminAddress, 1500000), activatedAccountsState: new ActivatedAccountsState(activatedAccounts), goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)), goldDistributions: new GoldDistribution[0], tableSheets: _sheets, pendingActivationStates: pendingActivationStates.ToArray() ), } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), StoreStatesCacheSize = 2, SwarmPrivateKey = new PrivateKey(), Port = null, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, StaticPeers = ImmutableHashSet <BoundPeer> .Empty }; var blockPolicy = NineChroniclesNodeService.GetTestBlockPolicy(); var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, NetworkType.Test); StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm?.BlockChain; var query = $"query {{ activationKeyNonce(invitationCode: \"{code}\") }}"; var queryResult = await ExecuteQueryAsync(query); Assert.Single(queryResult.Errors); Assert.Equal(msg, queryResult.Errors.First().Message); }
public async Task ActivateAccount() { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activateAccounts = new[] { adminAddress }.ToImmutableHashSet(); Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( new PolymorphicAction <ActionBase>[] { new InitializeStates() { RankingState = new RankingState(), ShopState = new ShopState(), TableSheetsState = new TableSheetsState(), GameConfigState = new GameConfigState(), RedeemCodeState = new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), AdminAddressState = new AdminState(adminAddress, 1500000), ActivatedAccountsState = new ActivatedAccountsState(activateAccounts), GoldCurrencyState = new GoldCurrencyState(new Currency("NCG", minter: null)), GoldDistributions = new GoldDistribution[0], }, } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = null, StoreStatesCacheSize = 2, PrivateKey = userPrivateKey, Port = null, MinimumDifficulty = 4096, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, }; var service = new NineChroniclesNodeService(properties, null); service.PrivateKey = userPrivateKey; StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm.BlockChain; var blockChain = StandaloneContextFx.BlockChain; var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation); blockChain.MakeTransaction(adminPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); var encodedActivationKey = activationKey.Encode(); var queryResult = await ExecuteQueryAsync( $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}"); await blockChain.MineBlock(adminAddress); var result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activateAccount"]; Assert.True(result); var state = (Bencodex.Types.Dictionary)blockChain.GetState( ActivatedAccountsState.Address); var activatedAccountsState = new ActivatedAccountsState(state); var userAddress = userPrivateKey.ToAddress(); Assert.True(activatedAccountsState.Accounts.Contains(userAddress)); }
public NineChroniclesNodeService( PrivateKey?minerPrivateKey, LibplanetNodeServiceProperties <NCAction> properties, IBlockPolicy <NCAction> blockPolicy, NetworkType networkType, Progress <PreloadState>?preloadProgress = null, bool ignoreBootstrapFailure = false, bool ignorePreloadFailure = false, bool strictRendering = false, bool authorizedMiner = false, bool isDev = false, int blockInterval = 10000, int reorgInterval = 0, TimeSpan txLifeTime = default, int minerCount = 1, int txQuotaPerSigner = 10 ) { MinerPrivateKey = minerPrivateKey; Properties = properties; LogEventLevel logLevel = LogEventLevel.Debug; var blockPolicySource = new BlockPolicySource(Log.Logger, logLevel); // Policies for dev mode. IBlockPolicy <NCAction>?easyPolicy = null; IBlockPolicy <NCAction>?hardPolicy = null; IStagePolicy <NCAction> stagePolicy = new StagePolicy(txLifeTime, txQuotaPerSigner); if (isDev) { easyPolicy = new ReorgPolicy(new RewardGold(), 1); hardPolicy = new ReorgPolicy(new RewardGold(), 2); } BlockRenderer = blockPolicySource.BlockRenderer; ActionRenderer = blockPolicySource.ActionRenderer; ExceptionRenderer = new ExceptionRenderer(); NodeStatusRenderer = new NodeStatusRenderer(); var renderers = new List <IRenderer <NCAction> >(); var strictRenderer = new StrictRenderer(onError: exc => ExceptionRenderer.RenderException( RPCException.InvalidRenderException, exc.Message.Split("\n")[0] ) ); if (Properties.Render) { renderers.Add(blockPolicySource.BlockRenderer); renderers.Add(blockPolicySource.LoggedActionRenderer); } else if (Properties.LogActionRenders) { renderers.Add(blockPolicySource.BlockRenderer); // The following "nullRenderer" does nothing. It's just for filling // the LoggedActionRenderer<T>() constructor's parameter: IActionRenderer <NCAction> nullRenderer = new AnonymousActionRenderer <NCAction>(); renderers.Add( new LoggedActionRenderer <NCAction>( nullRenderer, Log.Logger, logLevel ) ); } else { renderers.Add(blockPolicySource.LoggedBlockRenderer); } if (strictRendering) { Log.Debug( $"Strict rendering is on. Add {nameof(StrictRenderer)}."); renderers.Add(strictRenderer); } async Task minerLoopAction( BlockChain <NCAction> chain, Swarm <NCAction> swarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new Miner(chain, swarm, privateKey, authorizedMiner); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { long nextBlockIndex = chain.Tip.Index + 1; if (swarm.Running) { Log.Debug("Start mining."); if (chain.Policy is BlockPolicy bp) { if (bp.IsAllowedToMine(privateKey.ToAddress(), chain.Count)) { IEnumerable <Task <Block <NCAction> > > miners = Enumerable .Range(0, minerCount) .Select(_ => miner.MineBlockAsync(cancellationToken)); await Task.WhenAll(miners); } else { Log.Debug( "Miner {MinerAddress} is not allowed to mine a block with index {Index} " + "under current policy.", privateKey.ToAddress(), chain.Count); await Task.Delay(1000, cancellationToken); } } else { Log.Error( "No suitable policy was found for chain {ChainId}.", chain.Id); await Task.Delay(1000, cancellationToken); } } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } async Task devMinerLoopAction( Swarm <NCAction> mainSwarm, Swarm <NCAction> subSwarm, PrivateKey privateKey, CancellationToken cancellationToken) { var miner = new ReorgMiner(mainSwarm, subSwarm, privateKey, reorgInterval); Log.Debug("Miner called."); while (!cancellationToken.IsCancellationRequested) { try { if (mainSwarm.Running) { Log.Debug("Start mining."); await miner.MineBlockAsync(cancellationToken); await Task.Delay(blockInterval, cancellationToken); } else { await Task.Delay(1000, cancellationToken); } } catch (Exception ex) { Log.Error(ex, "Exception occurred."); } } } if (isDev) { NodeService = new DevLibplanetNodeService <NCAction>( Properties, easyPolicy, hardPolicy, stagePolicy, renderers, devMinerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure ); } else { NodeService = new LibplanetNodeService <NCAction>( Properties, blockPolicy, stagePolicy, renderers, minerLoopAction, preloadProgress, (code, msg) => { ExceptionRenderer.RenderException(code, msg); Log.Error(msg); }, isPreloadStarted => { NodeStatusRenderer.PreloadStatus(isPreloadStarted); }, ignoreBootstrapFailure, ignorePreloadFailure ); } strictRenderer.BlockChain = NodeService.BlockChain ?? throw new Exception("BlockChain is null."); }