Skip to content

Commit 875996a

Browse files
committed
Refactor TON sign data to prepare data without signing
1 parent d5d7302 commit 875996a

File tree

10 files changed

+167
-127
lines changed

10 files changed

+167
-127
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/gem_tron/src/provider/preload_mapper.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ mod tests {
119119
use primitives::delegation::DelegationValidator;
120120

121121
fn chain_parameter(key: &str, value: i64) -> ChainParameter {
122-
ChainParameter { key: key.to_string(), value: Some(value) }
122+
ChainParameter {
123+
key: key.to_string(),
124+
value: Some(value),
125+
}
123126
}
124127

125128
fn account_usage(free_bandwidth: u64, staked_bandwidth: u64, available_energy: u64) -> TronAccountUsage {

crates/signer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ serde = { workspace = true, features = ["derive"] }
1414
serde_json = { workspace = true }
1515
gem_hash = { path = "../gem_hash" }
1616
zeroize = { version = "1.8.2" }
17+
strum = { workspace = true }
1718

1819
[dev-dependencies]

crates/signer/src/lib.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::sui::assemble_signature;
1818
pub use eip712::hash_typed_data as hash_eip712;
1919
pub use error::SignerError;
2020
pub use sui::SUI_PERSONAL_MESSAGE_SIGNATURE_LEN;
21-
pub use ton::TonSignDataInput;
21+
pub use ton::{TonSignDataPayload, TonSignDataResponse, TonSignDataType};
2222

2323
#[derive(Debug, Default)]
2424
pub struct Signer;
@@ -61,10 +61,6 @@ impl Signer {
6161
let signature = Self::sign_digest(SignatureScheme::Secp256k1, digest.to_vec(), private_key_vec.to_vec())?;
6262
Ok(hex::encode(signature))
6363
}
64-
65-
pub fn sign_ton_personal_message(input: TonSignDataInput, private_key: Vec<u8>) -> Result<String, SignerError> {
66-
ton::sign_ton_personal_message(input, private_key)
67-
}
6864
}
6965

7066
#[cfg(test)]

crates/signer/src/ton.rs

Lines changed: 85 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,20 @@
1-
use std::time::{SystemTime, UNIX_EPOCH};
1+
use std::str::FromStr;
22

3-
use base64::Engine;
4-
use base64::engine::general_purpose::STANDARD;
5-
use ed25519_dalek::Signer as DalekSigner;
63
use serde::{Deserialize, Serialize};
7-
use zeroize::Zeroizing;
4+
use strum::{AsRefStr, EnumString};
85

9-
use crate::ed25519::signing_key_from_bytes;
106
use crate::error::SignerError;
117

12-
#[derive(Clone, Debug, PartialEq)]
13-
enum TonSignDataType {
8+
#[derive(Clone, Debug, PartialEq, AsRefStr, EnumString)]
9+
#[strum(serialize_all = "lowercase")]
10+
pub enum TonSignDataType {
1411
Text,
1512
Binary,
1613
Cell,
1714
}
1815

1916
impl TonSignDataType {
20-
fn as_str(&self) -> &'static str {
21-
match self {
22-
TonSignDataType::Text => "text",
23-
TonSignDataType::Binary => "binary",
24-
TonSignDataType::Cell => "cell",
25-
}
26-
}
27-
28-
fn data_field(&self) -> &'static str {
17+
pub fn data_field(&self) -> &'static str {
2918
match self {
3019
TonSignDataType::Text => "text",
3120
TonSignDataType::Binary => "bytes",
@@ -43,33 +32,40 @@ struct TonSignDataPayloadRaw {
4332
cell: Option<String>,
4433
}
4534

46-
struct TonSignDataPayload {
47-
payload_type: TonSignDataType,
48-
data: String,
35+
pub struct TonSignDataPayload {
36+
pub payload_type: TonSignDataType,
37+
pub data: String,
4938
}
5039

5140
impl TonSignDataPayload {
52-
fn parse(json: &str) -> Result<Self, SignerError> {
41+
pub fn parse(json: &str) -> Result<Self, SignerError> {
5342
let raw: TonSignDataPayloadRaw = serde_json::from_str(json)?;
5443

55-
let (payload_type, data) = match raw.payload_type.as_str() {
56-
"text" => (TonSignDataType::Text, raw.text.ok_or("Missing text field")?),
57-
"binary" => (TonSignDataType::Binary, raw.bytes.ok_or("Missing bytes field")?),
58-
"cell" => (TonSignDataType::Cell, raw.cell.ok_or("Missing cell field")?),
59-
_ => return Err(SignerError::new(format!("Unknown payload type: {}", raw.payload_type))),
44+
let payload_type = TonSignDataType::from_str(&raw.payload_type).map_err(|_| SignerError::new(format!("Unknown payload type: {}", raw.payload_type)))?;
45+
46+
let data = match payload_type {
47+
TonSignDataType::Text => raw.text.ok_or("Missing text field")?,
48+
TonSignDataType::Binary => raw.bytes.ok_or("Missing bytes field")?,
49+
TonSignDataType::Cell => raw.cell.ok_or("Missing cell field")?,
6050
};
6151

6252
Ok(Self { payload_type, data })
6353
}
64-
}
6554

66-
pub struct TonSignDataInput {
67-
pub domain: String,
68-
pub payload: Vec<u8>,
55+
pub fn hash(&self) -> Vec<u8> {
56+
self.data.as_bytes().to_vec()
57+
}
58+
59+
pub fn to_json(&self) -> serde_json::Value {
60+
serde_json::json!({
61+
"type": self.payload_type.as_ref(),
62+
self.payload_type.data_field(): self.data,
63+
})
64+
}
6965
}
7066

7167
#[derive(Serialize)]
72-
struct TonSignDataResponse {
68+
pub struct TonSignDataResponse {
7369
signature: String,
7470
#[serde(rename = "publicKey")]
7571
public_key: String,
@@ -78,38 +74,25 @@ struct TonSignDataResponse {
7874
payload: serde_json::Value,
7975
}
8076

81-
pub fn sign_ton_personal_message(input: TonSignDataInput, private_key: Vec<u8>) -> Result<String, SignerError> {
82-
let private_key = Zeroizing::new(private_key);
83-
let payload_str = std::str::from_utf8(&input.payload).map_err(|e| SignerError::new(e.to_string()))?;
84-
let parsed = TonSignDataPayload::parse(payload_str)?;
85-
86-
let signing_key = signing_key_from_bytes(&private_key)?;
87-
let signature = signing_key.sign(parsed.data.as_bytes());
88-
89-
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0);
90-
91-
let payload = serde_json::json!({
92-
"type": parsed.payload_type.as_str(),
93-
parsed.payload_type.data_field(): parsed.data,
94-
});
95-
96-
let response = TonSignDataResponse {
97-
signature: STANDARD.encode(signature.to_bytes()),
98-
public_key: STANDARD.encode(signing_key.verifying_key().to_bytes()),
99-
timestamp,
100-
domain: input.domain,
101-
payload,
102-
};
77+
impl TonSignDataResponse {
78+
pub fn new(signature: String, public_key: String, timestamp: u64, domain: String, payload: serde_json::Value) -> Self {
79+
Self {
80+
signature,
81+
public_key,
82+
timestamp,
83+
domain,
84+
payload,
85+
}
86+
}
10387

104-
Ok(serde_json::to_string(&response)?)
88+
pub fn to_json(&self) -> Result<String, SignerError> {
89+
serde_json::to_string(self).map_err(SignerError::from)
90+
}
10591
}
10692

10793
#[cfg(test)]
10894
mod tests {
10995
use super::*;
110-
use ed25519_dalek::Verifier;
111-
112-
const TEST_PRIVATE_KEY: &str = "1e9d38b5274152a78dff1a86fa464ceadc1f4238ca2c17060c3c507349424a34";
11396

11497
#[test]
11598
fn test_parse_payload_text() {
@@ -118,46 +101,62 @@ mod tests {
118101

119102
assert_eq!(parsed.payload_type, TonSignDataType::Text);
120103
assert_eq!(parsed.data, "Hello TON");
104+
assert_eq!(parsed.hash(), b"Hello TON".to_vec());
121105
}
122106

123107
#[test]
124-
fn test_verify_signature() {
125-
let private_key = hex::decode(TEST_PRIVATE_KEY).unwrap();
126-
let text = "Hello TON";
127-
let input = TonSignDataInput {
128-
domain: "example.com".to_string(),
129-
payload: format!(r#"{{"type":"text","text":"{}"}}"#, text).into_bytes(),
130-
};
131-
132-
let result_json = sign_ton_personal_message(input, private_key.clone()).unwrap();
133-
let result: serde_json::Value = serde_json::from_str(&result_json).unwrap();
108+
fn test_parse_payload_binary() {
109+
let json = r#"{"type":"binary","bytes":"SGVsbG8="}"#;
110+
let parsed = TonSignDataPayload::parse(json).unwrap();
134111

135-
let signing_key = signing_key_from_bytes(&private_key).unwrap();
136-
let public_key = signing_key.verifying_key();
112+
assert_eq!(parsed.payload_type, TonSignDataType::Binary);
113+
assert_eq!(parsed.data, "SGVsbG8=");
114+
}
137115

138-
let signature_base64 = result["signature"].as_str().unwrap();
139-
let signature_bytes = STANDARD.decode(signature_base64).unwrap();
140-
let signature = ed25519_dalek::Signature::from_slice(&signature_bytes).unwrap();
116+
#[test]
117+
fn test_parse_payload_cell() {
118+
let json = r#"{"type":"cell","cell":"te6c"}"#;
119+
let parsed = TonSignDataPayload::parse(json).unwrap();
141120

142-
assert!(public_key.verify(text.as_bytes(), &signature).is_ok());
143-
assert_eq!(result["publicKey"].as_str().unwrap(), STANDARD.encode(public_key.to_bytes()));
144-
assert!(result["timestamp"].as_u64().unwrap() > 0);
121+
assert_eq!(parsed.payload_type, TonSignDataType::Cell);
122+
assert_eq!(parsed.data, "te6c");
145123
}
146124

147125
#[test]
148-
fn test_response_format_text() {
149-
let private_key = hex::decode(TEST_PRIVATE_KEY).unwrap();
150-
let input = TonSignDataInput {
151-
domain: "react-app.walletconnect.com".to_string(),
152-
payload: r#"{"type":"text","text":"Hello from WalletConnect TON"}"#.as_bytes().to_vec(),
126+
fn test_payload_to_json() {
127+
let payload = TonSignDataPayload {
128+
payload_type: TonSignDataType::Text,
129+
data: "Hello TON".to_string(),
153130
};
154131

155-
let result_json = sign_ton_personal_message(input, private_key).unwrap();
156-
let result: serde_json::Value = serde_json::from_str(&result_json).unwrap();
132+
let json = payload.to_json();
133+
assert_eq!(json["type"], "text");
134+
assert_eq!(json["text"], "Hello TON");
135+
}
136+
137+
#[test]
138+
fn test_response_to_json() {
139+
let payload = TonSignDataPayload {
140+
payload_type: TonSignDataType::Text,
141+
data: "Hello TON".to_string(),
142+
};
157143

158-
assert_eq!(result["domain"], "react-app.walletconnect.com");
159-
assert!(result["timestamp"].as_u64().unwrap() > 0);
160-
assert_eq!(result["payload"]["type"], "text");
161-
assert_eq!(result["payload"]["text"], "Hello from WalletConnect TON");
144+
let response = TonSignDataResponse::new(
145+
"c2lnbmF0dXJl".to_string(),
146+
"cHVibGljS2V5".to_string(),
147+
1234567890,
148+
"example.com".to_string(),
149+
payload.to_json(),
150+
);
151+
152+
let json = response.to_json().unwrap();
153+
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
154+
155+
assert_eq!(parsed["signature"], "c2lnbmF0dXJl");
156+
assert_eq!(parsed["publicKey"], "cHVibGljS2V5");
157+
assert_eq!(parsed["timestamp"], 1234567890);
158+
assert_eq!(parsed["domain"], "example.com");
159+
assert_eq!(parsed["payload"]["type"], "text");
160+
assert_eq!(parsed["payload"]["text"], "Hello TON");
162161
}
163162
}

gemstone/src/config/wallet_connect.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ pub struct WalletConnectConfig {
66
}
77

88
pub fn get_wallet_connect_config() -> WalletConnectConfig {
9-
let chains: Vec<Chain> = [vec![Chain::Solana, Chain::Sui, Chain::Ton], EVMChain::all().iter().map(|x| x.to_chain()).collect()].concat();
9+
let chains: Vec<Chain> = [
10+
vec![Chain::Solana, Chain::Sui, Chain::Ton],
11+
EVMChain::all().iter().map(|x| x.to_chain()).collect(),
12+
]
13+
.concat();
1014

1115
WalletConnectConfig {
1216
chains: chains.into_iter().map(|x| x.to_string()).collect(),

gemstone/src/message/decoder.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloy_primitives::{eip191_hash_message, hex::encode_prefixed};
22
use base64::Engine;
33
use base64::engine::general_purpose::STANDARD as BASE64;
44
use bs58;
5+
use signer::{TonSignDataPayload, TonSignDataResponse};
56

67
use super::{
78
eip712::GemEIP712Message,
@@ -41,19 +42,10 @@ impl SignMessageDecoder {
4142
}
4243
SignDigestType::TonPersonal => {
4344
let string = String::from_utf8(self.message.data.clone()).map_err(|_| GemstoneError::from("Invalid UTF-8"))?;
44-
let Some(json) = serde_json::from_str::<serde_json::Value>(&string).ok() else {
45+
let Ok(payload) = TonSignDataPayload::parse(&string) else {
4546
return Ok(MessagePreview::Text(string));
4647
};
47-
let Some(payload_type) = json.get("type").and_then(|v| v.as_str()) else {
48-
return Ok(MessagePreview::Text(string));
49-
};
50-
let preview = match payload_type {
51-
"text" => json.get("text").and_then(|v| v.as_str()).unwrap_or_default().to_string(),
52-
"binary" => json.get("bytes").and_then(|v| v.as_str()).unwrap_or_default().to_string(),
53-
"cell" => json.get("cell").and_then(|v| v.as_str()).unwrap_or_default().to_string(),
54-
_ => string,
55-
};
56-
Ok(MessagePreview::Text(preview))
48+
Ok(MessagePreview::Text(payload.data))
5749
}
5850
SignDigestType::Eip712 => {
5951
let string = String::from_utf8(self.message.data.clone()).map_err(|_| GemstoneError::from("Invalid UTF-8 string for EIP712"))?;
@@ -100,7 +92,16 @@ impl SignMessageDecoder {
10092

10193
pub fn hash(&self) -> Vec<u8> {
10294
match &self.message.sign_type {
103-
SignDigestType::SuiPersonal | SignDigestType::TonPersonal => self.message.data.clone(),
95+
SignDigestType::SuiPersonal => self.message.data.clone(),
96+
SignDigestType::TonPersonal => {
97+
let Ok(string) = String::from_utf8(self.message.data.clone()) else {
98+
return Vec::new();
99+
};
100+
let Ok(payload) = TonSignDataPayload::parse(&string) else {
101+
return Vec::new();
102+
};
103+
payload.hash()
104+
}
104105
SignDigestType::Eip191 | SignDigestType::Siwe => eip191_hash_message(&self.message.data).to_vec(),
105106
SignDigestType::Eip712 => match std::str::from_utf8(&self.message.data) {
106107
Ok(json) => hash_eip712(json).map(|digest| digest.to_vec()).unwrap_or_default(),
@@ -131,6 +132,15 @@ impl SignMessageDecoder {
131132
SignDigestType::Base58 => bs58::encode(data).into_string(),
132133
}
133134
}
135+
136+
pub fn get_ton_result(&self, signature: &[u8], public_key: &[u8], timestamp: u64, domain: String) -> Result<String, GemstoneError> {
137+
let string = String::from_utf8(self.message.data.clone()).map_err(|_| GemstoneError::from("Invalid UTF-8"))?;
138+
let payload = TonSignDataPayload::parse(&string).map_err(|e| GemstoneError::from(e.to_string()))?;
139+
140+
let response = TonSignDataResponse::new(BASE64.encode(signature), BASE64.encode(public_key), timestamp, domain, payload.to_json());
141+
142+
response.to_json().map_err(|e| GemstoneError::from(e.to_string()))
143+
}
134144
}
135145

136146
#[cfg(test)]

gemstone/src/signer/crypto.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use gem_ton::address::base64_to_hex_address;
2-
use signer::{SignatureScheme as GemSignatureScheme, Signer, TonSignDataInput as SignerTonSignDataInput};
2+
use signer::{SignatureScheme as GemSignatureScheme, Signer};
33

44
use crate::GemstoneError;
55

@@ -25,11 +25,6 @@ impl CryptoSigner {
2525
Signer::sign_digest(scheme, digest, private_key).map_err(GemstoneError::from)
2626
}
2727

28-
pub fn sign_ton_personal_message(&self, domain: String, payload: Vec<u8>, private_key: Vec<u8>) -> Result<String, GemstoneError> {
29-
let signer_input = SignerTonSignDataInput { domain, payload };
30-
Signer::sign_ton_personal_message(signer_input, private_key).map_err(GemstoneError::from)
31-
}
32-
3328
pub fn ton_base64_to_raw_address(&self, base64_address: String) -> Result<String, GemstoneError> {
3429
base64_to_hex_address(base64_address).map_err(|e| GemstoneError::AnyError { msg: e.to_string() })
3530
}

0 commit comments

Comments
 (0)