Skip to content

Commit

Permalink
Move reqwest from core to external transport impl
Browse files Browse the repository at this point in the history
  • Loading branch information
pashinov committed Dec 21, 2024
1 parent f2793e2 commit f6f0cc3
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 111 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ parking_lot = "0.12.0"
pbkdf2 = { version = "0.12.2", optional = true }
quick_cache = "0.4.1"
rand = { version = "0.8", features = ["getrandom"], optional = true }
reqwest = { version = "0.11.8", features = ["json"], optional = true }
secstr = { version = "0.5.0", features = ["serde"], optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down Expand Up @@ -88,7 +87,7 @@ proto_transport = ["dep:nekoton-proto"]
extended_models = []
non_threadsafe = []
wallet_core = ["dep:pbkdf2", "dep:chacha20poly1305", "dep:zeroize", "dep:secstr", "dep:hmac", "dep:ed25519-dalek",
"dep:tiny-bip39", "dep:tiny-hderive", "dep:sha2", "dep:getrandom", "dep:rand", "dep:curve25519-dalek-ng", "dep:reqwest", "nekoton-utils/encryption"]
"dep:tiny-bip39", "dep:tiny-hderive", "dep:sha2", "dep:getrandom", "dep:rand", "dep:curve25519-dalek-ng", "nekoton-utils/encryption"]

[package.metadata.docs.rs]
all-features = true
3 changes: 3 additions & 0 deletions nekoton-transport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ nekoton-utils = { path = "../nekoton-utils" }
nekoton = { path = ".." }

[dev-dependencies]
base64 = "0.13"
tokio = { version = "1", features = ["sync", "time", "macros"] }

ton_types = { git = "https://github.com/broxus/ton-labs-types.git" }

[features]
default = ["gql_transport"]
gql_transport = ["nekoton/gql_transport"]
Expand Down
212 changes: 212 additions & 0 deletions nekoton-transport/src/gql_dton.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use std::convert::TryInto;
use std::sync::Arc;

use anyhow::{Context, Result};
use nekoton_utils::*;
use reqwest::Url;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GqlDtonNetworkSettings {
pub endpoint: String,
/// Gql node type
pub local: bool,
}

pub struct GqlDtonClient {
client: reqwest::Client,
endpoint: Endpoint,
local: bool,
}

impl GqlDtonClient {
pub fn new(settings: GqlDtonNetworkSettings) -> Result<Arc<Self>> {
let endpoint = &settings.endpoint;
let endpoint = Endpoint::new(endpoint)
.with_context(|| format!("failed to parse endpoint: {}", endpoint))?;

let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::CONTENT_TYPE,
reqwest::header::HeaderValue::from_static("application/json"),
);

let client = reqwest::ClientBuilder::new()
.default_headers(headers)
.build()
.context("failed to build http client")?;

Ok(Arc::new(Self {
client,
endpoint,
local: settings.local,
}))
}
}

#[cfg_attr(not(feature = "non_threadsafe"), async_trait::async_trait)]
#[cfg_attr(feature = "non_threadsafe", async_trait::async_trait(?Send))]
impl nekoton::external::GqlConnection for GqlDtonClient {
fn is_local(&self) -> bool {
self.local
}

async fn post(&self, req: nekoton::external::GqlRequest) -> Result<String> {
let response = self
.client
.post(self.endpoint.gql.clone())
.body(req.data)
.send()
.await?;
Ok(response.text().await?)
}
}

struct Endpoint {
gql: Url,
}

impl Endpoint {
fn new(url: &str) -> Result<Self> {
let gql = expand_address(url);
Ok(Self {
gql: gql.as_str().try_into()?,
})
}
}

fn expand_address(base_url: &str) -> String {
match base_url.trim_end_matches('/') {
url if base_url.starts_with("http://") || base_url.starts_with("https://") => {
format!("{}/graphql", url)
}
url @ ("localhost" | "127.0.0.1") => format!("http://{}/graphql", url),
url => format!("https://{}/graphql", url),
}
}

#[cfg(test)]
mod tests {
use nekoton::abi::num_traits::ToPrimitive;
use nekoton::abi::ExecutionContext;
use nekoton::contracts::jetton;
use nekoton::core::utils::update_library_cell;
use nekoton::external::{GqlConnection, GqlRequest};

use super::*;

#[tokio::test]
async fn gql_client_works() -> Result<()> {
let client = GqlDtonClient::new(GqlDtonNetworkSettings {
endpoint: "https://dton.io/graphql".to_string(),
local: false,
})?;

const QUERY: &str = r#"{"query":"{\n get_lib(\n lib_hash: \"0F1AD3D8A46BD283321DDE639195FB72602E9B31B1727FECC25E2EDC10966DF4\"\n )\n}"}"#;

let _response = client
.post(GqlRequest {
data: QUERY.to_string(),
long_query: false,
})
.await?;

Ok(())
}

#[tokio::test]
async fn usdt_wallet_token_contract() -> Result<()> {
let client = GqlDtonClient::new(GqlDtonNetworkSettings {
endpoint: "https://dton.io/graphql".to_string(),
local: false,
})?;

let cell = ton_types::deserialize_tree_of_cells(&mut base64::decode("te6ccgEBAwEAqAACbIAXsqVXAuRG6+GFp/25WVl2IsmatSkX0jbrXVjoBOwsnEQNAdiGdFv5kAABdRDp2cQZrn10JgIBAJEFJFfQYxaABHulQdJwYfnHP5r0FXhq3wjit36+D+zzx7bkE76OQgrwAsROplLUCShZxn2kTkyjrdZWWw4ol9ZAosUb+zcNiHf6CEICj0Utek39dAZraCNlF3JZ7QVzRDW+drX9S9XYryt8PWg=").unwrap().as_slice()).unwrap();
let mut state = deserialize_account_stuff(cell)?;

update_library_cell(client.as_ref(), &mut state.storage.state).await?;

let contract = jetton::TokenWalletContract(ExecutionContext {
clock: &SimpleClock,
account_stuff: &state,
});

let balance = contract.balance()?;
assert_eq!(balance.to_u128().unwrap(), 156092097302);

Ok(())
}

#[tokio::test]
async fn notcoin_wallet_token_contract() -> Result<()> {
let client = GqlDtonClient::new(GqlDtonNetworkSettings {
endpoint: "https://dton.io/graphql".to_string(),
local: false,
})?;

let cell = ton_types::deserialize_tree_of_cells(&mut base64::decode("te6ccgEBAwEAqgACbIAX5XxfY9N6rJiyOS4NGQc01nd0dzEnWBk87cdqg9bLTwQNAeCGdH/3UAABdXbIjToZrn5eJgIBAJUHFxcOBj4fBYAfGfo6PQWliRZGmmqpYpA1QxmYkyLZonLf41f59x68XdAAvlWFDxGF2lXm67y4yzC17wYKD9A0guwPkMs1gOsM//IIQgK6KRjIlH6bJa+awbiDNXdUFz5YEvgHo9bmQqFHCVlTlQ==").unwrap().as_slice()).unwrap();
let mut state = deserialize_account_stuff(cell)?;

update_library_cell(client.as_ref(), &mut state.storage.state).await?;

let contract = jetton::TokenWalletContract(ExecutionContext {
clock: &SimpleClock,
account_stuff: &state,
});

let balance = contract.balance()?;
assert_eq!(balance.to_u128().unwrap(), 6499273466060549);

Ok(())
}

#[tokio::test]
async fn mintless_points_token_wallet_contract() -> Result<()> {
let client = GqlDtonClient::new(GqlDtonNetworkSettings {
endpoint: "https://dton.io/graphql".to_string(),
local: false,
})?;

let cell =
ton_types::deserialize_tree_of_cells(&mut base64::decode("te6ccgEBAwEAyQACbIAMC6d7f4iHKlXHXBfufxF6w/5pIENHdpy1yJnyM+lsrQQNAl2Gc+Ll0AABc7gAAbghs2ElpgIBANQFAlQL5ACADZRqTnEksRaYvpXRMbgzB92SzFv/19WbfQQgdDo7lYwQA+mfQx3OTMfvDyPCOAYxl9HdjYWqWkQCtdgoLLcHjaDKvtRVlwuLLP8LwzhcDJNm1TPewFBFqmlIYet7ln0NupwfCEICDvGeG/QPK6SS/KrDhu7KWb9oJ6OFBwjZ/NmttoOrwzY=").unwrap().as_slice())
.unwrap();
let mut state = deserialize_account_stuff(cell)?;

update_library_cell(client.as_ref(), &mut state.storage.state).await?;

let contract = jetton::TokenWalletContract(ExecutionContext {
clock: &SimpleClock,
account_stuff: &state,
});

let data = contract.get_details()?;
assert_eq!(data.balance.to_u128().unwrap(), 10000000000);

Ok(())
}

#[tokio::test]
async fn hamster_token_wallet_contract() -> Result<()> {
let client = GqlDtonClient::new(GqlDtonNetworkSettings {
endpoint: "https://dton.io/graphql".to_string(),
local: false,
})?;

let cell =
ton_types::deserialize_tree_of_cells(&mut base64::decode("te6ccgEBAwEAyQACbIAKqccjBo+00V2Pb7qZhRYSHX52cx1iP9tpON3cdZrkP8QNAl2GdhkS0AABegbul1whs2ElpgIBANQFGHJ82gCACGZPh6infgRlai2q2zEzj6/XTCUYYz5sBXNuHUXFkiawACfLlnexAarJqUlmkXX/yPvEfPlx8Id4LDSocvlK3az1CNK1yFN5P0+WKSDutZY4tqmGqAE7w+lQchEcy4oOjEQUCEICDxrT2KRr0oMyHd5jkZX7cmAumzGxcn/swl4u3BCWbfQ=").unwrap().as_slice())
.unwrap();
let mut state = deserialize_account_stuff(cell)?;

update_library_cell(client.as_ref(), &mut state.storage.state).await?;

let contract = jetton::TokenWalletContract(ExecutionContext {
clock: &SimpleClock,
account_stuff: &state,
});

let data = contract.get_details()?;
assert_eq!(data.balance.to_u128().unwrap(), 105000000000);

Ok(())
}
}
2 changes: 2 additions & 0 deletions nekoton-transport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@

#[cfg(feature = "gql_transport")]
pub mod gql;
#[cfg(feature = "gql_transport")]
mod gql_dton;
#[cfg(feature = "jrpc_transport")]
pub mod jrpc;
#[cfg(feature = "proto_transport")]
Expand Down
15 changes: 10 additions & 5 deletions src/core/contract_subscription/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,36 @@ use super::models::{
use super::{utils, PollingMethod};

use crate::core::utils::{MessageContext, PendingTransactionsExt};
use crate::external::GqlConnection;
use crate::transport::models::{RawContractState, RawTransaction};
use crate::transport::Transport;

/// Used as a base object for different listeners implementation
pub struct ContractSubscription {
clock: Arc<dyn Clock>,
transport: Arc<dyn Transport>,
dton_connection: Option<Arc<dyn GqlConnection>>,
address: MsgAddressInt,
contract_state: ContractState,
latest_known_lt: Option<u64>,
pending_transactions: Vec<PendingTransaction>,
transactions_synced: bool,
supports_library_cells: bool,
}

impl ContractSubscription {
pub async fn subscribe(
clock: Arc<dyn Clock>,
transport: Arc<dyn Transport>,
dton_connection: Option<Arc<dyn GqlConnection>>,
address: MsgAddressInt,
on_contract_state: OnContractState<'_>,
on_transactions_found: Option<OnTransactionsFound<'_>>,
supports_library_cells: bool,
) -> Result<Self> {
let mut result = Self {
clock,
transport,
dton_connection,
address,
supports_library_cells,
contract_state: Default::default(),
latest_known_lt: None,
pending_transactions: Vec::new(),
Expand Down Expand Up @@ -415,9 +416,13 @@ impl ContractSubscription {
};

if updated {
if self.supports_library_cells {
if let Some(connection) = self.dton_connection.as_ref() {
if let RawContractState::Exists(state) = &mut contract_state {
utils::update_library_cell(&mut state.account.storage.state).await?;
utils::update_library_cell(
connection.as_ref(),
&mut state.account.storage.state,
)
.await?;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/generic_contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ impl GenericContract {
ContractSubscription::subscribe(
clock,
transport,
None,
address,
&mut make_contract_state_handler(handler),
on_transactions_found,
false,
)
.await?
};
Expand Down
Loading

0 comments on commit f6f0cc3

Please sign in to comment.