From 6b969be5c744a11c2438a77867e06cf5e2a7de2a Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 9 Sep 2022 14:18:42 +0200 Subject: [PATCH 01/34] add cap as storage service to eth_proxy --- eth_bridge/ic/Cargo.lock | 2 + eth_bridge/ic/src/eth_proxy/Cargo.toml | 4 +- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 3 +- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 12 +- eth_bridge/ic/src/eth_proxy/src/api/cap.rs | 17 ++ .../src/eth_proxy/src/api/claimable_assets.rs | 13 +- eth_bridge/ic/src/eth_proxy/src/api/mod.rs | 1 + eth_bridge/ic/src/eth_proxy/src/common/cap.rs | 35 ++++ eth_bridge/ic/src/eth_proxy/src/common/mod.rs | 1 + .../ic/src/eth_proxy/src/common/tera.rs | 2 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 151 +++++++++++------- 11 files changed, 170 insertions(+), 71 deletions(-) create mode 100644 eth_bridge/ic/src/eth_proxy/src/api/cap.rs create mode 100644 eth_bridge/ic/src/eth_proxy/src/common/cap.rs diff --git a/eth_bridge/ic/Cargo.lock b/eth_bridge/ic/Cargo.lock index 1667baf9..01de8693 100644 --- a/eth_bridge/ic/Cargo.lock +++ b/eth_bridge/ic/Cargo.lock @@ -555,6 +555,8 @@ version = "0.1.0" dependencies = [ "async-trait", "candid", + "cap-sdk", + "cap-standards", "hex", "ic-cdk 0.5.1", "ic-cdk-macros 0.4.0", diff --git a/eth_bridge/ic/src/eth_proxy/Cargo.toml b/eth_bridge/ic/src/eth_proxy/Cargo.toml index da09f3cc..c4abea4f 100644 --- a/eth_bridge/ic/src/eth_proxy/Cargo.toml +++ b/eth_bridge/ic/src/eth_proxy/Cargo.toml @@ -17,4 +17,6 @@ sha3 = "0.9.1" async-trait = "0.1.51" serde = "1.0.130" serde_bytes = "0.11.5" -num-bigint = "0.4.3" \ No newline at end of file +num-bigint = "0.4.3" +cap-std = { git = "https://github.com/Psychedelic/cap", branch = "main", package="cap-standards", features = ["alpha-dip20", "cap-sdk", "sdk-impls"] } +cap-sdk = { git = "https://github.com/Psychedelic/cap.git", branch = "main" } \ No newline at end of file diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index b5981b6f..16c748f8 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -26,8 +26,9 @@ service : { get_all : (principal) -> (vec ClaimableMessage) query; get_all_token_balance : () -> (Result_1); get_balance : (principal) -> (opt nat); + get_claimable_message_size : () -> (nat64) query; handle_message : (principal, nat, vec nat) -> (Result); mint : (nat, vec nat) -> (Result); - remove_claimable : (principal, nat) -> (Result_2); + perform_handshake : () -> (Result_2); withdraw : (principal, nat) -> (Result); } \ No newline at end of file diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index b59fcec6..6ae0c20a 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use ic_kit::candid::candid_method; use ic_kit::{ic, macros::update}; +use crate::common::cap::insert_claimable_asset; use crate::common::tera::Tera; use crate::common::weth::Weth; use crate::proxy::{ToNat, STATE, TERA_ADDRESS, WETH_ADDRESS_ETH, WETH_ADDRESS_IC}; @@ -67,17 +68,20 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { weth_ic_addr_pid, current_balance - amount.clone(), ); + s.remove_user_flag(caller); + }); - s.add_claimable_message(ClaimableMessage { + ic_kit::ic::spawn(async move { + insert_claimable_asset(ClaimableMessage { owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), msg_key: outgoing_message.msg_key.clone(), token: weth_ic_addr_pid.clone(), amount: amount.clone(), - }); - - s.remove_user_flag(caller) + }) + .await }); + // All correct return Ok(burn_txn_id); } diff --git a/eth_bridge/ic/src/eth_proxy/src/api/cap.rs b/eth_bridge/ic/src/eth_proxy/src/api/cap.rs new file mode 100644 index 00000000..b1722db2 --- /dev/null +++ b/eth_bridge/ic/src/eth_proxy/src/api/cap.rs @@ -0,0 +1,17 @@ +use candid::{candid_method, Principal}; +use ic_cdk_macros::update; + +use crate::proxy::{CAP_ADDRESS, STATE}; + +#[update(name = "perform_handshake")] +#[candid_method(update, rename = "perform_handshake")] +fn perform_handshake() -> Result<(), String> { + if STATE.with(|s| s.is_authorized().is_ok()) { + cap_sdk::handshake( + 2_000_000_000_000, + Some(Principal::from_text(CAP_ADDRESS).unwrap()), + ); + return Ok(()); + } + Err("Caller is not authorized".to_string()) +} diff --git a/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs b/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs index fa02546b..4deb0c00 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs @@ -1,7 +1,6 @@ -use candid::{candid_method, Nat}; -use ic_cdk_macros::{query, update}; +use candid::candid_method; +use ic_cdk_macros::query; -use crate::api::admin::is_authorized; use crate::{ common::types::{ClaimableMessage, EthereumAddr}, proxy::STATE, @@ -13,8 +12,8 @@ fn get_all(eth_address: EthereumAddr) -> Vec { STATE.with(|s| s.get_claimable_messages(eth_address)) } -#[update(name = "remove_claimable", guard = "is_authorized")] -#[candid_method(update, rename = "remove_claimable")] -fn remove_claimable(eth_address: EthereumAddr, amount: Nat) -> Result<(), String> { - STATE.with(|s| s.remove_claimable_message(eth_address, amount.clone())) +#[query(name = "get_claimable_message_size")] +#[candid_method(query, rename = "get_claimable_message_size")] +fn get_claimable_message_size() -> usize { + STATE.with(|s| s.get_claimable_messages_queue_size()) } diff --git a/eth_bridge/ic/src/eth_proxy/src/api/mod.rs b/eth_bridge/ic/src/eth_proxy/src/api/mod.rs index b7b1ee17..1949045a 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/mod.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/mod.rs @@ -1,5 +1,6 @@ mod admin; mod burn; +mod cap; mod claimable_assets; mod get_balance; mod handle_message; diff --git a/eth_bridge/ic/src/eth_proxy/src/common/cap.rs b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs new file mode 100644 index 00000000..901c7fab --- /dev/null +++ b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs @@ -0,0 +1,35 @@ +use cap_sdk::{insert, IndefiniteEvent}; + +use crate::proxy::{ToEvent, STATE}; + +use super::types::ClaimableMessage; + +pub async fn insert_claimable_asset(message: ClaimableMessage) { + // add message to STATE claimable messages queue. + STATE.with(|s| { + s.add_claimable_message(message.clone()); + }); + + // flush STATE claimable messages queue + register_messages().await +} + +pub async fn register_messages() { + let mut pending_registrations = STATE.with(|s| s.get_all_claimable_messages()); + let mut i = 0; + while i < pending_registrations.len() { + if insert_into_cap(pending_registrations[i].to_cap_event()).await { + STATE.with(|s| s.remove_claimable_message(pending_registrations[i].clone())); + pending_registrations.swap_remove(i); + } else { + i += 1; + } + } +} + +async fn insert_into_cap(event: IndefiniteEvent) -> bool { + match insert(event.clone()).await { + Ok(_nat) => true, + Err(_error) => false, + } +} diff --git a/eth_bridge/ic/src/eth_proxy/src/common/mod.rs b/eth_bridge/ic/src/eth_proxy/src/common/mod.rs index 4cebcf87..954aa8d5 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/mod.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/mod.rs @@ -1,3 +1,4 @@ +pub mod cap; pub mod tera; pub mod types; pub mod utils; diff --git a/eth_bridge/ic/src/eth_proxy/src/common/tera.rs b/eth_bridge/ic/src/eth_proxy/src/common/tera.rs index bde9ccb1..be226e04 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/tera.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/tera.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use ic_cdk::call; use ic_cdk::export::candid::{Nat, Principal}; -use crate::common::types::{Nonce, OutgoingMessage, TxError}; +use crate::common::types::{OutgoingMessage, TxError}; use super::types::NonceBytes; diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 29f2e185..3b5e6bf7 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -1,13 +1,15 @@ use std::{collections::HashMap, ops::AddAssign}; +use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; use crate::common::types::{ - ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, - StableProxyState, TokendId, TxFlag, + ClaimableMessage, MessageHash, MessageStatus, NonceBytes, ProxyState, StableProxyState, + TokendId, TxFlag, }; +pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; pub const TERA_ADDRESS: &str = "timop-6qaaa-aaaab-qaeea-cai"; pub const WETH_ADDRESS_IC: &str = "tgodh-faaaa-aaaab-qaefa-cai"; pub const WETH_ADDRESS_ETH: &str = "0x2e130e57021bb4dfb95eb4dd0dd8cfceb936148a"; @@ -77,44 +79,61 @@ impl ProxyState { } pub fn add_claimable_message(&self, message: ClaimableMessage) { - let mut map = self.messages_unclaimed.borrow_mut(); - let messages = map.entry(message.owner.clone()).or_insert_with(Vec::new); + let mut user_messages = self.messages_unclaimed.borrow_mut(); + match user_messages.get_mut(&message.owner) { + Some(messages) => messages.push(message), + None => { + let mut init_vector = Vec::::new(); + init_vector.push(message.clone()); + user_messages.insert(message.owner.clone(), init_vector); + } + } + } + + pub fn remove_claimable_message(&self, message: ClaimableMessage) { + let mut binding = self.messages_unclaimed.borrow_mut(); + let user_messages = binding.get_mut(&message.owner).unwrap(); + + let index = user_messages + .iter() + .position(|m| m.msg_key == message.msg_key); - messages.push(message.clone()); - return; + if index.is_some() { + user_messages.swap_remove(index.unwrap()); + } } - pub fn get_claimable_messages(&self, eth_address: EthereumAddr) -> Vec { - let unclaimed_messages = self - .messages_unclaimed - .borrow() - .get(ð_address) - .unwrap_or(&vec![]) - .clone(); - return unclaimed_messages; + pub fn get_all_claimable_messages(&self) -> Vec { + let mut messages: Vec = Vec::default(); + for address in self.messages_unclaimed.borrow().keys() { + let address_messages = self + .messages_unclaimed + .borrow() + .get(address) + .unwrap() + .to_owned(); + messages.extend(address_messages) + } + messages } - pub fn remove_claimable_message( + pub fn get_claimable_messages( &self, - eth_address: EthereumAddr, - amount: Nat, - ) -> Result<(), String> { - let eth_addr_pid = Principal::from_text(WETH_ADDRESS_IC).unwrap(); - - let mut map = self.messages_unclaimed.borrow_mut(); - let messages = map - .get_mut(ð_address) - .ok_or_else(|| "Eth address not found")?; - - // Eth address could have multiple messages with the same amount, so we only remove one - let item_index = messages - .iter() - .position(|m| m.amount == amount && m.token == eth_addr_pid) - .ok_or_else(|| "Message not found")?; - - messages.remove(item_index); + eth_address_as_principal: Principal, + ) -> Vec { + if let Some(messages) = self + .messages_unclaimed + .borrow() + .get(ð_address_as_principal) + { + messages.to_owned() + } else { + Vec::::default() + } + } - return Ok(()); + pub fn get_claimable_messages_queue_size(&self) -> usize { + STATE.with(|s| s.messages_unclaimed.borrow().len()) } pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { @@ -196,6 +215,12 @@ impl ToNat for Principal { } } +impl ToNat for [u8; 32] { + fn to_nat(&self) -> Nat { + Nat::from(num_bigint::BigUint::from_bytes_be(&self.as_slice()[..])) + } +} + pub trait FromNat { fn from_nat(input: Nat) -> Principal; } @@ -235,6 +260,29 @@ impl ToBytes for Nat { } } +pub trait ToEvent { + fn to_cap_event(&self) -> IndefiniteEvent; +} + +impl ToEvent for ClaimableMessage { + fn to_cap_event(&self) -> IndefiniteEvent { + let details = DetailsBuilder::default() + .insert("owner", self.owner) + .insert("ethContractAddress", self.token) + .insert("msgHash", self.msg_hash.clone()) + .insert("msgHashKey", self.msg_key.to_nat()) + .insert("amount", self.amount.clone()) + .build(); + + IndefiniteEventBuilder::new() + .caller(self.owner.clone()) + .operation("Bridge") + .details(details) + .build() + .unwrap() + } +} + #[cfg(test)] mod tests { use std::str::FromStr; @@ -490,32 +538,22 @@ mod tests { }; // add first msg - STATE.with(|s| s.add_claimable_message(message_1)); + STATE.with(|s| s.add_claimable_message(message_1.clone())); // add second msg - STATE.with(|s| s.add_claimable_message(message_2)); + STATE.with(|s| s.add_claimable_message(message_2.clone())); // check if both messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 2); // remove one msg (both have same amount and token) - let result_remove_1 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), amount_1.clone()); - }); - assert_eq!(result_remove_1, ()); - - // check if only one message is in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + STATE.with(|s| s.remove_claimable_message(message_1.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 1); // remove second msg (both have same amount and token) - let result_remove_2 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), amount_1.clone()); - }); - assert_eq!(result_remove_2, ()); - - // check if no messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + STATE.with(|s| s.remove_claimable_message(message_2.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 0); } @@ -553,20 +591,19 @@ mod tests { // add first msg STATE.with(|s| s.add_claimable_message(message_1)); // add second msg - STATE.with(|s| s.add_claimable_message(message_2)); + STATE.with(|s| s.add_claimable_message(message_2.clone())); // check if both messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 2); // remove one msg -> the one with amount_2 (both are the same token, but different amount) - let result_remove_1 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), amount_2.clone()); - }); - assert_eq!(result_remove_1, ()); + STATE.with(|s| s.remove_claimable_message(message_2)); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + assert_eq!(claimable_messages.len(), 1); // check if only one message is in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 1); // the message that is left is the one with amount_1 From b609899564c53651676aab86d7f2faec1451a5b1 Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 20 Sep 2022 17:06:16 +0200 Subject: [PATCH 02/34] add cap storage service for claimable message on dip20_proxy --- magic_bridge/ic/Cargo.lock | 185 +++++++++++++++++- magic_bridge/ic/src/dip20_proxy/Cargo.toml | 4 +- .../ic/src/dip20_proxy/dip20_proxy.did | 6 +- .../ic/src/dip20_proxy/src/api/burn.rs | 13 +- .../ic/src/dip20_proxy/src/api/cap.rs | 17 ++ .../dip20_proxy/src/api/claimable_assets.rs | 12 +- .../ic/src/dip20_proxy/src/api/mod.rs | 1 + .../ic/src/dip20_proxy/src/common/cap.rs | 35 ++++ .../ic/src/dip20_proxy/src/common/mod.rs | 1 + .../ic/src/dip20_proxy/src/common/types.rs | 1 + magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 146 +++++++++----- 11 files changed, 353 insertions(+), 68 deletions(-) create mode 100644 magic_bridge/ic/src/dip20_proxy/src/api/cap.rs create mode 100644 magic_bridge/ic/src/dip20_proxy/src/common/cap.rs diff --git a/magic_bridge/ic/Cargo.lock b/magic_bridge/ic/Cargo.lock index c961e3d3..07f7ae62 100644 --- a/magic_bridge/ic/Cargo.lock +++ b/magic_bridge/ic/Cargo.lock @@ -144,6 +144,27 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-task" version = "4.2.0" @@ -196,6 +217,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "binread" version = "2.2.0" @@ -250,6 +280,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -327,12 +366,89 @@ dependencies = [ "syn", ] +[[package]] +name = "cap-common" +version = "0.2.3" +source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +dependencies = [ + "async-std", + "candid", + "certified-vars", + "ic-cdk 0.5.2", + "ic-certified-map", + "ic-kit", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.10.6", +] + +[[package]] +name = "cap-sdk" +version = "0.2.4" +source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +dependencies = [ + "async-std", + "async-stream", + "candid", + "cap-sdk-core", + "futures", + "ic-cdk 0.5.2", + "ic-kit", + "lazy_static", + "serde", + "thiserror", +] + +[[package]] +name = "cap-sdk-core" +version = "0.2.3" +source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +dependencies = [ + "candid", + "cap-common", + "ic-cdk 0.5.2", + "ic-kit", + "serde", + "thiserror", +] + +[[package]] +name = "cap-standards" +version = "0.2.0" +source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +dependencies = [ + "bincode", + "candid", + "cap-sdk", + "ic-cdk 0.5.2", + "ic-cdk-macros 0.5.2", + "num-bigint", + "serde", + "thiserror", +] + [[package]] name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "certified-vars" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7983d9b0ad23199b6a22246ac2138f9a1713ed66d6aef67f0376044e3415ebc" +dependencies = [ + "candid", + "hex", + "ic-cdk 0.5.2", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.9.9", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -392,6 +508,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctor" version = "0.1.22" @@ -417,12 +543,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + [[package]] name = "dip20_proxy" version = "0.1.0" dependencies = [ "async-trait", "candid", + "cap-sdk", + "cap-standards", "hex", "ic-cdk 0.5.2", "ic-cdk-macros 0.4.0", @@ -734,6 +872,32 @@ dependencies = [ "syn", ] +[[package]] +name = "ic-cdk-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6d2c14db28f427154a52088077419edf1848e2bbd0f527dee3150e592a0863" +dependencies = [ + "candid", + "ic-cdk 0.5.2", + "proc-macro2", + "quote", + "serde", + "serde_tokenstream", + "syn", +] + +[[package]] +name = "ic-certified-map" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea6b4530bf4e304ba870da9f58f6e10a84c74fe2fdec574ec7a316f031c37fd" +dependencies = [ + "serde", + "serde_bytes", + "sha2 0.10.6", +] + [[package]] name = "ic-kit" version = "0.4.4" @@ -760,7 +924,7 @@ dependencies = [ "hex", "serde", "serde_bytes", - "sha2", + "sha2 0.9.9", "thiserror", ] @@ -1242,21 +1406,32 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "sha3" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] diff --git a/magic_bridge/ic/src/dip20_proxy/Cargo.toml b/magic_bridge/ic/src/dip20_proxy/Cargo.toml index a1eecca6..9667639d 100644 --- a/magic_bridge/ic/src/dip20_proxy/Cargo.toml +++ b/magic_bridge/ic/src/dip20_proxy/Cargo.toml @@ -17,4 +17,6 @@ sha3 = "0.9.1" async-trait = "0.1.51" serde = "1.0.130" serde_bytes = "0.11.5" -num-bigint = "0.4.3" \ No newline at end of file +num-bigint = "0.4.3" +cap-std = { git = "https://github.com/Psychedelic/cap", branch = "main", package="cap-standards", features = ["alpha-dip20", "cap-sdk", "sdk-impls"] } +cap-sdk = { git = "https://github.com/Psychedelic/cap.git", branch = "main" } \ No newline at end of file diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index 3381fd29..5b949aed 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -2,12 +2,13 @@ type ClaimableMessage = record { token : principal; msg_hash : text; owner : principal; + msg_key : vec nat8; amount : nat; token_name : text; }; type Result = variant { Ok : nat; Err : TxError }; type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; -type Result_2 = variant { Ok : bool; Err : text }; +type Result_2 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; InsufficientBalance; @@ -26,8 +27,9 @@ service : { claimable_get_all : (principal) -> (vec ClaimableMessage) query; get_all_token_balance : () -> (Result_1); get_balance : (principal) -> (opt nat); + get_claimable_message_size : () -> (nat64) query; handle_message : (principal, nat, vec nat) -> (Result); mint : (principal, nat, vec nat) -> (Result); - remove_claimable : (principal, principal, nat) -> (Result_2); + perform_handshake : () -> (Result_2); withdraw : (principal, principal, nat) -> (Result); } \ No newline at end of file diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 66edb85a..412aef55 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -1,6 +1,7 @@ use ic_kit::candid::candid_method; use ic_kit::{ic, macros::update}; +use crate::common::cap::insert_claimable_asset; use crate::common::dip20::Dip20; use crate::common::magic::Magic; use crate::common::tera::Tera; @@ -90,15 +91,19 @@ async fn burn( current_balance - amount.clone(), ); - s.add_claimable_message(ClaimableMessage { + s.remove_user_flag(caller, token_id) + }); + + ic_kit::ic::spawn(async move { + insert_claimable_asset(ClaimableMessage { owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), + msg_key: outgoing_message.msg_key.clone(), token_name: token_name_str, token: token_id.clone(), amount: amount.clone(), - }); - - s.remove_user_flag(caller, token_id) + }) + .await }); // All correct return Ok(burn_txn_id); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/cap.rs b/magic_bridge/ic/src/dip20_proxy/src/api/cap.rs new file mode 100644 index 00000000..b1722db2 --- /dev/null +++ b/magic_bridge/ic/src/dip20_proxy/src/api/cap.rs @@ -0,0 +1,17 @@ +use candid::{candid_method, Principal}; +use ic_cdk_macros::update; + +use crate::proxy::{CAP_ADDRESS, STATE}; + +#[update(name = "perform_handshake")] +#[candid_method(update, rename = "perform_handshake")] +fn perform_handshake() -> Result<(), String> { + if STATE.with(|s| s.is_authorized().is_ok()) { + cap_sdk::handshake( + 2_000_000_000_000, + Some(Principal::from_text(CAP_ADDRESS).unwrap()), + ); + return Ok(()); + } + Err("Caller is not authorized".to_string()) +} diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs b/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs index d76c75b1..3f6e0e8d 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs @@ -14,12 +14,8 @@ fn claimable_get_all(eth_address: EthereumAddr) -> Vec { STATE.with(|s| s.get_claimable_messages(eth_address)) } -#[update(name = "remove_claimable", guard = "is_authorized")] -#[candid_method(update, rename = "remove_claimable")] -fn remove_claimable( - eth_address: EthereumAddr, - token_id: TokendId, - amount: Nat, -) -> Result { - STATE.with(|s| s.remove_claimable_message(eth_address, token_id.clone(), amount.clone())) +#[query(name = "get_claimable_message_size")] +#[candid_method(query, rename = "get_claimable_message_size")] +fn get_claimable_message_size() -> usize { + STATE.with(|s| s.get_claimable_messages_queue_size()) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs index b7b1ee17..1949045a 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs @@ -1,5 +1,6 @@ mod admin; mod burn; +mod cap; mod claimable_assets; mod get_balance; mod handle_message; diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs new file mode 100644 index 00000000..901c7fab --- /dev/null +++ b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs @@ -0,0 +1,35 @@ +use cap_sdk::{insert, IndefiniteEvent}; + +use crate::proxy::{ToEvent, STATE}; + +use super::types::ClaimableMessage; + +pub async fn insert_claimable_asset(message: ClaimableMessage) { + // add message to STATE claimable messages queue. + STATE.with(|s| { + s.add_claimable_message(message.clone()); + }); + + // flush STATE claimable messages queue + register_messages().await +} + +pub async fn register_messages() { + let mut pending_registrations = STATE.with(|s| s.get_all_claimable_messages()); + let mut i = 0; + while i < pending_registrations.len() { + if insert_into_cap(pending_registrations[i].to_cap_event()).await { + STATE.with(|s| s.remove_claimable_message(pending_registrations[i].clone())); + pending_registrations.swap_remove(i); + } else { + i += 1; + } + } +} + +async fn insert_into_cap(event: IndefiniteEvent) -> bool { + match insert(event.clone()).await { + Ok(_nat) => true, + Err(_error) => false, + } +} diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/mod.rs b/magic_bridge/ic/src/dip20_proxy/src/common/mod.rs index 83e081f6..d28c5834 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/mod.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/mod.rs @@ -1,3 +1,4 @@ +pub mod cap; pub mod dip20; pub mod magic; pub mod tera; diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index dae3e248..4a327a5d 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -67,6 +67,7 @@ pub struct OutgoingMessage { pub struct ClaimableMessage { pub owner: EthereumAddr, pub msg_hash: String, + pub msg_key: [u8; 32], pub token_name: String, pub token: TokendId, pub amount: Nat, diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index 19ea9d7d..ada97a8f 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -1,3 +1,4 @@ +use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use std::{collections::HashMap, ops::AddAssign}; use ic_cdk::export::candid::{Nat, Principal}; @@ -8,6 +9,7 @@ use crate::common::types::{ StableProxyState, TokendId, TxFlag, }; +pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; pub const TERA_ADDRESS: &str = "timop-6qaaa-aaaab-qaeea-cai"; pub const MAGIC_ADDRESS_IC: &str = "7z6fu-giaaa-aaaab-qafkq-cai"; pub const ERC20_ADDRESS_ETH: &str = "0x8CA1651eadeF97D3aC36c25DAE4A552c1368F27d"; @@ -77,41 +79,61 @@ impl ProxyState { } pub fn add_claimable_message(&self, message: ClaimableMessage) { - let mut map = self.messages_unclaimed.borrow_mut(); - let messages = map.entry(message.owner.clone()).or_insert_with(Vec::new); - - messages.push(message.clone()); - return; + let mut user_messages = self.messages_unclaimed.borrow_mut(); + match user_messages.get_mut(&message.owner) { + Some(messages) => messages.push(message), + None => { + let mut init_vector = Vec::::new(); + init_vector.push(message.clone()); + user_messages.insert(message.owner.clone(), init_vector); + } + } } - pub fn remove_claimable_message( - &self, - eth_address: EthereumAddr, - token_id: TokendId, - amount: Nat, - ) -> Result { - let mut map = self.messages_unclaimed.borrow_mut(); - let messages = map - .get_mut(ð_address) - .ok_or_else(|| "Message not found")?; - - let item_index = messages + pub fn remove_claimable_message(&self, message: ClaimableMessage) { + let mut binding = self.messages_unclaimed.borrow_mut(); + let user_messages = binding.get_mut(&message.owner).unwrap(); + + let index = user_messages .iter() - .position(|m| m.amount == amount && m.token == token_id) - .ok_or_else(|| "Message not found")?; + .position(|m| m.msg_key == message.msg_key); - messages.remove(item_index); - return Ok(true); + if index.is_some() { + user_messages.swap_remove(index.unwrap()); + } + } + + pub fn get_all_claimable_messages(&self) -> Vec { + let mut messages: Vec = Vec::default(); + for address in self.messages_unclaimed.borrow().keys() { + let address_messages = self + .messages_unclaimed + .borrow() + .get(address) + .unwrap() + .to_owned(); + messages.extend(address_messages) + } + messages } - pub fn get_claimable_messages(&self, eth_address: EthereumAddr) -> Vec { - let unclaimed_messages = self + pub fn get_claimable_messages( + &self, + eth_address_as_principal: Principal, + ) -> Vec { + if let Some(messages) = self .messages_unclaimed .borrow() - .get(ð_address) - .unwrap_or(&vec![]) - .clone(); - return unclaimed_messages; + .get(ð_address_as_principal) + { + messages.to_owned() + } else { + Vec::::default() + } + } + + pub fn get_claimable_messages_queue_size(&self) -> usize { + STATE.with(|s| s.messages_unclaimed.borrow().len()) } pub fn set_user_flag( @@ -204,6 +226,12 @@ impl ToNat for Principal { } } +impl ToNat for [u8; 32] { + fn to_nat(&self) -> Nat { + Nat::from(num_bigint::BigUint::from_bytes_be(&self.as_slice()[..])) + } +} + pub trait FromNat { fn from_nat(input: Nat) -> Principal; } @@ -243,6 +271,29 @@ impl ToBytes for Nat { } } +pub trait ToEvent { + fn to_cap_event(&self) -> IndefiniteEvent; +} + +impl ToEvent for ClaimableMessage { + fn to_cap_event(&self) -> IndefiniteEvent { + let details = DetailsBuilder::default() + .insert("owner", self.owner) + .insert("ethContractAddress", self.token) + .insert("msgHash", self.msg_hash.clone()) + .insert("msgHashKey", self.msg_key.to_nat()) + .insert("amount", self.amount.clone()) + .build(); + + IndefiniteEventBuilder::new() + .caller(self.owner.clone()) + .operation("Bridge") + .details(details) + .build() + .unwrap() + } +} + #[cfg(test)] mod tests { use std::str::FromStr; @@ -475,6 +526,9 @@ mod tests { let token_name = String::from("USDC"); let msg_hash_1 = String::from("123123123"); + let msg_key_1: [u8; 32] = [0; 32]; + let msg_key_2: [u8; 32] = [1; 32]; + let token_id_1 = Principal::from_slice( &hex::decode("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(), ); @@ -484,6 +538,7 @@ mod tests { let message_1 = ClaimableMessage { owner: eth_addr_1.clone(), msg_hash: msg_hash_1.clone(), + msg_key: msg_key_1, token_name: token_name.clone(), token: token_id_1.clone(), amount: amount_1.clone(), @@ -493,38 +548,31 @@ mod tests { let message_2 = ClaimableMessage { owner: eth_addr_1.clone(), msg_hash: msg_hash_1.clone(), + msg_key: msg_key_2, token_name: token_name.clone(), token: token_id_1.clone(), amount: amount_1.clone(), }; // add first msg - STATE.with(|s| s.add_claimable_message(message_1)); + STATE.with(|s| s.add_claimable_message(message_1.clone())); // add second msg - STATE.with(|s| s.add_claimable_message(message_2)); + STATE.with(|s| s.add_claimable_message(message_2.clone())); // check if both messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 2); // remove one msg (both have same amount and token) - let result_remove_1 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), token_id_1.clone(), amount_1.clone()); - }); - assert_eq!(result_remove_1, ()); - - // check if only one message is in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + let result_remove_1 = STATE.with(|s| s.remove_claimable_message(message_1.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 1); // remove second msg (both have same amount and token) - let result_remove_2 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), token_id_1.clone(), amount_1.clone()); - }); - assert_eq!(result_remove_2, ()); + let result_remove_2 = STATE.with(|s| s.remove_claimable_message(message_2.clone())); // check if no messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 0); } @@ -541,10 +589,14 @@ mod tests { let amount_1 = Nat::from(1_u64); let amount_2 = Nat::from(2_u64); + let msg_key_1: [u8; 32] = [0; 32]; + let msg_key_2: [u8; 32] = [1; 32]; + // first msg let message_1 = ClaimableMessage { owner: eth_addr_1.clone(), msg_hash: msg_hash_1.clone(), + msg_key: msg_key_1, token_name: token_name.clone(), token: token_id_1.clone(), amount: amount_1.clone(), @@ -554,6 +606,7 @@ mod tests { let message_2 = ClaimableMessage { owner: eth_addr_1.clone(), msg_hash: msg_hash_1.clone(), + msg_key: msg_key_2, token_name: token_name.clone(), token: token_id_1.clone(), amount: amount_2.clone(), @@ -565,17 +618,14 @@ mod tests { STATE.with(|s| s.add_claimable_message(message_2)); // check if both messages are in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 2); // remove one msg -> the one with amount_2 (both are the same token, but different amount) - let result_remove_1 = STATE.with(|s| { - s.remove_claimable_message(eth_addr_1.clone(), token_id_1.clone(), amount_2.clone()); - }); - assert_eq!(result_remove_1, ()); + let result_remove_1 = STATE.with(|s| s.remove_claimable_message(message_1.clone())); // check if only one message is in the claimable messages list for eth_addr_1 - let claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); + claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 1); // the message that is left is the one with amount_1 From 05942ac8a476f767d9137639dce8e6d47001d2c2 Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 21 Sep 2022 14:35:51 +0200 Subject: [PATCH 03/34] upgrade cap_cdk and use insert_sync on eth_proxy --- eth_bridge/ic/.gitignore | 2 +- eth_bridge/ic/Cargo.lock | 150 ++++++++++++++++-- eth_bridge/ic/src/eth_proxy/Cargo.toml | 2 +- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 9 -- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 15 +- .../src/eth_proxy/src/api/claimable_assets.rs | 19 --- eth_bridge/ic/src/eth_proxy/src/api/mod.rs | 1 - .../ic/src/eth_proxy/src/api/upgrade.rs | 18 ++- eth_bridge/ic/src/eth_proxy/src/common/cap.rs | 35 +--- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 65 +++++--- 10 files changed, 201 insertions(+), 115 deletions(-) delete mode 100644 eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs diff --git a/eth_bridge/ic/.gitignore b/eth_bridge/ic/.gitignore index bf483f84..89657e48 100644 --- a/eth_bridge/ic/.gitignore +++ b/eth_bridge/ic/.gitignore @@ -1,4 +1,4 @@ .dfx target - +.idea .DS_Store \ No newline at end of file diff --git a/eth_bridge/ic/Cargo.lock b/eth_bridge/ic/Cargo.lock index 01de8693..e3aa2b76 100644 --- a/eth_bridge/ic/Cargo.lock +++ b/eth_bridge/ic/Cargo.lock @@ -290,6 +290,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -370,25 +379,43 @@ dependencies = [ [[package]] name = "cap-common" version = "0.1.0" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" +source = "git+https://github.com/Psychedelic/cap?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" dependencies = [ "async-std", "ic-cdk 0.3.3", - "ic-certified-map", + "ic-certified-map 0.1.0", "ic-kit", "serde", "serde_bytes", "serde_cbor", - "sha2", + "sha2 0.9.9", +] + +[[package]] +name = "cap-common" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de3cd634ba6bd7442b1889b4df8d31e3b7c55e66dd8a084706495cc6f93bb13" +dependencies = [ + "async-std", + "candid", + "certified-vars", + "ic-cdk 0.5.1", + "ic-certified-map 0.3.1", + "ic-kit", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.10.6", ] [[package]] name = "cap-sdk" version = "0.1.0-alpha1" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" +source = "git+https://github.com/Psychedelic/cap?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" dependencies = [ "async-stream", - "cap-sdk-core", + "cap-sdk-core 0.1.0-alpha1", "futures", "ic-cdk 0.3.3", "ic-kit", @@ -397,26 +424,58 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cap-sdk" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0745e2cce153b876a02ca30b58fc1bc5d7667611dbf60f62bb368273aca734" +dependencies = [ + "async-std", + "async-stream", + "candid", + "cap-sdk-core 0.2.3", + "futures", + "ic-cdk 0.5.1", + "ic-kit", + "lazy_static", + "serde", + "thiserror", +] + [[package]] name = "cap-sdk-core" version = "0.1.0-alpha1" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" +source = "git+https://github.com/Psychedelic/cap?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" dependencies = [ - "cap-common", + "cap-common 0.1.0", "ic-cdk 0.3.3", "ic-kit", "serde", "thiserror", ] +[[package]] +name = "cap-sdk-core" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bda800da9f054c973d479b315bd7f717fdf78427b0584bbe9127d8e4e609688" +dependencies = [ + "candid", + "cap-common 0.2.3", + "ic-cdk 0.5.1", + "ic-kit", + "serde", + "thiserror", +] + [[package]] name = "cap-standards" version = "0.1.0-alpha1" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" +source = "git+https://github.com/Psychedelic/cap?branch=main#69bf1fcf37af804968c348847cc93d94fee83fc2" dependencies = [ "bincode", "candid", - "cap-sdk", + "cap-sdk 0.1.0-alpha1", "ic-cdk 0.3.3", "num-bigint", "serde", @@ -429,6 +488,21 @@ version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +[[package]] +name = "certified-vars" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7983d9b0ad23199b6a22246ac2138f9a1713ed66d6aef67f0376044e3415ebc" +dependencies = [ + "candid", + "hex", + "ic-cdk 0.5.1", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.9.9", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -488,6 +562,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctor" version = "0.1.21" @@ -513,6 +597,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -555,7 +649,7 @@ version = "0.1.0" dependencies = [ "async-trait", "candid", - "cap-sdk", + "cap-sdk 0.2.4", "cap-standards", "hex", "ic-cdk 0.5.1", @@ -829,7 +923,18 @@ checksum = "54640ea6dc0dd1c7cddec8586f42c81aa5a02edd0b82ad7b96360ee5b591d7b6" dependencies = [ "serde", "serde_bytes", - "sha2", + "sha2 0.9.9", +] + +[[package]] +name = "ic-certified-map" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea6b4530bf4e304ba870da9f58f6e10a84c74fe2fdec574ec7a316f031c37fd" +dependencies = [ + "serde", + "serde_bytes", + "sha2 0.10.6", ] [[package]] @@ -858,7 +963,7 @@ dependencies = [ "hex", "serde", "serde_bytes", - "sha2", + "sha2 0.9.9", "thiserror", ] @@ -1327,21 +1432,32 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest", + "digest 0.9.0", "opaque-debug", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.5", +] + [[package]] name = "sha3" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] @@ -1454,7 +1570,7 @@ dependencies = [ "assert-panic", "async-std", "candid", - "cap-sdk", + "cap-sdk 0.1.0-alpha1", "cap-standards", "ic-cdk 0.5.1", "ic-cdk-macros 0.3.3", diff --git a/eth_bridge/ic/src/eth_proxy/Cargo.toml b/eth_bridge/ic/src/eth_proxy/Cargo.toml index c4abea4f..5fba1324 100644 --- a/eth_bridge/ic/src/eth_proxy/Cargo.toml +++ b/eth_bridge/ic/src/eth_proxy/Cargo.toml @@ -19,4 +19,4 @@ serde = "1.0.130" serde_bytes = "0.11.5" num-bigint = "0.4.3" cap-std = { git = "https://github.com/Psychedelic/cap", branch = "main", package="cap-standards", features = ["alpha-dip20", "cap-sdk", "sdk-impls"] } -cap-sdk = { git = "https://github.com/Psychedelic/cap.git", branch = "main" } \ No newline at end of file +cap-sdk = "0.2.4" \ No newline at end of file diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index 16c748f8..91a0377c 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -1,10 +1,3 @@ -type ClaimableMessage = record { - token : principal; - msg_hash : text; - owner : principal; - msg_key : vec nat8; - amount : nat; -}; type Result = variant { Ok : nat; Err : TxError }; type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; type Result_2 = variant { Ok; Err : text }; @@ -23,10 +16,8 @@ service : { authorize : (principal) -> (); authorized : () -> (vec principal) query; burn : (principal, nat) -> (Result); - get_all : (principal) -> (vec ClaimableMessage) query; get_all_token_balance : () -> (Result_1); get_balance : (principal) -> (opt nat); - get_claimable_message_size : () -> (nat64) query; handle_message : (principal, nat, vec nat) -> (Result); mint : (nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index 6ae0c20a..bd6ce4b3 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -71,15 +71,12 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { s.remove_user_flag(caller); }); - ic_kit::ic::spawn(async move { - insert_claimable_asset(ClaimableMessage { - owner: eth_addr.clone(), - msg_hash: outgoing_message.msg_hash.clone(), - msg_key: outgoing_message.msg_key.clone(), - token: weth_ic_addr_pid.clone(), - amount: amount.clone(), - }) - .await + insert_claimable_asset(ClaimableMessage { + owner: eth_addr.clone(), + msg_hash: outgoing_message.msg_hash.clone(), + msg_key: outgoing_message.msg_key.clone(), + token: weth_ic_addr_pid.clone(), + amount: amount.clone(), }); // All correct diff --git a/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs b/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs deleted file mode 100644 index 4deb0c00..00000000 --- a/eth_bridge/ic/src/eth_proxy/src/api/claimable_assets.rs +++ /dev/null @@ -1,19 +0,0 @@ -use candid::candid_method; -use ic_cdk_macros::query; - -use crate::{ - common::types::{ClaimableMessage, EthereumAddr}, - proxy::STATE, -}; - -#[query(name = "get_all")] -#[candid_method(query, rename = "get_all")] -fn get_all(eth_address: EthereumAddr) -> Vec { - STATE.with(|s| s.get_claimable_messages(eth_address)) -} - -#[query(name = "get_claimable_message_size")] -#[candid_method(query, rename = "get_claimable_message_size")] -fn get_claimable_message_size() -> usize { - STATE.with(|s| s.get_claimable_messages_queue_size()) -} diff --git a/eth_bridge/ic/src/eth_proxy/src/api/mod.rs b/eth_bridge/ic/src/eth_proxy/src/api/mod.rs index 1949045a..ef3a2851 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/mod.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/mod.rs @@ -1,7 +1,6 @@ mod admin; mod burn; mod cap; -mod claimable_assets; mod get_balance; mod handle_message; mod init; diff --git a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs index 7a33bea3..759b47d9 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs @@ -1,13 +1,19 @@ +use crate::proxy::ToEvent; +use cap_sdk::{pending_transactions, restore_pending_transactions}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; -use crate::common::types::StableProxyState; +use crate::common::types::{ClaimableMessage, StableProxyState}; use crate::proxy::STATE; #[pre_upgrade] fn pre_upgrade() { - let stable_proxy_state = STATE.with(|s| s.take_all()); + let cap_pending_tx = pending_transactions(); + let _ = cap_pending_tx + .iter() + .map(|tx| STATE.with(|s| s.add_claimable_message(ClaimableMessage::from(tx.to_owned())))); + let stable_proxy_state = STATE.with(|s| s.take_all()); ic::stable_store((stable_proxy_state,)).expect("failed to messsage state"); } @@ -19,4 +25,12 @@ fn post_upgrade() { ic::stable_restore().expect("failed to restore stable messsage state"); STATE.with(|s| s.replace_all(stable_proxy_state)); + + let pending_cap_tx = STATE.with(|s| s.get_all_claimable_messages()); + let events = pending_cap_tx + .iter() + .map(|m| m.to_owned().to_cap_event()) + .collect(); + + restore_pending_transactions(events) } diff --git a/eth_bridge/ic/src/eth_proxy/src/common/cap.rs b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs index 901c7fab..eeaa2b38 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/cap.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs @@ -1,35 +1,10 @@ -use cap_sdk::{insert, IndefiniteEvent}; +use cap_sdk::insert_sync; -use crate::proxy::{ToEvent, STATE}; +use crate::proxy::ToEvent; use super::types::ClaimableMessage; -pub async fn insert_claimable_asset(message: ClaimableMessage) { - // add message to STATE claimable messages queue. - STATE.with(|s| { - s.add_claimable_message(message.clone()); - }); - - // flush STATE claimable messages queue - register_messages().await -} - -pub async fn register_messages() { - let mut pending_registrations = STATE.with(|s| s.get_all_claimable_messages()); - let mut i = 0; - while i < pending_registrations.len() { - if insert_into_cap(pending_registrations[i].to_cap_event()).await { - STATE.with(|s| s.remove_claimable_message(pending_registrations[i].clone())); - pending_registrations.swap_remove(i); - } else { - i += 1; - } - } -} - -async fn insert_into_cap(event: IndefiniteEvent) -> bool { - match insert(event.clone()).await { - Ok(_nat) => true, - Err(_error) => false, - } +pub fn insert_claimable_asset(message: ClaimableMessage) { + let event = message.to_cap_event(); + insert_sync(event); } diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 3b5e6bf7..4fff546b 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -90,16 +90,18 @@ impl ProxyState { } } - pub fn remove_claimable_message(&self, message: ClaimableMessage) { - let mut binding = self.messages_unclaimed.borrow_mut(); - let user_messages = binding.get_mut(&message.owner).unwrap(); - - let index = user_messages - .iter() - .position(|m| m.msg_key == message.msg_key); - - if index.is_some() { - user_messages.swap_remove(index.unwrap()); + pub fn get_claimable_messages( + &self, + eth_address_as_principal: Principal, + ) -> Vec { + if let Some(messages) = self + .messages_unclaimed + .borrow() + .get(ð_address_as_principal) + { + messages.to_owned() + } else { + Vec::::default() } } @@ -117,23 +119,17 @@ impl ProxyState { messages } - pub fn get_claimable_messages( - &self, - eth_address_as_principal: Principal, - ) -> Vec { - if let Some(messages) = self - .messages_unclaimed - .borrow() - .get(ð_address_as_principal) - { - messages.to_owned() - } else { - Vec::::default() - } - } + pub fn remove_claimable_message(&self, message: ClaimableMessage) { + let mut binding = self.messages_unclaimed.borrow_mut(); + let user_messages = binding.get_mut(&message.owner).unwrap(); + + let index = user_messages + .iter() + .position(|m| m.msg_key == message.msg_key); - pub fn get_claimable_messages_queue_size(&self) -> usize { - STATE.with(|s| s.messages_unclaimed.borrow().len()) + if index.is_some() { + user_messages.swap_remove(index.unwrap()); + } } pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { @@ -283,6 +279,23 @@ impl ToEvent for ClaimableMessage { } } +impl From for ClaimableMessage { + fn from(event: IndefiniteEvent) -> Self { + let msg_key: Nat = event.details[3].1.clone().try_into().unwrap(); + let msg_hash: String = event.details[2].1.clone().try_into().unwrap(); + let token: Principal = event.details[1].1.clone().try_into().unwrap(); + let amount: Nat = event.details[4].1.clone().try_into().unwrap(); + + ClaimableMessage { + owner: event.caller, + msg_key: msg_key.to_nonce_bytes(), + msg_hash: msg_hash, + token: token, + amount: amount, + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; From 0d16aac6efb9443a1c3b2c658bc856be5109426c Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 21 Sep 2022 15:03:50 +0200 Subject: [PATCH 04/34] upgrade cap_cdk and use insert_sync on dip20_proxy --- magic_bridge/ic/Cargo.lock | 66 ++++++++++++++++--- magic_bridge/ic/src/dip20_proxy/Cargo.toml | 2 +- .../ic/src/dip20_proxy/src/api/burn.rs | 18 +++-- .../ic/src/dip20_proxy/src/api/upgrade.rs | 17 ++++- .../ic/src/dip20_proxy/src/common/cap.rs | 35 ++-------- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 32 +++++++-- 6 files changed, 115 insertions(+), 55 deletions(-) diff --git a/magic_bridge/ic/Cargo.lock b/magic_bridge/ic/Cargo.lock index 07f7ae62..1c4b85a2 100644 --- a/magic_bridge/ic/Cargo.lock +++ b/magic_bridge/ic/Cargo.lock @@ -369,7 +369,25 @@ dependencies = [ [[package]] name = "cap-common" version = "0.2.3" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de3cd634ba6bd7442b1889b4df8d31e3b7c55e66dd8a084706495cc6f93bb13" +dependencies = [ + "async-std", + "candid", + "certified-vars", + "ic-cdk 0.5.2", + "ic-certified-map", + "ic-kit", + "serde", + "serde_bytes", + "serde_cbor", + "sha2 0.10.6", +] + +[[package]] +name = "cap-common" +version = "0.2.3" +source = "git+https://github.com/Psychedelic/cap?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" dependencies = [ "async-std", "candid", @@ -386,12 +404,13 @@ dependencies = [ [[package]] name = "cap-sdk" version = "0.2.4" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0745e2cce153b876a02ca30b58fc1bc5d7667611dbf60f62bb368273aca734" dependencies = [ "async-std", "async-stream", "candid", - "cap-sdk-core", + "cap-sdk-core 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures", "ic-cdk 0.5.2", "ic-kit", @@ -400,13 +419,44 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cap-sdk" +version = "0.2.4" +source = "git+https://github.com/Psychedelic/cap?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +dependencies = [ + "async-std", + "async-stream", + "candid", + "cap-sdk-core 0.2.3 (git+https://github.com/Psychedelic/cap?branch=main)", + "futures", + "ic-cdk 0.5.2", + "ic-kit", + "lazy_static", + "serde", + "thiserror", +] + +[[package]] +name = "cap-sdk-core" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bda800da9f054c973d479b315bd7f717fdf78427b0584bbe9127d8e4e609688" +dependencies = [ + "candid", + "cap-common 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ic-cdk 0.5.2", + "ic-kit", + "serde", + "thiserror", +] + [[package]] name = "cap-sdk-core" version = "0.2.3" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +source = "git+https://github.com/Psychedelic/cap?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" dependencies = [ "candid", - "cap-common", + "cap-common 0.2.3 (git+https://github.com/Psychedelic/cap?branch=main)", "ic-cdk 0.5.2", "ic-kit", "serde", @@ -416,11 +466,11 @@ dependencies = [ [[package]] name = "cap-standards" version = "0.2.0" -source = "git+https://github.com/Psychedelic/cap.git?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" +source = "git+https://github.com/Psychedelic/cap?branch=main#4ae1d09aa46c51e01fed37457255bdbebe601585" dependencies = [ "bincode", "candid", - "cap-sdk", + "cap-sdk 0.2.4 (git+https://github.com/Psychedelic/cap?branch=main)", "ic-cdk 0.5.2", "ic-cdk-macros 0.5.2", "num-bigint", @@ -559,7 +609,7 @@ version = "0.1.0" dependencies = [ "async-trait", "candid", - "cap-sdk", + "cap-sdk 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "cap-standards", "hex", "ic-cdk 0.5.2", diff --git a/magic_bridge/ic/src/dip20_proxy/Cargo.toml b/magic_bridge/ic/src/dip20_proxy/Cargo.toml index 9667639d..2964a184 100644 --- a/magic_bridge/ic/src/dip20_proxy/Cargo.toml +++ b/magic_bridge/ic/src/dip20_proxy/Cargo.toml @@ -19,4 +19,4 @@ serde = "1.0.130" serde_bytes = "0.11.5" num-bigint = "0.4.3" cap-std = { git = "https://github.com/Psychedelic/cap", branch = "main", package="cap-standards", features = ["alpha-dip20", "cap-sdk", "sdk-impls"] } -cap-sdk = { git = "https://github.com/Psychedelic/cap.git", branch = "main" } \ No newline at end of file +cap-sdk = "0.2.4" \ No newline at end of file diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 412aef55..45268e5e 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -94,17 +94,15 @@ async fn burn( s.remove_user_flag(caller, token_id) }); - ic_kit::ic::spawn(async move { - insert_claimable_asset(ClaimableMessage { - owner: eth_addr.clone(), - msg_hash: outgoing_message.msg_hash.clone(), - msg_key: outgoing_message.msg_key.clone(), - token_name: token_name_str, - token: token_id.clone(), - amount: amount.clone(), - }) - .await + insert_claimable_asset(ClaimableMessage { + owner: eth_addr.clone(), + msg_hash: outgoing_message.msg_hash.clone(), + msg_key: outgoing_message.msg_key.clone(), + token_name: token_name_str, + token: token_id.clone(), + amount: amount.clone(), }); + // All correct return Ok(burn_txn_id); } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs index f944f344..70ba8e43 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs @@ -1,11 +1,18 @@ +use crate::proxy::ToEvent; +use cap_sdk::{pending_transactions, restore_pending_transactions}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; -use crate::common::types::StableProxyState; +use crate::common::types::{ClaimableMessage, StableProxyState}; use crate::proxy::STATE; #[pre_upgrade] fn pre_upgrade() { + let cap_pending_tx = pending_transactions(); + let _ = cap_pending_tx + .iter() + .map(|tx| STATE.with(|s| s.add_claimable_message(ClaimableMessage::from(tx.to_owned())))); + let stable_magic_state = STATE.with(|s| s.take_all()); ic::stable_store((stable_magic_state,)).expect("failed to messsage state"); @@ -19,4 +26,12 @@ fn post_upgrade() { ic::stable_restore().expect("failed to restore stable messsage state"); STATE.with(|s| s.replace_all(stable_message_state)); + + let pending_cap_tx = STATE.with(|s| s.get_all_claimable_messages()); + let events = pending_cap_tx + .iter() + .map(|m| m.to_owned().to_cap_event()) + .collect(); + + restore_pending_transactions(events) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs index 901c7fab..03be7f0c 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs @@ -1,35 +1,12 @@ -use cap_sdk::{insert, IndefiniteEvent}; +use cap_sdk::insert_sync; -use crate::proxy::{ToEvent, STATE}; +use crate::proxy::ToEvent; use super::types::ClaimableMessage; -pub async fn insert_claimable_asset(message: ClaimableMessage) { - // add message to STATE claimable messages queue. - STATE.with(|s| { - s.add_claimable_message(message.clone()); - }); - - // flush STATE claimable messages queue - register_messages().await -} - -pub async fn register_messages() { - let mut pending_registrations = STATE.with(|s| s.get_all_claimable_messages()); - let mut i = 0; - while i < pending_registrations.len() { - if insert_into_cap(pending_registrations[i].to_cap_event()).await { - STATE.with(|s| s.remove_claimable_message(pending_registrations[i].clone())); - pending_registrations.swap_remove(i); - } else { - i += 1; - } - } -} - -async fn insert_into_cap(event: IndefiniteEvent) -> bool { - match insert(event.clone()).await { - Ok(_nat) => true, - Err(_error) => false, +pub fn insert_claimable_asset(message: ClaimableMessage) { + pub fn insert_claimable_asset(message: ClaimableMessage) { + let event = message.to_cap_event(); + insert_sync(event); } } diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index ada97a8f..66913cd3 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -283,6 +283,7 @@ impl ToEvent for ClaimableMessage { .insert("msgHash", self.msg_hash.clone()) .insert("msgHashKey", self.msg_key.to_nat()) .insert("amount", self.amount.clone()) + .insert("name", self.token_name.clone()) .build(); IndefiniteEventBuilder::new() @@ -294,6 +295,25 @@ impl ToEvent for ClaimableMessage { } } +impl From for ClaimableMessage { + fn from(event: IndefiniteEvent) -> Self { + let msg_key: Nat = event.details[3].1.clone().try_into().unwrap(); + let msg_hash: String = event.details[2].1.clone().try_into().unwrap(); + let token: Principal = event.details[1].1.clone().try_into().unwrap(); + let amount: Nat = event.details[4].1.clone().try_into().unwrap(); + let name: String = event.details[5].1.clone().try_into().unwrap(); + + ClaimableMessage { + owner: event.caller, + msg_key: msg_key.to_nonce_bytes(), + msg_hash: msg_hash, + token: token, + amount: amount, + token_name: name, + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; @@ -613,23 +633,23 @@ mod tests { }; // add first msg - STATE.with(|s| s.add_claimable_message(message_1)); + STATE.with(|s| s.add_claimable_message(message_1.clone())); // add second msg - STATE.with(|s| s.add_claimable_message(message_2)); + STATE.with(|s| s.add_claimable_message(message_2.clone())); // check if both messages are in the claimable messages list for eth_addr_1 let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 2); - // remove one msg -> the one with amount_2 (both are the same token, but different amount) - let result_remove_1 = STATE.with(|s| s.remove_claimable_message(message_1.clone())); + // remove one msg -> the one with amount_1 (both are the same token, but different amount) + let _ = STATE.with(|s| s.remove_claimable_message(message_1.clone())); // check if only one message is in the claimable messages list for eth_addr_1 claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); assert_eq!(claimable_messages.len(), 1); - // the message that is left is the one with amount_1 - assert_eq!(claimable_messages[0].amount, amount_1); + // the message that is left is the one with amount_2 + assert_eq!(claimable_messages[0].amount, amount_2); } #[test] From d618c7d061470de6c19a181ff0042f29ccfe38b4 Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 21 Sep 2022 15:14:49 +0200 Subject: [PATCH 05/34] rename trait ToEvent to ToCapEvent --- eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs | 2 +- eth_bridge/ic/src/eth_proxy/src/common/cap.rs | 2 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 5 +++-- magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/common/cap.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 4 ++-- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs index 759b47d9..12f51136 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs @@ -1,4 +1,4 @@ -use crate::proxy::ToEvent; +use crate::proxy::ToCapEvent; use cap_sdk::{pending_transactions, restore_pending_transactions}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; diff --git a/eth_bridge/ic/src/eth_proxy/src/common/cap.rs b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs index eeaa2b38..4f6228aa 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/cap.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/cap.rs @@ -1,6 +1,6 @@ use cap_sdk::insert_sync; -use crate::proxy::ToEvent; +use crate::proxy::ToCapEvent; use super::types::ClaimableMessage; diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 4fff546b..093cc16a 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -256,11 +256,11 @@ impl ToBytes for Nat { } } -pub trait ToEvent { +pub trait ToCapEvent { fn to_cap_event(&self) -> IndefiniteEvent; } -impl ToEvent for ClaimableMessage { +impl ToCapEvent for ClaimableMessage { fn to_cap_event(&self) -> IndefiniteEvent { let details = DetailsBuilder::default() .insert("owner", self.owner) @@ -268,6 +268,7 @@ impl ToEvent for ClaimableMessage { .insert("msgHash", self.msg_hash.clone()) .insert("msgHashKey", self.msg_key.to_nat()) .insert("amount", self.amount.clone()) + .insert("name", String::from("Wrapped Ether")) .build(); IndefiniteEventBuilder::new() diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs index 70ba8e43..bd561205 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs @@ -1,4 +1,4 @@ -use crate::proxy::ToEvent; +use crate::proxy::ToCapEvent; use cap_sdk::{pending_transactions, restore_pending_transactions}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs index 03be7f0c..bd36d0ac 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs @@ -1,6 +1,6 @@ use cap_sdk::insert_sync; -use crate::proxy::ToEvent; +use crate::proxy::ToCapEvent; use super::types::ClaimableMessage; diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index 66913cd3..60fbc63e 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -271,11 +271,11 @@ impl ToBytes for Nat { } } -pub trait ToEvent { +pub trait ToCapEvent { fn to_cap_event(&self) -> IndefiniteEvent; } -impl ToEvent for ClaimableMessage { +impl ToCapEvent for ClaimableMessage { fn to_cap_event(&self) -> IndefiniteEvent { let details = DetailsBuilder::default() .insert("owner", self.owner) From d769dbae686b54d76c93512455bf08e379972200 Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 21 Sep 2022 16:32:06 +0200 Subject: [PATCH 06/34] remove claimable_assets candid methods from dip20_proxy --- .../ic/src/dip20_proxy/dip20_proxy.did | 10 --------- .../dip20_proxy/src/api/claimable_assets.rs | 21 ------------------- .../ic/src/dip20_proxy/src/api/mod.rs | 1 - 3 files changed, 32 deletions(-) delete mode 100644 magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index 5b949aed..a52af56f 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -1,11 +1,3 @@ -type ClaimableMessage = record { - token : principal; - msg_hash : text; - owner : principal; - msg_key : vec nat8; - amount : nat; - token_name : text; -}; type Result = variant { Ok : nat; Err : TxError }; type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; type Result_2 = variant { Ok; Err : text }; @@ -24,10 +16,8 @@ service : { authorize : (principal) -> (); authorized : () -> (vec principal) query; burn : (principal, principal, nat) -> (Result); - claimable_get_all : (principal) -> (vec ClaimableMessage) query; get_all_token_balance : () -> (Result_1); get_balance : (principal) -> (opt nat); - get_claimable_message_size : () -> (nat64) query; handle_message : (principal, nat, vec nat) -> (Result); mint : (principal, nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs b/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs deleted file mode 100644 index 3f6e0e8d..00000000 --- a/magic_bridge/ic/src/dip20_proxy/src/api/claimable_assets.rs +++ /dev/null @@ -1,21 +0,0 @@ -use candid::{candid_method, Nat}; -use ic_cdk_macros::{query, update}; - -use crate::api::admin::is_authorized; -use crate::common::types::TokendId; -use crate::{ - common::types::{ClaimableMessage, EthereumAddr}, - proxy::STATE, -}; - -#[query(name = "claimable_get_all")] -#[candid_method(query, rename = "claimable_get_all")] -fn claimable_get_all(eth_address: EthereumAddr) -> Vec { - STATE.with(|s| s.get_claimable_messages(eth_address)) -} - -#[query(name = "get_claimable_message_size")] -#[candid_method(query, rename = "get_claimable_message_size")] -fn get_claimable_message_size() -> usize { - STATE.with(|s| s.get_claimable_messages_queue_size()) -} diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs index 1949045a..ef3a2851 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs @@ -1,7 +1,6 @@ mod admin; mod burn; mod cap; -mod claimable_assets; mod get_balance; mod handle_message; mod init; From f9450c4f453903f6e1a9e25139138716741a1ff4 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 23 Sep 2022 10:21:43 +0200 Subject: [PATCH 07/34] use archive() fn on upgrade --- .../ic/src/dip20_proxy/src/api/upgrade.rs | 25 +-- .../ic/src/dip20_proxy/src/common/cap.rs | 6 +- .../ic/src/dip20_proxy/src/common/types.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 185 +----------------- 4 files changed, 20 insertions(+), 198 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs index bd561205..26218aff 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/upgrade.rs @@ -1,37 +1,28 @@ -use crate::proxy::ToCapEvent; -use cap_sdk::{pending_transactions, restore_pending_transactions}; +use cap_sdk::{archive, from_archive, Archive}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; -use crate::common::types::{ClaimableMessage, StableProxyState}; +use crate::common::types::StableProxyState; use crate::proxy::STATE; #[pre_upgrade] fn pre_upgrade() { - let cap_pending_tx = pending_transactions(); - let _ = cap_pending_tx - .iter() - .map(|tx| STATE.with(|s| s.add_claimable_message(ClaimableMessage::from(tx.to_owned())))); - let stable_magic_state = STATE.with(|s| s.take_all()); + let cap = archive(); - ic::stable_store((stable_magic_state,)).expect("failed to messsage state"); + ic::stable_store((stable_magic_state, cap)).expect("failed to messsage state"); } #[post_upgrade] fn post_upgrade() { STATE.with(|s| s.clear_all()); - let (stable_message_state,): (StableProxyState,) = + let (stable_message_state, cap): (StableProxyState, Option) = ic::stable_restore().expect("failed to restore stable messsage state"); STATE.with(|s| s.replace_all(stable_message_state)); - let pending_cap_tx = STATE.with(|s| s.get_all_claimable_messages()); - let events = pending_cap_tx - .iter() - .map(|m| m.to_owned().to_cap_event()) - .collect(); - - restore_pending_transactions(events) + if cap.is_some() { + from_archive(cap.unwrap()) + } } diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs index bd36d0ac..4f6228aa 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/cap.rs @@ -5,8 +5,6 @@ use crate::proxy::ToCapEvent; use super::types::ClaimableMessage; pub fn insert_claimable_asset(message: ClaimableMessage) { - pub fn insert_claimable_asset(message: ClaimableMessage) { - let event = message.to_cap_event(); - insert_sync(event); - } + let event = message.to_cap_event(); + insert_sync(event); } diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 4a327a5d..d8496416 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -67,7 +67,7 @@ pub struct OutgoingMessage { pub struct ClaimableMessage { pub owner: EthereumAddr, pub msg_hash: String, - pub msg_key: [u8; 32], + pub msg_key: Option<[u8; 32]>, pub token_name: String, pub token: TokendId, pub amount: Nat, diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index 60fbc63e..54007bfa 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -5,8 +5,8 @@ use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; use crate::common::types::{ - ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, - StableProxyState, TokendId, TxFlag, + ClaimableMessage, MessageHash, MessageStatus, NonceBytes, ProxyState, StableProxyState, + TokendId, TxFlag, }; pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; @@ -78,64 +78,6 @@ impl ProxyState { .insert(caller, HashMap::from([(token_id, amount)])); } - pub fn add_claimable_message(&self, message: ClaimableMessage) { - let mut user_messages = self.messages_unclaimed.borrow_mut(); - match user_messages.get_mut(&message.owner) { - Some(messages) => messages.push(message), - None => { - let mut init_vector = Vec::::new(); - init_vector.push(message.clone()); - user_messages.insert(message.owner.clone(), init_vector); - } - } - } - - pub fn remove_claimable_message(&self, message: ClaimableMessage) { - let mut binding = self.messages_unclaimed.borrow_mut(); - let user_messages = binding.get_mut(&message.owner).unwrap(); - - let index = user_messages - .iter() - .position(|m| m.msg_key == message.msg_key); - - if index.is_some() { - user_messages.swap_remove(index.unwrap()); - } - } - - pub fn get_all_claimable_messages(&self) -> Vec { - let mut messages: Vec = Vec::default(); - for address in self.messages_unclaimed.borrow().keys() { - let address_messages = self - .messages_unclaimed - .borrow() - .get(address) - .unwrap() - .to_owned(); - messages.extend(address_messages) - } - messages - } - - pub fn get_claimable_messages( - &self, - eth_address_as_principal: Principal, - ) -> Vec { - if let Some(messages) = self - .messages_unclaimed - .borrow() - .get(ð_address_as_principal) - { - messages.to_owned() - } else { - Vec::::default() - } - } - - pub fn get_claimable_messages_queue_size(&self) -> usize { - STATE.with(|s| s.messages_unclaimed.borrow().len()) - } - pub fn set_user_flag( &self, user: Principal, @@ -277,11 +219,16 @@ pub trait ToCapEvent { impl ToCapEvent for ClaimableMessage { fn to_cap_event(&self) -> IndefiniteEvent { + let hash_key = if self.msg_key.is_some() { + self.msg_key.unwrap() + } else { + [0; 32] + }; let details = DetailsBuilder::default() .insert("owner", self.owner) .insert("ethContractAddress", self.token) .insert("msgHash", self.msg_hash.clone()) - .insert("msgHashKey", self.msg_key.to_nat()) + .insert("msgHashKey", hash_key.to_nat()) .insert("amount", self.amount.clone()) .insert("name", self.token_name.clone()) .build(); @@ -305,7 +252,7 @@ impl From for ClaimableMessage { ClaimableMessage { owner: event.caller, - msg_key: msg_key.to_nonce_bytes(), + msg_key: Some(msg_key.to_nonce_bytes()), msg_hash: msg_hash, token: token, amount: amount, @@ -538,120 +485,6 @@ mod tests { ); } - #[test] - fn test_claimable_messages() { - let eth_addr_1 = Principal::from_slice( - &hex::decode("15B661f6D3FD9A7ED8Ed4c88bCcfD1546644443f").unwrap(), - ); - let token_name = String::from("USDC"); - - let msg_hash_1 = String::from("123123123"); - let msg_key_1: [u8; 32] = [0; 32]; - let msg_key_2: [u8; 32] = [1; 32]; - - let token_id_1 = Principal::from_slice( - &hex::decode("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(), - ); - let amount_1 = Nat::from(1_u64); - - // first msg - let message_1 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_1, - token_name: token_name.clone(), - token: token_id_1.clone(), - amount: amount_1.clone(), - }; - - // second msg -> identical to first msg but with different msg_key - let message_2 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_2, - token_name: token_name.clone(), - token: token_id_1.clone(), - amount: amount_1.clone(), - }; - - // add first msg - STATE.with(|s| s.add_claimable_message(message_1.clone())); - // add second msg - STATE.with(|s| s.add_claimable_message(message_2.clone())); - - // check if both messages are in the claimable messages list for eth_addr_1 - let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 2); - - // remove one msg (both have same amount and token) - let result_remove_1 = STATE.with(|s| s.remove_claimable_message(message_1.clone())); - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 1); - - // remove second msg (both have same amount and token) - let result_remove_2 = STATE.with(|s| s.remove_claimable_message(message_2.clone())); - - // check if no messages are in the claimable messages list for eth_addr_1 - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 0); - } - - #[test] - fn test_claimable_messages_with_different_amounts() { - let eth_addr_1 = Principal::from_slice( - &hex::decode("15B661f6D3FD9A7ED8Ed4c88bCcfD1546644443f").unwrap(), - ); - let token_name = String::from("DAI"); - let msg_hash_1 = String::from("123123123"); - let token_id_1 = Principal::from_slice( - &hex::decode("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48").unwrap(), - ); - let amount_1 = Nat::from(1_u64); - let amount_2 = Nat::from(2_u64); - - let msg_key_1: [u8; 32] = [0; 32]; - let msg_key_2: [u8; 32] = [1; 32]; - - // first msg - let message_1 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_1, - token_name: token_name.clone(), - token: token_id_1.clone(), - amount: amount_1.clone(), - }; - - // second msg -> same token, different amount - let message_2 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_2, - token_name: token_name.clone(), - token: token_id_1.clone(), - amount: amount_2.clone(), - }; - - // add first msg - STATE.with(|s| s.add_claimable_message(message_1.clone())); - // add second msg - STATE.with(|s| s.add_claimable_message(message_2.clone())); - - // check if both messages are in the claimable messages list for eth_addr_1 - let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 2); - - // remove one msg -> the one with amount_1 (both are the same token, but different amount) - let _ = STATE.with(|s| s.remove_claimable_message(message_1.clone())); - - // check if only one message is in the claimable messages list for eth_addr_1 - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 1); - - // the message that is left is the one with amount_2 - assert_eq!(claimable_messages[0].amount, amount_2); - } - #[test] fn test_user_flags() { let token_one = Principal::from_str("nnplb-2yaaa-aaaab-qagjq-cai").unwrap(); From db173158260bfdd15c377915d420ea2282c45607 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 23 Sep 2022 10:30:33 +0200 Subject: [PATCH 08/34] use archive() fn on upgrade eth_proxy --- .../ic/src/eth_proxy/src/api/upgrade.rs | 24 +-- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 157 ------------------ 2 files changed, 8 insertions(+), 173 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs index 12f51136..daaa492e 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/upgrade.rs @@ -1,36 +1,28 @@ -use crate::proxy::ToCapEvent; -use cap_sdk::{pending_transactions, restore_pending_transactions}; +use cap_sdk::{archive, from_archive, Archive}; use ic_kit::ic; use ic_kit::macros::{post_upgrade, pre_upgrade}; -use crate::common::types::{ClaimableMessage, StableProxyState}; +use crate::common::types::StableProxyState; use crate::proxy::STATE; #[pre_upgrade] fn pre_upgrade() { - let cap_pending_tx = pending_transactions(); - let _ = cap_pending_tx - .iter() - .map(|tx| STATE.with(|s| s.add_claimable_message(ClaimableMessage::from(tx.to_owned())))); + let cap = archive(); let stable_proxy_state = STATE.with(|s| s.take_all()); - ic::stable_store((stable_proxy_state,)).expect("failed to messsage state"); + ic::stable_store((stable_proxy_state, cap)).expect("failed to messsage state"); } #[post_upgrade] fn post_upgrade() { STATE.with(|s| s.clear_all()); - let (stable_proxy_state,): (StableProxyState,) = + let (stable_proxy_state, cap): (StableProxyState, Option) = ic::stable_restore().expect("failed to restore stable messsage state"); STATE.with(|s| s.replace_all(stable_proxy_state)); - let pending_cap_tx = STATE.with(|s| s.get_all_claimable_messages()); - let events = pending_cap_tx - .iter() - .map(|m| m.to_owned().to_cap_event()) - .collect(); - - restore_pending_transactions(events) + if cap.is_some() { + from_archive(cap.unwrap()) + } } diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 093cc16a..7bf6591d 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -78,60 +78,6 @@ impl ProxyState { .insert(caller, HashMap::from([(token_id, amount)])); } - pub fn add_claimable_message(&self, message: ClaimableMessage) { - let mut user_messages = self.messages_unclaimed.borrow_mut(); - match user_messages.get_mut(&message.owner) { - Some(messages) => messages.push(message), - None => { - let mut init_vector = Vec::::new(); - init_vector.push(message.clone()); - user_messages.insert(message.owner.clone(), init_vector); - } - } - } - - pub fn get_claimable_messages( - &self, - eth_address_as_principal: Principal, - ) -> Vec { - if let Some(messages) = self - .messages_unclaimed - .borrow() - .get(ð_address_as_principal) - { - messages.to_owned() - } else { - Vec::::default() - } - } - - pub fn get_all_claimable_messages(&self) -> Vec { - let mut messages: Vec = Vec::default(); - for address in self.messages_unclaimed.borrow().keys() { - let address_messages = self - .messages_unclaimed - .borrow() - .get(address) - .unwrap() - .to_owned(); - messages.extend(address_messages) - } - messages - } - - pub fn remove_claimable_message(&self, message: ClaimableMessage) { - let mut binding = self.messages_unclaimed.borrow_mut(); - let user_messages = binding.get_mut(&message.owner).unwrap(); - - let index = user_messages - .iter() - .position(|m| m.msg_key == message.msg_key); - - if index.is_some() { - user_messages.swap_remove(index.unwrap()); - } - } - pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { if self.user_is_flagged(user) { return Err(format!("User: {} is performing another action", user)); @@ -521,109 +467,6 @@ mod tests { ); } - #[test] - fn test_claimable_messages() { - let eth_addr_1 = Principal::from_slice( - &hex::decode("15B661f6D3FD9A7ED8Ed4c88bCcfD1546644443f").unwrap(), - ); - let msg_key_1: [u8; 32] = [0; 32]; - - let msg_hash_1 = String::from("123123123"); - let msg_key_2: [u8; 32] = [1; 32]; - let token_id_1 = Principal::from_text(WETH_ADDRESS_IC).unwrap(); - let amount_1 = Nat::from(1_u64); - - // first msg - let message_1 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_1.clone(), - token: token_id_1.clone(), - amount: amount_1.clone(), - }; - - // second msg -> identical to first msg but with different msg_key - let message_2 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_2.clone(), - token: token_id_1.clone(), - amount: amount_1.clone(), - }; - - // add first msg - STATE.with(|s| s.add_claimable_message(message_1.clone())); - // add second msg - STATE.with(|s| s.add_claimable_message(message_2.clone())); - - // check if both messages are in the claimable messages list for eth_addr_1 - let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 2); - - // remove one msg (both have same amount and token) - STATE.with(|s| s.remove_claimable_message(message_1.clone())); - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 1); - - // remove second msg (both have same amount and token) - STATE.with(|s| s.remove_claimable_message(message_2.clone())); - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 0); - } - - #[test] - fn test_claimable_messages_with_different_amounts() { - let eth_addr_1 = Principal::from_slice( - &hex::decode("15B661f6D3FD9A7ED8Ed4c88bCcfD1546644443f").unwrap(), - ); - let msg_key_1: [u8; 32] = [0; 32]; - let msg_hash_1 = String::from("123123123"); - let weth_principal = Principal::from_text(WETH_ADDRESS_IC).unwrap(); - let amount_1 = Nat::from(1_u64); - - let msg_key_2: [u8; 32] = [1; 32]; - let amount_2 = Nat::from(2_u64); - - // first msg - let message_1 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_1.clone(), - token: weth_principal.clone(), - amount: amount_1.clone(), - }; - - // second msg -> same token, different amount - let message_2 = ClaimableMessage { - owner: eth_addr_1.clone(), - msg_hash: msg_hash_1.clone(), - msg_key: msg_key_2.clone(), - token: weth_principal.clone(), - amount: amount_2.clone(), - }; - - // add first msg - STATE.with(|s| s.add_claimable_message(message_1)); - // add second msg - STATE.with(|s| s.add_claimable_message(message_2.clone())); - - // check if both messages are in the claimable messages list for eth_addr_1 - let mut claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 2); - - // remove one msg -> the one with amount_2 (both are the same token, but different amount) - STATE.with(|s| s.remove_claimable_message(message_2)); - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 1); - - // check if only one message is in the claimable messages list for eth_addr_1 - claimable_messages = STATE.with(|s| s.get_claimable_messages(eth_addr_1.clone())); - assert_eq!(claimable_messages.len(), 1); - - // the message that is left is the one with amount_1 - assert_eq!(claimable_messages[0].amount, amount_1); - } - #[test] fn test_user_flags() { let user = From c19a962ce75ddf33dc2589e137ea446127e9458a Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 18 Oct 2022 12:15:01 +0200 Subject: [PATCH 09/34] eth_proxy: add caller to claimable message --- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 1 + eth_bridge/ic/src/eth_proxy/src/common/types.rs | 1 + eth_bridge/ic/src/eth_proxy/src/proxy.rs | 3 +++ 3 files changed, 5 insertions(+) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index bd6ce4b3..99ad003f 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -72,6 +72,7 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { }); insert_claimable_asset(ClaimableMessage { + from: caller, owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), msg_key: outgoing_message.msg_key.clone(), diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index d7496188..dc021899 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -28,6 +28,7 @@ pub struct ClaimableMessage { pub msg_hash: MessageHash, pub token: TokendId, pub amount: Nat, + pub from: Principal, } #[derive(CandidType, Deserialize, Debug)] diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 7bf6591d..7b586c2e 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -215,6 +215,7 @@ impl ToCapEvent for ClaimableMessage { .insert("msgHashKey", self.msg_key.to_nat()) .insert("amount", self.amount.clone()) .insert("name", String::from("Wrapped Ether")) + .insert("from", self.from) .build(); IndefiniteEventBuilder::new() @@ -232,8 +233,10 @@ impl From for ClaimableMessage { let msg_hash: String = event.details[2].1.clone().try_into().unwrap(); let token: Principal = event.details[1].1.clone().try_into().unwrap(); let amount: Nat = event.details[4].1.clone().try_into().unwrap(); + let from: Principal = event.details[6].1.clone().try_into().unwrap(); ClaimableMessage { + from: from, owner: event.caller, msg_key: msg_key.to_nonce_bytes(), msg_hash: msg_hash, From 69c0b08f89bb79139cedd8d6a73bdd0aad22bf73 Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 18 Oct 2022 12:21:04 +0200 Subject: [PATCH 10/34] dip20_proxy: add caller to claimable message --- magic_bridge/ic/src/dip20_proxy/src/api/burn.rs | 3 ++- magic_bridge/ic/src/dip20_proxy/src/common/types.rs | 1 + magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 45268e5e..7df285bd 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -95,9 +95,10 @@ async fn burn( }); insert_claimable_asset(ClaimableMessage { + from: caller, owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), - msg_key: outgoing_message.msg_key.clone(), + msg_key: Some(outgoing_message.msg_key.clone()), token_name: token_name_str, token: token_id.clone(), amount: amount.clone(), diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index d8496416..5e911b33 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -71,6 +71,7 @@ pub struct ClaimableMessage { pub token_name: String, pub token: TokendId, pub amount: Nat, + pub from: Principal, } #[derive(CandidType, Deserialize, Default)] diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index 54007bfa..553451f4 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -231,6 +231,7 @@ impl ToCapEvent for ClaimableMessage { .insert("msgHashKey", hash_key.to_nat()) .insert("amount", self.amount.clone()) .insert("name", self.token_name.clone()) + .insert("from", self.from.clone()) .build(); IndefiniteEventBuilder::new() @@ -249,8 +250,10 @@ impl From for ClaimableMessage { let token: Principal = event.details[1].1.clone().try_into().unwrap(); let amount: Nat = event.details[4].1.clone().try_into().unwrap(); let name: String = event.details[5].1.clone().try_into().unwrap(); + let from: Principal = event.details[6].1.clone().try_into().unwrap(); ClaimableMessage { + from: from, owner: event.caller, msg_key: Some(msg_key.to_nonce_bytes()), msg_hash: msg_hash, From f6cc64cd74070eafc39a064fbea5333d314c0673 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 22 Sep 2022 13:23:54 +0200 Subject: [PATCH 11/34] add eth_contract_address to withdraw payload --- .../ic/src/dip20_proxy/src/api/withdraw.rs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 9bfc5db1..2f57da50 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -1,17 +1,18 @@ +use ic_cdk::export::candid::Principal; use ic_kit::{ candid::{candid_method, Nat}, ic, macros::update, - Principal, }; use crate::{ common::{ dip20::Dip20, + magic::Magic, tera::Tera, types::{EthereumAddr, TokendId, TxError, TxFlag, TxReceipt}, }, - proxy::{ToNat, ERC20_ADDRESS_ETH, STATE, TERA_ADDRESS}, + proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}, }; /// withdraw left over balance if burn/mint fails @@ -19,9 +20,23 @@ use crate::{ /// todo withdraw specific balance #[update(name = "withdraw")] #[candid_method(update, rename = "withdraw")] -pub async fn withdraw(token_id: TokendId, eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { +pub async fn withdraw( + eth_contract_as_principal: TokendId, + eth_addr: EthereumAddr, + _amount: Nat, +) -> TxReceipt { let caller = ic::caller(); + let magic_bridge = Principal::from_text(MAGIC_ADDRESS_IC).unwrap(); + + let token_id: Principal = match magic_bridge + .get_canister(eth_contract_as_principal.clone()) + .await + { + Ok(canister_id) => canister_id, + Err(error) => return Err(error), + }; + if (token_id.name().await).is_err() { return Err(TxError::Other(format!( "Token {} canister is not responding!", @@ -43,7 +58,12 @@ pub async fn withdraw(token_id: TokendId, eth_addr: EthereumAddr, _amount: Nat) let get_balance = STATE.with(|s| s.get_balance(caller, token_id)); if let Some(balance) = get_balance { - let payload = [eth_addr.clone().to_nat(), balance.clone()].to_vec(); + let payload = [ + eth_contract_as_principal.to_nat(), + eth_addr.clone().to_nat(), + balance.clone(), + ] + .to_vec(); let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); if tera_id.send_message(erc20_addr_pid, payload).await.is_err() { STATE.with(|s| s.remove_user_flag(caller, token_id)); From bb5b837bbb5f14e22fd5322e6a5ae0fa6c28f82e Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 22 Sep 2022 13:37:07 +0200 Subject: [PATCH 12/34] add claimable_asset in withdraw for dip20_proxy --- .../ic/src/dip20_proxy/src/api/withdraw.rs | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 2f57da50..a65b92fc 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -7,10 +7,11 @@ use ic_kit::{ use crate::{ common::{ + cap::insert_claimable_asset, dip20::Dip20, magic::Magic, tera::Tera, - types::{EthereumAddr, TokendId, TxError, TxFlag, TxReceipt}, + types::{ClaimableMessage, EthereumAddr, TokendId, TxError, TxFlag, TxReceipt}, }, proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}, }; @@ -26,7 +27,7 @@ pub async fn withdraw( _amount: Nat, ) -> TxReceipt { let caller = ic::caller(); - + let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); let magic_bridge = Principal::from_text(MAGIC_ADDRESS_IC).unwrap(); let token_id: Principal = match magic_bridge @@ -37,12 +38,15 @@ pub async fn withdraw( Err(error) => return Err(error), }; - if (token_id.name().await).is_err() { - return Err(TxError::Other(format!( - "Token {} canister is not responding!", - token_id.to_string(), - ))); - } + let token_name = match token_id.name().await { + Ok(name) => name, + Err(_) => { + return Err(TxError::Other(format!( + "Token {} canister is not responding!", + token_id.to_string() + ))) + } + }; let set_flag = STATE.with(|s| s.set_user_flag(caller, token_id, TxFlag::Withdrawing)); if set_flag.is_err() { @@ -64,17 +68,29 @@ pub async fn withdraw( balance.clone(), ] .to_vec(); - let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); - if tera_id.send_message(erc20_addr_pid, payload).await.is_err() { - STATE.with(|s| s.remove_user_flag(caller, token_id)); - return Err(TxError::Other(format!("Sending message to L1 failed!"))); - } - let zero = Nat::from(0_u32); - STATE.with(|s| { - s.update_balance(caller, token_id, zero); - s.remove_user_flag(caller, token_id); - }); + match tera_id.send_message(erc20_addr_pid, payload).await { + Ok(outgoing_message) => { + let zero = Nat::from(0_u32); + STATE.with(|s| { + s.update_balance(caller, token_id, zero); + s.remove_user_flag(caller, token_id); + }); + + insert_claimable_asset(ClaimableMessage { + owner: eth_addr.clone(), + msg_hash: outgoing_message.msg_hash.clone(), + msg_key: outgoing_message.msg_key.clone(), + token_name: token_name, + token: token_id.clone(), + amount: balance.clone(), + }) + } + Err(_) => { + STATE.with(|s| s.remove_user_flag(caller, token_id)); + return Err(TxError::Other(format!("Sending message to L1 failed!"))); + } + } } STATE.with(|s| s.remove_user_flag(caller, token_id)); From 32811791b31e7321165a1224157210ab73bdd537 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 22 Sep 2022 13:48:13 +0200 Subject: [PATCH 13/34] add claimable_asset in withdraw for eth_proxy --- .../ic/src/eth_proxy/src/api/withdraw.rs | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index 768fa819..f9502978 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -9,8 +9,9 @@ use ic_kit::{ use crate::{ common::{ + cap::insert_claimable_asset, tera::Tera, - types::{EthereumAddr, TxError, TxFlag, TxReceipt}, + types::{ClaimableMessage, EthereumAddr, TxError, TxFlag, TxReceipt}, weth::Weth, }, proxy::{ToNat, STATE, TERA_ADDRESS, WETH_ADDRESS_ETH, WETH_ADDRESS_IC}, @@ -24,6 +25,7 @@ use crate::{ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { let caller = ic::caller(); let weth_ic_addr_pid = Principal::from_str(WETH_ADDRESS_IC).unwrap(); + let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); if (weth_ic_addr_pid.name().await).is_err() { return Err(TxError::Other(format!( @@ -47,21 +49,28 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { let get_balance = STATE.with(|s| s.get_balance(caller, weth_ic_addr_pid)); if let Some(balance) = get_balance { let payload = [eth_addr.clone().to_nat(), balance.clone()].to_vec(); - let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); - if tera_id - .send_message(weth_eth_addr_pid, payload) - .await - .is_err() - { - STATE.with(|s| s.remove_user_flag(caller)); - return Err(TxError::Other(format!("Sending message to L1 failed!"))); - } - let zero = Nat::from(0_u32); - STATE.with(|s| { - s.update_balance(caller, weth_ic_addr_pid, zero); - s.remove_user_flag(caller); - }); + match tera_id.send_message(weth_eth_addr_pid, payload).await { + Ok(outgoing_message) => { + let zero = Nat::from(0_u32); + STATE.with(|s| { + s.update_balance(caller, weth_ic_addr_pid, zero); + s.remove_user_flag(caller); + }); + + insert_claimable_asset(ClaimableMessage { + owner: eth_addr.clone(), + msg_hash: outgoing_message.msg_hash.clone(), + msg_key: outgoing_message.msg_key.clone(), + token: weth_ic_addr_pid.clone(), + amount: balance.clone(), + }); + } + Err(_) => { + STATE.with(|s| s.remove_user_flag(caller)); + return Err(TxError::Other(format!("Sending message to L1 failed!"))); + } + } } STATE.with(|s| s.remove_user_flag(caller)); From cebc5e8e54eef2abc1f2e7758218ff181c769a51 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 23 Sep 2022 10:18:39 +0200 Subject: [PATCH 14/34] use eth address as principal to keep balance --- magic_bridge/ic/src/dip20_proxy/src/api/burn.rs | 9 +++++---- magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 7df285bd..801defec 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -59,7 +59,7 @@ async fn burn( match transfer_from { Ok(_) => { - STATE.with(|s| s.add_balance(caller, token_id, amount.clone())); + STATE.with(|s| s.add_balance(caller, eth_contract_as_principal, amount.clone())); let burn = token_id.burn(amount.clone()).await; @@ -82,12 +82,13 @@ async fn burn( STATE.with(|s| { // there could be an underflow here // like negative balance - let current_balance = - s.get_balance(caller, token_id).unwrap_or(Nat::from(0)); + let current_balance = s + .get_balance(caller, eth_contract_as_principal) + .unwrap_or(Nat::from(0)); s.update_balance( caller, - token_id, + eth_contract_as_principal, current_balance - amount.clone(), ); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index a65b92fc..107636e9 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -1,7 +1,7 @@ use ic_cdk::export::candid::Principal; use ic_kit::{ candid::{candid_method, Nat}, - ic, + ic::{self, balance}, macros::update, }; @@ -60,7 +60,7 @@ pub async fn withdraw( let erc20_addr_hex = ERC20_ADDRESS_ETH.trim_start_matches("0x"); let erc20_addr_pid = Principal::from_slice(&hex::decode(erc20_addr_hex).unwrap()); - let get_balance = STATE.with(|s| s.get_balance(caller, token_id)); + let get_balance = STATE.with(|s| s.get_balance(caller, eth_contract_as_principal)); if let Some(balance) = get_balance { let payload = [ eth_contract_as_principal.to_nat(), @@ -73,18 +73,19 @@ pub async fn withdraw( Ok(outgoing_message) => { let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, token_id, zero); + s.update_balance(caller, eth_contract_as_principal, zero); s.remove_user_flag(caller, token_id); }); insert_claimable_asset(ClaimableMessage { owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), - msg_key: outgoing_message.msg_key.clone(), + msg_key: Some(outgoing_message.msg_key.clone()), token_name: token_name, token: token_id.clone(), amount: balance.clone(), - }) + }); + return Ok(balance); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); @@ -97,6 +98,6 @@ pub async fn withdraw( Err(TxError::Other(format!( "No balance for caller {:?} in canister {:?}!", caller.to_string(), - token_id.to_string(), + eth_contract_as_principal.to_string(), ))) } From 230b66328cc4790895c192d3c0f02c03bd39f0c0 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 23 Sep 2022 16:42:55 +0200 Subject: [PATCH 15/34] return Ok(balance) on eth_proxy.withdraw --- eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index f9502978..b17dc4b4 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -65,6 +65,7 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { token: weth_ic_addr_pid.clone(), amount: balance.clone(), }); + return Ok(balance); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller)); From b7fa4ce1b93cc6783b43614060551ba3766a7a04 Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 27 Sep 2022 14:11:02 +0200 Subject: [PATCH 16/34] add withdrawable struct to eth_proxy --- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 4 +- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 9 +- .../ic/src/eth_proxy/src/api/get_balance.rs | 15 +- .../ic/src/eth_proxy/src/api/withdraw.rs | 12 +- .../ic/src/eth_proxy/src/common/types.rs | 7 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 331 ++++++++++++++---- 6 files changed, 298 insertions(+), 80 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index 91a0377c..3410b446 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -1,5 +1,5 @@ type Result = variant { Ok : nat; Err : TxError }; -type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; +type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; type Result_2 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; @@ -17,7 +17,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal) -> (opt nat); + get_balance : (principal, principal) -> (opt record { principal; nat }); handle_message : (principal, nat, vec nat) -> (Result); mint : (nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index 99ad003f..33bfd212 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -40,7 +40,7 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { match transfer_from { Ok(_) => { - STATE.with(|s| s.add_balance(caller, weth_ic_addr_pid, amount.clone())); + STATE.with(|s| s.add_balance(caller, eth_addr, weth_ic_addr_pid, amount.clone())); let burn = weth_ic_addr_pid.burn(amount.clone()).await; @@ -60,13 +60,14 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { // like negative balance STATE.with(|s| { let current_balance = s - .get_balance(caller, weth_ic_addr_pid) - .unwrap_or(Nat::from(0)); + .get_balance(caller, weth_ic_addr_pid, eth_addr) + .unwrap_or((Principal::anonymous(), Nat::from(0))); s.update_balance( caller, + eth_addr, weth_ic_addr_pid, - current_balance - amount.clone(), + current_balance.1 - amount.clone(), ); s.remove_user_flag(caller); }); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs index df543b6b..5521b983 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs @@ -1,21 +1,28 @@ +use candid::Principal; use ic_kit::{ candid::{candid_method, Nat}, ic, macros::update, }; -use crate::{common::types::TokendId, proxy::STATE}; +use crate::{ + common::types::{EthereumAddr, TokendId, WithdrawableBalance}, + proxy::STATE, +}; #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(token_id: TokendId) -> Option { +pub async fn get_balance( + token_id: TokendId, + eth_address: EthereumAddr, +) -> Option<(Principal, Nat)> { let caller = ic::caller(); - STATE.with(|s| s.get_balance(caller, token_id)) + STATE.with(|s| s.get_balance(caller, token_id, eth_address)) } #[update(name = "get_all_token_balance")] #[candid_method(update, rename = "get_all_token_balance")] -pub async fn get_all_balances() -> Result, String> { +pub async fn get_all_balances() -> Result { let caller = ic::caller(); STATE.with(|s| s.get_all_balances(caller)) } diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index b17dc4b4..b569ea88 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use ic_kit::{ candid::{candid_method, Nat}, - ic, + ic::{self}, macros::update, Principal, }; @@ -46,15 +46,15 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { )); } - let get_balance = STATE.with(|s| s.get_balance(caller, weth_ic_addr_pid)); + let get_balance = STATE.with(|s| s.get_balance(caller, weth_ic_addr_pid, eth_addr)); if let Some(balance) = get_balance { - let payload = [eth_addr.clone().to_nat(), balance.clone()].to_vec(); + let payload = [eth_addr.clone().to_nat(), balance.1.clone()].to_vec(); match tera_id.send_message(weth_eth_addr_pid, payload).await { Ok(outgoing_message) => { let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, weth_ic_addr_pid, zero); + s.update_balance(caller, balance.0.clone(), weth_ic_addr_pid, zero); s.remove_user_flag(caller); }); @@ -63,9 +63,9 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { msg_hash: outgoing_message.msg_hash.clone(), msg_key: outgoing_message.msg_key.clone(), token: weth_ic_addr_pid.clone(), - amount: balance.clone(), + amount: balance.1.clone(), }); - return Ok(balance); + return Ok(balance.1); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller)); diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index dc021899..55f2f3be 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -18,6 +18,9 @@ pub type MsgHashKey = [u8; 32]; pub type TxReceipt = Result; +#[derive(CandidType, Deserialize)] +pub struct WithdrawableBalance(pub Vec<(String, String, Nat)>); + #[derive(Serialize, CandidType, Deserialize)] pub struct Message; @@ -78,7 +81,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>, + pub balances: RefCell>>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -92,7 +95,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: HashMap>, + pub balances: Option>>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 7b586c2e..3a0c91ee 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -1,12 +1,15 @@ -use std::{collections::HashMap, ops::AddAssign}; +use std::{ + collections::HashMap, + ops::{AddAssign, Index}, +}; use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; use crate::common::types::{ - ClaimableMessage, MessageHash, MessageStatus, NonceBytes, ProxyState, StableProxyState, - TokendId, TxFlag, + ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, + StableProxyState, TokendId, TxFlag, WithdrawableBalance, }; pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; @@ -38,44 +41,88 @@ impl ProxyState { self.incoming_messages.borrow_mut().remove(&msg_hash) } - pub fn get_balance(&self, caller: Principal, token_id: TokendId) -> Option { - self.balances - .borrow() - .get(&caller) - .map(|s| s.get(&token_id)) - .map(|b| match b { - Some(balance) => balance.clone(), - None => Nat::from(0_u32), - }) + pub fn get_balance( + &self, + caller: Principal, + token_id: TokendId, + eth_address: EthereumAddr, + ) -> Option<(Principal, Nat)> { + let binding = self.balances.borrow(); + let user_balances = binding.get(&caller); + match user_balances { + Some(token_balances) => { + let token_balance = token_balances.get(&token_id); + match token_balance { + Some(destinations) => { + let destination = destinations.into_iter().find(|d| d.0 == eth_address); + match destination { + // user has balance for the token and the destination + Some((to, amount)) => return Some((*to, amount.to_owned())), + // user has no balance for specific token and specific destination + None => return None, + } + } + // user has no balance for specific token + None => None, + } + } + // user has no balance with any token + None => return None, + } } - pub fn get_all_balances(&self, caller: Principal) -> Result, String> { + pub fn get_all_balances(&self, caller: Principal) -> Result { let token_balances = self.balances.borrow().get(&caller).cloned(); if let Some(balances) = token_balances { - return Ok(balances - .into_iter() - .map(|(p, n)| (p.to_string(), n)) - .collect::>()); + let mut destination = Vec::default(); + for tokens in balances { + for tx in tokens.1 { + destination.push((tokens.0.to_string(), tx.0.to_string(), tx.1)) + } + } + return Ok(WithdrawableBalance(destination)); } - Err(format!("User {} has no token balances!", &caller)) } - pub fn add_balance(&self, caller: Principal, token_id: TokendId, amount: Nat) { - self.balances - .borrow_mut() - .entry(caller) - .or_default() - .entry(token_id) - .or_default() - .add_assign(amount.clone()) + pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokendId, amount: Nat) { + let mut binding = self.balances.borrow_mut(); + let balance = binding.entry(caller).or_default(); + match balance.get_mut(&token_id) { + Some(balance) => { + match balance.into_iter().find(|e| e.0 == to) { + Some(value) => value.1 += amount, + None => balance.push((to, amount)), + }; + } + None => { + let mut value = Vec::new(); + value.push((to, amount)); + balance.insert(token_id, value); + } + } } - pub fn update_balance(&self, caller: Principal, token_id: TokendId, amount: Nat) { - self.balances - .borrow_mut() - .insert(caller, HashMap::from([(token_id, amount)])); + // Panics if theres no balance for the user/token_id or destination + pub fn update_balance( + &self, + caller: Principal, + to: Principal, + token_id: TokendId, + amount: Nat, + ) { + let mut binding = self.balances.borrow_mut(); + let user_balances = binding.get_mut(&caller).unwrap(); + let user_txs = user_balances.get_mut(&token_id).unwrap(); + // if new amount is zero, we remove the tx. + if amount == 0 { + let index = user_txs.into_iter().position(|tx| tx.0 == to).unwrap(); + user_txs.remove(index); + } else { + let mut balance = user_txs.iter_mut().find(|tx| tx.0 == to).unwrap(); + balance.1 = amount; + } } pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { @@ -119,7 +166,7 @@ impl ProxyState { pub fn take_all(&self) -> StableProxyState { StableProxyState { - balances: self.balances.take(), + balances: Some(self.balances.take()), controllers: self.controllers.take(), incoming_messages: self.incoming_messages.take(), messages_unclaimed: self.messages_unclaimed.take(), @@ -136,7 +183,8 @@ impl ProxyState { } pub fn replace_all(&self, stable_message_state: StableProxyState) { - self.balances.replace(stable_message_state.balances); + self.balances + .replace(stable_message_state.balances.unwrap()); self.controllers.replace(stable_message_state.controllers); self.incoming_messages .replace(stable_message_state.incoming_messages); @@ -322,58 +370,217 @@ mod tests { #[test] fn test_add_balance() { - let amount = Nat::from(100_u32); - let pid = mock_principals::bob(); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(100_u32); + let amount_3 = Nat::from(100_u32); + let caller = Principal::from_str("fle2e-ltcun-tpi5w-25chp-byb56-dfl72-f664t-slvy").unwrap(); + let eth_address_1 = mock_principals::bob(); + let eth_address_2 = mock_principals::john(); let token_id = mock_principals::alice(); - STATE.with(|s| s.add_balance(pid, token_id, amount.clone())); - - let balance_of = STATE.with(|s| s.get_balance(pid, token_id)); - let balance = balance_of.unwrap(); + // add amount_1 for token_1 and eth_address_1 + STATE.with(|s| { + s.add_balance( + caller.clone(), + eth_address_1.clone(), + token_id.clone(), + amount_1.clone(), + ) + }); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + assert_eq!(current_balance_1.unwrap().1, amount_1.clone()); + + // add amount_2 for token_1 and eth_address_2 + STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), token_id.clone(), amount_2)); + let withdraw_address_count = STATE.with(|s| { + s.balances + .borrow() + .get(&caller) + .unwrap() + .get(&token_id) + .unwrap() + .len() + }); + assert_eq!(withdraw_address_count, 2); - assert_eq!(balance, amount.clone()); + // add amount_3 for token_1 and eth_address_1 (100 + 100) + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id.clone(), + amount_3.clone(), + ) + }); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + assert_eq!(current_balance_1.unwrap().1, amount_3 + amount_1); } #[test] fn test_get_all_balances() { - let amount = Nat::from(100_u32); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = mock_principals::bob(); - let token_id_1 = mock_principals::alice(); - let token_id_2 = mock_principals::john(); + let eth_address_1 = mock_principals::alice(); + let eth_address_2 = mock_principals::john(); + let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); + let token_id_2 = Principal::from_str("n7j4y-wiaaa-aaaab-qagkq-cai").unwrap(); + + /* + caller: { + token_id_1 : { + eth_address_1=100 + eth_address_2=0 + } + token_id_2: { + eth_address_1=300+100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_1.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_2.clone(), + token_id_2.clone(), + amount_2.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_2.clone(), + amount_3.clone(), + ) + }); - STATE.with(|s| s.add_balance(caller, token_id_1, amount.clone())); - STATE.with(|s| s.add_balance(caller, token_id_2, amount.clone())); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_2.clone(), + amount_1.clone(), + ) + }); let balances = STATE.with(|s| s.get_all_balances(caller)); - assert_eq!(balances.as_ref().unwrap()[0].0, token_id_1.to_string()); - assert_eq!(balances.as_ref().unwrap()[1].0, token_id_2.to_string()); + let all_balances = balances.unwrap().0; + + let x = ( + token_id_2.clone().to_string(), + eth_address_2.clone().to_string(), + Nat::from(200), + ); + let y = ( + token_id_2.clone().to_string(), + eth_address_1.clone().to_string(), + Nat::from(400), + ); + let z = ( + token_id_1.clone().to_string(), + eth_address_1.clone().to_string(), + Nat::from(100), + ); - assert_eq!(balances.as_ref().unwrap()[0].1, amount.clone()); - assert_eq!(balances.as_ref().unwrap()[1].1, amount.clone()); + assert!(all_balances.clone().into_iter().any(|e| e == x)); + assert!(all_balances.clone().into_iter().any(|e| e == y)); + assert!(all_balances.into_iter().any(|e| e == z)); } #[test] fn test_update_balance() { - let amount = Nat::from(100_u32); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = mock_principals::bob(); - let token_id = mock_principals::alice(); - - STATE.with(|s| s.add_balance(caller, token_id, amount.clone())); - - let balance_of = STATE.with(|s| s.get_balance(caller, token_id)); - let balance = balance_of.unwrap(); - - assert_eq!(balance, amount.clone()); + let eth_address_1 = mock_principals::alice(); + let eth_address_2 = mock_principals::john(); + let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); + + /* + caller: { + token_id_1 : { + eth_address_1=100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_1.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_2.clone(), + token_id_1.clone(), + amount_2.clone(), + ) + }); - let new_balance = Nat::from(134_u32); - STATE.with(|s| s.update_balance(caller, token_id, new_balance.clone())); + /* + ---- AFTER UPDATE --- + caller: { + token_id_1 : { + eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.update_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_3.clone(), + ) + }); - let balance_after_update = STATE.with(|s| s.get_balance(caller, token_id)); + let current_balance = STATE + .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone())) + .unwrap(); + + assert_eq!(current_balance.1, amount_3); + let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); + // there are 2 eth addess + assert!(balances.0.len() == 2); + + /* + ---- AFTER UPDATE --- + caller: { + token_id_1 : { + eth_address_1=300 + eth_address_2=0 <- Because its zero, it should be removed + } + } + */ + STATE.with(|s| { + s.update_balance( + caller, + eth_address_2.clone(), + token_id_1.clone(), + Nat::from(0), + ) + }); - assert_eq!(balance_after_update.unwrap(), new_balance); + let balances_final = STATE.with(|s| s.get_all_balances(caller)).unwrap(); + assert!(balances_final.0.len() == 1); } - #[test] fn test_store_incoming_message() { let nonce = Nat::from(4_u32); From b56a20cbc4b2c194c7bcd1e80786edf92bc9dfcd Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 27 Sep 2022 17:01:06 +0200 Subject: [PATCH 17/34] add withdrawable struct to dip20_proxy --- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 5 - .../ic/src/dip20_proxy/dip20_proxy.did | 4 +- .../ic/src/dip20_proxy/src/api/burn.rs | 11 +- .../ic/src/dip20_proxy/src/api/get_balance.rs | 16 +- .../ic/src/dip20_proxy/src/api/withdraw.rs | 10 +- .../ic/src/dip20_proxy/src/common/types.rs | 7 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 326 ++++++++++++++---- 7 files changed, 295 insertions(+), 84 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 3a0c91ee..085e8d13 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -1,8 +1,3 @@ -use std::{ - collections::HashMap, - ops::{AddAssign, Index}, -}; - use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index a52af56f..2d989f44 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -1,5 +1,5 @@ type Result = variant { Ok : nat; Err : TxError }; -type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; +type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; type Result_2 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; @@ -17,7 +17,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal) -> (opt nat); + get_balance : (principal, principal) -> (opt record { principal; nat }); handle_message : (principal, nat, vec nat) -> (Result); mint : (principal, nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 801defec..c1eb7f3a 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -59,7 +59,9 @@ async fn burn( match transfer_from { Ok(_) => { - STATE.with(|s| s.add_balance(caller, eth_contract_as_principal, amount.clone())); + STATE.with(|s| { + s.add_balance(caller, eth_addr, eth_contract_as_principal, amount.clone()) + }); let burn = token_id.burn(amount.clone()).await; @@ -83,13 +85,14 @@ async fn burn( // there could be an underflow here // like negative balance let current_balance = s - .get_balance(caller, eth_contract_as_principal) - .unwrap_or(Nat::from(0)); + .get_balance(caller, eth_contract_as_principal, eth_addr) + .unwrap_or((Principal::anonymous(), Nat::from(0))); s.update_balance( caller, + eth_addr, eth_contract_as_principal, - current_balance - amount.clone(), + current_balance.1 - amount.clone(), ); s.remove_user_flag(caller, token_id) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs index df543b6b..5d281419 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs @@ -1,21 +1,27 @@ use ic_kit::{ - candid::{candid_method, Nat}, + candid::{candid_method, Nat, Principal}, ic, macros::update, }; -use crate::{common::types::TokendId, proxy::STATE}; +use crate::{ + common::types::{EthereumAddr, TokendId, WithdrawableBalance}, + proxy::STATE, +}; #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(token_id: TokendId) -> Option { +pub async fn get_balance( + token_id: TokendId, + eth_address: EthereumAddr, +) -> Option<(Principal, Nat)> { let caller = ic::caller(); - STATE.with(|s| s.get_balance(caller, token_id)) + STATE.with(|s| s.get_balance(caller, token_id, eth_address)) } #[update(name = "get_all_token_balance")] #[candid_method(update, rename = "get_all_token_balance")] -pub async fn get_all_balances() -> Result, String> { +pub async fn get_all_balances() -> Result { let caller = ic::caller(); STATE.with(|s| s.get_all_balances(caller)) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 107636e9..0c36e46c 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -60,12 +60,12 @@ pub async fn withdraw( let erc20_addr_hex = ERC20_ADDRESS_ETH.trim_start_matches("0x"); let erc20_addr_pid = Principal::from_slice(&hex::decode(erc20_addr_hex).unwrap()); - let get_balance = STATE.with(|s| s.get_balance(caller, eth_contract_as_principal)); + let get_balance = STATE.with(|s| s.get_balance(caller, eth_contract_as_principal, eth_addr)); if let Some(balance) = get_balance { let payload = [ eth_contract_as_principal.to_nat(), eth_addr.clone().to_nat(), - balance.clone(), + balance.1.clone(), ] .to_vec(); @@ -73,7 +73,7 @@ pub async fn withdraw( Ok(outgoing_message) => { let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, eth_contract_as_principal, zero); + s.update_balance(caller, eth_addr, eth_contract_as_principal, zero); s.remove_user_flag(caller, token_id); }); @@ -83,9 +83,9 @@ pub async fn withdraw( msg_key: Some(outgoing_message.msg_key.clone()), token_name: token_name, token: token_id.clone(), - amount: balance.clone(), + amount: balance.1.clone(), }); - return Ok(balance); + return Ok(balance.1); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 5e911b33..c8b890f7 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -18,6 +18,9 @@ pub type TxReceipt = Result; pub type MagicResponse = Result; +#[derive(CandidType, Deserialize)] +pub struct WithdrawableBalance(pub Vec<(String, String, Nat)>); + #[derive(Serialize, CandidType, Deserialize)] pub struct Message; @@ -79,7 +82,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>, + pub balances: RefCell>>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -93,7 +96,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: HashMap>, + pub balances: Option>>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index 553451f4..f17b3305 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -1,12 +1,11 @@ use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; -use std::{collections::HashMap, ops::AddAssign}; use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; use crate::common::types::{ - ClaimableMessage, MessageHash, MessageStatus, NonceBytes, ProxyState, StableProxyState, - TokendId, TxFlag, + ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, + StableProxyState, TokendId, TxFlag, WithdrawableBalance, }; pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; @@ -38,44 +37,88 @@ impl ProxyState { self.incoming_messages.borrow_mut().remove(&msg_hash) } - pub fn get_balance(&self, caller: Principal, token_id: TokendId) -> Option { - self.balances - .borrow() - .get(&caller) - .map(|s| s.get(&token_id)) - .map(|b| match b { - Some(balance) => balance.clone(), - None => Nat::from(0_u32), - }) + pub fn get_balance( + &self, + caller: Principal, + token_id: TokendId, + eth_address: EthereumAddr, + ) -> Option<(Principal, Nat)> { + let binding = self.balances.borrow(); + let user_balances = binding.get(&caller); + match user_balances { + Some(token_balances) => { + let token_balance = token_balances.get(&token_id); + match token_balance { + Some(destinations) => { + let destination = destinations.into_iter().find(|d| d.0 == eth_address); + match destination { + // user has balance for the token and the destination + Some((to, amount)) => return Some((*to, amount.to_owned())), + // user has no balance for specific token and specific destination + None => return None, + } + } + // user has no balance for specific token + None => None, + } + } + // user has no balance with any token + None => return None, + } } - pub fn get_all_balances(&self, caller: Principal) -> Result, String> { + pub fn get_all_balances(&self, caller: Principal) -> Result { let token_balances = self.balances.borrow().get(&caller).cloned(); if let Some(balances) = token_balances { - return Ok(balances - .into_iter() - .map(|(p, n)| (p.to_string(), n)) - .collect::>()); + let mut destination = Vec::default(); + for tokens in balances { + for tx in tokens.1 { + destination.push((tokens.0.to_string(), tx.0.to_string(), tx.1)) + } + } + return Ok(WithdrawableBalance(destination)); } - Err(format!("User {} has no token balances!", &caller)) } - pub fn add_balance(&self, caller: Principal, token_id: TokendId, amount: Nat) { - self.balances - .borrow_mut() - .entry(caller) - .or_default() - .entry(token_id) - .or_default() - .add_assign(amount.clone()) + pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokendId, amount: Nat) { + let mut binding = self.balances.borrow_mut(); + let balance = binding.entry(caller).or_default(); + match balance.get_mut(&token_id) { + Some(balance) => { + match balance.into_iter().find(|e| e.0 == to) { + Some(value) => value.1 += amount, + None => balance.push((to, amount)), + }; + } + None => { + let mut value = Vec::new(); + value.push((to, amount)); + balance.insert(token_id, value); + } + } } - pub fn update_balance(&self, caller: Principal, token_id: TokendId, amount: Nat) { - self.balances - .borrow_mut() - .insert(caller, HashMap::from([(token_id, amount)])); + // Panics if theres no balance for the user/token_id or destination + pub fn update_balance( + &self, + caller: Principal, + to: Principal, + token_id: TokendId, + amount: Nat, + ) { + let mut binding = self.balances.borrow_mut(); + let user_balances = binding.get_mut(&caller).unwrap(); + let user_txs = user_balances.get_mut(&token_id).unwrap(); + // if new amount is zero, we remove the tx. + if amount == 0 { + let index = user_txs.into_iter().position(|tx| tx.0 == to).unwrap(); + user_txs.remove(index); + } else { + let mut balance = user_txs.iter_mut().find(|tx| tx.0 == to).unwrap(); + balance.1 = amount; + } } pub fn set_user_flag( @@ -130,7 +173,7 @@ impl ProxyState { pub fn take_all(&self) -> StableProxyState { StableProxyState { - balances: self.balances.take(), + balances: Some(self.balances.take()), controllers: self.controllers.take(), incoming_messages: self.incoming_messages.take(), messages_unclaimed: self.messages_unclaimed.take(), @@ -147,7 +190,8 @@ impl ProxyState { } pub fn replace_all(&self, stable_message_state: StableProxyState) { - self.balances.replace(stable_message_state.balances); + self.balances + .replace(stable_message_state.balances.unwrap_or_default()); self.controllers.replace(stable_message_state.controllers); self.incoming_messages .replace(stable_message_state.incoming_messages); @@ -340,56 +384,216 @@ mod tests { #[test] fn test_add_balance() { - let amount = Nat::from(100_u32); - let pid = mock_principals::bob(); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(100_u32); + let amount_3 = Nat::from(100_u32); + let caller = Principal::from_str("fle2e-ltcun-tpi5w-25chp-byb56-dfl72-f664t-slvy").unwrap(); + let eth_address_1 = mock_principals::bob(); + let eth_address_2 = mock_principals::john(); let token_id = mock_principals::alice(); - STATE.with(|s| s.add_balance(pid, token_id, amount.clone())); - - let balance_of = STATE.with(|s| s.get_balance(pid, token_id)); - let balance = balance_of.unwrap(); + // add amount_1 for token_1 and eth_address_1 + STATE.with(|s| { + s.add_balance( + caller.clone(), + eth_address_1.clone(), + token_id.clone(), + amount_1.clone(), + ) + }); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + assert_eq!(current_balance_1.unwrap().1, amount_1.clone()); + + // add amount_2 for token_1 and eth_address_2 + STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), token_id.clone(), amount_2)); + let withdraw_address_count = STATE.with(|s| { + s.balances + .borrow() + .get(&caller) + .unwrap() + .get(&token_id) + .unwrap() + .len() + }); + assert_eq!(withdraw_address_count, 2); - assert_eq!(balance, amount.clone()); + // add amount_3 for token_1 and eth_address_1 (100 + 100) + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id.clone(), + amount_3.clone(), + ) + }); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + assert_eq!(current_balance_1.unwrap().1, amount_3 + amount_1); } #[test] fn test_get_all_balances() { - let amount = Nat::from(100_u32); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = mock_principals::bob(); - let token_id_1 = mock_principals::alice(); - let token_id_2 = mock_principals::john(); + let eth_address_1 = mock_principals::alice(); + let eth_address_2 = mock_principals::john(); + let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); + let token_id_2 = Principal::from_str("n7j4y-wiaaa-aaaab-qagkq-cai").unwrap(); + + /* + caller: { + token_id_1 : { + eth_address_1=100 + eth_address_2=0 + } + token_id_2: { + eth_address_1=300+100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_1.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_2.clone(), + token_id_2.clone(), + amount_2.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_2.clone(), + amount_3.clone(), + ) + }); - STATE.with(|s| s.add_balance(caller, token_id_1, amount.clone())); - STATE.with(|s| s.add_balance(caller, token_id_2, amount.clone())); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_2.clone(), + amount_1.clone(), + ) + }); let balances = STATE.with(|s| s.get_all_balances(caller)); - assert_eq!(balances.as_ref().unwrap()[0].0, token_id_1.to_string()); - assert_eq!(balances.as_ref().unwrap()[1].0, token_id_2.to_string()); + let all_balances = balances.unwrap().0; - assert_eq!(balances.as_ref().unwrap()[0].1, amount.clone()); - assert_eq!(balances.as_ref().unwrap()[1].1, amount.clone()); + let x = ( + token_id_2.clone().to_string(), + eth_address_2.clone().to_string(), + Nat::from(200), + ); + let y = ( + token_id_2.clone().to_string(), + eth_address_1.clone().to_string(), + Nat::from(400), + ); + let z = ( + token_id_1.clone().to_string(), + eth_address_1.clone().to_string(), + Nat::from(100), + ); + + assert!(all_balances.clone().into_iter().any(|e| e == x)); + assert!(all_balances.clone().into_iter().any(|e| e == y)); + assert!(all_balances.into_iter().any(|e| e == z)); } #[test] fn test_update_balance() { - let amount = Nat::from(100_u32); + let amount_1 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = mock_principals::bob(); - let token_id = mock_principals::alice(); - - STATE.with(|s| s.add_balance(caller, token_id, amount.clone())); - - let balance_of = STATE.with(|s| s.get_balance(caller, token_id)); - let balance = balance_of.unwrap(); - - assert_eq!(balance, amount.clone()); + let eth_address_1 = mock_principals::alice(); + let eth_address_2 = mock_principals::john(); + let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); + + /* + caller: { + token_id_1 : { + eth_address_1=100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.add_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_1.clone(), + ) + }); + STATE.with(|s| { + s.add_balance( + caller, + eth_address_2.clone(), + token_id_1.clone(), + amount_2.clone(), + ) + }); - let new_balance = Nat::from(134_u32); - STATE.with(|s| s.update_balance(caller, token_id, new_balance.clone())); + /* + ---- AFTER UPDATE --- + caller: { + token_id_1 : { + eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 + eth_address_2=200 + } + } + */ + STATE.with(|s| { + s.update_balance( + caller, + eth_address_1.clone(), + token_id_1.clone(), + amount_3.clone(), + ) + }); - let balance_after_update = STATE.with(|s| s.get_balance(caller, token_id)); + let current_balance = STATE + .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone())) + .unwrap(); + + assert_eq!(current_balance.1, amount_3); + let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); + // there are 2 eth addess + assert!(balances.0.len() == 2); + + /* + ---- AFTER UPDATE --- + caller: { + token_id_1 : { + eth_address_1=300 + eth_address_2=0 <- Because its zero, it should be removed + } + } + */ + STATE.with(|s| { + s.update_balance( + caller, + eth_address_2.clone(), + token_id_1.clone(), + Nat::from(0), + ) + }); - assert_eq!(balance_after_update.unwrap(), new_balance); + let balances_final = STATE.with(|s| s.get_all_balances(caller)).unwrap(); + assert!(balances_final.0.len() == 1); } #[test] From 9f820394c6f031ebf82f9259bbd577b8d5ae4873 Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 27 Sep 2022 17:39:35 +0200 Subject: [PATCH 18/34] remove token_id from withdrawable balance on eth_proxy --- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 4 +- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 5 +- .../ic/src/eth_proxy/src/api/get_balance.rs | 9 +- .../ic/src/eth_proxy/src/api/withdraw.rs | 4 +- .../ic/src/eth_proxy/src/common/types.rs | 6 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 230 ++++-------------- .../ic/src/dip20_proxy/src/api/withdraw.rs | 2 +- .../ic/src/dip20_proxy/src/common/tera.rs | 2 +- 8 files changed, 61 insertions(+), 201 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index 3410b446..d9252803 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -1,5 +1,5 @@ type Result = variant { Ok : nat; Err : TxError }; -type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; +type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; type Result_2 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; @@ -17,7 +17,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal, principal) -> (opt record { principal; nat }); + get_balance : (principal) -> (opt record { principal; nat }); handle_message : (principal, nat, vec nat) -> (Result); mint : (nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index 33bfd212..c041f2ee 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -40,7 +40,7 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { match transfer_from { Ok(_) => { - STATE.with(|s| s.add_balance(caller, eth_addr, weth_ic_addr_pid, amount.clone())); + STATE.with(|s| s.add_balance(caller, eth_addr, amount.clone())); let burn = weth_ic_addr_pid.burn(amount.clone()).await; @@ -60,13 +60,12 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { // like negative balance STATE.with(|s| { let current_balance = s - .get_balance(caller, weth_ic_addr_pid, eth_addr) + .get_balance(caller, eth_addr) .unwrap_or((Principal::anonymous(), Nat::from(0))); s.update_balance( caller, eth_addr, - weth_ic_addr_pid, current_balance.1 - amount.clone(), ); s.remove_user_flag(caller); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs index 5521b983..16c0f2b8 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs @@ -6,18 +6,15 @@ use ic_kit::{ }; use crate::{ - common::types::{EthereumAddr, TokendId, WithdrawableBalance}, + common::types::{EthereumAddr, WithdrawableBalance}, proxy::STATE, }; #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance( - token_id: TokendId, - eth_address: EthereumAddr, -) -> Option<(Principal, Nat)> { +pub async fn get_balance(eth_address: EthereumAddr) -> Option<(Principal, Nat)> { let caller = ic::caller(); - STATE.with(|s| s.get_balance(caller, token_id, eth_address)) + STATE.with(|s| s.get_balance(caller, eth_address)) } #[update(name = "get_all_token_balance")] diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index b569ea88..7047b67f 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -46,7 +46,7 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { )); } - let get_balance = STATE.with(|s| s.get_balance(caller, weth_ic_addr_pid, eth_addr)); + let get_balance = STATE.with(|s| s.get_balance(caller, eth_addr)); if let Some(balance) = get_balance { let payload = [eth_addr.clone().to_nat(), balance.1.clone()].to_vec(); @@ -54,7 +54,7 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { Ok(outgoing_message) => { let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, balance.0.clone(), weth_ic_addr_pid, zero); + s.update_balance(caller, balance.0.clone(), zero); s.remove_user_flag(caller); }); diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index 55f2f3be..1da8693d 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -19,7 +19,7 @@ pub type MsgHashKey = [u8; 32]; pub type TxReceipt = Result; #[derive(CandidType, Deserialize)] -pub struct WithdrawableBalance(pub Vec<(String, String, Nat)>); +pub struct WithdrawableBalance(pub Vec<(String, Nat)>); #[derive(Serialize, CandidType, Deserialize)] pub struct Message; @@ -81,7 +81,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>>, + pub balances: RefCell>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -95,7 +95,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>>, + pub balances: Option>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 085e8d13..8c1a0f2a 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -4,7 +4,7 @@ use ic_kit::ic; use crate::common::types::{ ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, - StableProxyState, TokendId, TxFlag, WithdrawableBalance, + StableProxyState, TxFlag, WithdrawableBalance, }; pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; @@ -39,30 +39,16 @@ impl ProxyState { pub fn get_balance( &self, caller: Principal, - token_id: TokendId, eth_address: EthereumAddr, ) -> Option<(Principal, Nat)> { let binding = self.balances.borrow(); let user_balances = binding.get(&caller); match user_balances { - Some(token_balances) => { - let token_balance = token_balances.get(&token_id); - match token_balance { - Some(destinations) => { - let destination = destinations.into_iter().find(|d| d.0 == eth_address); - match destination { - // user has balance for the token and the destination - Some((to, amount)) => return Some((*to, amount.to_owned())), - // user has no balance for specific token and specific destination - None => return None, - } - } - // user has no balance for specific token - None => None, - } - } - // user has no balance with any token - None => return None, + Some(txs) => match txs.into_iter().find(|tx| tx.0 == eth_address) { + Some((to, amount)) => Some((to.clone(), amount.clone())), + None => None, + }, + None => None, } } @@ -71,51 +57,35 @@ impl ProxyState { if let Some(balances) = token_balances { let mut destination = Vec::default(); - for tokens in balances { - for tx in tokens.1 { - destination.push((tokens.0.to_string(), tx.0.to_string(), tx.1)) - } + for tx in balances { + destination.push((tx.0.to_string(), tx.1)); } return Ok(WithdrawableBalance(destination)); } Err(format!("User {} has no token balances!", &caller)) } - pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokendId, amount: Nat) { + pub fn add_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); let balance = binding.entry(caller).or_default(); - match balance.get_mut(&token_id) { - Some(balance) => { - match balance.into_iter().find(|e| e.0 == to) { - Some(value) => value.1 += amount, - None => balance.push((to, amount)), - }; - } + match balance.into_iter().find(|tx| tx.0 == to) { + Some(value) => value.1 += amount, None => { - let mut value = Vec::new(); - value.push((to, amount)); - balance.insert(token_id, value); + balance.push((to, amount)); } } } // Panics if theres no balance for the user/token_id or destination - pub fn update_balance( - &self, - caller: Principal, - to: Principal, - token_id: TokendId, - amount: Nat, - ) { + pub fn update_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); let user_balances = binding.get_mut(&caller).unwrap(); - let user_txs = user_balances.get_mut(&token_id).unwrap(); // if new amount is zero, we remove the tx. if amount == 0 { - let index = user_txs.into_iter().position(|tx| tx.0 == to).unwrap(); - user_txs.remove(index); + let index = user_balances.into_iter().position(|tx| tx.0 == to).unwrap(); + user_balances.remove(index); } else { - let mut balance = user_txs.iter_mut().find(|tx| tx.0 == to).unwrap(); + let mut balance = user_balances.iter_mut().find(|tx| tx.0 == to).unwrap(); balance.1 = amount; } } @@ -371,43 +341,21 @@ mod tests { let caller = Principal::from_str("fle2e-ltcun-tpi5w-25chp-byb56-dfl72-f664t-slvy").unwrap(); let eth_address_1 = mock_principals::bob(); let eth_address_2 = mock_principals::john(); - let token_id = mock_principals::alice(); - // add amount_1 for token_1 and eth_address_1 - STATE.with(|s| { - s.add_balance( - caller.clone(), - eth_address_1.clone(), - token_id.clone(), - amount_1.clone(), - ) - }); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + // add amount_1 for eth_address_1 + STATE.with(|s| s.add_balance(caller.clone(), eth_address_1.clone(), amount_1.clone())); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); assert_eq!(current_balance_1.unwrap().1, amount_1.clone()); - // add amount_2 for token_1 and eth_address_2 - STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), token_id.clone(), amount_2)); - let withdraw_address_count = STATE.with(|s| { - s.balances - .borrow() - .get(&caller) - .unwrap() - .get(&token_id) - .unwrap() - .len() - }); + // add amount_2 for eth_address_2 + STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), amount_2)); + let withdraw_address_count = + STATE.with(|s| s.balances.borrow().get(&caller).unwrap().len()); assert_eq!(withdraw_address_count, 2); - // add amount_3 for token_1 and eth_address_1 (100 + 100) - STATE.with(|s| { - s.add_balance( - caller, - eth_address_1.clone(), - token_id.clone(), - amount_3.clone(), - ) - }); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + // add amount_3 for eth_address_1 (100 + 100) + STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_3.clone())); + let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); assert_eq!(current_balance_1.unwrap().1, amount_3 + amount_1); } @@ -419,78 +367,27 @@ mod tests { let caller = mock_principals::bob(); let eth_address_1 = mock_principals::alice(); let eth_address_2 = mock_principals::john(); - let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); - let token_id_2 = Principal::from_str("n7j4y-wiaaa-aaaab-qagkq-cai").unwrap(); /* caller: { - token_id_1 : { - eth_address_1=100 - eth_address_2=0 - } - token_id_2: { - eth_address_1=300+100 - eth_address_2=200 - } + eth_address_1= 100 + 300 + 100 + eth_address_2= 200 } */ - STATE.with(|s| { - s.add_balance( - caller, - eth_address_1.clone(), - token_id_1.clone(), - amount_1.clone(), - ) - }); - STATE.with(|s| { - s.add_balance( - caller, - eth_address_2.clone(), - token_id_2.clone(), - amount_2.clone(), - ) - }); - STATE.with(|s| { - s.add_balance( - caller, - eth_address_1.clone(), - token_id_2.clone(), - amount_3.clone(), - ) - }); - - STATE.with(|s| { - s.add_balance( - caller, - eth_address_1.clone(), - token_id_2.clone(), - amount_1.clone(), - ) - }); + STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_1.clone())); + STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), amount_2.clone())); + STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_3.clone())); + STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_1.clone())); let balances = STATE.with(|s| s.get_all_balances(caller)); let all_balances = balances.unwrap().0; - let x = ( - token_id_2.clone().to_string(), - eth_address_2.clone().to_string(), - Nat::from(200), - ); - let y = ( - token_id_2.clone().to_string(), - eth_address_1.clone().to_string(), - Nat::from(400), - ); - let z = ( - token_id_1.clone().to_string(), - eth_address_1.clone().to_string(), - Nat::from(100), - ); + let y = (eth_address_1.clone().to_string(), Nat::from(500)); + let x = (eth_address_2.clone().to_string(), Nat::from(200)); assert!(all_balances.clone().into_iter().any(|e| e == x)); assert!(all_balances.clone().into_iter().any(|e| e == y)); - assert!(all_balances.into_iter().any(|e| e == z)); } #[test] @@ -501,53 +398,28 @@ mod tests { let caller = mock_principals::bob(); let eth_address_1 = mock_principals::alice(); let eth_address_2 = mock_principals::john(); - let token_id_1 = Principal::from_str("tgodh-faaaa-aaaab-qaefa-cai").unwrap(); /* caller: { - token_id_1 : { - eth_address_1=100 - eth_address_2=200 - } + eth_address_1=100 + eth_address_2=200 + } */ - STATE.with(|s| { - s.add_balance( - caller, - eth_address_1.clone(), - token_id_1.clone(), - amount_1.clone(), - ) - }); - STATE.with(|s| { - s.add_balance( - caller, - eth_address_2.clone(), - token_id_1.clone(), - amount_2.clone(), - ) - }); + STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_1.clone())); + STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), amount_2.clone())); /* ---- AFTER UPDATE --- caller: { - token_id_1 : { - eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 - eth_address_2=200 - } + eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 + eth_address_2=200 } */ - STATE.with(|s| { - s.update_balance( - caller, - eth_address_1.clone(), - token_id_1.clone(), - amount_3.clone(), - ) - }); + STATE.with(|s| s.update_balance(caller, eth_address_1.clone(), amount_3.clone())); let current_balance = STATE - .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone())) + .with(|s| s.get_balance(caller, eth_address_1.clone())) .unwrap(); assert_eq!(current_balance.1, amount_3); @@ -558,24 +430,16 @@ mod tests { /* ---- AFTER UPDATE --- caller: { - token_id_1 : { - eth_address_1=300 - eth_address_2=0 <- Because its zero, it should be removed - } + eth_address_1=300 + eth_address_2=0 <- Because its zero, it should be removed } */ - STATE.with(|s| { - s.update_balance( - caller, - eth_address_2.clone(), - token_id_1.clone(), - Nat::from(0), - ) - }); + STATE.with(|s| s.update_balance(caller, eth_address_2.clone(), Nat::from(0))); let balances_final = STATE.with(|s| s.get_all_balances(caller)).unwrap(); assert!(balances_final.0.len() == 1); } + #[test] fn test_store_incoming_message() { let nonce = Nat::from(4_u32); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 0c36e46c..5803bbfa 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -1,7 +1,7 @@ use ic_cdk::export::candid::Principal; use ic_kit::{ candid::{candid_method, Nat}, - ic::{self, balance}, + ic::{self}, macros::update, }; diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/tera.rs b/magic_bridge/ic/src/dip20_proxy/src/common/tera.rs index bde9ccb1..be226e04 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/tera.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/tera.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use ic_cdk::call; use ic_cdk::export::candid::{Nat, Principal}; -use crate::common::types::{Nonce, OutgoingMessage, TxError}; +use crate::common::types::{OutgoingMessage, TxError}; use super::types::NonceBytes; From eca1bfa6332d778a69011ec74ec17d2875a81886 Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 28 Sep 2022 16:06:29 +0200 Subject: [PATCH 19/34] Fix typo TokendId -> TokenId --- magic_bridge/ic/src/dip20_proxy/src/api/burn.rs | 4 ++-- .../ic/src/dip20_proxy/src/api/get_balance.rs | 7 ++----- magic_bridge/ic/src/dip20_proxy/src/api/mint.rs | 4 ++-- .../ic/src/dip20_proxy/src/api/withdraw.rs | 4 ++-- .../ic/src/dip20_proxy/src/common/types.rs | 8 ++++---- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 14 ++++---------- 6 files changed, 16 insertions(+), 25 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index c1eb7f3a..2550711d 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -6,7 +6,7 @@ use crate::common::dip20::Dip20; use crate::common::magic::Magic; use crate::common::tera::Tera; use crate::common::types::{ - ClaimableMessage, EthereumAddr, OutgoingMessage, TokendId, TxError, TxFlag, TxReceipt, + ClaimableMessage, EthereumAddr, OutgoingMessage, TokenId, TxError, TxFlag, TxReceipt, }; use crate::proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}; use ic_cdk::export::candid::{Nat, Principal}; @@ -14,7 +14,7 @@ use ic_cdk::export::candid::{Nat, Principal}; #[update(name = "burn")] #[candid_method(update, rename = "burn")] async fn burn( - eth_contract_as_principal: TokendId, + eth_contract_as_principal: TokenId, eth_addr: EthereumAddr, amount: Nat, ) -> TxReceipt { diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs index 5d281419..be265c34 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs @@ -5,16 +5,13 @@ use ic_kit::{ }; use crate::{ - common::types::{EthereumAddr, TokendId, WithdrawableBalance}, + common::types::{EthereumAddr, TokenId, WithdrawableBalance}, proxy::STATE, }; #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance( - token_id: TokendId, - eth_address: EthereumAddr, -) -> Option<(Principal, Nat)> { +pub async fn get_balance(token_id: TokenId, eth_address: EthereumAddr) -> Option<(Principal, Nat)> { let caller = ic::caller(); STATE.with(|s| s.get_balance(caller, token_id, eth_address)) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/mint.rs b/magic_bridge/ic/src/dip20_proxy/src/api/mint.rs index a81d1616..83b89328 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/mint.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/mint.rs @@ -8,12 +8,12 @@ use crate::proxy::{FromNat, ToBytes, ToNat, ERC20_ADDRESS_ETH, STATE, TERA_ADDRE use ic_cdk::export::candid::{Nat, Principal}; use crate::common::types::{ - IncomingMessageHashParams, Message, MessageStatus, Nonce, TokendId, TxError, TxReceipt, + IncomingMessageHashParams, Message, MessageStatus, Nonce, TokenId, TxError, TxReceipt, }; #[update(name = "mint")] #[candid_method(update, rename = "mint")] -pub async fn mint(token_id: TokendId, nonce: Nonce, payload: Vec) -> TxReceipt { +pub async fn mint(token_id: TokenId, nonce: Nonce, payload: Vec) -> TxReceipt { if (token_id.name().await).is_err() { return Err(TxError::Other(format!( "Token {} canister is not responding!", diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 5803bbfa..f5425102 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -11,7 +11,7 @@ use crate::{ dip20::Dip20, magic::Magic, tera::Tera, - types::{ClaimableMessage, EthereumAddr, TokendId, TxError, TxFlag, TxReceipt}, + types::{ClaimableMessage, EthereumAddr, TokenId, TxError, TxFlag, TxReceipt}, }, proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}, }; @@ -22,7 +22,7 @@ use crate::{ #[update(name = "withdraw")] #[candid_method(update, rename = "withdraw")] pub async fn withdraw( - eth_contract_as_principal: TokendId, + eth_contract_as_principal: TokenId, eth_addr: EthereumAddr, _amount: Nat, ) -> TxReceipt { diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index c8b890f7..879a706b 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -8,7 +8,7 @@ pub type Nonce = Nat; pub type NonceBytes = [u8; 32]; -pub type TokendId = Principal; +pub type TokenId = Principal; pub type MessageHash = String; @@ -72,7 +72,7 @@ pub struct ClaimableMessage { pub msg_hash: String, pub msg_key: Option<[u8; 32]>, pub token_name: String, - pub token: TokendId, + pub token: TokenId, pub amount: Nat, pub from: Principal, } @@ -82,7 +82,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>>, + pub balances: RefCell>>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -96,7 +96,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>>, + pub balances: Option>>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index f17b3305..a6035c9b 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -5,7 +5,7 @@ use ic_kit::ic; use crate::common::types::{ ClaimableMessage, EthereumAddr, MessageHash, MessageStatus, NonceBytes, ProxyState, - StableProxyState, TokendId, TxFlag, WithdrawableBalance, + StableProxyState, TokenId, TxFlag, WithdrawableBalance, }; pub const CAP_ADDRESS: &str = "lj532-6iaaa-aaaah-qcc7a-cai"; @@ -40,7 +40,7 @@ impl ProxyState { pub fn get_balance( &self, caller: Principal, - token_id: TokendId, + token_id: TokenId, eth_address: EthereumAddr, ) -> Option<(Principal, Nat)> { let binding = self.balances.borrow(); @@ -82,7 +82,7 @@ impl ProxyState { Err(format!("User {} has no token balances!", &caller)) } - pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokendId, amount: Nat) { + pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); let balance = binding.entry(caller).or_default(); match balance.get_mut(&token_id) { @@ -101,13 +101,7 @@ impl ProxyState { } // Panics if theres no balance for the user/token_id or destination - pub fn update_balance( - &self, - caller: Principal, - to: Principal, - token_id: TokendId, - amount: Nat, - ) { + pub fn update_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); let user_balances = binding.get_mut(&caller).unwrap(); let user_txs = user_balances.get_mut(&token_id).unwrap(); From 5af4f7edc2bbde943997ea72b389ca1e80ae95b3 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 29 Sep 2022 15:59:53 +0200 Subject: [PATCH 20/34] dip20_proxy: replace Vec for hashmap to store usertx --- .../ic/src/dip20_proxy/dip20_proxy.did | 2 +- .../ic/src/dip20_proxy/src/api/burn.rs | 4 +- .../ic/src/dip20_proxy/src/api/get_balance.rs | 2 +- .../ic/src/dip20_proxy/src/api/withdraw.rs | 6 +- .../ic/src/dip20_proxy/src/common/types.rs | 4 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 77 ++++++++----------- 6 files changed, 41 insertions(+), 54 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index 2d989f44..ee70fef2 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -17,7 +17,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal, principal) -> (opt record { principal; nat }); + get_balance : (principal, principal) -> (opt nat); handle_message : (principal, nat, vec nat) -> (Result); mint : (principal, nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 2550711d..63cf8abc 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -86,13 +86,13 @@ async fn burn( // like negative balance let current_balance = s .get_balance(caller, eth_contract_as_principal, eth_addr) - .unwrap_or((Principal::anonymous(), Nat::from(0))); + .unwrap_or(Nat::from(0)); s.update_balance( caller, eth_addr, eth_contract_as_principal, - current_balance.1 - amount.clone(), + current_balance - amount.clone(), ); s.remove_user_flag(caller, token_id) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs index be265c34..c44f011b 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs @@ -11,7 +11,7 @@ use crate::{ #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(token_id: TokenId, eth_address: EthereumAddr) -> Option<(Principal, Nat)> { +pub async fn get_balance(token_id: TokenId, eth_address: EthereumAddr) -> Option { let caller = ic::caller(); STATE.with(|s| s.get_balance(caller, token_id, eth_address)) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index f5425102..8d5f1b05 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -65,7 +65,7 @@ pub async fn withdraw( let payload = [ eth_contract_as_principal.to_nat(), eth_addr.clone().to_nat(), - balance.1.clone(), + balance.clone(), ] .to_vec(); @@ -83,9 +83,9 @@ pub async fn withdraw( msg_key: Some(outgoing_message.msg_key.clone()), token_name: token_name, token: token_id.clone(), - amount: balance.1.clone(), + amount: balance.clone(), }); - return Ok(balance.1); + return Ok(balance); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 879a706b..31e164ce 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -82,7 +82,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>>, + pub balances: RefCell>>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -96,7 +96,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>>, + pub balances: Option>>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index a6035c9b..f6d5b43a 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use ic_cdk::export::candid::{Nat, Principal}; @@ -42,29 +44,15 @@ impl ProxyState { caller: Principal, token_id: TokenId, eth_address: EthereumAddr, - ) -> Option<(Principal, Nat)> { - let binding = self.balances.borrow(); - let user_balances = binding.get(&caller); - match user_balances { - Some(token_balances) => { - let token_balance = token_balances.get(&token_id); - match token_balance { - Some(destinations) => { - let destination = destinations.into_iter().find(|d| d.0 == eth_address); - match destination { - // user has balance for the token and the destination - Some((to, amount)) => return Some((*to, amount.to_owned())), - // user has no balance for specific token and specific destination - None => return None, - } - } - // user has no balance for specific token - None => None, - } - } - // user has no balance with any token - None => return None, - } + ) -> Option { + self.balances + .borrow() + .get(&caller) + .unwrap_or(&HashMap::new()) + .get(&token_id) + .unwrap_or(&HashMap::new()) + .get(ð_address) + .cloned() } pub fn get_all_balances(&self, caller: Principal) -> Result { @@ -84,18 +72,17 @@ impl ProxyState { pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let balance = binding.entry(caller).or_default(); - match balance.get_mut(&token_id) { - Some(balance) => { - match balance.into_iter().find(|e| e.0 == to) { - Some(value) => value.1 += amount, - None => balance.push((to, amount)), - }; - } + let caller_txs: HashMap> = HashMap::new(); + let token_txs: HashMap = HashMap::new(); + let user_tx = binding + .entry(caller) + .or_insert(caller_txs) + .entry(token_id) + .or_insert(token_txs); + match user_tx.get_mut(&to) { + Some(balance) => *balance += amount, None => { - let mut value = Vec::new(); - value.push((to, amount)); - balance.insert(token_id, value); + user_tx.insert(to, amount); } } } @@ -103,16 +90,16 @@ impl ProxyState { // Panics if theres no balance for the user/token_id or destination pub fn update_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let user_balances = binding.get_mut(&caller).unwrap(); - let user_txs = user_balances.get_mut(&token_id).unwrap(); - // if new amount is zero, we remove the tx. + let tx = binding + .get_mut(&caller) + .unwrap() + .get_mut(&token_id) + .unwrap(); if amount == 0 { - let index = user_txs.into_iter().position(|tx| tx.0 == to).unwrap(); - user_txs.remove(index); - } else { - let mut balance = user_txs.iter_mut().find(|tx| tx.0 == to).unwrap(); - balance.1 = amount; + tx.remove_entry(&to); + return; } + tx.get_mut(&to).and_then(|v| Some(*v = amount)); } pub fn set_user_flag( @@ -396,7 +383,7 @@ mod tests { ) }); let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); - assert_eq!(current_balance_1.unwrap().1, amount_1.clone()); + assert_eq!(current_balance_1.unwrap(), amount_1.clone()); // add amount_2 for token_1 and eth_address_2 STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), token_id.clone(), amount_2)); @@ -421,7 +408,7 @@ mod tests { ) }); let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); - assert_eq!(current_balance_1.unwrap().1, amount_3 + amount_1); + assert_eq!(current_balance_1.unwrap(), amount_3 + amount_1); } #[test] @@ -563,7 +550,7 @@ mod tests { .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone())) .unwrap(); - assert_eq!(current_balance.1, amount_3); + assert_eq!(current_balance, amount_3); let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); // there are 2 eth addess assert!(balances.0.len() == 2); From a810f524c017f621a2f4c2670a4952c8c9b8c90c Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 29 Sep 2022 16:31:35 +0200 Subject: [PATCH 21/34] eth_proxy: replace Vec for hashmap to store usertx --- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 2 +- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 7 ++- .../ic/src/eth_proxy/src/api/get_balance.rs | 3 +- .../ic/src/eth_proxy/src/api/withdraw.rs | 8 +-- .../ic/src/eth_proxy/src/common/types.rs | 4 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 51 ++++++++----------- 6 files changed, 33 insertions(+), 42 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index d9252803..91a0377c 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -17,7 +17,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal) -> (opt record { principal; nat }); + get_balance : (principal) -> (opt nat); handle_message : (principal, nat, vec nat) -> (Result); mint : (nat, vec nat) -> (Result); perform_handshake : () -> (Result_2); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index c041f2ee..cb26fce4 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -59,14 +59,13 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { // there could be an underflow here // like negative balance STATE.with(|s| { - let current_balance = s - .get_balance(caller, eth_addr) - .unwrap_or((Principal::anonymous(), Nat::from(0))); + let current_balance = + s.get_balance(caller, eth_addr).unwrap_or(Nat::from(0)); s.update_balance( caller, eth_addr, - current_balance.1 - amount.clone(), + current_balance - amount.clone(), ); s.remove_user_flag(caller); }); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs index 16c0f2b8..9480e3b5 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs @@ -1,4 +1,3 @@ -use candid::Principal; use ic_kit::{ candid::{candid_method, Nat}, ic, @@ -12,7 +11,7 @@ use crate::{ #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(eth_address: EthereumAddr) -> Option<(Principal, Nat)> { +pub async fn get_balance(eth_address: EthereumAddr) -> Option { let caller = ic::caller(); STATE.with(|s| s.get_balance(caller, eth_address)) } diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index 7047b67f..1da5bf9c 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -48,13 +48,13 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { let get_balance = STATE.with(|s| s.get_balance(caller, eth_addr)); if let Some(balance) = get_balance { - let payload = [eth_addr.clone().to_nat(), balance.1.clone()].to_vec(); + let payload = [eth_addr.clone().to_nat(), balance.clone()].to_vec(); match tera_id.send_message(weth_eth_addr_pid, payload).await { Ok(outgoing_message) => { let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, balance.0.clone(), zero); + s.update_balance(caller, eth_addr, zero); s.remove_user_flag(caller); }); @@ -63,9 +63,9 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { msg_hash: outgoing_message.msg_hash.clone(), msg_key: outgoing_message.msg_key.clone(), token: weth_ic_addr_pid.clone(), - amount: balance.1.clone(), + amount: balance.clone(), }); - return Ok(balance.1); + return Ok(balance); } Err(_) => { STATE.with(|s| s.remove_user_flag(caller)); diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index 1da8693d..aeffbcfb 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -81,7 +81,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>, + pub balances: RefCell>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -95,7 +95,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>, + pub balances: Option>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index 8c1a0f2a..bc0fe5d7 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use cap_sdk::{DetailsBuilder, IndefiniteEvent, IndefiniteEventBuilder}; use ic_cdk::export::candid::{Nat, Principal}; use ic_kit::ic; @@ -36,20 +38,13 @@ impl ProxyState { self.incoming_messages.borrow_mut().remove(&msg_hash) } - pub fn get_balance( - &self, - caller: Principal, - eth_address: EthereumAddr, - ) -> Option<(Principal, Nat)> { - let binding = self.balances.borrow(); - let user_balances = binding.get(&caller); - match user_balances { - Some(txs) => match txs.into_iter().find(|tx| tx.0 == eth_address) { - Some((to, amount)) => Some((to.clone(), amount.clone())), - None => None, - }, - None => None, - } + pub fn get_balance(&self, caller: Principal, eth_address: EthereumAddr) -> Option { + self.balances + .borrow() + .get(&caller) + .unwrap_or(&HashMap::new()) + .get(ð_address) + .cloned() } pub fn get_all_balances(&self, caller: Principal) -> Result { @@ -67,27 +62,25 @@ impl ProxyState { pub fn add_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let balance = binding.entry(caller).or_default(); - match balance.into_iter().find(|tx| tx.0 == to) { - Some(value) => value.1 += amount, + let caller_txs: HashMap = HashMap::new(); + let user_tx = binding.entry(caller).or_insert(caller_txs); + match user_tx.get_mut(&to) { + Some(balance) => *balance += amount, None => { - balance.push((to, amount)); + user_tx.insert(to, amount); } } } - // Panics if theres no balance for the user/token_id or destination + // Panics if theres no balance for the caller or destination pub fn update_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let user_balances = binding.get_mut(&caller).unwrap(); - // if new amount is zero, we remove the tx. + let tx = binding.get_mut(&caller).unwrap(); if amount == 0 { - let index = user_balances.into_iter().position(|tx| tx.0 == to).unwrap(); - user_balances.remove(index); - } else { - let mut balance = user_balances.iter_mut().find(|tx| tx.0 == to).unwrap(); - balance.1 = amount; + tx.remove_entry(&to); + return; } + tx.get_mut(&to).and_then(|v| Some(*v = amount)); } pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { @@ -345,7 +338,7 @@ mod tests { // add amount_1 for eth_address_1 STATE.with(|s| s.add_balance(caller.clone(), eth_address_1.clone(), amount_1.clone())); let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); - assert_eq!(current_balance_1.unwrap().1, amount_1.clone()); + assert_eq!(current_balance_1.unwrap(), amount_1.clone()); // add amount_2 for eth_address_2 STATE.with(|s| s.add_balance(caller, eth_address_2.clone(), amount_2)); @@ -356,7 +349,7 @@ mod tests { // add amount_3 for eth_address_1 (100 + 100) STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_3.clone())); let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); - assert_eq!(current_balance_1.unwrap().1, amount_3 + amount_1); + assert_eq!(current_balance_1.unwrap(), amount_3 + amount_1); } #[test] @@ -422,7 +415,7 @@ mod tests { .with(|s| s.get_balance(caller, eth_address_1.clone())) .unwrap(); - assert_eq!(current_balance.1, amount_3); + assert_eq!(current_balance, amount_3); let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); // there are 2 eth addess assert!(balances.0.len() == 2); From 188972689852a38d7adc47a89673a0c327564469 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 6 Oct 2022 16:17:44 +0200 Subject: [PATCH 22/34] dip20_proxy: add operation failure on burn and withdraw --- .../ic/src/dip20_proxy/dip20_proxy.did | 20 +++++++++--- .../ic/src/dip20_proxy/src/api/burn.rs | 31 ++++++++++--------- .../ic/src/dip20_proxy/src/api/get_balance.rs | 2 +- .../ic/src/dip20_proxy/src/api/withdraw.rs | 31 ++++++++++--------- .../ic/src/dip20_proxy/src/common/types.rs | 13 +++++++- 5 files changed, 61 insertions(+), 36 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index ee70fef2..148fee0e 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -1,6 +1,16 @@ -type Result = variant { Ok : nat; Err : TxError }; +type OperationFailure = variant { + SendMessage : opt TxError; + Burn : opt TxError; + TokenCanisterIdNotFound : opt TxError; + MultipleTxWithToken : opt TxError; + UserHasNotBalanceToWithdraw : opt TxError; + DIP20NotResponding : opt TxError; + TransferFrom : opt TxError; +}; +type Result = variant { Ok : nat; Err : OperationFailure }; type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; -type Result_2 = variant { Ok; Err : text }; +type Result_2 = variant { Ok : nat; Err : TxError }; +type Result_3 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; InsufficientBalance; @@ -18,8 +28,8 @@ service : { burn : (principal, principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); get_balance : (principal, principal) -> (opt nat); - handle_message : (principal, nat, vec nat) -> (Result); - mint : (principal, nat, vec nat) -> (Result); - perform_handshake : () -> (Result_2); + handle_message : (principal, nat, vec nat) -> (Result_2); + mint : (principal, nat, vec nat) -> (Result_2); + perform_handshake : () -> (Result_3); withdraw : (principal, principal, nat) -> (Result); } \ No newline at end of file diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 63cf8abc..05264d7e 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -6,7 +6,7 @@ use crate::common::dip20::Dip20; use crate::common::magic::Magic; use crate::common::tera::Tera; use crate::common::types::{ - ClaimableMessage, EthereumAddr, OutgoingMessage, TokenId, TxError, TxFlag, TxReceipt, + ClaimableMessage, EthereumAddr, OperationFailure, OutgoingMessage, TokenId, TxError, TxFlag, }; use crate::proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}; use ic_cdk::export::candid::{Nat, Principal}; @@ -17,7 +17,7 @@ async fn burn( eth_contract_as_principal: TokenId, eth_addr: EthereumAddr, amount: Nat, -) -> TxReceipt { +) -> Result { let caller = ic::caller(); let self_id = ic::id(); @@ -28,15 +28,14 @@ async fn burn( .await { Ok(canister_id) => canister_id, - Err(error) => return Err(error), + Err(error) => return Err(OperationFailure::TokenCanisterIdNotFound(Some(error))), }; let token_name = token_id.name().await; if token_name.is_err() { - return Err(TxError::Other(format!( - "Token {} canister is not responding!", - token_id.to_string(), - ))); + return Err(OperationFailure::DIP20NotResponding(Some(TxError::Other( + format!("Token {} canister is not responding!", token_id.to_string(),), + )))); } let token_name_str = token_name.unwrap(); @@ -46,11 +45,11 @@ async fn burn( // One user cannot make multiple tx at the same time for the same token let set_flag = STATE.with(|s| s.set_user_flag(caller, token_id, TxFlag::Burning)); if set_flag.is_err() { - return Err(TxError::Other( + return Err(OperationFailure::MultipleTxWithToken(Some(TxError::Other( set_flag .err() .unwrap_or("Multiple token transactions".to_string()), - )); + )))); } let transfer_from = token_id @@ -114,24 +113,26 @@ async fn burn( // send_message error Err(_) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); - return Err(TxError::Other(format!( - "Sending message to L1 failed with caller {:?}!", - caller.to_string() - ))); + return Err(OperationFailure::SendMessage(Some(TxError::Other( + format!( + "Sending message to L1 failed with caller {:?}!", + caller.to_string() + ), + )))); } } } // burn error Err(error) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); - return Err(error); + return Err(OperationFailure::Burn(Some(error))); } }; } // transfer error Err(error) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); - Err(error) + Err(OperationFailure::TransferFrom(Some(error))) } } } diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs index c44f011b..06814d76 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs @@ -1,5 +1,5 @@ use ic_kit::{ - candid::{candid_method, Nat, Principal}, + candid::{candid_method, Nat}, ic, macros::update, }; diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 8d5f1b05..91218298 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -11,7 +11,7 @@ use crate::{ dip20::Dip20, magic::Magic, tera::Tera, - types::{ClaimableMessage, EthereumAddr, TokenId, TxError, TxFlag, TxReceipt}, + types::{ClaimableMessage, EthereumAddr, OperationFailure, TokenId, TxError, TxFlag}, }, proxy::{ToNat, ERC20_ADDRESS_ETH, MAGIC_ADDRESS_IC, STATE, TERA_ADDRESS}, }; @@ -25,7 +25,7 @@ pub async fn withdraw( eth_contract_as_principal: TokenId, eth_addr: EthereumAddr, _amount: Nat, -) -> TxReceipt { +) -> Result { let caller = ic::caller(); let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); let magic_bridge = Principal::from_text(MAGIC_ADDRESS_IC).unwrap(); @@ -35,26 +35,25 @@ pub async fn withdraw( .await { Ok(canister_id) => canister_id, - Err(error) => return Err(error), + Err(error) => return Err(OperationFailure::TokenCanisterIdNotFound(Some(error))), }; let token_name = match token_id.name().await { Ok(name) => name, Err(_) => { - return Err(TxError::Other(format!( - "Token {} canister is not responding!", - token_id.to_string() - ))) + return Err(OperationFailure::DIP20NotResponding(Some(TxError::Other( + format!("Token {} canister is not responding!", token_id.to_string()), + )))); } }; let set_flag = STATE.with(|s| s.set_user_flag(caller, token_id, TxFlag::Withdrawing)); if set_flag.is_err() { - return Err(TxError::Other( + return Err(OperationFailure::MultipleTxWithToken(Some(TxError::Other( set_flag .err() .unwrap_or("Multiple token transactions".to_string()), - )); + )))); }; let erc20_addr_hex = ERC20_ADDRESS_ETH.trim_start_matches("0x"); @@ -89,15 +88,19 @@ pub async fn withdraw( } Err(_) => { STATE.with(|s| s.remove_user_flag(caller, token_id)); - return Err(TxError::Other(format!("Sending message to L1 failed!"))); + return Err(OperationFailure::SendMessage(Some(TxError::Other( + format!("Sending message to L1 failed!"), + )))); } } } STATE.with(|s| s.remove_user_flag(caller, token_id)); - Err(TxError::Other(format!( - "No balance for caller {:?} in canister {:?}!", - caller.to_string(), - eth_contract_as_principal.to_string(), + Err(OperationFailure::UserHasNotBalanceToWithdraw(Some( + TxError::Other(format!( + "No balance for caller {:?} in canister {:?}!", + caller.to_string(), + eth_contract_as_principal.to_string(), + )), ))) } diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 31e164ce..63a2c492 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -111,7 +111,7 @@ pub enum TokenType { DIP721, } -#[derive(Deserialize, CandidType, Debug, PartialEq)] +#[derive(CandidType, Debug, Deserialize, PartialEq)] pub enum TxError { InsufficientBalance, InsufficientAllowance, @@ -123,3 +123,14 @@ pub enum TxError { ErrorTo, Other(String), } + +#[derive(CandidType, Deserialize, PartialEq)] +pub enum OperationFailure { + Burn(Option), + DIP20NotResponding(Option), + UserHasNotBalanceToWithdraw(Option), + MultipleTxWithToken(Option), + SendMessage(Option), + TokenCanisterIdNotFound(Option), + TransferFrom(Option), +} From c933397f78fb31fcf5f21aaa24f15f83bfc1bfa8 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 6 Oct 2022 16:40:03 +0200 Subject: [PATCH 23/34] eth_proxy: add operation failure on burn and withdraw --- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 20 +++++++++--- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 32 +++++++++++-------- .../ic/src/eth_proxy/src/api/withdraw.rs | 32 +++++++++++-------- .../ic/src/eth_proxy/src/common/types.rs | 11 +++++++ 4 files changed, 63 insertions(+), 32 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index 91a0377c..46003cad 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -1,6 +1,16 @@ -type Result = variant { Ok : nat; Err : TxError }; +type OperationFailure = variant { + SendMessage : opt TxError; + Burn : opt TxError; + TokenCanisterIdNotFound : opt TxError; + MultipleTxWithToken : opt TxError; + UserHasNotBalanceToWithdraw : opt TxError; + DIP20NotResponding : opt TxError; + TransferFrom : opt TxError; +}; +type Result = variant { Ok : nat; Err : OperationFailure }; type Result_1 = variant { Ok : vec record { text; nat }; Err : text }; -type Result_2 = variant { Ok; Err : text }; +type Result_2 = variant { Ok : nat; Err : TxError }; +type Result_3 = variant { Ok; Err : text }; type TxError = variant { InsufficientAllowance; InsufficientBalance; @@ -18,8 +28,8 @@ service : { burn : (principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); get_balance : (principal) -> (opt nat); - handle_message : (principal, nat, vec nat) -> (Result); - mint : (nat, vec nat) -> (Result); - perform_handshake : () -> (Result_2); + handle_message : (principal, nat, vec nat) -> (Result_2); + mint : (nat, vec nat) -> (Result_2); + perform_handshake : () -> (Result_3); withdraw : (principal, nat) -> (Result); } \ No newline at end of file diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index cb26fce4..972bbb8b 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -9,29 +9,31 @@ use crate::common::weth::Weth; use crate::proxy::{ToNat, STATE, TERA_ADDRESS, WETH_ADDRESS_ETH, WETH_ADDRESS_IC}; use ic_cdk::export::candid::{Nat, Principal}; -use crate::common::types::{ClaimableMessage, EthereumAddr, TxError, TxFlag, TxReceipt}; +use crate::common::types::{ClaimableMessage, EthereumAddr, OperationFailure, TxError, TxFlag}; #[update(name = "burn")] #[candid_method(update, rename = "burn")] -async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { +async fn burn(eth_addr: EthereumAddr, amount: Nat) -> Result { let caller = ic::caller(); let self_id = ic::id(); let weth_ic_addr_pid = Principal::from_str(WETH_ADDRESS_IC).unwrap(); if (weth_ic_addr_pid.name().await).is_err() { - return Err(TxError::Other(format!( - "Token {} canister is not responding!", - weth_ic_addr_pid.to_string(), - ))); + return Err(OperationFailure::DIP20NotResponding(Some(TxError::Other( + format!( + "Token {} canister is not responding!", + weth_ic_addr_pid.to_string() + ), + )))); } let set_flag = STATE.with(|s| s.set_user_flag(caller, TxFlag::Burning)); if set_flag.is_err() { - return Err(TxError::Other( + return Err(OperationFailure::MultipleTxWithToken(Some(TxError::Other( set_flag .err() .unwrap_or("Multiple token transactions".to_string()), - )); + )))); } let transfer_from = weth_ic_addr_pid @@ -85,24 +87,26 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> TxReceipt { // send_message to Tera error Err(_) => { STATE.with(|s| s.remove_user_flag(caller)); - return Err(TxError::Other(format!( - "Sending message to L1 failed with caller {:?}!", - caller.to_string() - ))); + return Err(OperationFailure::SendMessage(Some(TxError::Other( + format!( + "Sending message to L1 failed with caller {:?}!", + caller.to_string() + ), + )))); } } } // burn error Err(error) => { STATE.with(|s| s.remove_user_flag(caller)); - return Err(error); + return Err(OperationFailure::Burn(Some(error))); } }; } // transfer_from error Err(error) => { STATE.with(|s| s.remove_user_flag(caller)); - Err(error) + Err(OperationFailure::TransferFrom(Some(error))) } } } diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index 1da5bf9c..cab0a442 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -11,7 +11,7 @@ use crate::{ common::{ cap::insert_claimable_asset, tera::Tera, - types::{ClaimableMessage, EthereumAddr, TxError, TxFlag, TxReceipt}, + types::{ClaimableMessage, EthereumAddr, OperationFailure, TxError, TxFlag}, weth::Weth, }, proxy::{ToNat, STATE, TERA_ADDRESS, WETH_ADDRESS_ETH, WETH_ADDRESS_IC}, @@ -22,16 +22,18 @@ use crate::{ /// todo withdraw specific balance #[update(name = "withdraw")] #[candid_method(update, rename = "withdraw")] -pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { +pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> Result { let caller = ic::caller(); let weth_ic_addr_pid = Principal::from_str(WETH_ADDRESS_IC).unwrap(); let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); if (weth_ic_addr_pid.name().await).is_err() { - return Err(TxError::Other(format!( - "Token {} canister is not responding!", - weth_ic_addr_pid.to_string(), - ))); + return Err(OperationFailure::DIP20NotResponding(Some(TxError::Other( + format!( + "Token {} canister is not responding!", + weth_ic_addr_pid.to_string(), + ), + )))); } let eth_addr_hex = WETH_ADDRESS_ETH.trim_start_matches("0x"); @@ -39,11 +41,11 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { let set_flag = STATE.with(|s| s.set_user_flag(caller, TxFlag::Withdrawing)); if set_flag.is_err() { - return Err(TxError::Other( + return Err(OperationFailure::MultipleTxWithToken(Some(TxError::Other( set_flag .err() .unwrap_or("Multiple token transactions".to_string()), - )); + )))); } let get_balance = STATE.with(|s| s.get_balance(caller, eth_addr)); @@ -69,15 +71,19 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> TxReceipt { } Err(_) => { STATE.with(|s| s.remove_user_flag(caller)); - return Err(TxError::Other(format!("Sending message to L1 failed!"))); + return Err(OperationFailure::SendMessage(Some(TxError::Other( + format!("Sending message to L1 failed!"), + )))); } } } STATE.with(|s| s.remove_user_flag(caller)); - Err(TxError::Other(format!( - "No balance for caller {:?} in canister {:?}!", - caller.to_string(), - weth_ic_addr_pid.to_string(), + Err(OperationFailure::UserHasNotBalanceToWithdraw(Some( + TxError::Other(format!( + "No balance for caller {:?} in canister {:?}!", + caller.to_string(), + weth_ic_addr_pid.to_string(), + )), ))) } diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index aeffbcfb..82454240 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -122,3 +122,14 @@ pub enum TxError { ErrorTo, Other(String), } + +#[derive(CandidType, Deserialize, PartialEq)] +pub enum OperationFailure { + Burn(Option), + DIP20NotResponding(Option), + UserHasNotBalanceToWithdraw(Option), + MultipleTxWithToken(Option), + SendMessage(Option), + TokenCanisterIdNotFound(Option), + TransferFrom(Option), +} From ec846e36f18df7955f092c9cbbb3a5e66ac1feaa Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 12 Oct 2022 14:22:38 +0200 Subject: [PATCH 24/34] dip20_proxy: refactor pending balance logic --- .../ic/src/dip20_proxy/dip20_proxy.did | 2 +- .../ic/src/dip20_proxy/src/api/burn.rs | 10 +- .../ic/src/dip20_proxy/src/api/get_balance.rs | 4 +- .../ic/src/dip20_proxy/src/api/withdraw.rs | 8 +- .../ic/src/dip20_proxy/src/common/types.rs | 4 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 131 ++++++++++-------- 6 files changed, 88 insertions(+), 71 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index 148fee0e..fb367fff 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -27,7 +27,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal, principal) -> (opt nat); + get_balance : (principal, principal, nat) -> (opt nat); handle_message : (principal, nat, vec nat) -> (Result_2); mint : (principal, nat, vec nat) -> (Result_2); perform_handshake : () -> (Result_3); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 05264d7e..7aeb1774 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -81,17 +81,11 @@ async fn burn( match send_message { Ok(outgoing_message) => { STATE.with(|s| { - // there could be an underflow here - // like negative balance - let current_balance = s - .get_balance(caller, eth_contract_as_principal, eth_addr) - .unwrap_or(Nat::from(0)); - - s.update_balance( + s.remove_balance( caller, eth_addr, eth_contract_as_principal, - current_balance - amount.clone(), + amount.clone(), ); s.remove_user_flag(caller, token_id) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs index 06814d76..d898f253 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/get_balance.rs @@ -11,9 +11,9 @@ use crate::{ #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(token_id: TokenId, eth_address: EthereumAddr) -> Option { +pub async fn get_balance(token_id: TokenId, eth_address: EthereumAddr, amount: Nat) -> Option { let caller = ic::caller(); - STATE.with(|s| s.get_balance(caller, token_id, eth_address)) + STATE.with(|s| s.get_balance(caller, token_id, eth_address, amount)) } #[update(name = "get_all_token_balance")] diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 91218298..13115981 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -24,7 +24,7 @@ use crate::{ pub async fn withdraw( eth_contract_as_principal: TokenId, eth_addr: EthereumAddr, - _amount: Nat, + amount: Nat, ) -> Result { let caller = ic::caller(); let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); @@ -59,7 +59,8 @@ pub async fn withdraw( let erc20_addr_hex = ERC20_ADDRESS_ETH.trim_start_matches("0x"); let erc20_addr_pid = Principal::from_slice(&hex::decode(erc20_addr_hex).unwrap()); - let get_balance = STATE.with(|s| s.get_balance(caller, eth_contract_as_principal, eth_addr)); + let get_balance = + STATE.with(|s| s.get_balance(caller, eth_contract_as_principal, eth_addr, amount.clone())); if let Some(balance) = get_balance { let payload = [ eth_contract_as_principal.to_nat(), @@ -70,9 +71,8 @@ pub async fn withdraw( match tera_id.send_message(erc20_addr_pid, payload).await { Ok(outgoing_message) => { - let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, eth_addr, eth_contract_as_principal, zero); + s.remove_balance(caller, eth_addr, eth_contract_as_principal, amount); s.remove_user_flag(caller, token_id); }); diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 63a2c492..fa7007a2 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -82,7 +82,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>>, + pub balances: RefCell>>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -96,7 +96,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>>, + pub balances: Option>>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index f6d5b43a..6fead3d3 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -44,62 +44,68 @@ impl ProxyState { caller: Principal, token_id: TokenId, eth_address: EthereumAddr, + amount: Nat, ) -> Option { - self.balances + if let Some(balance) = self + .balances .borrow() .get(&caller) .unwrap_or(&HashMap::new()) .get(&token_id) - .unwrap_or(&HashMap::new()) - .get(ð_address) - .cloned() + .unwrap_or(&Vec::new()) + .into_iter() + .find(|m| m.0 == eth_address && m.1 == amount) + { + return Some(balance.1.clone()); + } else { + return None; + } } pub fn get_all_balances(&self, caller: Principal) -> Result { - let token_balances = self.balances.borrow().get(&caller).cloned(); + let token_balances: Option>> = + self.balances.borrow().get(&caller).cloned(); if let Some(balances) = token_balances { - let mut destination = Vec::default(); - for tokens in balances { - for tx in tokens.1 { - destination.push((tokens.0.to_string(), tx.0.to_string(), tx.1)) - } + let mut transactions: Vec<(String, String, Nat)> = Vec::new(); + for txs in balances { + let token_tx: Vec<(String, String, Nat)> = txs + .1 + .into_iter() + .map(|tx| (txs.0.to_string(), tx.0.to_string(), tx.1)) + .collect(); + transactions.extend(token_tx); } - return Ok(WithdrawableBalance(destination)); + return Ok(WithdrawableBalance(transactions)); } Err(format!("User {} has no token balances!", &caller)) } pub fn add_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let caller_txs: HashMap> = HashMap::new(); - let token_txs: HashMap = HashMap::new(); - let user_tx = binding + let caller_txs: HashMap> = HashMap::new(); + let token_txs: Vec<(Principal, Nat)> = Vec::new(); + binding .entry(caller) .or_insert(caller_txs) .entry(token_id) - .or_insert(token_txs); - match user_tx.get_mut(&to) { - Some(balance) => *balance += amount, - None => { - user_tx.insert(to, amount); - } - } + .or_insert(token_txs) + .push((to, amount)); } - // Panics if theres no balance for the user/token_id or destination - pub fn update_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { + /// Panics if theres no balance for the user/token_id or destination + pub fn remove_balance(&self, caller: Principal, to: Principal, token_id: TokenId, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let tx = binding + let txs = binding .get_mut(&caller) .unwrap() .get_mut(&token_id) .unwrap(); - if amount == 0 { - tx.remove_entry(&to); - return; - } - tx.get_mut(&to).and_then(|v| Some(*v = amount)); + let index = txs + .into_iter() + .position(|tx| tx.0 == to && tx.1 == amount) + .unwrap(); + txs.remove(index); } pub fn set_user_flag( @@ -366,8 +372,8 @@ mod tests { #[test] fn test_add_balance() { let amount_1 = Nat::from(100_u32); - let amount_2 = Nat::from(100_u32); - let amount_3 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = Principal::from_str("fle2e-ltcun-tpi5w-25chp-byb56-dfl72-f664t-slvy").unwrap(); let eth_address_1 = mock_principals::bob(); let eth_address_2 = mock_principals::john(); @@ -382,7 +388,8 @@ mod tests { amount_1.clone(), ) }); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); + let current_balance_1 = + STATE.with(|s| s.get_balance(caller, token_id, eth_address_1, amount_1.clone())); assert_eq!(current_balance_1.unwrap(), amount_1.clone()); // add amount_2 for token_1 and eth_address_2 @@ -398,7 +405,7 @@ mod tests { }); assert_eq!(withdraw_address_count, 2); - // add amount_3 for token_1 and eth_address_1 (100 + 100) + // add amount_3 for token_1 and eth_address_1 (100 , 100) STATE.with(|s| { s.add_balance( caller, @@ -407,8 +414,9 @@ mod tests { amount_3.clone(), ) }); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, token_id, eth_address_1)); - assert_eq!(current_balance_1.unwrap(), amount_3 + amount_1); + let current_balance_1 = + STATE.with(|s| s.get_balance(caller, token_id, eth_address_1, amount_3.clone())); + assert_eq!(current_balance_1.unwrap(), amount_3); } #[test] @@ -429,7 +437,8 @@ mod tests { eth_address_2=0 } token_id_2: { - eth_address_1=300+100 + eth_address_1=300 + eth_address_1=100 eth_address_2=200 } } @@ -472,6 +481,12 @@ mod tests { let all_balances = balances.unwrap().0; + let w = ( + token_id_2.clone().to_string(), + eth_address_1.clone().to_string(), + Nat::from(100), + ); + let x = ( token_id_2.clone().to_string(), eth_address_2.clone().to_string(), @@ -480,7 +495,7 @@ mod tests { let y = ( token_id_2.clone().to_string(), eth_address_1.clone().to_string(), - Nat::from(400), + Nat::from(300), ); let z = ( token_id_1.clone().to_string(), @@ -488,6 +503,7 @@ mod tests { Nat::from(100), ); + assert!(all_balances.clone().into_iter().any(|e| e == w)); assert!(all_balances.clone().into_iter().any(|e| e == x)); assert!(all_balances.clone().into_iter().any(|e| e == y)); assert!(all_balances.into_iter().any(|e| e == z)); @@ -497,7 +513,6 @@ mod tests { fn test_update_balance() { let amount_1 = Nat::from(100_u32); let amount_2 = Nat::from(200_u32); - let amount_3 = Nat::from(300_u32); let caller = mock_principals::bob(); let eth_address_1 = mock_principals::alice(); let eth_address_2 = mock_principals::john(); @@ -528,53 +543,61 @@ mod tests { ) }); + let all_balances = STATE.with(|s| s.get_all_balances(caller.clone())); + assert_eq!(all_balances.unwrap().0.len(), 2); + /* ---- AFTER UPDATE --- caller: { token_id_1 : { - eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 eth_address_2=200 } } */ STATE.with(|s| { - s.update_balance( + s.remove_balance( caller, eth_address_1.clone(), token_id_1.clone(), - amount_3.clone(), + amount_1.clone(), ) }); let current_balance = STATE - .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone())) + .with(|s| { + s.get_balance( + caller, + token_id_1.clone(), + eth_address_2.clone(), + amount_2.clone(), + ) + }) .unwrap(); - assert_eq!(current_balance, amount_3); + assert_eq!(current_balance, amount_2); + + let removed_balance = STATE + .with(|s| s.get_balance(caller, token_id_1.clone(), eth_address_1.clone(), amount_1)); + + assert!(removed_balance.is_none()); + let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); - // there are 2 eth addess - assert!(balances.0.len() == 2); + // there is 1 eth addess + assert!(balances.0.len() == 1); /* ---- AFTER UPDATE --- caller: { token_id_1 : { - eth_address_1=300 - eth_address_2=0 <- Because its zero, it should be removed } } */ STATE.with(|s| { - s.update_balance( - caller, - eth_address_2.clone(), - token_id_1.clone(), - Nat::from(0), - ) + s.remove_balance(caller, eth_address_2.clone(), token_id_1.clone(), amount_2) }); let balances_final = STATE.with(|s| s.get_all_balances(caller)).unwrap(); - assert!(balances_final.0.len() == 1); + assert!(balances_final.0.len() == 0); } #[test] From c312ecfebf744bb2183deca80febb3d680ef6fdf Mon Sep 17 00:00:00 2001 From: Federico Date: Wed, 12 Oct 2022 21:20:54 +0200 Subject: [PATCH 25/34] eth_proxy: refactor pending balance logic --- eth_bridge/ic/src/eth_proxy/eth_proxy.did | 2 +- eth_bridge/ic/src/eth_proxy/src/api/burn.rs | 11 +- .../ic/src/eth_proxy/src/api/get_balance.rs | 4 +- .../ic/src/eth_proxy/src/api/withdraw.rs | 7 +- .../ic/src/eth_proxy/src/common/types.rs | 4 +- eth_bridge/ic/src/eth_proxy/src/proxy.rs | 102 +++++++++++------- 6 files changed, 73 insertions(+), 57 deletions(-) diff --git a/eth_bridge/ic/src/eth_proxy/eth_proxy.did b/eth_bridge/ic/src/eth_proxy/eth_proxy.did index 46003cad..baf9ae66 100644 --- a/eth_bridge/ic/src/eth_proxy/eth_proxy.did +++ b/eth_bridge/ic/src/eth_proxy/eth_proxy.did @@ -27,7 +27,7 @@ service : { authorized : () -> (vec principal) query; burn : (principal, nat) -> (Result); get_all_token_balance : () -> (Result_1); - get_balance : (principal) -> (opt nat); + get_balance : (principal, nat) -> (opt nat); handle_message : (principal, nat, vec nat) -> (Result_2); mint : (nat, vec nat) -> (Result_2); perform_handshake : () -> (Result_3); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs index 972bbb8b..ac8619e0 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/burn.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/burn.rs @@ -58,17 +58,8 @@ async fn burn(eth_addr: EthereumAddr, amount: Nat) -> Result { - // there could be an underflow here - // like negative balance STATE.with(|s| { - let current_balance = - s.get_balance(caller, eth_addr).unwrap_or(Nat::from(0)); - - s.update_balance( - caller, - eth_addr, - current_balance - amount.clone(), - ); + s.remove_balance(caller, eth_addr, amount.clone()); s.remove_user_flag(caller); }); diff --git a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs index 9480e3b5..5c577d3e 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/get_balance.rs @@ -11,9 +11,9 @@ use crate::{ #[update(name = "get_balance")] #[candid_method(update, rename = "get_balance")] -pub async fn get_balance(eth_address: EthereumAddr) -> Option { +pub async fn get_balance(eth_address: EthereumAddr, amount: Nat) -> Option { let caller = ic::caller(); - STATE.with(|s| s.get_balance(caller, eth_address)) + STATE.with(|s| s.get_balance(caller, eth_address, amount)) } #[update(name = "get_all_token_balance")] diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index cab0a442..14c92f14 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -22,7 +22,7 @@ use crate::{ /// todo withdraw specific balance #[update(name = "withdraw")] #[candid_method(update, rename = "withdraw")] -pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> Result { +pub async fn withdraw(eth_addr: EthereumAddr, amount: Nat) -> Result { let caller = ic::caller(); let weth_ic_addr_pid = Principal::from_str(WETH_ADDRESS_IC).unwrap(); let tera_id = Principal::from_text(TERA_ADDRESS).unwrap(); @@ -48,15 +48,14 @@ pub async fn withdraw(eth_addr: EthereumAddr, _amount: Nat) -> Result { - let zero = Nat::from(0_u32); STATE.with(|s| { - s.update_balance(caller, eth_addr, zero); + s.remove_balance(caller, eth_addr, amount); s.remove_user_flag(caller); }); diff --git a/eth_bridge/ic/src/eth_proxy/src/common/types.rs b/eth_bridge/ic/src/eth_proxy/src/common/types.rs index 82454240..6282f6c2 100644 --- a/eth_bridge/ic/src/eth_proxy/src/common/types.rs +++ b/eth_bridge/ic/src/eth_proxy/src/common/types.rs @@ -81,7 +81,7 @@ pub struct ProxyState { /// store incoming messages against status locks pub incoming_messages: RefCell>, /// user balances - pub balances: RefCell>>, + pub balances: RefCell>>, /// authorized principals pub controllers: RefCell>, // store outgoing massages waiting to be claimed @@ -95,7 +95,7 @@ pub struct StableProxyState { /// store incoming messages against status locks pub incoming_messages: HashMap, /// user balances - pub balances: Option>>, + pub balances: Option>>, /// authorized principals pub controllers: Vec, // store outgoing massages waiting to be claimed diff --git a/eth_bridge/ic/src/eth_proxy/src/proxy.rs b/eth_bridge/ic/src/eth_proxy/src/proxy.rs index bc0fe5d7..6efb5ce4 100644 --- a/eth_bridge/ic/src/eth_proxy/src/proxy.rs +++ b/eth_bridge/ic/src/eth_proxy/src/proxy.rs @@ -38,13 +38,25 @@ impl ProxyState { self.incoming_messages.borrow_mut().remove(&msg_hash) } - pub fn get_balance(&self, caller: Principal, eth_address: EthereumAddr) -> Option { - self.balances + pub fn get_balance( + &self, + caller: Principal, + eth_address: EthereumAddr, + amount: Nat, + ) -> Option { + if let Some(balance) = self + .balances .borrow() .get(&caller) - .unwrap_or(&HashMap::new()) - .get(ð_address) + .unwrap_or(&Vec::new()) + .into_iter() + .find(|m| m.0 == eth_address && m.1 == amount) .cloned() + { + return Some(balance.1); + } else { + return None; + } } pub fn get_all_balances(&self, caller: Principal) -> Result { @@ -62,25 +74,17 @@ impl ProxyState { pub fn add_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let caller_txs: HashMap = HashMap::new(); + let caller_txs: Vec<(Principal, Nat)> = Vec::new(); let user_tx = binding.entry(caller).or_insert(caller_txs); - match user_tx.get_mut(&to) { - Some(balance) => *balance += amount, - None => { - user_tx.insert(to, amount); - } - } + user_tx.push((to, amount)) } - // Panics if theres no balance for the caller or destination - pub fn update_balance(&self, caller: Principal, to: Principal, amount: Nat) { + /// Panics if theres no balance for the caller or destination + pub fn remove_balance(&self, caller: Principal, to: Principal, amount: Nat) { let mut binding = self.balances.borrow_mut(); - let tx = binding.get_mut(&caller).unwrap(); - if amount == 0 { - tx.remove_entry(&to); - return; - } - tx.get_mut(&to).and_then(|v| Some(*v = amount)); + let txs = binding.get_mut(&caller).unwrap(); + let index = txs.into_iter().position(|tx| tx.0 == to && tx.1 == amount); + txs.remove(index.unwrap()); } pub fn set_user_flag(&self, user: Principal, flag: TxFlag) -> Result<(), String> { @@ -329,15 +333,16 @@ mod tests { #[test] fn test_add_balance() { let amount_1 = Nat::from(100_u32); - let amount_2 = Nat::from(100_u32); - let amount_3 = Nat::from(100_u32); + let amount_2 = Nat::from(200_u32); + let amount_3 = Nat::from(300_u32); let caller = Principal::from_str("fle2e-ltcun-tpi5w-25chp-byb56-dfl72-f664t-slvy").unwrap(); let eth_address_1 = mock_principals::bob(); let eth_address_2 = mock_principals::john(); // add amount_1 for eth_address_1 STATE.with(|s| s.add_balance(caller.clone(), eth_address_1.clone(), amount_1.clone())); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); + let current_balance_1 = + STATE.with(|s| s.get_balance(caller, eth_address_1.clone(), amount_1.clone())); assert_eq!(current_balance_1.unwrap(), amount_1.clone()); // add amount_2 for eth_address_2 @@ -346,10 +351,15 @@ mod tests { STATE.with(|s| s.balances.borrow().get(&caller).unwrap().len()); assert_eq!(withdraw_address_count, 2); - // add amount_3 for eth_address_1 (100 + 100) + // add amount_3 for eth_address_1 (100, 300) STATE.with(|s| s.add_balance(caller, eth_address_1.clone(), amount_3.clone())); - let current_balance_1 = STATE.with(|s| s.get_balance(caller, eth_address_1)); - assert_eq!(current_balance_1.unwrap(), amount_3 + amount_1); + let current_balance_1 = + STATE.with(|s| s.get_balance(caller, eth_address_1.clone(), amount_3.clone())); + assert_eq!(current_balance_1.unwrap(), amount_3); + + let withdraw_address_count = + STATE.with(|s| s.balances.borrow().get(&caller).unwrap().len()); + assert_eq!(withdraw_address_count, 3); } #[test] @@ -363,7 +373,7 @@ mod tests { /* caller: { - eth_address_1= 100 + 300 + 100 + eth_address_1= 100 ; 300 ; 100 eth_address_2= 200 } */ @@ -376,15 +386,25 @@ mod tests { let all_balances = balances.unwrap().0; - let y = (eth_address_1.clone().to_string(), Nat::from(500)); + let w = (eth_address_1.clone().to_string(), Nat::from(300)); + let y = (eth_address_1.clone().to_string(), Nat::from(100)); let x = (eth_address_2.clone().to_string(), Nat::from(200)); + assert!( + all_balances + .clone() + .into_iter() + .filter(|m| m.0 == y.0 && m.1 == y.1) + .count() + == 2 + ); + assert!(all_balances.clone().into_iter().any(|e| e == w)); assert!(all_balances.clone().into_iter().any(|e| e == x)); assert!(all_balances.clone().into_iter().any(|e| e == y)); } #[test] - fn test_update_balance() { + fn test_remove_balance() { let amount_1 = Nat::from(100_u32); let amount_2 = Nat::from(200_u32); let amount_3 = Nat::from(300_u32); @@ -405,32 +425,38 @@ mod tests { /* ---- AFTER UPDATE --- caller: { - eth_address_1=300 <- THIS IS UPDATED 300 INTEAD OF 100 + eth_address_1=100 <- THIS SHOULD BE REMOVED eth_address_2=200 } */ - STATE.with(|s| s.update_balance(caller, eth_address_1.clone(), amount_3.clone())); + STATE.with(|s| s.remove_balance(caller, eth_address_1.clone(), amount_1.clone())); + + assert!(STATE.with(|s| s.balances.borrow().clone().into_iter().count() == 1)); + + assert!(STATE.with(|s| s + .get_balance(caller, eth_address_1.clone(), amount_1.clone()) + .is_none())); let current_balance = STATE - .with(|s| s.get_balance(caller, eth_address_1.clone())) + .with(|s| s.get_balance(caller, eth_address_2.clone(), amount_2.clone())) .unwrap(); - assert_eq!(current_balance, amount_3); + assert_eq!(current_balance, amount_2.clone()); + let balances = STATE.with(|s| s.get_all_balances(caller)).unwrap(); - // there are 2 eth addess - assert!(balances.0.len() == 2); + // there is 1 eth addess + assert!(balances.0.len() == 1); /* ---- AFTER UPDATE --- caller: { - eth_address_1=300 - eth_address_2=0 <- Because its zero, it should be removed + eth_address_2=0 <- it should be removed } */ - STATE.with(|s| s.update_balance(caller, eth_address_2.clone(), Nat::from(0))); + STATE.with(|s| s.remove_balance(caller, eth_address_2.clone(), amount_2.clone())); let balances_final = STATE.with(|s| s.get_all_balances(caller)).unwrap(); - assert!(balances_final.0.len() == 1); + assert!(balances_final.0.len() == 0); } #[test] From 44d66f906929b4b3f4da6c4edf660413643a0466 Mon Sep 17 00:00:00 2001 From: Federico Date: Tue, 18 Oct 2022 13:09:43 +0200 Subject: [PATCH 26/34] add caller to claimable message on withdraw --- eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs | 1 + magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs index 14c92f14..aac5259b 100644 --- a/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs +++ b/eth_bridge/ic/src/eth_proxy/src/api/withdraw.rs @@ -60,6 +60,7 @@ pub async fn withdraw(eth_addr: EthereumAddr, amount: Nat) -> Result Date: Tue, 18 Oct 2022 05:57:30 -0300 Subject: [PATCH 27/34] chore: out going message store (#38) * add max limit to remove_messages * add outgoing messages max limit * increase MAX_REMOVE_MESSAGES upto 10_000 * add payload size validation to inspect_message * remove MAX_REMOVE_MSG limit --- core/ic/Cargo.lock | 25 +++++++++++- core/ic/src/tera/Cargo.toml | 1 + core/ic/src/tera/src/api/inspect_message.rs | 17 +++++++- core/ic/src/tera/src/api/messages.rs | 18 +++++++-- core/ic/src/tera/src/common/types.rs | 2 +- core/ic/src/tera/src/tera.rs | 43 ++++++++++++++++++++- core/ic/src/tera/tera.did | 1 + 7 files changed, 98 insertions(+), 9 deletions(-) diff --git a/core/ic/Cargo.lock b/core/ic/Cargo.lock index 44e0e03c..038825eb 100644 --- a/core/ic/Cargo.lock +++ b/core/ic/Cargo.lock @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "beef" @@ -737,6 +737,16 @@ dependencies = [ "syn", ] +[[package]] +name = "ic-kit-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14e1035cec37712bb8250ec275dab651d177d981c25262de2a0012dc499cbf1" +dependencies = [ + "futures", + "tokio", +] + [[package]] name = "ic-types" version = "0.4.1" @@ -1288,6 +1298,7 @@ dependencies = [ "ic-cdk", "ic-cdk-macros", "ic-kit", + "ic-kit-sys", "num-bigint", "serde", "serde_bytes", @@ -1344,6 +1355,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "pin-project-lite", +] + [[package]] name = "toml" version = "0.5.8" diff --git a/core/ic/src/tera/Cargo.toml b/core/ic/src/tera/Cargo.toml index 859951c8..e25c1233 100644 --- a/core/ic/src/tera/Cargo.toml +++ b/core/ic/src/tera/Cargo.toml @@ -14,6 +14,7 @@ ic-cdk = "0.5.5" sha2 = "0.10.1" ic-cdk-macros = "0.5.5" ic-kit = "0.4.8" +ic-kit-sys = "0.1.3" sha3 = "0.9.1" hex = "0.4.3" serde = "1.0.130" diff --git a/core/ic/src/tera/src/api/inspect_message.rs b/core/ic/src/tera/src/api/inspect_message.rs index 502ed583..49145df2 100644 --- a/core/ic/src/tera/src/api/inspect_message.rs +++ b/core/ic/src/tera/src/api/inspect_message.rs @@ -1,11 +1,26 @@ use ic_cdk::api; use ic_cdk_macros::inspect_message; +use ic_kit_sys::ic0; use super::admin::is_authorized; +const MAX_ARG_LIMIT: usize = 1_900_000; // 1.9MB #[inspect_message] fn inspect_message() { - if is_authorized().is_ok() { + if is_authorized().is_ok() && payload_size().is_ok() { api::call::accept_message() } } + +fn payload_size() -> Result<(), String> { + let args_size = arg_data_size(); + if args_size >= MAX_ARG_LIMIT { + return Err("Payload too big".to_string()); + } + Ok(()) +} + +/// Return the size of the raw argument to this entry point. +fn arg_data_size() -> usize { + unsafe { ic0::msg_arg_data_size() as usize } +} diff --git a/core/ic/src/tera/src/api/messages.rs b/core/ic/src/tera/src/api/messages.rs index 047d5c9a..0cec2ddf 100644 --- a/core/ic/src/tera/src/api/messages.rs +++ b/core/ic/src/tera/src/api/messages.rs @@ -1,5 +1,5 @@ use candid::candid_method; -use ic_cdk_macros::update; +use ic_cdk_macros::{query, update}; use super::admin::is_authorized; use crate::{ @@ -19,6 +19,13 @@ fn get_messages() -> Vec { STATE.with(|s| s.get_messages()) } +#[query(name = "get_messages_count", guard = "is_authorized")] +#[candid_method(query, rename = "get_messages_count")] +fn get_messages_count() -> u32 { + let count = STATE.with(|s| s.outgoing_messages_count()) as u32; + count +} + #[cfg(test)] mod tests { use ic_kit::{mock_principals, MockContext}; @@ -49,6 +56,9 @@ mod tests { assert_eq!(stored_messages.len(), 1); assert_eq!(stored_messages.first().unwrap().msg_hash, msg_hash()); + + let out_messages_count = get_messages_count(); + assert_eq!(out_messages_count, 1); } #[test] @@ -60,13 +70,13 @@ mod tests { let msg_key = hex::encode(store_message.unwrap().msg_key); let messages_to_remove = vec![OutgoingMessagePair { - msg_key, + msg_key: msg_key.clone(), msg_hash: msg_hash(), }]; - let remove_messages = remove_messages(messages_to_remove); + let remove_messages_res = remove_messages(messages_to_remove); - assert!(remove_messages.0.is_ok()); + assert!(remove_messages_res.0.is_ok()); let stored_messages = get_messages(); diff --git a/core/ic/src/tera/src/common/types.rs b/core/ic/src/tera/src/common/types.rs index a88d79ab..00b98717 100644 --- a/core/ic/src/tera/src/common/types.rs +++ b/core/ic/src/tera/src/common/types.rs @@ -47,7 +47,7 @@ pub struct OutgoingMessage { pub(crate) msg_hash: String, } -#[derive(Serialize, CandidType, Deserialize)] +#[derive(Serialize, CandidType, Deserialize, Clone)] pub struct OutgoingMessagePair { pub(crate) msg_key: String, pub(crate) msg_hash: String, diff --git a/core/ic/src/tera/src/tera.rs b/core/ic/src/tera/src/tera.rs index 4b7977c5..2498b222 100644 --- a/core/ic/src/tera/src/tera.rs +++ b/core/ic/src/tera/src/tera.rs @@ -11,7 +11,9 @@ thread_local! { pub static STATE: TerabetiaState = TerabetiaState::default(); } -#[derive(CandidType, Deserialize, Default)] +const MAX_OUTGOING_MESSAGES_COUNT: usize = 10_000; + +#[derive(CandidType, Deserialize)] pub struct TerabetiaState { /// Incoming messages from L1 pub messages: RefCell>, @@ -128,6 +130,18 @@ impl FromNat for Principal { } } +impl Default for TerabetiaState { + fn default() -> Self { + TerabetiaState { + messages: RefCell::new(HashMap::default()), + nonce: RefCell::new(HashSet::default()), + messages_out: RefCell::new(HashSet::with_capacity(MAX_OUTGOING_MESSAGES_COUNT)), + message_out_index: RefCell::new(u64::default()), + authorized: RefCell::new(Vec::default()), + } + } +} + impl TerabetiaState { /// /// Outgoing @@ -144,6 +158,9 @@ impl TerabetiaState { /// Store outgoing messages to L1 pub fn store_outgoing_message(&self, msg_hash: String) -> Result { + if self.outgoing_messages_count() >= MAX_OUTGOING_MESSAGES_COUNT { + return Err(String::from("Max OutgoingMessages amount reached")); + } // we increment outgoing message counter let mut index = self.message_out_index.borrow_mut(); *index += 1; @@ -167,6 +184,10 @@ impl TerabetiaState { Ok(true) } + pub fn outgoing_messages_count(&self) -> usize { + self.messages_out.borrow().len() + } + /// /// Incoming /// @@ -484,6 +505,26 @@ mod tests { assert!(is_authorized.is_ok()); } + #[test] + fn store_message_with_max_limit_reached() { + let capacity = STATE.with(|s| s.messages_out.borrow().capacity()); + assert!(capacity >= MAX_OUTGOING_MESSAGES_COUNT); + + for i in 0..MAX_OUTGOING_MESSAGES_COUNT { + let result = STATE.with(|s| s.store_outgoing_message(i.to_string())); + assert!(result.is_ok()); + } + assert_eq!( + STATE.with(|s| s.outgoing_messages_count()), + MAX_OUTGOING_MESSAGES_COUNT + ); + + // when reach max it returns error + let result = STATE.with(|s| s.store_outgoing_message("0x00".to_string())); + assert!(result.is_err()); + assert_eq!(result.err().unwrap(), "Max OutgoingMessages amount reached"); + } + #[test] fn test_take_all() { // ToDo diff --git a/core/ic/src/tera/tera.did b/core/ic/src/tera/tera.did index 3fab500d..7dc415d5 100644 --- a/core/ic/src/tera/tera.did +++ b/core/ic/src/tera/tera.did @@ -8,6 +8,7 @@ service : { authorize : (principal) -> (); consume_message : (principal, vec nat8, vec nat) -> (ConsumeMessageResponse); get_messages : () -> (vec OutgoingMessagePair); + get_messages_count : () -> (nat32) query; get_nonces : () -> (vec nat) query; remove_messages : (vec OutgoingMessagePair) -> (ConsumeMessageResponse); send_message : (principal, vec nat) -> (SendMessageResponse); From 0f3e67ef3373140047c3d7ecee324e4b2c2109fd Mon Sep 17 00:00:00 2001 From: Federico Cavazzoli Date: Mon, 31 Oct 2022 14:47:43 -0300 Subject: [PATCH 28/34] add account contract (#68) --- core/starknet/contracts/account.cairo | 186 ++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 core/starknet/contracts/account.cairo diff --git a/core/starknet/contracts/account.cairo b/core/starknet/contracts/account.cairo new file mode 100644 index 00000000..04ff0225 --- /dev/null +++ b/core/starknet/contracts/account.cairo @@ -0,0 +1,186 @@ +%lang starknet + +from starkware.cairo.common.registers import get_fp_and_pc +from starkware.starknet.common.syscalls import get_contract_address +from starkware.cairo.common.signature import verify_ecdsa_signature +from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin +from starkware.starknet.common.syscalls import call_contract, get_caller_address, get_tx_signature +from starkware.cairo.common.hash_state import ( + hash_init, hash_finalize, hash_update, hash_update_single) + +# +# Structs +# + +struct Message: + member sender : felt + member to : felt + member selector : felt + member calldata : felt* + member calldata_size : felt + member nonce : felt +end + +# +# Storage +# + +@storage_var +func current_nonce() -> (res : felt): +end + +@storage_var +func public_key() -> (res : felt): +end + +# +# Guards +# + +@view +func assert_only_self{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}(): + let (self) = get_contract_address() + let (caller) = get_caller_address() + assert self = caller + return () +end + +# +# Getters +# + +@view +func get_public_key{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> ( + res : felt): + let (res) = public_key.read() + return (res=res) +end + +@view +func get_nonce{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> (res : felt): + let (res) = current_nonce.read() + return (res=res) +end + +@view +func is_account{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}() -> ( + res : felt): + return (1) +end + +# +# Setters +# + +@external +func set_public_key{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( + new_public_key : felt): + assert_only_self() + public_key.write(new_public_key) + return () +end + +# +# Constructor +# + +@constructor +func constructor{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr}( + _public_key : felt): + public_key.write(_public_key) + return () +end + +# +# Business logic +# + +@view +func is_valid_signature{ + syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr, + ecdsa_ptr : SignatureBuiltin*}(hash : felt, signature_len : felt, signature : felt*) -> (): + let (_public_key) = public_key.read() + + # This interface expects a signature pointer and length to make + # no assumption about signature validation schemes. + # But this implementation does, and it expects a (sig_r, sig_s) pair. + let sig_r = signature[0] + let sig_s = signature[1] + + verify_ecdsa_signature( + message=hash, public_key=_public_key, signature_r=sig_r, signature_s=sig_s) + + return () +end + +@external +func execute{ + syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr, + ecdsa_ptr : SignatureBuiltin*}( + to : felt, selector : felt, calldata_len : felt, calldata : felt*, nonce : felt) -> ( + response_len : felt, response : felt*): + alloc_locals + + let (__fp__, _) = get_fp_and_pc() + let (_address) = get_contract_address() + let (_current_nonce) = current_nonce.read() + + # validate nonce + assert _current_nonce = nonce + + local message : Message = Message( + _address, + to, + selector, + calldata, + calldata_size=calldata_len, + _current_nonce + ) + + # validate transaction + let (hash) = hash_message(&message) + let (signature_len, signature) = get_tx_signature() + is_valid_signature(hash, signature_len, signature) + + # bump nonce + current_nonce.write(_current_nonce + 1) + + # execute call + let response = call_contract( + contract_address=message.to, + function_selector=message.selector, + calldata_size=message.calldata_size, + calldata=message.calldata) + + return (response_len=response.retdata_size, response=response.retdata) +end + +func hash_message{pedersen_ptr : HashBuiltin*}(message : Message*) -> (res : felt): + alloc_locals + # we need to make `res_calldata` local + # to prevent the reference from being revoked + let (local res_calldata) = hash_calldata(message.calldata, message.calldata_size) + let hash_ptr = pedersen_ptr + with hash_ptr: + let (hash_state_ptr) = hash_init() + # first three iterations are 'sender', 'to', and 'selector' + let (hash_state_ptr) = hash_update(hash_state_ptr, message, 3) + let (hash_state_ptr) = hash_update_single(hash_state_ptr, res_calldata) + let (hash_state_ptr) = hash_update_single(hash_state_ptr, message.nonce) + let (res) = hash_finalize(hash_state_ptr) + let pedersen_ptr = hash_ptr + return (res=res) + end +end + +func hash_calldata{pedersen_ptr : HashBuiltin*}(calldata : felt*, calldata_size : felt) -> ( + res : felt): + let hash_ptr = pedersen_ptr + with hash_ptr: + let (hash_state_ptr) = hash_init() + let (hash_state_ptr) = hash_update(hash_state_ptr, calldata, calldata_size) + let (res) = hash_finalize(hash_state_ptr) + let pedersen_ptr = hash_ptr + return (res=res) + end +end From a2d95dc3afc827ecfcde47fbca51b54b9daea74c Mon Sep 17 00:00:00 2001 From: Federico Cavazzoli Date: Thu, 3 Nov 2022 16:14:45 -0300 Subject: [PATCH 29/34] starknet: increase max fee (#70) --- core/aws/src/libs/starknet/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/aws/src/libs/starknet/index.ts b/core/aws/src/libs/starknet/index.ts index af4b29b8..bcd4904d 100644 --- a/core/aws/src/libs/starknet/index.ts +++ b/core/aws/src/libs/starknet/index.ts @@ -9,7 +9,7 @@ export enum NetworkName { TESTNET = 'goerli-alpha' } -const STARKNET_MAX_FEE = 1_000_000_000_000_000; +const STARKNET_MAX_FEE = new BN('10000000000000000'); class TerabethiaStarknet { private provider: Provider; From 8342d3790bca299c854b469875b92efb0bebff34 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 3 Nov 2022 21:37:08 -0300 Subject: [PATCH 30/34] core: add nonce_exist candid method --- core/ic/src/tera/src/api/nonce.rs | 6 ++++++ core/ic/src/tera/tera.did | 1 + 2 files changed, 7 insertions(+) diff --git a/core/ic/src/tera/src/api/nonce.rs b/core/ic/src/tera/src/api/nonce.rs index 6abdbc09..53774ab5 100644 --- a/core/ic/src/tera/src/api/nonce.rs +++ b/core/ic/src/tera/src/api/nonce.rs @@ -9,3 +9,9 @@ use crate::{common::types::Nonce, tera::STATE}; fn get_nonces() -> Vec { STATE.with(|s| s.get_nonces()) } + +#[query(name = "nonce_exist")] +#[candid_method(query, rename = "nonce_exist")] +fn nonce_exist(nonce: Nonce) -> bool { + STATE.with(|s| s.nonce_exists(&nonce)) +} diff --git a/core/ic/src/tera/tera.did b/core/ic/src/tera/tera.did index 7dc415d5..b8496443 100644 --- a/core/ic/src/tera/tera.did +++ b/core/ic/src/tera/tera.did @@ -10,6 +10,7 @@ service : { get_messages : () -> (vec OutgoingMessagePair); get_messages_count : () -> (nat32) query; get_nonces : () -> (vec nat) query; + nonce_exist : (nat) -> (bool) query; remove_messages : (vec OutgoingMessagePair) -> (ConsumeMessageResponse); send_message : (principal, vec nat) -> (SendMessageResponse); store_message : (principal, principal, nat, vec nat) -> ( From f674f321aa5d9836c1ac39734f41173b0ac099ff Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 3 Nov 2022 21:42:53 -0300 Subject: [PATCH 31/34] dip20_proxy: add message_exist candid method query --- magic_bridge/ic/src/dip20_proxy/dip20_proxy.did | 2 ++ magic_bridge/ic/src/dip20_proxy/src/api/messages.rs | 10 ++++++++++ magic_bridge/ic/src/dip20_proxy/src/api/mod.rs | 1 + 3 files changed, 13 insertions(+) create mode 100644 magic_bridge/ic/src/dip20_proxy/src/api/messages.rs diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index fb367fff..a82344d7 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -7,6 +7,7 @@ type OperationFailure = variant { DIP20NotResponding : opt TxError; TransferFrom : opt TxError; }; +type MessageStatus = variant { ConsumedNotMinted; Consuming }; type Result = variant { Ok : nat; Err : OperationFailure }; type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; type Result_2 = variant { Ok : nat; Err : TxError }; @@ -29,6 +30,7 @@ service : { get_all_token_balance : () -> (Result_1); get_balance : (principal, principal, nat) -> (opt nat); handle_message : (principal, nat, vec nat) -> (Result_2); + message_exist : (text) -> (opt MessageStatus) query; mint : (principal, nat, vec nat) -> (Result_2); perform_handshake : () -> (Result_3); withdraw : (principal, principal, nat) -> (Result); diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/messages.rs b/magic_bridge/ic/src/dip20_proxy/src/api/messages.rs new file mode 100644 index 00000000..ea9b1810 --- /dev/null +++ b/magic_bridge/ic/src/dip20_proxy/src/api/messages.rs @@ -0,0 +1,10 @@ +use candid::candid_method; +use ic_cdk_macros::query; + +use crate::{common::types::MessageStatus, proxy::STATE}; + +#[query(name = "message_exist")] +#[candid_method(query, rename = "message_exist")] +fn message_exist(msg_hash: String) -> Option { + STATE.with(|s| s.get_message(&msg_hash)) +} diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs index ef3a2851..daad227e 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/mod.rs @@ -4,6 +4,7 @@ mod cap; mod get_balance; mod handle_message; mod init; +mod messages; mod mint; mod upgrade; mod withdraw; From 756fbf7659f0326c1312259c68c986abcd3b1286 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 4 Nov 2022 11:31:42 -0300 Subject: [PATCH 32/34] dip20_proxy: update candid file --- magic_bridge/ic/src/dip20_proxy/dip20_proxy.did | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did index a82344d7..c3d49913 100644 --- a/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did +++ b/magic_bridge/ic/src/dip20_proxy/dip20_proxy.did @@ -1,3 +1,4 @@ +type MessageStatus = variant { ConsumedNotMinted; Consuming }; type OperationFailure = variant { SendMessage : opt TxError; Burn : opt TxError; @@ -7,7 +8,6 @@ type OperationFailure = variant { DIP20NotResponding : opt TxError; TransferFrom : opt TxError; }; -type MessageStatus = variant { ConsumedNotMinted; Consuming }; type Result = variant { Ok : nat; Err : OperationFailure }; type Result_1 = variant { Ok : vec record { text; text; nat }; Err : text }; type Result_2 = variant { Ok : nat; Err : TxError }; From 91b35b845245893b8574cfc5376583fe2371b241 Mon Sep 17 00:00:00 2001 From: Federico Date: Thu, 10 Nov 2022 15:05:56 -0300 Subject: [PATCH 33/34] fix upgrade with Option<> --- magic_bridge/ic/src/dip20_proxy/src/api/burn.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/common/types.rs | 2 +- magic_bridge/ic/src/dip20_proxy/src/proxy.rs | 9 +++++++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs index 7aeb1774..1874a644 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/burn.rs @@ -92,7 +92,7 @@ async fn burn( }); insert_claimable_asset(ClaimableMessage { - from: caller, + from: Some(caller), owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), msg_key: Some(outgoing_message.msg_key.clone()), diff --git a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs index 68522608..c2dce532 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/api/withdraw.rs @@ -77,7 +77,7 @@ pub async fn withdraw( }); insert_claimable_asset(ClaimableMessage { - from: caller, + from: Some(caller), owner: eth_addr.clone(), msg_hash: outgoing_message.msg_hash.clone(), msg_key: Some(outgoing_message.msg_key.clone()), diff --git a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs index 6f2f21fc..78047c39 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/common/types.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/common/types.rs @@ -75,7 +75,7 @@ pub struct ClaimableMessage { pub token_name: String, pub token: TokenId, pub amount: Nat, - pub from: Principal, + pub from: Option, } #[derive(CandidType, Deserialize, Default)] diff --git a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs index cb32f57b..2066643f 100644 --- a/magic_bridge/ic/src/dip20_proxy/src/proxy.rs +++ b/magic_bridge/ic/src/dip20_proxy/src/proxy.rs @@ -288,6 +288,11 @@ impl ToCapEvent for ClaimableMessage { } else { [0; 32] }; + let from = if self.from.is_some() { + self.from.unwrap() + } else { + Principal::anonymous() + }; let details = DetailsBuilder::default() .insert("owner", self.owner) .insert("ethContractAddress", self.token) @@ -295,7 +300,7 @@ impl ToCapEvent for ClaimableMessage { .insert("msgHashKey", hash_key.to_nat()) .insert("amount", self.amount.clone()) .insert("name", self.token_name.clone()) - .insert("from", self.from.clone()) + .insert("from", from) .build(); IndefiniteEventBuilder::new() @@ -317,7 +322,7 @@ impl From for ClaimableMessage { let from: Principal = event.details[6].1.clone().try_into().unwrap(); ClaimableMessage { - from: from, + from: Some(from), owner: event.caller, msg_key: Some(msg_key.to_nonce_bytes()), msg_hash: msg_hash, From fbc0b7fad81b6ea38b723fcdd75afb0aafa18872 Mon Sep 17 00:00:00 2001 From: Federico Date: Fri, 11 Nov 2022 12:04:10 -0300 Subject: [PATCH 34/34] resolve merge conflict --- core/ic/src/tera/src/tera.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core/ic/src/tera/src/tera.rs b/core/ic/src/tera/src/tera.rs index b1ccbc4a..ba0c64e8 100644 --- a/core/ic/src/tera/src/tera.rs +++ b/core/ic/src/tera/src/tera.rs @@ -145,18 +145,6 @@ impl Default for TerabetiaState { } } -impl Default for TerabetiaState { - fn default() -> Self { - TerabetiaState { - messages: RefCell::new(HashMap::default()), - nonce: RefCell::new(HashSet::default()), - messages_out: RefCell::new(HashSet::with_capacity(MAX_OUTGOING_MESSAGES_COUNT)), - message_out_index: RefCell::new(u64::default()), - authorized: RefCell::new(Vec::default()), - } - } -} - impl TerabetiaState { /// /// Outgoing