Skip to content

Commit

Permalink
refactor: move the sbtc::rpc module over to the signer (#566)
Browse files Browse the repository at this point in the history
* refactor: move sbtc::rpc module over to the signer
* refactor: remove the unnecessary rpc module the sbtc crate
* refactor: remove unused error variants from the sbtc crate. Move the logging module over.
* chore: clean up local dependency features
  • Loading branch information
djordon authored Sep 25, 2024
1 parent 611ade3 commit 36a75c8
Show file tree
Hide file tree
Showing 28 changed files with 729 additions and 827 deletions.
6 changes: 1 addition & 5 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ lto = "thin"
codegen-units = 16

[workspace.dependencies]
sbtc = { path = "./sbtc" }
sbtc = { path = "./sbtc", default-features = false }
emily-handler = { version = "0.1.0", path = "./emily/handler" }
emily-client = { version = "0.1.0", path = "./.generated-sources/emily/client/rust" }

Expand Down
13 changes: 4 additions & 9 deletions sbtc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,24 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["testing"]
default = []
integration-tests = ["testing"]
testing = []
testing = ["dep:bitcoincore-rpc", "dep:bitcoincore-rpc-json"]

[dependencies]
bitcoin = { workspace = true, features = ["rand-std"] }
bitcoincore-rpc.workspace = true
bitcoincore-rpc-json.workspace = true
bitcoincore-rpc = { workspace = true, optional = true }
bitcoincore-rpc-json = { workspace = true, optional = true }
clarity.workspace = true
rand.workspace = true
serde.workspace = true
serde_json.workspace = true
stacks-common.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing-attributes.workspace = true
tracing-subscriber.workspace = true
url.workspace = true

[dependencies.secp256k1]
version = "0.29.0"
features = ["rand-std", "global-context"]

[dev-dependencies]
test-case = "3.1"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
92 changes: 3 additions & 89 deletions sbtc/src/deposits.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
//! This is the transaction analysis module
//!

use std::marker::PhantomData;

use bitcoin::opcodes::all as opcodes;
use bitcoin::script::PushBytesBuf;
use bitcoin::taproot::LeafVersion;
use bitcoin::taproot::NodeInfo;
use bitcoin::taproot::TaprootSpendInfo;
use bitcoin::Address;
use bitcoin::BlockHash;
use bitcoin::Network;
use bitcoin::OutPoint;
use bitcoin::ScriptBuf;
Expand All @@ -21,7 +18,6 @@ use secp256k1::SECP256K1;
use stacks_common::types::chainstate::STACKS_ADDRESS_ENCODED_SIZE;

use crate::error::Error;
use crate::rpc::BitcoinClient;

/// This is the length of the fixed portion of the deposit script, which
/// is:
Expand Down Expand Up @@ -114,55 +110,7 @@ pub struct DepositInfo {
pub lock_time: u64,
}

/// A full "deposit", containing the bitcoin transaction and a fully
/// extracted and verified `scriptPubKey` from one of the transaction's
/// UTXOs.
#[derive(Debug, Clone)]
pub struct Deposit {
/// The transaction spent to the signers as a deposit for sBTC.
pub tx: Transaction,
/// The deposit information included in one of the output
/// `scriptPubKey`s of the above transaction.
pub info: DepositInfo,
/// The block hash of the block that includes this transaction. If this
/// is `None` then this transaction is in the mempool. TODO(384): In
/// the case of a reorg, it's not clear what happens if this was
/// confirmed.
pub block_hash: Option<BlockHash>,
/// The number of confirmations deep from that chain tip of the bitcoin
/// block that includes this transaction. If `None` then this is in the
/// mempool. TODO(384): In the case of a reorg, it's not clear what
/// happens if this was confirmed.
pub confirmations: Option<u32>,
/// This is to make sure that this struct cannot be created without the
/// above invariants being upheld.
_phantom: PhantomData<()>,
}

impl CreateDepositRequest {
/// Validate this deposit request from the transaction.
///
/// This function fetches the transaction using the given client and
/// checks that the transaction has been submitted. The transaction
/// need not be confirmed.
pub async fn validate<C>(&self, client: &C) -> Result<Deposit, Error>
where
C: BitcoinClient,
{
// Fetch the transaction from either a block or from the mempool
let response = client
.get_tx(&self.outpoint.txid)
.await
.map_err(|e| Error::BitcoinClient(Box::new(e)))?;

Ok(Deposit {
info: self.validate_tx(&response.tx)?,
tx: response.tx,
block_hash: response.block_hash,
confirmations: response.confirmations,
_phantom: PhantomData,
})
}
/// Validate this deposit request.
///
/// This function checks the following
Expand Down Expand Up @@ -557,8 +505,6 @@ fn scriptint_parse(v: &[u8]) -> i64 {

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use bitcoin::hashes::Hash as _;
use bitcoin::AddressType;
use bitcoin::Txid;
Expand All @@ -568,38 +514,13 @@ mod tests {
use stacks_common::types::chainstate::StacksAddress;

use super::*;
use crate::rpc::GetTxResponse;
use crate::testing;
use crate::testing::deposits::TxSetup;

use test_case::test_case;

const CONTRACT_ADDRESS: &str = "ST1RQHF4VE5CZ6EK3MZPZVQBA0JVSMM9H5PMHMS1Y.contract-name";

struct DummyClient(pub HashMap<Txid, Transaction>);

impl DummyClient {
fn new_from_tx(tx: &Transaction) -> Self {
let mut map = HashMap::new();
map.insert(tx.compute_txid(), tx.clone());
Self(map)
}
}

impl BitcoinClient for DummyClient {
type Error = Error;
async fn get_tx(&self, txid: &Txid) -> Result<GetTxResponse, Self::Error> {
let tx = self.0.get(txid).cloned();

Ok(GetTxResponse {
tx: tx.ok_or(Error::InvalidDepositScript)?,
block_hash: None,
confirmations: None,
block_time: None,
})
}
}

/// A full reclaim script with a p2pk script at the end.
fn reclaim_p2pk(lock_time: i64) -> ScriptBuf {
ScriptBuf::builder()
Expand Down Expand Up @@ -833,10 +754,8 @@ mod tests {
assert!(reclaim.is_ok());
}

#[test_case(true ; "use client")]
#[test_case(false ; "no client")]
#[tokio::test]
async fn happy_path_tx_validation(use_client: bool) {
#[test]
fn happy_path_tx_validation() {
let max_fee: u64 = 15000;
let amount_sats = 500_000;
let lock_time = 150;
Expand All @@ -849,12 +768,7 @@ mod tests {
deposit_script: setup.deposit.deposit_script(),
};

let parsed = if use_client {
let client = DummyClient::new_from_tx(&setup.tx);
request.validate(&client).await.unwrap().info
} else {
request.validate_tx(&setup.tx).unwrap()
};
let parsed = request.validate_tx(&setup.tx).unwrap();

assert_eq!(parsed.outpoint, request.outpoint);
assert_eq!(parsed.deposit_script, request.deposit_script);
Expand Down
36 changes: 0 additions & 36 deletions sbtc/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,6 @@ use bitcoin::Txid;
/// Errors
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Error when the port is not provided
#[error("a port must be specified")]
PortRequired,
/// Error when parsing a URL
#[error("could not parse the provided URL: {0}")]
InvalidUrl(#[source] url::ParseError),
/// Error when using a BitcoinClient trait function
#[error("could not execute bitcoin client RPC call {0}")]
BitcoinClient(#[source] Box<dyn std::error::Error + Sync + Send + 'static>),
/// Error when creating an RPC client to bitcoin-core
#[error("could not create RPC client to {1}: {0}")]
BitcoinCoreRpcClient(#[source] bitcoincore_rpc::Error, String),
/// Returned when we could not decode the hex into a
/// bitcoin::Transaction.
#[error("failed to decode the provided hex into a transaction. txid: {1}. {0}")]
DecodeTx(#[source] bitcoin::consensus::encode::Error, Txid),
/// Could not deserialize the "blockchain.transaction.get" response
/// into a GetTxResponse.
#[error("failed to deserialize the blockchain.transaction.get response. txid: {1}. {0}")]
DeserializeGetTransaction(#[source] serde_json::Error, Txid),
/// Received an error in call to estimatesmartfee RPC call
#[error("failed to get fee estimate from bitcoin-core for target {1}. {0}")]
EstimateSmartFee(#[source] bitcoincore_rpc::Error, u16),
/// Received an error in response to estimatesmartfee RPC call
#[error("failed to get fee estimate from bitcoin-core for target {1}. {0:?}")]
EstimateSmartFeeResponse(Option<Vec<String>>, u16),
/// Received an error in response to getrawtransaction RPC call
#[error("failed to retrieve the raw transaction for txid {1} from bitcoin-core. {0}")]
GetTransactionBitcoinCore(#[source] bitcoincore_rpc::Error, Txid),
/// The end of the deposit script has a fixed format that is very
/// similar to a P2PK check_sig script, the script violated that format
#[error("script is CHECKSIG part of script")]
Expand All @@ -54,13 +25,6 @@ pub enum Error {
/// The reclaim script was invalid.
#[error("the reclaim script format was invalid")]
InvalidReclaimScript,
/// This should never happen.
#[error("could not serialize the type into JSON")]
JsonSerialize(#[source] serde_json::Error),
/// Failed to convert response into an Amount, which is unsigned and
/// bounded.
#[error("Could not convert float {1} into bitcoin::Amount: {0}")]
ParseAmount(#[source] bitcoin::amount::ParseAmountError, f64),
/// The reclaim script lock time was invalid
#[error("reclaim script lock time was either too large or non-minimal: {0}")]
ScriptNum(#[source] bitcoin::script::Error),
Expand Down
4 changes: 1 addition & 3 deletions sbtc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ use bitcoin::XOnlyPublicKey;

pub mod deposits;
pub mod error;
pub mod logging;
pub mod rpc;

#[cfg(feature = "testing")]
#[cfg(any(test, feature = "testing"))]
pub mod testing;

/// The x-coordinate public key with no known discrete logarithm.
Expand Down
Loading

0 comments on commit 36a75c8

Please sign in to comment.