Skip to content

Commit

Permalink
feat: add emily call to update deposit after tx broadcast
Browse files Browse the repository at this point in the history
  • Loading branch information
matteojug committed Oct 3, 2024
1 parent 3b73797 commit 54387d0
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 8 deletions.
8 changes: 8 additions & 0 deletions signer/src/block_observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,5 +940,13 @@ mod tests {
async fn get_deposits(&self) -> Result<Vec<CreateDepositRequest>, Error> {
Ok(self.pending_deposits.clone())
}

async fn update_broadcasted_deposits<'a>(
&'a self,
_transaction: &'a utxo::UnsignedTransaction<'a>,
_bitcoin_chain_tip: &'a model::BitcoinBlockRef,
) -> Result<emily_client::models::UpdateDepositsResponse, Error> {
unimplemented!()
}
}
}
59 changes: 59 additions & 0 deletions signer/src/emily_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@
use emily_client::apis::configuration::Configuration as EmilyApiConfig;
use emily_client::apis::deposit_api;
use emily_client::apis::Error as EmilyError;
use emily_client::models::DepositUpdate;
use emily_client::models::Status;
use emily_client::models::UpdateDepositsRequestBody;
use emily_client::models::UpdateDepositsResponse;
use hex::ToHex as _;
use sbtc::deposits::CreateDepositRequest;
use url::Url;

use crate::bitcoin::utxo::RequestRef;
use crate::bitcoin::utxo::UnsignedTransaction;
use crate::error::Error;
use crate::storage::model::BitcoinBlockRef;
use crate::util::ApiFallbackClient;

/// Emily client error variants.
Expand All @@ -23,6 +31,10 @@ pub enum EmilyClientError {
/// An error occurred while getting deposits
#[error("error getting deposits: {0}")]
GetDeposits(EmilyError<deposit_api::GetDepositsError>),

/// An error occurred while updating deposits
#[error("error updating deposits: {0}")]
UpdateDeposits(EmilyError<deposit_api::UpdateDepositsError>),
}

/// Trait describing the interactions with Emily API.
Expand All @@ -32,6 +44,13 @@ pub trait EmilyInteract: Sync + Send {
fn get_deposits(
&self,
) -> impl std::future::Future<Output = Result<Vec<CreateDepositRequest>, Error>> + Send;

/// Update deposits.
fn update_broadcasted_deposits<'a>(
&'a self,
transaction: &'a UnsignedTransaction<'a>,
bitcoin_chain_tip: &'a BitcoinBlockRef,
) -> impl std::future::Future<Output = Result<UpdateDepositsResponse, Error>> + Send;
}

/// Emily API client.
Expand Down Expand Up @@ -82,6 +101,37 @@ impl EmilyInteract for EmilyClient {
// .await
// .map_err(EmilyClientError::GetDeposits)?;
}

async fn update_broadcasted_deposits<'a>(
&'a self,
transaction: &'a UnsignedTransaction<'a>,
bitcoin_chain_tip: &'a BitcoinBlockRef,
) -> Result<UpdateDepositsResponse, Error> {
let deposits = transaction.requests.iter().filter_map(|e| match e {
RequestRef::Deposit(d) => Some(d),
_ => None,
});
let mut update_request = Vec::new();
for deposit in deposits {
update_request.push(DepositUpdate {
bitcoin_tx_output_index: i32::try_from(deposit.outpoint.vout)
.map_err(|_| Error::TypeConversion)?,
bitcoin_txid: deposit.outpoint.txid.encode_hex(),
fulfillment: None,
last_update_block_hash: bitcoin_chain_tip.block_hash.encode_hex(),
last_update_height: i64::try_from(bitcoin_chain_tip.block_height)
.map_err(|_| Error::TypeConversion)?,
status: Status::Accepted,
status_message: "TODO?".to_string(),
});
}
let update_request = UpdateDepositsRequestBody { deposits: update_request };

deposit_api::update_deposits(&self.config, update_request)
.await
.map_err(EmilyClientError::UpdateDeposits)
.map_err(Error::EmilyApi)
}
}

impl EmilyInteract for ApiFallbackClient<EmilyClient> {
Expand All @@ -90,6 +140,15 @@ impl EmilyInteract for ApiFallbackClient<EmilyClient> {
) -> impl std::future::Future<Output = Result<Vec<CreateDepositRequest>, Error>> {
self.exec(|client, _| client.get_deposits())
}

async fn update_broadcasted_deposits<'a>(
&'a self,
transaction: &'a UnsignedTransaction<'a>,
bitcoin_chain_tip: &'a BitcoinBlockRef,
) -> Result<UpdateDepositsResponse, Error> {
self.exec(|client, _| client.update_broadcasted_deposits(transaction, bitcoin_chain_tip))
.await
}
}

impl TryFrom<&[Url]> for ApiFallbackClient<EmilyClient> {
Expand Down
17 changes: 16 additions & 1 deletion signer/src/testing/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use clarity::types::chainstate::{StacksAddress, StacksBlockId};
use tokio::sync::Mutex;

use crate::{
bitcoin::{rpc::GetTxResponse, BitcoinInteract, MockBitcoinInteract},
bitcoin::{
rpc::GetTxResponse, utxo::UnsignedTransaction, BitcoinInteract, MockBitcoinInteract,
},
config::Settings,
context::{Context, SignerContext},
emily_client::{EmilyInteract, MockEmilyInteract},
Expand All @@ -23,6 +25,7 @@ use crate::{
},
storage::{
in_memory::{SharedStore, Store},
model::BitcoinBlockRef,
DbRead, DbWrite,
},
};
Expand Down Expand Up @@ -338,6 +341,18 @@ impl EmilyInteract for WrappedMock<MockEmilyInteract> {
async fn get_deposits(&self) -> Result<Vec<sbtc::deposits::CreateDepositRequest>, Error> {
self.inner.lock().await.get_deposits().await
}

async fn update_broadcasted_deposits<'a>(
&'a self,
transaction: &'a UnsignedTransaction<'a>,
bitcoin_chain_tip: &'a BitcoinBlockRef,
) -> Result<emily_client::models::UpdateDepositsResponse, Error> {
self.inner
.lock()
.await
.update_broadcasted_deposits(transaction, bitcoin_chain_tip)
.await
}
}

/// Struct which holds the current configuration of the context builder.
Expand Down
20 changes: 16 additions & 4 deletions signer/src/testing/transaction_coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::bitcoin::utxo::SignerUtxo;
use crate::bitcoin::MockBitcoinInteract;
use crate::context::Context;
use crate::context::SignerEvent;
use crate::emily_client::EmilyInteract;
use crate::emily_client::MockEmilyInteract;
use crate::error;
use crate::keys;
use crate::keys::PrivateKey;
Expand Down Expand Up @@ -111,19 +111,18 @@ pub struct TestEnvironment<Context> {
pub test_model_parameters: testing::storage::model::Params,
}

impl<Storage, Stacks, Emily>
impl<Storage, Stacks>
TestEnvironment<
TestContext<
Storage,
WrappedMock<MockBitcoinInteract>, // We specify this explicitly to gain access to the mock client
Stacks,
Emily,
WrappedMock<MockEmilyInteract>, // We specify this explicitly to gain access to the mock client
>,
>
where
Storage: DbRead + DbWrite + Clone + Sync + Send + 'static,
Stacks: StacksInteract + Clone + Sync + Send + 'static,
Emily: EmilyInteract + Clone + Sync + Send + 'static,
{
/// Assert that a coordinator should be able to coordiante a signing round
pub async fn assert_should_be_able_to_coordinate_signing_rounds(mut self) {
Expand Down Expand Up @@ -168,6 +167,19 @@ where
})
.await;

self.context
.with_emily_client(|client| {
client
.expect_update_broadcasted_deposits()
.once()
.returning(|_, _| {
Box::pin(async {
Ok(emily_client::models::UpdateDepositsResponse { deposits: vec![] })
})
});
})
.await;

// Create a channel to log all transactions broadcasted by the coordinator.
// The receiver is created by this method but not used as it is held as a
// handle to ensure that the channel is alive until the end of the test.
Expand Down
20 changes: 17 additions & 3 deletions signer/src/transaction_coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use sha2::Digest;
use crate::bitcoin::utxo;
use crate::bitcoin::BitcoinInteract;
use crate::context::{messaging::SignerEvent, messaging::SignerSignal, Context};
use crate::emily_client::EmilyInteract;
use crate::error::Error;
use crate::keys::PrivateKey;
use crate::keys::PublicKey;
Expand Down Expand Up @@ -198,6 +199,13 @@ where
signer_public_keys: &BTreeSet<PublicKey>,
) -> Result<(), Error> {
let signer_btc_state = self.get_btc_state(&aggregate_key).await?;
let bitcoin_chain_tip_block: model::BitcoinBlockRef = self
.context
.get_storage()
.get_bitcoin_block(bitcoin_chain_tip)
.await?
.ok_or(Error::NoChainTip)?
.into();

let pending_requests = self
.get_pending_requests(
Expand All @@ -210,14 +218,20 @@ where

let transaction_package = pending_requests.construct_transactions()?;

for transaction in transaction_package {
for mut transaction in transaction_package {
self.sign_and_broadcast(
bitcoin_chain_tip,
aggregate_key,
signer_public_keys,
transaction,
&mut transaction,
)
.await?;

// TODO: need to add a retry (somewhere else?) if this fails?
self.context
.get_emily_client()
.update_broadcasted_deposits(&transaction, &bitcoin_chain_tip_block)
.await?;
}

Ok(())
Expand All @@ -244,7 +258,7 @@ where
bitcoin_chain_tip: &model::BitcoinBlockHash,
aggregate_key: PublicKey,
signer_public_keys: &BTreeSet<PublicKey>,
mut transaction: utxo::UnsignedTransaction<'_>,
transaction: &mut utxo::UnsignedTransaction<'_>,
) -> Result<(), Error> {
let mut coordinator_state_machine = wsts_state_machine::CoordinatorStateMachine::load(
&mut self.context.get_storage_mut(),
Expand Down

0 comments on commit 54387d0

Please sign in to comment.