Skip to content

Commit 1b9b7b7

Browse files
committed
initial implementation query stakes
1 parent e6c963e commit 1b9b7b7

File tree

4 files changed

+218
-6
lines changed

4 files changed

+218
-6
lines changed

data_structures/src/staking/stakes.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,45 @@ use crate::{chain::PublicKeyHash, get_environment, transaction::StakeTransaction
1111

1212
use super::prelude::*;
1313

14+
/// Message for querying stakes
15+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
16+
pub enum QueryStakesKey<Address: Default + Ord> {
17+
/// Query stakes by validator address
18+
Validator(Address),
19+
/// Query stakes by withdrawer address
20+
Withdrawer(Address),
21+
/// Query stakes by validator and withdrawer addresses
22+
Key(StakeKey<Address>),
23+
}
24+
25+
impl<Address: Default + Ord> Default for QueryStakesKey<Address> {
26+
fn default() -> Self {
27+
QueryStakesKey::Validator(Address::default())
28+
}
29+
}
30+
31+
impl<Address: Default + Ord, T> From<(Option<T>, Option<T>)> for QueryStakesKey<Address>
32+
where
33+
T: Into<Address>,
34+
{
35+
fn from(val: (Option<T>, Option<T>)) -> Self {
36+
let validator = val.0;
37+
let withdrawer = val.1;
38+
39+
if validator.is_some() && withdrawer.is_some() {
40+
QueryStakesKey::Key(StakeKey {
41+
validator: validator.unwrap().into(),
42+
withdrawer: withdrawer.unwrap().into(),
43+
})
44+
} else if validator.is_some() {
45+
QueryStakesKey::Validator(validator.unwrap().into())
46+
// } else if withdrawer.is_some() {
47+
} else {
48+
QueryStakesKey::Withdrawer(withdrawer.unwrap().into())
49+
}
50+
}
51+
}
52+
1453
/// The main data structure that provides the "stakes tracker" functionality.
1554
///
1655
/// This structure holds indexes of stake entries. Because the entries themselves are reference
@@ -24,6 +63,10 @@ where
2463
{
2564
/// A listing of all the stakers, indexed by their address.
2665
by_key: BTreeMap<StakeKey<Address>, SyncStake<Address, Coins, Epoch, Power>>,
66+
/// A listing of all the stakers, indexed by validator.
67+
by_validator: BTreeMap<Address, SyncStake<Address, Coins, Epoch, Power>>,
68+
/// A listing of all the stakers, indexed by withdrawer.
69+
by_withdrawer: BTreeMap<Address, SyncStake<Address, Coins, Epoch, Power>>,
2770
/// A listing of all the stakers, indexed by their coins and address.
2871
///
2972
/// Because this uses a compound key to prevent duplicates, if we want to know which addresses
@@ -83,7 +126,15 @@ where
83126
addresses: key,
84127
};
85128
self.by_coins.remove(&key);
86-
self.by_coins.insert(key, stake.clone());
129+
self.by_coins.insert(key.clone(), stake.clone());
130+
131+
let validator_key = key.clone().addresses.validator;
132+
self.by_validator.remove(&validator_key);
133+
self.by_validator.insert(validator_key, stake.clone());
134+
135+
let withdrawer_key = key.addresses.withdrawer;
136+
self.by_withdrawer.remove(&withdrawer_key);
137+
self.by_withdrawer.insert(withdrawer_key, stake.clone());
87138

88139
Ok(stake.value.read()?.clone())
89140
}
@@ -228,6 +279,39 @@ where
228279
..Default::default()
229280
}
230281
}
282+
283+
/// Query stakes based on different keys.
284+
pub fn query_stakes<IQSK>(&mut self, query: IQSK) -> Option<Coins>
285+
where
286+
IQSK: Into<QueryStakesKey<Address>>,
287+
{
288+
match query.into() {
289+
QueryStakesKey::Key(key) => self.query_by_key(&key),
290+
QueryStakesKey::Validator(validator) => self.query_by_validator(&validator),
291+
QueryStakesKey::Withdrawer(withdrawer) => self.query_by_withdrawer(&withdrawer),
292+
}
293+
}
294+
295+
/// Query stakes by stake key.
296+
fn query_by_key(&self, key: &StakeKey<Address>) -> Option<Coins> {
297+
self.by_key
298+
.get_key_value(key)
299+
.and_then(|entry| entry.1.value.read().map(|stake| stake.coins).ok())
300+
}
301+
302+
/// Query stakes by validator address.
303+
fn query_by_validator(&self, validator: &Address) -> Option<Coins> {
304+
self.by_validator
305+
.get_key_value(validator)
306+
.and_then(|entry| entry.1.value.read().map(|stake| stake.coins).ok())
307+
}
308+
309+
/// Query stakes by withdrawer address.
310+
fn query_by_withdrawer(&self, withdrawer: &Address) -> Option<Coins> {
311+
self.by_withdrawer
312+
.get_key_value(withdrawer)
313+
.and_then(|entry| entry.1.value.read().map(|stake| stake.coins).ok())
314+
}
231315
}
232316

233317
/// Adds stake, based on the data from a stake transaction.
@@ -591,4 +675,27 @@ mod tests {
591675
]
592676
);
593677
}
678+
679+
#[test]
680+
fn test_query_stakes() {
681+
// First, lets create a setup with a few stakers
682+
let mut stakes = Stakes::<String, u64, u64, u64>::with_minimum(5);
683+
let alice = "Alice";
684+
let bob = "Bob";
685+
let charlie = "Charlie";
686+
let david = "David";
687+
let erin = "Erin";
688+
689+
let alice_charlie = (alice, charlie);
690+
let bob_david = (bob, david);
691+
let charlie_erin = (charlie, erin);
692+
693+
stakes.add_stake(alice_charlie, 10, 0).unwrap();
694+
stakes.add_stake(bob_david, 20, 20).unwrap();
695+
stakes.add_stake(charlie_erin, 30, 30).unwrap();
696+
697+
let result = stakes.query_stakes(QueryStakesKey::Key(alice_charlie.into()));
698+
699+
assert_eq!(result, Some(10))
700+
}
594701
}

node/src/actors/chain_manager/handlers.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use witnet_data_structures::{
1919
Hashable, NodeStats, PublicKeyHash, SuperBlockVote, SupplyInfo,
2020
},
2121
error::{ChainInfoError, TransactionError::DataRequestNotFound},
22+
staking::{aux::StakeKey, stakes::QueryStakesKey},
2223
transaction::{DRTransaction, StakeTransaction, Transaction, VTTransaction},
2324
transaction_factory::{self, NodeBalance},
2425
types::LastBeacon,
@@ -37,9 +38,9 @@ use crate::{
3738
GetDataRequestInfo, GetHighestCheckpointBeacon, GetMemoryTransaction, GetMempool,
3839
GetMempoolResult, GetNodeStats, GetReputation, GetReputationResult, GetSignalingInfo,
3940
GetState, GetSuperBlockVotes, GetSupplyInfo, GetUtxoInfo, IsConfirmedBlock,
40-
PeersBeacons, ReputationStats, Rewind, SendLastBeacon, SessionUnitResult,
41-
SetLastBeacon, SetPeersLimits, SignalingInfo, SnapshotExport, SnapshotImport,
42-
TryMineBlock,
41+
PeersBeacons, QueryStake, QueryStakesParams, ReputationStats, Rewind, SendLastBeacon,
42+
SessionUnitResult, SetLastBeacon, SetPeersLimits, SignalingInfo, SnapshotExport,
43+
SnapshotImport, TryMineBlock,
4344
},
4445
sessions_manager::SessionsManager,
4546
},
@@ -1356,6 +1357,24 @@ impl Handler<BuildStake> for ChainManager {
13561357
}
13571358
}
13581359

1360+
impl Handler<QueryStake> for ChainManager {
1361+
type Result = <QueryStake as Message>::Result;
1362+
1363+
fn handle(&mut self, msg: QueryStake, _ctx: &mut Self::Context) -> Self::Result {
1364+
let query_stake_param = match msg.key {
1365+
QueryStakesParams::Key(key) => QueryStakesKey::Key(StakeKey {
1366+
validator: key.0,
1367+
withdrawer: key.1,
1368+
}),
1369+
QueryStakesParams::Validator(v) => QueryStakesKey::Validator(v),
1370+
QueryStakesParams::Withdrawer(w) => QueryStakesKey::Withdrawer(w),
1371+
};
1372+
1373+
// build address from public key hash
1374+
Ok(self.chain_state.stakes.query_stakes(query_stake_param))
1375+
}
1376+
}
1377+
13591378
impl Handler<BuildDrt> for ChainManager {
13601379
type Result = ResponseActFuture<Self, Result<DRTransaction, failure::Error>>;
13611380

node/src/actors/json_rpc/api.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ use crate::{
4545
GetConsolidatedPeers, GetDataRequestInfo, GetEpoch, GetHighestCheckpointBeacon,
4646
GetItemBlock, GetItemSuperblock, GetItemTransaction, GetKnownPeers,
4747
GetMemoryTransaction, GetMempool, GetNodeStats, GetReputation, GetSignalingInfo,
48-
GetState, GetSupplyInfo, GetUtxoInfo, InitializePeers, IsConfirmedBlock, Rewind,
49-
SnapshotExport, SnapshotImport, StakeAuthorization,
48+
GetState, GetSupplyInfo, GetUtxoInfo, InitializePeers, IsConfirmedBlock, QueryStake,
49+
QueryStakesParams, Rewind, SnapshotExport, SnapshotImport, StakeAuthorization,
5050
},
5151
peers_manager::PeersManager,
5252
sessions_manager::SessionsManager,
@@ -139,6 +139,9 @@ pub fn attach_regular_methods<H>(
139139
Box::pin(signaling_info())
140140
});
141141
server.add_actix_method(system, "priority", |_params: Params| Box::pin(priority()));
142+
server.add_actix_method(system, "queryStakes", |params: Params| {
143+
Box::pin(query_stakes(params.parse()))
144+
});
142145
}
143146

144147
/// Attach the sensitive JSON-RPC methods to a multi-transport server.
@@ -2094,6 +2097,60 @@ pub async fn authorize_stake(params: Result<AuthorizeStake, Error>) -> JsonRpcRe
20942097
.await
20952098
}
20962099

2100+
/// Param for query_stakes
2101+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
2102+
pub enum QueryStakesArgument {
2103+
/// To query by stake validator
2104+
Validator(String),
2105+
/// To query by stake withdrawer
2106+
Withdrawer(String),
2107+
/// To query by stake validator and withdrawer
2108+
Key((String, String)),
2109+
}
2110+
2111+
/// Query the amount of nanowits staked by an address.
2112+
pub async fn query_stakes(params: Result<Option<QueryStakesArgument>, Error>) -> JsonRpcResult {
2113+
// Short-circuit if parameters are wrong
2114+
let params = params?;
2115+
2116+
// If a withdrawer address is not specified, default to local node address
2117+
let key: QueryStakesParams = if let Some(address) = params {
2118+
match address {
2119+
QueryStakesArgument::Validator(k) => QueryStakesParams::Validator(
2120+
PublicKeyHash::from_bech32(get_environment(), &k).map_err(internal_error)?,
2121+
),
2122+
QueryStakesArgument::Withdrawer(k) => QueryStakesParams::Withdrawer(
2123+
PublicKeyHash::from_bech32(get_environment(), &k).map_err(internal_error)?,
2124+
),
2125+
QueryStakesArgument::Key((v, w)) => QueryStakesParams::Key((
2126+
PublicKeyHash::from_bech32(get_environment(), &v).map_err(internal_error)?,
2127+
PublicKeyHash::from_bech32(get_environment(), &w).map_err(internal_error)?,
2128+
)),
2129+
}
2130+
} else {
2131+
let pk = signature_mngr::public_key().await.map_err(internal_error)?;
2132+
2133+
QueryStakesParams::Validator(PublicKeyHash::from_public_key(&pk))
2134+
};
2135+
2136+
ChainManager::from_registry()
2137+
.send(QueryStake { key })
2138+
.map(|res| match res {
2139+
Ok(Ok(staked_amount)) => {
2140+
serde_json::to_value(staked_amount.unwrap()).map_err(internal_error)
2141+
}
2142+
Ok(Err(e)) => {
2143+
let err = internal_error_s(e);
2144+
Err(err)
2145+
}
2146+
Err(e) => {
2147+
let err = internal_error_s(e);
2148+
Err(err)
2149+
}
2150+
})
2151+
.await
2152+
}
2153+
20972154
#[cfg(test)]
20982155
mod mock_actix {
20992156
use actix::{MailboxError, Message};

node/src/actors/messages.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use witnet_data_structures::{
3434
transaction_factory::NodeBalance,
3535
types::LastBeacon,
3636
utxo_pool::{UtxoInfo, UtxoSelectionStrategy},
37+
wit::Wit,
3738
};
3839
use witnet_p2p::{
3940
error::SessionsError,
@@ -305,6 +306,34 @@ impl Message for StakeAuthorization {
305306
type Result = Result<String, failure::Error>;
306307
}
307308

309+
/// Stake key for quering stakes
310+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
311+
pub enum QueryStakesParams {
312+
/// To search by the validator public key hash
313+
Validator(PublicKeyHash),
314+
/// To search by the withdrawer public key hash
315+
Withdrawer(PublicKeyHash),
316+
/// To search by validator and withdrawer public key hashes
317+
Key((PublicKeyHash, PublicKeyHash)),
318+
}
319+
320+
impl Default for QueryStakesParams {
321+
fn default() -> Self {
322+
QueryStakesParams::Validator(PublicKeyHash::default())
323+
}
324+
}
325+
326+
/// Message for querying stakes
327+
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
328+
pub struct QueryStake {
329+
/// stake key used to search the stake
330+
pub key: QueryStakesParams,
331+
}
332+
333+
impl Message for QueryStake {
334+
type Result = Result<Option<Wit>, failure::Error>;
335+
}
336+
308337
/// Builds a `DataRequestTransaction` from a `DataRequestOutput`
309338
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
310339
pub struct BuildDrt {

0 commit comments

Comments
 (0)