Skip to content

Commit

Permalink
Add protx & verify lockRPC calls (#8)
Browse files Browse the repository at this point in the history
* feat: add `protx register_prepare` RPC call

* feat: protx register_submit

* feat: add `protx revoke` RPC call

* feat: add protx `update_registrar` RPC

* feat: add `protx update_service` RPC call

* feat: add `verifychainlock` RPC call

* feat: add `verifyislock` RPC call

* fixes

* typo fix

* fix: typo
  • Loading branch information
mayoreee authored Jan 8, 2023
1 parent 94a2835 commit 6224176
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 25 deletions.
48 changes: 46 additions & 2 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1302,17 +1302,61 @@ pub trait RpcApi: Sized {
}

/// Creates a ProRegTx referencing an existing collateral and and sends it to the network
fn get_protx_register(&self, collateral_hash: &str, collateral_index: u32, ip_and_port: &str, owner_address: &str, operator_pub_key: &str, voting_address: &str, operator_reward: u32, payout_address: &str, fee_source_address: Option<&str>, submit: Option<bool>) -> Result<json::ProRegTxHash> {
fn get_protx_register(&self, collateral_hash: &str, collateral_index: u32, ip_and_port: &str, owner_address: dashcore::Address, operator_pub_key: &str, voting_address: dashcore::Address, operator_reward: u32, payout_address: dashcore::Address, fee_source_address: Option<dashcore::Address>, submit: Option<bool>) -> Result<json::ProRegTxHash> {
let mut args = ["register".into(), into_json(collateral_hash)?, into_json(collateral_index)?, into_json(ip_and_port)?, into_json(owner_address)?, into_json(operator_pub_key)?, into_json(voting_address)?, into_json(operator_reward)?, into_json(payout_address)?, opt_into_json(fee_source_address)?, opt_into_json(submit)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Creates and funds a ProRegTx with the 1,000 DASH necessary for a masternode and then sends it to the network
fn get_protx_register_fund(&self, collateral_address: &str, ip_and_port: &str, owner_address: &str, operator_pub_key: &str, voting_address: &str, operator_reward: u32, payout_address: &str, fund_address: Option<&str>, submit: Option<bool>) -> Result<json::ProRegTxHash> {
fn get_protx_register_fund(&self, collateral_address: dashcore::Address, ip_and_port: &str, owner_address: dashcore::Address, operator_pub_key: &str, voting_address: dashcore::Address, operator_reward: u32, payout_address: dashcore::Address, fund_address: Option<dashcore::Address>, submit: Option<bool>) -> Result<json::ProRegTxHash> {
let mut args = ["register_fund".into(), into_json(collateral_address)?, into_json(ip_and_port)?, into_json(owner_address)?, into_json(operator_pub_key)?, into_json(voting_address)?, into_json(operator_reward)?, into_json(payout_address)?, opt_into_json(fund_address)?, opt_into_json(submit)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Creates an unsigned ProTx and a message that must be signed externally
fn get_protx_register_prepare(&self, collateral_hash: &str, collateral_index: u32, ip_and_port: &str, owner_address: dashcore::Address, operator_pub_key: &str, voting_address: dashcore::Address, operator_reward: u32, payout_address: dashcore::Address, fee_source_address: Option<dashcore::Address>) -> Result<json::ProTxRegPrepare> {
let mut args = ["register_prepare".into(), into_json(collateral_address)?, into_json(collateral_index)?, into_json(ip_and_port)?, into_json(owner_address)?, into_json(operator_pub_key)?, into_json(voting_address)?, into_json(operator_reward)?, into_json(payout_address)?, opt_into_json(fee_source_address)?];
self.call::<json::ProTxRegPrepare>("protx", handle_defaults(&mut args, &[null()]))
}

/// Combines the unsigned ProTx and a signature of the signMessage, signs all inputs which were added to
/// cover fees and submits the resulting transaction to the network
fn get_protx_register_submit(&self, tx: &str, sig: &str) -> Result<json::ProRegTxHash> {
let mut args = ["register_submit".into(), into_json(tx)?, into_json(sig)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Creates and sends a ProUpRevTx to the network
fn get_protx_revoke(&self, pro_tx_hash: &str, operator_pub_key: &str, reason: json::ProTxRevokeReason, fee_source_address: Option<dashcore::Address>) -> Result<json::ProRegTxHash> {
let mut args = ["revoke".into(), into_json(pro_tx_hash)?, into_json(operator_pub_key)?, into_json(reason)?, opt_into_json(fee_source_address)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Creates and sends a ProUpRegTx to the network
fn get_protx_update_registrar(&self, pro_tx_hash: &str, operator_pub_key: &str, voting_address: dashcore::Address, payout_address: Option<dashcore::Address>, fee_source_address: Option<dashcore::Address>) -> Result<json::ProRegTxHash> {
let mut args = ["update_registrar".into(), into_json(pro_tx_hash)?, into_json(operator_pub_key)?, into_json(voting_address)?, opt_into_json(payout_address)?, opt_into_json(fee_source_address)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Creates and sends a ProUpServTx to the network
fn get_protx_update_service(&self, pro_tx_hash: &str, ip_and_port: &str, operator_key: &str, operator_payout_address: Option<dashcore::Address>, fee_source_address: Option<dashcore::Address>) -> Result<json::ProRegTxHash> {
let mut args = ["update_service".into(), into_json(pro_tx_hash)?, into_json(ip_and_port)?, into_json(operator_key)?, opt_into_json(payout_address)?, opt_into_json(fee_source_address)?];
self.call::<json::ProRegTxHash>("protx", handle_defaults(&mut args, &[null()]))
}

/// Tests if a quorum signature is valid for a ChainLock
fn get_verifychainlock(&self, block_hash: &str, signature: &str, block_height: Option<u32>) -> Result<bool> {
let mut args = [into_json(block_hash)?, into_json(signature)?, opt_into_json(block_height)?];
self.call::<bool>("verifychainlock", handle_defaults(&mut args, &[null()]))
}

/// Tests if a quorum signature is valid for an InstantSend Lock
fn get_verifyislock(&self, id: &str, tx_id: &str, signature: &str, max_height: Option<u32>) -> Result<bool> {
let mut args = [into_json(id)?, into_json(tx_id)?, into_json(signature)?, opt_into_json(max_height)?];
self.call::<bool>("verifyislock", handle_defaults(&mut args, &[null()]))
}


}

/// Client implements a JSON-RPC client for the Dash Core daemon or compatible APIs.
Expand Down
140 changes: 119 additions & 21 deletions integration_test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use dashcore::hashes::hex::{FromHex, ToHex};
use dashcore::hashes::Hash;
use dashcore::secp256k1;
use dashcore::{
Address, Amount, Network, OutPoint, PrivateKey, Script, EcdsaSighashType, SignedAmount, Transaction,
TxIn, TxOut, Txid, Witness,
Address, Amount, EcdsaSighashType, Network, OutPoint, PrivateKey, Script, SignedAmount,
Transaction, TxIn, TxOut, Txid, Witness,
};
use dashcore_rpc::dashcore_rpc_json::{
GetBlockTemplateModes, GetBlockTemplateRules, ScanTxOutRequest,
Expand Down Expand Up @@ -237,6 +237,13 @@ fn main() {
test_get_protx_list(&cl);
test_get_protx_register(&cl);
test_get_protx_register_fund(&cl);
test_get_protx_register_prepare(&cl);
test_get_protx_register_submit(&cl);
test_get_protx_revoke(&cl);
test_get_protx_update_registrar(&cl);
test_get_protx_update_service(&cl);
test_get_verifychainlock(&cl);
test_get_verifyislock(&cl);
}

fn test_get_network_info(cl: &Client) {
Expand Down Expand Up @@ -620,8 +627,9 @@ fn test_sign_raw_transaction_with_send_raw_transaction(cl: &Client) {
}],
};

let res =
cl.sign_raw_transaction_with_key(&tx, &[sk], None, Some(EcdsaSighashType::All.into())).unwrap();
let res = cl
.sign_raw_transaction_with_key(&tx, &[sk], None, Some(EcdsaSighashType::All.into()))
.unwrap();
assert!(res.complete);
let _ = cl.send_raw_transaction(&res.transaction().unwrap()).unwrap();
}
Expand Down Expand Up @@ -1105,11 +1113,7 @@ fn test_add_ban(cl: &Client) {
let res = cl.list_banned().unwrap();
assert_eq!(res.len(), 0);

assert_error_message!(
cl.add_ban("INVALID_STRING", 0, false),
-30,
"Error: Invalid IP/Subnet"
);
assert_error_message!(cl.add_ban("INVALID_STRING", 0, false), -30, "Error: Invalid IP/Subnet");
}

fn test_set_network_active(cl: &Client) {
Expand Down Expand Up @@ -1161,7 +1165,6 @@ fn test_stop(cl: Client) {
println!("Stopping: '{}'", cl.stop().unwrap());
}


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

fn test_get_masternode_count(cl: &Client) {
Expand All @@ -1186,7 +1189,10 @@ fn test_get_masternode_payments(cl: &Client) {
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);
assert_eq!(
masternode_payments[0].amount,
masternode_payments[0].masternodes[0].payees[0].amount
);
}

fn test_get_masternode_status(cl: &Client) {
Expand All @@ -1197,15 +1203,20 @@ fn test_get_masternode_winners(cl: &Client) {
let masternode_winners = rpc.get_masternode_winners(None, None).unwrap();
}


// ---------------------- Quorum RPC tests---------------------

fn test_get_quorum_list(cl: &Client) {
let quorum_list = rpc.get_quorum_list(Some("1")).unwrap();
}

fn test_get_quorum_info(cl: &Client) {
let quorum_info = rpc.get_quorum_info("1", "000000000c9eddd5d2a707281b7e30d5aac974dac600ff10f01937e1ca36066f", None).unwrap();
let quorum_info = rpc
.get_quorum_info(
"1",
"000000000c9eddd5d2a707281b7e30d5aac974dac600ff10f01937e1ca36066f",
None,
)
.unwrap();
assert!(quorum_info.height > 0);
assert!(quorum_info.quorum_index >= 0);
assert!(quorum_info.members.len() >= 0);
Expand All @@ -1220,32 +1231,74 @@ fn test_get_quorum_dkgstatus(cl: &Client) {
}

fn test_get_quorum_sign(cl: &Client) {
let quorum_dkgstatus = rpc.get_quorum_sign(1, "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", "51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239", None, None).unwrap();
let quorum_dkgstatus = rpc
.get_quorum_sign(
1,
"abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
"51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239",
None,
None,
)
.unwrap();
}

fn test_get_quorum_getrecsig(cl: &Client) {
let quorum_getrecsig = rpc.get_quorum_getrecsig(1, "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", "51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239").unwrap();
let quorum_getrecsig = rpc
.get_quorum_getrecsig(
1,
"abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
"51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239",
)
.unwrap();
}

fn test_get_quorum_hasrecsig(cl: &Client) {
let quorum_hasrecsig = rpc.get_quorum_hasrecsig(1, "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", "51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239").unwrap();
let quorum_hasrecsig = rpc
.get_quorum_hasrecsig(
1,
"abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
"51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239",
)
.unwrap();
}

fn test_get_quorum_isconflicting(cl: &Client) {
let quorum_isconflicting = rpc.get_quorum_isconflicting(1, "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", "51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239").unwrap();
let quorum_isconflicting = rpc
.get_quorum_isconflicting(
1,
"abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
"51c11d287dfa85aef3eebb5420834c8e443e01d15c0b0a8e397d67e2e51aa239",
)
.unwrap();
}

fn test_get_quorum_memberof(cl: &Client) {
let quorum_memberof = rpc.get_quorum_memberof("39c07d2c9c6d0ead56f52726b63c15e295cb5c3ecf7fe1fefcfb23b2e3cfed1f", Some(1)).unwrap();
let quorum_memberof = rpc
.get_quorum_memberof(
"39c07d2c9c6d0ead56f52726b63c15e295cb5c3ecf7fe1fefcfb23b2e3cfed1f",
Some(1),
)
.unwrap();
assert!(quorum_memberof[0].height > 0);
}

fn test_get_quorum_rotationinfo(cl: &Client) {
let quorum_rotationinfo = rpc.get_quorum_rotationinfo("0000012197b7ca6360af3756c6a49c217dbbdf8b595fd55e0fcef7ffcd546044", None, None).unwrap();
let quorum_rotationinfo = rpc
.get_quorum_rotationinfo(
"0000012197b7ca6360af3756c6a49c217dbbdf8b595fd55e0fcef7ffcd546044",
None,
None,
)
.unwrap();
}

fn test_get_quorum_selectquorum(cl: &Client) {
let quorum_selectquorum = rpc.get_quorum_selectquorum(1, "b95205c3bba72e9edfbe7380ec91fe5a97e16a189e28f39b03c6822757ad1a34").unwrap();
let quorum_selectquorum = rpc
.get_quorum_selectquorum(
1,
"b95205c3bba72e9edfbe7380ec91fe5a97e16a189e28f39b03c6822757ad1a34",
)
.unwrap();
}

fn test_get_quorum_verify(cl: &Client) {
Expand All @@ -1271,7 +1324,9 @@ fn test_get_protx_diff(cl: &Client) {
}

fn test_get_protx_info(cl: &Client) {
let protx_info = rpc.get_protx_info("000000000c9eddd5d2a707281b7e30d5aac974dac600ff10f01937e1ca36066f").unwrap();
let protx_info = rpc
.get_protx_info("000000000c9eddd5d2a707281b7e30d5aac974dac600ff10f01937e1ca36066f")
.unwrap();
assert!(protx_info.collateralIndex >= 0);
assert!(protx_info.operatorReward >= 0);
}
Expand All @@ -1287,3 +1342,46 @@ fn test_get_protx_register(cl: &Client) {
fn test_get_protx_register_fund(cl: &Client) {
let protx_register_fund = rpc.get_protx_register_fund("yakx4mMRptKhgfjedNzX5FGQq7kSSBF2e7", "3.4.5.6:3456", "yURczr3qY31xkQZfFu8eZvKz19eAEPQxsd", "0e02146e9c34cfbcb3f3037574a1abb35525e2ca0c3c6901dbf82ac591e30218d1711223b7ca956edf39f3d984d06d51", "yURczr3qY31xkQZfFu8eZvKz19eAEPQxsd", 5, "yUYTxqjpCfAAK4vgxXtBPywRBtZqsxN7Vy", Some("yRMFHxcJ2aS2vfo5whhE2Gg73dfQVm8LAF"), Some(false)).unwrap();
}

fn test_get_protx_register_prepare(cl: &Client) {
let protx_register_prepare = rpc.get_protx_register_prepare("df41e398bb245e973340d434d386f431dbd69735a575721b0b6833856e7d31ec", 1, "9.8.7.6:9876", "yemjhGQ99V5ayJMjoyGGPtxteahii6G1Jz", "06849865d01e4f73a6d5a025117e48f50b897e14235800501c8bfb8a6365cc8dbf5ddb67a3635d0f1dcc7d46a7ee280c", "yemjhGQ99V5ayJMjoyGGPtxteahii6G1Jz", 1.2, "yjJJLkYDUN6X8gWjXbCoKEXoiLeKxxMMRt", None).unwrap();
}

fn test_get_protx_register_submit(cl: &Client) {
let protx_register_submit = rpc.get_protx_register_submit("03000100012d988526d5d1efd32320023c92eff09c2963dcb021b0de9761", "H90IvqVtFjZkwLJb08yMEgGixs0/FpcdvwImBcir4cYLJhD3pdX+lKD2GsPl6KNxghVXNk5/HpOdBoWAHo9u++Y=").unwrap();
}

fn test_get_protx_revoke(cl: &Client) {
let protx_revoke = rpc
.get_protx_revoke(
"ba1b3330e16a0876b7a186e7ceb689f03ec646e611e91d7139de021bbf13afdd",
"4da7e1ea30fb9e55c73ad23df0b9d3d34342acb24facf4b19420e1a26ae272d1",
1,
None,
)
.unwrap();
}

fn test_get_protx_update_registrar(cl: &Client) {
let protx_update_registrar = rpc.get_protx_update_registrar("ba1b3330e16a0876b7a186e7ceb689f03ec646e611e91d7139de021bbf13afdd", "0e02146e9c34cfbcb3f3037574a1abb35525e2ca0c3c6901dbf82ac591e30218d1711223b7ca956edf39f3d984d06d51", "yX2cDS4kcJ4LK4uq9Hd4TG7kURV3sGLZrw", "yakx4mMRptKhgfjedNzX5FGQq7kSSBF2e7", None).unwrap();
}

fn test_get_protx_update_service(cl: &Client) {
let protx_update_service = rpc
.get_protx_update_service(
"ba1b3330e16a0876b7a186e7ceb689f03ec646e611e91d7139de021bbf13afdd",
"4.3.2.1:4321",
"4da7e1ea30fb9e55c73ad23df0b9d3d34342acb24facf4b19420e1a26ae272d1",
None,
None,
)
.unwrap();
}

fn test_get_verifychainlock(cl: &Client) {
let verifychainlock = rpc.get_verifychainlock( "00000036d5c520be6e9a32d3829efc983a7b5e88052bf138f80a2b3988689a24", "97ec34efd1615b84af62495e54024880752f57790cf450ae974b80002440963592d96826e24f109e6c149411b70bb9a0035443752368590adae60365cf4251464e0423c1263e9c56a33eae9be9e9c79a117151b2173bcee93497008cace8d793", None).unwrap();
}

fn test_get_verifyislock(cl: &Client) {
let verifychainlock = rpc.get_verifyislock("d0b1a9c70fdfff6bf7f6cbe3d1fe33a4ca44ceb17059b6381a4ac25d9c9b6495", "8b5174d0e95b5642ebec23c3fe8f0bbf8f6993502f4210322871bba0e818ff3b", "97ec34efd1615b84af62495e54024880752f57790cf450ae974b80002440963592d96826e24f109e6c149411b70bb9a0035443752368590adae60365cf4251464e0423c1263e9c56a33eae9be9e9c79a117151b2173bcee93497008cace8d793", None).unwrap();
}
56 changes: 54 additions & 2 deletions json/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2120,7 +2120,8 @@ pub struct BLS {
#[serde_as(as = "Bytes")]
pub secret: Vec<u8>,
#[serde_as(as = "Bytes")]
pub public: Vec<u8>
pub public: Vec<u8>,
}

// --------------------------- Quorum -------------------------------

Expand Down Expand Up @@ -2454,6 +2455,36 @@ pub enum ProTxList{
Info(Vec<ProTxInfo>)
}

#[serde_as]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProTxRegPrepare {
pub tx: ProRegTxHash,
#[serde_as(as = "Bytes")]
pub collateral_address: Vec<u8>,
#[serde_as(as = "Bytes")]
pub sign_message: Vec<u8>
}

#[serde(untagged)]
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
pub enum ProTxRevokeReason {
#[serde(deserialize_with = "deserialize_protx_revoke_reason")]
NOT_SPECIFIED,

#[serde(deserialize_with = "deserialize_protx_revoke_reason")]
TERMINATION_OF_SERVICE,

#[serde(deserialize_with = "deserialize_protx_revoke_reason")]
COMPROMISED_KEYS,

#[serde(deserialize_with = "deserialize_protx_revoke_reason")]
CHANGE_OF_KEYS,

#[serde(deserialize_with = "deserialize_protx_revoke_reason")]
NOT_RECOGNISED,
}

// Custom deserializer functions.

/// deserialize_hex_array_opt deserializes a vector of hex-encoded byte arrays.
Expand Down Expand Up @@ -2546,4 +2577,25 @@ where
)
}
}
}
}



/// deserialize_protx_revoke_reason deserializes a ProTx revoke reason
fn deserialize_protx_revoke_reason<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
D: serde::Deserializer<'de>,
{
let input_value = IntegerOrString::deserialize(deserializer)?;
let value: u8 = IntegerOrString::Integer(input_value);

Ok(
match value {
0 => ProTxRevokeReason::NOT_SPECIFIED,
1 => ProTxRevokeReason::TERMINATION_OF_SERVICE,
2 => ProTxRevokeReason::COMPROMISED_KEYS,
3 => ProTxRevokeReason::CHANGE_OF_KEYS,
_ => ProTxRevokeReason::NOT_RECOGNISED,
}
)
}

0 comments on commit 6224176

Please sign in to comment.