Skip to content

Commit

Permalink
Update validation to check the stacks node
Browse files Browse the repository at this point in the history
for whether the deposit has been completed.
  • Loading branch information
djordon committed Feb 5, 2025
1 parent 7a17d86 commit b1125dd
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 35 deletions.
77 changes: 45 additions & 32 deletions signer/src/stacks/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,22 +338,24 @@ impl AsContractCall for CompleteDepositV1 {
/// Validates that the Complete deposit request satisfies the following
/// criteria:
///
/// 1. That the smart contract deployer matches the deployer in our
/// context.
/// 2. That the signer has a record of the deposit request in its list
/// of pending and accepted deposit requests.
/// 3. That the signer sweep transaction is on the canonical bitcoin
/// blockchain.
/// 4. That the sweep transaction uses the indicated deposit outpoint
/// as an input.
/// 5. That the recipients in the transaction matches that of the
/// deposit request.
/// 6. That the amount to mint is above the dust amount.
/// 7. That the fee matches the expected assessed fee for the outpoint.
/// 8. That the fee is less than the specified max-fee.
/// 9. That the first input into the sweep transaction is the signers'
/// UTXO. This checks that the sweep transaction was generated by
/// the signers.
/// 1. That the smart contract deployer matches the deployer in our
/// context.
/// 2. That the signer has a record of the deposit request in its list
/// of pending and accepted deposit requests.
/// 3. That the signer sweep transaction is on the canonical bitcoin
/// blockchain.
/// 4. That the sweep transaction uses the indicated deposit outpoint
/// as an input.
/// 5. That the recipients in the transaction matches that of the
/// deposit request.
/// 6. That the amount to mint is above the dust amount.
/// 7. That the fee matches the expected assessed fee for the
/// outpoint.
/// 8. That the fee is less than the specified max-fee.
/// 9. That the first input into the sweep transaction is the signers'
/// UTXO. This checks that the sweep transaction was generated by
/// the signers.
/// 10. That sBTC has not been minted for the deposit already.
///
/// # Notes
///
Expand All @@ -368,7 +370,7 @@ impl AsContractCall for CompleteDepositV1 {
where
C: Context + Send + Sync,
{
// Covers points 3-4 & 9
// Covers points 3-4 & 9-10
let fee = self.validate_sweep_tx(ctx, req_ctx).await?;
let db = ctx.get_storage();
// Covers points 1-2 & 5-8
Expand Down Expand Up @@ -404,15 +406,12 @@ impl CompleteDepositV1 {
return Err(DepositErrorMsg::DeployerMismatch.into_error(req_ctx, self));
}
// 2. Check that the signer has a record of the deposit request
// from our list of swept deposit requests.
let deposit_requests = db
.get_swept_deposit_requests(&req_ctx.chain_tip.block_hash, req_ctx.context_window)
.await?;

let deposit_request = deposit_requests
.into_iter()
.find(|req| req.deposit_outpoint() == self.outpoint)
.ok_or_else(|| DepositErrorMsg::RequestMissing.into_error(req_ctx, self))?;
// from our list of deposit requests.
let txid = self.outpoint.txid.into();
let output_index = self.outpoint.vout;
let Some(deposit_request) = db.get_deposit_request(&txid, output_index).await? else {
return Err(DepositErrorMsg::RequestMissing.into_error(req_ctx, self));
};

// 5. Check that the recipients in the transaction matches that of
// the deposit request.
Expand Down Expand Up @@ -442,12 +441,13 @@ impl CompleteDepositV1 {
///
/// Specifically, this function checks the following points (from the
/// docs of [`CompleteDepositV1::validate`]):
/// 3. Check that the signer sweep transaction is on the canonical
/// bitcoin blockchain.
/// 4. Check that the sweep transaction uses the indicated deposit
/// outpoint as an input.
/// 9. That the first input into the sweep transaction is the signers'
/// UTXO.
/// 3. Check that the signer sweep transaction is on the canonical
/// bitcoin blockchain.
/// 4. Check that the sweep transaction uses the indicated deposit
/// outpoint as an input.
/// 9. That the first input into the sweep transaction is the signers'
/// UTXO.
/// 10. That sBTC has not been minted for the deposit already.
async fn validate_sweep_tx<C>(&self, ctx: &C, req_ctx: &ReqContext) -> Result<Amount, Error>
where
C: Context + Send + Sync,
Expand Down Expand Up @@ -505,6 +505,16 @@ impl CompleteDepositV1 {
return Err(DepositErrorMsg::InvalidSweep.into_error(req_ctx, self));
}

// 10. Check that sBTC has not been minted for the deposit already.
let stacks = ctx.get_stacks_client();
let is_deposit_completed = stacks
.is_deposit_completed(&req_ctx.deployer, &self.outpoint)
.await?;

if is_deposit_completed {
return Err(DepositErrorMsg::DepositCompleted.into_error(req_ctx, self));
}

// None is only returned from BitcoinTxInfo::assess_output_fee when:
// a) The indicated output index is 0 or 1, since or those cannot
// be valid output indices for sweep transactions, or
Expand Down Expand Up @@ -556,6 +566,9 @@ pub enum DepositErrorMsg {
/// The smart contract deployer is fixed, so this should always match.
#[error("the deployer in the transaction does not match the expected deployer")]
DeployerMismatch,
/// We have already minted sBTC for the given deposit request
#[error("sBTC has already been minted for the deposit request")]
DepositCompleted,
/// The fee paid to the bitcoin miners exceeded the max fee.
#[error("fee paid to the bitcoin miners exceeded the max fee")]
FeeTooHigh,
Expand Down
6 changes: 3 additions & 3 deletions signer/src/storage/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,9 +681,9 @@ impl PgStore {
/// the deposit request, and whether it was confirmed on the blockchain
/// that we just listed out.
///
/// `None` is returned if deposit request in the database (we always
/// write the associated transaction to the database for each deposit
/// so that cannot be the reason for why the query here returns
/// `None` is returned if no deposit request is in the database (we
/// always write the associated transaction to the database for each
/// deposit so that cannot be the reason for why the query here returns
/// `None`).
async fn get_deposit_request_status_summary(
&self,
Expand Down

0 comments on commit b1125dd

Please sign in to comment.