Skip to content

Commit

Permalink
Merge pull request #3 from mayoreee/feat/masternode_rpc_calls
Browse files Browse the repository at this point in the history
Updated Dash Masternode RPC Calls
  • Loading branch information
QuantumExplorer authored Aug 2, 2022
2 parents 8d3c4f4 + df7f3e6 commit 81ec1a9
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ use dashcore_rpc::{Auth, Client, RpcApi};

fn main() {

let rpc = Client::new("localhost:19998".to_string(),
let rpc = Client::new(
"localhost:19998",
Auth::UserPass("<FILL RPC USERNAME>".to_string(),
"<FILL RPC PASSWORD>".to_string())).unwrap();
let best_block_hash = rpc.get_best_block_hash().unwrap();
Expand Down
26 changes: 24 additions & 2 deletions client/examples/connect_to_masternode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use dashcore_rpc::{Auth, Client, RpcApi};

fn main() {
let rpc = Client::new(
"localhost:19998".to_string(),
"localhost:19998",
Auth::UserPass("dashrpc".to_string(), "rpcpassword".to_string()),
)
.unwrap();
Expand All @@ -23,9 +23,31 @@ fn main() {

// Get block hash (for the a specified block height)
let block_hash = rpc.get_block_hash(block_count).unwrap();
println!("\n\nBlock hash at block height \n{}: {}", block_count, block_hash);
println!("\n\nBlock hash at block height {}: \n{}", block_count, block_hash);

// Get masternode count
let masternode_count = rpc.get_masternode_count().unwrap();
println!("\n\nMasternode Count: \n{:?}", masternode_count);


// Get masternode list
let mn_list = rpc.get_masternode_list(Some("json"), None).unwrap();
println!("\n\nMasternode List: \n{:?}", mn_list);

// Get masternode outputs
let mn_outputs = rpc.get_masternode_outputs().unwrap();
println!("\n\nMasternode Outputs: \n{:?}", mn_outputs);

// Get masternode payments
let mn_payments = rpc.get_masternode_payments(None, None).unwrap();
println!("\n\nMasternode Payments: \n{:?}", mn_payments);

// Get masternode status
let mn_status = rpc.get_masternode_status().unwrap();
println!("\n\nMasternode Status: \n{:?}", mn_status);

// Get masternode winners
let mn_winners = rpc.get_masternode_winners(None, None).unwrap();
println!("\n\nMasternode Winners: \n{:?}", mn_winners);

}
34 changes: 33 additions & 1 deletion client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,9 +1168,41 @@ pub trait RpcApi: Sized {
fn get_masternode_count(&self) -> Result<json::GetMasternodeCountResult> {
self.call("masternode", &["count".into()])
}


/// Returns a list of known masternodes
fn get_masternode_list(&self, mode: Option<&str>, filter: Option<&str>) -> Result<HashMap<String, json::Masternode>>{
let mut args = ["list".into(), into_json(mode)?, opt_into_json(filter)?];
self.call::<HashMap<String, json::Masternode>>("masternode", handle_defaults(&mut args, &["json".into(), null()]))
}

/// Returns masternode compatible outputs
fn get_masternode_outputs(&self) -> Result<HashMap<String, String>>{
let mut args = ["outputs".into()];
self.call::<HashMap<String, String>>("masternode", handle_defaults(&mut args, &[null()]))
}

/// Returns an array of deterministic masternodes and their payments for the specified block
fn get_masternode_payments(&self, block_hash: Option<&str>, count: Option<&str>) -> Result<Vec<json::GetMasternodePaymentsResult>>{
let mut args = ["payments".into(), opt_into_json(block_hash)?, opt_into_json(count)?];
self.call::<Vec<json::GetMasternodePaymentsResult>>("masternode", handle_defaults(&mut args, &[null(), null()]))
}

/// Returns masternode status information
fn get_masternode_status(&self) -> Result<json::MasternodeStatus> {
self.call("masternode", &["status".into()])
}

/// Returns the list of masternode winners
fn get_masternode_winners(&self, count: Option<&str>, filter: Option<&str>) -> Result<HashMap<String, String>> {
let mut args = ["winners".into(), opt_into_json(count)?, opt_into_json(filter)?];
self.call::<HashMap<String, String>>("masternode", handle_defaults(&mut args, &["10".into(), null()]))
}


}

/// Client implements a JSON-RPC client for the Bitcoin Core daemon or compatible APIs.
/// Client implements a JSON-RPC client for the Dash Core daemon or compatible APIs.
pub struct Client {
client: jsonrpc::client::Client,
}
Expand Down
42 changes: 42 additions & 0 deletions integration_test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ fn main() {
test_add_ban(&cl);
test_set_network_active(&cl);
test_stop(cl);
test_get_masternode_count(cl);
test_get_masternode_list(cl);
test_get_masternode_outputs(cl);
test_get_masternode_payments(cl);
test_get_masternode_status(cl);
test_get_masternode_winners(cl);
}

fn test_get_network_info(cl: &Client) {
Expand Down Expand Up @@ -1136,3 +1142,39 @@ fn test_getblocktemplate(cl: &Client) {
fn test_stop(cl: Client) {
println!("Stopping: '{}'", cl.stop().unwrap());
}


// ---------------------- Masternode RPC tests---------------------

fn test_get_masternode_count(cl: &Client) {
let masternode_count = rpc.get_masternode_count().unwrap();
assert!(masternode_count.total > 0);
assert!(masternode_count.enabled > 0);
assert!(masternode_count.total >= masternode_count.enabled);
}

fn test_get_masternode_list(cl: &Client) {
let masternode_list = rpc.get_masternode_list(Some("json"), None).unwrap();
}

fn test_get_masternode_outputs(cl: &Client) {
let masternode_outputs = rpc.get_masternode_outputs().unwrap();
}

fn test_get_masternode_payments(cl: &Client) {
let masternode_payments = rpc.get_masternode_payments(None, None).unwrap();
assert!(masternode_payments[0].height > 0);
assert!(masternode_payments[0].amount > 0);
assert!(masternode_payments[0].masternodes[0].amount > 0);
assert!(masternode_payments[0].masternodes[0].payees[0].amount > 0);
assert_eq!(masternode_payments[0].amount, masternode_payments[0].masternodes[0].amount);
assert_eq!(masternode_payments[0].amount, masternode_payments[0].masternodes[0].payees[0].amount);
}

fn test_get_masternode_status(cl: &Client) {
let masternode_status = rpc.get_masternode_status().unwrap();
}

fn test_get_masternode_winners(cl: &Client) {
let masternode_winners = rpc.get_masternode_winners(None, None).unwrap();
}
1 change: 1 addition & 0 deletions json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ path = "src/lib.rs"
[dependencies]
serde = { version = "1", features = [ "derive" ] }
serde_json = "1"
serde_with = "2.0.0"

dashcore = { git = "https://github.com/dashevo/rust-dashcore", branch = "master", features = [ "use-serde" ] }
159 changes: 159 additions & 0 deletions json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub extern crate dashcore;
#[macro_use] // `macro_use` is needed for v1.24.0 compilation.
extern crate serde;
extern crate serde_json;
extern crate serde_with;

use std::collections::HashMap;

Expand All @@ -31,7 +32,9 @@ use dashcore::util::{bip158, bip32};
use dashcore::{Address, Amount, PrivateKey, PublicKey, Script, SignedAmount, Transaction};
use serde::de::Error as SerdeError;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr, Bytes};
use std::fmt;
use std::net::{SocketAddr};

//TODO(stevenroose) consider using a Time type

Expand Down Expand Up @@ -1986,6 +1989,118 @@ pub struct GetMasternodeCountResult {
pub enabled: u32,
}

#[serde_as]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct Masternode {
#[serde(rename = "proTxHash", with = "::serde_hex")]
pub pro_tx_hash: Vec<u8>,
#[serde_as(as = "DisplayFromStr")]
pub address: SocketAddr,
#[serde_as(as = "Bytes")]
pub payee: Vec<u8>,
pub status: String,
#[serde(rename = "lastpaidtime")]
pub last_paid_time: u32,
#[serde(rename = "lastpaidblock")]
pub last_paid_block: u32,
#[serde_as(as = "Bytes")]
#[serde(rename = "owneraddress")]
pub owner_address: Vec<u8>,
#[serde_as(as = "Bytes")]
#[serde(rename = "votingaddress")]
pub voting_address: Vec<u8>,
#[serde_as(as = "Bytes")]
#[serde(rename = "collateraladdress")]
pub collateral_address: Vec<u8>,
#[serde_as(as = "Bytes")]
#[serde(rename = "pubkeyoperator")]
pub pubkey_operator: Vec<u8>,
}

#[serde_as]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct Payee {
#[serde_as(as = "Bytes")]
pub address: Vec<u8>,
pub script: Script,
pub amount: u32,
}

#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct MasternodePayment {
#[serde(rename = "proTxHash", with = "::serde_hex")]
pub pro_tx_hash: Vec<u8>,
pub amount: u32,
pub payees: Vec<Payee>,
}

#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct GetMasternodePaymentsResult {
pub height: u32,
#[serde(rename = "blockhash")]
pub block_hash: dashcore::BlockHash,
pub amount: u32,
pub masternodes: Vec<MasternodePayment>,
}

#[serde_as]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DMNState {
#[serde_as(as = "DisplayFromStr")]
pub service: SocketAddr,
pub registered_height: u32,
pub last_paid_height: u32,
#[serde(rename = "PoSePenalty")]
pub pose_penalty: u32,
#[serde(rename = "PoSeRevivedHeight")]
pub pose_revived_height: u32,
#[serde(rename = "PoSeBanHeight")]
pub pose_ban_height: u32,
pub revocation_reason: u32,
#[serde_as(as = "Bytes")]
pub owner_address: Vec<u8>,
#[serde_as(as = "Bytes")]
pub voting_address: Vec<u8>,
#[serde_as(as = "Bytes")]
pub payout_address: Vec<u8>,
#[serde_as(as = "Bytes")]
pub pub_key_operator: Vec<u8>,
}

#[serde(untagged)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub enum MasternodeState{
MASTERNODE_WAITING_FOR_PROTX,
MASTERNODE_POSE_BANNED,
MASTERNODE_REMOVED,
MASTERNODE_OPERATOR_KEY_CHANGED,
MASTERNODE_PROTX_IP_CHANGED,
MASTERNODE_READY,
MASTERNODE_ERROR,
UNKNOWN,
NONRECOGNISED,
}

#[serde_as]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub struct MasternodeStatus {
#[serde(default, deserialize_with = "deserialize_outpoint")]
pub outpoint: dashcore::OutPoint,
#[serde_as(as = "DisplayFromStr")]
pub service: SocketAddr,
#[serde(rename = "proTxHash", with = "::serde_hex")]
pub pro_tx_hash: Vec<u8>,
#[serde(rename = "collateralHash", with = "::serde_hex")]
pub collateral_hash: Vec<u8>,
#[serde(rename = "collateralIndex")]
pub collateral_index: u32,
#[serde(rename = "dmnState")]
pub dmn_state: DMNState,
#[serde(deserialize_with = "deserialize_mn_state")]
pub state: MasternodeState,
pub status: String,
}

// Custom deserializer functions.

Expand All @@ -2005,3 +2120,47 @@ where
Ok(Some(res))
}

/// deserialize_outpoint deserializes a hex-encoded outpoint
fn deserialize_outpoint<'de, D>(deserializer: D) -> Result<dashcore::OutPoint, D::Error>
where
D: serde::Deserializer<'de>,
{
let str_sequence = String::deserialize(deserializer)?;
let str_array: Vec<String> = str_sequence
.split('-')
.map(|item| item.to_owned())
.collect();

let txid: dashcore::Txid = dashcore::Txid::from_hex(&str_array[0]).unwrap();
let vout: u32 = str_array[1].parse().unwrap();

let outpoint = dashcore::OutPoint{
txid: txid,
vout: vout,
};
Ok(outpoint)
}

/// deserialize_mn_state deserializes a masternode state
fn deserialize_mn_state<'de, D>(deserializer: D) -> Result<MasternodeState, D::Error>
where
D: serde::Deserializer<'de>,
{
let str_sequence = String::deserialize(deserializer)?;

Ok(
match str_sequence.as_str() {
"WAITING_FOR_PROTX" => MasternodeState::MASTERNODE_WAITING_FOR_PROTX,
"POSE_BANNED" => MasternodeState::MASTERNODE_POSE_BANNED,
"REMOVED" => MasternodeState::MASTERNODE_REMOVED,
"OPERATOR_KEY_CHANGED" => MasternodeState::MASTERNODE_OPERATOR_KEY_CHANGED,
"PROTX_IP_CHANGED" => MasternodeState::MASTERNODE_PROTX_IP_CHANGED,
"READY" => MasternodeState::MASTERNODE_READY,
"ERROR" => MasternodeState::MASTERNODE_ERROR,
"UNKNOWN" => MasternodeState::UNKNOWN,
_ => MasternodeState::NONRECOGNISED,
}
)
}


0 comments on commit 81ec1a9

Please sign in to comment.