Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Dash Masternode RPC Calls #3

Merged
merged 26 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f40b61e
fix: replace name
mayoreee Jul 26, 2022
de1b9a6
tests: add get masternode count test
mayoreee Jul 26, 2022
b26988e
tests: update masternode count test
mayoreee Jul 26, 2022
ca304a8
feat: add get masternode list
mayoreee Jul 28, 2022
b3b5991
fix: masternode list test
mayoreee Jul 28, 2022
85df6a1
feat: add get masternode outputs
mayoreee Jul 28, 2022
5a095f5
feat: add masternode payments
mayoreee Jul 28, 2022
d543264
feat: add masternode status
mayoreee Jul 28, 2022
4165fa1
feat: add masternode winners
mayoreee Jul 28, 2022
45e9f11
fix: swap `to_string()` for string literal type `&str`
mayoreee Jul 28, 2022
dd94519
docs: update example script
mayoreee Jul 28, 2022
e9325e3
tests: add masternode winners
mayoreee Jul 28, 2022
0969664
fix: rename data structures and types
mayoreee Jul 28, 2022
7a39ebe
fix: proTxHash decoded to array bytes
mayoreee Jul 29, 2022
1ba5638
fix: deserialize `dashcore::Outpoint` struct
mayoreee Jul 29, 2022
ca72938
fix: deserialize masternode address
mayoreee Jul 29, 2022
c2e72e5
fix: deserialize `payoutAddress` and `votingAddress`
mayoreee Jul 29, 2022
63225e6
fix: deserialize socket address
mayoreee Jul 29, 2022
914982e
fix: rename types
mayoreee Jul 29, 2022
2137ed1
fix: rename types
mayoreee Jul 29, 2022
5105439
fix: rpc call options
mayoreee Jul 30, 2022
bf6f608
docs: update docs
mayoreee Jul 30, 2022
458e6cf
docs: update docs
mayoreee Jul 30, 2022
67ac398
fix: deserialize masternode state
mayoreee Jul 30, 2022
51a923c
tests: update tests
mayoreee Jul 30, 2022
df7f3e6
fix: add `NONRECONISED` MasternodeState
mayoreee Aug 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}
)
}