Skip to content

Commit

Permalink
fix: don't ask for total supply if token doesn't exist (#1058)
Browse files Browse the repository at this point in the history
* fix: don't ask for total supply if token doesn't exist

* nit: move sc deployed flag in state

* nit: check for deployed sc even if not coordinator
  • Loading branch information
matteojug authored Dec 9, 2024
1 parent 3ffa0af commit 69f13b3
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 124 deletions.
28 changes: 16 additions & 12 deletions signer/src/block_observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,18 +489,22 @@ impl<C: Context, B> BlockObserver<C, B> {
/// Update the sBTC peg limits from Emily
async fn update_sbtc_limits(&self) -> Result<(), Error> {
let limits = self.context.get_emily_client().get_limits().await?;
let max_mintable = match limits.total_cap() {
Amount::MAX_MONEY => Amount::MAX_MONEY,
total_cap => {
let sbtc_supply = self
.context
.get_stacks_client()
.get_sbtc_total_supply(&self.context.config().signer.deployer)
.await?;
// The maximum amount of sBTC that can be minted is the total cap
// minus the current supply.
total_cap.checked_sub(sbtc_supply).unwrap_or(Amount::ZERO)
}
let sbtc_deployed = self.context.state().sbtc_contracts_deployed();

let max_mintable = if limits.total_cap_exists() && sbtc_deployed {
let sbtc_supply = self
.context
.get_stacks_client()
.get_sbtc_total_supply(&self.context.config().signer.deployer)
.await?;
// The maximum amount of sBTC that can be minted is the total cap
// minus the current supply.
limits
.total_cap()
.checked_sub(sbtc_supply)
.unwrap_or(Amount::ZERO)
} else {
Amount::MAX_MONEY
};

let limits = SbtcLimits::new(
Expand Down
21 changes: 20 additions & 1 deletion signer/src/context/signer_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Module for signer state
use std::sync::RwLock;
use std::sync::{
atomic::{AtomicBool, Ordering},
RwLock,
};

use bitcoin::Amount;
use hashbrown::HashSet;
Expand All @@ -15,6 +18,7 @@ use crate::keys::PublicKey;
pub struct SignerState {
current_signer_set: SignerSet,
current_limits: RwLock<SbtcLimits>,
sbtc_contracts_deployed: AtomicBool,
}

impl SignerState {
Expand All @@ -41,6 +45,16 @@ impl SignerState {
.expect("BUG: Failed to acquire write lock");
*limits = new_limits;
}

/// Returns true if sbtc smart contracts are deployed
pub fn sbtc_contracts_deployed(&self) -> bool {
self.sbtc_contracts_deployed.load(Ordering::SeqCst)
}

/// Set the sbtc smart contracts deployed flag
pub fn set_sbtc_contracts_deployed(&self) {
self.sbtc_contracts_deployed.store(true, Ordering::SeqCst);
}
}

/// Represents the current sBTC limits.
Expand Down Expand Up @@ -87,6 +101,11 @@ impl SbtcLimits {
self.total_cap.unwrap_or(Amount::MAX_MONEY)
}

/// Check if total cap is set
pub fn total_cap_exists(&self) -> bool {
self.total_cap.is_some()
}

/// Get the maximum amount of BTC allowed to be pegged-in per transaction.
pub fn per_deposit_cap(&self) -> Amount {
self.per_deposit_cap.unwrap_or(Amount::MAX_MONEY)
Expand Down
1 change: 0 additions & 1 deletion signer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,6 @@ async fn run_transaction_coordinator(ctx: impl Context) -> Result<(), Error> {
bitcoin_presign_request_max_duration: config.signer.bitcoin_presign_request_max_duration,
threshold: config.signer.bootstrap_signatures_required,
dkg_max_duration: config.signer.dkg_max_duration,
sbtc_contracts_deployed: false,
is_epoch3: false,
};

Expand Down
3 changes: 2 additions & 1 deletion signer/src/stacks/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use crate::storage::DbRead;
use crate::util::ApiFallbackClient;

use super::contracts::AsTxPayload;
use super::contracts::SmartContract;
use super::wallet::SignerWallet;

const REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
Expand Down Expand Up @@ -1281,7 +1282,7 @@ impl StacksInteract for StacksClient {
let result = self
.call_read(
deployer,
&ContractName::from("sbtc-token"),
&ContractName::from(SmartContract::SbtcToken.contract_name()),
&ClarityName::from("get-total-supply"),
deployer,
)
Expand Down
18 changes: 18 additions & 0 deletions signer/src/testing/stacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
use blockstack_lib::chainstate::nakamoto::NakamotoBlockHeader;
use blockstack_lib::net::api::getsortition::SortitionInfo;
use blockstack_lib::net::api::gettenureinfo::RPCGetTenureInfo;
use clarity::types::chainstate::StacksBlockId;
use stacks_common::types::chainstate::BurnchainHeaderHash;
use stacks_common::types::chainstate::ConsensusHash;
use stacks_common::types::chainstate::SortitionId;
Expand All @@ -26,6 +28,22 @@ pub const DUMMY_SORTITION_INFO: SortitionInfo = SortitionInfo {
committed_block_hash: None,
};

/// Some dummy tenure info
pub const DUMMY_TENURE_INFO: RPCGetTenureInfo = RPCGetTenureInfo {
consensus_hash: ConsensusHash([0; 20]),
tenure_start_block_id: StacksBlockId([0; 32]),
parent_consensus_hash: ConsensusHash([0; 20]),
// The following bytes are the ones returned by StacksBlockId::first_mined()
parent_tenure_start_block_id: StacksBlockId([
0x55, 0xc9, 0x86, 0x1b, 0xe5, 0xcf, 0xf9, 0x84, 0xa2, 0x0c, 0xe6, 0xd9, 0x9d, 0x4a, 0xa6,
0x59, 0x41, 0x41, 0x28, 0x89, 0xbd, 0xc6, 0x65, 0x09, 0x41, 0x36, 0x42, 0x9b, 0x84, 0xf8,
0xc2, 0xee,
]),
tip_block_id: StacksBlockId([0; 32]),
tip_height: 0,
reward_cycle: 0,
};

impl TenureBlocks {
/// Create a TenureBlocks struct that is basically empty.
pub fn nearly_empty() -> Result<Self, Error> {
Expand Down
8 changes: 3 additions & 5 deletions signer/src/testing/transaction_coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ where
context_window: u16,
private_key: PrivateKey,
threshold: u16,
sbtc_contracts_deployed: bool,
) -> Self {
Self {
event_loop: transaction_coordinator::TxCoordinatorEventLoop {
Expand All @@ -98,7 +97,6 @@ where
private_key,
context_window,
threshold,
sbtc_contracts_deployed,
signing_round_max_duration: Duration::from_secs(10),
bitcoin_presign_request_max_duration: Duration::from_secs(10),
dkg_max_duration: Duration::from_secs(10),
Expand Down Expand Up @@ -200,6 +198,7 @@ where
.await;

// Create the coordinator
self.context.state().set_sbtc_contracts_deployed();
let signer_network = SignerNetwork::single(&self.context);
let mut coordinator = TxCoordinatorEventLoop {
context: self.context,
Expand All @@ -210,7 +209,6 @@ where
signing_round_max_duration: Duration::from_millis(500),
bitcoin_presign_request_max_duration: Duration::from_millis(500),
dkg_max_duration: Duration::from_millis(500),
sbtc_contracts_deployed: true,
is_epoch3: true,
};

Expand Down Expand Up @@ -349,13 +347,13 @@ where
let private_key = select_coordinator(&bitcoin_chain_tip.block_hash, &signer_info);

// Bootstrap the tx coordinator within an event loop harness.
self.context.state().set_sbtc_contracts_deployed();
let event_loop_harness = TxCoordinatorEventLoopHarness::create(
self.context.clone(),
network.connect(),
self.context_window,
private_key,
self.signing_threshold,
true,
);

// Start the tx coordinator run loop.
Expand Down Expand Up @@ -539,13 +537,13 @@ where
let private_key = select_coordinator(&bitcoin_chain_tip.block_hash, &signer_info);

// Bootstrap the tx coordinator within an event loop harness.
// We don't `set_sbtc_contracts_deployed` to force the coordinator to deploy the contracts
let event_loop_harness = TxCoordinatorEventLoopHarness::create(
self.context.clone(),
network.connect(),
self.context_window,
private_key,
self.signing_threshold,
false, // Force the coordinator to deploy the contracts
);

// Start the tx coordinator run loop.
Expand Down
11 changes: 7 additions & 4 deletions signer/src/transaction_coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,6 @@ pub struct TxCoordinatorEventLoop<Context, Network> {
/// The maximum duration of distributed key generation before the
/// coordinator will time out and return an error.
pub dkg_max_duration: Duration,
/// Whether the coordinator has already deployed the contracts.
pub sbtc_contracts_deployed: bool,
/// An indicator for whether the Stacks blockchain has reached Nakamoto
/// 3. If we are not in Nakamoto 3 or later, then the coordinator does
/// not do any work.
Expand Down Expand Up @@ -294,6 +292,11 @@ where
// coordinating DKG or constructing bitcoin and stacks
// transactions, might as well return early.
if !self.is_coordinator(&bitcoin_chain_tip, &signer_public_keys) {
// Before returning, we also check if all the smart contracts are
// deployed: we do this as some other coordinator could have deployed
// them, in which case we need to updated our state.
self.all_smart_contracts_deployed().await?;

tracing::debug!("we are not the coordinator, so nothing to do");
return Ok(());
}
Expand Down Expand Up @@ -1477,7 +1480,7 @@ where
}

async fn all_smart_contracts_deployed(&mut self) -> Result<bool, Error> {
if self.sbtc_contracts_deployed {
if self.context.state().sbtc_contracts_deployed() {
return Ok(true);
}

Expand All @@ -1490,7 +1493,7 @@ where
}
}

self.sbtc_contracts_deployed = true;
self.context.state().set_sbtc_contracts_deployed();
Ok(true)
}

Expand Down
Loading

0 comments on commit 69f13b3

Please sign in to comment.