Feature/dkg snapshot epoch (#5900)
* define storage item for holding historical DKG state * make all epoch storage operations go through proxy functions * make each saving action also apply to the historical item * removed usage of update_epoch function * test correct save heights * exposed query for the epoch state at specified height * regenerated contract schema * restored default cw-plus behaviour as in hindsight it makes more sense
This commit is contained in:
committed by
GitHub
parent
6c1149708b
commit
e9165763b6
Generated
+1
@@ -1091,6 +1091,7 @@ dependencies = [
|
||||
name = "nym-coconut-dkg"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
|
||||
@@ -29,6 +29,7 @@ cw4 = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
easy-addr = { path = "../../common/cosmwasm-smart-contracts/easy_addr" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
|
||||
@@ -361,6 +361,29 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_epoch_state_at_height"
|
||||
],
|
||||
"properties": {
|
||||
"get_epoch_state_at_height": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2036,6 +2059,271 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_epoch_state_at_height": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Nullable_Epoch",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Epoch"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Epoch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"state",
|
||||
"state_progress",
|
||||
"time_configuration"
|
||||
],
|
||||
"properties": {
|
||||
"deadline": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
},
|
||||
"state_progress": {
|
||||
"$ref": "#/definitions/StateProgress"
|
||||
},
|
||||
"time_configuration": {
|
||||
"$ref": "#/definitions/TimeConfiguration"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"EpochState": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"public_key_submission"
|
||||
],
|
||||
"properties": {
|
||||
"public_key_submission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_exchange"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_exchange": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_submission"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_submission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_validation"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_validation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_finalization"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_finalization": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"StateProgress": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"registered_dealers",
|
||||
"registered_resharing_dealers",
|
||||
"submitted_dealings",
|
||||
"submitted_key_shares",
|
||||
"verified_keys"
|
||||
],
|
||||
"properties": {
|
||||
"registered_dealers": {
|
||||
"description": "Counts the number of dealers that have registered in this epoch.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"registered_resharing_dealers": {
|
||||
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_dealings": {
|
||||
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_key_shares": {
|
||||
"description": "Counts the number of submitted verification key shared from the dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verified_keys": {
|
||||
"description": "Counts the number of verified key shares.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TimeConfiguration": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_exchange_time_secs",
|
||||
"in_progress_time_secs",
|
||||
"public_key_submission_time_secs",
|
||||
"verification_key_finalization_time_secs",
|
||||
"verification_key_submission_time_secs",
|
||||
"verification_key_validation_time_secs"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_exchange_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"in_progress_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"public_key_submission_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_finalization_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_submission_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_validation_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Timestamp": {
|
||||
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Uint64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Uint64": {
|
||||
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_epoch_threshold": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "uint64",
|
||||
|
||||
@@ -28,6 +28,29 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_epoch_state_at_height"
|
||||
],
|
||||
"properties": {
|
||||
"get_epoch_state_at_height": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"height"
|
||||
],
|
||||
"properties": {
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Nullable_Epoch",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Epoch"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"definitions": {
|
||||
"Epoch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"state",
|
||||
"state_progress",
|
||||
"time_configuration"
|
||||
],
|
||||
"properties": {
|
||||
"deadline": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
},
|
||||
"state_progress": {
|
||||
"$ref": "#/definitions/StateProgress"
|
||||
},
|
||||
"time_configuration": {
|
||||
"$ref": "#/definitions/TimeConfiguration"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"EpochState": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"public_key_submission"
|
||||
],
|
||||
"properties": {
|
||||
"public_key_submission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_exchange"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_exchange": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_submission"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_submission": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_validation"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_validation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"verification_key_finalization"
|
||||
],
|
||||
"properties": {
|
||||
"verification_key_finalization": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"StateProgress": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"registered_dealers",
|
||||
"registered_resharing_dealers",
|
||||
"submitted_dealings",
|
||||
"submitted_key_shares",
|
||||
"verified_keys"
|
||||
],
|
||||
"properties": {
|
||||
"registered_dealers": {
|
||||
"description": "Counts the number of dealers that have registered in this epoch.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"registered_resharing_dealers": {
|
||||
"description": "Counts the number of resharing dealers that have registered in this epoch. This field is only populated during a resharing exchange. It is always <= registered_dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_dealings": {
|
||||
"description": "Counts the number of fully received dealings (i.e. full chunks) from all the allowed dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"submitted_key_shares": {
|
||||
"description": "Counts the number of submitted verification key shared from the dealers.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verified_keys": {
|
||||
"description": "Counts the number of verified key shares.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"TimeConfiguration": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_exchange_time_secs",
|
||||
"in_progress_time_secs",
|
||||
"public_key_submission_time_secs",
|
||||
"verification_key_finalization_time_secs",
|
||||
"verification_key_submission_time_secs",
|
||||
"verification_key_validation_time_secs"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_exchange_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"in_progress_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"public_key_submission_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_finalization_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_submission_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"verification_key_validation_time_secs": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Timestamp": {
|
||||
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Uint64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Uint64": {
|
||||
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,14 @@ use crate::dealings::queries::{
|
||||
use crate::dealings::transactions::{try_commit_dealings_chunk, try_submit_dealings_metadata};
|
||||
use crate::epoch_state::queries::{
|
||||
query_can_advance_state, query_current_epoch, query_current_epoch_threshold,
|
||||
query_epoch_threshold,
|
||||
query_epoch_at_height, query_epoch_threshold,
|
||||
};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, EPOCH_THRESHOLDS, THRESHOLD};
|
||||
use crate::epoch_state::storage::save_epoch;
|
||||
use crate::epoch_state::transactions::{
|
||||
try_advance_epoch_state, try_initiate_dkg, try_trigger_reset, try_trigger_resharing,
|
||||
};
|
||||
use crate::error::ContractError;
|
||||
use crate::queued_migrations::introduce_historical_epochs;
|
||||
use crate::state::queries::query_state;
|
||||
use crate::state::storage::{DKG_ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::{query_vk_share, query_vk_shares_paged};
|
||||
@@ -68,8 +69,9 @@ pub fn instantiate(
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH.save(
|
||||
save_epoch(
|
||||
deps.storage,
|
||||
env.block.height,
|
||||
&Epoch::new(
|
||||
EpochState::WaitingInitialisation,
|
||||
0,
|
||||
@@ -101,6 +103,7 @@ pub fn execute(
|
||||
resharing,
|
||||
} => try_add_dealer(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
@@ -119,7 +122,7 @@ pub fn execute(
|
||||
try_commit_verification_key_share(deps, env, info, share, resharing)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => {
|
||||
try_verify_verification_key_share(deps, info, owner, resharing)
|
||||
try_verify_verification_key_share(deps, env, info, owner, resharing)
|
||||
}
|
||||
ExecuteMsg::AdvanceEpochState {} => try_advance_epoch_state(deps, env),
|
||||
ExecuteMsg::TriggerReset {} => try_trigger_reset(deps, env, info),
|
||||
@@ -132,6 +135,9 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
let response = match msg {
|
||||
QueryMsg::GetState {} => to_json_binary(&query_state(deps.storage)?)?,
|
||||
QueryMsg::GetCurrentEpochState {} => to_json_binary(&query_current_epoch(deps.storage)?)?,
|
||||
QueryMsg::GetEpochStateAtHeight { height } => {
|
||||
to_json_binary(&query_epoch_at_height(deps.storage, height)?)?
|
||||
}
|
||||
QueryMsg::CanAdvanceState {} => {
|
||||
to_json_binary(&query_can_advance_state(deps.storage, env)?)?
|
||||
}
|
||||
@@ -242,16 +248,11 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
set_build_information!(deps.storage)?;
|
||||
cw2::ensure_from_older_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
// MAINNET MIGRATION ASSERTION
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
assert_eq!(0, epoch.epoch_id);
|
||||
|
||||
let threshold = THRESHOLD.load(deps.storage)?;
|
||||
EPOCH_THRESHOLDS.save(deps.storage, 0, &threshold)?;
|
||||
introduce_historical_epochs(deps, env)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
@@ -355,7 +356,7 @@ mod tests {
|
||||
let api = MockApi::default();
|
||||
const MEMBER_SIZE: usize = 100;
|
||||
let members: [Addr; MEMBER_SIZE] =
|
||||
std::array::from_fn(|idx| api.addr_make(&format!("member{}", idx)));
|
||||
std::array::from_fn(|idx| api.addr_make(&format!("member{idx}")));
|
||||
|
||||
let mut app = AppBuilder::new().build(|router, _, storage| {
|
||||
router
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::dealers::storage::{
|
||||
self, get_dealer_details, get_dealer_index, get_registration_details, DEALERS_INDICES,
|
||||
EPOCH_DEALERS_MAP,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::load_current_epoch;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_coconut_dkg_common::dealer::{
|
||||
@@ -23,7 +23,7 @@ pub fn query_registered_dealer_details(
|
||||
|
||||
let epoch_id = match epoch_id {
|
||||
Some(epoch_id) => epoch_id,
|
||||
None => CURRENT_EPOCH.load(deps.storage)?.epoch_id,
|
||||
None => load_current_epoch(deps.storage)?.epoch_id,
|
||||
};
|
||||
|
||||
Ok(RegisteredDealerDetails {
|
||||
@@ -36,7 +36,7 @@ pub fn query_dealer_details(
|
||||
dealer_address: String,
|
||||
) -> StdResult<DealerDetailsResponse> {
|
||||
let addr = deps.api.addr_validate(&dealer_address)?;
|
||||
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
|
||||
let current_epoch_id = load_current_epoch(deps.storage)?.epoch_id;
|
||||
|
||||
// if the address has registration data for the current epoch, it means it's an active dealer
|
||||
if let Ok(dealer_details) = get_dealer_details(deps.storage, &addr, current_epoch_id) {
|
||||
@@ -157,7 +157,7 @@ pub fn query_current_dealers_paged(
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedDealerResponse> {
|
||||
let current_epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
|
||||
let current_epoch_id = load_current_epoch(deps.storage)?.epoch_id;
|
||||
query_epoch_dealers_paged(deps, current_epoch_id, start_after, limit)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
use crate::dealers::storage::{
|
||||
get_or_assign_index, is_dealer, save_dealer_details_if_not_a_dealer,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::{load_current_epoch, save_epoch};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::STATE;
|
||||
use crate::Dealer;
|
||||
use cosmwasm_std::{Deps, DepsMut, MessageInfo, Response, StdResult};
|
||||
use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::dealer::DealerRegistrationDetails;
|
||||
use nym_coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
|
||||
@@ -28,13 +28,14 @@ fn ensure_group_member(deps: Deps, dealer: Dealer) -> Result<(), ContractError>
|
||||
// for a recurring dealer just let it refresh the keys without having to do all the storage operations
|
||||
pub fn try_add_dealer(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: String,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let epoch = load_current_epoch(deps.storage)?;
|
||||
check_epoch_state(deps.storage, EpochState::PublicKeySubmission { resharing })?;
|
||||
|
||||
// make sure this potential dealer actually belong to the group
|
||||
@@ -68,16 +69,16 @@ pub fn try_add_dealer(
|
||||
);
|
||||
|
||||
// increment the number of registered dealers
|
||||
CURRENT_EPOCH.update(deps.storage, |epoch| -> StdResult<_> {
|
||||
let mut updated_epoch = epoch;
|
||||
{
|
||||
let current_epoch = load_current_epoch(deps.storage)?;
|
||||
let mut updated_epoch = current_epoch;
|
||||
updated_epoch.state_progress.registered_dealers += 1;
|
||||
|
||||
if is_resharing_dealer {
|
||||
updated_epoch.state_progress.registered_resharing_dealers += 1;
|
||||
}
|
||||
|
||||
Ok(updated_epoch)
|
||||
})?;
|
||||
save_epoch(deps.storage, env.block.height, &updated_epoch)?;
|
||||
}
|
||||
|
||||
Ok(Response::new().add_attribute("node_index", node_index.to_string()))
|
||||
}
|
||||
@@ -115,10 +116,11 @@ pub(crate) mod tests {
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let ret = try_add_dealer(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::dealers::storage::ensure_dealer;
|
||||
use crate::dealings::storage::{
|
||||
metadata_exists, must_read_metadata, store_metadata, StoredDealing,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::{load_current_epoch, save_epoch};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::STATE;
|
||||
@@ -42,7 +42,7 @@ pub fn try_submit_dealings_metadata(
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let epoch = load_current_epoch(deps.storage)?;
|
||||
let state = STATE.load(deps.storage)?;
|
||||
|
||||
ensure_permission(deps.storage, &info.sender, epoch.epoch_id, resharing)?;
|
||||
@@ -137,7 +137,7 @@ pub fn try_commit_dealings_chunk(
|
||||
// note: checking permissions is implicit as if the metadata exists,
|
||||
// the sender must have been allowed to submit it
|
||||
|
||||
let mut epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let mut epoch = load_current_epoch(deps.storage)?;
|
||||
|
||||
// read meta
|
||||
let mut metadata = must_read_metadata(
|
||||
@@ -197,7 +197,7 @@ pub fn try_commit_dealings_chunk(
|
||||
// there won't be a lot of them
|
||||
if metadata.is_complete() {
|
||||
epoch.state_progress.submitted_dealings += 1;
|
||||
CURRENT_EPOCH.save(deps.storage, &epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &epoch)?;
|
||||
}
|
||||
|
||||
Ok(Response::new())
|
||||
@@ -309,12 +309,10 @@ pub(crate) mod tests {
|
||||
);
|
||||
|
||||
// same index, but next epoch
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.epoch_id += 1;
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
let mut epoch = load_current_epoch(&deps.storage).unwrap();
|
||||
epoch.epoch_id += 1;
|
||||
save_epoch(deps.as_mut().storage, epoch.epoch_id, &epoch).unwrap();
|
||||
|
||||
re_register_dealer(deps.as_mut(), &info.sender);
|
||||
|
||||
try_submit_dealings_metadata(
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, EPOCH_THRESHOLDS, THRESHOLD};
|
||||
use crate::epoch_state::storage::{
|
||||
load_current_epoch, EPOCH_THRESHOLDS, HISTORICAL_EPOCH, THRESHOLD,
|
||||
};
|
||||
use crate::epoch_state::utils::check_state_completion;
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{Env, Storage};
|
||||
use cosmwasm_std::{Env, StdResult, Storage};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochId, EpochState, StateAdvanceResponse};
|
||||
|
||||
pub(crate) fn query_can_advance_state(
|
||||
storage: &dyn Storage,
|
||||
env: Env,
|
||||
) -> Result<StateAdvanceResponse, ContractError> {
|
||||
let epoch = CURRENT_EPOCH.load(storage)?;
|
||||
let epoch = load_current_epoch(storage)?;
|
||||
|
||||
if epoch.state == EpochState::WaitingInitialisation {
|
||||
return Ok(StateAdvanceResponse::default());
|
||||
@@ -34,9 +36,14 @@ pub(crate) fn query_can_advance_state(
|
||||
}
|
||||
|
||||
pub(crate) fn query_current_epoch(storage: &dyn Storage) -> Result<Epoch, ContractError> {
|
||||
CURRENT_EPOCH
|
||||
.load(storage)
|
||||
.map_err(|_| ContractError::EpochNotInitialised)
|
||||
load_current_epoch(storage).map_err(|_| ContractError::EpochNotInitialised)
|
||||
}
|
||||
|
||||
pub(crate) fn query_epoch_at_height(
|
||||
storage: &dyn Storage,
|
||||
height: u64,
|
||||
) -> StdResult<Option<Epoch>> {
|
||||
HISTORICAL_EPOCH.may_load_at_height(storage, height)
|
||||
}
|
||||
|
||||
pub(crate) fn query_current_epoch_threshold(
|
||||
|
||||
@@ -1,11 +1,225 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cw_storage_plus::{Item, Map};
|
||||
use cosmwasm_std::{StdResult, Storage};
|
||||
use cw_storage_plus::{Item, Map, SnapshotItem, Strategy};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochId};
|
||||
|
||||
#[deprecated]
|
||||
// leave old values in storage for backwards compatibility, but make sure everything in the contract
|
||||
// uses the new reference
|
||||
pub(crate) const CURRENT_EPOCH: Item<Epoch> = Item::new("current_epoch");
|
||||
pub const HISTORICAL_EPOCH: SnapshotItem<Epoch> = SnapshotItem::new(
|
||||
"historical_epoch",
|
||||
"historical_epoch__checkpoints",
|
||||
"historical_epoch__changelog",
|
||||
Strategy::EveryBlock,
|
||||
);
|
||||
|
||||
pub const THRESHOLD: Item<u64> = Item::new("threshold");
|
||||
|
||||
pub const EPOCH_THRESHOLDS: Map<EpochId, u64> = Map::new("epoch_thresholds");
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub fn save_epoch(storage: &mut dyn Storage, height: u64, epoch: &Epoch) -> StdResult<()> {
|
||||
CURRENT_EPOCH.save(storage, epoch)?;
|
||||
HISTORICAL_EPOCH.save(storage, epoch, height)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub fn load_current_epoch(storage: &dyn Storage) -> StdResult<Epoch> {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let current = CURRENT_EPOCH.load(storage);
|
||||
let historical = HISTORICAL_EPOCH.load(storage);
|
||||
debug_assert_eq!(current, historical);
|
||||
}
|
||||
HISTORICAL_EPOCH.load(storage)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env};
|
||||
use cosmwasm_std::{Addr, Env};
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[test]
|
||||
fn full_dkg_correctly_updates_historical_epoch() -> anyhow::Result<()> {
|
||||
struct EnvWrapper {
|
||||
env: Env,
|
||||
}
|
||||
|
||||
impl EnvWrapper {
|
||||
fn next_block(&mut self) {
|
||||
self.env.block.height += 1;
|
||||
self.env.block.time = self.env.block.time.plus_seconds(5);
|
||||
}
|
||||
|
||||
fn height(&self) -> u64 {
|
||||
self.block.height
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EnvWrapper {
|
||||
type Target = Env;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.env
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for EnvWrapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.env
|
||||
}
|
||||
}
|
||||
|
||||
let mut empty_deps = mock_dependencies();
|
||||
|
||||
// before contract is initialised, there's nothing saved
|
||||
assert!(HISTORICAL_EPOCH
|
||||
.may_load(empty_deps.as_mut().storage)?
|
||||
.is_none());
|
||||
|
||||
let mut deps = init_contract();
|
||||
let mut env = EnvWrapper { env: mock_env() };
|
||||
|
||||
let init_height = env.height();
|
||||
// after init it has initial state
|
||||
assert_eq!(HISTORICAL_EPOCH.load(deps.as_mut().storage)?.epoch_id, 0);
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::WaitingInitialisation
|
||||
);
|
||||
|
||||
env.next_block();
|
||||
let pub_key_submission_height = env.height();
|
||||
try_initiate_dkg(
|
||||
deps.as_mut(),
|
||||
(*env).clone(),
|
||||
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
|
||||
)?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100000);
|
||||
env.next_block();
|
||||
let dealing_exchange_height = env.height();
|
||||
try_advance_epoch_state(deps.as_mut(), (*env).clone())?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100000);
|
||||
env.next_block();
|
||||
let verification_key_submission_height = env.height();
|
||||
try_advance_epoch_state(deps.as_mut(), (*env).clone())?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100000);
|
||||
env.next_block();
|
||||
let verification_key_validation_height = env.height();
|
||||
try_advance_epoch_state(deps.as_mut(), (*env).clone())?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100000);
|
||||
env.next_block();
|
||||
let verification_key_finalization_height = env.height();
|
||||
try_advance_epoch_state(deps.as_mut(), (*env).clone())?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(100000);
|
||||
env.next_block();
|
||||
let in_progress_height = env.height();
|
||||
try_advance_epoch_state(deps.as_mut(), (*env).clone())?;
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH.load(deps.as_mut().storage)?.state,
|
||||
EpochState::InProgress {}
|
||||
);
|
||||
|
||||
// check old data
|
||||
assert!(HISTORICAL_EPOCH
|
||||
.may_load_at_height(deps.as_mut().storage, init_height - 1)?
|
||||
.is_none());
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(deps.as_mut().storage, init_height + 1)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::WaitingInitialisation
|
||||
);
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(deps.as_mut().storage, pub_key_submission_height + 1)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(deps.as_mut().storage, dealing_exchange_height + 1)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(
|
||||
deps.as_mut().storage,
|
||||
verification_key_submission_height + 1
|
||||
)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(
|
||||
deps.as_mut().storage,
|
||||
verification_key_validation_height + 1
|
||||
)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(
|
||||
deps.as_mut().storage,
|
||||
verification_key_finalization_height + 1
|
||||
)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
HISTORICAL_EPOCH
|
||||
.may_load_at_height(deps.as_mut().storage, in_progress_height + 1)?
|
||||
.unwrap()
|
||||
.state,
|
||||
EpochState::InProgress
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, EPOCH_THRESHOLDS, THRESHOLD};
|
||||
use crate::epoch_state::storage::{load_current_epoch, save_epoch, EPOCH_THRESHOLDS, THRESHOLD};
|
||||
use crate::epoch_state::transactions::reset_dkg_state;
|
||||
use crate::epoch_state::utils::check_state_completion;
|
||||
use crate::error::ContractError;
|
||||
@@ -39,7 +39,7 @@ fn ensure_can_advance_state(
|
||||
pub fn try_advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
// TODO: the only case where this can retrigger itself is when insufficient number of parties completed it, i.e. we don't have threshold
|
||||
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let current_epoch = load_current_epoch(deps.storage)?;
|
||||
|
||||
// checks whether the given phase has either completed or reached its deadline
|
||||
ensure_can_advance_state(deps.as_ref(), &env, ¤t_epoch)?;
|
||||
@@ -82,7 +82,7 @@ pub fn try_advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response,
|
||||
};
|
||||
|
||||
// update the epoch state
|
||||
CURRENT_EPOCH.save(deps.storage, &next_epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &next_epoch)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
@@ -90,23 +90,33 @@ pub fn try_advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response,
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::load_current_epoch;
|
||||
use crate::epoch_state::transactions::try_initiate_dkg;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::state::storage::STATE;
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{message_info, mock_env};
|
||||
use cosmwasm_std::{Addr, StdResult, Storage};
|
||||
use cosmwasm_std::{Addr, Storage};
|
||||
use nym_coconut_dkg_common::types::TimeConfiguration;
|
||||
|
||||
fn update_epoch<A>(storage: &mut dyn Storage, env: &Env, action: A)
|
||||
where
|
||||
A: Fn(Epoch) -> Epoch,
|
||||
{
|
||||
let current = load_current_epoch(storage).unwrap();
|
||||
let updated = action(current);
|
||||
save_epoch(storage, env.block.height, &updated).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_circuit_advance_state() {
|
||||
fn epoch_in_state(state: EpochState, env: &Env) -> Epoch {
|
||||
Epoch::new(state, 0, Default::default(), env.block.time)
|
||||
}
|
||||
|
||||
fn set_epoch(storage: &mut dyn Storage, epoch: Epoch) {
|
||||
CURRENT_EPOCH.save(storage, &epoch).unwrap();
|
||||
fn set_epoch(storage: &mut dyn Storage, env: &Env, epoch: Epoch) {
|
||||
save_epoch(storage, env.block.height, &epoch).unwrap();
|
||||
}
|
||||
|
||||
let mut deps = init_contract();
|
||||
@@ -114,18 +124,18 @@ mod tests {
|
||||
|
||||
// it's never possible to short-circuit `WaitingInitialisation`
|
||||
let epoch = epoch_in_state(EpochState::WaitingInitialisation, &env);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
// neither PublicKeySubmission (in either resharing or non-resharing)
|
||||
let epoch = epoch_in_state(EpochState::PublicKeySubmission { resharing: false }, &env);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
let epoch = epoch_in_state(EpochState::PublicKeySubmission { resharing: true }, &env);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -138,7 +148,7 @@ mod tests {
|
||||
// no dealings
|
||||
let mut epoch = epoch_in_state(EpochState::DealingExchange { resharing: false }, &env);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -146,7 +156,7 @@ mod tests {
|
||||
let mut epoch = epoch_in_state(EpochState::DealingExchange { resharing: false }, &env);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_dealings = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -154,7 +164,7 @@ mod tests {
|
||||
let mut epoch = epoch_in_state(EpochState::DealingExchange { resharing: false }, &env);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_dealings = key_size * 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(
|
||||
@@ -167,7 +177,7 @@ mod tests {
|
||||
let mut epoch = epoch_in_state(EpochState::DealingExchange { resharing: true }, &env);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.registered_resharing_dealers = 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -176,7 +186,7 @@ mod tests {
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.registered_resharing_dealers = 4;
|
||||
epoch.state_progress.submitted_dealings = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -185,7 +195,7 @@ mod tests {
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.registered_resharing_dealers = 4;
|
||||
epoch.state_progress.submitted_dealings = key_size * 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(
|
||||
@@ -200,7 +210,7 @@ mod tests {
|
||||
&env,
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -209,7 +219,7 @@ mod tests {
|
||||
&env,
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -219,7 +229,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_key_shares = 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -229,7 +239,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_key_shares = 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -239,7 +249,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(
|
||||
@@ -254,7 +264,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.registered_dealers = 5;
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(
|
||||
@@ -268,7 +278,7 @@ mod tests {
|
||||
EpochState::VerificationKeyValidation { resharing: false },
|
||||
&env,
|
||||
);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -276,7 +286,7 @@ mod tests {
|
||||
EpochState::VerificationKeyValidation { resharing: true },
|
||||
&env,
|
||||
);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -286,7 +296,7 @@ mod tests {
|
||||
&env,
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -295,7 +305,7 @@ mod tests {
|
||||
&env,
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -305,7 +315,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
epoch.state_progress.verified_keys = 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -315,7 +325,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
epoch.state_progress.verified_keys = 4;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
|
||||
@@ -325,7 +335,7 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
epoch.state_progress.verified_keys = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(deps.as_ref().storage, EpochState::InProgress).unwrap();
|
||||
@@ -336,14 +346,14 @@ mod tests {
|
||||
);
|
||||
epoch.state_progress.submitted_key_shares = 5;
|
||||
epoch.state_progress.verified_keys = 5;
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_ok());
|
||||
check_epoch_state(deps.as_ref().storage, EpochState::InProgress).unwrap();
|
||||
|
||||
// it's never possible to short-circuit `InProgress`
|
||||
let epoch = epoch_in_state(EpochState::InProgress, &env);
|
||||
set_epoch(deps.as_mut().storage, epoch);
|
||||
set_epoch(deps.as_mut().storage, &env, epoch);
|
||||
let res = try_advance_epoch_state(deps.as_mut(), env.clone());
|
||||
assert!(res.is_err());
|
||||
}
|
||||
@@ -366,7 +376,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
@@ -390,19 +400,16 @@ mod tests {
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
|
||||
// add some dealers to prevent short-circuiting
|
||||
CURRENT_EPOCH
|
||||
.update(deps.as_mut().storage, |mut e| -> StdResult<_> {
|
||||
e.state_progress.registered_dealers = 42;
|
||||
Ok(e)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
update_epoch(deps.as_mut().storage, &env, |mut e| {
|
||||
e.state_progress.registered_dealers = 42;
|
||||
e
|
||||
});
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
@@ -425,7 +432,7 @@ mod tests {
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
@@ -452,7 +459,7 @@ mod tests {
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
@@ -478,16 +485,13 @@ mod tests {
|
||||
);
|
||||
|
||||
// add some key shares to prevent short-circuiting
|
||||
CURRENT_EPOCH
|
||||
.update(deps.as_mut().storage, |mut e| -> StdResult<_> {
|
||||
e.state_progress.submitted_key_shares = 42;
|
||||
Ok(e)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
update_epoch(deps.as_mut().storage, &env, |mut e| {
|
||||
e.state_progress.submitted_key_shares = 42;
|
||||
e
|
||||
});
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
@@ -512,16 +516,14 @@ mod tests {
|
||||
);
|
||||
|
||||
// add some finalized keys to prevent reset
|
||||
CURRENT_EPOCH
|
||||
.update(deps.as_mut().storage, |mut e| -> StdResult<_> {
|
||||
e.state_progress.verified_keys = 42;
|
||||
Ok(e)
|
||||
})
|
||||
.unwrap();
|
||||
update_epoch(deps.as_mut().storage, &env, |mut e| {
|
||||
e.state_progress.verified_keys = 42;
|
||||
e
|
||||
});
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.deadline.unwrap(),
|
||||
@@ -547,9 +549,9 @@ mod tests {
|
||||
|
||||
// Group hasn't changed, so we remain in the same epoch, with updated finish timestamp
|
||||
env.block.time = env.block.time.plus_seconds(100);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let prev_epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let curr_epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
let mut expected_epoch = Epoch::new(
|
||||
EpochState::InProgress,
|
||||
prev_epoch.epoch_id,
|
||||
@@ -570,11 +572,11 @@ mod tests {
|
||||
|
||||
// fewer than the threshold
|
||||
epoch.state_progress.verified_keys = 41;
|
||||
CURRENT_EPOCH.save(deps.as_mut().storage, &epoch).unwrap();
|
||||
save_epoch(deps.as_mut().storage, env.block.height, &epoch).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(5000000);
|
||||
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let curr_epoch = load_current_epoch(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: false },
|
||||
epoch.epoch_id + 1,
|
||||
@@ -598,12 +600,10 @@ mod tests {
|
||||
|
||||
assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none());
|
||||
|
||||
CURRENT_EPOCH
|
||||
.update(deps.as_mut().storage, |mut e| -> StdResult<_> {
|
||||
e.state_progress.registered_dealers = 100;
|
||||
Ok(e)
|
||||
})
|
||||
.unwrap();
|
||||
update_epoch(deps.as_mut().storage, &env, |mut e| {
|
||||
e.state_progress.registered_dealers = 100;
|
||||
e
|
||||
});
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::epoch_state::storage::{load_current_epoch, save_epoch, THRESHOLD};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::DKG_ADMIN;
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Storage};
|
||||
@@ -29,7 +29,7 @@ pub(crate) fn try_initiate_dkg(
|
||||
// only the admin is allowed to kick start the process
|
||||
DKG_ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let epoch = load_current_epoch(deps.storage)?;
|
||||
if !matches!(epoch.state, EpochState::WaitingInitialisation) {
|
||||
return Err(ContractError::AlreadyInitialised);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub(crate) fn try_initiate_dkg(
|
||||
// the first exchange won't involve resharing
|
||||
let initial_state = EpochState::PublicKeySubmission { resharing: false };
|
||||
let initial_epoch = Epoch::new(initial_state, 0, epoch.time_configuration, env.block.time);
|
||||
CURRENT_EPOCH.save(deps.storage, &initial_epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &initial_epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn try_trigger_reset(
|
||||
) -> Result<Response, ContractError> {
|
||||
// only the admin is allowed to trigger DKG reset
|
||||
DKG_ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let current_epoch = load_current_epoch(deps.storage)?;
|
||||
|
||||
// only allow reset when the DKG exchange isn't in progress
|
||||
if !current_epoch.state.is_in_progress() {
|
||||
@@ -57,7 +57,7 @@ pub(crate) fn try_trigger_reset(
|
||||
}
|
||||
|
||||
let next_epoch = current_epoch.next_reset(env.block.time);
|
||||
CURRENT_EPOCH.save(deps.storage, &next_epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &next_epoch)?;
|
||||
|
||||
reset_dkg_state(deps.storage)?;
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(crate) fn try_trigger_resharing(
|
||||
) -> Result<Response, ContractError> {
|
||||
// only the admin is allowed to trigger DKG resharing
|
||||
DKG_ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let current_epoch = load_current_epoch(deps.storage)?;
|
||||
|
||||
// only allow resharing when the DKG exchange isn't in progress
|
||||
if !current_epoch.state.is_in_progress() {
|
||||
@@ -79,7 +79,7 @@ pub(crate) fn try_trigger_resharing(
|
||||
}
|
||||
|
||||
let next_epoch = current_epoch.next_resharing(env.block.time);
|
||||
CURRENT_EPOCH.save(deps.storage, &next_epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &next_epoch)?;
|
||||
|
||||
reset_dkg_state(deps.storage)?;
|
||||
|
||||
@@ -89,6 +89,7 @@ pub(crate) fn try_trigger_resharing(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::load_current_epoch;
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{message_info, mock_env};
|
||||
use cosmwasm_std::Addr;
|
||||
@@ -99,7 +100,7 @@ pub(crate) mod tests {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
let initial_epoch_info = load_current_epoch(&deps.storage).unwrap();
|
||||
assert!(initial_epoch_info.deadline.is_none());
|
||||
|
||||
let not_admin = deps.api.addr_make("not an admin");
|
||||
@@ -125,7 +126,7 @@ pub(crate) mod tests {
|
||||
assert_eq!(ContractError::AlreadyInitialised, res);
|
||||
|
||||
// sets the correct epoch data
|
||||
let epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
let epoch = load_current_epoch(&deps.storage).unwrap();
|
||||
assert_eq!(epoch.epoch_id, 0);
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::load_current_epoch;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::Storage;
|
||||
@@ -52,7 +52,7 @@ pub(crate) fn check_epoch_state(
|
||||
storage: &dyn Storage,
|
||||
against: EpochState,
|
||||
) -> Result<(), ContractError> {
|
||||
let epoch_state = CURRENT_EPOCH.load(storage)?.state;
|
||||
let epoch_state = load_current_epoch(storage)?.state;
|
||||
if epoch_state != against {
|
||||
Err(ContractError::IncorrectEpochState {
|
||||
current_state: epoch_state.to_string(),
|
||||
@@ -66,6 +66,7 @@ pub(crate) fn check_epoch_state(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::save_epoch;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::Timestamp;
|
||||
@@ -210,12 +211,12 @@ pub(crate) mod test {
|
||||
let env = mock_env();
|
||||
|
||||
for fixed_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
CURRENT_EPOCH
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Epoch::new(fixed_state, 0, TimeConfiguration::default(), env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
save_epoch(
|
||||
deps.as_mut().storage,
|
||||
env.block.height,
|
||||
&Epoch::new(fixed_state, 0, TimeConfiguration::default(), env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
for against_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
let ret = check_epoch_state(deps.as_mut().storage, against_state);
|
||||
if fixed_state == against_state {
|
||||
|
||||
@@ -10,6 +10,9 @@ use thiserror::Error;
|
||||
/// Custom errors for contract failure conditions.
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
#[error("could not perform contract migration: {comment}")]
|
||||
FailedMigration { comment: String },
|
||||
|
||||
#[error(transparent)]
|
||||
Std(#[from] StdError),
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ mod dealers;
|
||||
mod dealings;
|
||||
mod epoch_state;
|
||||
pub mod error;
|
||||
mod queued_migrations;
|
||||
mod state;
|
||||
mod support;
|
||||
mod verification_key_shares;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::HISTORICAL_EPOCH;
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{DepsMut, Env};
|
||||
|
||||
pub fn introduce_historical_epochs(deps: DepsMut, env: Env) -> Result<(), ContractError> {
|
||||
if HISTORICAL_EPOCH.may_load(deps.storage)?.is_some() {
|
||||
return Err(ContractError::FailedMigration {
|
||||
comment: "this migration has already been run before".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let current = crate::epoch_state::storage::CURRENT_EPOCH.load(deps.storage)?;
|
||||
// we won't have information on intermediate states prior to now, but that's not the end of the world
|
||||
HISTORICAL_EPOCH.save(deps.storage, ¤t, env.block.height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -12,8 +12,8 @@ pub const TEST_MIX_DENOM: &str = "unym";
|
||||
|
||||
pub fn vk_share_fixture(owner: &str, index: u64) -> ContractVKShare {
|
||||
ContractVKShare {
|
||||
share: format!("share{}", index),
|
||||
announce_address: format!("localhost:{}", index),
|
||||
share: format!("share{index}"),
|
||||
announce_address: format!("localhost:{index}"),
|
||||
node_index: index,
|
||||
owner: Addr::unchecked(owner),
|
||||
epoch_id: index,
|
||||
@@ -43,7 +43,7 @@ pub fn dealing_metadata_fixture() -> Vec<DealingChunkInfo> {
|
||||
|
||||
pub fn dealer_details_fixture(api: &MockApi, assigned_index: u64) -> DealerDetails {
|
||||
DealerDetails {
|
||||
address: api.addr_make(&format!("owner{}", assigned_index)),
|
||||
address: api.addr_make(&format!("owner{assigned_index}")),
|
||||
bte_public_key_with_proof: "".to_string(),
|
||||
ed25519_identity: "".to_string(),
|
||||
announce_address: "".to_string(),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::contract::instantiate;
|
||||
use crate::dealers::storage::{DEALERS_INDICES, EPOCH_DEALERS_MAP};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::load_current_epoch;
|
||||
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier};
|
||||
use cosmwasm_std::{
|
||||
from_json, to_json_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps,
|
||||
@@ -27,7 +27,7 @@ pub const MULTISIG_CONTRACT: &str = addr!("multisig contract address");
|
||||
pub(crate) static GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn re_register_dealer(deps: DepsMut, dealer: &Addr) {
|
||||
let epoch_id = CURRENT_EPOCH.load(deps.storage).unwrap().epoch_id;
|
||||
let epoch_id = load_current_epoch(deps.storage).unwrap().epoch_id;
|
||||
let previous = epoch_id - 1;
|
||||
let details = EPOCH_DEALERS_MAP
|
||||
.load(deps.storage, (previous, dealer))
|
||||
@@ -38,7 +38,7 @@ pub fn re_register_dealer(deps: DepsMut, dealer: &Addr) {
|
||||
}
|
||||
|
||||
pub fn add_current_dealer(deps: DepsMut<'_>, details: &DealerDetails) {
|
||||
let epoch_id = CURRENT_EPOCH.load(deps.storage).unwrap().epoch_id;
|
||||
let epoch_id = load_current_epoch(deps.storage).unwrap().epoch_id;
|
||||
insert_dealer(deps, epoch_id, details)
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ pub(crate) mod tests {
|
||||
let mut deps = init_contract();
|
||||
let limit = 2;
|
||||
for n in 0..1000 {
|
||||
let owner = format!("owner{}", n);
|
||||
let owner = format!("owner{n}");
|
||||
let vk_share = vk_share_fixture(&owner, 0);
|
||||
let sender = Addr::unchecked(owner);
|
||||
vk_shares()
|
||||
@@ -115,7 +115,7 @@ pub(crate) mod tests {
|
||||
fn vk_shares_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
for n in 0..1000 {
|
||||
let owner = format!("owner{}", n);
|
||||
let owner = format!("owner{n}");
|
||||
let vk_share = vk_share_fixture(&owner, 0);
|
||||
let sender = Addr::unchecked(owner);
|
||||
vk_shares()
|
||||
@@ -136,7 +136,7 @@ pub(crate) mod tests {
|
||||
fn vk_shares_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
for n in 0..1000 {
|
||||
let owner = format!("owner{}", n);
|
||||
let owner = format!("owner{n}");
|
||||
let vk_share = vk_share_fixture(&owner, 0);
|
||||
let sender = Addr::unchecked(owner);
|
||||
vk_shares()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::constants::BLOCK_TIME_FOR_VERIFICATION_SECS;
|
||||
use crate::dealers::storage::get_dealer_details;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::storage::{load_current_epoch, save_epoch};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::storage::{MULTISIG, STATE};
|
||||
@@ -25,7 +25,7 @@ pub fn try_commit_verification_key_share(
|
||||
deps.storage,
|
||||
EpochState::VerificationKeySubmission { resharing },
|
||||
)?;
|
||||
let mut epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let mut epoch = load_current_epoch(deps.storage)?;
|
||||
let epoch_id = epoch.epoch_id;
|
||||
|
||||
let details = get_dealer_details(deps.storage, &info.sender, epoch_id)?;
|
||||
@@ -43,7 +43,7 @@ pub fn try_commit_verification_key_share(
|
||||
node_index: details.assigned_index,
|
||||
announce_address: details.announce_address,
|
||||
owner: info.sender.clone(),
|
||||
epoch_id: CURRENT_EPOCH.load(deps.storage)?.epoch_id,
|
||||
epoch_id: load_current_epoch(deps.storage)?.epoch_id,
|
||||
verified: false,
|
||||
};
|
||||
vk_shares().save(deps.storage, (&info.sender, epoch_id), &data)?;
|
||||
@@ -60,13 +60,14 @@ pub fn try_commit_verification_key_share(
|
||||
)?;
|
||||
|
||||
epoch.state_progress.submitted_key_shares += 1;
|
||||
CURRENT_EPOCH.save(deps.storage, &epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &epoch)?;
|
||||
|
||||
Ok(Response::new().add_message(msg))
|
||||
}
|
||||
|
||||
pub fn try_verify_verification_key_share(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
owner: String,
|
||||
resharing: bool,
|
||||
@@ -77,7 +78,7 @@ pub fn try_verify_verification_key_share(
|
||||
deps.storage,
|
||||
EpochState::VerificationKeyFinalization { resharing },
|
||||
)?;
|
||||
let mut epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let mut epoch = load_current_epoch(deps.storage)?;
|
||||
let epoch_id = epoch.epoch_id;
|
||||
|
||||
MULTISIG.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
@@ -93,7 +94,7 @@ pub fn try_verify_verification_key_share(
|
||||
})?;
|
||||
|
||||
epoch.state_progress.verified_keys += 1;
|
||||
CURRENT_EPOCH.save(deps.storage, &epoch)?;
|
||||
save_epoch(deps.storage, env.block.height, &epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
@@ -259,9 +260,14 @@ mod tests {
|
||||
let owner = deps.api.addr_make("owner").to_string();
|
||||
let multisig_info = message_info(&Addr::unchecked(MULTISIG_CONTRACT), &[]);
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret = try_verify_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
owner.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
@@ -291,15 +297,26 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), info, owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret = try_verify_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info,
|
||||
owner.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::Admin(AdminError::NotAdmin {}));
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret = try_verify_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
multisig_info,
|
||||
owner.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::NoCommitForOwner {
|
||||
@@ -356,9 +373,15 @@ mod tests {
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
try_advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.to_string(), false)
|
||||
.unwrap();
|
||||
try_verify_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env,
|
||||
multisig_info,
|
||||
owner.to_string(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user