Skip to content

Commit

Permalink
add eip712 data struct for starknet
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-zkp committed Dec 15, 2023
1 parent 1886441 commit b049bef
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 44 deletions.
6 changes: 3 additions & 3 deletions bindings/wasm/src/json_rpc_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ pub struct JsonRpcSigner {
//#[wasm_bindgen(constructor)]
#[wasm_bindgen(js_name=newRpcSignerWtihProvider)]
pub fn new_with_provider(provider: Provider) -> Result<JsonRpcSigner, JsValue> {
let inner = InterfaceJsonRpcSigner::new(JsonRpcProvider::Provider(provider),None)?;
let inner = InterfaceJsonRpcSigner::new(JsonRpcProvider::Provider(provider),None,None)?;
Ok(JsonRpcSigner { inner })
}

//#[wasm_bindgen(constructor)]
#[wasm_bindgen(js_name=newRpcSignerWithSigner)]
pub fn new_with_signer(signer: Signer, pub_key: String) -> Result<JsonRpcSigner, JsValue> {
let inner = InterfaceJsonRpcSigner::new(JsonRpcProvider::Signer(signer),Some(pub_key))?;
pub fn new_with_signer(signer: Signer, pub_key: String,chain_id: String) -> Result<JsonRpcSigner, JsValue> {
let inner = InterfaceJsonRpcSigner::new(JsonRpcProvider::Signer(signer),Some(pub_key),Some(chain_id))?;
Ok(JsonRpcSigner { inner })
}

Expand Down
4 changes: 2 additions & 2 deletions interface/src/json_rpc_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ pub struct JsonRpcSigner {
}

impl JsonRpcSigner {
pub fn new(provider: JsonRpcProvider,pub_key: Option<String>) -> Result<Self, SignError> {
pub fn new(provider: JsonRpcProvider,pub_key: Option<String>,chain_id: Option<String>) -> Result<Self, SignError> {
let eth_json_rpc_signer = match provider {
JsonRpcProvider::Provider(provider) =>
Layer1JsonRpcSigner::EthSigner(EthJsonRpcSigner::new(provider)),
JsonRpcProvider::Signer(signer) =>
Layer1JsonRpcSigner::StarknetSigner(StarknetJsonRpcSigner::new(signer,pub_key.unwrap()))
Layer1JsonRpcSigner::StarknetSigner(StarknetJsonRpcSigner::new(signer,pub_key.unwrap(),chain_id.unwrap()))
};
let default_zklink_signer = ZkLinkSigner::new()?;
Ok(Self {
Expand Down
4 changes: 2 additions & 2 deletions interface/src/sign_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ pub async fn sign_starknet_transfer(
token_symbol: &str,
) -> Result<TxSignature, SignError> {
tx.signature = zklink_signer.sign_musig(&tx.get_bytes())?;
let message = TypedDataMessage::Transaction(tx.get_starknet_sign_msg(token_symbol));
let starknet_signature = starknet_signer.sign_message(message).await?;
let message = tx.get_starknet_sign_msg(token_symbol);
let starknet_signature = starknet_signer.sign_message(TypedDataMessage::Transaction(message)).await?;

Ok(TxSignature {
tx: tx.into(),
Expand Down
1 change: 1 addition & 0 deletions signers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde_json = "1.0"
sha2 = "0.10"
starknet = { git = "https://github.com/xJonathanLEI/starknet-rs" }
starknet-signers = { git = "https://github.com/xJonathanLEI/starknet-rs" }
starknet-crypto = "0.6.1"
thiserror = "1.0"
wasm-bindgen = { version = "0.2.87", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4"
Expand Down
2 changes: 2 additions & 0 deletions signers/src/starknet_signer/ecdsa_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use starknet_signers::VerifyingKey;
use std::fmt;
use std::fmt::Formatter;
use zklink_sdk_utils::serde::ZeroPrefixHexSerde;
use num::BigUint;
use std::str::FromStr;

#[derive(Clone, PartialEq,Serialize, Deserialize,Eq,Debug)]
Expand Down Expand Up @@ -46,6 +47,7 @@ impl StarkSignature {
Ok(Self { s,r })
}


pub fn from_bytes_be(bytes: &[u8]) -> Result<Self, StarkSignerError> {
let mut s = [0_u8; 32];
let mut r = [0_u8; 32];
Expand Down
42 changes: 21 additions & 21 deletions signers/src/starknet_signer/pk_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ mod tests {
use super::*;
use serde::{Deserialize, Serialize};
use crate::starknet_signer::typed_data::TypedData;
use crate::starknet_signer::typed_data::message::{TypedDataMessage, Message};
use crate::starknet_signer::typed_data::message::{TypedDataMessage, Message, TxMessage};
use starknet_signers::VerifyingKey;
use num::BigUint;
use std::str::FromStr;
use starknet::core::crypto::Signature;

#[derive(Serialize, Deserialize, Debug)]
struct TestSignature {
Expand Down Expand Up @@ -99,32 +100,31 @@ mod tests {
let s_str = "3203688086163592132535350422117785905751559823323905824858605377390311728388";
let pubkey = "1082125475812817975721104073212648033952831721853656627074253194227094744819";
let msg_hash = "0x51d5faacb1bdeb6293d52fd4be0a7c62417cb73962cdd6aff385b67239cf081";
// let msg = TypedDataMessage::CreateL2Key(Message {
// data: "Create zkLink L2".to_string()
// });
// let typed_data = TypedData::new(msg);
//
let mut s = [0;32];
let mut r = [0;32];
let r_num = BigUint::from_str(r_str).unwrap();
let s_num = BigUint::from_str(s_str).unwrap();
s.clone_from_slice(&s_num.to_bytes_be());
r.clone_from_slice(&r_num.to_bytes_be());
let sig_str = "0x02647618b4fe405d0dccbdfd25c20bfdeb87631a332491c633943e6f59f16ef306f72dfce21313b636bef4afff3fdc929e5c3d01e3a1f586690ef7db7ebc280a042b54b6bfc5970163d3b9166fd8f24671dfbf850554eeaf12a5e8f4db06c7f3";
let addr = "0x04A69b67bcaBfA7D3CCb96e1d25C2e6fC93589fE24A6fD04566B8700ff97a71a";
let pub_key = FieldElement::from_str(&pubkey).unwrap();
// let signature = StarkECDSASignature {
// pub_key: FieldElement::from_hex_be(&pubkey).unwrap(),
// signature: StarkSignature {
// s: FieldElement::from_bytes_be(BigUint::from_str(s).unwrap().to_bytes_be()).unwrap(),
// r: FieldElement::from_bytes_be(BigUint::from_str(r).unwrap().to_bytes_be()).unwrap()
// } };

let transfer = TxMessage {
amount: "0.0012345678998".to_string(),
fee: "0.00000001".to_string(),
nonce: "1".to_string(),
to: "0x5505a8cd4594dbf79d8c59c0df1414ab871ca896".to_string(),
token: "USDC".to_string(),
transaction: "Transfer".to_string(),
};

let message = transfer.clone();
let typed_data = TypedData::new(TypedDataMessage::Transaction(transfer),"SN_GOERLI".to_string());
let msg_hash = typed_data.get_message_hash(addr.to_string()).unwrap();
println!("{:?}",msg_hash);
let signature = StarkECDSASignature::from_hex(sig_str).unwrap();
let verifying_key = VerifyingKey::from_scalar(pub_key);
let is_ok = verifying_key
.verify(
&FieldElement::from_hex_be(msg_hash).unwrap(),
&FieldElement::from_hex_be(&hex::encode(msg_hash.to_bytes_be())).unwrap(),
&Signature {
s: FieldElement::from_str(&s_str).unwrap(),
r: FieldElement::from_str(&r_str).unwrap()
s: signature.signature.s,
r: signature.signature.r,
},
)
.unwrap();
Expand Down
14 changes: 10 additions & 4 deletions signers/src/starknet_signer/starknet_json_rpc_signer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use wasm_bindgen::prelude::*;
use crate::starknet_signer::{StarkSignature, StarkECDSASignature};
use crate::starknet_signer::error::StarkSignerError;
use crate::starknet_signer::typed_data::{message::TypedDataMessage, TypedData};
use crate::starknet_signer::typed_data::message::TypedDataMessage;
use starknet::core::types::FieldElement;
use std::str::FromStr;
use serde::Serialize;
use crate::starknet_signer::typed_data::TypedData;

#[wasm_bindgen]
// Rustfmt removes the 'async' keyword from async functions in extern blocks. It's fixed
Expand All @@ -15,23 +17,27 @@ extern "C" {

#[wasm_bindgen(structural,catch, method)]
async fn signMessage(_: &Signer,msg: &JsValue) -> Result<JsValue, JsValue>;

#[wasm_bindgen(method, getter)]
fn address(this: &Signer) -> String;
}

pub struct StarknetJsonRpcSigner {
signer: Signer,
pub_key: String,
chain_id: String,
}

impl StarknetJsonRpcSigner {
pub fn new(signer: Signer,pub_key: String) -> StarknetJsonRpcSigner{
StarknetJsonRpcSigner { signer,pub_key }
pub fn new(signer: Signer,pub_key: String,chain_id: String) -> StarknetJsonRpcSigner{
StarknetJsonRpcSigner { signer,pub_key,chain_id }
}

pub async fn sign_message(
&self,
message: TypedDataMessage,
) -> Result<StarkECDSASignature, StarkSignerError> {
let typed_data = TypedData::new(message);
let typed_data = TypedData::new(message,self.chain_id.clone());
let typed_data = serde_wasm_bindgen::to_value(&typed_data)
.map_err(|e| StarkSignerError::SignError(e.to_string()))?;
let signature = self.signer.signMessage(&typed_data).await.map_err(|e| {
Expand Down
4 changes: 2 additions & 2 deletions signers/src/starknet_signer/typed_data/message.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize,Debug)]
#[derive(Serialize, Deserialize,Debug,Clone)]
pub struct TxMessage {
pub transaction: String,
pub amount: String,
pub fee: String,
pub token: String,
pub to: String,
pub nonce: u64,
pub nonce: String,
}

#[derive(Serialize, Deserialize,Debug)]
Expand Down
123 changes: 119 additions & 4 deletions signers/src/starknet_signer/typed_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ pub mod message;

use serde::{Deserialize, Serialize};
use crate::starknet_signer::typed_data::message::TypedDataMessage;
use crate::starknet_signer::error::StarkSignerError;
use num::{BigUint, Num};
use starknet_crypto::FieldElement;
use starknet::core::utils::starknet_keccak;

#[derive(Serialize, Deserialize,Debug)]
#[serde(rename_all = "camelCase")]
Expand All @@ -11,7 +15,7 @@ pub struct StarknetDomain {
pub chain_id: String
}

#[derive(Serialize, Deserialize,Debug)]
#[derive(Serialize, Deserialize,Debug,Clone)]
pub struct TypeDefine {
pub name: String,
pub r#type: String,
Expand All @@ -34,7 +38,7 @@ pub struct TypedData {
}

impl TypedData {
pub fn new(message: TypedDataMessage) -> Self {
pub fn new(message: TypedDataMessage,chain_id: String) -> Self {
let starknet_domain_type = vec![
TypeDefine { name: "name".to_string(), r#type: "string".to_string() },
TypeDefine { name: "version".to_string(), r#type: "string".to_string() },
Expand All @@ -48,7 +52,7 @@ impl TypedData {
let domain = StarknetDomain {
name: "zklink".to_string(),
version: "1".to_string(),
chain_id: "SN_GOERLI".to_string()
chain_id,
};
Self {
types,
Expand All @@ -72,9 +76,120 @@ impl TypedData {
TypeDefine { name: "fee".to_string(), r#type: "string".to_string() },
TypeDefine { name: "token".to_string(), r#type: "string".to_string() },
TypeDefine { name: "to".to_string(), r#type: "string".to_string() },
TypeDefine { name: "nonce".to_string(), r#type: "felt".to_string() },
TypeDefine { name: "nonce".to_string(), r#type: "string".to_string() },
]
},
}
}

fn string_to_hex(s: &str) -> String {
if let Ok(num) = BigUint::from_str_radix(s.trim_start_matches("0x"),16) {
format!("0x{}",num.to_str_radix(16))
} else {
format!("0x{}",hex::encode(s))
}
}

pub fn get_type_define(&self,struct_type: &str) -> Vec<TypeDefine> {
if struct_type == "StarkNetDomain" {
self.types.stark_net_domain.clone()
} else if struct_type == "Message" {
self.types.message.clone()
} else {
vec![]
}
}

pub fn encode_type(&self,struct_type: &str) -> String {
let td = self.get_type_define(struct_type);
let mut ret = struct_type.to_string() + "(";
let mut fields = vec![];
for t in td {
let field = format!("{}:{}",t.name,t.r#type);
fields.push(field);
}
ret += &fields.join(",");
ret += ")";
ret
}

pub fn compute_hash_on_elements(data: Vec<String>) -> Result<FieldElement,StarkSignerError> {
let mut result = FieldElement::from(0u32);
for e in &data {
let fe = FieldElement::from_hex_be(e)
.map_err(|e| StarkSignerError::SignError(e.to_string()))?;
result = starknet_crypto::pedersen_hash(&result, &fe);
}

let data_len = FieldElement::from(data.len());
Ok(starknet_crypto::pedersen_hash(&result, &data_len))
}

pub fn get_struct_hash<T: Serialize>(&self,struct_type: &str,data: &T) -> Result<[u8;32],StarkSignerError> {
let mut types_arry = vec![];
let mut data_arry = vec![];
types_arry.push("felt".to_string());
let encoded_type = self.encode_type(struct_type);
println!("{}",encoded_type);
let type_hash = starknet_keccak(self.encode_type(struct_type).as_bytes());
data_arry.push(format!("0x{}",hex::encode(type_hash.to_bytes_be())));
let data_value = serde_json::to_value(data)
.map_err(|e| StarkSignerError::SignError(e.to_string()))?;
let data_map = data_value.as_object().unwrap();
//type must be exist
let td = self.get_type_define(struct_type);
if td.is_empty() {
return Err(StarkSignerError::SignError("Invalid type name".to_string()));
}

for t in td {
types_arry.push(t.r#type.clone());
let v_str = data_map.get(&t.name).unwrap().as_str().unwrap();
let v = Self::string_to_hex(&v_str);
data_arry.push(v);
}

println!("{:?}",data_arry);
let result = Self::compute_hash_on_elements(data_arry)?;
Ok(result.to_bytes_be())
}

pub fn encode(&self,addr: String) -> Result<Vec<String>, StarkSignerError> {
let domain = self.get_struct_hash("StarkNetDomain", &self.domain)
.map_err(|e| StarkSignerError::SignError(e.to_string()))?;
let message = self.get_struct_hash("Message",&self.message)
.map_err(|e| StarkSignerError::SignError(e.to_string()))?;
//StarkNet Message
let stark_net_message = Self::string_to_hex("StarkNet Message");
Ok(vec![stark_net_message,format!("0x{}",hex::encode(&domain)),addr,format!("0x{}",hex::encode(&message))])
}

pub fn get_message_hash(&self,addr: String) -> Result<FieldElement,StarkSignerError> {
let data = self.encode(addr)?;
println!("{:?}",data);
Ok(Self::compute_hash_on_elements(data)?)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::starknet_signer::typed_data::message::Message;
use num::Num;

#[test]
fn test_typed_data() {
let addr = "04a69b67bcabfa7d3ccb96e1d25c2e6fc93589fe24a6fd04566b8700ff97a71a";
let message = Message {
data: "Create zkLink L2".to_string()
};

let s = "0x5505a8cd4594dbf79d8c59c0df1414ab871ca896";
BigUint::from_str_radix(s.trim_start_matches("0x"),16).unwrap();
let typed_data = TypedData::new(TypedDataMessage::CreateL2Key(message),"SN_GOERLI".to_string());
let data = typed_data.encode(
"0x04a69b67bcabfa7d3ccb96e1d25c2e6fc93589fe24a6fd04566b8700ff97a71a".to_string()).unwrap();
println!("{:?}",data);

}
}
6 changes: 3 additions & 3 deletions signers/src/zklink_signer/pk_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ impl ZkLinkSigner {

#[cfg(feature = "web")]
pub async fn new_from_starknet_rpc_signer(starknet_signer: &StarknetJsonRpcSigner) -> Result<Self, Error> {
let message = TypedDataMessage::CreateL2Key(Message {
let message = Message {
data: Self::STARKNET_SIGN_MESSAGE.to_string()
});
};
let signature = starknet_signer
.sign_message(message)
.sign_message(TypedDataMessage::CreateL2Key(message))
.await?;
let seed = signature.signature.to_bytes_be();
Self::new_from_seed(&seed)
Expand Down
2 changes: 1 addition & 1 deletion types/src/tx_type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub fn starknet_sign_message_part(
amount: &BigUint,
fee: &BigUint,
to: &ZkLinkAddress,
nonce: u64,
nonce: String,
) -> TxMessage {
let message = TxMessage {
transaction: transaction.to_string(),
Expand Down
2 changes: 1 addition & 1 deletion types/src/tx_type/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl Transfer {
&self.amount,
&self.fee,
&self.to,
self.nonce.into(),
self.nonce.to_string(),
)
}

Expand Down
2 changes: 1 addition & 1 deletion types/src/tx_type/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl Withdraw {
&self.amount,
&self.fee,
&self.to,
self.nonce.into(),
self.nonce.to_string(),
)
}

Expand Down

0 comments on commit b049bef

Please sign in to comment.