From 85ec80346699dca0ae1e383c5cd6793598d6404e Mon Sep 17 00:00:00 2001 From: AshtonStephens Date: Thu, 10 Oct 2024 16:09:44 +0100 Subject: [PATCH] feat: Use deposit script parameters in deposit resource --- Cargo.lock | 3 + Cargo.toml | 4 +- emily/handler/Cargo.toml | 4 ++ emily/handler/src/api/handlers/deposit.rs | 68 ++++++++++++++++++++++- emily/handler/src/common/error.rs | 11 ++++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5eaee6c..e30fc7e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,6 +1798,7 @@ dependencies = [ "aws-config", "aws-sdk-dynamodb", "base64 0.22.1", + "bitcoin", "clap", "config 0.11.0", "fake", @@ -1806,9 +1807,11 @@ dependencies = [ "openssl", "rand 0.8.5", "reqwest 0.11.27", + "sbtc", "serde 1.0.203", "serde_dynamo", "serde_json", + "test-case", "thiserror", "time 0.3.36", "tokio", diff --git a/Cargo.toml b/Cargo.toml index be0ee0b7..dea2de66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,8 +71,8 @@ wsts = "9.2.0" zeromq = { version = "0.4.0", default-features = false, features = ["tokio-runtime", "all-transport"] } hex = "0.4.3" libp2p = { version = "0.54.1", features = [ - "macros", "kad", "noise", "ping", "tcp", - "tokio", "yamux", "mdns", "quic", "gossipsub", + "macros", "kad", "noise", "ping", "tcp", + "tokio", "yamux", "mdns", "quic", "gossipsub", "relay", "identify", "tls", "dns", "autonat" ] } diff --git a/emily/handler/Cargo.toml b/emily/handler/Cargo.toml index 2cde14d3..f1d4179f 100644 --- a/emily/handler/Cargo.toml +++ b/emily/handler/Cargo.toml @@ -19,10 +19,12 @@ populate = ["testing"] aws-config.workspace = true aws-sdk-dynamodb.workspace = true base64.workspace = true +bitcoin.workspace = true config.workspace = true clap.workspace = true openssl.workspace = true reqwest.workspace = true +sbtc.workspace = true serde = { workspace = true, features = ["derive"] } serde_dynamo.workspace = true serde_json.workspace = true @@ -42,3 +44,5 @@ fake = { version = "2.9.2", features = ["derive", "time"], optional = true } rand = "0.8" mockito = "0.28" mockall = "0.10" +sbtc = { workspace = true, features = ["testing"] } +test-case = "3.1" diff --git a/emily/handler/src/api/handlers/deposit.rs b/emily/handler/src/api/handlers/deposit.rs index 5051be7a..6d77911a 100644 --- a/emily/handler/src/api/handlers/deposit.rs +++ b/emily/handler/src/api/handlers/deposit.rs @@ -6,6 +6,7 @@ use crate::api::models::deposit::responses::{ use crate::database::entries::StatusEntry; use warp::reply::{json, with_status, Reply}; +use bitcoin::ScriptBuf; use warp::http::StatusCode; use crate::api::models::deposit::{Deposit, DepositInfo}; @@ -208,13 +209,22 @@ pub async fn create_deposit( let stacks_block_hash: String = chaintip.key.hash; let stacks_block_height: u64 = chaintip.key.height; let status = Status::Pending; + + // Get parameters from scripts. + let script_parameters = + scripts_to_resource_parameters(&body.deposit_script, &body.reclaim_script)?; + // Make table entry. let deposit_entry: DepositEntry = DepositEntry { key: DepositEntryKey { bitcoin_txid: body.bitcoin_txid, bitcoin_tx_output_index: body.bitcoin_tx_output_index, }, - parameters: DepositParametersEntry { ..Default::default() }, + recipient: script_parameters.recipient, + parameters: DepositParametersEntry { + max_fee: script_parameters.max_fee, + lock_time: script_parameters.lock_time, + }, history: vec![DepositEvent { status: StatusEntry::Pending, message: "Just received deposit".to_string(), @@ -242,6 +252,35 @@ pub async fn create_deposit( .map_or_else(Reply::into_response, Reply::into_response) } +/// Parameters from the deposit and reclaim scripts. +struct ScriptParameters { + max_fee: u64, + recipient: String, + lock_time: u64, +} + +/// Convert scripts to resource parameters. +/// +/// This function is used to convert the deposit and reclaim scripts into the +/// parameters that are stored in the database. +fn scripts_to_resource_parameters( + deposit_script: &String, + reclaim_script: &String, +) -> Result { + let deposit_script_buf = ScriptBuf::from_hex(deposit_script)?; + let deposit_script_inputs = sbtc::deposits::DepositScriptInputs::parse(&deposit_script_buf)?; + + let reclaim_script_buf = ScriptBuf::from_hex(reclaim_script)?; + let reclaim_script_inputs = sbtc::deposits::ReclaimScriptInputs::parse(&reclaim_script_buf)?; + + // TODO: Convert the recipient into a hex encoding of the recipient. + Ok(ScriptParameters { + max_fee: deposit_script_inputs.max_fee, + recipient: deposit_script_inputs.recipient.to_string(), + lock_time: reclaim_script_inputs.lock_time(), + }) +} + /// Update deposits handler. #[utoipa::path( put, @@ -300,3 +339,30 @@ pub async fn update_deposits( } // TODO(393): Add handler unit tests. + +// Test module +#[cfg(test)] +mod tests { + + use super::*; + use sbtc::testing::{self, deposits::TxSetup}; + use test_case::test_case; + + #[test_case(15000, 500_000, 150; "All parameters are normal numbers")] + #[test_case(0, 0, 0; "All parameters are zeros")] + fn test_scripts_to_resource_parameters(max_fee: u64, amount_sats: u64, lock_time: u32) { + let setup: TxSetup = testing::deposits::tx_setup(lock_time as i64, max_fee, amount_sats); + + let deposit_script = setup.deposit.deposit_script().to_hex_string(); + let reclaim_script = setup.reclaim.reclaim_script().to_hex_string(); + + let script_parameters: ScriptParameters = + scripts_to_resource_parameters(&deposit_script, &reclaim_script).unwrap(); + + assert_eq!(script_parameters.max_fee, max_fee); + assert_eq!(script_parameters.lock_time, lock_time as u64); + + // TODO: Test the recipient with an input value. + assert!(script_parameters.recipient.len() > 0); + } +} diff --git a/emily/handler/src/common/error.rs b/emily/handler/src/common/error.rs index f05660ab..f650fb03 100644 --- a/emily/handler/src/common/error.rs +++ b/emily/handler/src/common/error.rs @@ -10,6 +10,7 @@ use aws_sdk_dynamodb::{ update_item::UpdateItemError, }, }; +use bitcoin::hex::HexToBytesError; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -247,6 +248,16 @@ impl From for Error { Error::Debug(format!("serde_dynamo::Error - {err:?}")) } } +impl From for Error { + fn from(err: HexToBytesError) -> Self { + Error::Debug(format!("HexToBytesError - {err:?}")) + } +} +impl From for Error { + fn from(err: sbtc::error::Error) -> Self { + Error::Debug(format!("sbtc::error::Error - {err:?}")) + } +} /// Structure representing an error response /// This is used to serialize error messages in HTTP responses