Skip to content

Commit 592b3b3

Browse files
authored
Pending endpoints (#1150)
* Add QueryPendingDelegationEvents QueryMsg * Add pending_delegation_events to nymd client * Add pending delegation events to wallet * Get rid of double epoch accounting * Fix reward saving * Try batching operations to reduce fees * Bundle all transactions into one * make nice
1 parent c2ff786 commit 592b3b3

File tree

27 files changed

+347
-596
lines changed

27 files changed

+347
-596
lines changed

common/client-libs/validator-client/src/client.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::{validator_api, ValidatorClientError};
55
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
6+
use mixnet_contract_common::mixnode::DelegationEvent;
67
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, Interval, MixNodeBond};
78
use network_defaults::DEFAULT_NETWORK;
89
use url::Url;
@@ -270,7 +271,20 @@ impl<C> Client<C> {
270271
.u128())
271272
}
272273

273-
pub async fn get_current_epoch(&self) -> Result<Option<Interval>, ValidatorClientError>
274+
pub async fn get_pending_delegation_events(
275+
&self,
276+
owner_address: String,
277+
) -> Result<Vec<DelegationEvent>, ValidatorClientError>
278+
where
279+
C: CosmWasmClient + Sync,
280+
{
281+
Ok(self
282+
.nymd
283+
.get_pending_delegation_events(owner_address)
284+
.await?)
285+
}
286+
287+
pub async fn get_current_epoch(&self) -> Result<Interval, ValidatorClientError>
274288
where
275289
C: CosmWasmClient + Sync,
276290
{

common/client-libs/validator-client/src/nymd/mod.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use cosmrs::rpc::HttpClientUrl;
1414
use cosmwasm_std::{Coin, Uint128};
1515
pub use fee::gas_price::GasPrice;
1616
use fee::helpers::Operation;
17+
use mixnet_contract_common::mixnode::DelegationEvent;
1718
use mixnet_contract_common::{
1819
ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse,
1920
IdentityKey, Interval, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse,
@@ -317,7 +318,20 @@ impl<C> NymdClient<C> {
317318
.await
318319
}
319320

320-
pub async fn get_current_epoch(&self) -> Result<Option<Interval>, NymdError>
321+
pub async fn get_pending_delegation_events(
322+
&self,
323+
owner_address: String,
324+
) -> Result<Vec<DelegationEvent>, NymdError>
325+
where
326+
C: CosmWasmClient + Sync,
327+
{
328+
let request = QueryMsg::GetPendingDelegationEvents { owner_address };
329+
self.client
330+
.query_contract_smart(self.mixnet_contract_address()?, &request)
331+
.await
332+
}
333+
334+
pub async fn get_current_epoch(&self) -> Result<Interval, NymdError>
321335
where
322336
C: CosmWasmClient + Sync,
323337
{

common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs

+3
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ pub enum QueryMsg {
176176
address: String,
177177
mix_identity: IdentityKey,
178178
},
179+
GetPendingDelegationEvents {
180+
owner_address: String,
181+
},
179182
}
180183

181184
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]

contracts/mixnet/src/contract.rs

+28-19
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::constants::{ACTIVE_SET_WORK_FACTOR, INTERVAL_REWARD_PERCENT, SYBIL_RE
55
use crate::delegations::queries::query_all_network_delegations_paged;
66
use crate::delegations::queries::query_delegator_delegations_paged;
77
use crate::delegations::queries::query_mixnode_delegation;
8-
use crate::delegations::queries::query_mixnode_delegations_paged;
8+
use crate::delegations::queries::{
9+
query_mixnode_delegations_paged, query_pending_delegation_events,
10+
};
911
use crate::error::ContractError;
1012
use crate::gateways::queries::query_gateways_paged;
1113
use crate::gateways::queries::query_owns_gateway;
@@ -14,7 +16,7 @@ use crate::interval::queries::{
1416
query_current_rewarded_set_height, query_rewarded_set,
1517
query_rewarded_set_refresh_minimum_blocks, query_rewarded_set_update_details,
1618
};
17-
use crate::interval::storage as interval_storage;
19+
use crate::interval::transactions::init_epoch;
1820
use crate::mixnet_contract_settings::models::ContractState;
1921
use crate::mixnet_contract_settings::queries::{
2022
query_contract_settings_params, query_contract_version,
@@ -31,7 +33,7 @@ use cosmwasm_std::{
3133
entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128,
3234
};
3335
use mixnet_contract_common::{
34-
ContractStateParams, Delegation, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg,
36+
ContractStateParams, Delegation, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
3537
};
3638
use time::OffsetDateTime;
3739

@@ -77,13 +79,11 @@ pub fn instantiate(
7779
) -> Result<Response, ContractError> {
7880
let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?;
7981
let state = default_initial_state(info.sender, rewarding_validator_address);
80-
let rewarding_interval = Interval::init_epoch(env.clone());
82+
init_epoch(deps.storage, env)?;
8183

8284
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?;
8385
mixnet_params_storage::LAYERS.save(deps.storage, &Default::default())?;
8486
rewards_storage::REWARD_POOL.save(deps.storage, &Uint128::new(INITIAL_REWARD_POOL))?;
85-
interval_storage::save_epoch(deps.storage, &rewarding_interval)?;
86-
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
8787

8888
Ok(Response::default())
8989
}
@@ -146,18 +146,13 @@ pub fn execute(
146146
deps, info, params,
147147
)
148148
}
149+
#[allow(unused_variables)]
150+
// FIXME: remove interval_id
149151
ExecuteMsg::RewardMixnode {
150152
identity,
151153
params,
152154
interval_id,
153-
} => crate::rewards::transactions::try_reward_mixnode(
154-
deps,
155-
env,
156-
info,
157-
identity,
158-
params,
159-
interval_id,
160-
),
155+
} => crate::rewards::transactions::try_reward_mixnode(deps, env, info, identity, params),
161156
ExecuteMsg::DelegateToMixnode { mix_identity } => {
162157
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
163158
}
@@ -238,9 +233,11 @@ pub fn execute(
238233
rewarded_set,
239234
expected_active_set_size,
240235
),
241-
ExecuteMsg::AdvanceCurrentEpoch {} => {
242-
crate::interval::transactions::try_advance_epoch(env, deps.storage)
243-
}
236+
ExecuteMsg::AdvanceCurrentEpoch {} => crate::interval::transactions::try_advance_epoch(
237+
env,
238+
deps.storage,
239+
info.sender.to_string(),
240+
),
244241
ExecuteMsg::CompoundDelegatorReward { mix_identity } => {
245242
crate::rewards::transactions::try_compound_delegator_reward(
246243
deps,
@@ -370,13 +367,18 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
370367
address,
371368
mix_identity,
372369
)?),
370+
QueryMsg::GetPendingDelegationEvents { owner_address } => to_binary(
371+
&query_pending_delegation_events(deps.storage, owner_address)?,
372+
),
373373
};
374374

375375
Ok(query_res?)
376376
}
377377

378-
#[entry_point]
379-
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
378+
// MIGRATE OLD DELEGATION STORAGE
379+
// applied on QAnet
380+
#[allow(dead_code)]
381+
fn migrate_delegations(deps: DepsMut<'_>) -> Result<(), ContractError> {
380382
use crate::delegations::storage::{
381383
DelegationIndex, DELEGATION_MIXNODE_IDX_NAMESPACE, DELEGATION_OWNER_IDX_NAMESPACE,
382384
DELEGATION_PK_NAMESPACE,
@@ -420,6 +422,13 @@ pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respons
420422
&delegation,
421423
)?;
422424
}
425+
Ok(())
426+
}
427+
428+
#[entry_point]
429+
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
430+
// TODO: Uncomment for sandbox and mainnet
431+
// migrate_delegations(deps)?;
423432

424433
Ok(Default::default())
425434
}

contracts/mixnet/src/delegations/queries.rs

+13
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,24 @@ use cosmwasm_std::Order;
77
use cosmwasm_std::StdResult;
88
use cosmwasm_std::{Api, Deps, Storage};
99
use cw_storage_plus::{Bound, PrimaryKey};
10+
use mixnet_contract_common::mixnode::DelegationEvent;
1011
use mixnet_contract_common::{
1112
Delegation, IdentityKey, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
1213
PagedMixDelegationsResponse,
1314
};
1415

16+
pub(crate) fn query_pending_delegation_events(
17+
storage: &dyn Storage,
18+
owner_address: String,
19+
) -> Result<Vec<DelegationEvent>, ContractError> {
20+
Ok(storage::PENDING_DELEGATION_EVENTS
21+
.sub_prefix(owner_address.as_bytes().to_vec())
22+
.range(storage, None, None, Order::Ascending)
23+
.filter_map(|r| r.ok())
24+
.map(|(_key, delegation_event)| delegation_event)
25+
.collect::<Vec<DelegationEvent>>())
26+
}
27+
1528
pub(crate) fn query_all_network_delegations_paged(
1629
deps: Deps<'_>,
1730
start_after: Option<(IdentityKey, Vec<u8>, u64)>,

contracts/mixnet/src/delegations/storage.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ pub const DELEGATION_OWNER_IDX_NAMESPACE: &str = "dlo";
1010
pub const DELEGATION_MIXNODE_IDX_NAMESPACE: &str = "dlm";
1111

1212
pub const PENDING_DELEGATION_EVENTS: Map<
13-
(BlockHeight, IdentityKey, OwnerAddress),
13+
(OwnerAddress, BlockHeight, IdentityKey),
1414
DelegationEvent,
15-
> = Map::new("pend");
15+
> = Map::new("pend2");
1616

1717
// paged retrieval limits for all queries and transactions
1818
pub(crate) const DELEGATION_PAGE_MAX_LIMIT: u32 = 500;

contracts/mixnet/src/delegations/transactions.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub(crate) fn _try_reconcile_all_delegation_events(
4040
let pending_delegation_events = PENDING_DELEGATION_EVENTS
4141
.range(storage, None, None, Order::Ascending)
4242
.filter_map(|r| r.ok())
43-
.collect::<Vec<((u64, String, Vec<u8>), DelegationEvent)>>();
43+
.collect::<Vec<((Vec<u8>, u64, String), DelegationEvent)>>();
4444

4545
let mut response = Response::new();
4646

@@ -193,7 +193,7 @@ pub(crate) fn _try_delegate_to_mixnode(
193193
}
194194

195195
let maybe_proxy_storage = generate_storage_key(&delegate, proxy.as_ref());
196-
let storage_key = (block_height, mix_identity.to_string(), maybe_proxy_storage);
196+
let storage_key = (maybe_proxy_storage, block_height, mix_identity.to_string());
197197

198198
storage::PENDING_DELEGATION_EVENTS.save(
199199
storage,
@@ -389,9 +389,9 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
389389
PENDING_DELEGATION_EVENTS.save(
390390
deps.storage,
391391
(
392+
delegate.as_bytes().to_vec(),
392393
env.block.height,
393394
mix_identity.to_string(),
394-
delegate.as_bytes().to_vec(),
395395
),
396396
&DelegationEvent::Undelegate(PendingUndelegate::new(
397397
mix_identity.to_string(),
@@ -1069,7 +1069,7 @@ mod tests {
10691069

10701070
_try_reconcile_all_delegation_events(&mut deps.storage, &deps.api).unwrap();
10711071

1072-
let delegation = query_mixnode_delegation(
1072+
let _delegation = query_mixnode_delegation(
10731073
&deps.storage,
10741074
&deps.api,
10751075
identity.clone(),

contracts/mixnet/src/interval/queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use mixnet_contract_common::{
1010
RewardedSetUpdateDetails,
1111
};
1212

13-
pub fn query_current_epoch(storage: &dyn Storage) -> Result<Option<Interval>, ContractError> {
13+
pub fn query_current_epoch(storage: &dyn Storage) -> Result<Interval, ContractError> {
1414
storage::current_epoch(storage)
1515
}
1616

contracts/mixnet/src/interval/storage.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ pub fn save_epoch_reward_params(
5555
Ok(())
5656
}
5757

58-
pub fn current_epoch(storage: &dyn Storage) -> Result<Option<Interval>, ContractError> {
59-
Ok(CURRENT_EPOCH.may_load(storage)?)
58+
pub fn current_epoch(storage: &dyn Storage) -> Result<Interval, ContractError> {
59+
CURRENT_EPOCH
60+
.load(storage)
61+
.map_err(|_| ContractError::EpochNotInitialized)
6062
}
6163

6264
pub(crate) fn save_rewarded_set(

contracts/mixnet/src/interval/transactions.rs

+21-24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use super::storage;
55
use crate::error::ContractError;
66
use crate::error::ContractError::EpochInProgress;
77
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
8+
use crate::support::helpers::is_authorized;
89
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Storage};
910
use mixnet_contract_common::events::{new_advance_interval_event, new_change_rewarded_set_event};
1011
use mixnet_contract_common::{IdentityKey, Interval};
@@ -64,23 +65,26 @@ pub fn try_write_rewarded_set(
6465
)))
6566
}
6667

67-
fn init_epoch(storage: &mut dyn Storage, env: Env) -> Result<Interval, ContractError> {
68+
pub fn init_epoch(storage: &mut dyn Storage, env: Env) -> Result<Interval, ContractError> {
6869
let epoch = Interval::init_epoch(env);
6970
storage::save_epoch(storage, &epoch)?;
7071
Ok(epoch)
7172
}
7273

73-
pub fn try_advance_epoch(env: Env, storage: &mut dyn Storage) -> Result<Response, ContractError> {
74+
pub fn try_advance_epoch(
75+
env: Env,
76+
storage: &mut dyn Storage,
77+
sender: String,
78+
) -> Result<Response, ContractError> {
7479
// in theory, we could have just changed the state and relied on its reversal upon failed
7580
// execution, but better safe than sorry and do not modify the state at all unless we know
7681
// all checks have succeeded.
77-
let current_epoch = if let Some(epoch) = storage::current_epoch(storage)? {
78-
epoch
79-
} else {
80-
let epoch = init_epoch(storage, env)?;
81-
return Ok(Response::new().add_event(new_advance_interval_event(epoch)));
82-
};
8382

83+
// Only rewarding validator can attempt to advance epoch
84+
85+
is_authorized(sender, storage)?;
86+
87+
let current_epoch = storage::current_epoch(storage)?;
8488
if current_epoch.is_over(env.clone()) {
8589
let next_epoch = current_epoch.next_on_chain(env);
8690

@@ -102,9 +106,8 @@ mod tests {
102106
use crate::support::tests::test_helpers;
103107
use cosmwasm_std::testing::{mock_env, mock_info};
104108
use cosmwasm_std::Timestamp;
105-
use mixnet_contract_common::{Interval, RewardedSetNodeStatus};
106-
use std::time::Duration;
107-
use time::OffsetDateTime;
109+
use mixnet_contract_common::RewardedSetNodeStatus;
110+
use mixnet_params_storage::rewarding_validator_address;
108111

109112
#[test]
110113
fn writing_rewarded_set() {
@@ -232,22 +235,15 @@ mod tests {
232235
fn advancing_epoch() {
233236
let mut env = mock_env();
234237
let mut deps = test_helpers::init_contract();
235-
236-
// 1609459200 = 2021-01-01
237-
// 1640995200 = 2022-01-01
238-
// 1641081600 = 2022-01-02
239-
// 1643673600 = 2022-02-01
240-
// 1672531200 = 2023-01-01
238+
let sender = rewarding_validator_address(&deps.storage).unwrap();
241239

242240
let _current_epoch = init_epoch(&mut deps.storage, env.clone()).unwrap();
243241

244242
// Works as its after the current epoch
245243
env.block.time = Timestamp::from_seconds(1641081600);
246-
assert!(try_advance_epoch(env.clone(), deps.as_mut().storage).is_ok());
244+
assert!(try_advance_epoch(env.clone(), deps.as_mut().storage, sender.clone()).is_ok());
247245

248-
let current_epoch = crate::interval::storage::current_epoch(&mut deps.storage)
249-
.unwrap()
250-
.unwrap();
246+
let current_epoch = crate::interval::storage::current_epoch(&mut deps.storage).unwrap();
251247

252248
// same if the current blocktime is set to BEFORE the first interval has even begun
253249
// (say we decided to set the first interval to be some time in the future at initialisation)
@@ -258,7 +254,7 @@ mod tests {
258254
epoch_start: current_epoch.start_unix_timestamp(),
259255
epoch_end: current_epoch.end_unix_timestamp()
260256
}),
261-
try_advance_epoch(env.clone(), deps.as_mut().storage)
257+
try_advance_epoch(env.clone(), deps.as_mut().storage, sender.clone(),)
262258
);
263259

264260
// works otherwise
@@ -271,7 +267,7 @@ mod tests {
271267
Response::new().add_event(new_advance_interval_event(expected_new_epoch));
272268
assert_eq!(
273269
Ok(expected_response),
274-
try_advance_epoch(env.clone(), deps.as_mut().storage)
270+
try_advance_epoch(env.clone(), deps.as_mut().storage, sender)
275271
);
276272

277273
// interval way back in the past (i.e. 'somebody' failed to advance it for a long time)
@@ -280,9 +276,10 @@ mod tests {
280276
let expected_new_epoch = current_epoch.next_on_chain(env.clone());
281277
let expected_response =
282278
Response::new().add_event(new_advance_interval_event(expected_new_epoch));
279+
let sender = rewarding_validator_address(&deps.storage).unwrap();
283280
assert_eq!(
284281
Ok(expected_response),
285-
try_advance_epoch(env.clone(), deps.as_mut().storage)
282+
try_advance_epoch(env.clone(), deps.as_mut().storage, sender)
286283
);
287284
}
288285
}

0 commit comments

Comments
 (0)