Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan authored Aug 16, 2024
2 parents 6821954 + 160a6e6 commit a11b664
Show file tree
Hide file tree
Showing 26 changed files with 378 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.PrivateKey
import wallet.core.jni.TONMessageSigner
import wallet.core.jni.TONWallet

class TestTheOpenNetworkMessageSigner {
init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun TheOpenNetworkMessageSignerSignMessage() {
// The private key has been derived by using [ton-mnemonic](https://www.npmjs.com/package/tonweb-mnemonic/v/0.0.2)
// from the following mnemonic:
// document shield addict crime broom point story depend suit satisfy test chicken valid tail speak fortune sound drill seek cube cheap body music recipe
val privateKey = PrivateKey("112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18".toHexByteArray())
val message = "Hello world"
val signature = TONMessageSigner.signMessage(privateKey, message)
// The following signature has been computed by calling `window.ton.send("ton_personalSign", { data: "Hello world" });`.
assertEquals(signature, "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504")
}
}
26 changes: 26 additions & 0 deletions include/TrustWalletCore/TWTONMessageSigner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

#pragma once

#include "TWBase.h"
#include "TWPrivateKey.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN

/// TON message signing.
TW_EXPORT_CLASS
struct TWTONMessageSigner;

/// Signs an arbitrary message to prove ownership of an address for off-chain services.
/// https://github.com/ton-foundation/specs/blob/main/specs/wtf-0002.md
///
/// \param privateKey: the private key used for signing
/// \param message: A custom message which is input to the signing.
/// \returns the signature, Hex-encoded. On invalid input null is returned. Returned object needs to be deleted after use.
TW_EXPORT_STATIC_METHOD
TWString *_Nullable TWTONMessageSignerSignMessage(struct TWPrivateKey *_Nonnull privateKey, TWString* _Nonnull message);

TW_EXTERN_C_END
46 changes: 29 additions & 17 deletions jni/cpp/Random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <jni.h>
#include <string.h>
#include <stdint.h>
#include <fstream>

static JavaVM* cachedJVM;

Expand All @@ -25,31 +26,42 @@ uint32_t random32() {
}

void random_buffer(uint8_t *buf, size_t len) {
JNIEnv *env;

if (cachedJVM)
{
JNIEnv *env;

#if defined(__ANDROID__) || defined(ANDROID)
cachedJVM->AttachCurrentThread(&env, nullptr);
#else
cachedJVM->AttachCurrentThread((void **) &env, nullptr);
#endif

// SecureRandom random = new SecureRandom();
jclass secureRandomClass = env->FindClass("java/security/SecureRandom");
jmethodID constructor = env->GetMethodID(secureRandomClass, "<init>", "()V");
jobject random = env->NewObject(secureRandomClass, constructor);
// SecureRandom random = new SecureRandom();
jclass secureRandomClass = env->FindClass("java/security/SecureRandom");
jmethodID constructor = env->GetMethodID(secureRandomClass, "<init>", "()V");
jobject random = env->NewObject(secureRandomClass, constructor);

//byte array[] = new byte[len];
jbyteArray array = env->NewByteArray(static_cast<jsize>(len));
//byte array[] = new byte[len];
jbyteArray array = env->NewByteArray(static_cast<jsize>(len));

//random.nextBytes(bytes);
jmethodID nextBytes = env->GetMethodID(secureRandomClass, "nextBytes", "([B)V");
env->CallVoidMethod(random, nextBytes, array);
//random.nextBytes(bytes);
jmethodID nextBytes = env->GetMethodID(secureRandomClass, "nextBytes", "([B)V");
env->CallVoidMethod(random, nextBytes, array);

jbyte* bytes = env->GetByteArrayElements(array, nullptr);
memcpy(buf, bytes, len);
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);
jbyte* bytes = env->GetByteArrayElements(array, nullptr);
memcpy(buf, bytes, len);
env->ReleaseByteArrayElements(array, bytes, JNI_ABORT);

env->DeleteLocalRef(array);
env->DeleteLocalRef(random);
env->DeleteLocalRef(secureRandomClass);
env->DeleteLocalRef(array);
env->DeleteLocalRef(random);
env->DeleteLocalRef(secureRandomClass);
}
else
{
std::ifstream randomData("/dev/urandom", std::ios::in | std::ios::binary);
if (randomData.is_open()) {
randomData.read(reinterpret_cast<char*>(buf), len);
randomData.close();
}
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_solana/src/defined_addresses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ macro_rules! define {
define!(SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111");
define!(STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111");
define!(TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
define!(TOKEN_2022_PROGRAM_ID_ADDRESS = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
define!(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
define!(SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111");
define!(SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ impl TokenInstructionBuilder {
other_main_pubkey: SolanaAddress,
token_mint_pubkey: SolanaAddress,
token_pubkey: SolanaAddress,
token_program_id: SolanaAddress,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(funding_pubkey, true),
AccountMeta::new(token_pubkey, false),
AccountMeta::readonly(other_main_pubkey, false),
AccountMeta::readonly(token_mint_pubkey, false),
AccountMeta::readonly(*SYSTEM_PROGRAM_ID_ADDRESS, false),
AccountMeta::readonly(*TOKEN_PROGRAM_ID_ADDRESS, false),
AccountMeta::readonly(token_program_id, false),
AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false),
];
let data = Data::default();
Expand All @@ -96,6 +97,7 @@ impl TokenInstructionBuilder {
signer: SolanaAddress,
amount: u64,
decimals: u8,
token_program_id: SolanaAddress,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(sender_token_pubkey, false),
Expand All @@ -105,6 +107,6 @@ impl TokenInstructionBuilder {
];

let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
Instruction::new(*TOKEN_PROGRAM_ID_ADDRESS, data, account_metas)
Instruction::new(token_program_id, data, account_metas)
}
}
13 changes: 12 additions & 1 deletion rust/chains/tw_solana/src/modules/message_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::address::SolanaAddress;
use crate::blockhash::Blockhash;
use crate::defined_addresses::{TOKEN_2022_PROGRAM_ID_ADDRESS, TOKEN_PROGRAM_ID_ADDRESS};
use crate::instruction::Instruction;
use crate::modules::compiled_instructions::compile_instructions;
use crate::modules::compiled_keys::CompiledKeys;
Expand Down Expand Up @@ -333,6 +334,7 @@ impl<'a> MessageBuilder<'a> {
other_main_address,
token_mint_address,
token_address,
match_program_id(create_token_acc.token_program_id),
);
let mut builder = InstructionBuilder::default();
builder
Expand Down Expand Up @@ -370,14 +372,14 @@ impl<'a> MessageBuilder<'a> {
.context("Invalid token decimals. Expected lower than 256")?;

let references = Self::parse_references(&token_transfer.references)?;

let transfer_instruction = TokenInstructionBuilder::transfer_checked(
sender_token_address,
token_mint_address,
recipient_token_address,
signer,
token_transfer.amount,
decimals,
match_program_id(token_transfer.token_program_id),
)
.with_references(references);

Expand Down Expand Up @@ -432,6 +434,7 @@ impl<'a> MessageBuilder<'a> {
recipient_main_address,
token_mint_address,
recipient_token_address,
match_program_id(create_and_transfer.token_program_id),
);
let transfer_instruction = TokenInstructionBuilder::transfer_checked(
sender_token_address,
Expand All @@ -440,6 +443,7 @@ impl<'a> MessageBuilder<'a> {
signer,
create_and_transfer.amount,
decimals,
match_program_id(create_and_transfer.token_program_id),
)
.with_references(references);

Expand Down Expand Up @@ -769,3 +773,10 @@ where
{
u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big)
}

fn match_program_id(program_id: Proto::TokenProgramId) -> SolanaAddress {
match program_id {
Proto::TokenProgramId::TokenProgram => *TOKEN_PROGRAM_ID_ADDRESS,
Proto::TokenProgramId::Token2022Program => *TOKEN_2022_PROGRAM_ID_ADDRESS,
}
}
1 change: 1 addition & 0 deletions rust/chains/tw_ton/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
// Copyright © 2017 Trust Wallet.

pub mod address_converter;
pub mod personal_message_signer;
pub mod wallet_provider;
50 changes: 50 additions & 0 deletions rust/chains/tw_ton/src/modules/personal_message_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright © 2017 Trust Wallet.

use tw_hash::sha2::sha512;
use tw_keypair::ed25519::sha512::PrivateKey;
use tw_keypair::ed25519::Signature;
use tw_keypair::traits::SigningKeyTrait;
use tw_keypair::KeyPairResult;

pub const TON_PERSONAL_MESSAGE_PREFIX: &str = "ton-safe-sign-magic";

pub struct PersonalMessageSigner;

impl PersonalMessageSigner {
/// Signs an arbitrary message.
/// https://www.openmask.app/docs/api-reference/rpc-api#ton_personalsign
/// https://github.com/OpenProduct/openmask-extension/blob/7566ceb2772fed7a3a27d2a67bd34bf89e862557/src/view/screen/notifications/sign/api.ts#L21-L48
pub fn sign(private_key: &PrivateKey, msg: &str) -> KeyPairResult<Signature> {
let msg_hash = sha512(msg.as_bytes());

let mut msg_to_sign = vec![0xff_u8, 0xff];
msg_to_sign.extend_from_slice(TON_PERSONAL_MESSAGE_PREFIX.as_bytes());
msg_to_sign.extend_from_slice(msg_hash.as_slice());

private_key.sign(msg_to_sign)
}
}

#[cfg(test)]
mod tests {
use super::*;
use tw_hash::H512;

#[test]
fn test_sign_personal_message() {
// The private key has been derived by using [ton-mnemonic](https://www.npmjs.com/package/tonweb-mnemonic/v/0.0.2)
// from the following mnemonic:
// document shield addict crime broom point story depend suit satisfy test chicken valid tail speak fortune sound drill seek cube cheap body music recipe
let private_key = PrivateKey::try_from(
"112d4e2e700a468f1eae699329202f1ee671d6b665caa2d92dea038cf3868c18",
)
.unwrap();

let signature = PersonalMessageSigner::sign(&private_key, "Hello world").unwrap();
// The following signature has been computed by calling `window.ton.send("ton_personalSign", { data: "Hello world" });`.
let expected_sig = "2490fbaa72aec0b77b19162bbbe0b0e3f7afd42cc9ef469f0494cd4a366a4bf76643300cd5991f66bce6006336742b8d1d435d541d244dcc013d428472e89504";
assert_eq!(signature.to_bytes(), H512::from(expected_sig));
}
}
55 changes: 55 additions & 0 deletions rust/tw_any_coin/tests/chains/solana/solana_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ fn test_solana_sign_create_token_account() {
main_address: "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"),
Expand All @@ -413,6 +414,7 @@ fn test_solana_sign_create_token_account_5ktpn1() {
main_address: "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
Expand All @@ -438,6 +440,7 @@ fn test_solana_sign_create_token_account_for_other_3e6ufv() {
main_address: "3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
Expand Down Expand Up @@ -469,6 +472,7 @@ fn test_solana_sign_create_token_account_with_priority_fee_price() {
// WBTC
token_mint_address: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh".into(),
token_address: "94JfTH8qCeBQDBatvULREG43msjSQscT7oHnqx7jppX".into(),
..Proto::CreateTokenAccount::default()
};

let input = Proto::SigningInput {
Expand Down Expand Up @@ -611,6 +615,7 @@ fn test_solana_sign_create_and_transfer_token_with_memo_and_references() {
"CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq".into(),
"tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS".into(),
],
..Proto::CreateAndTransferToken::default()
};
let input = Proto::SigningInput {
private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"),
Expand Down Expand Up @@ -918,3 +923,53 @@ fn test_solana_sign_raw_message_v0() {
// Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet
assert_eq!(output.encoded, "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw");
}

#[test]
fn test_solana_sign_create_and_transfer_token_2022() {
let create_transfer_token: Proto::CreateAndTransferToken = Proto::CreateAndTransferToken {
recipient_main_address: "EbHdsfVpWzeQV4TceYQ2xENS8meBHyztyTKVSFtgHPUw".into(),
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
amount: 1000000000,
decimals: 9,
token_program_id: Proto::TokenProgramId::Token2022Program,
..Proto::CreateAndTransferToken::default()
};
let input = Proto::SigningInput {
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
recent_blockhash: "5oba9g5nWnvutTTb935aBMkHBYGXoak1ot4U2p34zEiJ".into(),
transaction_type: TransactionType::create_and_transfer_token_transaction(
create_transfer_token,
),
..Proto::SigningInput::default()
};
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let output = signer.sign(CoinType::Solana, input);
assert_eq!(output.error, SigningError::OK);
assert_eq!(output.encoded, "2xzg9AVGv8wWEn9S4m8954WSzh2MUQPCTCyFmyrSs4DJCkSaZRMAbGL8NcyDeJFT3RwUabHsX1m5CFuqzJ5Jg9knNwG6uBjYjWjNjGLBEBURa3ARqziaMAL2mZY8uZwaZETE33WZeSxNrm7zv1jJYLfqbWxquEedGND9vB9AuEspHg7TCZxfJbzY4W8QtLqyQ598z9adxWgwNXanHzqu7B4bNsp1wfKPPyx8AGQaVSx6fepaevDEZX9h2Rg1daW9TjVpktp7EHrriYVs4m44WJ18fejWLyqituXqQPdhos5oZ3e5vNXE8KcgARKXtwsXCGwwKwc9ZEVNvUp6qyUZZV8os2FHorodrT9g3Xrso5dgdsRCb42AUrKHyDdXMpRA1PmeZX6UdzgL8knt2xfzCFxzGPuMKeTtvZKFcEPJvNg73CSMPVH1mm3jz75nATdChR7xu5R4m5Gy8vhr5ndEnb8fM5P1gv6hDbfmesAEf5wye4mKTVAC4B8Mhf8WC8YNaGUG7CcxeQZXrjEfUQenboArhqbxqHFYrURK3GJLAQojXmkwSMGwv4TYL");
}

#[test]
fn test_solana_sign_transfer_token_2022() {
let transfer_token: Proto::TokenTransfer = Proto::TokenTransfer {
amount: 1000000000,
decimals: 9,
token_program_id: Proto::TokenProgramId::Token2022Program,
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
..Proto::TokenTransfer::default()
};
let input = Proto::SigningInput {
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
recent_blockhash: "9U2eTS9b2Essvo1s5hDmwgC1atkSCCUipj2FemLvdWbj".into(),
transaction_type: TransactionType::token_transfer_transaction(transfer_token),
..Proto::SigningInput::default()
};
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let output = signer.sign(CoinType::Solana, input);
assert_eq!(output.error, SigningError::OK);
assert_eq!(output.encoded, "SAXNFUd7dNBu956Gi4XNuvMkKKjS9vp6puz45ErYMHFpMNwC3AQxDxGbweXt4GzY2FnUZ6ubm231NrdwWa8dg9bqgRMaHPLuPiy99YwtvcQ1E6mHxHqq8nL5VaN8wiVnrMU57zCLfHsSsVCHZc5peHHAPXMDE318uMCLLBwgDWuD1FfAvUAyXRSYniXzWG3jtBdDhuDohh13E2TMrtqTcKVv3crejFqFjtsNuW7KCqrZwxCv1ASNiiL2XScQBdHwStyjH2UTqLmT6wjGLiDYy7PZ88Tbz65r8NLr4Vb1aYSTChasfVjMLdybetfNaf4nJuBE4ZuXca7W66txKbHesxQbzrjUCXX12JFbKyaA8KJKBpbgkc9jWJjQkzyn");
// https://explorer.solana.com/tx/Lg1xWzsC9GatQMu1ZXv23t7snC92RRvbKJe22bsS76GUb8C8a9q3HPkiUnFoK6AWKSoNSsmko1EBnvKkCnL8b7w?cluster=devnet
}
6 changes: 6 additions & 0 deletions rust/tw_keypair/src/ffi/privkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ pub struct TWPrivateKey(pub(crate) PrivateKey);

impl RawPtrTrait for TWPrivateKey {}

impl AsRef<PrivateKey> for TWPrivateKey {
fn as_ref(&self) -> &PrivateKey {
&self.0
}
}

/// Create a private key with the given block of data.
///
/// \param input *non-null* byte array.
Expand Down
Loading

0 comments on commit a11b664

Please sign in to comment.