use blockbook::{ hashes::{self, hex::FromHex}, websocket::{self, Info}, Address, AddressBlockVout, AddressFilter, AddressInfo, AddressInfoBasic, AddressInfoPaging, Amount, Asset, Block, BlockHash, BlockTransaction, BlockVin, BlockVout, Chain, Currency, Height, LockTime, NetworkUnchecked, OpReturn, ScriptBuf, ScriptPubKey, ScriptPubKeyType, ScriptSig, Sequence, Ticker, TickersList, Time, Token, Transaction, TransactionSpecific, Tx, TxDetail, Txid, Utxo, Version, Vin, VinSpecific, Vout, VoutSpecific, Witness, XPubInfo, XPubInfoBasic, }; use std::str::FromStr; async fn blockbook() -> blockbook::Client { blockbook::Client::new( format!("https://{}", std::env::var("BLOCKBOOK_SERVER").unwrap()) .parse() .unwrap(), ) .await .unwrap() } async fn blockbook_ws() -> websocket::Client { websocket::Client::new( format!( "wss://{}/websocket", std::env::var("BLOCKBOOK_SERVER").unwrap() ) .parse() .unwrap(), ) .await .unwrap() } #[ignore] #[tokio::test] async fn test_status() { let status = blockbook().await.status().await.unwrap(); assert_eq!(status.blockbook.coin, Asset::Bitcoin); assert_eq!(status.blockbook.decimals, 8); assert_eq!(status.backend.chain, Chain::Main); assert_eq!(status.backend.protocol_version, "70016"); } #[ignore] #[tokio::test] async fn test_block_hash() { let hash = blockbook() .await .block_hash(&Height::from_consensus(763_672).unwrap()) .await .unwrap(); assert_eq!( hash.as_ref(), [ 0x4e, 0x67, 0xa2, 0x6f, 0x64, 0xcf, 0xbe, 0xef, 0x2c, 0x7f, 0x6a, 0x83, 0xd9, 0xa8, 0x25, 0x51, 0x76, 0x25, 0x51, 0xcb, 0x1e, 0xbe, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ], ); } #[allow(clippy::too_many_lines)] #[ignore] #[tokio::test] async fn test_tx() { let txid = "b0714235addd08daf83b979aa35cc9ed7558efb8327b86b4d3ccacd8b0482ae1" .parse() .unwrap(); let tx = blockbook().await.transaction(&txid).await.unwrap(); let expected_tx = Transaction { txid, version: bitcoin::blockdata::transaction::Version::TWO, lock_time: None, script: ScriptBuf::from_hex( "0200000000010227fa15587fdefa0e4bddef8297e8e309478b6ecad3c79ccc4f11e0bf5a0ee4a60200000000ffffffff5a43b6f57bf76889924a806ae1cf2124de949c3f5acbeade82489f213837dde00000000000ffffffff034078f30100000000160014fff48015913acb35add9b3c74b5a6f76f2d145caf6c19c0000000000160014e1a8d8ab85131ec8a9521ad2e7daf16bcf3fe9e2187e36030000000016001431ac0b37f2dfa0a34c65e57c209361a916feb62d02483045022100e6bc8783be4d00222da4684b495c0e38578c72bc72d74321b776e777f430b5be02203dce27e811f0cb0b9ebad0d11d541a83344c985ec37a0273993caf582988c0a301210285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff002473044022036563b247efe66f50f453a6417d03bca152ad70913d7b69b29d7abcb602dd389022033f841a69c985ba457fb1c41a533fb0bce4b68a3bd42fdec60a89ab66623995901210285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff000000000" ) .unwrap(), block_hash: Some(BlockHash::from_raw_hash( hashes::sha256d::Hash::from_str( "00000000000000000006b7e2a7110c174f21633adbe955c8f86f36699bba6716", ) .unwrap(), )), size: 402, vsize: 240, block_height: Some(Height::from_consensus(765_165).unwrap()), confirmations: tx.confirmations, block_time: Time::from_consensus(1_669_723_092).unwrap(), value: Amount::from_sat(96_909_390), value_in: Amount::from_sat(96_935_907), fees: Amount::from_sat(26517), vin: vec![ Vin { txid: "a6e40e5abfe0114fcc9cc7d3ca6e8b4709e3e89782efdd4b0efade7f5815fa27" .parse() .unwrap(), sequence: Some(Sequence(4_294_967_295)), n: 0, vout: Some(2), addresses: vec!["bc1qxxkqkdljm7s2xnr9u47zpymp4yt0ad3df4hudm" .parse::>() .unwrap() .assume_checked()], is_address: true, value: Amount::from_sat(59_231_084), }, Vin { txid: "e0dd3738219f4882deeacb5a3f9c94de2421cfe16a804a928968f77bf5b6435a" .parse() .unwrap(), vout: None, sequence: Some(Sequence(4_294_967_295)), n: 1, addresses: vec!["bc1qxxkqkdljm7s2xnr9u47zpymp4yt0ad3df4hudm" .parse::>() .unwrap() .assume_checked()], is_address: true, value: Amount::from_sat(37_704_823), }, ], vout: vec![ Vout { value: Amount::from_sat(32_733_248), n: 0, spent: Some(true), spent_tx_id: Some("bbbd318561518a2cb422fe0628262b0d5ce85bada0841d81ffc755d9f7ce841c".parse().unwrap()), spent_height: Some(Height::from_consensus(765_340).unwrap()), spent_index: Some(23), script: ScriptBuf::from_hex("0014fff48015913acb35add9b3c74b5a6f76f2d145ca") .unwrap(), addresses: vec!["bc1qll6gq9v38t9nttwek0r5kkn0wmedz3w2gshe0a" .parse::>() .unwrap() .assume_checked()], is_address: true, is_own: None, }, Vout { value: Amount::from_sat(10_273_270), n: 1, spent: Some(true), spent_tx_id: Some("79f46438043db23e4449627a92097531995c57d6d60c438425ba61de13484e7a".parse().unwrap()), spent_height: Some(Height::from_consensus(780_500).unwrap()), spent_index: None, script: ScriptBuf::from_hex("0014e1a8d8ab85131ec8a9521ad2e7daf16bcf3fe9e2") .unwrap(), addresses: vec!["bc1qux5d32u9zv0v322jrtfw0kh3d08nl60z8q964g" .parse::>() .unwrap() .assume_checked()], is_address: true, is_own: None, }, Vout { value: Amount::from_sat(53_902_872), n: 2, spent: Some(true), spent_tx_id: Some("2a96ae0fc11ab3c61e01b52aea2136745c973ca112b61ed32fbfeb09795f6f10".parse().unwrap()), spent_height: Some(Height::from_consensus(765_169).unwrap()), spent_index: None, script: ScriptBuf::from_hex("001431ac0b37f2dfa0a34c65e57c209361a916feb62d") .unwrap(), addresses: vec!["bc1qxxkqkdljm7s2xnr9u47zpymp4yt0ad3df4hudm" .parse::>() .unwrap() .assume_checked()], is_address: true, is_own: None, }, ], }; assert_eq!(tx, expected_tx); let mut client = blockbook_ws().await; assert_eq!(client.transaction(txid).await.unwrap(), expected_tx); } #[ignore] #[tokio::test] async fn test_lock_time() { let tx = blockbook() .await .transaction( &"bd99f123432e23aa8b88af0e9f701a4d6c8f0638dc133a14c7ccf57fb06596ac" .parse() .unwrap(), ) .await .unwrap(); assert_eq!(tx.lock_time, Some(Height::from_consensus(777_536).unwrap())); } #[ignore] #[tokio::test] async fn test_sequence() { let tx = blockbook() .await .transaction( &"bd99f123432e23aa8b88af0e9f701a4d6c8f0638dc133a14c7ccf57fb06596ac" .parse() .unwrap(), ) .await .unwrap(); assert_eq!( tx.vin.get(0).unwrap().sequence, Some(Sequence(4_294_967_293)) ); let tx = blockbook() .await .transaction( &"c8d7b00135b9bd03055a8f47851eafae747b759b4608bd9f35e85b3285185679" .parse() .unwrap(), ) .await .unwrap(); assert_eq!(tx.vin.get(0).unwrap().sequence, None); } #[allow(clippy::too_many_lines)] #[ignore] #[tokio::test] async fn test_tx_specific() { let txid = "b0714235addd08daf83b979aa35cc9ed7558efb8327b86b4d3ccacd8b0482ae1" .parse() .unwrap(); let tx = blockbook().await.transaction_specific(&txid).await.unwrap(); let expected_tx = TransactionSpecific { txid, version: bitcoin::blockdata::transaction::Version::TWO, script: ScriptBuf::from_hex( "0200000000010227fa15587fdefa0e4bddef8297e8e309478b6ecad3c79ccc4f11e0bf5a0ee4a60200000000ffffffff5a43b6f57bf76889924a806ae1cf2124de949c3f5acbeade82489f213837dde00000000000ffffffff034078f30100000000160014fff48015913acb35add9b3c74b5a6f76f2d145caf6c19c0000000000160014e1a8d8ab85131ec8a9521ad2e7daf16bcf3fe9e2187e36030000000016001431ac0b37f2dfa0a34c65e57c209361a916feb62d02483045022100e6bc8783be4d00222da4684b495c0e38578c72bc72d74321b776e777f430b5be02203dce27e811f0cb0b9ebad0d11d541a83344c985ec37a0273993caf582988c0a301210285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff002473044022036563b247efe66f50f453a6417d03bca152ad70913d7b69b29d7abcb602dd389022033f841a69c985ba457fb1c41a533fb0bce4b68a3bd42fdec60a89ab66623995901210285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff000000000" ) .unwrap(), wtxid: "5eb2c13ef559e9aee53e803e6a8abcbfc7bb60830e2bb7fec1437f98f463e889" .parse() .unwrap(), size: 402, time: Some(Time::from_consensus(1_669_723_092).unwrap()), vsize: 240, weight: 957, blockhash: Some("00000000000000000006b7e2a7110c174f21633adbe955c8f86f36699bba6716" .parse() .unwrap()), blocktime: Some(Time::from_consensus(1_669_723_092).unwrap()), confirmations: tx.confirmations, locktime: LockTime::ZERO, vin: vec![ VinSpecific { txid: "a6e40e5abfe0114fcc9cc7d3ca6e8b4709e3e89782efdd4b0efade7f5815fa27" .parse() .unwrap(), sequence: Sequence(4_294_967_295), vout: 2, tx_in_witness: Some(Witness::from_slice(&[ Vec::from_hex( "3045022100e6bc8783be4d00222da4684b495c0e38578c72bc72d74321b776e777f430b5be02203dce27e811f0cb0b9ebad0d11d541a83344c985ec37a0273993caf582988c0a301" ) .unwrap(), Vec::from_hex( "0285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff0", ) .unwrap(), ])), script_sig: blockbook::ScriptSig::default(), }, VinSpecific { txid: "e0dd3738219f4882deeacb5a3f9c94de2421cfe16a804a928968f77bf5b6435a" .parse() .unwrap(), sequence: Sequence(4_294_967_295), vout: 0, tx_in_witness: Some(Witness::from_slice(&[ Vec::from_hex( "3044022036563b247efe66f50f453a6417d03bca152ad70913d7b69b29d7abcb602dd389022033f841a69c985ba457fb1c41a533fb0bce4b68a3bd42fdec60a89ab66623995901" ) .unwrap(), Vec::from_hex( "0285b14271e50491ac26111dd42a6d9004f06a8e77355dac918c2fe7b1a7526ff0", ) .unwrap(), ])), script_sig: blockbook::ScriptSig::default(), }, ], vout: vec![ VoutSpecific { value: Amount::from_sat(32_733_248), n: 0, script_pub_key: ScriptPubKey { address: "bc1qll6gq9v38t9nttwek0r5kkn0wmedz3w2gshe0a" .parse::>() .unwrap() .assume_checked(), asm: "0 fff48015913acb35add9b3c74b5a6f76f2d145ca".into(), desc: Some("addr(bc1qll6gq9v38t9nttwek0r5kkn0wmedz3w2gshe0a)#k54tyxd5".into()), script: ScriptBuf::from_hex("0014fff48015913acb35add9b3c74b5a6f76f2d145ca") .unwrap(), r#type: ScriptPubKeyType::WitnessV0PubKeyHash, }, }, VoutSpecific { value: Amount::from_sat(10_273_270), n: 1, script_pub_key: ScriptPubKey { address: "bc1qux5d32u9zv0v322jrtfw0kh3d08nl60z8q964g" .parse::>() .unwrap() .assume_checked(), asm: "0 e1a8d8ab85131ec8a9521ad2e7daf16bcf3fe9e2".into(), desc: Some("addr(bc1qux5d32u9zv0v322jrtfw0kh3d08nl60z8q964g)#f20g3jce".into()), script: ScriptBuf::from_hex("0014e1a8d8ab85131ec8a9521ad2e7daf16bcf3fe9e2") .unwrap(), r#type: ScriptPubKeyType::WitnessV0PubKeyHash, }, }, VoutSpecific { value: Amount::from_sat(53_902_872), n: 2, script_pub_key: ScriptPubKey { address: "bc1qxxkqkdljm7s2xnr9u47zpymp4yt0ad3df4hudm" .parse::>() .unwrap() .assume_checked(), asm: "0 31ac0b37f2dfa0a34c65e57c209361a916feb62d".into(), desc: Some("addr(bc1qxxkqkdljm7s2xnr9u47zpymp4yt0ad3df4hudm)#mxf8kxkq".into()), script: ScriptBuf::from_hex("001431ac0b37f2dfa0a34c65e57c209361a916feb62d") .unwrap(), r#type: ScriptPubKeyType::WitnessV0PubKeyHash, }, }, ], }; assert_eq!(tx, expected_tx); assert_eq!( blockbook_ws() .await .transaction_specific(txid) .await .unwrap(), expected_tx ); } #[ignore] #[tokio::test] async fn test_tx_specific_pre_segwit() { #![allow(clippy::similar_names)] let txid = "0c5cb51f39ecb826cd477d94576abde1d2b6ef1b2e0ac7b9cea5d5ab28aba902"; let wtxid = txid.parse().unwrap(); let txid = txid.parse().unwrap(); let tx = blockbook().await.transaction_specific(&txid).await.unwrap(); let expected_tx = TransactionSpecific { txid, version: bitcoin::blockdata::transaction::Version::ONE, script: ScriptBuf::from_hex( "01000000013cdefb50d22666b59b24f047b019e09a2c077ad0fb8febda33a5e0bad45990e2000000006a47304402202015dfc5b5d9030f9538c1f6e0b99fe8dbf46260044e45ad2f883744292af09b0220066353c0d19f9734278ba7072fa8b3ba1c1c30bdd583721439b8ee375a098ad8012103de2010f23c4eda698d373cfc8f7ecd576fbb4e40f67a8634ac007bb4b80a4fd4ffffffff01e62e1900000000001976a914029f45cefe259733c9d860b70f7a8385596607bf88ac00000000" ) .unwrap(), wtxid, size: 191, time: Some(Time::from_consensus(1_513_622_125).unwrap()), vsize: 191, weight: 764, blockhash: Some("00000000000000000024fb37364cbf81fd49cc2d51c09c75c35433c3a1945d04" .parse() .unwrap()), blocktime: Some(Time::from_consensus(1_513_622_125).unwrap()), confirmations: tx.confirmations, locktime: LockTime::ZERO, vin: vec![VinSpecific { txid: "e29059d4bae0a533daeb8ffbd07a072c9ae019b047f0249bb56626d250fbde3c" .parse() .unwrap(), sequence: Sequence(4_294_967_295), vout: 0, tx_in_witness: None, script_sig: ScriptSig { asm: "304402202015dfc5b5d9030f9538c1f6e0b99fe8dbf46260044e45ad2f883744292af09b0220066353c0d19f9734278ba7072fa8b3ba1c1c30bdd583721439b8ee375a098ad8[ALL] 03de2010f23c4eda698d373cfc8f7ecd576fbb4e40f67a8634ac007bb4b80a4fd4" .into(), script: ScriptBuf::from_hex( "47304402202015dfc5b5d9030f9538c1f6e0b99fe8dbf46260044e45ad2f883744292af09b0220066353c0d19f9734278ba7072fa8b3ba1c1c30bdd583721439b8ee375a098ad8012103de2010f23c4eda698d373cfc8f7ecd576fbb4e40f67a8634ac007bb4b80a4fd4" ) .unwrap(), }, }], vout: vec![VoutSpecific { value: Amount::from_sat(1_650_406), n: 0, script_pub_key: ScriptPubKey { address: "1Es9qximvz5W9TqtZfxx9cV7thmvDutWf" .parse::>() .unwrap() .assume_checked(), asm: "OP_DUP OP_HASH160 029f45cefe259733c9d860b70f7a8385596607bf OP_EQUALVERIFY OP_CHECKSIG" .into(), desc: Some("addr(1Es9qximvz5W9TqtZfxx9cV7thmvDutWf)#4h8xrdj5".into()), script: ScriptBuf::from_hex("76a914029f45cefe259733c9d860b70f7a8385596607bf88ac") .unwrap(), r#type: ScriptPubKeyType::PubKeyHash, }, }], }; assert_eq!(tx, expected_tx); } #[allow(clippy::too_many_lines)] #[ignore] #[tokio::test] async fn test_block_by_hash() { let block = blockbook() .await .block_by_hash( &"000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), ) .await .unwrap(); let expected_block = Block { page: 1, total_pages: 1, items_on_page: 1000, hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), previous_block_hash: Some( "000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250" .parse() .unwrap(), ), next_block_hash: Some( "00000000000080b66c911bd5ba14a74260057311eaeb1982802f7010f1a9f090" .parse() .unwrap(), ), height: Height::from_consensus(100_000).unwrap(), confirmations: block.confirmations, size: 957, time: Time::from_consensus(1_293_623_863).unwrap(), version: bitcoin::blockdata::block::Version::ONE, merkle_root: "f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766" .parse() .unwrap(), nonce: "274148111".into(), bits: "1b04864c".into(), difficulty: "14484.1623612254".into(), tx_count: 4, txs: vec![ BlockTransaction { txid: "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87" .parse() .unwrap(), vsize: 135, vin: vec![BlockVin { n: 0, addresses: None, is_address: false, value: Amount::ZERO, }], vout: vec![BlockVout { value: Amount::from_sat(5_000_000_000), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1HWqMzw1jfpXb3xyuUZ4uWXY4tqL2cW47J" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }], block_hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), block_height: Height::from_consensus(100_000).unwrap(), confirmations: block.confirmations, block_time: Time::from_consensus(1_293_623_863).unwrap(), value: Amount::from_sat(5_000_000_000), value_in: Amount::ZERO, fees: Amount::ZERO, }, BlockTransaction { txid: "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4" .parse() .unwrap(), vsize: 259, vin: vec![BlockVin { n: 0, addresses: Some(vec!["1BNwxHGaFbeUBitpjy2AsKpJ29Ybxntqvb" .parse::>() .unwrap() .assume_checked()]), is_address: true, value: Amount::from_sat(5_000_000_000), }], vout: vec![ BlockVout { value: Amount::from_sat(556_000_000), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1JqDybm2nWTENrHvMyafbSXXtTk5Uv5QAn" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, BlockVout { value: Amount::from_sat(4_444_000_000), n: 1, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1EYTGtG4LnFfiMvjJdsU7GMGCQvsRSjYhx" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, ], block_hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), block_height: Height::from_consensus(100_000).unwrap(), confirmations: block.confirmations, block_time: Time::from_consensus(1_293_623_863).unwrap(), value: Amount::from_sat(5_000_000_000), value_in: Amount::from_sat(5_000_000_000), fees: Amount::ZERO, }, BlockTransaction { txid: "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4" .parse() .unwrap(), vsize: 257, vin: vec![BlockVin { n: 0, addresses: Some(vec!["15vScfMHNrXN4QvWe54q5hwfVoYwG79CS1" .parse::>() .unwrap() .assume_checked()]), is_address: true, value: Amount::from_sat(300_000_000), }], vout: vec![ BlockVout { value: Amount::from_sat(1_000_000), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1H8ANdafjpqYntniT3Ddxh4xPBMCSz33pj" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, BlockVout { value: Amount::from_sat(299_000_000), n: 1, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1Am9UTGfdnxabvcywYG2hvzr6qK8T3oUZT" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, ], block_hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), block_height: Height::from_consensus(100_000).unwrap(), confirmations: block.confirmations, block_time: Time::from_consensus(1_293_623_863).unwrap(), value: Amount::from_sat(300_000_000), value_in: Amount::from_sat(300_000_000), fees: Amount::ZERO, }, BlockTransaction { txid: "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d" .parse() .unwrap(), vsize: 225, vin: vec![BlockVin { n: 0, addresses: Some(vec!["1JxDJCyWNakZ5kECKdCU9Zka6mh34mZ7B2" .parse::>() .unwrap() .assume_checked()]), is_address: true, value: Amount::from_sat(1_000_000), }], vout: vec![BlockVout { value: Amount::from_sat(1_000_000), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address( "16FuTPaeRSPVxxCnwQmdyx2PQWxX6HWzhQ" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }], block_hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" .parse() .unwrap(), block_height: Height::from_consensus(100_000).unwrap(), confirmations: block.confirmations, block_time: Time::from_consensus(1_293_623_863).unwrap(), value: Amount::from_sat(1_000_000), value_in: Amount::from_sat(1_000_000), fees: Amount::ZERO, }, ], }; assert_eq!(block, expected_block); } #[ignore] #[tokio::test] async fn test_block_by_height_with_opreturn_output() { let block = blockbook() .await .block_by_height(&Height::from_consensus(500_044).unwrap()) .await .unwrap(); let expected_block = Block { page: 1, total_pages: 1, items_on_page: 1000, hash: "0000000000000000001f9ba01120351182680ceba085ffabeaa532cda35f2cc7" .parse() .unwrap(), previous_block_hash: Some( "00000000000000000075807003ae64990ee53422788451b795839f70a177695d" .parse() .unwrap(), ), next_block_hash: Some( "0000000000000000000401882bfcb7e7f14a14e21b827e5fb5981d07a5cea0f2" .parse() .unwrap(), ), height: Height::from_consensus(500_044).unwrap(), confirmations: block.confirmations, size: 285, time: Time::from_consensus(1_513_638_772).unwrap(), version: bitcoin::blockdata::block::Version::from_consensus(536_870_912), merkle_root: "db5f956e4f48e79895021a9f7e64035fd03680e96253aafd438118485bfe49cb" .parse() .unwrap(), nonce: "437791427".into(), bits: "18009645".into(), difficulty: "1873105475221.611".into(), tx_count: 1, txs: vec![BlockTransaction { txid: "db5f956e4f48e79895021a9f7e64035fd03680e96253aafd438118485bfe49cb" .parse() .unwrap(), vsize: 177, vin: vec![BlockVin { n: 0, addresses: None, is_address: false, value: Amount::ZERO, }], vout: vec![ BlockVout { value: Amount::from_sat(1_250_000_000), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1NS4gbx1G2D5rc9PnvVsPys12nKxGiQg72" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, BlockVout { value: Amount::ZERO, n: 1, spent: block.txs.get(0).unwrap().vout.get(1).unwrap().spent, addresses: vec![AddressBlockVout::OpReturn(OpReturn( "OP_RETURN aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9".into()) )], is_address: false, }, ], block_hash: "0000000000000000001f9ba01120351182680ceba085ffabeaa532cda35f2cc7" .parse() .unwrap(), block_height: Height::from_consensus(500_044).unwrap(), confirmations: block.confirmations, block_time: Time::from_consensus(1_513_638_772).unwrap(), value: Amount::from_sat(1_250_000_000), value_in: Amount::ZERO, fees: Amount::ZERO, }], }; assert_eq!(block, expected_block); } #[ignore] #[tokio::test] async fn test_tickers_list() { let tickers_list = blockbook() .await .tickers_list(&Time::from_consensus(1_674_821_349).unwrap()) .await .unwrap(); let expected_tickers_list = TickersList { timestamp: Time::from_consensus(1_674_864_000).unwrap(), available_currencies: vec![ Currency::Aed, Currency::Ars, Currency::Aud, Currency::Bdt, Currency::Bhd, Currency::Bmd, Currency::Brl, Currency::Btc, Currency::Cad, Currency::Chf, Currency::Clp, Currency::Cny, Currency::Czk, Currency::Dkk, Currency::Eth, Currency::Eur, Currency::Gbp, Currency::Hkd, Currency::Huf, Currency::Idr, Currency::Ils, Currency::Inr, Currency::Jpy, Currency::Krw, Currency::Kwd, Currency::Lkr, Currency::Mmk, Currency::Mxn, Currency::Myr, Currency::Ngn, Currency::Nok, Currency::Nzd, Currency::Php, Currency::Pkr, Currency::Pln, Currency::Rub, Currency::Sar, Currency::Sek, Currency::Sgd, Currency::Thb, Currency::Try, Currency::Twd, Currency::Uah, Currency::Usd, Currency::Vef, Currency::Vnd, Currency::Zar, ], }; assert_eq!(tickers_list, expected_tickers_list); } #[ignore] #[tokio::test] async fn test_current_tickers_list() { blockbook() .await .tickers_list( &Time::from_consensus( std::time::SystemTime::now() .duration_since(std::time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs() .try_into() .unwrap(), ) .unwrap(), ) .await .unwrap(); } #[ignore] #[tokio::test] async fn test_tickers() { let tickers = blockbook() .await .tickers(Some(&Time::from_consensus(1_674_821_349).unwrap())) .await .unwrap(); let expected_tickers = Ticker { timestamp: Time::from_consensus(1_674_864_000).unwrap(), rates: std::collections::HashMap::from([ (Currency::Aed, 84_783.336), (Currency::Ars, 4_282_867.5), (Currency::Aud, 32_274.684), (Currency::Bdt, 2_447_401.8), (Currency::Bhd, 8_703.507), (Currency::Bmd, 23_082.857), (Currency::Brl, 117_937.25), (Currency::Btc, 1.0), (Currency::Cad, 30_791.379), (Currency::Chf, 21_293.938), (Currency::Clp, 18_614_248.0), (Currency::Cny, 156_582.56), (Currency::Czk, 505_740.8), (Currency::Dkk, 157_962.92), (Currency::Eth, 14.43425), (Currency::Eur, 21_232.443), (Currency::Gbp, 18_643.771), (Currency::Hkd, 180_742.25), (Currency::Huf, 8_293_902.0), (Currency::Idr, 345_612_700.0), (Currency::Ils, 79_411.266), (Currency::Inr, 1_881_595.6), (Currency::Jpy, 2_998_118.5), (Currency::Krw, 28_393_070.0), (Currency::Kwd, 7_044.703_6), (Currency::Lkr, 8_404_290.0), (Currency::Mmk, 48_486_224.0), (Currency::Mxn, 433_456.84), (Currency::Myr, 97_963.65), (Currency::Ngn, 10_624_578.0), (Currency::Nok, 228_294.1), (Currency::Nzd, 35_566.81), (Currency::Php, 1_260_220.1), (Currency::Pkr, 5_783_717.5), (Currency::Pln, 99_952.24), (Currency::Rub, 1_609_952.6), (Currency::Sar, 86_651.664), (Currency::Sek, 237_953.11), (Currency::Sgd, 30_321.643), (Currency::Thb, 756_436.75), (Currency::Try, 434_174.72), (Currency::Twd, 698_637.3), (Currency::Uah, 848_393.44), (Currency::Usd, 23_082.857), (Currency::Vef, 2_311.286_6), (Currency::Vnd, 541_523_840.0), (Currency::Zar, 397_099.03), ]), }; assert_eq!(tickers, expected_tickers); } #[ignore] #[tokio::test] async fn test_current_tickers() { blockbook().await.tickers(None).await.unwrap(); } #[ignore] #[tokio::test] async fn test_ticker() { let ticker = blockbook() .await .ticker( &Currency::Idr, Some(&Time::from_consensus(1_674_692_106).unwrap()), ) .await .unwrap(); let expected_ticker = Ticker { timestamp: Time::from_consensus(1_674_777_600).unwrap(), rates: std::collections::HashMap::from([(Currency::Idr, 344_337_380.0)]), }; assert_eq!(ticker, expected_ticker); } #[ignore] #[tokio::test] async fn test_info_ws() { let info = blockbook_ws().await.info().await.unwrap(); let expected_info = Info { name: "Bitcoin".into(), shortcut: "BTC".into(), decimals: 8, version: info.version.clone(), best_height: info.best_height, best_hash: info.best_hash, block_0_hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" .parse() .unwrap(), testnet: false, backend_version: Version { version: "250000".into(), subversion: "/Satoshi:25.0.0/".into(), }, }; assert_eq!(info, expected_info); } #[ignore] #[tokio::test] async fn test_block_hash_ws() { assert_eq!( blockbook_ws() .await .block_hash(Height::from_consensus(500_044).unwrap()) .await .unwrap() .to_string(), "0000000000000000001f9ba01120351182680ceba085ffabeaa532cda35f2cc7" ); } #[ignore] #[tokio::test] async fn test_current_fiat_rates() { let mut client = blockbook_ws().await; let rates = client .current_fiat_rates(vec![Currency::Btc, Currency::Chf]) .await .unwrap() .rates; assert!((rates.get(&Currency::Btc).unwrap() - 1.0).abs() < f64::EPSILON); assert!(rates.get(&Currency::Chf).unwrap() > &0.0); } #[ignore] #[tokio::test] async fn test_available_currencies() { let mut client = blockbook_ws().await; let tickers = client .available_currencies(Time::from_consensus(1_682_415_368).unwrap()) .await .unwrap() .available_currencies; assert!(tickers.contains(&Currency::Btc)); assert!(tickers.contains(&Currency::Chf)); } #[ignore] #[tokio::test] async fn test_fiat_rates_for_timestamps() { let mut client = blockbook_ws().await; let tickers = client .fiat_rates_for_timestamps(vec![Time::from_consensus(1_575_288_000).unwrap()], None) .await .unwrap(); assert_eq!(tickers.len(), 1); let ticker = tickers.get(0).unwrap(); assert_eq!(ticker.timestamp.to_consensus_u32(), 1_575_331_200); assert!(ticker.rates.contains_key(&Currency::Chf)); assert!(ticker.rates.contains_key(&Currency::Cad)); let tickers = client .fiat_rates_for_timestamps( vec![ Time::from_consensus(1_575_288_000).unwrap(), Time::from_consensus(1_675_288_000).unwrap(), ], Some(vec![Currency::Chf, Currency::Usd]), ) .await .unwrap(); assert_eq!(tickers.len(), 2); assert_eq!( tickers.get(0).unwrap().timestamp.to_consensus_u32(), 1_575_331_200 ); assert_eq!( tickers.get(1).unwrap().timestamp.to_consensus_u32(), 1_675_296_000 ); assert_eq!(tickers.get(0).unwrap().rates.len(), 2); assert_eq!(tickers.get(1).unwrap().rates.len(), 2); } #[ignore] #[tokio::test] async fn test_estimate_fee() { let mut client = blockbook_ws().await; let fees = client .estimate_fee(vec![1, 2, 5, 10, 100, 1000]) .await .unwrap(); let mut fees_cloned = fees.clone(); // should already be reverse-sorted fees_cloned.sort_by(|a, b| b.cmp(a)); assert_eq!(fees, fees_cloned); fees.iter().for_each(|f| assert!(f.to_sat() > 0)); } #[ignore] #[tokio::test] async fn test_estimate_tx_fee() { let mut client = blockbook_ws().await; let fees = client .estimate_tx_fee(vec![1, 2, 5, 10, 100, 1000], 600) .await .unwrap(); let mut fees_cloned = fees.clone(); // should already be reverse-sorted fees_cloned.sort_by(|a, b| b.cmp(a)); assert_eq!(fees, fees_cloned); fees.iter().for_each(|f| assert!(f.to_sat() > 0)); } #[ignore] #[tokio::test] async fn test_utxos_from_address_ws() { let mut client = blockbook_ws().await; let utxos = client .utxos_from_address( "bc1q5au2nmza9pmplnvgzyd4ky7egu2wya56qa024u" .parse::>() .unwrap() .assume_checked(), ) .await .unwrap(); assert_eq!(utxos.len(), 1); let expected_utxo = Utxo { txid: "d60691ecdf678eb3f88ebfaf315d3907d0be62ccd40fd1f027938249966f268d" .parse() .unwrap(), vout: 1, value: Amount::from_sat(821), height: Some(Height::from_consensus(784_027).unwrap()), confirmations: utxos.get(0).unwrap().confirmations, locktime: None, address: None, path: None, coinbase: None, }; assert_eq!(utxos.get(0).unwrap(), &expected_utxo); } #[ignore] #[tokio::test] async fn test_balance_history() { let client = blockbook().await; let mut ws_client = blockbook_ws().await; let address: Address = "399WojVNByUJZSuJDAKYJ8EQmbkDUgHuT6" .parse::>() .unwrap() .assume_checked(); let from = Time::from_consensus(1_676_000_000).unwrap(); let to = Time::from_consensus(1_682_000_000).unwrap(); let group_by = 1_000_000; let history_1 = ws_client .balance_history( address.clone(), Some(from), Some(to), Some(vec![Currency::Chf]), Some(group_by), ) .await .unwrap(); assert_eq!( history_1, client .balance_history( &address, Some(&from), Some(&to), Some(&Currency::Chf), Some(group_by) ) .await .unwrap() ); assert_eq!(history_1.len(), 6); let tx_count_1: u32 = history_1.iter().map(|entry| entry.txs).sum(); let received_count_1: Amount = history_1.iter().map(|entry| entry.received).sum(); let group_by = 2_000_000; let history_2 = ws_client .balance_history( address.clone(), Some(from), Some(to), Some(vec![Currency::Chf]), Some(group_by), ) .await .unwrap(); assert_eq!( history_2, client .balance_history( &address, Some(&from), Some(&to), Some(&Currency::Chf), Some(group_by) ) .await .unwrap() ); assert_eq!(history_2.len(), 3); let tx_count_2: u32 = history_2.iter().map(|entry| entry.txs).sum(); let received_count_2 = history_2.iter().map(|entry| entry.received).sum(); assert_eq!(tx_count_1, tx_count_2); assert_eq!(received_count_1, received_count_2); } fn addr_1() -> Address { "bc1qsej2fzpejkar82t8nyc2dhkvk54kn905vpvzpw" .parse::>() .unwrap() .assume_checked() } #[ignore] #[tokio::test] async fn test_address_info() { let address_info = blockbook().await.address_info(&addr_1()).await.unwrap(); assert_eq!(&address_info.basic.address, &addr_1()); assert_eq!( address_info.txids.unwrap().last().unwrap(), &Txid::from_str("98f08111f08baba3d33af28c74facc223a07d868c0568258980119761dea441d") .unwrap() ); } #[ignore] #[tokio::test] async fn test_address_info_specific_no_args() { let address_info = blockbook() .await .address_info_specific(&addr_1(), None, None, None, None, None) .await .unwrap(); assert_eq!(&address_info.basic.address, &addr_1()); assert_eq!( address_info.txids.as_ref().unwrap().last().unwrap(), &Txid::from_str("98f08111f08baba3d33af28c74facc223a07d868c0568258980119761dea441d") .unwrap() ); let mut websocket_info = blockbook_ws() .await .address_info_txids(addr_1(), None, None, None, None, None) .await .unwrap(); websocket_info.paging.items_on_page = address_info.paging.items_on_page; assert_eq!(address_info, websocket_info); } fn counterparty_burner_addr() -> Address { "1CounterpartyXXXXXXXXXXXXXXXUWLpVr" .parse::>() .unwrap() .assume_checked() } #[ignore] #[tokio::test] async fn test_address_info_specific_page() { let address = counterparty_burner_addr(); let number_of_txs = blockbook() .await .address_info_specific_basic(&address, None) .await .unwrap() .txs; let address_info = blockbook() .await .address_info_specific( &address, Some(&std::num::NonZeroU32::new(number_of_txs).unwrap()), Some(&std::num::NonZeroU16::new(1).unwrap()), None, None, None, ) .await .unwrap(); assert_eq!(&address_info.basic.address, &address); assert_eq!( address_info.txids.as_ref().unwrap().get(0).unwrap(), &Txid::from_str("685623401c3f5e9d2eaaf0657a50454e56a270ee7630d409e98d3bc257560098") .unwrap(), ); let websocket_info = blockbook_ws() .await .address_info_txids( address, Some(std::num::NonZeroU32::new(number_of_txs).unwrap()), Some(std::num::NonZeroU16::new(1).unwrap()), None, None, None, ) .await .unwrap(); assert_eq!(address_info, websocket_info); } fn addr_2() -> Address { "3Kzh9qAqVWQhEsfQz7zEQL1EuSx5tyNLNS" .parse::>() .unwrap() .assume_checked() } #[ignore] #[tokio::test] async fn test_address_info_specific_blocks_basic() { let address_info = blockbook() .await .address_info_specific_basic(&addr_2(), Some(&Currency::Usd)) .await .unwrap(); assert_eq!(&address_info.address, &addr_2()); assert_eq!( address_info, blockbook_ws() .await .address_info_basic(addr_2(), Some(Currency::Usd)) .await .unwrap() ); } #[ignore] #[tokio::test] async fn test_address_info_specific_blocks() { let address_info = blockbook() .await .address_info_specific( &addr_2(), None, None, Some(&Height::from_consensus(500_000).unwrap()), Some(&Height::from_consensus(503_000).unwrap()), None, ) .await .unwrap(); let expected_address_info = AddressInfo { paging: AddressInfoPaging { page: 1, total_pages: address_info.paging.total_pages, items_on_page: 1000, }, basic: AddressInfoBasic { address: addr_2(), balance: address_info.basic.balance, total_received: address_info.basic.total_received, total_sent: address_info.basic.total_sent, unconfirmed_balance: address_info.basic.unconfirmed_balance, unconfirmed_txs: address_info.basic.unconfirmed_txs, txs: address_info.basic.txs, secondary_value: None, }, txids: Some(vec![ "ae1484c0cecf39700bb1697793bec24fbb1980207eeb1374eb293a5c403ac8c3" .parse() .unwrap(), "a4b4b879af01563cccadca66d36a0f47afcf78f263bed0966df8abf0a2699f3d" .parse() .unwrap(), "e09390893277b6957cf93ad9ec4b72c6c140aceaa8e62874151ebfca403a76e1" .parse() .unwrap(), "67a6147be5216a0b77e87002e9911f62e2b3dcfa44ce15e8c28e39d77860c59e" .parse() .unwrap(), "2c3ca46df14114490e5d22ddcbcf08a730854e7554a54094c0fb4d7b7a576ed7" .parse() .unwrap(), ]), transactions: None, }; assert_eq!(address_info, expected_address_info); let mut websocket_info = blockbook_ws() .await .address_info_txids( addr_2(), None, None, Some(Height::from_consensus(500_000).unwrap()), Some(Height::from_consensus(503_000).unwrap()), None, ) .await .unwrap(); websocket_info.paging.items_on_page = address_info.paging.items_on_page; assert_eq!(address_info, websocket_info); } #[allow(clippy::too_many_lines)] #[ignore] #[tokio::test] async fn test_address_info_specific_blocks_details() { let address_info = blockbook() .await .address_info_specific_detailed( &addr_2(), None, None, Some(&Height::from_consensus(501_000).unwrap()), Some(&Height::from_consensus(502_000).unwrap()), &TxDetail::Full, Some(&Currency::Zar), ) .await .unwrap(); let expected_address_info = AddressInfo { paging: AddressInfoPaging { page: 1, total_pages: address_info.paging.total_pages, items_on_page: 1000, }, basic: AddressInfoBasic { address: addr_2(), balance: address_info.basic.balance, total_received: address_info.basic.total_received, total_sent: address_info.basic.total_sent, unconfirmed_balance: address_info.basic.unconfirmed_balance, unconfirmed_txs: address_info.basic.unconfirmed_txs, txs: address_info.basic.txs, secondary_value: address_info.basic.secondary_value, }, txids: None, transactions: Some(vec![Tx::Ordinary(Transaction { txid: "67a6147be5216a0b77e87002e9911f62e2b3dcfa44ce15e8c28e39d77860c59e" .parse() .unwrap(), version: bitcoin::blockdata::transaction::Version::ONE, lock_time: None, vin: vec![Vin { txid: "a563f78cac895c1abf411eb93000f751cf20c94e9f32360e643841e37080f906" .parse() .unwrap(), vout: Some(1), sequence: Some(Sequence(4_294_967_295)), is_address: true, value: Amount::from_sat(3_084_293), n: 0, addresses: vec!["1LyqvGRjLoznNX2RbytTvuyswDpDVqoYt7" .parse::>() .unwrap() .assume_checked()], }], vout: vec![ Vout { value: Amount::from_sat(11_700), n: 0, spent: Some(true), spent_tx_id: Some("0c169b54bdf3ffb5ee9617ad2cc93b60490820cd6d9453ccc7b837cc9400388a".parse().unwrap()), spent_height: Some(Height::from_consensus(543_468).unwrap()), spent_index: Some(3), script: ScriptBuf::from_hex("a914c8ca150ee82589d47f69b8dcd7cad684d88283f187") .unwrap(), addresses: vec![addr_2()], is_address: true, is_own: Some(true), }, Vout { value: Amount::from_sat(3_049_993), n: 1, spent: Some(true), spent_tx_id: Some("7da6913461443120bdbd013140cb1d521d030c1ead31c6e70688e520c5332078".parse().unwrap()), spent_height: Some(Height::from_consensus(501_554).unwrap()), spent_index: None, script: ScriptBuf::from_hex("76a9147d55684397c290fbc638bdc52528350088b8837488ac") .unwrap(), addresses: vec!["1CRhnBV2q8ToQcaKMBBkeooJdNX9ohSWDc" .parse::>() .unwrap() .assume_checked()], is_address: true, is_own: None, }, ], size: 223, vsize: 223, block_hash: Some("0000000000000000001617fb8817ecd53e1093247bcf813b2eae793033af0c0a" .parse() .unwrap()), block_height: Some(Height::from_consensus(501_498).unwrap()), confirmations: if let Tx::Ordinary(tx) = &address_info.transactions.as_ref().unwrap().get(0).unwrap() { tx.confirmations } else { panic!() }, block_time: Time::from_consensus(1_514_513_020).unwrap(), value: Amount::from_sat(3_061_693), value_in: Amount::from_sat(3_084_293), fees: Amount::from_sat(22_600), script: ScriptBuf::from_hex( "010000000106f98070e34138640e36329f4ec920cf51f70030b91e41bf1a5c89ac8cf763a5010000006a47304402200a54b9076c0fd91c3bcaa5c55a0721d18893b6aa204c87198d072555aff3bf2e02206aa296427da8eb404203044e9e33d5f69dbbdbc43be95dd4ac5c675b8c341c7301210241d3f009960b9695c8b7c546128aa4d01daf57c4ff562f6d1f30c2a85119af1cffffffff02b42d00000000000017a914c8ca150ee82589d47f69b8dcd7cad684d88283f187098a2e00000000001976a9147d55684397c290fbc638bdc52528350088b8837488ac00000000" ) .unwrap(), })]), }; assert_eq!(&address_info, &expected_address_info); let mut websocket_info = blockbook_ws() .await .address_info_txs( addr_2(), None, None, Some(Height::from_consensus(501_000).unwrap()), Some(Height::from_consensus(502_000).unwrap()), Some(Currency::Zar), ) .await .unwrap(); websocket_info.paging.items_on_page = address_info.paging.items_on_page; assert_eq!(websocket_info, expected_address_info); } #[ignore] #[tokio::test] async fn test_address_info_specific_blocks_details_light() { let address_info = blockbook() .await .address_info_specific_detailed( &addr_2(), None, None, Some(&Height::from_consensus(501_000).unwrap()), Some(&Height::from_consensus(502_000).unwrap()), &TxDetail::Light, None, ) .await .unwrap(); let expected_address_info = AddressInfo { paging: AddressInfoPaging { page: 1, total_pages: address_info.paging.total_pages, items_on_page: 1000, }, basic: AddressInfoBasic { address: addr_2(), balance: address_info.basic.balance, total_received: address_info.basic.total_received, total_sent: address_info.basic.total_sent, unconfirmed_balance: address_info.basic.unconfirmed_balance, unconfirmed_txs: address_info.basic.unconfirmed_txs, txs: address_info.basic.txs, secondary_value: None, }, txids: None, transactions: Some(vec![Tx::Light(BlockTransaction { txid: "67a6147be5216a0b77e87002e9911f62e2b3dcfa44ce15e8c28e39d77860c59e" .parse() .unwrap(), vsize: 223, vin: vec![BlockVin { is_address: true, value: Amount::from_sat(3_084_293), n: 0, addresses: Some(vec!["1LyqvGRjLoznNX2RbytTvuyswDpDVqoYt7" .parse::>() .unwrap() .assume_checked()]), }], vout: vec![ BlockVout { value: Amount::from_sat(11_700), n: 0, spent: Some(true), addresses: vec![AddressBlockVout::Address(addr_2())], is_address: true, }, BlockVout { value: Amount::from_sat(3_049_993), n: 1, spent: Some(true), addresses: vec![AddressBlockVout::Address( "1CRhnBV2q8ToQcaKMBBkeooJdNX9ohSWDc" .parse::>() .unwrap() .assume_checked(), )], is_address: true, }, ], block_hash: "0000000000000000001617fb8817ecd53e1093247bcf813b2eae793033af0c0a" .parse() .unwrap(), block_height: Height::from_consensus(501_498).unwrap(), confirmations: if let Tx::Light(tx) = &address_info.transactions.as_ref().unwrap().get(0).unwrap() { tx.confirmations } else { panic!() }, block_time: Time::from_consensus(1_514_513_020).unwrap(), value: Amount::from_sat(3_061_693), value_in: Amount::from_sat(3_084_293), fees: Amount::from_sat(22_600), })]), }; assert_eq!(&address_info, &expected_address_info); } #[ignore] #[tokio::test] async fn test_address_info_correct_variant_full() { let address_info_full = blockbook() .await .address_info_specific_detailed( &"bc1qhjhn2gm6mv4k99942ud4spe54483drh330faax" .parse::>() .unwrap() .assume_checked(), None, None, None, None, &TxDetail::Full, None, ) .await .unwrap(); assert!(matches!( address_info_full.transactions.unwrap().get(0).unwrap(), Tx::Ordinary(..) )); } #[ignore] #[tokio::test] async fn test_address_info_correct_variant_light() { let address_info_light = blockbook() .await .address_info_specific_detailed( &"bc1qhjhn2gm6mv4k99942ud4spe54483drh330faax" .parse::>() .unwrap() .assume_checked(), None, None, None, None, &TxDetail::Light, None, ) .await .unwrap(); assert!(matches!( address_info_light.transactions.unwrap().get(0).unwrap(), Tx::Light(..) )); } fn xpub_addr() -> Address { "bc1q5au2nmza9pmplnvgzyd4ky7egu2wya56qa024u" .parse::>() .unwrap() .assume_checked() } #[ignore] #[tokio::test] async fn test_address_info_block_and_pagesize_filter_combination() { let address_info = blockbook() .await .address_info_specific( &counterparty_burner_addr(), None, Some(&std::num::NonZeroU16::new(1).unwrap()), Some(&Height::from_consensus(600_000).unwrap()), Some(&Height::from_consensus(700_000).unwrap()), None, ) .await .unwrap(); assert_eq!(address_info.paging.total_pages, None); let mut websocket_info = blockbook_ws() .await .address_info_txids( counterparty_burner_addr(), None, Some(std::num::NonZeroU16::new(1).unwrap()), Some(Height::from_consensus(600_000).unwrap()), Some(Height::from_consensus(700_000).unwrap()), None, ) .await .unwrap(); websocket_info.paging.items_on_page = address_info.paging.items_on_page; assert_eq!(address_info, websocket_info); let address_info = blockbook() .await .address_info_specific(&xpub_addr(), None, None, None, None, None) .await .unwrap(); assert_eq!(address_info.paging.total_pages, Some(1)); let mut websocket_info = blockbook_ws() .await .address_info_txids(xpub_addr(), None, None, None, None, None) .await .unwrap(); websocket_info.paging.items_on_page = address_info.paging.items_on_page; assert_eq!(address_info, websocket_info); } #[ignore] #[tokio::test] async fn test_utxos_from_address() { let utxos = blockbook() .await .utxos_from_address(&counterparty_burner_addr(), false) .await .unwrap(); let last_utxo = Utxo { txid: "685623401c3f5e9d2eaaf0657a50454e56a270ee7630d409e98d3bc257560098" .parse() .unwrap(), vout: 0, value: Amount::from_sat(50_000), height: Some(Height::from_consensus(278_319).unwrap()), confirmations: utxos.last().unwrap().confirmations, locktime: None, coinbase: None, address: None, path: None, }; assert_eq!(utxos.last().unwrap(), &last_utxo); } fn xpub() -> &'static str { "zpub6qd36EtRVbyDyJToANn1vnuvhGenvepKnjeUzBXDk7JE4JYBxGUAPbjh22QqZ7JkRGuAtpgBfgKP1iT9GzgQxP1TgKPEBoN3e3vN3WtY2Su" } #[ignore] #[tokio::test] async fn test_utxos_from_xpub() { let utxos = blockbook() .await .utxos_from_xpub(xpub(), true) .await .unwrap(); let expected_utxo = Utxo { txid: "d60691ecdf678eb3f88ebfaf315d3907d0be62ccd40fd1f027938249966f268d" .parse() .unwrap(), vout: 1, value: Amount::from_sat(821), height: Some(Height::from_consensus(784_027).unwrap()), confirmations: utxos.get(0).unwrap().confirmations, locktime: None, address: Some(xpub_addr()), path: Some("m/84'/0'/0'/0/0".parse().unwrap()), coinbase: None, }; assert_eq!(utxos.get(0).unwrap(), &expected_utxo); } #[ignore] #[tokio::test] async fn test_xpub_info_basic_no_tokens() { let xpub_info = blockbook() .await .xpub_info_basic(xpub(), false, None, Some(&Currency::Btc)) .await .unwrap(); let expected_xpub_info = XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: Some(821f64 / 10f64.powf(8.0)), used_tokens: 1, tokens: None, }; assert_eq!(xpub_info, expected_xpub_info); } fn token() -> Token { Token { r#type: "XPUBAddress".to_string(), address: "bc1q5au2nmza9pmplnvgzyd4ky7egu2wya56qa024u" .parse::>() .unwrap() .assume_checked(), path: "m/84'/0'/0'/0/0".parse().unwrap(), transfers: 1, decimals: 8, balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), } } #[ignore] #[tokio::test] async fn test_xpub_info_txids() { let xpub_info = blockbook() .await .xpub_info(xpub(), None, None, None, None, false, None, None) .await .unwrap(); let expected_xpub_info = XPubInfo { paging: AddressInfoPaging { page: 1, total_pages: Some(1), items_on_page: 1000, }, basic: XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: None, used_tokens: 1, tokens: Some(vec![token()]), }, txids: Some(vec![ "d60691ecdf678eb3f88ebfaf315d3907d0be62ccd40fd1f027938249966f268d" .parse() .unwrap(), ]), transactions: None, }; assert_eq!(xpub_info, expected_xpub_info); } #[ignore] #[tokio::test] async fn test_xpub_info_specific() { let xpub_info = blockbook() .await .xpub_info( xpub(), Some(&std::num::NonZeroU32::new(1).unwrap()), Some(&std::num::NonZeroU16::new(1).unwrap()), Some(&Height::from_consensus(784_026).unwrap()), Some(&Height::from_consensus(784_028).unwrap()), false, Some(&AddressFilter::Used), Some(&Currency::Btc), ) .await .unwrap(); let expected_xpub_info = XPubInfo { paging: AddressInfoPaging { page: 1, total_pages: None, items_on_page: 1, }, basic: XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: Some(0.000_008_21), used_tokens: 1, tokens: Some(vec![token()]), }, txids: Some(vec![ "d60691ecdf678eb3f88ebfaf315d3907d0be62ccd40fd1f027938249966f268d" .parse() .unwrap(), ]), transactions: None, }; assert_eq!(xpub_info, expected_xpub_info); } #[ignore] #[tokio::test] async fn test_xpub_info_empty() { let xpub_info = blockbook() .await .xpub_info( xpub(), None, None, Some(&Height::from_consensus(784_000).unwrap()), Some(&Height::from_consensus(784_002).unwrap()), false, None, None, ) .await .unwrap(); let expected_xpub_info = XPubInfo { paging: AddressInfoPaging { page: 1, total_pages: Some(1), items_on_page: 1000, }, basic: XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: None, used_tokens: 1, tokens: Some(vec![token()]), }, txids: None, transactions: None, }; assert_eq!(xpub_info, expected_xpub_info); } #[allow(clippy::too_many_lines)] #[ignore] #[tokio::test] async fn test_xpub_info_entire_txs() { let xpub_info = blockbook() .await .xpub_info(xpub(), None, None, None, None, true, None, None) .await .unwrap(); let expected_xpub_info = XPubInfo { paging: AddressInfoPaging { page: 1, total_pages: Some(1), items_on_page: 1000, }, basic: XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: None, used_tokens: 1, tokens: Some(vec![token()]), }, txids: None, transactions: Some(vec![Transaction { txid: "d60691ecdf678eb3f88ebfaf315d3907d0be62ccd40fd1f027938249966f268d" .parse() .unwrap(), version: bitcoin::blockdata::transaction::Version::TWO, lock_time: Some(Height::from_consensus(784_026).unwrap()), vin: vec![Vin { txid: "6c646dc364b600afcfc16ae44ee910cfee11a176a0d015cf423e453e3b3e9e16" .parse() .unwrap(), vout: Some(24), sequence: Some(Sequence(4_294_967_293)), n: 0, addresses: vec!["3AVvCje8AHCXrvgL8ZNsqUFdiu4LxmdNJk" .parse::>() .unwrap() .assume_checked()], is_address: true, value: Amount::from_sat(137_000), }], vout: vec![ Vout { value: Amount::from_sat(135_146), n: 0, script: ScriptBuf::from_hex("a9143d46e616b172612000e823699338a5b0a9e6c6f387") .unwrap(), addresses: vec!["37H25Nx78ueHFhwr2KWGVcVu1155WLtb92" .parse::>() .unwrap() .assume_checked()], is_address: true, spent: Some(true), spent_tx_id: Some("c3a043db12a4f30c4b85d123a32230566d762a16767d5d1f285d8f539342e746".parse().unwrap()), spent_height: Some(Height::from_consensus(787_200).unwrap()), spent_index: None, is_own: None, }, Vout { value: Amount::from_sat(821), n: 1, script: ScriptBuf::from_hex("0014a778a9ec5d28761fcd88111b5b13d94714e2769a") .unwrap(), addresses: vec!["bc1q5au2nmza9pmplnvgzyd4ky7egu2wya56qa024u" .parse::>() .unwrap() .assume_checked()], is_address: true, spent: None, spent_tx_id: None, spent_height: None, spent_index: None, is_own: Some(true), }, ], block_hash: Some("0000000000000000000036fe411dd9dd3ef7ce4531d65314e9ab73637dab3f68" .parse() .unwrap()), block_height: Some(Height::from_consensus(784_027).unwrap()), confirmations: xpub_info .transactions .as_ref() .unwrap() .get(0) .unwrap() .confirmations, block_time: "1680680375".parse().unwrap(), size: 375, vsize: 206, value: Amount::from_sat(135_967), value_in: Amount::from_sat(137_000), fees: Amount::from_sat(1_033), script: ScriptBuf::from_hex( "02000000000101169e3e3b3e453e42cf15d0a076a111eecf10e94ee46ac1cfaf00b664c36d646c18000000232200206e0d706cbd560f4c9845604991ce15a47dea5e854f9b56ae11decf6d98a7a821fdffffff02ea0f02000000000017a9143d46e616b172612000e823699338a5b0a9e6c6f3873503000000000000160014a778a9ec5d28761fcd88111b5b13d94714e2769a03473044022018286becb369315af56ea7ab496588ace0e34e17b9c27b6ea06c65f794f07a4202202617528bfffcb75083700a86ee40551c25c8f62717ac962ce4733d033729abda014730440220276366c3cc6b1947d7be49ceb54c5ba6511ad74596119b7beeaf8b0bb21c267f02201a6e96d8735e08db6155b49e0ef4c9344c8f663a3dda3f795c718e43d7210294014e2102fab4f766e50937a07b96b045d12e210a28ca43570e9d46f8a6f6b3c385d5e7c7ad2102eef58d1fe659a43ef061b3646d51719f1005de7b74967786e75c5c6fb4e739a9ac73640380ca00b2689af60b00" ) .unwrap(), }]), }; assert_eq!(xpub_info, expected_xpub_info); } #[ignore] #[tokio::test] async fn test_xpub_info_basic_tokens() { let xpub_info = blockbook() .await .xpub_info_basic(xpub(), true, None, None) .await .unwrap(); let expected_xpub_info = XPubInfoBasic { address: xpub().to_string(), balance: Amount::from_sat(821), total_received: Amount::from_sat(821), total_sent: Amount::from_sat(0), unconfirmed_balance: Amount::from_sat(0), unconfirmed_txs: 0, txs: 1, used_addresses_count: 1, secondary_value: None, used_tokens: 1, tokens: Some(vec![token()]), }; assert_eq!(xpub_info, expected_xpub_info); }