Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ed25519-dalek = { git = "https://github.com/broxus/ed25519-dalek.git", optional
tiny-bip39 = { git = "https://github.com/broxus/tiny-bip39.git", default-features = false, optional = true }
tiny-hderive = { git = "https://github.com/broxus/tiny-hderive.git", optional = true }

ton_abi = { git = "https://github.com/broxus/ton-labs-abi" }
ton_abi = { git = "https://github.com/broxus/ton-labs-abi"}
ton_block = { git = "https://github.com/broxus/ton-labs-block.git" }
ton_executor = { git = "https://github.com/broxus/ton-labs-executor.git" }
ton_types = { git = "https://github.com/broxus/ton-labs-types.git" }
Expand All @@ -67,7 +67,7 @@ nekoton-utils = { path = "nekoton-utils" }
nekoton-proto = { path = "nekoton-proto", optional = true }

[dev-dependencies]
reqwest = { version = "0.11.8", features = ["gzip"] }
reqwest = { version = "0.11.8", features = ["gzip", "json"] }
cargo-husky = { version = "1", features = ["default", "run-cargo-fmt", "run-cargo-check"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

Expand All @@ -85,6 +85,7 @@ web = [
gql_transport = ["dep:erased-serde"]
jrpc_transport = ["dep:tiny-jsonrpc"]
proto_transport = ["dep:nekoton-proto"]
ton_transport = []
extended_models = []
non_threadsafe = []
wallet_core = ["dep:pbkdf2", "dep:chacha20poly1305", "dep:zeroize", "dep:secstr", "dep:hmac", "dep:ed25519-dalek",
Expand Down
3 changes: 2 additions & 1 deletion nekoton-transport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ futures-util = "0.3"
log = "0.4"
reqwest = { version = "0.11", features = ["json", "gzip", "rustls-tls"], default-features = false }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1", features = ["sync", "time"] }

Expand All @@ -24,7 +25,6 @@ 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" }
Expand All @@ -34,3 +34,4 @@ default = ["gql_transport"]
gql_transport = ["nekoton/gql_transport"]
jrpc_transport = ["nekoton/jrpc_transport"]
proto_transport = ["nekoton/proto_transport"]
ton_transport = ["nekoton/ton_transport"]
2 changes: 2 additions & 0 deletions nekoton-transport/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ pub mod gql;
pub mod jrpc;
#[cfg(feature = "proto_transport")]
pub mod proto;
#[cfg(feature = "ton_transport")]
pub mod ton;
72 changes: 72 additions & 0 deletions nekoton-transport/src/ton.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use reqwest::{IntoUrl, Url};
use serde::Serialize;

pub struct TonClient {
endpoint: Url,
client: reqwest::Client,
}

impl TonClient {
pub fn new_v4<U: IntoUrl>(endpoint: U) -> anyhow::Result<Self> {
let url = endpoint.into_url()?;
Ok(Self {
endpoint: url,
client: reqwest::Client::new(),
})
}

pub fn endpoint(&self) -> &Url {
&self.endpoint
}

pub async fn send_get<U: IntoUrl>(&self, path: U) -> anyhow::Result<Option<String>> {
let path = path.into_url()?;
let result = self
.client
.get(self.endpoint.clone().join(path.as_str())?)
.header("ContentType", "application/json")
.send()
.await?;

if matches!(result.status(), reqwest::StatusCode::NOT_FOUND) {
return Ok(None);
}

let result = result.text().await?;
Ok(Some(result))
}

pub async fn send_post<R: Serialize, U: IntoUrl>(
&self,
body: R,
path: U,
) -> anyhow::Result<Option<String>> {
let path = path.into_url()?;
let result = self
.client
.post(self.endpoint.clone().join(path.as_str())?)
.body(serde_json::to_string(&body)?)
.header("ContentType", "application/json")
.send()
.await?;

if matches!(result.status(), reqwest::StatusCode::NOT_FOUND) {
return Ok(None);
}

let result = result.text().await?;
Ok(Some(result))
}
}

#[cfg_attr(not(feature = "non_threadsafe"), async_trait::async_trait)]
#[cfg_attr(feature = "non_threadsafe", async_trait::async_trait(?Send))]
impl nekoton::external::TonConnection for TonClient {
async fn send_get(&self, path: &str) -> anyhow::Result<Option<String>> {
self.send_get(path).await
}

async fn send_post(&self, body: &str, path: &str) -> anyhow::Result<Option<String>> {
self.send_post(body, path).await
}
}
171 changes: 171 additions & 0 deletions nekoton-utils/src/serde_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,42 @@ impl<'de> Deserialize<'de> for StringOrNumber {
}
}

struct U128StringOrNumber(u128);

impl Serialize for U128StringOrNumber {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.0 <= 0x1fffffffffffff_u128 || !serializer.is_human_readable() {
serializer.serialize_u128(self.0)
} else {
serializer.serialize_str(&self.0.to_string())
}
}
}

impl<'de> Deserialize<'de> for U128StringOrNumber {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Value<'a> {
String(#[serde(borrow)] Cow<'a, str>),
Number(u128),
}

match Value::deserialize(deserializer)? {
Value::String(str) => u128::from_str(str.as_ref())
.map(Self)
.map_err(|_| D::Error::custom("Invalid number")),
Value::Number(value) => Ok(Self(value)),
}
}
}

pub mod serde_u64 {
use super::*;

Expand All @@ -64,6 +100,24 @@ pub mod serde_u64 {
}
}

pub mod serde_u128 {
use super::*;

pub fn serialize<S>(data: &u128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
U128StringOrNumber(*data).serialize(serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<u128, D::Error>
where
D: serde::Deserializer<'de>,
{
U128StringOrNumber::deserialize(deserializer).map(|U128StringOrNumber(x)| x)
}
}

pub mod serde_optional_u64 {
use super::*;

Expand Down Expand Up @@ -138,6 +192,33 @@ pub mod serde_base64_array {
}
}

pub mod serde_optional_base64_array {
use super::*;

pub fn serialize<S>(data: &dyn AsRef<[u8]>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_bytes_base64::serialize(data, serializer)
}

pub fn deserialize<'de, D, const N: usize>(deserializer: D) -> Result<Option<[u8; N]>, D::Error>
where
D: serde::Deserializer<'de>,
{
let data = serde_bytes_base64::deserialize_string_optional(deserializer)?;
match data {
Some(data) => {
let result = data.try_into().map_err(|_| {
D::Error::custom(format!("Invalid array length, expected: {N}"))
})?;
Ok(Some(result))
}
None => Ok(None),
}
}
}

pub mod serde_hex_array {
use super::*;

Expand Down Expand Up @@ -292,6 +373,25 @@ pub mod serde_uint256 {
}
}

pub mod serde_base64_uint256 {
use super::*;

pub fn serialize<S>(data: &UInt256, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_base64_array::serialize(data.as_slice(), serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<UInt256, D::Error>
where
D: serde::Deserializer<'de>,
{
let data: [u8; 32] = serde_base64_array::deserialize(deserializer)?;
Ok(UInt256::from_slice(&data[..]))
}
}

pub mod serde_optional_uint256 {
use super::*;

Expand Down Expand Up @@ -397,6 +497,26 @@ pub mod serde_address {
}
}

pub mod serde_base64_address {
use super::*;
use crate::repack_address;

pub fn serialize<S>(data: &MsgAddressInt, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&data.to_string())
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<MsgAddressInt, D::Error>
where
D: serde::Deserializer<'de>,
{
let data = String::deserialize(deserializer)?;
repack_address(&data).map_err(|_| D::Error::custom("Invalid address"))
}
}

pub mod serde_optional_address {
use super::*;

Expand Down Expand Up @@ -579,6 +699,37 @@ pub mod serde_bytes_base64 {
deserializer.deserialize_bytes(BytesVisitor)
}
}

pub fn deserialize_string_optional<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Base64Visitor;

impl<'de> Visitor<'de> for Base64Visitor {
type Value = Option<Vec<u8>>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("base64-encoded byte array")
}

fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
if value.is_empty() {
return Ok(None);
}
base64::decode(value)
.map(|x| Some(x))
.map_err(|_| E::invalid_type(Unexpected::Str(value), &self))
}

// See the `deserializing_flattened_field` test for an example why this is needed.
fn visit_bytes<E: Error>(self, value: &[u8]) -> Result<Self::Value, E> {
Ok(Some(value.to_vec()))
}
}

deserializer.deserialize_str(Base64Visitor)
}
}

pub mod serde_bytes_base64_optional {
Expand Down Expand Up @@ -672,6 +823,26 @@ pub mod serde_cell {
}
}

pub mod serde_transaction_array {
use super::*;
use ton_block::{Deserializable, Transaction};
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<ton_block::Transaction>, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes = serde_bytes_base64::deserialize(deserializer)?;
let cells =
ton_types::deserialize_cells_tree(&mut bytes.as_slice()).map_err(Error::custom)?;
let mut transactions = Vec::new();
for c in cells {
let t = Transaction::construct_from_cell(c).map_err(Error::custom)?;
transactions.push(t);
}

Ok(transactions)
}
}

pub mod serde_ton_block {
use ton_block::{Deserializable, Serializable};

Expand Down
2 changes: 0 additions & 2 deletions src/core/contract_subscription/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,9 @@ impl ContractSubscription {
pending_transactions: Vec::new(),
transactions_synced: false,
};

result.transactions_synced = !result
.refresh_contract_state_impl(None, on_contract_state)
.await?;

if !result.transactions_synced {
if let Some(on_transactions_found) = on_transactions_found {
// Preload transactions if `on_transactions_found` specified
Expand Down
2 changes: 1 addition & 1 deletion src/core/ton_wallet/wallet_v5r1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ mod tests {

if let AccountState::AccountActive { state_init } = state.storage.state() {
let init_data = InitData::try_from(state_init.data().unwrap())?;
assert_eq!(init_data.is_signature_allowed, true);
assert!(init_data.is_signature_allowed);
assert_eq!(
init_data.public_key.to_hex_string(),
"9107a65271437e1a982bb98404bd9a82c434f31ee30c621b6596702bb59bf0a0"
Expand Down
8 changes: 8 additions & 0 deletions src/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ pub trait ProtoConnection: Send + Sync {
async fn post(&self, req: ProtoRequest) -> Result<Vec<u8>>;
}

#[cfg(feature = "ton_transport")]
#[cfg_attr(not(feature = "non_threadsafe"), async_trait::async_trait)]
#[cfg_attr(feature = "non_threadsafe", async_trait::async_trait(?Send))]
pub trait TonConnection: Send + Sync {
async fn send_get(&self, path: &str) -> Result<Option<String>>;
async fn send_post(&self, body: &str, path: &str) -> Result<Option<String>>;
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LedgerSignatureContext {
Expand Down
2 changes: 1 addition & 1 deletion src/transport/jrpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ mod tests {
transport
.get_transactions(&address, 21968513000000, 10)
.await?;

transport
.get_transaction(&ton_types::UInt256::from_slice(
&hex::decode("4a0a06bfbfaba4da8fcc7f5ad617fdee5344d954a1794e35618df2a4b349d15c")
Expand Down
Loading
Loading