1use crate::runtime::Runtime;
4
5use crate::error::Error;
6use crate::pb::{
7 self,
8 eth_sign_typed_message_request::{DataType, Member, MemberType, StructType},
9 eth_typed_message_value_response::RootObject,
10 request::Request,
11 response::Response,
12};
13use crate::Keypath;
14use crate::PairedBitBox;
15
16use std::collections::HashMap;
17use std::str::FromStr;
18
19use num_bigint::{BigInt, BigUint};
20use serde_json::Value;
22
23impl<R: Runtime> PairedBitBox<R> {
24 async fn query_proto_eth(
25 &self,
26 request: pb::eth_request::Request,
27 ) -> Result<pb::eth_response::Response, Error> {
28 match self
29 .query_proto(Request::Eth(pb::EthRequest {
30 request: Some(request),
31 }))
32 .await?
33 {
34 Response::Eth(pb::EthResponse {
35 response: Some(response),
36 }) => Ok(response),
37 _ => Err(Error::UnexpectedResponse),
38 }
39 }
40
41 pub fn eth_supported(&self) -> bool {
43 self.is_multi_edition()
44 }
45
46 pub async fn eth_xpub(&self, keypath: &Keypath) -> Result<String, Error> {
48 match self
49 .query_proto_eth(pb::eth_request::Request::Pub(pb::EthPubRequest {
50 keypath: keypath.to_vec(),
51 coin: 0,
52 output_type: pb::eth_pub_request::OutputType::Xpub as _,
53 display: false,
54 contract_address: vec![],
55 chain_id: 0,
56 }))
57 .await?
58 {
59 pb::eth_response::Response::Pub(pb::PubResponse { r#pub }) => Ok(r#pub),
60 _ => Err(Error::UnexpectedResponse),
61 }
62 }
63
64 pub async fn eth_address(
66 &self,
67 chain_id: u64,
68 keypath: &Keypath,
69 display: bool,
70 ) -> Result<String, Error> {
71 match self
72 .query_proto_eth(pb::eth_request::Request::Pub(pb::EthPubRequest {
73 keypath: keypath.to_vec(),
74 coin: 0,
75 output_type: pb::eth_pub_request::OutputType::Address as _,
76 display,
77 contract_address: vec![],
78 chain_id,
79 }))
80 .await?
81 {
82 pb::eth_response::Response::Pub(pb::PubResponse { r#pub }) => Ok(r#pub),
83 _ => Err(Error::UnexpectedResponse),
84 }
85 }
86}
87
88#[cfg_attr(
89 feature = "wasm",
90 derive(serde::Deserialize),
91 serde(rename_all = "camelCase")
92)]
93pub struct Transaction {
94 pub nonce: Vec<u8>,
96 pub gas_price: Vec<u8>,
98 pub gas_limit: Vec<u8>,
100 pub recipient: [u8; 20],
101 pub value: Vec<u8>,
103 pub data: Vec<u8>,
104}
105
106#[cfg_attr(
107 feature = "wasm",
108 derive(serde::Deserialize),
109 serde(rename_all = "camelCase")
110)]
111pub struct EIP1559Transaction {
112 pub chain_id: u64,
113 pub nonce: Vec<u8>,
115 pub max_priority_fee_per_gas: Vec<u8>,
117 pub max_fee_per_gas: Vec<u8>,
119 pub gas_limit: Vec<u8>,
121 pub recipient: [u8; 20],
122 pub value: Vec<u8>,
124 pub data: Vec<u8>,
125}
126
127pub fn eth_identify_case(recipient_address: &str) -> pb::EthAddressCase {
131 if recipient_address
132 .chars()
133 .all(|c| !c.is_ascii_alphabetic() || c.is_ascii_uppercase())
134 {
135 pb::EthAddressCase::Upper
136 } else if recipient_address
137 .chars()
138 .all(|c| !c.is_ascii_alphabetic() || c.is_ascii_lowercase())
139 {
140 pb::EthAddressCase::Lower
141 } else {
142 pb::EthAddressCase::Mixed
143 }
144}
145
146#[cfg(feature = "rlp")]
147impl TryFrom<&[u8]> for Transaction {
148 type Error = ();
149
150 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
151 let [nonce, gas_price, gas_limit, recipient, value, data, _, _, _]: [Vec<u8>; 9] =
152 rlp::decode_list(value).try_into().map_err(|_| ())?;
153 Ok(Transaction {
154 nonce,
155 gas_price,
156 gas_limit,
157 recipient: recipient.try_into().map_err(|_| ())?,
158 value,
159 data,
160 })
161 }
162}
163
164#[cfg(feature = "rlp")]
165impl TryFrom<&[u8]> for EIP1559Transaction {
166 type Error = ();
167
168 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
169 let [mut chain_id_vec, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, recipient, value, data, _, _, _]: [Vec<u8>; 11] =
170 rlp::decode_list(value).try_into().map_err(|_| ())?;
171 while chain_id_vec.len() < 8 {
172 chain_id_vec.insert(0, 0);
173 }
174 let chain_id = u64::from_be_bytes(chain_id_vec.try_into().map_err(|_| ())?);
175 Ok(EIP1559Transaction {
176 chain_id,
177 nonce,
178 max_priority_fee_per_gas,
179 max_fee_per_gas,
180 gas_limit,
181 recipient: recipient.try_into().map_err(|_| ())?,
182 value,
183 data,
184 })
185 }
186}
187
188#[derive(Debug, PartialEq, serde::Deserialize)]
189struct Eip712TypeMember {
190 name: String,
191 r#type: String,
192}
193
194#[derive(Debug, PartialEq, serde::Deserialize)]
195#[serde(rename_all = "camelCase")]
196struct Eip712Message {
197 types: HashMap<String, Vec<Eip712TypeMember>>,
198 primary_type: String,
199 domain: HashMap<String, Value>,
200 message: HashMap<String, Value>,
201}
202
203fn parse_type(
204 typ: &str,
205 types: &HashMap<String, Vec<Eip712TypeMember>>,
206) -> Result<MemberType, String> {
207 if typ.ends_with(']') {
208 let index = typ
209 .rfind('[')
210 .ok_or(format!("Invalid type format: {typ}"))?;
211 let (rest, size) = (&typ[..index], &typ[index + 1..typ.len() - 1]);
212 let size_int = if !size.is_empty() {
213 u32::from_str(size).map_err(|e| format!("Error parsing size: {e}"))?
214 } else {
215 0
216 };
217 let array_type = Box::new(parse_type(rest, types)?);
218 Ok(MemberType {
219 r#type: DataType::Array.into(),
220 size: size_int,
221 struct_name: String::new(),
222 array_type: Some(array_type),
223 })
224 } else if let Some(size) = typ.strip_prefix("bytes") {
225 let size_int = if !size.is_empty() {
226 u32::from_str(size).map_err(|e| format!("Error parsing size: {e}"))?
227 } else {
228 0
229 };
230 Ok(MemberType {
231 r#type: DataType::Bytes.into(),
232 size: size_int,
233 struct_name: String::new(),
234 array_type: None,
235 })
236 } else if let Some(size) = typ.strip_prefix("uint") {
237 if size.is_empty() {
238 return Err("uint must be sized".to_string());
239 }
240 let size_int = u32::from_str(size).map_err(|e| format!("Error parsing size: {e}"))? / 8;
241 Ok(MemberType {
242 r#type: DataType::Uint.into(),
243 size: size_int,
244 struct_name: String::new(),
245 array_type: None,
246 })
247 } else if let Some(size) = typ.strip_prefix("int") {
248 if size.is_empty() {
249 return Err("int must be sized".to_string());
250 }
251 let size_int = u32::from_str(size).map_err(|e| format!("Error parsing size: {e}"))? / 8;
252 Ok(MemberType {
253 r#type: DataType::Int.into(),
254 size: size_int,
255 struct_name: String::new(),
256 array_type: None,
257 })
258 } else if typ == "bool" {
259 Ok(MemberType {
260 r#type: DataType::Bool.into(),
261 size: 0,
262 struct_name: String::new(),
263 array_type: None,
264 })
265 } else if typ == "address" {
266 Ok(MemberType {
267 r#type: DataType::Address.into(),
268 size: 0,
269 struct_name: String::new(),
270 array_type: None,
271 })
272 } else if typ == "string" {
273 Ok(MemberType {
274 r#type: DataType::String.into(),
275 size: 0,
276 struct_name: String::new(),
277 array_type: None,
278 })
279 } else if types.contains_key(typ) {
280 Ok(MemberType {
281 r#type: DataType::Struct.into(),
282 size: 0,
283 struct_name: typ.to_string(),
284 array_type: None,
285 })
286 } else {
287 Err(format!("Can't recognize type: {typ}"))
288 }
289}
290
291fn encode_value(typ: &MemberType, value: &Value) -> Result<Vec<u8>, String> {
292 match DataType::try_from(typ.r#type).unwrap() {
293 DataType::Bytes => {
294 if let Value::String(v) = value {
295 if v.starts_with("0x") || v.starts_with("0X") {
296 hex::decode(&v[2..]).map_err(|e| e.to_string())
297 } else {
298 Ok(v.as_bytes().to_vec())
299 }
300 } else {
301 Err("Expected a string for bytes type".to_string())
302 }
303 }
304 DataType::Uint => match value {
305 Value::String(v) => {
306 if v.starts_with("0x") || v.starts_with("0X") {
307 Ok(BigUint::parse_bytes(&v.as_bytes()[2..], 16)
308 .ok_or(format!("could not parse {v} as hex"))?
309 .to_bytes_be())
310 } else {
311 Ok(BigUint::from_str(v)
312 .map_err(|e| e.to_string())?
313 .to_bytes_be())
314 }
315 }
316 Value::Number(n) => {
317 if let Some(v) = n.as_f64() {
318 let v64: u64 = v as _;
319 if (v64 as f64) != v {
320 Err("Number is not an uint".to_string())
321 } else {
322 Ok(BigUint::from(v64).to_bytes_be())
323 }
324 } else {
325 Err("Number is not an uint".to_string())
326 }
327 }
328 _ => Err("Wrong type for uint".to_string()),
329 },
330 DataType::Int => match value {
331 Value::String(v) => Ok(BigInt::from_str(v)
332 .map_err(|e| e.to_string())?
333 .to_signed_bytes_be()),
334 Value::Number(n) => {
335 if let Some(v) = n.as_f64() {
336 let v64: i64 = v as _;
337 if (v64 as f64) != v {
338 Err("Number is not an int".to_string())
339 } else {
340 let mut bytes = BigInt::from(v64).to_signed_bytes_be();
341 if let [0, ..] = bytes.as_slice() {
343 bytes.remove(0);
344 }
345 Ok(bytes)
346 }
347 } else {
348 Err("Number is not an int".to_string())
349 }
350 }
351 _ => Err("Wrong type for int".to_string()),
352 },
353 DataType::Bool => {
354 if value.as_bool().ok_or("Expected a boolean value")? {
355 Ok(vec![1])
356 } else {
357 Ok(vec![0])
358 }
359 }
360 DataType::Address | DataType::String => {
361 if let Value::String(v) = value {
362 Ok(v.as_bytes().to_vec())
363 } else {
364 Err("Expected a string value".to_string())
365 }
366 }
367 DataType::Array => {
368 if let Value::Array(arr) = value {
369 let size = arr.len() as u32;
370 Ok(size.to_be_bytes().to_vec())
371 } else {
372 Err("Expected an array".to_string())
373 }
374 }
375 _ => Err("looked up value must not be a map or array".to_string()),
376 }
377}
378
379fn get_value(
380 what: &pb::EthTypedMessageValueResponse,
381 msg: &Eip712Message,
382) -> Result<Vec<u8>, String> {
383 enum Either<'a> {
384 HashMap(&'a HashMap<String, Value>),
385 JsonValue(Value),
386 }
387 impl Either<'_> {
388 fn get(&self, key: &str) -> Option<&Value> {
389 match self {
390 Either::HashMap(map) => map.get(key),
391 Either::JsonValue(Value::Object(map)) => map.get(key),
392 _ => None,
393 }
394 }
395 }
396
397 let (mut value, mut typ): (Either, MemberType) =
398 match RootObject::try_from(what.root_object).unwrap() {
399 RootObject::Unknown => return Err("unkown root object".into()),
400 RootObject::Domain => (
401 Either::HashMap(&msg.domain),
402 parse_type("EIP712Domain", &msg.types)?,
403 ),
404 RootObject::Message => (
405 Either::HashMap(&msg.message),
406 parse_type(&msg.primary_type, &msg.types)?,
407 ),
408 };
409 for element in what.path.iter() {
410 match DataType::try_from(typ.r#type).unwrap() {
411 DataType::Struct => {
412 let struct_member: &Eip712TypeMember = msg
413 .types
414 .get(&typ.struct_name)
415 .ok_or(format!(
416 "could not lookup type of name: {}",
417 &typ.struct_name
418 ))?
419 .get(*element as usize)
420 .ok_or(format!(
421 "could not lookup member #{} of type: {}",
422 *element, &typ.struct_name
423 ))?;
424 value = Either::JsonValue(
425 value
426 .get(&struct_member.name)
427 .ok_or(format!("could not lookup: {}", struct_member.name.as_str()))?
428 .clone(),
429 );
430 typ = parse_type(&struct_member.r#type, &msg.types)?;
431 }
432 DataType::Array => {
433 if let Either::JsonValue(Value::Array(list)) = value {
434 value = Either::JsonValue(
435 list.get(*element as usize)
436 .ok_or(format!("could not lookup array index: {}", *element))?
437 .clone(),
438 );
439 typ = *typ.array_type.unwrap();
440 }
441 }
442 _ => return Err("path element does not point to struct or array".into()),
443 }
444 }
445 if let Either::JsonValue(value) = &value {
446 encode_value(&typ, value)
447 } else {
448 Err("path points to struct or array; value expected".to_string())
449 }
450}
451
452impl<R: Runtime> PairedBitBox<R> {
453 async fn handle_antiklepto(
454 &self,
455 response: &pb::eth_response::Response,
456 host_nonce: [u8; 32],
457 ) -> Result<[u8; 65], Error> {
458 match response {
459 pb::eth_response::Response::AntikleptoSignerCommitment(
460 pb::AntiKleptoSignerCommitment { commitment },
461 ) => {
462 match self
463 .query_proto_eth(pb::eth_request::Request::AntikleptoSignature(
464 pb::AntiKleptoSignatureRequest {
465 host_nonce: host_nonce.to_vec(),
466 },
467 ))
468 .await?
469 {
470 pb::eth_response::Response::Sign(pb::EthSignResponse { signature }) => {
471 crate::antiklepto::verify_ecdsa(&host_nonce, commitment, &signature)?;
472 signature.try_into().map_err(|_| Error::UnexpectedResponse)
473 }
474 _ => Err(Error::UnexpectedResponse),
475 }
476 }
477 _ => Err(Error::UnexpectedResponse),
478 }
479 }
480
481 pub async fn eth_sign_transaction(
485 &self,
486 chain_id: u64,
487 keypath: &Keypath,
488 tx: &Transaction,
489 address_case: Option<pb::EthAddressCase>,
490 ) -> Result<[u8; 65], Error> {
491 self.validate_version(">=9.10.0")?;
493
494 let host_nonce = crate::antiklepto::gen_host_nonce()?;
495 let request = pb::eth_request::Request::Sign(pb::EthSignRequest {
496 coin: 0,
497 keypath: keypath.to_vec(),
498 nonce: crate::util::remove_leading_zeroes(&tx.nonce),
499 gas_price: crate::util::remove_leading_zeroes(&tx.gas_price),
500 gas_limit: crate::util::remove_leading_zeroes(&tx.gas_limit),
501 recipient: tx.recipient.to_vec(),
502 value: crate::util::remove_leading_zeroes(&tx.value),
503 data: tx.data.clone(),
504 host_nonce_commitment: Some(pb::AntiKleptoHostNonceCommitment {
505 commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(),
506 }),
507 chain_id,
508 address_case: address_case.unwrap_or(pb::EthAddressCase::Mixed).into(),
509 });
510 let response = self.query_proto_eth(request).await?;
511 self.handle_antiklepto(&response, host_nonce).await
512 }
513
514 pub async fn eth_sign_1559_transaction(
518 &self,
519 keypath: &Keypath,
520 tx: &EIP1559Transaction,
521 address_case: Option<pb::EthAddressCase>,
522 ) -> Result<[u8; 65], Error> {
523 self.validate_version(">=9.16.0")?;
525
526 let host_nonce = crate::antiklepto::gen_host_nonce()?;
527 let request = pb::eth_request::Request::SignEip1559(pb::EthSignEip1559Request {
528 chain_id: tx.chain_id,
529 keypath: keypath.to_vec(),
530 nonce: crate::util::remove_leading_zeroes(&tx.nonce),
531 max_priority_fee_per_gas: crate::util::remove_leading_zeroes(
532 &tx.max_priority_fee_per_gas,
533 ),
534 max_fee_per_gas: crate::util::remove_leading_zeroes(&tx.max_fee_per_gas),
535 gas_limit: crate::util::remove_leading_zeroes(&tx.gas_limit),
536 recipient: tx.recipient.to_vec(),
537 value: crate::util::remove_leading_zeroes(&tx.value),
538 data: tx.data.clone(),
539 host_nonce_commitment: Some(pb::AntiKleptoHostNonceCommitment {
540 commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(),
541 }),
542 address_case: address_case.unwrap_or(pb::EthAddressCase::Mixed).into(),
543 });
544 let response = self.query_proto_eth(request).await?;
545 self.handle_antiklepto(&response, host_nonce).await
546 }
547
548 pub async fn eth_sign_message(
553 &self,
554 chain_id: u64,
555 keypath: &Keypath,
556 msg: &[u8],
557 ) -> Result<[u8; 65], Error> {
558 self.validate_version(">=9.10.0")?;
560
561 let host_nonce = crate::antiklepto::gen_host_nonce()?;
562 let request = pb::eth_request::Request::SignMsg(pb::EthSignMessageRequest {
563 coin: 0,
564 keypath: keypath.to_vec(),
565 msg: msg.to_vec(),
566 host_nonce_commitment: Some(pb::AntiKleptoHostNonceCommitment {
567 commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(),
568 }),
569 chain_id,
570 });
571 let response = self.query_proto_eth(request).await?;
572 let mut signature = self.handle_antiklepto(&response, host_nonce).await?;
573 signature[64] += 27;
575 Ok(signature)
576 }
577
578 pub async fn eth_sign_typed_message(
581 &self,
582 chain_id: u64,
583 keypath: &Keypath,
584 json_msg: &str,
585 ) -> Result<[u8; 65], Error> {
586 self.validate_version(">=9.12.0")?;
587
588 let msg: Eip712Message = serde_json::from_str(json_msg)
589 .map_err(|_| Error::EthTypedMessage("Could not parse EIP-712 JSON message".into()))?;
590
591 let parsed_types: Vec<StructType> = msg
592 .types
593 .iter()
594 .map(|(name, members)| {
595 Ok(StructType {
596 name: name.clone(),
597 members: members
598 .iter()
599 .map(|member| {
600 Ok(Member {
601 name: member.name.clone(),
602 r#type: Some(parse_type(&member.r#type, &msg.types)?),
603 })
604 })
605 .collect::<Result<Vec<Member>, String>>()?,
606 })
607 })
608 .collect::<Result<Vec<StructType>, String>>()
609 .map_err(Error::EthTypedMessage)?;
610
611 let host_nonce = crate::antiklepto::gen_host_nonce()?;
612
613 let mut response = self
614 .query_proto_eth(pb::eth_request::Request::SignTypedMsg(
615 pb::EthSignTypedMessageRequest {
616 chain_id,
617 keypath: keypath.to_vec(),
618 types: parsed_types,
619 primary_type: msg.primary_type.clone(),
620 host_nonce_commitment: Some(pb::AntiKleptoHostNonceCommitment {
621 commitment: crate::antiklepto::host_commit(&host_nonce).to_vec(),
622 }),
623 },
624 ))
625 .await?;
626 while let pb::eth_response::Response::TypedMsgValue(typed_msg_value) = &response {
627 let value = get_value(typed_msg_value, &msg).map_err(Error::EthTypedMessage)?;
628 response = self
629 .query_proto_eth(pb::eth_request::Request::TypedMsgValue(
630 pb::EthTypedMessageValueRequest { value },
631 ))
632 .await?;
633 }
634 let mut signature = self.handle_antiklepto(&response, host_nonce).await?;
635 signature[64] += 27;
637 Ok(signature)
638 }
639}
640
641#[cfg(test)]
642mod tests {
643 use super::*;
644
645 const EIP712_MSG: &str = r#"
646 {
647 "types": {
648 "EIP712Domain": [
649 { "name": "name", "type": "string" },
650 { "name": "version", "type": "string" },
651 { "name": "chainId", "type": "uint256" },
652 { "name": "verifyingContract", "type": "address" }
653 ],
654 "Attachment": [
655 { "name": "contents", "type": "string" }
656 ],
657 "Person": [
658 { "name": "name", "type": "string" },
659 { "name": "wallet", "type": "address" },
660 { "name": "age", "type": "uint8" }
661 ],
662 "Mail": [
663 { "name": "from", "type": "Person" },
664 { "name": "to", "type": "Person" },
665 { "name": "contents", "type": "string" },
666 { "name": "attachments", "type": "Attachment[]" }
667 ]
668 },
669 "primaryType": "Mail",
670 "domain": {
671 "name": "Ether Mail",
672 "version": "1",
673 "chainId": 1,
674 "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
675 },
676 "message": {
677 "from": {
678 "name": "Cow",
679 "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
680 "age": 20
681 },
682 "to": {
683 "name": "Bob",
684 "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
685 "age": "0x1e"
686 },
687 "contents": "Hello, Bob!",
688 "attachments": [{ "contents": "attachment1" }, { "contents": "attachment2" }]
689 }
690}
691 "#;
692
693 fn map_from(elements: &[(&str, Value)]) -> Value {
694 let mut m = serde_json::Map::<String, Value>::new();
695 for (k, v) in elements.iter().cloned() {
696 m.insert(k.into(), v);
697 }
698 m.into()
699 }
700
701 #[test]
702 fn test_deserialize_eip713_message() {
703 let msg: Eip712Message = serde_json::from_str(EIP712_MSG).unwrap();
704 assert_eq!(
705 msg,
706 Eip712Message {
707 types: HashMap::from([
708 (
709 "EIP712Domain".into(),
710 vec![
711 Eip712TypeMember {
712 name: "name".into(),
713 r#type: "string".into()
714 },
715 Eip712TypeMember {
716 name: "version".into(),
717 r#type: "string".into()
718 },
719 Eip712TypeMember {
720 name: "chainId".into(),
721 r#type: "uint256".into()
722 },
723 Eip712TypeMember {
724 name: "verifyingContract".into(),
725 r#type: "address".into()
726 },
727 ]
728 ),
729 (
730 "Attachment".into(),
731 vec![Eip712TypeMember {
732 name: "contents".into(),
733 r#type: "string".into()
734 },]
735 ),
736 (
737 "Person".into(),
738 vec![
739 Eip712TypeMember {
740 name: "name".into(),
741 r#type: "string".into()
742 },
743 Eip712TypeMember {
744 name: "wallet".into(),
745 r#type: "address".into()
746 },
747 Eip712TypeMember {
748 name: "age".into(),
749 r#type: "uint8".into()
750 },
751 ]
752 ),
753 (
754 "Mail".into(),
755 vec![
756 Eip712TypeMember {
757 name: "from".into(),
758 r#type: "Person".into()
759 },
760 Eip712TypeMember {
761 name: "to".into(),
762 r#type: "Person".into()
763 },
764 Eip712TypeMember {
765 name: "contents".into(),
766 r#type: "string".into()
767 },
768 Eip712TypeMember {
769 name: "attachments".into(),
770 r#type: "Attachment[]".into()
771 },
772 ]
773 ),
774 ]),
775 primary_type: "Mail".into(),
776 domain: HashMap::from([
777 ("name".into(), "Ether Mail".into()),
778 ("version".into(), "1".into()),
779 ("chainId".into(), 1.into()),
780 (
781 "verifyingContract".into(),
782 "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC".into()
783 ),
784 ]),
785 message: HashMap::from([
786 (
787 "from".into(),
788 map_from(&[
789 ("name", "Cow".into()),
790 (
791 "wallet",
792 "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826".into()
793 ),
794 ("age", 20.into())
795 ]),
796 ),
797 (
798 "to".into(),
799 map_from(&[
800 ("name", "Bob".into()),
801 (
802 "wallet",
803 "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB".into()
804 ),
805 ("age", "0x1e".into())
806 ]),
807 ),
808 ("contents".into(), "Hello, Bob!".into()),
809 (
810 "attachments".into(),
811 Value::Array(vec![
812 map_from(&[("contents", "attachment1".into())]),
813 map_from(&[("contents", "attachment2".into())])
814 ])
815 ),
816 ]),
817 }
818 );
819 }
820
821 fn parse_type_no_err(typ: &str, types: &HashMap<String, Vec<Eip712TypeMember>>) -> MemberType {
822 parse_type(typ, types).unwrap()
823 }
824
825 #[test]
826 fn test_parse_type() {
827 assert_eq!(
828 MemberType {
829 r#type: DataType::String.into(),
830 ..Default::default()
831 },
832 parse_type_no_err("string", &HashMap::new())
833 );
834
835 assert_eq!(
837 MemberType {
838 r#type: DataType::Bytes.into(),
839 ..Default::default()
840 },
841 parse_type_no_err("bytes", &HashMap::new())
842 );
843 assert_eq!(
844 MemberType {
845 r#type: DataType::Bytes.into(),
846 size: 1,
847 ..Default::default()
848 },
849 parse_type_no_err("bytes1", &HashMap::new())
850 );
851 assert_eq!(
852 MemberType {
853 r#type: DataType::Bytes.into(),
854 size: 10,
855 ..Default::default()
856 },
857 parse_type_no_err("bytes10", &HashMap::new())
858 );
859 assert_eq!(
860 MemberType {
861 r#type: DataType::Bytes.into(),
862 size: 32,
863 ..Default::default()
864 },
865 parse_type_no_err("bytes32", &HashMap::new())
866 );
867
868 assert_eq!(
869 MemberType {
870 r#type: DataType::Bool.into(),
871 ..Default::default()
872 },
873 parse_type_no_err("bool", &HashMap::new())
874 );
875
876 assert_eq!(
877 MemberType {
878 r#type: DataType::Address.into(),
879 ..Default::default()
880 },
881 parse_type_no_err("address", &HashMap::new())
882 );
883
884 assert_eq!(
886 MemberType {
887 r#type: DataType::Uint.into(),
888 size: 1,
889 ..Default::default()
890 },
891 parse_type_no_err("uint8", &HashMap::new())
892 );
893 assert_eq!(
894 MemberType {
895 r#type: DataType::Uint.into(),
896 size: 2,
897 ..Default::default()
898 },
899 parse_type_no_err("uint16", &HashMap::new())
900 );
901 assert_eq!(
902 MemberType {
903 r#type: DataType::Uint.into(),
904 size: 32,
905 ..Default::default()
906 },
907 parse_type_no_err("uint256", &HashMap::new())
908 );
909 assert!(parse_type("uint", &HashMap::new()).is_err());
910 assert!(parse_type("uintfoo", &HashMap::new()).is_err());
911
912 assert_eq!(
914 MemberType {
915 r#type: DataType::Int.into(),
916 size: 1,
917 ..Default::default()
918 },
919 parse_type_no_err("int8", &HashMap::new())
920 );
921 assert_eq!(
922 MemberType {
923 r#type: DataType::Int.into(),
924 size: 2,
925 ..Default::default()
926 },
927 parse_type_no_err("int16", &HashMap::new())
928 );
929 assert_eq!(
930 MemberType {
931 r#type: DataType::Int.into(),
932 size: 32,
933 ..Default::default()
934 },
935 parse_type_no_err("int256", &HashMap::new())
936 );
937 assert!(parse_type("int", &HashMap::new()).is_err());
938 assert!(parse_type("intfoo", &HashMap::new()).is_err());
939
940 assert_eq!(
942 MemberType {
943 r#type: DataType::Array.into(),
944 array_type: Some(Box::new(MemberType {
945 r#type: DataType::String.into(),
946 ..Default::default()
947 })),
948 ..Default::default()
949 },
950 parse_type_no_err("string[]", &HashMap::new())
951 );
952 assert_eq!(
953 MemberType {
954 r#type: DataType::Array.into(),
955 size: 521,
956 array_type: Some(Box::new(MemberType {
957 r#type: DataType::String.into(),
958 ..Default::default()
959 })),
960 ..Default::default()
961 },
962 parse_type_no_err("string[521]", &HashMap::new())
963 );
964 assert_eq!(
965 MemberType {
966 r#type: DataType::Array.into(),
967 size: 521,
968 array_type: Some(Box::new(MemberType {
969 r#type: DataType::Uint.into(),
970 size: 4,
971 ..Default::default()
972 })),
973 ..Default::default()
974 },
975 parse_type_no_err("uint32[521]", &HashMap::new())
976 );
977 assert_eq!(
978 MemberType {
979 r#type: DataType::Array.into(),
980 array_type: Some(Box::new(MemberType {
981 r#type: DataType::Array.into(),
982 size: 521,
983 array_type: Some(Box::new(MemberType {
984 r#type: DataType::Uint.into(),
985 size: 4,
986 ..Default::default()
987 })),
988 ..Default::default()
989 })),
990 ..Default::default()
991 },
992 parse_type_no_err("uint32[521][]", &HashMap::new())
993 );
994
995 assert!(parse_type("Unknown", &HashMap::new()).is_err());
997
998 assert_eq!(
999 MemberType {
1000 r#type: DataType::Struct.into(),
1001 struct_name: "Person".to_string(),
1002 ..Default::default()
1003 },
1004 parse_type_no_err(
1005 "Person",
1006 &HashMap::from([("Person".to_string(), Vec::new())])
1007 )
1008 );
1009 }
1010
1011 #[test]
1012 fn test_encode_value() {
1013 let encoded =
1014 encode_value(&parse_type_no_err("bytes", &HashMap::new()), &"foo".into()).unwrap();
1015 assert_eq!(b"foo".to_vec(), encoded);
1016
1017 let encoded = encode_value(
1018 &parse_type_no_err("bytes3", &HashMap::new()),
1019 &"0xaabbcc".into(),
1020 )
1021 .unwrap();
1022 assert_eq!(vec![0xaa, 0xbb, 0xcc], encoded);
1023
1024 let encoded = encode_value(
1025 &parse_type_no_err("uint64", &HashMap::new()),
1026 &2983742332.0.into(),
1027 )
1028 .unwrap();
1029 assert_eq!(vec![0xb1, 0xd8, 0x4b, 0x7c], encoded);
1030
1031 let encoded = encode_value(
1032 &parse_type_no_err("uint64", &HashMap::new()),
1033 &"0xb1d84b7c".into(),
1034 )
1035 .unwrap();
1036 assert_eq!(vec![0xb1, 0xd8, 0x4b, 0x7c], encoded);
1037
1038 let encoded =
1039 encode_value(&parse_type_no_err("uint64", &HashMap::new()), &"0x1".into()).unwrap();
1040 assert_eq!(vec![0x01], encoded);
1041
1042 let encoded = encode_value(
1043 &parse_type_no_err("uint64", &HashMap::new()),
1044 &"0x0001".into(),
1045 )
1046 .unwrap();
1047 assert_eq!(vec![0x01], encoded);
1048
1049 assert!(encode_value(
1050 &parse_type_no_err("uint64", &HashMap::new()),
1051 &"0xnot correct".into(),
1052 )
1053 .is_err());
1054
1055 let encoded = encode_value(
1056 &parse_type_no_err("int64", &HashMap::new()),
1057 &2983742332.0.into(),
1058 )
1059 .unwrap();
1060 assert_eq!(vec![0xb1, 0xd8, 0x4b, 0x7c], encoded);
1061
1062 let encoded = encode_value(
1063 &parse_type_no_err("int64", &HashMap::new()),
1064 &(-2983742332.0).into(),
1065 )
1066 .unwrap();
1067 assert_eq!(vec![0xff, 0x4e, 0x27, 0xb4, 0x84], encoded);
1068
1069 let encoded =
1070 encode_value(&parse_type_no_err("string", &HashMap::new()), &"foo".into()).unwrap();
1071 assert_eq!(b"foo".to_vec(), encoded);
1072
1073 let encoded = encode_value(
1074 &parse_type_no_err("address", &HashMap::new()),
1075 &"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC".into(),
1076 )
1077 .unwrap();
1078 assert_eq!(
1079 b"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC".to_vec(),
1080 encoded
1081 );
1082
1083 let encoded =
1084 encode_value(&parse_type_no_err("bool", &HashMap::new()), &false.into()).unwrap();
1085 assert_eq!(vec![0], encoded);
1086
1087 let encoded =
1088 encode_value(&parse_type_no_err("bool", &HashMap::new()), &true.into()).unwrap();
1089 assert_eq!(vec![1], encoded);
1090
1091 let encoded = encode_value(
1093 &parse_type_no_err("bool[]", &HashMap::new()),
1094 &Value::Array(vec![]),
1095 )
1096 .unwrap();
1097 assert_eq!(b"\x00\x00\x00\x00".to_vec(), encoded);
1098
1099 let encoded = encode_value(
1100 &parse_type_no_err("uint8[]", &HashMap::new()),
1101 &Value::Array(vec![1.into(); 10]),
1102 )
1103 .unwrap();
1104 assert_eq!(b"\x00\x00\x00\x0a".to_vec(), encoded);
1105
1106 let encoded = encode_value(
1107 &parse_type_no_err("uint8[]", &HashMap::new()),
1108 &Value::Array(vec![1.into(); 1000]),
1109 )
1110 .unwrap();
1111 assert_eq!(b"\x00\x00\x03\xe8".to_vec(), encoded);
1112 }
1113
1114 #[test]
1115 fn test_get_value() {
1116 let msg: Eip712Message = serde_json::from_str(EIP712_MSG).unwrap();
1117
1118 assert!(get_value(
1122 &pb::EthTypedMessageValueResponse {
1123 root_object: RootObject::Domain as _,
1124 path: vec![],
1125 },
1126 &msg,
1127 )
1128 .is_err());
1129 assert!(get_value(
1131 &pb::EthTypedMessageValueResponse {
1132 root_object: RootObject::Domain as _,
1133 path: vec![0, 0],
1134 },
1135 &msg,
1136 )
1137 .is_err());
1138
1139 let value = get_value(
1141 &pb::EthTypedMessageValueResponse {
1142 root_object: RootObject::Domain as _,
1143 path: vec![0],
1144 },
1145 &msg,
1146 )
1147 .unwrap();
1148 assert_eq!(value, b"Ether Mail".to_vec());
1149
1150 let value = get_value(
1152 &pb::EthTypedMessageValueResponse {
1153 root_object: RootObject::Domain as _,
1154 path: vec![1],
1155 },
1156 &msg,
1157 )
1158 .unwrap();
1159 assert_eq!(value, b"1".to_vec());
1160
1161 let value = get_value(
1163 &pb::EthTypedMessageValueResponse {
1164 root_object: RootObject::Domain as _,
1165 path: vec![2],
1166 },
1167 &msg,
1168 )
1169 .unwrap();
1170 assert_eq!(value, b"\x01".to_vec());
1171
1172 let value = get_value(
1174 &pb::EthTypedMessageValueResponse {
1175 root_object: RootObject::Domain as _,
1176 path: vec![3],
1177 },
1178 &msg,
1179 )
1180 .unwrap();
1181 assert_eq!(
1182 value,
1183 b"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC".to_vec()
1184 );
1185 assert!(get_value(
1187 &pb::EthTypedMessageValueResponse {
1188 root_object: RootObject::Domain as _,
1189 path: vec![4],
1190 },
1191 &msg,
1192 )
1193 .is_err());
1194
1195 let value = get_value(
1199 &pb::EthTypedMessageValueResponse {
1200 root_object: RootObject::Message as _,
1201 path: vec![0, 0],
1202 },
1203 &msg,
1204 )
1205 .unwrap();
1206 assert_eq!(value, b"Cow".to_vec());
1207
1208 let value = get_value(
1210 &pb::EthTypedMessageValueResponse {
1211 root_object: RootObject::Message as _,
1212 path: vec![0, 1],
1213 },
1214 &msg,
1215 )
1216 .unwrap();
1217 assert_eq!(
1218 value,
1219 b"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826".to_vec()
1220 );
1221
1222 let value = get_value(
1224 &pb::EthTypedMessageValueResponse {
1225 root_object: RootObject::Message as _,
1226 path: vec![1, 1],
1227 },
1228 &msg,
1229 )
1230 .unwrap();
1231 assert_eq!(
1232 value,
1233 b"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB".to_vec()
1234 );
1235
1236 let value = get_value(
1238 &pb::EthTypedMessageValueResponse {
1239 root_object: RootObject::Message as _,
1240 path: vec![3, 0, 0],
1241 },
1242 &msg,
1243 )
1244 .unwrap();
1245 assert_eq!(value, b"attachment1".to_vec());
1246
1247 let value = get_value(
1249 &pb::EthTypedMessageValueResponse {
1250 root_object: RootObject::Message as _,
1251 path: vec![3, 1, 0],
1252 },
1253 &msg,
1254 )
1255 .unwrap();
1256 assert_eq!(value, b"attachment2".to_vec());
1257
1258 assert!(get_value(
1260 &pb::EthTypedMessageValueResponse {
1261 root_object: RootObject::Message as _,
1262 path: vec![3, 2, 0],
1263 },
1264 &msg,
1265 )
1266 .is_err());
1267
1268 assert!(get_value(
1270 &pb::EthTypedMessageValueResponse {
1271 root_object: RootObject::Message as _,
1272 path: vec![3, 1, 1],
1273 },
1274 &msg,
1275 )
1276 .is_err());
1277 }
1278
1279 #[test]
1280 fn test_eth_identify_case() {
1281 assert_eq!(
1282 eth_identify_case("0XF39FD6E51AAD88F6F4CE6AB8827279CFFFB92266"),
1283 pb::EthAddressCase::Upper
1284 );
1285 assert_eq!(
1286 eth_identify_case("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"),
1287 pb::EthAddressCase::Lower
1288 );
1289 assert_eq!(
1290 eth_identify_case("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
1291 pb::EthAddressCase::Mixed
1292 );
1293 }
1294}