-
-
Save al-f4lc0n/25bd18b41484fab5e3a121df236bbc7d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{thread, time::Duration}; | |
| use bitcoin::{ absolute, transaction::Version, Amount, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut }; | |
| use bitcoin::consensus::encode::serialize_hex; | |
| use bitcoincore_rpc::{json, Client as BitcoinClient, RpcApi as _}; | |
| use clarity::types::{chainstate::StacksAddress, Address as _}; | |
| use clarity::vm::types::{PrincipalData, StandardPrincipalData}; | |
| use emily_client::apis::configuration::{ApiKey, Configuration}; | |
| use emily_client::apis::deposit_api; | |
| use emily_client::models::CreateDepositRequestBody; | |
| use sbtc::deposits::{DepositScriptInputs, ReclaimScriptInputs}; | |
| use signer::config::Settings; | |
| use signer::keys::{PrivateKey, PublicKey, SignerScriptPubKey}; | |
| use signer::stacks::api::{StacksClient, StacksInteract}; | |
| /* secret key: | |
| spawn knee orchard patrol merge forget dust position daring short bridge elevator attitude leopard opera appear auction limit magic hover tunnel museum quantum manual | |
| */ | |
| const TEST_AUDITOR_STACKS_ADDR: &str = "ST2BEV097EV2R9ZMFRMRT904QB5RFYMA0683TC111"; | |
| const TEST_CONFIG_PATH: &str = "signer/src/config/default.toml"; | |
| const TEST_DEPLOYER_STACKS_ADDR: &str = "SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS"; | |
| const TEST_RECIPIENT_STACKS_ADDR: &str = "ST3497E9JFQ7KB9VEHAZRWYKF3296WQZEXBPXG193"; | |
| const TEST_DEPOSIT_MAX_FEE: u64 = 100000; | |
| const TEST_DEPOSIT_LOCK_TIME: u32 = 10; | |
| const TEST_BTC_TX_FEE: u64 = 200; | |
| const SATOSHI_PER_BTC: u64 = 100000000; | |
| #[tokio::main] | |
| async fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| wait_sbtc_deployment().await?; | |
| println!("┏ donation 10 BTC to aggregate signer BTC wallet"); | |
| donation_to_aggregate_key(10 * SATOSHI_PER_BTC).await?; | |
| println!("┗ end"); | |
| println!("┏ deposit 1 sBTC to aggregate signer Stacks wallet"); | |
| deposit_sbtc_to( | |
| TEST_AUDITOR_STACKS_ADDR, | |
| SATOSHI_PER_BTC, | |
| ).await?; | |
| println!("┗ end"); | |
| println!("┏ deposit 1 sBTC to test auditor Stacks wallet"); | |
| deposit_sbtc_to( | |
| TEST_AUDITOR_STACKS_ADDR, | |
| SATOSHI_PER_BTC, | |
| ).await?; | |
| println!("┗ end"); | |
| Ok(()) | |
| } | |
| async fn wait_sbtc_deployment() -> Result<(), Box<dyn std::error::Error>> { | |
| println!("┏ waiting for sBTC contract deployment"); | |
| loop { | |
| println!("┃ ..."); | |
| thread::sleep(Duration::from_secs(5)); | |
| match get_current_aggregate_key().await { | |
| Err(_) => {} | |
| Ok(_) => break | |
| } | |
| } | |
| println!("┗ sBTC contract has been deployed"); | |
| Ok(()) | |
| } | |
| fn get_emily_client_config() -> Result<Configuration, Box<dyn std::error::Error>> { | |
| let settings = Settings::new(Some(TEST_CONFIG_PATH))?; | |
| let emily_api_endpoint = settings.emily.endpoints.first().unwrap().clone(); | |
| Ok(Configuration { | |
| base_path: emily_api_endpoint.to_string().trim_end_matches("/").to_string(), | |
| api_key: Some(ApiKey { | |
| prefix: None, | |
| key: emily_api_endpoint.username().to_string(), | |
| }), | |
| ..Default::default() | |
| }) | |
| } | |
| fn get_stacks_client() -> Result<StacksClient, Box<dyn std::error::Error>> { | |
| let settings = Settings::new(Some(TEST_CONFIG_PATH))?; | |
| let url = settings | |
| .stacks | |
| .endpoints | |
| .first() | |
| .expect("No Stacks endpoints configured"); | |
| Ok(StacksClient::new(url.clone()).expect("Failed to create Stacks client")) | |
| } | |
| fn get_btc_client() -> Result<BitcoinClient, Box<dyn std::error::Error>> { | |
| let settings = Settings::new(Some(TEST_CONFIG_PATH))?; | |
| let bitcoin_url = format!( | |
| "{}wallet/depositor", | |
| settings | |
| .bitcoin | |
| .rpc_endpoints | |
| .first() | |
| .expect("No Bitcoin RPC endpoints configured") | |
| ); | |
| let btc_client = BitcoinClient::new( | |
| &bitcoin_url, | |
| bitcoincore_rpc::Auth::UserPass("devnet".into(), "devnet".into()), | |
| ).expect("Failed to create Bitcoin RPC client"); | |
| Ok(btc_client) | |
| } | |
| async fn get_current_aggregate_key() -> Result<PublicKey, Box<dyn std::error::Error>> { | |
| let stacks_client = get_stacks_client()?; | |
| let deployer = PrincipalData::parse_standard_principal(&TEST_DEPLOYER_STACKS_ADDR) | |
| .map(StacksAddress::from)?; | |
| match stacks_client.get_current_signers_aggregate_key(&deployer).await? { | |
| Some(aggregate_key) => Ok(aggregate_key), | |
| _ => Err(Box::from("no aggregate key")), | |
| } | |
| } | |
| async fn donation_to_aggregate_key( | |
| amount: u64, | |
| ) -> Result<(), Box<dyn std::error::Error>> { | |
| let aggregate_key = get_current_aggregate_key().await?; | |
| let btc_client = get_btc_client()?; | |
| let unsigned_tx = get_transaction_from_btc_wallet( | |
| &btc_client, | |
| TxOut { | |
| value: Amount::from_sat(amount), | |
| script_pubkey: aggregate_key.signers_script_pubkey(), | |
| }, | |
| Amount::from_sat(TEST_BTC_TX_FEE), | |
| )?; | |
| let signed_tx = btc_client.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)?; | |
| let txid = btc_client.send_raw_transaction(&signed_tx.hex)?; | |
| println!("┃ btc tx sent: {:?}", txid); | |
| Ok(()) | |
| } | |
| async fn deposit_sbtc_to( | |
| recipient: &str, | |
| amount: u64, | |
| ) -> Result<(), Box<dyn std::error::Error>> { | |
| let aggregate_key = get_current_aggregate_key().await?; | |
| let deposit_script = DepositScriptInputs { | |
| signers_public_key: aggregate_key.into(), | |
| max_fee: TEST_DEPOSIT_MAX_FEE, | |
| recipient: PrincipalData::Standard(StandardPrincipalData::from( | |
| StacksAddress::from_string(recipient).unwrap(), | |
| )), | |
| }; | |
| let reclaim_script = ReclaimScriptInputs::try_new(TEST_DEPOSIT_LOCK_TIME, ScriptBuf::new())?; | |
| let btc_client = get_btc_client()?; | |
| let unsigned_tx = get_transaction_from_btc_wallet( | |
| &btc_client, | |
| TxOut { | |
| value: Amount::from_sat(amount), | |
| script_pubkey: sbtc::deposits::to_script_pubkey( | |
| deposit_script.deposit_script(), | |
| reclaim_script.reclaim_script(), | |
| ), | |
| }, | |
| Amount::from_sat(TEST_BTC_TX_FEE), | |
| )?; | |
| let signed_tx = btc_client.sign_raw_transaction_with_wallet(&unsigned_tx, None, None)?; | |
| let txid = btc_client.send_raw_transaction(&signed_tx.hex)?; | |
| println!("┃ btc tx sent: {:?}", txid); | |
| let emily_client_config = get_emily_client_config()?; | |
| let emily_deposit = deposit_api::create_deposit( | |
| &emily_client_config, | |
| CreateDepositRequestBody { | |
| bitcoin_tx_output_index: 0, | |
| bitcoin_txid: txid.to_string(), | |
| deposit_script: deposit_script.deposit_script().to_hex_string(), | |
| reclaim_script: reclaim_script.reclaim_script().to_hex_string(), | |
| transaction_hex: serialize_hex(&unsigned_tx), | |
| }, | |
| ).await?; | |
| println!("┃ deposit request created: {:?}", emily_deposit); | |
| Ok(()) | |
| } | |
| fn get_transaction_from_btc_wallet( | |
| bitcoin_client: &BitcoinClient, | |
| tx_out: TxOut, | |
| fee: Amount, | |
| ) -> Result<Transaction, Box<dyn std::error::Error>> { | |
| let amount = tx_out.value; | |
| let opts = json::ListUnspentQueryOptions { | |
| minimum_amount: Some(amount + fee), | |
| ..Default::default() | |
| }; | |
| let unspent = bitcoin_client | |
| .list_unspent(Some(1), None, None, None, Some(opts))? | |
| .into_iter() | |
| .next() | |
| .unwrap(); | |
| Ok(Transaction { | |
| input: vec![TxIn { | |
| previous_output: OutPoint { | |
| txid: unspent.txid, | |
| vout: unspent.vout, | |
| }, | |
| script_sig: Default::default(), | |
| sequence: Sequence::ZERO, | |
| witness: Default::default(), | |
| }], | |
| output: vec![ | |
| tx_out, | |
| TxOut { | |
| value: unspent.amount - amount - fee, | |
| script_pubkey: unspent.script_pub_key, | |
| }, | |
| ], | |
| version: Version::TWO, | |
| lock_time: absolute::LockTime::ZERO, | |
| }) | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An insignificant patch.