diff --git a/src/Blockcore/Interfaces/INetworkWeight.cs b/src/Blockcore/Interfaces/INetworkWeight.cs new file mode 100644 index 000000000..0037e2397 --- /dev/null +++ b/src/Blockcore/Interfaces/INetworkWeight.cs @@ -0,0 +1,9 @@ +using NBitcoin; + +namespace Blockcore.Interfaces +{ + public interface INetworkWeight + { + double GetPosNetworkWeight(); + } +} diff --git a/src/Features/Blockcore.Features.Miner/Api/Controllers/StakingController.cs b/src/Features/Blockcore.Features.Miner/Api/Controllers/StakingController.cs index 9f348f65f..cdbb4f428 100644 --- a/src/Features/Blockcore.Features.Miner/Api/Controllers/StakingController.cs +++ b/src/Features/Blockcore.Features.Miner/Api/Controllers/StakingController.cs @@ -66,7 +66,7 @@ public IActionResult GetStakingInfo() try { if (!this.fullNode.Network.Consensus.IsProofOfStake) - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not available for Proof of Stake"); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); GetStakingInfoModel model = this.posMinting != null ? this.posMinting.GetGetStakingInfoModel() : new GetStakingInfoModel(); @@ -79,6 +79,31 @@ public IActionResult GetStakingInfo() } } + /// + /// Get staking info from the miner. + /// + /// All staking info details as per the GetStakingInfoModel. + [Route("getnetworkstakinginfo")] + [HttpGet] + public IActionResult GetNetworkStakingInfo() + { + try + { + if (!this.fullNode.Network.Consensus.IsProofOfStake) + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); + + double networkWeight = this.posMinting.GetNetworkWeight(); + double posDifficulty = this.posMinting.GetDifficulty(null); + + return this.Json(new GetNetworkStakingInfoModel { Difficulty = posDifficulty, NetStakeWeight = (long)networkWeight }); + } + catch (Exception e) + { + this.logger.LogError("Exception occurred: {0}", e.ToString()); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()); + } + } + /// /// Start staking. /// @@ -93,7 +118,7 @@ public IActionResult StartStaking([FromBody] StartStakingRequest request) try { if (!this.fullNode.Network.Consensus.IsProofOfStake) - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not available for Proof of Stake"); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); if (!this.ModelState.IsValid) { @@ -139,7 +164,7 @@ public IActionResult StopStaking([FromBody] bool corsProtection = true) try { if (!this.fullNode.Network.Consensus.IsProofOfStake) - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not available for Proof of Stake"); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); this.fullNode.NodeFeature(true).StopStaking(); return this.Ok(); @@ -162,7 +187,7 @@ public IActionResult StakingExpiry([FromBody] StakingExpiryRequest request) try { if (!this.fullNode.Network.Consensus.IsProofOfStake) - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not available for Proof of Stake"); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); if (!this.minerSettings.EnforceStakingFlag) return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Forbidden, "Operation not allowed", "This operation is only allowed if EnforceStakingFlag is true"); @@ -196,7 +221,7 @@ public IActionResult GetStakingNotExpired([FromBody] StakingNotExpiredRequest re try { if (!this.fullNode.Network.Consensus.IsProofOfStake) - return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not available for Proof of Stake"); + return ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method only available for Proof of Stake"); if (!this.minerSettings.EnforceStakingFlag) return ErrorHelpers.BuildErrorResponse(HttpStatusCode.Forbidden, "Operation not allowed", "This operation is only allowed if EnforceStakingFlag is true"); diff --git a/src/Features/Blockcore.Features.Miner/Api/Models/GetNetworkStakingInfoModel.cs b/src/Features/Blockcore.Features.Miner/Api/Models/GetNetworkStakingInfoModel.cs new file mode 100644 index 000000000..7d4bbc56d --- /dev/null +++ b/src/Features/Blockcore.Features.Miner/Api/Models/GetNetworkStakingInfoModel.cs @@ -0,0 +1,20 @@ +using System; +using Newtonsoft.Json; + +namespace Blockcore.Features.Miner.Api.Models +{ + /// + /// Data structure returned by RPC command "getstakinginfo". + /// + public class GetNetworkStakingInfoModel + { + /// Target difficulty that the next block must meet. + [JsonProperty(PropertyName = "difficulty")] + public double Difficulty { get; set; } + + /// Estimation of the total staking weight of all nodes on the network. + [JsonProperty(PropertyName = "netStakeWeight")] + public long NetStakeWeight { get; set; } + + } +} diff --git a/src/Features/Blockcore.Features.Miner/Blockcore.Features.Miner.csproj b/src/Features/Blockcore.Features.Miner/Blockcore.Features.Miner.csproj index 6dc16157c..bbd23358d 100644 --- a/src/Features/Blockcore.Features.Miner/Blockcore.Features.Miner.csproj +++ b/src/Features/Blockcore.Features.Miner/Blockcore.Features.Miner.csproj @@ -18,9 +18,6 @@ - - - 1701;1702;1705;IDE0008; diff --git a/src/Features/Blockcore.Features.Miner/MiningFeature.cs b/src/Features/Blockcore.Features.Miner/MiningFeature.cs index dbd744dfa..36d1e3002 100644 --- a/src/Features/Blockcore.Features.Miner/MiningFeature.cs +++ b/src/Features/Blockcore.Features.Miner/MiningFeature.cs @@ -17,6 +17,7 @@ using Blockcore.Features.RPC; using Blockcore.Features.Wallet; using Blockcore.Features.Wallet.UI; +using Blockcore.Interfaces; using Blockcore.Interfaces.UI; using Blockcore.Mining; using Blockcore.Networks; @@ -256,7 +257,8 @@ public static IFullNodeBuilder AddPowPosMining(this IFullNodeBuilder fullNodeBui .FeatureServices(services => { services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton() + .AddSingleton(provider => (PosMinting)provider.GetService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Features/Blockcore.Features.Miner/Staking/PosMinting.cs b/src/Features/Blockcore.Features.Miner/Staking/PosMinting.cs index 11fc8dbeb..142562692 100644 --- a/src/Features/Blockcore.Features.Miner/Staking/PosMinting.cs +++ b/src/Features/Blockcore.Features.Miner/Staking/PosMinting.cs @@ -71,7 +71,7 @@ namespace Blockcore.Features.Miner.Staking /// and the new value depends on the kernel, it is hard to predict its value in the future. /// /// - public class PosMinting : IPosMinting + public class PosMinting : IPosMinting, INetworkWeight { /// /// Indicates the current state: idle, staking requested, staking in progress and stop staking requested. @@ -1189,6 +1189,11 @@ public double GetNetworkWeight() return res; } + public double GetPosNetworkWeight() + { + return GetNetworkWeight(); + } + /// public GetStakingInfoModel GetGetStakingInfoModel() { diff --git a/src/Features/Blockcore.Features.RPC/Controllers/FullNodeController.cs b/src/Features/Blockcore.Features.RPC/Controllers/FullNodeController.cs index ee65e47ce..f9a6f106a 100644 --- a/src/Features/Blockcore.Features.RPC/Controllers/FullNodeController.cs +++ b/src/Features/Blockcore.Features.RPC/Controllers/FullNodeController.cs @@ -50,6 +50,9 @@ public class FullNodeController : FeatureController /// An interface implementation used to retrieve the network difficulty target. private readonly INetworkDifficulty networkDifficulty; + /// An interface implementation used to retrieve the total network weight. + private readonly INetworkWeight networkWeight; + /// An interface implementation for the blockstore. private readonly IBlockStore blockStore; @@ -73,7 +76,8 @@ public FullNodeController( IConsensusManager consensusManager = null, IBlockStore blockStore = null, IInitialBlockDownloadState ibdState = null, - IStakeChain stakeChain = null) + IStakeChain stakeChain = null, + INetworkWeight networkWeight = null) : base( fullNode: fullNode, network: network, @@ -91,6 +95,7 @@ public FullNodeController( this.blockStore = blockStore; this.ibdState = ibdState; this.stakeChain = stakeChain; + this.networkWeight = networkWeight; } /// @@ -462,6 +467,7 @@ public BlockchainInfoModel GetBlockchainInfo() Headers = (uint)(this.ChainIndexer?.Height ?? 0), BestBlockHash = this.ChainState?.ConsensusTip?.HashBlock, Difficulty = this.GetNetworkDifficulty()?.Difficulty ?? 0.0, + NetworkWeight = (long)this.GetPosNetworkWeight(), MedianTime = this.ChainState?.ConsensusTip?.GetMedianTimePast().ToUnixTimeSeconds() ?? 0, VerificationProgress = 0.0, IsInitialBlockDownload = this.ibdState?.IsInitialBlockDownload() ?? true, @@ -550,5 +556,10 @@ private Target GetNetworkDifficulty() { return this.networkDifficulty?.GetNetworkDifficulty(); } + + private double GetPosNetworkWeight() + { + return this.networkWeight?.GetPosNetworkWeight() ?? 0; + } } } \ No newline at end of file diff --git a/src/Features/Blockcore.Features.RPC/Models/BlockchainInfoModel.cs b/src/Features/Blockcore.Features.RPC/Models/BlockchainInfoModel.cs index 83de2bfe4..16feac040 100644 --- a/src/Features/Blockcore.Features.RPC/Models/BlockchainInfoModel.cs +++ b/src/Features/Blockcore.Features.RPC/Models/BlockchainInfoModel.cs @@ -11,6 +11,7 @@ namespace Blockcore.Features.RPC.Models // * "headers": xxxxxx, (numeric) the current number of headers we have validated // "bestblockhash": "...", (string) the hash of the currently best block // "difficulty": xxxxxx, (numeric) the current difficulty + // "networkWeight: xxxxx, (numeric) the total network weight // * "mediantime": xxxxxx, (numeric) median time for the current best block // * "verificationprogress": xxxx, (numeric) estimate of verification progress[0..1] // * "initialblockdownload": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode. @@ -64,6 +65,9 @@ public class BlockchainInfoModel [JsonProperty(PropertyName = "difficulty")] public double Difficulty { get; set; } + + [JsonProperty(PropertyName = "networkWeight")] + public long NetworkWeight { get; set; } [JsonProperty(PropertyName = "mediantime")] public long MedianTime { get; set; } diff --git a/src/Node/Blockcore.Node/Properties/launchSettings.json b/src/Node/Blockcore.Node/Properties/launchSettings.json index 51416cf1d..8a806e1f8 100644 --- a/src/Node/Blockcore.Node/Properties/launchSettings.json +++ b/src/Node/Blockcore.Node/Properties/launchSettings.json @@ -76,6 +76,10 @@ "commandName": "Project", "commandLineArgs": "--chain=X42 -server -rpcallowip=127.0.0.1 -rpcbind=127.0.0.1 -rpcpassword=rpcpassword -rpcuser=rpcuser -datadir=nodedata -testnet" }, + "XDS (MAIN)": { + "commandName": "Project", + "commandLineArgs": "--chain=XDS -server -rpcallowip=127.0.0.1 -rpcbind=127.0.0.1 -rpcpassword=rpcpassword -rpcuser=rpcuser" + }, "XDS (MAIN/LOCAL)": { "commandName": "Project", "commandLineArgs": "--chain=XDS -server -rpcallowip=127.0.0.1 -rpcbind=127.0.0.1 -rpcpassword=rpcpassword -rpcuser=rpcuser -datadir=nodedata" diff --git a/src/Tests/Blockcore.Features.Miner.Tests/Controllers/StakingControllerTest.cs b/src/Tests/Blockcore.Features.Miner.Tests/Controllers/StakingControllerTest.cs index eefbd2133..9c4322ecb 100644 --- a/src/Tests/Blockcore.Features.Miner.Tests/Controllers/StakingControllerTest.cs +++ b/src/Tests/Blockcore.Features.Miner.Tests/Controllers/StakingControllerTest.cs @@ -262,7 +262,7 @@ public void StartStaking_OnProofOfWorkNetwork_Returns_MethodNotAllowed() ErrorModel error = errorResponse.Errors[0]; Assert.Equal(405, error.Status); - Assert.Equal("Method not available for Proof of Stake", error.Description); + Assert.Equal("Method only available for Proof of Stake", error.Description); } } }