Skip to content
This repository was archived by the owner on Dec 4, 2024. It is now read-only.

Commit aed33a2

Browse files
committed
assert on balances
1 parent 9be4f1f commit aed33a2

File tree

10 files changed

+190
-28
lines changed

10 files changed

+190
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ tracing = "0.1.37"
3535
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
3636
url = "2.4.1"
3737
wsts = "1.2"
38+
blockstack-core = { git = "https://github.com/stacks-network/stacks-blockchain/", branch = "master" }

devenv/integration/bin/test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
set -ueo >/dev/null
33

44
ENTRYPOINT_PATH="./integration/docker/entrypoint"
5+
CONFIG_PATH="./sbtc/docker/config.json"
56

67
project_name() {
78
local input="$1"
@@ -23,7 +24,7 @@ for filter in "${filters[@]}"; do
2324
./integration/bin/up docker-compose.yml $project
2425

2526
network=${project}_default
26-
id=$(docker run -td --network $network -v $ENTRYPOINT_PATH:/usr/local/bin/entrypoint -e PROJECT_NAME=$project testbed "$filter")
27+
id=$(docker run -td --network $network -v $ENTRYPOINT_PATH:/usr/local/bin/entrypoint -v $CONFIG_PATH:"/romeo/config.json" -e PROJECT_NAME=$project testbed "$filter")
2728
ids+=($id)
2829
echo with container id: $id
2930

romeo/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
anyhow.workspace = true
88
backoff = { workspace = true, features = ["tokio"] }
99
bdk = { workspace = true, features = ["rpc", "esplora", "use-esplora-async"] }
10-
blockstack-core = { git = "https://github.com/stacks-network/stacks-blockchain/", branch = "master" }
10+
blockstack-core.workspace = true
1111
clap = { workspace = true, features = ["derive"] }
1212
derivative = { workspace = true }
1313
futures.workspace = true
@@ -27,3 +27,4 @@ rs_merkle.workspace = true
2727
[dev-dependencies]
2828
reqwest = { workspace = true, features = ["json", "blocking"] }
2929
sbtc-cli = { path = "../sbtc-cli" }
30+
stacks-core = { path = "../stacks-core", features = ["test-utils"] }

romeo/src/stacks_client.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use blockstack_lib::{
1212
codec::StacksMessageCodec,
1313
core::CHAIN_ID_TESTNET,
1414
types::chainstate::StacksPrivateKey,
15+
util::hash::bytes_to_hex,
1516
vm::{
1617
types::{QualifiedContractIdentifier, StandardPrincipalData},
1718
ContractName,
@@ -21,7 +22,7 @@ use futures::Future;
2122
use rand::{distributions::Alphanumeric, thread_rng, Rng};
2223
use reqwest::{Request, RequestBuilder, Response, StatusCode};
2324
use serde::de::DeserializeOwned;
24-
use serde_json::Value;
25+
use serde_json::{json, Value};
2526
use stacks_core::{codec::Codec, uint::Uint256};
2627
use tokio::{
2728
sync::{Mutex, MutexGuard},
@@ -463,6 +464,46 @@ impl StacksClient {
463464
}
464465
}
465466

467+
impl StacksClient {
468+
/// Call read-only functions from smart contracts
469+
pub async fn call_read_only_fn<R>(
470+
&self,
471+
contract: QualifiedContractIdentifier,
472+
method: &str,
473+
sender: &str,
474+
args: Vec<blockstack_lib::vm::Value>,
475+
) -> anyhow::Result<R>
476+
where
477+
R: DeserializeOwned,
478+
{
479+
Ok(self
480+
.http_client
481+
.post(
482+
self.config
483+
.stacks_node_url
484+
.join(
485+
format!(
486+
"/v2/contracts/call-read/{}/{}/{}",
487+
contract.issuer, contract.name, method
488+
)
489+
.as_str(),
490+
)
491+
.unwrap(),
492+
)
493+
.json(&json!({
494+
"sender": sender,
495+
"arguments": args
496+
.iter()
497+
.map(|a| bytes_to_hex(&a.serialize_to_vec()))
498+
.collect::<Vec<_>>() }
499+
))
500+
.send()
501+
.await?
502+
.json()
503+
.await?)
504+
}
505+
}
506+
466507
#[derive(serde::Deserialize)]
467508
struct NonceInfo {
468509
possible_next_nonce: u64,

romeo/tests/tests/bitcoin_client.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
use std::{str::FromStr, thread::sleep, time::Duration};
1+
use std::{io::Cursor, str::FromStr, thread::sleep, time::Duration};
22

33
use bdk::{
44
bitcoin::{hash_types::Txid, Address, BlockHash},
55
bitcoincore_rpc::{Auth, Client as BClient, RpcApi},
66
};
7+
use blockstack_lib::{
8+
codec::StacksMessageCodec,
9+
types::chainstate::StacksAddress,
10+
util::hash::hex_bytes,
11+
vm::{
12+
types::{QualifiedContractIdentifier, StandardPrincipalData},
13+
ContractName, Value,
14+
},
15+
};
16+
use romeo::stacks_client::StacksClient;
717
use url::Url;
818

19+
/// devenv's service url
920
pub fn bitcoin_url() -> Url {
1021
Url::parse("http://bitcoin:18443").unwrap()
1122
}
1223

24+
/// devenv's service url
1325
pub fn electrs_url() -> Url {
1426
Url::parse("tcp://electrs:60401").unwrap()
1527
}
@@ -49,3 +61,35 @@ pub fn wait_for_tx_confirmation(
4961
sleep(Duration::from_secs(1));
5062
}
5163
}
64+
65+
pub async fn sbtc_balance(
66+
stacks_client: &StacksClient,
67+
deployer_address: StacksAddress,
68+
recipient_address: StacksAddress,
69+
contract_name: ContractName,
70+
) -> u128 {
71+
let res: serde_json::Value = stacks_client
72+
.call_read_only_fn(
73+
QualifiedContractIdentifier::new(
74+
StandardPrincipalData::from(deployer_address),
75+
contract_name,
76+
),
77+
"get-balance",
78+
recipient_address.to_string().as_str(),
79+
vec![StandardPrincipalData::from(recipient_address).into()],
80+
)
81+
.await
82+
.unwrap();
83+
84+
assert!(res["okay"].as_bool().unwrap());
85+
// request token balance from the asset contract.
86+
let bytes =
87+
hex_bytes(res["result"].as_str().unwrap().trim_start_matches("0x"))
88+
.unwrap();
89+
90+
let mut cursor = Cursor::new(&bytes);
91+
Value::consensus_deserialize(&mut cursor)
92+
.unwrap()
93+
.expect_result_ok()
94+
.expect_u128()
95+
}

romeo/tests/tests/deposit.rs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{str::FromStr, thread::sleep, time::Duration};
1+
use std::{str::FromStr, time::Duration};
22

33
use anyhow::Result;
44
use bdk::{
@@ -11,22 +11,25 @@ use bdk::{
1111
template::P2Wpkh,
1212
SyncOptions, Wallet,
1313
};
14+
use romeo::{config::Config, stacks_client::StacksClient};
1415
use sbtc_cli::commands::{
1516
broadcast::{broadcast_tx, BroadcastArgs},
1617
deposit::{build_deposit_tx, DepositArgs},
1718
};
19+
use stacks_core::address::StacksAddress;
20+
use tokio::time::sleep;
1821

1922
use super::{
2023
bitcoin_client::{
21-
bitcoin_url, client_new, electrs_url, mine_blocks,
24+
bitcoin_url, client_new, electrs_url, mine_blocks, sbtc_balance,
2225
wait_for_tx_confirmation,
2326
},
24-
KeyType::*,
27+
KeyType::{self, *},
2528
WALLETS,
2629
};
2730

28-
#[test]
29-
fn broadcast_deposit() -> Result<()> {
31+
#[tokio::test]
32+
async fn broadcast_deposit() -> Result<()> {
3033
let b_client = client_new(bitcoin_url().as_str(), "devnet", "devnet");
3134

3235
b_client
@@ -46,8 +49,6 @@ fn broadcast_deposit() -> Result<()> {
4649

4750
let electrum_url = electrs_url();
4851

49-
// suboptimal, replace once we have better events.
50-
// replace with b_client balance?
5152
{
5253
let blockchain =
5354
ElectrumBlockchain::from_config(&ElectrumBlockchainConfig {
@@ -76,34 +77,74 @@ fn broadcast_deposit() -> Result<()> {
7677
if balance.confirmed != 0 {
7778
break;
7879
}
79-
sleep(Duration::from_millis(1_000));
80+
sleep(Duration::from_millis(1_000)).await;
8081
}
8182
}
8283

8384
let amount = 10_000;
85+
let deployer_stacks_address = WALLETS[0][KeyType::Stacks].address;
86+
let recipient_stacks_address = WALLETS[1][KeyType::Stacks].address;
8487

8588
let tx = {
8689
let args = DepositArgs {
8790
node_url: electrum_url.clone(),
8891
wif: WALLETS[1][P2wpkh].wif.into(),
8992
network: bdk::bitcoin::Network::Regtest,
90-
recipient: WALLETS[1][Stacks].address.into(),
93+
recipient: recipient_stacks_address.into(),
9194
amount,
9295
sbtc_wallet: WALLETS[0][P2tr].address.into(),
9396
};
9497

9598
build_deposit_tx(&args).unwrap()
9699
};
97100

98-
broadcast_tx(&BroadcastArgs {
99-
node_url: electrum_url,
100-
tx: hex::encode(tx.serialize()),
101-
})
102-
.unwrap();
101+
let config = Config::from_path("config.json").unwrap();
103102

104-
let txid = tx.txid();
103+
// make sure config urls match devenv.
104+
let stacks_client =
105+
StacksClient::new(config.clone(), reqwest::Client::new());
105106

106-
wait_for_tx_confirmation(&b_client, &txid, 1);
107+
let deployer_address =
108+
StacksAddress::transmute_stacks_address(deployer_stacks_address);
109+
let recipient_address =
110+
StacksAddress::transmute_stacks_address(recipient_stacks_address);
111+
112+
// prior balance
113+
assert_eq!(
114+
sbtc_balance(
115+
&stacks_client,
116+
deployer_address,
117+
recipient_address,
118+
config.contract_name.clone()
119+
)
120+
.await,
121+
0
122+
);
123+
124+
// Sign, send and wait for confirmation.
125+
{
126+
broadcast_tx(&BroadcastArgs {
127+
node_url: electrum_url,
128+
tx: hex::encode(tx.serialize()),
129+
})
130+
.unwrap();
131+
132+
let txid = tx.txid();
133+
134+
wait_for_tx_confirmation(&b_client, &txid, 1);
135+
}
136+
137+
// assert on new sbtc token balance
138+
while sbtc_balance(
139+
&stacks_client,
140+
deployer_address,
141+
recipient_address,
142+
config.contract_name.clone(),
143+
)
144+
.await != amount as u128
145+
{
146+
sleep(Duration::from_secs(2)).await
147+
}
107148

108149
Ok(())
109150
}

romeo/tests/tests/stacks_client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use reqwest::blocking::Client;
22
use url::Url;
33

4+
/// devenv's service url
45
pub fn stacks_url() -> Url {
56
Url::parse("http://stacks:20443").unwrap()
67
}

sbtc-cli/src/main.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! and interact with the Bitcoin and Stacks networks.
88
use std::io::stdout;
99

10-
use bdk::bitcoin::psbt::serialize::Serialize;
10+
use bdk::bitcoin::{psbt::serialize::Serialize, Transaction};
1111
use clap::{Parser, Subcommand};
1212
use sbtc_cli::commands::{
1313
broadcast::{broadcast_tx, BroadcastArgs},
@@ -31,19 +31,23 @@ enum Command {
3131
GenerateFrom(GenerateArgs),
3232
}
3333

34+
fn to_stdout_pretty(txn: Transaction) -> serde_json::Result<()> {
35+
serde_json::to_writer_pretty(
36+
stdout(),
37+
&utils::TransactionData {
38+
id: txn.txid().to_string(),
39+
hex: hex::encode(txn.serialize()),
40+
},
41+
)
42+
}
43+
3444
fn main() -> Result<(), anyhow::Error> {
3545
let args = Cli::parse();
3646

3747
match args.command {
3848
Command::Deposit(deposit_args) => build_deposit_tx(&deposit_args)
3949
.and_then(|t| {
40-
serde_json::to_writer_pretty(
41-
stdout(),
42-
&utils::TransactionData {
43-
id: t.txid().to_string(),
44-
hex: hex::encode(t.serialize()),
45-
},
46-
)?;
50+
to_stdout_pretty(t)?;
4751
Ok(())
4852
}),
4953
Command::Withdraw(withdrawal_args) => {

stacks-core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ serde = { workspace = true, features = ["derive"] }
2727
sha2.workspace = true
2828
strum = { workspace = true, features = ["derive"] }
2929
thiserror.workspace = true
30+
blockstack-core.workspace = true
3031

3132
[dev-dependencies]
3233
hex.workspace = true
3334
rand.workspace = true
35+
36+
[features]
37+
test-utils = []

stacks-core/src/address.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ use std::{
66
use bdk::bitcoin::blockdata::{
77
opcodes::all::OP_CHECKMULTISIG, script::Builder,
88
};
9+
use blockstack_lib::{
10+
codec::StacksMessageCodec,
11+
types::chainstate::StacksAddress as CStacksAddress,
12+
};
913
use serde::Serialize;
1014
use strum::{EnumIter, FromRepr};
1115

@@ -100,6 +104,16 @@ impl StacksAddress {
100104
pub fn from_public_key(version: AddressVersion, key: &PublicKey) -> Self {
101105
Self::p2pkh(version, key)
102106
}
107+
108+
/// Transmute to stacks-blockchain type
109+
pub fn into_native_stacks_address(self) -> CStacksAddress {
110+
use std::io::Cursor;
111+
112+
CStacksAddress::consensus_deserialize(&mut Cursor::new(
113+
self.serialize_to_vec(),
114+
))
115+
.unwrap()
116+
}
103117
}
104118

105119
impl Codec for StacksAddress {
@@ -226,6 +240,16 @@ fn hash_p2wsh<'a>(
226240
Hash160Hasher::new(&buff)
227241
}
228242

243+
#[cfg(feature = "test-utils")]
244+
impl StacksAddress {
245+
/// copy StacksAddress as a native stacks-blockchain address type
246+
pub fn transmute_stacks_address(address: &str) -> CStacksAddress {
247+
StacksAddress::try_from(address)
248+
.unwrap()
249+
.into_native_stacks_address()
250+
}
251+
}
252+
229253
#[cfg(test)]
230254
mod tests {
231255
use super::*;

0 commit comments

Comments
 (0)