Skip to content

Commit 0ceea5c

Browse files
committed
Add tests for Elements-specific functionality
Confidentiality, asset issuance and signed blocks (dynafed);
1 parent f82b138 commit 0ceea5c

File tree

2 files changed

+194
-17
lines changed

2 files changed

+194
-17
lines changed

tests/common.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,32 @@ impl TestRunner {
213213
self.sync()?;
214214
Ok(txid)
215215
}
216+
217+
#[cfg(feature = "liquid")]
218+
pub fn send_asset(
219+
&mut self,
220+
addr: &Address,
221+
amount: bitcoin::Amount,
222+
assetid: elements::AssetId,
223+
) -> Result<Txid> {
224+
let txid = self.node_client().call(
225+
"sendtoaddress",
226+
&[
227+
addr.to_string().into(),
228+
json!(amount.as_btc()),
229+
Value::Null,
230+
Value::Null,
231+
Value::Null,
232+
Value::Null,
233+
Value::Null,
234+
Value::Null,
235+
Value::Null,
236+
json!(assetid),
237+
],
238+
)?;
239+
self.sync()?;
240+
Ok(txid)
241+
}
216242
}
217243

218244
pub fn init_rest_tester() -> Result<(rest::Handle, net::SocketAddr, TestRunner)> {

tests/rest.rs

Lines changed: 168 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use bitcoind::bitcoincore_rpc::RpcApi;
1+
use bitcoin::hashes::{sha256, Hash};
2+
use bitcoind::bitcoincore_rpc::{self, RpcApi};
23
use serde_json::Value;
34
use std::collections::HashSet;
45

@@ -24,23 +25,8 @@ fn test_rest() -> Result<()> {
2425
.into_string()?)
2526
};
2627

27-
let newaddress = || -> Result<Address> {
28-
let addr = tester.node_client().call::<Address>("getnewaddress", &[])?;
29-
// On Liquid, return the unconfidential address, so that the tests below work
30-
// on both Bitcoin and Liquid mode. The Liquid-specific functionality, including
31-
// confidentially, is tested separately below.
32-
#[cfg(feature = "liquid")]
33-
let addr = {
34-
let mut info = tester
35-
.node_client()
36-
.call::<Value>("getaddressinfo", &[addr.to_string().into()])?;
37-
serde_json::from_value(info["unconfidential"].take())?
38-
};
39-
Ok(addr)
40-
};
41-
4228
// Send transaction and confirm it
43-
let addr1 = newaddress()?;
29+
let addr1 = newaddress(tester.node_client())?;
4430
let txid1_confirmed = tester.send(&addr1, "1.19123 BTC".parse().unwrap())?;
4531
tester.mine()?;
4632

@@ -183,6 +169,171 @@ fn test_rest() -> Result<()> {
183169
tester.mine()?;
184170
assert_eq!(get_json("/mempool")?["count"].as_u64(), Some(0));
185171

172+
// Elements-only tests
173+
#[cfg(feature = "liquid")]
174+
{
175+
// Test confidential transactions
176+
{
177+
let (c_addr, uc_addr) = elements_newaddress(tester.node_client())?;
178+
let txid = tester.send(&c_addr, "3.5 BTC".parse().unwrap())?;
179+
tester.mine()?;
180+
181+
let tx = get_json(&format!("/tx/{}", txid))?;
182+
log::debug!("blinded tx = {:#?}", tx);
183+
assert_eq!(tx["status"]["confirmed"].as_bool(), Some(true));
184+
let outs = tx["vout"].as_array().expect("array of outs");
185+
let vout = outs
186+
.iter()
187+
.find(|vout| vout["scriptpubkey_address"].as_str() == Some(&uc_addr.to_string()))
188+
.expect("our output");
189+
assert!(vout["value"].is_null());
190+
assert!(vout["valuecommitment"].is_string());
191+
assert!(vout["assetcommitment"].is_string());
192+
}
193+
194+
// Test blinded asset issuance
195+
{
196+
let contract_hash = sha256::Hash::hash(&[0x11, 0x22, 0x33, 0x44]).to_string();
197+
let contract_hash = contract_hash.as_str();
198+
let issuance = tester.node_client().call::<Value>(
199+
"issueasset",
200+
&[1.5.into(), 0.into(), true.into(), contract_hash.into()],
201+
)?;
202+
tester.mine()?;
203+
204+
let assetid = issuance["asset"].as_str().expect("asset id");
205+
let issuance_txid = issuance["txid"].as_str().expect("issuance txid");
206+
207+
// Test GET /tx/:txid for issuance tx
208+
let asset = get_json(&format!("/asset/{}", assetid))?;
209+
let stats = &asset["chain_stats"];
210+
assert_eq!(asset["asset_id"].as_str(), Some(assetid));
211+
assert_eq!(asset["issuance_txin"]["txid"].as_str(), Some(issuance_txid));
212+
assert_eq!(asset["contract_hash"].as_str(), Some(contract_hash));
213+
assert_eq!(asset["status"]["confirmed"].as_bool(), Some(true));
214+
assert_eq!(stats["issuance_count"].as_u64(), Some(1));
215+
assert_eq!(stats["has_blinded_issuances"].as_bool(), Some(true));
216+
assert_eq!(stats["issued_amount"].as_u64(), Some(0));
217+
218+
// Test GET /asset/:assetid
219+
let issuance_tx = get_json(&format!("/tx/{}", issuance_txid))?;
220+
let issuance_in_index = asset["issuance_txin"]["vin"].as_u64().unwrap();
221+
let issuance_in = &issuance_tx["vin"][issuance_in_index as usize];
222+
let issuance_data = &issuance_in["issuance"];
223+
assert_eq!(issuance_data["asset_id"].as_str(), Some(assetid));
224+
assert_eq!(issuance_data["is_reissuance"].as_bool(), Some(false));
225+
assert_eq!(issuance_data["contract_hash"].as_str(), Some(contract_hash));
226+
assert!(issuance_data["assetamount"].is_null());
227+
assert!(issuance_data["assetamountcommitment"].is_string());
228+
}
229+
230+
// Test unblinded asset issuance
231+
{
232+
let issuance = tester
233+
.node_client()
234+
.call::<Value>("issueasset", &[1.5.into(), 0.into(), false.into()])?;
235+
tester.mine()?;
236+
let assetid = issuance["asset"].as_str().expect("asset id");
237+
let issuance_txid = issuance["txid"].as_str().expect("issuance txid");
238+
239+
// Test GET /tx/:txid for issuance tx
240+
let asset = get_json(&format!("/asset/{}", assetid))?;
241+
let stats = &asset["chain_stats"];
242+
assert_eq!(stats["has_blinded_issuances"].as_bool(), Some(false));
243+
assert_eq!(stats["issued_amount"].as_u64(), Some(150000000));
244+
245+
// Test GET /asset/:assetid
246+
let issuance_tx = get_json(&format!("/tx/{}", issuance_txid))?;
247+
let issuance_in_index = asset["issuance_txin"]["vin"].as_u64().unwrap();
248+
let issuance_in = &issuance_tx["vin"][issuance_in_index as usize];
249+
let issuance_data = &issuance_in["issuance"];
250+
assert_eq!(issuance_data["assetamount"].as_u64(), Some(150000000));
251+
assert!(issuance_data["assetamountcommitment"].is_null());
252+
}
253+
254+
// Test a regular (non-issuance) transaction sending an issued asset
255+
{
256+
let issuance = tester
257+
.node_client()
258+
.call::<Value>("issueasset", &[1.5.into(), 0.into(), false.into()])?;
259+
let assetid = issuance["asset"].as_str().expect("asset id");
260+
tester.mine()?;
261+
262+
let (c_addr, uc_addr) = elements_newaddress(tester.node_client())?;
263+
264+
// With blinding off
265+
let txid = tester.send_asset(
266+
&uc_addr,
267+
"0.3 BTC".parse().unwrap(), // not actually BTC, but this is what Amount expects
268+
assetid.parse().unwrap(),
269+
)?;
270+
let tx = get_json(&format!("/tx/{}", txid))?;
271+
let outs = tx["vout"].as_array().expect("array of outs");
272+
let vout = outs
273+
.iter()
274+
.find(|vout| vout["scriptpubkey_address"].as_str() == Some(&uc_addr.to_string()))
275+
.expect("our output");
276+
assert_eq!(vout["asset"].as_str(), Some(assetid));
277+
assert_eq!(vout["value"].as_u64(), Some(30000000));
278+
279+
// With blinding on
280+
let txid = tester.send_asset(
281+
&c_addr,
282+
"0.3 BTC".parse().unwrap(),
283+
assetid.parse().unwrap(),
284+
)?;
285+
let tx = get_json(&format!("/tx/{}", txid))?;
286+
let outs = tx["vout"].as_array().expect("array of outs");
287+
let vout = outs
288+
.iter()
289+
.find(|vout| vout["scriptpubkey_address"].as_str() == Some(&uc_addr.to_string()))
290+
.expect("our output");
291+
assert!(vout["asset"].is_null());
292+
assert!(vout["value"].is_null());
293+
assert!(vout["assetcommitment"].is_string());
294+
assert!(vout["valuecommitment"].is_string());
295+
}
296+
297+
// Test GET /block/:hash
298+
{
299+
let bestblockhash = get_plain("/blocks/tip/hash")?;
300+
let block = get_json(&format!("/block/{}", bestblockhash))?;
301+
302+
// No PoW-related stuff
303+
assert!(block["bits"].is_null());
304+
assert!(block["nonce"].is_null());
305+
assert!(block["difficulty"].is_null());
306+
307+
// Dynamic Federations (dynafed) fields
308+
assert!(block["ext"]["current"]["signblockscript"].is_string());
309+
assert!(block["ext"]["current"]["fedpegscript"].is_string());
310+
assert!(block["ext"]["current"]["fedpeg_program"].is_string());
311+
assert!(block["ext"]["current"]["signblock_witness_limit"].is_u64());
312+
assert!(block["ext"]["current"]["extension_space"].is_array());
313+
assert!(block["ext"]["proposed"].is_object());
314+
assert!(block["ext"]["signblock_witness"].is_array());
315+
}
316+
}
317+
186318
rest_handle.stop();
187319
Ok(())
188320
}
321+
322+
fn newaddress(client: &bitcoincore_rpc::Client) -> Result<Address> {
323+
#[cfg(not(feature = "liquid"))]
324+
return Ok(client.get_new_address(None, None)?);
325+
326+
// Return the unconfidential address on Liquid, so that the tests below work
327+
// on both Bitcoin and Liquid mode. The Liquid-specific functionality, including
328+
// confidentially, is tested separately below.
329+
#[cfg(feature = "liquid")]
330+
return Ok(elements_newaddress(client)?.1);
331+
}
332+
333+
#[cfg(feature = "liquid")]
334+
fn elements_newaddress(client: &bitcoincore_rpc::Client) -> Result<(Address, Address)> {
335+
let c_addr = client.call::<Address>("getnewaddress", &[])?;
336+
let mut info = client.call::<Value>("getaddressinfo", &[c_addr.to_string().into()])?;
337+
let uc_addr = serde_json::from_value(info["unconfidential"].take())?;
338+
Ok((c_addr, uc_addr))
339+
}

0 commit comments

Comments
 (0)