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