Migrate legacy delegation data (#771)

* Skip ReadOnlyBucket deserialization errors

* empty migration

* clippy

* cargo schema

* Drop invalid delegation data

* Dont drop old data

* Add todo

* Unify on type param

* gateways are different

* cargo fmt

Co-authored-by: Drazen Urch <durch@users.noreply.guthub.com>
This commit is contained in:
Drazen Urch
2021-09-14 15:15:26 +02:00
committed by GitHub
parent 26b032c15c
commit e84af4f601
10 changed files with 851 additions and 198 deletions
+2 -2
View File
@@ -727,9 +727,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "syn"
version = "1.0.60"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
dependencies = [
"proc-macro2",
"quote",
+349
View File
@@ -0,0 +1,349 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"anyOf": [
{
"type": "object",
"required": [
"bond_mixnode"
],
"properties": {
"bond_mixnode": {
"type": "object",
"required": [
"mix_node"
],
"properties": {
"mix_node": {
"$ref": "#/definitions/MixNode"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unbond_mixnode"
],
"properties": {
"unbond_mixnode": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"bond_gateway"
],
"properties": {
"bond_gateway": {
"type": "object",
"required": [
"gateway"
],
"properties": {
"gateway": {
"$ref": "#/definitions/Gateway"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"unbond_gateway"
],
"properties": {
"unbond_gateway": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"update_state_params"
],
"properties": {
"update_state_params": {
"$ref": "#/definitions/StateParams"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delegate_to_mixnode"
],
"properties": {
"delegate_to_mixnode": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"undelegate_from_mixnode"
],
"properties": {
"undelegate_from_mixnode": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delegate_to_gateway"
],
"properties": {
"delegate_to_gateway": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"undelegate_from_gateway"
],
"properties": {
"undelegate_from_gateway": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"reward_mixnode"
],
"properties": {
"reward_mixnode": {
"type": "object",
"required": [
"identity",
"uptime"
],
"properties": {
"identity": {
"type": "string"
},
"uptime": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"reward_gateway"
],
"properties": {
"reward_gateway": {
"type": "object",
"required": [
"identity",
"uptime"
],
"properties": {
"identity": {
"type": "string"
},
"uptime": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
}
},
"additionalProperties": false
}
],
"definitions": {
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"Gateway": {
"type": "object",
"required": [
"clients_port",
"host",
"identity_key",
"location",
"mix_port",
"sphinx_key",
"version"
],
"properties": {
"clients_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients",
"type": "string"
},
"location": {
"type": "string"
},
"mix_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"MixNode": {
"type": "object",
"required": [
"host",
"http_api_port",
"identity_key",
"mix_port",
"sphinx_key",
"verloc_port",
"version"
],
"properties": {
"host": {
"type": "string"
},
"http_api_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"mix_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"sphinx_key": {
"type": "string"
},
"verloc_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"version": {
"type": "string"
}
}
},
"StateParams": {
"type": "object",
"required": [
"epoch_length",
"gateway_bond_reward_rate",
"gateway_delegation_reward_rate",
"minimum_gateway_bond",
"minimum_mixnode_bond",
"mixnode_active_set_size",
"mixnode_bond_reward_rate",
"mixnode_delegation_reward_rate"
],
"properties": {
"epoch_length": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"gateway_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"gateway_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"minimum_gateway_bond": {
"$ref": "#/definitions/Uint128"
},
"minimum_mixnode_bond": {
"$ref": "#/definitions/Uint128"
},
"mixnode_active_set_size": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"mixnode_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"mixnode_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 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 `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
-134
View File
@@ -1,134 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "HandleMsg",
"anyOf": [
{
"type": "object",
"required": [
"register_mixnode"
],
"properties": {
"register_mixnode": {
"type": "object",
"required": [
"mix_node"
],
"properties": {
"mix_node": {
"$ref": "#/definitions/MixNode"
}
}
}
}
},
{
"type": "object",
"required": [
"un_register_mixnode"
],
"properties": {
"un_register_mixnode": {
"type": "object"
}
}
},
{
"type": "object",
"required": [
"bond_gateway"
],
"properties": {
"bond_gateway": {
"type": "object",
"required": [
"gateway"
],
"properties": {
"gateway": {
"$ref": "#/definitions/Gateway"
}
}
}
}
},
{
"type": "object",
"required": [
"unbond_gateway"
],
"properties": {
"unbond_gateway": {
"type": "object"
}
}
}
],
"definitions": {
"Gateway": {
"type": "object",
"required": [
"clients_host",
"identity_key",
"location",
"mix_host",
"sphinx_key",
"version"
],
"properties": {
"clients_host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients",
"type": "string"
},
"location": {
"type": "string"
},
"mix_host": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
},
"MixNode": {
"type": "object",
"required": [
"host",
"identity_key",
"layer",
"location",
"sphinx_key",
"version"
],
"properties": {
"host": {
"type": "string"
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"layer": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"location": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"version": {
"type": "string"
}
}
}
}
}
@@ -1,5 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InitMsg",
"title": "InstantiateMsg",
"type": "object"
}
+47 -17
View File
@@ -3,25 +3,40 @@
"title": "MixNodeBond",
"type": "object",
"required": [
"amount",
"bond_amount",
"layer",
"mix_node",
"owner"
"owner",
"total_delegation"
],
"properties": {
"amount": {
"type": "array",
"items": {
"$ref": "#/definitions/Coin"
}
"block_height": {
"default": 0,
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"bond_amount": {
"$ref": "#/definitions/Coin"
},
"layer": {
"$ref": "#/definitions/Layer"
},
"mix_node": {
"$ref": "#/definitions/MixNode"
},
"owner": {
"$ref": "#/definitions/HumanAddr"
"$ref": "#/definitions/Addr"
},
"total_delegation": {
"$ref": "#/definitions/Coin"
}
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
@@ -37,44 +52,59 @@
}
}
},
"HumanAddr": {
"type": "string"
"Layer": {
"type": "string",
"enum": [
"Gateway",
"One",
"Two",
"Three"
]
},
"MixNode": {
"type": "object",
"required": [
"host",
"http_api_port",
"identity_key",
"layer",
"location",
"mix_port",
"sphinx_key",
"verloc_port",
"version"
],
"properties": {
"host": {
"type": "string"
},
"http_api_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"identity_key": {
"description": "Base58 encoded ed25519 EdDSA public key.",
"type": "string"
},
"layer": {
"mix_port": {
"type": "integer",
"format": "uint64",
"format": "uint16",
"minimum": 0.0
},
"location": {
"type": "string"
},
"sphinx_key": {
"type": "string"
},
"verloc_port": {
"type": "integer",
"format": "uint16",
"minimum": 0.0
},
"version": {
"type": "string"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 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 `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
+262 -11
View File
@@ -20,18 +20,15 @@
"minimum": 0.0
},
"start_after": {
"anyOf": [
{
"$ref": "#/definitions/HumanAddr"
},
{
"type": "null"
}
"type": [
"string",
"null"
]
}
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
@@ -50,10 +47,96 @@
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"owns_mixnode"
],
"properties": {
"owns_mixnode": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"owns_gateway"
],
"properties": {
"owns_gateway": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"state_params"
],
"properties": {
"state_params": {
"type": "object"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_mix_delegations"
],
"properties": {
"get_mix_delegations": {
"type": "object",
"required": [
"mix_identity"
],
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"mix_identity": {
"type": "string"
},
"start_after": {
"anyOf": [
{
"$ref": "#/definitions/HumanAddr"
"$ref": "#/definitions/Addr"
},
{
"type": "null"
@@ -62,11 +145,179 @@
}
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_reverse_mix_delegations"
],
"properties": {
"get_reverse_mix_delegations": {
"type": "object",
"required": [
"delegation_owner"
],
"properties": {
"delegation_owner": {
"$ref": "#/definitions/Addr"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_mix_delegation"
],
"properties": {
"get_mix_delegation": {
"type": "object",
"required": [
"address",
"mix_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"mix_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_gateway_delegations"
],
"properties": {
"get_gateway_delegations": {
"type": "object",
"required": [
"gateway_identity"
],
"properties": {
"gateway_identity": {
"type": "string"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"anyOf": [
{
"$ref": "#/definitions/Addr"
},
{
"type": "null"
}
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_reverse_gateway_delegations"
],
"properties": {
"get_reverse_gateway_delegations": {
"type": "object",
"required": [
"delegation_owner"
],
"properties": {
"delegation_owner": {
"$ref": "#/definitions/Addr"
},
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_gateway_delegation"
],
"properties": {
"get_gateway_delegation": {
"type": "object",
"required": [
"address",
"gateway_identity"
],
"properties": {
"address": {
"$ref": "#/definitions/Addr"
},
"gateway_identity": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"layer_distribution"
],
"properties": {
"layer_distribution": {
"type": "object"
}
},
"additionalProperties": false
}
],
"definitions": {
"HumanAddr": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
}
}
+79 -3
View File
@@ -3,15 +3,91 @@
"title": "State",
"type": "object",
"required": [
"owner"
"gateway_epoch_bond_reward",
"gateway_epoch_delegation_reward",
"mixnode_epoch_bond_reward",
"mixnode_epoch_delegation_reward",
"network_monitor_address",
"owner",
"params"
],
"properties": {
"gateway_epoch_bond_reward": {
"$ref": "#/definitions/Decimal"
},
"gateway_epoch_delegation_reward": {
"$ref": "#/definitions/Decimal"
},
"mixnode_epoch_bond_reward": {
"$ref": "#/definitions/Decimal"
},
"mixnode_epoch_delegation_reward": {
"$ref": "#/definitions/Decimal"
},
"network_monitor_address": {
"$ref": "#/definitions/Addr"
},
"owner": {
"$ref": "#/definitions/HumanAddr"
"$ref": "#/definitions/Addr"
},
"params": {
"$ref": "#/definitions/StateParams"
}
},
"definitions": {
"HumanAddr": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"StateParams": {
"type": "object",
"required": [
"epoch_length",
"gateway_bond_reward_rate",
"gateway_delegation_reward_rate",
"minimum_gateway_bond",
"minimum_mixnode_bond",
"mixnode_active_set_size",
"mixnode_bond_reward_rate",
"mixnode_delegation_reward_rate"
],
"properties": {
"epoch_length": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"gateway_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"gateway_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"minimum_gateway_bond": {
"$ref": "#/definitions/Uint128"
},
"minimum_mixnode_bond": {
"$ref": "#/definitions/Uint128"
},
"mixnode_active_set_size": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"mixnode_bond_reward_rate": {
"$ref": "#/definitions/Decimal"
},
"mixnode_delegation_reward_rate": {
"$ref": "#/definitions/Decimal"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 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 `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
+51 -12
View File
@@ -10,7 +10,9 @@ use cosmwasm_std::{
entry_point, to_binary, Addr, Decimal, Deps, DepsMut, Env, MessageInfo, QueryResponse,
Response, Uint128,
};
use mixnet_contract::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, StateParams};
use mixnet_contract::{
ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, RawDelegationData, StateParams,
};
pub const INITIAL_DEFAULT_EPOCH_LENGTH: u32 = 2;
@@ -205,31 +207,68 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
}
#[entry_point]
pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use crate::storage::{gateways, gateways_read, mixnodes, mixnodes_read};
pub fn migrate(mut deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
use crate::storage::{
gateway_delegations, gateway_delegations_read, gateway_delegations_read_old, gateways_read,
mix_delegations, mix_delegations_read, mix_delegations_read_old, mixnodes_read,
};
use crate::transactions::delegations;
use cosmwasm_std::{Order, StdResult};
use mixnet_contract::CURRENT_BLOCK_HEIGHT;
use mixnet_contract::{GatewayBond, MixNodeBond};
use std::sync::atomic::Ordering;
CURRENT_BLOCK_HEIGHT.store(env.block.height, Ordering::Relaxed);
// Read existing delegations data, drop invalid values, and rewrite delegations data with valid data only
fn overwrite_mixnode_delegations_data(
identity: &str,
deps: &mut DepsMut,
) -> Result<(), ContractError> {
let delegations_bucket = mix_delegations_read(deps.storage, identity);
let old_delegations_bucket = mix_delegations_read_old(deps.storage, identity);
let mut delegations_vec = delegations(delegations_bucket)?;
let old_delegations = delegations::<Uint128>(old_delegations_bucket)?;
for delegation in old_delegations {
delegations_vec.push((delegation.0, RawDelegationData::new(delegation.1, 1)))
}
let bonds = mixnodes_read(deps.storage)
for (key, delegation) in delegations_vec {
mix_delegations(deps.storage, identity).save(&key, &delegation)?;
}
Ok(())
}
fn overwrite_gateway_delegations_data(
identity: &str,
deps: &mut DepsMut,
) -> Result<(), ContractError> {
let delegations_bucket = gateway_delegations_read(deps.storage, identity);
let old_delegations_bucket = gateway_delegations_read_old(deps.storage, identity);
let mut delegations_vec = delegations(delegations_bucket)?;
let old_delegations = delegations::<Uint128>(old_delegations_bucket)?;
for delegation in old_delegations {
delegations_vec.push((delegation.0, RawDelegationData::new(delegation.1, 1)))
}
for (key, delegation) in delegations_vec {
gateway_delegations(deps.storage, identity).save(&key, &delegation)?;
}
Ok(())
}
let mixnet_bonds = mixnodes_read(deps.storage)
.range(None, None, Order::Ascending)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<MixNodeBond>>>()?;
for bond in bonds {
mixnodes(deps.storage).save(bond.identity().as_bytes(), &bond)?;
for bond in mixnet_bonds {
overwrite_mixnode_delegations_data(bond.identity(), &mut deps)?;
}
let bonds = gateways_read(deps.storage)
let gateway_bonds = gateways_read(deps.storage)
.range(None, None, Order::Ascending)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<GatewayBond>>>()?;
for bond in bonds {
gateways(deps.storage).save(bond.identity().as_bytes(), &bond)?;
for bond in gateway_bonds {
overwrite_gateway_delegations_data(bond.identity(), &mut deps)?;
}
Ok(Default::default())
+20
View File
@@ -36,6 +36,8 @@ const PREFIX_REVERSE_GATEWAY_DELEGATION: &[u8] = b"dg";
// Contract-level stuff
// TODO Unify bucket and mixnode storage functions
pub fn config(storage: &mut dyn Storage) -> Singleton<State> {
singleton(storage, CONFIG_KEY)
}
@@ -316,6 +318,14 @@ pub fn mix_delegations_read<'a>(
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
// https://github.com/nymtech/nym/blob/122f5d9f2e5c1ced96e3b9ba0c74ef8b7dbde2c7/contracts/mixnet/src/storage.rs
pub fn mix_delegations_read_old<'a>(
storage: &'a dyn Storage,
mix_identity: IdentityKeyRef,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(storage, &[PREFIX_MIX_DELEGATION, mix_identity.as_bytes()])
}
pub fn reverse_mix_delegations<'a>(storage: &'a mut dyn Storage, owner: &Addr) -> Bucket<'a, ()> {
Bucket::multilevel(storage, &[PREFIX_REVERSE_MIX_DELEGATION, owner.as_bytes()])
}
@@ -347,6 +357,16 @@ pub fn gateway_delegations_read<'a>(
)
}
pub fn gateway_delegations_read_old<'a>(
storage: &'a dyn Storage,
gateway_identity: IdentityKeyRef,
) -> ReadonlyBucket<'a, Uint128> {
ReadonlyBucket::multilevel(
storage,
&[PREFIX_GATEWAY_DELEGATION, gateway_identity.as_bytes()],
)
}
pub fn reverse_gateway_delegations<'a>(
storage: &'a mut dyn Storage,
owner: &Addr,
+40 -18
View File
@@ -14,6 +14,8 @@ use cosmwasm_storage::ReadonlyBucket;
use mixnet_contract::{
Gateway, GatewayBond, IdentityKey, Layer, MixNode, MixNodeBond, RawDelegationData, StateParams,
};
use serde::de::DeserializeOwned;
use serde::Serialize;
const OLD_DELEGATIONS_CHUNK_SIZE: usize = 500;
pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
@@ -25,12 +27,15 @@ pub(crate) const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 17280;
// 3. The node unbonds
// 4. Some of the addresses that delegated in the past have not removed the delegation yet
// 5. The node rebonds with the same identity
fn find_old_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -> StdResult<Coin> {
pub fn delegations<T: DeserializeOwned + Serialize>(
delegations_bucket: ReadonlyBucket<T>,
) -> StdResult<Vec<(Vec<u8>, T)>> {
// I think it's incredibly unlikely to ever read more than that
// but in case we do, we should guard ourselves against possible
// out of memory errors (wasm contracts can only allocate at most 2MB
// of RAM, so we don't want to box the entire iterator)
let mut total_delegation = Coin::new(0, DENOM);
let mut delegations = Vec::new();
// let mut total_delegation = Coin::new(0, DENOM);
let mut start = None;
loop {
let iterator = delegations_bucket
@@ -39,16 +44,23 @@ fn find_old_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -
let mut iterated = 0;
for delegation in iterator {
for result_tuple in iterator {
iterated += 1;
if iterated == OLD_DELEGATIONS_CHUNK_SIZE + 1 {
// we reached start of next chunk, don't process it, mark it for the next iteration of the loop
start = Some(delegation?.0);
continue;
match result_tuple {
Ok((position, delegation)) => {
// We might skip some values due to deserializatio errors
if iterated > OLD_DELEGATIONS_CHUNK_SIZE {
// we reached start of next chunk, don't process it, mark it for the next iteration of the loop
start = Some(position);
continue;
}
delegations.push((position, delegation))
}
Err(_e) => {
// Skip errors
continue;
}
}
let value = delegation?.1.amount;
total_delegation.amount += value;
}
if iterated <= OLD_DELEGATIONS_CHUNK_SIZE {
@@ -57,7 +69,17 @@ fn find_old_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -
}
}
Ok(total_delegation)
Ok(delegations)
}
fn total_delegations(delegations_bucket: ReadonlyBucket<RawDelegationData>) -> StdResult<Coin> {
match delegations(delegations_bucket) {
Ok(delegations) => Ok(Coin::new(
delegations.iter().fold(0, |acc, x| acc + x.1.amount.u128()),
"upunk",
)),
Err(e) => Err(e),
}
}
fn validate_mixnode_bond(bond: &[Coin], minimum_bond: Uint128) -> Result<(), ContractError> {
@@ -138,7 +160,7 @@ pub(crate) fn try_add_mixnode(
// this might potentially require more gas if a significant number of delegations was there
let delegations_bucket = mix_delegations_read(deps.storage, &bond.mix_node.identity_key);
let existing_delegation = find_old_delegations(delegations_bucket)?;
let existing_delegation = total_delegations(delegations_bucket)?;
bond.total_delegation = existing_delegation;
let identity = bond.identity();
@@ -270,7 +292,7 @@ pub(crate) fn try_add_gateway(
// this might potentially require more gas if a significant number of delegations was there
let delegations_bucket = gateway_delegations_read(deps.storage, &bond.gateway.identity_key);
let existing_delegation = find_old_delegations(delegations_bucket)?;
let existing_delegation = total_delegations(delegations_bucket)?;
bond.total_delegation = existing_delegation;
let identity = bond.identity();
@@ -3928,7 +3950,7 @@ pub mod tests {
let node_identity: IdentityKey = "nodeidentity".into();
let read_bucket = mix_delegations_read(&deps.storage, &node_identity);
let old_delegations = find_old_delegations(read_bucket).unwrap();
let old_delegations = total_delegations(read_bucket).unwrap();
assert_eq!(Coin::new(0, DENOM), old_delegations);
}
@@ -3945,14 +3967,14 @@ pub mod tests {
OLD_DELEGATIONS_CHUNK_SIZE * 3 + 1,
];
for total_delegations in num_delegations {
for delegations in num_delegations {
let mut deps = helpers::init_contract();
let node_identity: IdentityKey = "nodeidentity".into();
// delegate some stake
let mut write_bucket = mix_delegations(&mut deps.storage, &node_identity);
for i in 1..=total_delegations {
for i in 1..=delegations {
let delegator = Addr::unchecked(format!("delegator{}", i));
let delegation = raw_delegation_fixture(i as u128);
write_bucket
@@ -3961,9 +3983,9 @@ pub mod tests {
}
let read_bucket = mix_delegations_read(&deps.storage, &node_identity);
let old_delegations = find_old_delegations(read_bucket).unwrap();
let old_delegations = total_delegations(read_bucket).unwrap();
let total_delegation = (1..=total_delegations as u128).into_iter().sum();
let total_delegation = (1..=delegations as u128).into_iter().sum();
assert_eq!(Coin::new(total_delegation, DENOM), old_delegations);
}
}