From 6a31911799dfdfec9185e7a6e50a2640cb3ac624 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:43:19 +0700 Subject: [PATCH] [TON]: Add `TONWallet.buildStateInit` function for dapps support (#3970) * feat(ton): Add `tw_ton_wallet_create_v4_r2_state_init` in Rust * feat(ton): Add `TWTONWalletCreateV4R2StateInit` C FFI * feat(ton): Rename `TWTONWalletCreateV4R2StateInit` to `TWTONWalletBuildV4R2StateInit` for Swift and Kotlin codegen compat * Add iOS, Android tests --- .../TestTheOpenNetworkWallet.kt | 29 ++++++++++++++++ include/TrustWalletCore/TWTONWallet.h | 26 +++++++++++++++ rust/chains/tw_ton/src/modules/mod.rs | 1 + .../tw_ton/src/modules/wallet_provider.rs | 29 ++++++++++++++++ rust/wallet_core_rs/src/ffi/ton/mod.rs | 1 + rust/wallet_core_rs/src/ffi/ton/wallet.rs | 33 +++++++++++++++++++ rust/wallet_core_rs/tests/ton_wallet.rs | 21 ++++++++++++ src/interface/TWTONWallet.cpp | 21 ++++++++++++ .../Blockchains/TheOpenNetworkTests.swift | 7 ++++ .../TheOpenNetwork/TWTONWalletTests.cpp | 22 +++++++++++++ 10 files changed, 190 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkWallet.kt create mode 100644 include/TrustWalletCore/TWTONWallet.h create mode 100644 rust/chains/tw_ton/src/modules/wallet_provider.rs create mode 100644 rust/wallet_core_rs/src/ffi/ton/wallet.rs create mode 100644 rust/wallet_core_rs/tests/ton_wallet.rs create mode 100644 src/interface/TWTONWallet.cpp create mode 100644 tests/chains/TheOpenNetwork/TWTONWalletTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkWallet.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkWallet.kt new file mode 100644 index 00000000000..9305072bb75 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/theopennetwork/TestTheOpenNetworkWallet.kt @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +package com.trustwallet.core.app.blockchains.theopennetwork + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.PublicKey +import wallet.core.jni.PublicKeyType +import wallet.core.jni.TONWallet + +class TestTheOpenNetworkWallet { + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun TheOpenNetworkWalletBuildV4R2StateInit() { + val publicKey = PublicKey("f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357".toHexByteArray(), PublicKeyType.ED25519) + val baseWorkchain = 0 + val defaultWalletId = 0x29a9a317 + val stateInit = TONWallet.buildV4R2StateInit(publicKey, baseWorkchain, defaultWalletId) + + val expected = "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg=" + assertEquals(stateInit, expected) + } +} \ No newline at end of file diff --git a/include/TrustWalletCore/TWTONWallet.h b/include/TrustWalletCore/TWTONWallet.h new file mode 100644 index 00000000000..098702faa6a --- /dev/null +++ b/include/TrustWalletCore/TWTONWallet.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#pragma once + +#include "TWBase.h" +#include "TWPublicKey.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// TON wallet operations. +TW_EXPORT_CLASS +struct TWTONWallet; + +/// Constructs a TON Wallet V4R2 stateInit encoded as BoC (BagOfCells) for the given `public_key`. +/// +/// \param publicKey wallet's public key. +/// \param workchain TON workchain to which the wallet belongs. Usually, base chain is used (0). +/// \param walletId wallet's ID allows to create multiple wallets for the same private key. +/// \return Pointer to a base64 encoded Bag Of Cells (BoC) StateInit. Null if invalid public key provided. +TW_EXPORT_STATIC_METHOD +TWString *_Nullable TWTONWalletBuildV4R2StateInit(struct TWPublicKey *_Nonnull publicKey, int32_t workchain, int32_t walletId); + +TW_EXTERN_C_END diff --git a/rust/chains/tw_ton/src/modules/mod.rs b/rust/chains/tw_ton/src/modules/mod.rs index 3ed4c72f589..aafb323eedb 100644 --- a/rust/chains/tw_ton/src/modules/mod.rs +++ b/rust/chains/tw_ton/src/modules/mod.rs @@ -3,3 +3,4 @@ // Copyright © 2017 Trust Wallet. pub mod address_converter; +pub mod wallet_provider; diff --git a/rust/chains/tw_ton/src/modules/wallet_provider.rs b/rust/chains/tw_ton/src/modules/wallet_provider.rs new file mode 100644 index 00000000000..212e0617126 --- /dev/null +++ b/rust/chains/tw_ton/src/modules/wallet_provider.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use crate::wallet::wallet_v4::WalletV4; +use crate::wallet::TonWallet; +use tw_keypair::ed25519::sha512::PublicKey; +use tw_ton_sdk::boc::BagOfCells; +use tw_ton_sdk::error::CellResult; + +const HAS_CRC32: bool = true; + +/// TON Wallet common functionality. +pub struct WalletProvider; + +impl WalletProvider { + /// Constructs a TON Wallet V4R2 stateInit encoded as BoC (BagOfCells) for the given `public_key`. + pub fn v4r2_state_init( + public_key: PublicKey, + workchain: i32, + wallet_id: i32, + ) -> CellResult { + let state_init = + TonWallet::with_public_key(workchain, WalletV4::r2()?, public_key, wallet_id)? + .state_init()? + .to_cell()?; + BagOfCells::from_root(state_init).to_base64(HAS_CRC32) + } +} diff --git a/rust/wallet_core_rs/src/ffi/ton/mod.rs b/rust/wallet_core_rs/src/ffi/ton/mod.rs index 3ed4c72f589..64062dc056b 100644 --- a/rust/wallet_core_rs/src/ffi/ton/mod.rs +++ b/rust/wallet_core_rs/src/ffi/ton/mod.rs @@ -3,3 +3,4 @@ // Copyright © 2017 Trust Wallet. pub mod address_converter; +pub mod wallet; diff --git a/rust/wallet_core_rs/src/ffi/ton/wallet.rs b/rust/wallet_core_rs/src/ffi/ton/wallet.rs new file mode 100644 index 00000000000..0e5272d899a --- /dev/null +++ b/rust/wallet_core_rs/src/ffi/ton/wallet.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#![allow(clippy::missing_safety_doc)] + +use tw_keypair::ffi::pubkey::TWPublicKey; +use tw_memory::ffi::tw_string::TWString; +use tw_memory::ffi::RawPtrTrait; +use tw_misc::try_or_else; +use tw_ton::modules::wallet_provider::WalletProvider; + +/// Constructs a TON Wallet V4R2 stateInit encoded as BoC (BagOfCells) for the given `public_key`. +/// +/// \param public_key wallet's public key. +/// \param workchain TON workchain to which the wallet belongs. Usually, base chain is used (0). +/// \param wallet_id wallet's ID allows to create multiple wallets for the same private key. +/// \return Pointer to a base64 encoded Bag Of Cells (BoC) StateInit. Null if invalid public key provided. +#[no_mangle] +pub unsafe extern "C" fn tw_ton_wallet_build_v4_r2_state_init( + public_key: *const TWPublicKey, + workchain: i32, + wallet_id: i32, +) -> *mut TWString { + let public_key = try_or_else!(TWPublicKey::from_ptr_as_ref(public_key), std::ptr::null_mut); + let ed_pubkey = try_or_else!(public_key.as_ref().to_ed25519(), std::ptr::null_mut).clone(); + + let state_init = try_or_else!( + WalletProvider::v4r2_state_init(ed_pubkey, workchain, wallet_id), + std::ptr::null_mut + ); + TWString::from(state_init).into_ptr() +} diff --git a/rust/wallet_core_rs/tests/ton_wallet.rs b/rust/wallet_core_rs/tests/ton_wallet.rs new file mode 100644 index 00000000000..c73f864f236 --- /dev/null +++ b/rust/wallet_core_rs/tests/ton_wallet.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +use tw_keypair::test_utils::tw_public_key_helper::TWPublicKeyHelper; +use tw_keypair::tw::PublicKeyType; +use tw_memory::test_utils::tw_string_helper::TWStringHelper; +use wallet_core_rs::ffi::ton::wallet::tw_ton_wallet_build_v4_r2_state_init; + +#[test] +fn test_ton_wallet_create_state_init() { + let public_key = TWPublicKeyHelper::with_hex( + "f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357", + PublicKeyType::Ed25519, + ); + assert!(!public_key.is_null()); + let state_init_boc = TWStringHelper::wrap(unsafe { + tw_ton_wallet_build_v4_r2_state_init(public_key.ptr(), 0, 0x29a9a317) + }); + assert_eq!(state_init_boc.to_string().unwrap(), "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg="); +} diff --git a/src/interface/TWTONWallet.cpp b/src/interface/TWTONWallet.cpp new file mode 100644 index 00000000000..7e6495491ab --- /dev/null +++ b/src/interface/TWTONWallet.cpp @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TrustWalletCore/TWTONWallet.h" +#include "rust/Wrapper.h" +#include "PublicKey.h" + +using namespace TW; + +TWString *_Nullable TWTONWalletBuildV4R2StateInit(struct TWPublicKey *_Nonnull publicKey, int32_t workchain, int32_t walletId) { + auto keyType = static_cast(TWPublicKeyKeyType(publicKey)); + auto* publicKeyRustRaw = Rust::tw_public_key_create_with_data(publicKey->impl.bytes.data(), publicKey->impl.bytes.size(), keyType); + const auto publicKeyRust = Rust::wrapTWPublicKey(publicKeyRustRaw); + + Rust::TWStringWrapper stateInit = Rust::tw_ton_wallet_build_v4_r2_state_init(publicKeyRust.get(), workchain, walletId); + if (!stateInit) { + return nullptr; + } + return TWStringCreateWithUTF8Bytes(stateInit.c_str()); +} diff --git a/swift/Tests/Blockchains/TheOpenNetworkTests.swift b/swift/Tests/Blockchains/TheOpenNetworkTests.swift index 10ef0f7725e..605b58d33e2 100644 --- a/swift/Tests/Blockchains/TheOpenNetworkTests.swift +++ b/swift/Tests/Blockchains/TheOpenNetworkTests.swift @@ -58,6 +58,13 @@ class TheOpenNetworkTests: XCTestCase { let jettonAddress = TONAddressConverter.fromBoc(boc: jettonAddressBocEncoded) XCTAssertEqual(jettonAddress, "UQAt6fNdWGLjZT5CdU8mBf0FHDMXrd0qI4FpdvDkcYrpXV5H") } + + func testBuildV4R2StateInit() { + let publicKeyData = Data(hexString: "f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357")! + let publicKey = PublicKey(data: publicKeyData, type: .ed25519)! + let stateInit = TONWallet.buildV4R2StateInit(publicKey: publicKey, workchain: 0, walletId: 0x29a9a317)! + XCTAssertEqual(stateInit, "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg=") + } func testSign() { let privateKeyData = Data(hexString: "c38f49de2fb13223a9e7d37d5d0ffbdd89a5eb7c8b0ee4d1c299f2cefe7dc4a0")! diff --git a/tests/chains/TheOpenNetwork/TWTONWalletTests.cpp b/tests/chains/TheOpenNetwork/TWTONWalletTests.cpp new file mode 100644 index 00000000000..b346ee5568b --- /dev/null +++ b/tests/chains/TheOpenNetwork/TWTONWalletTests.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" + +#include "HexCoding.h" +#include "TrustWalletCore/TWTONWallet.h" + +namespace TW::TheOpenNetwork::tests { + +TEST(TWTONWallet, BuildV4R2StateInit) { + auto publicKeyBytes = DATA("f229a9371fa7c2108b3d90ea22c9be705ff5d0cfeaee9cbb9366ff0171579357"); + auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(publicKeyBytes.get(), TWPublicKeyTypeED25519)); + + const int32_t baseWorkchain = 0; + const int32_t defaultWalletId = 0x29a9a317; + const auto stateInit = WRAPS(TWTONWalletBuildV4R2StateInit(publicKey.get(), baseWorkchain, defaultWalletId)); + assertStringsEqual(stateInit, "te6cckECFgEAAwQAAgE0AQIBFP8A9KQT9LzyyAsDAFEAAAAAKamjF/IpqTcfp8IQiz2Q6iLJvnBf9dDP6u6cu5Nm/wFxV5NXQAIBIAQFAgFIBgcE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/8ICQoLAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNDA0CASAODwBu0gf6ANTUIvkABcjKBxXL/8nQd3SAGMjLBcsCIs8WUAX6AhTLaxLMzMlz+wDIQBSBAQj0UfKnAgBwgQEI1xj6ANM/yFQgR4EBCPRR8qeCEG5vdGVwdIAYyMsFywJQBs8WUAT6AhTLahLLH8s/yXP7AAIAbIEBCNcY+gDTPzBSJIEBCPRZ8qeCEGRzdHJwdIAYyMsFywJQBc8WUAP6AhPLassfEss/yXP7AAAK9ADJ7VQAeAH6APQEMPgnbyIwUAqhIb7y4FCCEHBsdWeDHrFwgBhQBMsFJs8WWPoCGfQAy2kXyx9SYMs/IMmAQPsABgCKUASBAQj0WTDtRNCBAUDXIMgBzxb0AMntVAFysI4jghBkc3Rygx6xcIAYUAXLBVADzxYj+gITy2rLH8s/yYBA+wCSXwPiAgEgEBEAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAIBWBITABG4yX7UTQ1wsfgAPbKd+1E0IEBQNch9AQwAsjKB8v/ydABgQEI9ApvoTGACASAUFQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwEXtMkg="); +} + +} // namespace TW::TheOpenNetwork::tests