From d7a862d31bf2eeaed975776ec45478c8ecd1d357 Mon Sep 17 00:00:00 2001 From: higherordertech Date: Thu, 25 Apr 2024 17:43:05 +1000 Subject: [PATCH] feat: P-584 added bitcoin holding amount vc using new dataprovider https://blockchain.info --- bitacross-worker/docker/docker-compose.yml | 1 + .../docker/multiworker-docker-compose.yml | 3 + local-setup/.env.dev | 1 + primitives/core/src/assertion/web3_token.rs | 9 + .../commands/litentry/request_vc.rs | 2 + .../interfaces/vc/definitions.ts | 1 + tee-worker/docker/docker-compose.yml | 1 + .../docker/multiworker-docker-compose.yml | 3 + .../src/token_holding_amount/mod.rs | 51 ++++++ .../core/common/src/web3_token/mod.rs | 7 +- .../data-providers/src/blockchain_info.rs | 166 ++++++++++++++++++ .../litentry/core/data-providers/src/lib.rs | 36 +++- .../core/mock-server/src/blockchain_info.rs | 37 ++++ .../litentry/core/mock-server/src/lib.rs | 2 + .../web3_token/token_balance/btc_balance.rs | 60 +++++++ .../src/web3_token/token_balance/mod.rs | 2 + 16 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 tee-worker/litentry/core/data-providers/src/blockchain_info.rs create mode 100644 tee-worker/litentry/core/mock-server/src/blockchain_info.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/btc_balance.rs diff --git a/bitacross-worker/docker/docker-compose.yml b/bitacross-worker/docker/docker-compose.yml index c7f2fba9c4..88b349080b 100644 --- a/bitacross-worker/docker/docker-compose.yml +++ b/bitacross-worker/docker/docker-compose.yml @@ -140,6 +140,7 @@ services: - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: diff --git a/bitacross-worker/docker/multiworker-docker-compose.yml b/bitacross-worker/docker/multiworker-docker-compose.yml index 6e992ed2ec..d352548703 100644 --- a/bitacross-worker/docker/multiworker-docker-compose.yml +++ b/bitacross-worker/docker/multiworker-docker-compose.yml @@ -141,6 +141,7 @@ services: - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: @@ -196,6 +197,7 @@ services: - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: @@ -251,6 +253,7 @@ services: - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: diff --git a/local-setup/.env.dev b/local-setup/.env.dev index aca616450a..358875ca36 100644 --- a/local-setup/.env.dev +++ b/local-setup/.env.dev @@ -55,3 +55,4 @@ LITENTRY_ARCHIVE_URL=http://localhost:19527 MORALIS_API_URL=http://localhost:19527/moralis/ MORALIS_SOLANA_API_URL=http://localhost:19527/moralis_solana/ KARAT_DAO_API_URL=http://localhost:19527/karat_dao/ +BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ diff --git a/primitives/core/src/assertion/web3_token.rs b/primitives/core/src/assertion/web3_token.rs index 05cf45a96d..d48d406d80 100644 --- a/primitives/core/src/assertion/web3_token.rs +++ b/primitives/core/src/assertion/web3_token.rs @@ -72,6 +72,8 @@ pub enum Web3TokenType { Sol, #[codec(index = 24)] Mcrt, + #[codec(index = 25)] + Btc, } impl Web3TokenType { @@ -88,6 +90,13 @@ impl Web3TokenType { Self::Nfp => vec![Web3Network::Bsc], Self::Sol | Self::Mcrt => vec![Web3Network::Bsc, Web3Network::Ethereum, Web3Network::Solana], + Self::Btc => vec![ + Web3Network::BitcoinP2tr, + Web3Network::BitcoinP2pkh, + Web3Network::BitcoinP2sh, + Web3Network::BitcoinP2wpkh, + Web3Network::BitcoinP2wsh, + ], _ => vec![Web3Network::Ethereum], } } diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index 1e0a15d7b3..076d26433c 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -265,6 +265,7 @@ pub enum TokenHoldingAmountCommand { Nfp, Sol, Mcrt, + Btc, } #[derive(Subcommand, Debug)] @@ -606,6 +607,7 @@ impl Command { TokenHoldingAmountCommand::Nfp => TokenHoldingAmount(Web3TokenType::Nfp), TokenHoldingAmountCommand::Sol => TokenHoldingAmount(Web3TokenType::Sol), TokenHoldingAmountCommand::Mcrt => TokenHoldingAmount(Web3TokenType::Mcrt), + TokenHoldingAmountCommand::Btc => TokenHoldingAmount(Web3TokenType::Btc), }, Command::PlatformUser(arg) => match arg { PlatformUserCommand::KaratDaoUser => PlatformUser(PlatformUserType::KaratDaoUser), diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts index eb20aa6b49..7e669ea748 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts @@ -184,6 +184,7 @@ export default { "Nfp", "Sol", "Mcrt", + "Btc", ], }, // PlatformUserType diff --git a/tee-worker/docker/docker-compose.yml b/tee-worker/docker/docker-compose.yml index 890c931eaf..8f0903dea9 100644 --- a/tee-worker/docker/docker-compose.yml +++ b/tee-worker/docker/docker-compose.yml @@ -142,6 +142,7 @@ services: - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID - LITENTRY_ARCHIVE_URL=http://localhost:19527 - VIP3_URL=http://localhost:19527 + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: diff --git a/tee-worker/docker/multiworker-docker-compose.yml b/tee-worker/docker/multiworker-docker-compose.yml index ef344da0f0..22f86dc020 100644 --- a/tee-worker/docker/multiworker-docker-compose.yml +++ b/tee-worker/docker/multiworker-docker-compose.yml @@ -143,6 +143,7 @@ services: - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID - LITENTRY_ARCHIVE_URL=http://localhost:19527 - VIP3_URL=http://localhost:19527 + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: @@ -200,6 +201,7 @@ services: - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID - LITENTRY_ARCHIVE_URL=http://localhost:19527 - VIP3_URL=http://localhost:19527 + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: @@ -257,6 +259,7 @@ services: - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID - LITENTRY_ARCHIVE_URL=http://localhost:19527 - VIP3_URL=http://localhost:19527 + - BLOCKCHAIN_INFO_API_URL=http://localhost:19527/blockchain_info/ networks: - litentry-test-network healthcheck: diff --git a/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs index c81d76ad0d..f515c11c23 100644 --- a/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs +++ b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs @@ -151,6 +151,9 @@ mod tests { data_provider_config .set_nodereal_api_chain_network_url(url.clone() + "/nodereal_jsonrpc/") .unwrap(); + data_provider_config + .set_blockchain_info_api_url(url.clone() + "/blockchain_info/") + .unwrap(); data_provider_config.set_achainable_url(url.clone()).unwrap(); data_provider_config.set_moralis_api_url(url.clone() + "/moralis/").unwrap(); data_provider_config @@ -608,4 +611,52 @@ mod tests { }, } } + + #[test] + fn build_btc_holding_amount_works() { + let data_provider_config: DataProviderConfig = init(); + // bc1pgr5fw4p9gl9me0vzjklnlnap669caxc0gsk4j62gff2qktlw6naqm4m3d0 + let address = decode_hex( + "0x02e8c39e82aaaa143c3def8d3c7084a539b227244ac9067c3f7fc86cb73a0b7aed" + .as_bytes() + .to_vec(), + ) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Bitcoin(address), vec![Web3Network::BitcoinP2tr])]; + + let req = crate_assertion_build_request(Web3TokenType::Btc, identities); + + match build(&req, Web3TokenType::Btc, &data_provider_config) { + Ok(credential) => { + log::info!("build btc TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::Btc), + create_network_address_assertion_logics(Web3TokenType::Btc), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "1600".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "3000".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build btc TokenHoldingAmount failed with error {:?}", e); + }, + } + } } diff --git a/tee-worker/litentry/core/common/src/web3_token/mod.rs b/tee-worker/litentry/core/common/src/web3_token/mod.rs index ec0612436b..94a21330e1 100644 --- a/tee-worker/litentry/core/common/src/web3_token/mod.rs +++ b/tee-worker/litentry/core/common/src/web3_token/mod.rs @@ -58,6 +58,7 @@ impl TokenName for Web3TokenType { Self::Nfp => "NFP", Self::Sol => "SOL", Self::Mcrt => "MCRT", + Self::Btc => "BTC", } } } @@ -214,7 +215,11 @@ impl TokenDecimals for Web3TokenType { // Wbtc (Self::Wbtc, Web3Network::Bsc) | (Self::Wbtc, Web3Network::Ethereum) | // Mcrt - (Self::Mcrt, Web3Network::Solana) => 8, + (Self::Mcrt, Web3Network::Solana) | + // Btc + (Self::Btc, Web3Network::BitcoinP2tr) | (Self::Btc, Web3Network::BitcoinP2pkh) | + (Self::Btc, Web3Network::BitcoinP2sh) | (Self::Btc, Web3Network::BitcoinP2wpkh) | + (Self::Btc, Web3Network::BitcoinP2wsh) => 8, // Usdc (Self::Usdc, Web3Network::Ethereum) | // Usdt diff --git a/tee-worker/litentry/core/data-providers/src/blockchain_info.rs b/tee-worker/litentry/core/data-providers/src/blockchain_info.rs new file mode 100644 index 0000000000..13d95b0e0e --- /dev/null +++ b/tee-worker/litentry/core/data-providers/src/blockchain_info.rs @@ -0,0 +1,166 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use crate::{ + build_client_with_cert, DataProviderConfig, Error, HttpError, ReqPath, RetryOption, + RetryableRestGet, +}; +use http::header::CONNECTION; +use http_req::response::Headers; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestPath, +}; +use log::debug; +use serde::{Deserialize, Serialize}; +use std::{ + format, str, + string::{String, ToString}, + vec, + vec::Vec, +}; + +pub struct BlockChainInfoClient { + retry_option: RetryOption, + client: RestClient>, +} + +#[derive(Debug)] +pub struct BlockChainInfoRequest { + path: String, + query: Option>, +} + +impl BlockChainInfoClient { + pub fn new(data_provider_config: &DataProviderConfig) -> Self { + let api_retry_delay = data_provider_config.blockchain_info_api_retry_delay; + let api_retry_times = data_provider_config.blockchain_info_api_retry_times; + let api_url = data_provider_config.blockchain_info_api_url.clone(); + let retry_option = + RetryOption { retry_delay: Some(api_retry_delay), retry_times: Some(api_retry_times) }; + + let mut headers = Headers::new(); + headers.insert(CONNECTION.as_str(), "close"); + let client = build_client_with_cert(api_url.as_str(), headers); + + BlockChainInfoClient { retry_option, client } + } + + fn get(&mut self, params: BlockChainInfoRequest, fast_fail: bool) -> Result + where + T: serde::de::DeserializeOwned + for<'a> RestPath>, + { + let retry_option: Option = + if fast_fail { None } else { Some(self.retry_option.clone()) }; + if let Some(query) = params.query { + let transformed_query: Vec<(&str, &str)> = + query.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); + self.client.get_with_retry::( + ReqPath::new(params.path.as_str()), + &transformed_query, + retry_option, + ) + } else { + self.client + .get_retry::(ReqPath::new(params.path.as_str()), retry_option) + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetSingleAddressResponse { + pub final_balance: u128, +} + +impl<'a> RestPath> for GetSingleAddressResponse { + fn get_path(path: ReqPath) -> Result { + Ok(path.path.into()) + } +} + +pub trait BlockChainInfoDataApi { + fn get_single_address( + &mut self, + address: String, + fail_fast: bool, + ) -> Result; +} + +impl BlockChainInfoDataApi for BlockChainInfoClient { + fn get_single_address( + &mut self, + address: String, + fail_fast: bool, + ) -> Result { + let query: Vec<(String, String)> = vec![("limit".to_string(), "0".into())]; + + let params = + BlockChainInfoRequest { path: format!("rawaddr/{}", address), query: Some(query) }; + + debug!("get_single_address, params: {:?}", params); + + match self.get::(params, fail_fast) { + Ok(resp) => { + debug!("get_single_address, response: {:?}", resp); + Ok(resp) + }, + Err(e) => { + debug!("get_single_address, error: {:?}", e); + Err(e) + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use lc_mock_server::run; + + fn init() -> DataProviderConfig { + let _ = env_logger::builder().is_test(true).try_init(); + let url = run(0).unwrap() + "/blockchain_info/"; + + let mut config = DataProviderConfig::new().unwrap(); + config.set_blockchain_info_api_url(url).unwrap(); + config + } + + #[test] + fn does_get_single_address_works() { + let config = init(); + let mut client = BlockChainInfoClient::new(&config); + let mut response = client + .get_single_address( + "bc1pgr5fw4p9gl9me0vzjklnlnap669caxc0gsk4j62gff2qktlw6naqm4m3d0".into(), + true, + ) + .unwrap(); + assert_eq!(response.final_balance, 185123167511); + + response = client + .get_single_address("bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj".into(), false) + .unwrap(); + assert_eq!(response.final_balance, 0); + } +} diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index ae989e1c90..4f6bfd162d 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -64,6 +64,7 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam pub mod achainable; pub mod achainable_names; +pub mod blockchain_info; pub mod discord_litentry; pub mod discord_official; pub mod geniidata; @@ -197,6 +198,9 @@ pub struct DataProviderConfig { pub moralis_api_retry_delay: u64, pub moralis_api_retry_times: u16, pub moralis_api_key: String, + pub blockchain_info_api_retry_delay: u64, + pub blockchain_info_api_retry_times: u16, + pub blockchain_info_api_url: String, } impl DataProviderConfig { @@ -238,6 +242,9 @@ impl DataProviderConfig { moralis_api_retry_times: 2, moralis_api_url: "https://deep-index.moralis.io/api/v2.2/".to_string(), moralis_solana_api_url: "https://solana-gateway.moralis.io/".to_string(), + blockchain_info_api_retry_delay: 5000, + blockchain_info_api_retry_times: 2, + blockchain_info_api_url: "https://blockchain.info/".to_string(), }; // we allow to override following config properties for non prod dev @@ -273,7 +280,7 @@ impl DataProviderConfig { if let Ok(v) = env::var("NODEREAL_API_RETRY_DELAY") { config.set_nodereal_api_retry_delay(v.parse::().unwrap()); } - if let Ok(v) = env::var("NODEREAL_API_RETRY_TIME") { + if let Ok(v) = env::var("NODEREAL_API_RETRY_TIMES") { config.set_nodereal_api_retry_times(v.parse::().unwrap()); } if let Ok(v) = env::var("NODEREAL_API_CHAIN_NETWORK_URL") { @@ -300,7 +307,7 @@ impl DataProviderConfig { if let Ok(v) = env::var("KARAT_DAO_API_RETRY_DELAY") { config.set_karat_dao_api_retry_delay(v.parse::().unwrap()); } - if let Ok(v) = env::var("KARAT_DAO_API_RETRY_TIME") { + if let Ok(v) = env::var("KARAT_DAO_API_RETRY_TIMES") { config.set_karat_dao_api_retry_times(v.parse::().unwrap()); } if let Ok(v) = env::var("KARAT_DAO_API_URL") { @@ -315,9 +322,18 @@ impl DataProviderConfig { if let Ok(v) = env::var("MORALIS_API_RETRY_DELAY") { config.set_moralis_api_retry_delay(v.parse::().unwrap()); } - if let Ok(v) = env::var("MORALIS_API_RETRY_TIME") { + if let Ok(v) = env::var("MORALIS_API_RETRY_TIMES") { config.set_moralis_api_retry_times(v.parse::().unwrap()); } + if let Ok(v) = env::var("BLOCKCHAIN_INFO_API_URL") { + config.set_blockchain_info_api_url(v)?; + } + if let Ok(v) = env::var("BLOCKCHAIN_INFO_API_RETRY_DELAY") { + config.set_blockchain_info_api_retry_delay(v.parse::().unwrap()); + } + if let Ok(v) = env::var("BLOCKCHAIN_INFO_API_RETRY_TIMES") { + config.set_blockchain_info_api_retry_times(v.parse::().unwrap()); + } }; // set secrets from env variables if let Ok(v) = env::var("TWITTER_AUTH_TOKEN_V2") { @@ -497,6 +513,20 @@ impl DataProviderConfig { self.moralis_solana_api_url = v; Ok(()) } + pub fn set_blockchain_info_api_retry_delay(&mut self, v: u64) { + debug!("set_blockchain_info_api_retry_delay: {:?}", v); + self.blockchain_info_api_retry_delay = v; + } + pub fn set_blockchain_info_api_retry_times(&mut self, v: u16) { + debug!("set_blockchain_info_api_retry_times: {:?}", v); + self.blockchain_info_api_retry_times = v; + } + pub fn set_blockchain_info_api_url(&mut self, v: String) -> Result<(), Error> { + check_url(&v)?; + debug!("set_blockchain_info_api_url: {:?}", v); + self.blockchain_info_api_url = v; + Ok(()) + } } fn check_url(v: &String) -> Result<(), Error> { diff --git a/tee-worker/litentry/core/mock-server/src/blockchain_info.rs b/tee-worker/litentry/core/mock-server/src/blockchain_info.rs new file mode 100644 index 0000000000..cee5e97a2a --- /dev/null +++ b/tee-worker/litentry/core/mock-server/src/blockchain_info.rs @@ -0,0 +1,37 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +#![allow(opaque_hidden_inferred_bound)] + +use std::collections::HashMap; + +use lc_data_providers::blockchain_info::GetSingleAddressResponse; + +use warp::{http::Response, Filter}; + +pub(crate) fn query() -> impl Filter + Clone { + warp::get() + .and(warp::path!("blockchain_info" / "rawaddr" / String)) + .and(warp::query::>()) + .map(move |address, _| { + if address == "bc1pgr5fw4p9gl9me0vzjklnlnap669caxc0gsk4j62gff2qktlw6naqm4m3d0" { + let body = GetSingleAddressResponse { final_balance: 185123167511 }; + Response::builder().body(serde_json::to_string(&body).unwrap()) + } else { + let body = GetSingleAddressResponse { final_balance: 0 }; + Response::builder().body(serde_json::to_string(&body).unwrap()) + } + }) +} diff --git a/tee-worker/litentry/core/mock-server/src/lib.rs b/tee-worker/litentry/core/mock-server/src/lib.rs index cb29eb3cbe..04d06f44ea 100644 --- a/tee-worker/litentry/core/mock-server/src/lib.rs +++ b/tee-worker/litentry/core/mock-server/src/lib.rs @@ -22,6 +22,7 @@ use tokio::{ use warp::Filter; pub mod achainable; +pub mod blockchain_info; pub mod discord_litentry; pub mod discord_official; pub mod geniidata; @@ -69,6 +70,7 @@ pub fn run(port: u16) -> Result { .or(karat_dao::query()) .or(moralis::query()) .or(moralis::query_solana()) + .or(blockchain_info::query()) .or(achainable::query()) .or(litentry_archive::query_user_joined_evm_campaign()) .or(vip3::query_user_sbt_level()) diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/btc_balance.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/btc_balance.rs new file mode 100644 index 0000000000..cd1123b9df --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/btc_balance.rs @@ -0,0 +1,60 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; + +use lc_common::{ + abort_strategy::{loop_with_abort_strategy, AbortStrategy, LoopControls}, + web3_token::TokenDecimals, +}; +use lc_data_providers::blockchain_info::{BlockChainInfoClient, BlockChainInfoDataApi}; + +use crate::*; + +use super::common::calculate_balance_with_decimals; + +pub fn get_balance( + addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, +) -> Result { + let mut total_balance = 0_f64; + + loop_with_abort_strategy( + addresses, + |(network, address)| { + let decimals = Web3TokenType::Btc.get_decimals(network.clone()); + let mut client = BlockChainInfoClient::new(data_provider_config); + + match client.get_single_address(address.clone(), false) { + Ok(response) => { + total_balance += + calculate_balance_with_decimals(response.final_balance, decimals); + Ok(LoopControls::Continue) + }, + Err(err) => Err(err.into_error_detail()), + } + }, + AbortStrategy::FailFast:: bool>, + )?; + + Ok(total_balance) +} diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs index b82c8932cb..6965036f69 100644 --- a/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs @@ -25,6 +25,7 @@ use core::result::Result; use crate::*; mod bnb_balance; +mod btc_balance; mod common; mod eth_balance; mod lit_balance; @@ -40,6 +41,7 @@ pub fn get_token_balance( Web3TokenType::Eth => eth_balance::get_balance(addresses, data_provider_config), Web3TokenType::Lit => lit_balance::get_balance(addresses, data_provider_config), Web3TokenType::Sol => sol_balance::get_balance(addresses, data_provider_config), + Web3TokenType::Btc => btc_balance::get_balance(addresses, data_provider_config), _ => common::get_balance(addresses, token_type, data_provider_config), } }