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 ;
63use serde:: { Deserialize , Serialize } ;
7- use zeroize :: Zeroizing ;
4+ use strum :: { AsRefStr , EnumString } ;
85
9- use crate :: ed25519:: signing_key_from_bytes;
106use 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
1916impl 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
5140impl 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) ]
10894mod 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}
0 commit comments