Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
76 changes: 55 additions & 21 deletions contracts/satoshi-bridge/src/account.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{near, u128_dec_format, AccountId, Contract};
use crate::{near, require, u128_dec_format, AccountId, Contract};
use near_sdk::env;
use std::collections::HashSet;

Expand All @@ -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,29 +91,39 @@ 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(),
}
}

pub fn pending_sign_count(&self) -> u32 {
u32::try_from(self.btc_pending_sign_ids.len()).unwrap_or(u32::MAX)
}
}

impl Contract {
pub fn check_account_exists(&self, account_id: &AccountId) -> bool {
self.data().accounts.contains_key(account_id)
pub fn get_max_pending_sign_txs(&self, account_id: &AccountId) -> u32 {
self.data()
.pending_tx_limits
.get(account_id)
.copied()
.unwrap_or(1)
}

pub fn internal_get_account(&self, account_id: &AccountId) -> Option<&Account> {
self.data().accounts.get(account_id).map(Into::into)
pub fn require_pending_sign_capacity(&self, account_id: &AccountId) {
require!(
self.get_account(account_id)
.unwrap_or_else(|| {
env::panic_str(&format!("ERR_ACCOUNT_NOT_REGISTERED: {}", account_id))
})
.pending_sign_count()
< self.get_max_pending_sign_txs(account_id),
"Too many pending sign transactions"
);
}

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 check_account_exists(&self, account_id: &AccountId) -> bool {
self.data().accounts.contains_key(account_id)
}

pub fn internal_unwrap_mut_account(&mut self, account_id: &AccountId) -> &mut Account {
Expand Down
37 changes: 7 additions & 30 deletions contracts/satoshi-bridge/src/api/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,7 @@ impl Contract {
chain_specific_data: Option<ChainSpecificData>,
) {
let account_id = env::predecessor_account_id();
require!(
self.internal_unwrap_account(&account_id)
.btc_pending_sign_id
.is_none(),
"Previous btc tx has not been signed"
);
self.require_pending_sign_capacity(&account_id);

self.withdraw_rbf_chain_specific(
account_id,
Expand All @@ -241,12 +236,7 @@ impl Contract {
.internal_unwrap_btc_pending_info(&original_btc_pending_verify_id)
.account_id
.clone();
require!(
self.internal_unwrap_account(&user_account_id)
.btc_pending_sign_id
.is_none(),
"Assisted user previous btc tx has not been signed"
);
self.require_pending_sign_capacity(&user_account_id);

self.cancel_withdraw_chain_specific(
user_account_id,
Expand Down Expand Up @@ -329,12 +319,7 @@ impl Contract {
) {
assert_one_yocto();
let account_id = env::predecessor_account_id();
require!(
self.internal_unwrap_account(&account_id)
.btc_pending_sign_id
.is_none(),
"Previous btc tx has not been signed"
);
self.require_pending_sign_capacity(&account_id);
self.active_utxo_management_rbf_chain_specific(
account_id,
original_btc_pending_verify_id,
Expand Down Expand Up @@ -362,12 +347,7 @@ impl Contract {
.internal_unwrap_btc_pending_info(&original_btc_pending_verify_id)
.account_id
.clone();
require!(
self.internal_unwrap_account(&user_account_id)
.btc_pending_sign_id
.is_none(),
"Assisted user previous btc tx has not been signed"
);
self.require_pending_sign_capacity(&user_account_id);
self.cancel_active_utxo_management_chain_specific(
user_account_id,
original_btc_pending_verify_id,
Expand Down Expand Up @@ -432,11 +412,7 @@ impl Contract {
account_id: AccountId,
mut psbt: PsbtWrapper,
) {
let account = self.internal_unwrap_account(&account_id);
require!(
account.btc_pending_sign_id.is_none(),
"Previous btc tx has not been signed"
);
self.require_pending_sign_capacity(&account_id);

let (utxo_storage_keys, vutxos) = self.generate_vutxos(&mut psbt);
let (actual_received_amount, gas_fee) =
Expand Down Expand Up @@ -480,7 +456,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
18 changes: 18 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,24 @@ impl Contract {
}
}

#[payable]
#[access_control_any(roles(Role::DAO))]
pub fn set_pending_tx_limit(&mut self, account_id: AccountId, max_pending: Option<u32>) {
assert_one_yocto();
if let Some(max_pending) = max_pending {
require!(max_pending >= 1, "Invalid max_pending value");
self.data_mut()
.pending_tx_limits
.insert(account_id, max_pending);
} else {
let prev = self.data_mut().pending_tx_limits.remove(&account_id);
require!(
prev.is_some(),
format!("Invalid account_id: {}", account_id)
);
}
}

#[payable]
#[access_control_any(roles(Role::DAO))]
pub fn extend_post_action_msg_templates(
Expand Down
11 changes: 6 additions & 5 deletions contracts/satoshi-bridge/src/api/token_receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ impl Contract {
max_gas_fee: Option<U128>,
) {
let (utxo_storage_keys, vutxos) = self.generate_vutxos(&mut psbt);
let max_pending = self.get_max_pending_sign_txs(&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(),
"Previous btc tx has not been signed"
account.pending_sign_count() < max_pending,
"Too many pending sign transactions"
);

let withdraw_change_address_script_pubkey =
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
15 changes: 12 additions & 3 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 pending_tx_limits: HashMap<AccountId, u32>,
#[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(),
pending_tx_limits: root_state
.pending_tx_limits
.iter()
.map(|(k, v)| (k.clone(), *v))
.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 @@ -72,8 +78,8 @@ impl Contract {
self.internal_config().clone()
}

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

pub fn list_accounts(
Expand All @@ -82,7 +88,10 @@ impl Contract {
) -> HashMap<AccountId, Option<Account>> {
account_ids
.into_iter()
.map(|key| (key.clone(), self.internal_get_account(&key).cloned()))
.map(|key| {
let account = self.get_account(&key);
(key, account)
})
.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