Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contracts/satoshi-bridge/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "satoshi-bridge"
version = "0.7.5"
version = "0.8.0"
edition.workspace = true
publish.workspace = true
repository.workspace = true
Expand Down
56 changes: 38 additions & 18 deletions contracts/satoshi-bridge/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,44 @@ pub struct OutstandingInfo {
#[derive(Clone)]
#[cfg_attr(not(target_arch = "wasm32"), derive(Debug))]
pub struct Account {
pub account_id: AccountId,
pub btc_pending_sign_ids: HashSet<String>,
Comment thread
karim-en marked this conversation as resolved.
pub btc_pending_verify_list: HashSet<String>,
}

/// Old Account format (v0.7.5 and earlier) with single pending sign id.
#[near(serializers = [borsh])]
#[derive(Clone)]
pub struct AccountV0 {
pub account_id: AccountId,
pub btc_pending_sign_id: Option<String>,
pub btc_pending_verify_list: HashSet<String>,
}

impl From<AccountV0> for Account {
fn from(v: AccountV0) -> Self {
let mut btc_pending_sign_ids = HashSet::new();
if let Some(id) = v.btc_pending_sign_id {
btc_pending_sign_ids.insert(id);
}
Self {
account_id: v.account_id,
btc_pending_sign_ids,
btc_pending_verify_list: v.btc_pending_verify_list,
}
}
}

#[near(serializers = [borsh])]
pub enum VAccount {
V0(AccountV0),
Current(Account),
}

impl From<VAccount> for Account {
fn from(v: VAccount) -> Self {
match v {
VAccount::V0(c) => c.into(),
VAccount::Current(c) => c,
}
}
Expand All @@ -36,23 +61,22 @@ impl From<VAccount> for Account {
impl From<&VAccount> for Account {
fn from(v: &VAccount) -> Self {
match v {
VAccount::V0(c) => c.clone().into(),
VAccount::Current(c) => c.clone(),
}
}
}

impl<'a> From<&'a mut VAccount> for &'a mut Account {
fn from(v: &'a mut VAccount) -> Self {
match v {
VAccount::Current(c) => c,
// Lazy migrate V0 -> Current on first mutable access
if let VAccount::V0(old) = v {
let migrated: Account = old.clone().into();
*v = VAccount::Current(migrated);
}
}
}

impl<'a> From<&'a VAccount> for &'a Account {
fn from(v: &'a VAccount) -> Self {
match v {
VAccount::Current(c) => c,
_ => unreachable!(),
}
}
}
Expand All @@ -67,7 +91,7 @@ impl Account {
pub fn new(account_id: &AccountId) -> Self {
Self {
account_id: account_id.clone(),
btc_pending_sign_id: None,
btc_pending_sign_ids: HashSet::new(),
btc_pending_verify_list: HashSet::new(),
}
}
Expand All @@ -78,18 +102,14 @@ impl Contract {
self.data().accounts.contains_key(account_id)
}

pub fn internal_get_account(&self, account_id: &AccountId) -> Option<&Account> {
self.data().accounts.get(account_id).map(Into::into)
pub fn internal_get_account(&self, account_id: &AccountId) -> Option<Account> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we return cloned version now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just decided to implement a lazy account migration. If we request mut, we upgrade the account to the new version. If it’s view, we convert it locally. Because of this approach, we can’t return a reference, since a conversion happens.

As an alternative, we could implement a separate migration function and migrate accounts in batches. But I kind of like the lazy approach more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function can be removed. there is get_account that can be used directly

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we'd need an unnecessary clone, since AccountId is taken by value in view method. So I'd leave it as is for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use ref in view methods as I know

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed: 3f977ef

self.data().accounts.get(account_id).map(Account::from)
}

pub fn internal_unwrap_account(&self, account_id: &AccountId) -> &Account {
self.data()
.accounts
.get(account_id)
.map(|o| o.into())
.unwrap_or_else(|| {
env::panic_str(&format!("ERR_ACCOUNT_NOT_REGISTERED: {}", account_id))
})
pub fn internal_unwrap_account(&self, account_id: &AccountId) -> Account {
Comment thread
karim-en marked this conversation as resolved.
Outdated
self.internal_get_account(account_id).unwrap_or_else(|| {
env::panic_str(&format!("ERR_ACCOUNT_NOT_REGISTERED: {}", account_id))
})
}

pub fn internal_unwrap_mut_account(&mut self, account_id: &AccountId) -> &mut Account {
Expand Down
21 changes: 11 additions & 10 deletions contracts/satoshi-bridge/src/api/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ impl Contract {
let account_id = env::predecessor_account_id();
require!(
self.internal_unwrap_account(&account_id)
.btc_pending_sign_id
.is_none(),
.btc_pending_sign_ids
.is_empty(),
Comment thread
frolvanya marked this conversation as resolved.
Outdated
"Previous btc tx has not been signed"
);

Expand Down Expand Up @@ -243,8 +243,8 @@ impl Contract {
.clone();
require!(
self.internal_unwrap_account(&user_account_id)
.btc_pending_sign_id
.is_none(),
.btc_pending_sign_ids
.is_empty(),
"Assisted user previous btc tx has not been signed"
);

Expand Down Expand Up @@ -331,8 +331,8 @@ impl Contract {
let account_id = env::predecessor_account_id();
require!(
self.internal_unwrap_account(&account_id)
.btc_pending_sign_id
.is_none(),
.btc_pending_sign_ids
.is_empty(),
"Previous btc tx has not been signed"
);
self.active_utxo_management_rbf_chain_specific(
Expand Down Expand Up @@ -364,8 +364,8 @@ impl Contract {
.clone();
require!(
self.internal_unwrap_account(&user_account_id)
.btc_pending_sign_id
.is_none(),
.btc_pending_sign_ids
.is_empty(),
"Assisted user previous btc tx has not been signed"
);
self.cancel_active_utxo_management_chain_specific(
Expand Down Expand Up @@ -434,7 +434,7 @@ impl Contract {
) {
let account = self.internal_unwrap_account(&account_id);
require!(
account.btc_pending_sign_id.is_none(),
account.btc_pending_sign_ids.is_empty(),
"Previous btc tx has not been signed"
);

Expand Down Expand Up @@ -480,7 +480,8 @@ impl Contract {
"pending info already exist"
);
self.internal_unwrap_mut_account(&account_id)
.btc_pending_sign_id = Some(btc_pending_id.clone());
.btc_pending_sign_ids
.insert(btc_pending_id.clone());
Event::UtxoRemoved { utxo_storage_keys }.emit();
Event::GenerateBtcPendingInfo {
account_id: &account_id,
Expand Down
12 changes: 6 additions & 6 deletions contracts/satoshi-bridge/src/api/chain_signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ impl Contract {
btc_pending_info.assert_pending_sign();
if let Some(original_tx_id) = btc_pending_info.get_original_tx_id() {
if !self.check_btc_pending_info_exists(original_tx_id) {
let clear_account_btc_pending_sign_id = self
.internal_unwrap_mut_account(&btc_pending_info.account_id.clone())
.btc_pending_sign_id
.take()
== Some(btc_pending_sign_id.clone());
require!(clear_account_btc_pending_sign_id, "Internal error");
require!(
self.internal_unwrap_mut_account(&btc_pending_info.account_id.clone())
.btc_pending_sign_ids
.remove(&btc_pending_sign_id),
"Internal error"
);
self.internal_remove_btc_pending_info(&btc_pending_sign_id);
return PromiseOrValue::Value(true);
}
Expand Down
26 changes: 26 additions & 0 deletions contracts/satoshi-bridge/src/api/management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,32 @@ impl Contract {
}
}

#[payable]
#[access_control_any(roles(Role::DAO))]
pub fn extend_unlimited_txs_white_list(&mut self, account_ids: Vec<AccountId>) {
assert_one_yocto();
for account_id in account_ids {
let is_success = self
.data_mut()
.unlimited_txs_white_list
.insert(account_id.clone());
require!(
is_success,
format!("Already exist account_id: {}", account_id)
);
}
}

#[payable]
#[access_control_any(roles(Role::DAO))]
pub fn remove_unlimited_txs_white_list(&mut self, account_ids: Vec<AccountId>) {
assert_one_yocto();
for account_id in account_ids {
let is_success = self.data_mut().unlimited_txs_white_list.remove(&account_id);
require!(is_success, format!("Invalid account_id: {}", account_id));
}
}

#[payable]
#[access_control_any(roles(Role::DAO))]
pub fn extend_post_action_msg_templates(
Expand Down
9 changes: 5 additions & 4 deletions contracts/satoshi-bridge/src/api/token_receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ impl Contract {
max_gas_fee: Option<U128>,
) {
let (utxo_storage_keys, vutxos) = self.generate_vutxos(&mut psbt);
let is_unlimited = self.data().unlimited_txs_white_list.contains(&sender_id);
let account = self.internal_unwrap_or_create_mut_account(&sender_id);
require!(
self.internal_unwrap_or_create_mut_account(&sender_id)
.btc_pending_sign_id
.is_none(),
is_unlimited || account.btc_pending_sign_ids.is_empty(),
"Previous btc tx has not been signed"
);

Expand Down Expand Up @@ -129,7 +129,8 @@ impl Contract {
"pending info already exist"
);
self.internal_unwrap_mut_account(&sender_id)
.btc_pending_sign_id = Some(btc_pending_id.clone());
.btc_pending_sign_ids
.insert(btc_pending_id.clone());
Event::UtxoRemoved { utxo_storage_keys }.emit();
Event::GenerateBtcPendingInfo {
account_id: &sender_id,
Expand Down
10 changes: 8 additions & 2 deletions contracts/satoshi-bridge/src/api/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Metadata {
pub relayer_white_list: Vec<AccountId>,
pub extra_msg_relayer_white_list: Vec<AccountId>,
pub post_action_receiver_id_white_list: Vec<AccountId>,
pub unlimited_txs_white_list: Vec<AccountId>,
#[serde(with = "u128_dec_format")]
pub acc_collected_protocol_fee: u128,
#[serde(with = "u128_dec_format")]
Expand Down Expand Up @@ -57,6 +58,11 @@ impl Contract {
.iter()
.cloned()
.collect(),
unlimited_txs_white_list: root_state
.unlimited_txs_white_list
.iter()
.cloned()
.collect(),
acc_collected_protocol_fee: root_state.acc_collected_protocol_fee,
cur_available_protocol_fee: root_state.cur_available_protocol_fee,
acc_claimed_protocol_fee: root_state.acc_claimed_protocol_fee,
Expand All @@ -73,7 +79,7 @@ impl Contract {
}

pub fn get_account(&self, account_id: AccountId) -> Option<Account> {
self.internal_get_account(&account_id).cloned()
self.internal_get_account(&account_id)
}

pub fn list_accounts(
Expand All @@ -82,7 +88,7 @@ impl Contract {
) -> HashMap<AccountId, Option<Account>> {
account_ids
.into_iter()
.map(|key| (key.clone(), self.internal_get_account(&key).cloned()))
.map(|key| (key.clone(), self.internal_get_account(&key)))
.collect()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ macro_rules! define_rbf_method {
);

self.internal_unwrap_mut_account(&account_id)
.btc_pending_sign_id = Some(btc_pending_id.clone());
.btc_pending_sign_ids
.insert(btc_pending_id.clone());

Event::GenerateBtcPendingInfo {
account_id: &account_id,
Expand Down
2 changes: 1 addition & 1 deletion contracts/satoshi-bridge/src/btc_pending_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ impl Contract {

pub fn generate_btc_pending_sign_id(payload_preimages: &[Vec<u8>]) -> String {
let hash_bytes = env::sha256_array(
&payload_preimages
payload_preimages
.iter()
.flatten()
.copied()
Expand Down
7 changes: 4 additions & 3 deletions contracts/satoshi-bridge/src/chain_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,10 @@ impl Contract {

let is_original_tx = btc_pending_info.get_original_tx_id().is_none();
let account = self.internal_unwrap_mut_account(&account_id);
let clear_account_btc_pending_sign_id =
account.btc_pending_sign_id.take() == Some(btc_pending_sign_id.clone());
require!(clear_account_btc_pending_sign_id, "Internal error");
require!(
account.btc_pending_sign_ids.remove(&btc_pending_sign_id),
"Internal error"
Comment thread
karim-en marked this conversation as resolved.
);
if is_original_tx {
account
.btc_pending_verify_list
Expand Down
Loading