-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TON]: Add functions to help to generate Jetton user address (#3922)
* [TON]: Add functions to help to generate Jetton user address * [TON]: Rename `TONAddress` module to `TONAddressConverter` * Add ios, android tests * [CI] Trigger CI * [TON]: Fix Android test
- Loading branch information
1 parent
13ff342
commit feafc27
Showing
18 changed files
with
591 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Copyright © 2017 Trust Wallet. | ||
|
||
#pragma once | ||
|
||
#include "TWBase.h" | ||
#include "TWString.h" | ||
|
||
TW_EXTERN_C_BEGIN | ||
|
||
/// TON address operations. | ||
TW_EXPORT_CLASS | ||
struct TWTONAddressConverter; | ||
|
||
/// Converts a TON user address into a Bag of Cells (BoC) with a single root Cell. | ||
/// The function is mostly used to request a Jetton user address via `get_wallet_address` RPC. | ||
/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user | ||
/// | ||
/// \param address Address to be converted into a Bag Of Cells (BoC). | ||
/// \return Pointer to a base64 encoded Bag Of Cells (BoC). Null if invalid address provided. | ||
TW_EXPORT_STATIC_METHOD | ||
TWString *_Nullable TWTONAddressConverterToBoc(TWString *_Nonnull address); | ||
|
||
/// Parses a TON address from a Bag of Cells (BoC) with a single root Cell. | ||
/// The function is mostly used to parse a Jetton user address received on `get_wallet_address` RPC. | ||
/// https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user | ||
/// | ||
/// \param boc Base64 encoded Bag Of Cells (BoC). | ||
/// \return Pointer to a Jetton address. | ||
TW_EXPORT_STATIC_METHOD | ||
TWString *_Nullable TWTONAddressConverterFromBoc(TWString *_Nonnull boc); | ||
|
||
TW_EXTERN_C_END |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Copyright © 2017 Trust Wallet. | ||
|
||
#![allow(clippy::missing_safety_doc)] | ||
|
||
use bitreader::{BitReader, BitReaderError}; | ||
use tw_memory::ffi::c_byte_array::{CByteArray, CByteArrayResult}; | ||
use tw_memory::ffi::c_result::{CUInt8Result, ErrorCode}; | ||
use tw_memory::ffi::tw_data::TWData; | ||
use tw_memory::ffi::RawPtrTrait; | ||
use tw_memory::Data; | ||
use tw_misc::try_or_else; | ||
|
||
#[derive(Debug, Eq, PartialEq)] | ||
#[repr(C)] | ||
pub enum CBitReaderCode { | ||
Ok = 0, | ||
/// Requested more bits than there are left in the byte slice at the current position. | ||
NotEnoughData = 1, | ||
/// Requested more bits than the returned variable can hold, for example more than 8 bits when | ||
/// reading into a u8. | ||
TooManyBitsForType = 2, | ||
InalidInput = 3, | ||
} | ||
|
||
impl From<BitReaderError> for CBitReaderCode { | ||
fn from(value: BitReaderError) -> Self { | ||
match value { | ||
BitReaderError::NotEnoughData { .. } => CBitReaderCode::NotEnoughData, | ||
BitReaderError::TooManyBitsForType { .. } => CBitReaderCode::TooManyBitsForType, | ||
} | ||
} | ||
} | ||
|
||
impl From<CBitReaderCode> for ErrorCode { | ||
fn from(error: CBitReaderCode) -> Self { | ||
error as ErrorCode | ||
} | ||
} | ||
|
||
/// BitReader reads data from a big-endian byte slice at the granularity of a single bit. | ||
#[derive(Debug)] | ||
pub struct TWBitReader { | ||
buffer: Data, | ||
bit_position: u64, | ||
bit_len: u64, | ||
} | ||
|
||
impl TWBitReader { | ||
pub fn with_relative_bit_len(buffer: Data, bit_len: u64) -> TWBitReader { | ||
TWBitReader { | ||
buffer, | ||
bit_position: 0, | ||
bit_len, | ||
} | ||
} | ||
|
||
/// Read at most 8 bits into a u8. | ||
pub fn read_u8(&mut self, bit_count: u8) -> Result<u8, CBitReaderCode> { | ||
let mut reader = self.make_reader()?; | ||
let res = reader.read_u8(bit_count)?; | ||
// Update the bit position in case of success read. | ||
self.bit_position += bit_count as u64; | ||
Ok(res) | ||
} | ||
|
||
// Reads an entire slice of `byte_count` bytes. If there aren't enough bits remaining | ||
// after the internal cursor's current position, returns none. | ||
pub fn read_u8_slice(&mut self, byte_count: usize) -> Result<Data, CBitReaderCode> { | ||
let mut reader = self.make_reader()?; | ||
|
||
let mut res = vec![0_u8; byte_count]; | ||
reader.read_u8_slice(&mut res)?; | ||
|
||
// Update the bit position in case of success read. | ||
self.bit_position += byte_count as u64 * 8; | ||
Ok(res) | ||
} | ||
|
||
pub fn is_finished(&self) -> bool { | ||
self.bit_len == self.bit_position | ||
} | ||
|
||
fn make_reader(&self) -> Result<BitReader<'_>, CBitReaderCode> { | ||
let mut reader = BitReader::new(&self.buffer).relative_reader_atmost(self.bit_len); | ||
reader.skip(self.bit_position)?; | ||
Ok(reader) | ||
} | ||
} | ||
|
||
impl RawPtrTrait for TWBitReader {} | ||
|
||
/// Constructs a new `TWBitReader` from a big-endian byte slice | ||
/// that will not allow reading more than `bit_len` bits. It must be deleted at the end. | ||
/// | ||
/// \param data big-endian byte slice to be read. | ||
/// \param bit_len length this reader is allowed to read from the slice. | ||
/// \return nullable pointer to a `TWBitReader` instance. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn tw_bit_reader_create( | ||
data: *const TWData, | ||
bit_len: u64, | ||
) -> *mut TWBitReader { | ||
let data = try_or_else!(TWData::from_ptr_as_ref(data), std::ptr::null_mut); | ||
TWBitReader::with_relative_bit_len(data.to_vec(), bit_len).into_ptr() | ||
} | ||
|
||
/// Deletes a `TWBitReader` and frees the memory. | ||
/// \param reader a `TWBitReader` pointer. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn tw_bit_reader_delete(reader: *mut TWBitReader) { | ||
// Take the ownership back to rust and drop the owner. | ||
let _ = TWBitReader::from_ptr(reader); | ||
} | ||
|
||
/// Read at most 8 bits into a u8. | ||
/// | ||
/// \param reader a `TWBitReader` pointer. | ||
/// \param bit_count number of bits to read. Expected from 1 to 8. | ||
/// \return u8 or error. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn tw_bit_reader_read_u8( | ||
reader: *mut TWBitReader, | ||
bit_count: u8, | ||
) -> CUInt8Result { | ||
let tw_reader = try_or_else!( | ||
TWBitReader::from_ptr_as_mut(reader), | ||
|| CUInt8Result::error(CBitReaderCode::InalidInput) | ||
); | ||
tw_reader.read_u8(bit_count).into() | ||
} | ||
|
||
/// Reads an entire slice of `byteCount` bytes. If there aren't enough bits remaining | ||
/// after the internal cursor's current position, returns null. | ||
/// | ||
/// \param reader a `TWBitReader` pointer. | ||
/// \param byte_count number of bytes to read. | ||
/// \return byte array or error. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn tw_bit_reader_read_u8_slice( | ||
reader: *mut TWBitReader, | ||
byte_count: usize, | ||
) -> CByteArrayResult { | ||
let tw_reader = try_or_else!(TWBitReader::from_ptr_as_mut(reader), || { | ||
CByteArrayResult::error(CBitReaderCode::InalidInput) | ||
}); | ||
tw_reader | ||
.read_u8_slice(byte_count) | ||
.map(CByteArray::from) | ||
.into() | ||
} | ||
|
||
/// Checks whether all bits were read. | ||
/// | ||
/// \param reader a `TWBitReader` pointer. | ||
/// \return whether all bits were read. | ||
#[no_mangle] | ||
pub unsafe extern "C" fn tw_bit_reader_finished(reader: *const TWBitReader) -> bool { | ||
try_or_else!(TWBitReader::from_ptr_as_ref(reader), || true).is_finished() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
// | ||
// Copyright © 2017 Trust Wallet. | ||
|
||
pub mod bit_reader_ffi; | ||
pub mod uuid_ffi; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Copyright © 2017 Trust Wallet. | ||
|
||
use tw_encoding::hex::{DecodeHex, ToHex}; | ||
use tw_memory::test_utils::tw_data_helper::TWDataHelper; | ||
use wallet_core_rs::ffi::utils::bit_reader_ffi::{ | ||
tw_bit_reader_create, tw_bit_reader_finished, tw_bit_reader_read_u8, | ||
tw_bit_reader_read_u8_slice, CBitReaderCode, | ||
}; | ||
|
||
#[test] | ||
fn test_tw_bit_reader_success() { | ||
let ton_address_cell = "8005bd3e6bab0c5c6ca7c84ea9e4c0bfa0a38662f5bba544702d2ede1c8e315d2ba0" | ||
.decode_hex() | ||
.unwrap(); | ||
let ton_address_cell = TWDataHelper::create(ton_address_cell); | ||
|
||
let reader = unsafe { tw_bit_reader_create(ton_address_cell.ptr(), 267) }; | ||
assert!(!reader.is_null()); | ||
|
||
let tp = unsafe { tw_bit_reader_read_u8(reader, 2) }; | ||
assert_eq!(tp.into_result(), Ok(2)); | ||
|
||
let res1 = unsafe { tw_bit_reader_read_u8(reader, 1) }; | ||
assert_eq!(res1.into_result(), Ok(0)); | ||
|
||
let wc = unsafe { tw_bit_reader_read_u8(reader, 8) }; | ||
assert_eq!(wc.into_result(), Ok(0)); | ||
|
||
assert!(!unsafe { tw_bit_reader_finished(reader) }); | ||
|
||
let hash_part = unsafe { tw_bit_reader_read_u8_slice(reader, 32).unwrap().into_vec() }; | ||
assert_eq!( | ||
hash_part.to_hex(), | ||
"2de9f35d5862e3653e42754f2605fd051c3317addd2a23816976f0e4718ae95d" | ||
); | ||
|
||
assert!(unsafe { tw_bit_reader_finished(reader) }); | ||
} | ||
|
||
#[test] | ||
fn test_tw_bit_reader_error() { | ||
let bytes_len = 2; | ||
// Less than two bytes. | ||
let bits_len = 15; | ||
|
||
let data = TWDataHelper::create(vec![1; bytes_len]); | ||
|
||
let reader = unsafe { tw_bit_reader_create(data.ptr(), bits_len as u64) }; | ||
assert!(!reader.is_null()); | ||
|
||
// Cannot read u8 from 9 bits. | ||
let res = unsafe { tw_bit_reader_read_u8(reader, 9) }; | ||
assert_eq!( | ||
res.into_result().unwrap_err(), | ||
CBitReaderCode::TooManyBitsForType as i32 | ||
); | ||
|
||
// Read a dummy u8. | ||
let _ = unsafe { tw_bit_reader_read_u8_slice(reader, 8) }; | ||
|
||
// Cannot read 8 bits as there are 7 bits left only. | ||
let res = unsafe { tw_bit_reader_read_u8_slice(reader, 8) }; | ||
assert_eq!( | ||
res.into_result().unwrap_err(), | ||
CBitReaderCode::NotEnoughData as i32 | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Copyright © 2017 Trust Wallet. | ||
|
||
#include "BitReader.h" | ||
|
||
namespace TW::CommonTON { | ||
|
||
std::optional<BitReader> BitReader::createExact(const TW::Data &buffer, uint64_t bitLen) { | ||
Rust::TWDataWrapper twData(buffer); | ||
auto *readerPtr = Rust::tw_bit_reader_create(twData.get(), bitLen); | ||
if (!readerPtr) { | ||
return std::nullopt; | ||
} | ||
|
||
return BitReader(std::shared_ptr<Rust::TWBitReader>(readerPtr, Rust::tw_bit_reader_delete)); | ||
} | ||
|
||
std::optional<uint8_t> BitReader::readU8(uint8_t bitCount) { | ||
Rust::CUInt8ResultWrapper res = Rust::tw_bit_reader_read_u8(reader.get(), bitCount); | ||
if (res.isErr()) { | ||
return std::nullopt; | ||
} | ||
return res.unwrap().value; | ||
} | ||
|
||
std::optional<Data> BitReader::readU8Slice(uint64_t byteCount) { | ||
Rust::CByteArrayResultWrapper res = Rust::tw_bit_reader_read_u8_slice(reader.get(), byteCount); | ||
if (res.isErr()) { | ||
return std::nullopt; | ||
} | ||
return res.unwrap().data; | ||
} | ||
|
||
bool BitReader::finished() const { | ||
return Rust::tw_bit_reader_finished(reader.get()); | ||
} | ||
|
||
} // namespace TW::CommonTON |
Oops, something went wrong.
feafc27
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uploading VID-20240630-WA0001.mp4…