diff --git a/Cargo.lock b/Cargo.lock index e420451..bc11c30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,15 +16,15 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "ark-bls12-381" @@ -194,9 +194,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cfg-if" @@ -212,21 +212,22 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cosmwasm-core" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6ceb8624260d0d3a67c4e1a1d43fc7e9406720afbcb124521501dd138f90aa" +checksum = "c34c440d4d8e3ecec783d0f9c89d25565168b0f4cdb80a1f6a387cf2168c0740" [[package]] name = "cosmwasm-crypto" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4125381e5fd7fefe9f614640049648088015eca2b60d861465329a5d87dfa538" +checksum = "134e765161d60228cc27635032d2a466542ca83fd6c87f3c87f4963c0bd51008" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-serialize", "cosmwasm-core", + "curve25519-dalek", "digest", "ecdsa", "ed25519-zebra", @@ -241,20 +242,20 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5658b1dc64e10b56ae7a449f678f96932a96f6cfad1769d608d1d1d656480a" +checksum = "3c94a4b93e722c91d2e58471cfe69480f4a656cfccacd8bfda5638f2a5d4512b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "cosmwasm-schema" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b4d949b6041519c58993a73f4bbfba8083ba14f7001eae704865a09065845" +checksum = "3e9a7b56d154870ec4b57b224509854f706c9744449548d8a3bf91ac75c59192" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -265,20 +266,20 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ef1b5835a65fcca3ab8b9a02b4f4dacc78e233a5c2f20b270efb9db0666d12" +checksum = "edd3d80310cd7b86b09dbe886f4f2ca235a5ddb8d478493c6e50e720a3b38a42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "cosmwasm-std" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70eb7ab0c1e99dd6207496963ba2a457c4128ac9ad9c72a83f8d9808542b849b" +checksum = "4434e556b0aebff34bf082e75d175b5d7edbcf1d90d4cedb59623a1249fff567" dependencies = [ "base64", "bech32", @@ -289,6 +290,7 @@ dependencies = [ "derive_more", "hex", "rand_core", + "rmp-serde", "schemars", "serde", "serde-json-wasm", @@ -308,9 +310,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -327,9 +329,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-bigint" @@ -377,7 +379,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -397,7 +399,7 @@ dependencies = [ "schemars", "serde", "sha2", - "thiserror 2.0.3", + "thiserror 2.0.8", ] [[package]] @@ -477,7 +479,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "unicode-xid", ] @@ -696,9 +698,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.166" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "memchr" @@ -787,9 +789,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" dependencies = [ "bytes", "prost-derive", @@ -797,15 +799,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -877,6 +879,28 @@ dependencies = [ "subtle", ] +[[package]] +name = "rmp" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -914,7 +938,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -932,8 +956,8 @@ dependencies = [ [[package]] name = "seda-common" -version = "0.2.0" -source = "git+https://github.com/sedaprotocol/seda-common-rs.git?tag=v0.3.0#d1ba3e4868e36be460c29a3bcd3258ff97716f90" +version = "0.4.0" +source = "git+https://github.com/sedaprotocol/seda-common-rs.git?branch=main#ad0a6d6c78265bff9e01d71ca540f76088399ac4" dependencies = [ "base64", "cosmwasm-schema", @@ -945,7 +969,7 @@ dependencies = [ "semver", "serde", "sha3", - "thiserror 2.0.3", + "thiserror 2.0.8", "vrf-rs", ] @@ -969,24 +993,24 @@ dependencies = [ "serde-big-array", "serde_json", "sha3", - "thiserror 2.0.3", + "thiserror 2.0.8", "vrf-rs", ] [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -1011,13 +1035,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1028,7 +1052,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1099,9 +1123,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -1119,11 +1143,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.8", ] [[package]] @@ -1134,18 +1158,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1239,7 +1263,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1259,5 +1283,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] diff --git a/Cargo.toml b/Cargo.toml index eaff778..1a69a5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ lazy_static = "1.4" libfuzzer-sys = "0.4" rand = "0.8" schemars = { version = "0.8", features = ["semver"] } -seda-common = { git = "https://github.com/sedaprotocol/seda-common-rs.git", tag = "v0.3.0" } +seda-common = { git = "https://github.com/sedaprotocol/seda-common-rs.git", branch = "main" } # leaving this in to make local development easier # seda-common = { path = "../seda-common-rs/crates/common" } semver = { version = "1.0", features = ["serde"] } diff --git a/contract/Cargo.toml b/contract/Cargo.toml index 8b3cd47..fdefe41 100644 --- a/contract/Cargo.toml +++ b/contract/Cargo.toml @@ -31,6 +31,7 @@ seda-common = { workspace = true, features = ["cosmwasm"] } semver.workspace = true serde.workspace = true serde-big-array.workspace = true +serde_json.workspace = true sha3.workspace = true thiserror.workspace = true vrf-rs.workspace = true @@ -39,5 +40,4 @@ vrf-rs.workspace = true cw-multi-test.workspace = true k256.workspace = true seda-common = { workspace = true, features = ["cosmwasm", "test-utils"] } -serde_json.workspace = true lazy_static.workspace = true diff --git a/contract/src/error.rs b/contract/src/error.rs index d8d0141..62c9de9 100644 --- a/contract/src/error.rs +++ b/contract/src/error.rs @@ -65,6 +65,9 @@ pub enum ContractError { #[error(transparent)] Common(#[from] seda_common::error::Error), + #[error(transparent)] + Overflow(#[from] cosmwasm_std::OverflowError), + #[error("Invalid hash length `{0}` expected 32 bytes")] InvalidHashLength(usize), #[error("Invalid public key length `{0}` expected 33 bytes")] diff --git a/contract/src/msgs/data_requests/execute/post_request.rs b/contract/src/msgs/data_requests/execute/post_request.rs index 2f21993..6c33c3f 100644 --- a/contract/src/msgs/data_requests/execute/post_request.rs +++ b/contract/src/msgs/data_requests/execute/post_request.rs @@ -51,7 +51,7 @@ impl ExecuteHandler for execute::post_request::Execute { &dr_id, &Escrow { amount: funds, - staker: info.sender, + poster: info.sender, }, )?; diff --git a/contract/src/msgs/data_requests/state/mod.rs b/contract/src/msgs/data_requests/state/mod.rs index 777fd67..aac9232 100644 --- a/contract/src/msgs/data_requests/state/mod.rs +++ b/contract/src/msgs/data_requests/state/mod.rs @@ -11,12 +11,12 @@ use timeouts::Timeouts; /// Governance-controlled timeout configuration parameters. pub const TIMEOUT_CONFIG: Item = Item::new("timeout_config"); -/// Stores the amount, and the staker address. +/// Stores the amount, and the poster address. #[cw_serde] pub struct Escrow { pub amount: Uint128, // Safe to use Addr here as we aren't taking the type from a user input. - pub staker: Addr, + pub poster: Addr, } /// Maps a data request ID to the staked funds. diff --git a/contract/src/msgs/data_requests/sudo/expire_data_requests.rs b/contract/src/msgs/data_requests/sudo/expire_data_requests.rs index 8105f5d..39ce1ed 100644 --- a/contract/src/msgs/data_requests/sudo/expire_data_requests.rs +++ b/contract/src/msgs/data_requests/sudo/expire_data_requests.rs @@ -1,7 +1,8 @@ use cosmwasm_std::{to_json_string, DepsMut, Env, Response}; use seda_common::msgs::data_requests::sudo::expire_data_requests; -use super::{state, ContractError, SudoHandler}; +use super::{ContractError, SudoHandler}; +use crate::msgs::data_requests::state; impl SudoHandler for expire_data_requests::Sudo { /// Expires all data requests that have timed out diff --git a/contract/src/msgs/data_requests/sudo/mod.rs b/contract/src/msgs/data_requests/sudo/mod.rs index 8787af4..343f1cf 100644 --- a/contract/src/msgs/data_requests/sudo/mod.rs +++ b/contract/src/msgs/data_requests/sudo/mod.rs @@ -1,30 +1,14 @@ -use super::{ - msgs::data_requests::sudo::{self, SudoMsg}, - *, -}; -pub(in crate::msgs::data_requests) mod expire_data_requests; -pub(in crate::msgs::data_requests) mod remove_request; -pub(in crate::msgs::data_requests) mod remove_requests; - -fn remove_request(request: sudo::RemoveDataRequest, deps: &mut DepsMut, env: &Env) -> Result { - // find the data request from the committed pool (if it exists, otherwise error) - let dr_id = Hash::from_hex_str(&request.dr_id)?; - state::load_request(deps.storage, &dr_id)?; - - let block_height: u64 = env.block.height; +use cosmwasm_std::{DepsMut, Env, Response}; +use seda_common::msgs::data_requests::sudo::SudoMsg; - let event = - Event::new("remove-dr").add_attributes([("dr_id", request.dr_id), ("block_height", block_height.to_string())]); +use super::{ContractError, SudoHandler}; - state::remove_request(deps.storage, dr_id)?; - - Ok(event) -} +pub(in crate::msgs::data_requests) mod expire_data_requests; +pub(in crate::msgs::data_requests) mod remove_requests; impl SudoHandler for SudoMsg { fn sudo(self, deps: DepsMut, env: Env) -> Result { match self { - SudoMsg::RemoveDataRequest(sudo) => sudo.sudo(deps, env), SudoMsg::RemoveDataRequests(sudo) => sudo.sudo(deps, env), SudoMsg::ExpireDataRequests(sudo) => sudo.sudo(deps, env), } diff --git a/contract/src/msgs/data_requests/sudo/remove_request.rs b/contract/src/msgs/data_requests/sudo/remove_request.rs deleted file mode 100644 index 65d32b3..0000000 --- a/contract/src/msgs/data_requests/sudo/remove_request.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::*; - -impl SudoHandler for sudo::RemoveDataRequest { - /// Removes a data request from the contract - fn sudo(self, mut deps: DepsMut, env: Env) -> Result { - let event = remove_request(self, &mut deps, &env)?; - - Ok(Response::new().add_event(event)) - } -} diff --git a/contract/src/msgs/data_requests/sudo/remove_requests.rs b/contract/src/msgs/data_requests/sudo/remove_requests.rs index b769739..b611f8c 100644 --- a/contract/src/msgs/data_requests/sudo/remove_requests.rs +++ b/contract/src/msgs/data_requests/sudo/remove_requests.rs @@ -1,14 +1,147 @@ -use super::*; +use cosmwasm_std::{to_json_string, Addr, BankMsg, Coin, DepsMut, Env, Event, Response, Uint128}; +use cw_storage_plus::KeyDeserialize; +use seda_common::{ + msgs::data_requests::sudo::{remove_requests, DistributionKind, DistributionMessages}, + types::Hash, +}; +use serde_json::json; -impl SudoHandler for sudo::remove_requests::Sudo { +use super::{ContractError, SudoHandler}; +use crate::{ + msgs::{ + data_requests::state::{self, DR_ESCROW}, + staking::state::{STAKERS, STAKING_CONFIG}, + PublicKey, + }, + state::TOKEN, + types::FromHexStr, +}; + +fn amount_to_tokens(amount: Uint128, token: &str) -> Coin { + Coin { + denom: token.to_string(), + amount, + } +} + +fn remove_request( + dr_id_str: String, + messages: DistributionMessages, + deps: &mut DepsMut, + env: &Env, + token: &str, +) -> Result<(Event, Vec), ContractError> { + // find the data request from the committed pool (if it exists, otherwise error) + let dr_id = Hash::from_hex_str(&dr_id_str)?; + state::load_request(deps.storage, &dr_id)?; + + let block_height: u64 = env.block.height; + + let mut event = + Event::new("seda-remove-dr").add_attributes([("dr_id", dr_id_str), ("block_height", block_height.to_string())]); + + let mut dr_escrow = DR_ESCROW.load(deps.storage, &dr_id)?; + + // add 1 so we can account for the refund message that may be sent + let mut bank_messages = Vec::with_capacity(messages.messages.len() + 1); + for message in messages.messages { + match &message.kind { + DistributionKind::Burn(distribution_burn) => { + dr_escrow.amount = dr_escrow.amount.checked_sub(distribution_burn.amount)?; + bank_messages.push(BankMsg::Burn { + amount: vec![amount_to_tokens(distribution_burn.amount, token)], + }); + event = event.add_attribute( + "burn", + to_json_string(&json!({ + "amount": distribution_burn.amount, + "type": to_json_string(&message.type_)?, + "kind": to_json_string(&message.kind)?, + }))?, + ); + } + DistributionKind::ExecutorReward(distribution_executor_reward) => { + let public_key = PublicKey::from_hex_str(&distribution_executor_reward.identity)?; + let mut staker = STAKERS.get_staker(deps.storage, &public_key)?; + + let minimum_stake = STAKING_CONFIG.load(deps.storage)?.minimum_stake_to_register; + let remaining_reward = if staker.tokens_staked < minimum_stake { + // top the staker up to minimum stake from the amount in the reward & escrow + let top_up = minimum_stake.checked_sub(staker.tokens_staked)?; + let top_up = top_up.min(distribution_executor_reward.amount); + staker.tokens_staked += top_up; + dr_escrow.amount = dr_escrow.amount.checked_sub(top_up)?; + distribution_executor_reward.amount.checked_sub(top_up)? + } else { + distribution_executor_reward.amount + }; + + // send remaining reward to the staker pending withdrawal + staker.tokens_pending_withdrawal += remaining_reward; + dr_escrow.amount = dr_escrow.amount.checked_sub(remaining_reward)?; + STAKERS.update(deps.storage, public_key, &staker)?; + + event = event.add_attribute( + "executor_reward", + to_json_string(&json!({ + "amount": distribution_executor_reward.amount, + "identity": distribution_executor_reward.identity, + "type": to_json_string(&message.type_)?, + "kind": to_json_string(&message.kind)?, + }))?, + ); + } + DistributionKind::Send(distribution_send) => { + dr_escrow.amount = dr_escrow.amount.checked_sub(distribution_send.amount)?; + bank_messages.push(BankMsg::Send { + to_address: Addr::from_vec(distribution_send.to.to_vec())?.to_string(), + amount: vec![amount_to_tokens(distribution_send.amount, token)], + }); + event = event.add_attribute( + "send", + to_json_string(&json!({ + "amount": distribution_send.amount, + "to": distribution_send.to, + "type": to_json_string(&message.type_)?, + "kind": to_json_string(&message.kind)?, + }))?, + ); + } + } + } + + if !dr_escrow.amount.is_zero() { + bank_messages.push(BankMsg::Send { + to_address: dr_escrow.poster.to_string(), + amount: vec![amount_to_tokens(dr_escrow.amount, token)], + }); + event = event.add_attribute( + "refund", + to_json_string(&json!({ + "amount": dr_escrow.amount, + "type": to_json_string(&messages.refund_type)?, + }))?, + ); + event = event.add_attribute("refund", dr_escrow.amount.to_string()); + } + + state::remove_request(deps.storage, dr_id)?; + DR_ESCROW.remove(deps.storage, &dr_id); + + Ok((event, bank_messages)) +} + +impl SudoHandler for remove_requests::Sudo { fn sudo(self, mut deps: DepsMut, env: Env) -> Result { + let token = TOKEN.load(deps.storage)?; let mut response = Response::new(); - for event in self + for removal in self .requests .into_iter() - .map(|request| remove_request(request, &mut deps, &env)) + .map(|(dr_id, messages)| remove_request(dr_id, messages, &mut deps, &env, &token)) { - response = response.add_event(event?); + let (event, bank_messages) = removal?; + response = response.add_event(event).add_messages(bank_messages); } Ok(response) } diff --git a/contract/src/msgs/data_requests/test_helpers.rs b/contract/src/msgs/data_requests/test_helpers.rs index 7e7539b..9b253e2 100644 --- a/contract/src/msgs/data_requests/test_helpers.rs +++ b/contract/src/msgs/data_requests/test_helpers.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use msgs::data_requests::sudo::expire_data_requests; +use msgs::data_requests::sudo::{expire_data_requests, DistributionMessages}; use semver::{BuildMetadata, Prerelease, Version}; use sha3::{Digest, Keccak256}; @@ -190,20 +190,19 @@ impl TestInfo { } #[track_caller] - pub fn remove_data_request(&mut self, dr_id: String) -> Result<(), ContractError> { - let msg = sudo::RemoveDataRequest { dr_id }.into(); + pub fn remove_data_request(&mut self, dr_id: String, msgs: DistributionMessages) -> Result<(), ContractError> { + let mut requests = HashMap::new(); + requests.insert(dr_id, msgs); + let msg = sudo::remove_requests::Sudo { requests }.into(); self.sudo(&msg) } #[track_caller] - pub fn remove_data_requests(&mut self, results: Vec) -> Result<(), ContractError> { - let msg = sudo::remove_requests::Sudo { - requests: results - .into_iter() - .map(|dr_id| sudo::RemoveDataRequest { dr_id }) - .collect(), - } - .into(); + pub fn remove_data_requests( + &mut self, + requests: HashMap, + ) -> Result<(), ContractError> { + let msg = sudo::remove_requests::Sudo { requests }.into(); self.sudo(&msg) } diff --git a/contract/src/msgs/data_requests/tests.rs b/contract/src/msgs/data_requests/tests.rs index da5d1a9..a371496 100644 --- a/contract/src/msgs/data_requests/tests.rs +++ b/contract/src/msgs/data_requests/tests.rs @@ -1,3 +1,14 @@ +use std::collections::HashMap; + +use msgs::data_requests::sudo::{ + DistributionBurn, + DistributionExecutorReward, + DistributionKind, + DistributionMessage, + DistributionMessages, + DistributionSend, + DistributionType, +}; use state::DR_ESCROW; use super::*; @@ -106,7 +117,7 @@ fn post_data_request() { ) .unwrap(); assert_eq!(20, staked.amount.u128()); - assert_eq!(anyone.addr(), staked.staker); + assert_eq!(anyone.addr(), staked.poster); // expect an error when trying to post it again let res = test_info.post_data_request(&mut anyone, dr.clone(), vec![], vec![1, 2, 3], 1); @@ -721,6 +732,8 @@ fn remove_data_request() { alice.stake(&mut test_info, 1).unwrap(); let dr = test_helpers::calculate_dr_id_and_args(1, 1); let dr_id = test_info.post_data_request(&mut alice, dr, vec![], vec![], 1).unwrap(); + let mut executor = test_info.new_executor("exec", Some(51)); + executor.stake(&mut test_info, 1).unwrap(); // alice commits a data result let alice_reveal = RevealBody { @@ -737,7 +750,42 @@ fn remove_data_request() { test_info.reveal_result(&alice, &dr_id, alice_reveal.clone()).unwrap(); // owner removes a data result - test_info.remove_data_request(dr_id).unwrap(); + // reward goes to executor + // remainder refunds to alice + test_info + .remove_data_request( + dr_id, + DistributionMessages { + messages: vec![ + DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(executor.addr().to_string().as_bytes().to_vec()), + amount: 5u128.into(), + }), + type_: DistributionType::ExecutorReward, + }, + DistributionMessage { + kind: DistributionKind::ExecutorReward(DistributionExecutorReward { + identity: executor.pub_key_hex(), + amount: 5u128.into(), + }), + type_: DistributionType::ExecutorReward, + }, + DistributionMessage { + kind: DistributionKind::Burn(DistributionBurn { amount: 1u128.into() }), + type_: DistributionType::ExecutorReward, + }, + ], + refund_type: DistributionType::RemainderRefund, + }, + ) + .unwrap(); + assert_eq!(55, test_info.executor_balance("exec")); + assert_eq!(10, test_info.executor_balance("alice")); + + // get the staker info for the executor + let staker = test_info.get_staker(executor.pub_key()).unwrap(); + assert_eq!(5, staker.tokens_pending_withdrawal.u128()); } #[test] @@ -783,7 +831,34 @@ fn remove_data_requests() { test_info.reveal_result(&alice, &dr_id2, alice_reveal2.clone()).unwrap(); // owner posts data results - test_info.remove_data_requests(vec![dr_id1, dr_id2]).unwrap(); + let mut to_remove = HashMap::new(); + to_remove.insert( + dr_id1.clone(), + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(alice.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ); + to_remove.insert( + dr_id2.clone(), + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(alice.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ); + test_info.remove_data_requests(to_remove).unwrap(); } #[test] @@ -875,7 +950,21 @@ fn remove_data_request_with_more_drs_in_the_pool() { // Remove only first dr ready to be tallied (while there is another one in the pool and not ready) // This checks part of the swap_remove logic let dr = dr_to_be_tallied[0].clone(); - test_info.remove_data_request(dr.id).unwrap(); + test_info + .remove_data_request( + dr.id, + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(alice.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ) + .unwrap(); assert_eq!( 0, test_info @@ -890,7 +979,21 @@ fn remove_data_request_with_more_drs_in_the_pool() { // Remove last dr let dr = dr_to_be_tallied[0].clone(); - test_info.remove_data_request(dr.id).unwrap(); + test_info + .remove_data_request( + dr.id, + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(alice.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ) + .unwrap(); // Check dr to be tallied is empty assert_eq!( @@ -1052,7 +1155,21 @@ fn get_data_requests_by_status_with_many_more_drs_in_pool() { .enumerate() { if i % 8 == 0 { - test_info.remove_data_request(request.id.to_string()).unwrap(); + test_info + .remove_data_request( + request.id.to_string(), + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(alice.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ) + .unwrap(); } } assert_eq!( diff --git a/contract/src/msgs/staking/test_helpers.rs b/contract/src/msgs/staking/test_helpers.rs index 18cfe16..282e589 100644 --- a/contract/src/msgs/staking/test_helpers.rs +++ b/contract/src/msgs/staking/test_helpers.rs @@ -112,9 +112,7 @@ impl TestInfo { let proof = sender.prove(factory.get_hash()); let msg = factory.create_message(proof); - let res = self.execute(sender, &msg); - sender.add_seda(10); - res + self.execute(sender, &msg) } #[track_caller] diff --git a/contract/src/msgs/staking/tests.rs b/contract/src/msgs/staking/tests.rs index f3350a1..b2e4d99 100644 --- a/contract/src/msgs/staking/tests.rs +++ b/contract/src/msgs/staking/tests.rs @@ -1,4 +1,7 @@ -use msgs::data_requests::RevealBody; +use msgs::data_requests::{ + sudo::{DistributionKind, DistributionMessage, DistributionMessages, DistributionSend, DistributionType}, + RevealBody, +}; use seda_common::msgs::staking::{Staker, StakingConfig}; use super::*; @@ -351,7 +354,21 @@ fn executor_not_eligible_if_dr_resolved() { test_info.reveal_result(&anyone, &dr_id, reveal.clone()).unwrap(); // Owner removes the data request - test_info.remove_data_request(dr_id.clone()).unwrap(); + test_info + .remove_data_request( + dr_id.clone(), + DistributionMessages { + messages: vec![DistributionMessage { + kind: DistributionKind::Send(DistributionSend { + to: Binary::new(anyone.addr().as_bytes().to_vec()), + amount: 10u128.into(), + }), + type_: DistributionType::ExecutorReward, + }], + refund_type: DistributionType::RemainderRefund, + }, + ) + .unwrap(); // perform the check let is_executor_eligible = test_info.is_executor_eligible(&anyone, dr_id); diff --git a/contract/src/test_utils.rs b/contract/src/test_utils.rs index 392f5bb..2fa0f26 100644 --- a/contract/src/test_utils.rs +++ b/contract/src/test_utils.rs @@ -1,16 +1,7 @@ use std::collections::HashMap; -use cosmwasm_std::{ - coins, - from_json, - testing::{message_info, MockApi}, - to_json_binary, - Addr, - MessageInfo, - StdError, - Uint128, -}; -use cw_multi_test::{no_init, App, AppBuilder, BankSudo, ContractWrapper, Executor}; +use cosmwasm_std::{coins, from_json, testing::MockApi, to_json_binary, Addr, StdError}; +use cw_multi_test::{App, AppBuilder, ContractWrapper, Executor}; use k256::{ ecdsa::{SigningKey, VerifyingKey}, elliptic_curve::rand_core::OsRng, @@ -39,15 +30,21 @@ pub struct TestInfo { impl TestInfo { pub fn init() -> Self { + let mut executors = HashMap::new(); let mut app = AppBuilder::default() .with_api(MockApi::default().with_prefix("seda")) - .build(no_init); + .build(|router, api, storage| { + let creator_addr = api.addr_make("creator"); + let creator = TestExecutor::new("creator", creator_addr.clone()); + router + .bank + .init_balance(storage, &creator_addr, coins(1_000_000, "aseda")) + .unwrap(); + executors.insert("creator", creator); + }); let contract = Box::new(ContractWrapper::new(execute, instantiate, query).with_sudo(sudo)); - - let creator_addr = app.api().addr_make("creator"); - let creator = TestExecutor::new("creator", creator_addr.clone(), Some(1_000_000)); - let chain_id = "seda_test".to_string(); + let creator = executors.get("creator").unwrap(); let code_id = app.store_code_with_creator(creator.addr(), contract); let init_msg = &InstantiateMsg { @@ -58,11 +55,8 @@ impl TestInfo { timeout_config: None, }; - let mut executors = HashMap::new(); - executors.insert("creator", creator.clone()); - let contract_addr = app - .instantiate_contract(code_id, creator.addr, &init_msg, &[], "core", None) + .instantiate_contract(code_id, creator.addr(), &init_msg, &[], "core", None) .unwrap(); let mut info = Self { @@ -83,21 +77,19 @@ impl TestInfo { pub fn new_executor(&mut self, name: &'static str, amount: Option) -> TestExecutor { let addr = self.new_address(name); - let executor = TestExecutor::new(name, addr, amount); + let executor = TestExecutor::new(name, addr); + self.executors.insert(name, executor); + let executor = self.executor(name).clone(); + if let Some(amount) = amount { - self.app - .sudo( - BankSudo::Mint { - to_address: executor.addr().into_string(), - amount: coins(amount, "aseda"), - } - .into(), - ) - .unwrap(); + self.app.init_modules(|router, _api, storage| { + router + .bank + .init_balance(storage, &executor.addr, coins(amount, "aseda")) + .unwrap(); + }); } - - self.executors.insert(name, executor); - self.executor(name).clone() + executor } #[track_caller] @@ -105,6 +97,17 @@ impl TestInfo { self.executors.get(name).unwrap() } + #[track_caller] + pub fn executor_balance(&self, name: &'static str) -> u128 { + let executor = self.executors.get(name).unwrap(); + self.app() + .wrap() + .query_balance(executor.addr(), "aseda") + .unwrap() + .amount + .u128() + } + pub fn app(&self) -> &App { &self.app } @@ -211,10 +214,7 @@ impl TestInfo { }); Ok(match res?.data { - Some(data) => { - sender.sub_seda(amount); - from_json(data).unwrap() - } + Some(data) => from_json(data).unwrap(), None => from_json(to_json_binary(&serde_json::Value::Null).unwrap()).unwrap(), }) } @@ -226,20 +226,14 @@ pub struct TestExecutor { addr: Addr, signing_key: SigningKey, public_key: PublicKey, - info: MessageInfo, } impl TestExecutor { - fn new(name: &'static str, addr: Addr, amount: Option) -> Self { + fn new(name: &'static str, addr: Addr) -> Self { let (signing_key, public_key) = new_public_key(); - let coins = if let Some(amount) = amount { - coins(amount, "aseda") - } else { - vec![] - }; + TestExecutor { name, - info: message_info(&addr, &coins), addr, signing_key, public_key, @@ -258,22 +252,6 @@ impl TestExecutor { self.public_key.to_hex() } - pub fn info(&self) -> MessageInfo { - self.info.clone() - } - - fn funds(&self) -> Uint128 { - self.info.funds.iter().find(|c| c.denom == "aseda").unwrap().amount - } - - pub fn add_seda(&mut self, amount: u128) { - self.info = message_info(&self.addr(), &coins(self.funds().u128() + amount, "aseda")); - } - - pub fn sub_seda(&mut self, amount: u128) { - self.info = message_info(&self.addr(), &coins(self.funds().u128() - amount, "aseda")); - } - pub fn sign_key(&self) -> Vec { self.signing_key.to_bytes().to_vec() }