|
1 | 1 | use std::net::IpAddr; |
2 | 2 |
|
3 | | -use crate::ProTxHash; |
4 | 3 | use crate::sml::masternode_list::MasternodeList; |
| 4 | +use crate::{ProTxHash, PubkeyHash}; |
5 | 5 |
|
6 | 6 | impl MasternodeList { |
| 7 | + /// Every masternode in the list whose voting key hash matches |
| 8 | + /// `voting_key_id`, returned as their registration proTxHashes. |
| 9 | + /// |
| 10 | + /// Mirrors dashj's `MasternodeList.getMasternodesByVotingKey(votingKeyId)` |
| 11 | + /// — the lookup contested-username voting uses to resolve which |
| 12 | + /// masternode(s) a given voting key is entitled to cast a vote for. |
| 13 | + /// `key_id_voting` is the 20-byte hash160 of the voting public key; a |
| 14 | + /// single voting key can back more than one masternode, so the result is |
| 15 | + /// a `Vec` (empty when no entry matches). |
| 16 | + pub fn masternodes_by_voting_key(&self, voting_key_id: &PubkeyHash) -> Vec<ProTxHash> { |
| 17 | + self.masternodes |
| 18 | + .values() |
| 19 | + .filter(|node| node.masternode_list_entry.key_id_voting == *voting_key_id) |
| 20 | + .map(|node| node.masternode_list_entry.pro_reg_tx_hash) |
| 21 | + .collect() |
| 22 | + } |
| 23 | + |
7 | 24 | pub fn has_valid_masternode(&self, pro_reg_tx_hash: &ProTxHash) -> bool { |
8 | 25 | self.masternodes |
9 | 26 | .get(pro_reg_tx_hash) |
@@ -46,3 +63,69 @@ pub fn reverse_cmp_sup(lhs: [u8; 32], rhs: [u8; 32]) -> bool { |
46 | 63 | // equal |
47 | 64 | false |
48 | 65 | } |
| 66 | + |
| 67 | +#[cfg(test)] |
| 68 | +mod tests { |
| 69 | + use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; |
| 70 | + |
| 71 | + use hashes::Hash; |
| 72 | + |
| 73 | + use crate::bls_sig_utils::BLSPublicKey; |
| 74 | + use crate::sml::masternode_list::MasternodeList; |
| 75 | + use crate::sml::masternode_list_entry::{ |
| 76 | + EntryMasternodeType, MasternodeListEntry, MasternodeNetInfo, |
| 77 | + }; |
| 78 | + use crate::{BlockHash, ProTxHash, PubkeyHash}; |
| 79 | + |
| 80 | + /// Build a `MasternodeList` from `(proTxHash-seed, voting-key-id)` pairs so |
| 81 | + /// each entry gets a distinct proTxHash and a caller-chosen voting key. |
| 82 | + fn list_from(entries: Vec<(u8, [u8; 20])>) -> MasternodeList { |
| 83 | + let masternodes = entries |
| 84 | + .into_iter() |
| 85 | + .map(|(seed, voting_key_id)| { |
| 86 | + let mut hash_bytes = [0u8; 32]; |
| 87 | + hash_bytes[0] = seed; |
| 88 | + let pro_tx_hash = ProTxHash::from_byte_array(hash_bytes); |
| 89 | + let entry = MasternodeListEntry { |
| 90 | + version: 1, |
| 91 | + pro_reg_tx_hash: pro_tx_hash, |
| 92 | + confirmed_hash: None, |
| 93 | + service_address: MasternodeNetInfo::Legacy(SocketAddr::V4(SocketAddrV4::new( |
| 94 | + Ipv4Addr::new(10, 0, 0, seed), |
| 95 | + 9999, |
| 96 | + ))), |
| 97 | + operator_public_key: BLSPublicKey::from([0u8; 48]), |
| 98 | + key_id_voting: PubkeyHash::from_byte_array(voting_key_id), |
| 99 | + is_valid: true, |
| 100 | + mn_type: EntryMasternodeType::Regular, |
| 101 | + }; |
| 102 | + (pro_tx_hash, entry.into()) |
| 103 | + }) |
| 104 | + .collect(); |
| 105 | + MasternodeList::build(masternodes, Default::default(), BlockHash::from_byte_array([0u8; 32]), 0) |
| 106 | + .build() |
| 107 | + } |
| 108 | + |
| 109 | + #[test] |
| 110 | + fn masternodes_by_voting_key_filters_and_collects() { |
| 111 | + let key_a = [0xAAu8; 20]; |
| 112 | + let key_b = [0xBBu8; 20]; |
| 113 | + // Two masternodes share voting key A, one uses key B. |
| 114 | + let list = list_from(vec![(1, key_a), (2, key_b), (3, key_a)]); |
| 115 | + |
| 116 | + let mut matched = list.masternodes_by_voting_key(&PubkeyHash::from_byte_array(key_a)); |
| 117 | + // Order is BTreeMap (proTxHash) order; sort the seed byte for a stable assert. |
| 118 | + matched.sort_by_key(|h| h.to_byte_array()[0]); |
| 119 | + assert_eq!(matched.len(), 2); |
| 120 | + assert_eq!(matched[0].to_byte_array()[0], 1); |
| 121 | + assert_eq!(matched[1].to_byte_array()[0], 3); |
| 122 | + |
| 123 | + let single = list.masternodes_by_voting_key(&PubkeyHash::from_byte_array(key_b)); |
| 124 | + assert_eq!(single.len(), 1); |
| 125 | + assert_eq!(single[0].to_byte_array()[0], 2); |
| 126 | + |
| 127 | + // A voting key no masternode uses yields an empty vec. |
| 128 | + let none = list.masternodes_by_voting_key(&PubkeyHash::from_byte_array([0xCCu8; 20])); |
| 129 | + assert!(none.is_empty()); |
| 130 | + } |
| 131 | +} |
0 commit comments