Răsfoiți Sursa

sdk/crypto: FieldElemAsStr trait which provides to/from_str() for Fp/Fq

zero 2 ani în urmă
părinte
comite
4c049778bb
2 a modificat fișierele cu 57 adăugiri și 0 ștergeri
  1. 51 0
      src/sdk/src/crypto/util.rs
  2. 6 0
      src/sdk/src/error.rs

+ 51 - 0
src/sdk/src/crypto/util.rs

@@ -25,6 +25,8 @@ use pasta_curves::{
 use std::io::Cursor;
 use subtle::CtOption;
 
+use crate::error::{ContractError, GenericResult};
+
 #[inline]
 fn hash_to_field_elem<F: FromUniformBytes<64>>(persona: &[u8], vals: &[&[u8]]) -> F {
     let mut hasher = blake2b_simd::Params::new().hash_length(64).personal(persona).to_state();
@@ -85,6 +87,43 @@ pub fn fp_to_u64(value: pallas::Base) -> Option<u64> {
     Some(uint)
 }
 
+// Not allowed to implement external traits for external crates
+pub trait FieldElemAsStr: PrimeField<Repr = [u8; 32]> {
+    fn to_str(&self) -> String {
+        let mut repr = String::new();
+        for &b in self.to_repr().iter().rev() {
+            repr += &format!("{:02x}", b);
+        }
+        repr
+    }
+
+    fn from_str(hex: &str) -> GenericResult<Self> {
+        if hex.len() != 32 * 2 {
+            return Err(ContractError::HexFmtErr)
+        }
+
+        let mut bytes = [0u8; 32];
+        for i in 0..32 {
+            // Bytes are little endian but str repr is big endian
+            bytes[32 - i - 1] = if let Ok(byte) = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16) {
+                byte
+            } else {
+                return Err(ContractError::HexFmtErr)
+            };
+        }
+
+        let value = Self::from_repr(bytes);
+        if value.is_some().into() {
+            Ok(value.unwrap())
+        } else {
+            Err(ContractError::HexFmtErr)
+        }
+    }
+}
+
+impl FieldElemAsStr for pallas::Base {}
+impl FieldElemAsStr for pallas::Scalar {}
+
 #[test]
 fn test_fp_to_u64() {
     use super::pasta_prelude::Field;
@@ -93,3 +132,15 @@ fn test_fp_to_u64() {
     assert_eq!(fp_to_u64(fp), Some(u64::MAX));
     assert_eq!(fp_to_u64(fp + pallas::Base::ONE), None);
 }
+
+#[test]
+fn test_fp_to_str() {
+    use self::FieldElemAsStr;
+    let fpstr = "227ae0da79929f3e23f8d5bc9992f5f140f5198932378731e1b49b67fdc296c8";
+    assert_eq!(pallas::Base::from_str(fpstr).unwrap().to_str(), fpstr);
+
+    let fpstr = "000000000000000000000000000000000000000000000000ffffffffffffffff";
+    let fp = pallas::Base::from(u64::MAX);
+    assert_eq!(fp.to_str(), fpstr);
+    assert_eq!(pallas::Base::from_str(fpstr).unwrap(), fp);
+}

+ 6 - 0
src/sdk/src/error.rs

@@ -89,6 +89,9 @@ pub enum ContractError {
     // size and present this error.
     #[error("Data too large")]
     DataTooLarge,
+
+    #[error("Hex string is not properly formatted")]
+    HexFmtErr,
 }
 
 /// Builtin return values occupy the upper 32 bits
@@ -118,6 +121,7 @@ pub const DB_DEL_FAILED: i64 = to_builtin!(17);
 pub const SMT_PUT_FAILED: i64 = to_builtin!(18);
 pub const GET_SYSTEM_TIME_FAILED: i64 = to_builtin!(19);
 pub const DATA_TOO_LARGE: i64 = to_builtin!(20);
+pub const HEX_FMT_ERR: i64 = to_builtin!(21);
 
 impl From<ContractError> for i64 {
     fn from(err: ContractError) -> Self {
@@ -141,6 +145,7 @@ impl From<ContractError> for i64 {
             ContractError::SmtPutFailed => SMT_PUT_FAILED,
             ContractError::GetSystemTimeFailed => GET_SYSTEM_TIME_FAILED,
             ContractError::DataTooLarge => DATA_TOO_LARGE,
+            ContractError::HexFmtErr => HEX_FMT_ERR,
             ContractError::Custom(error) => {
                 if error == 0 {
                     CUSTOM_ZERO
@@ -175,6 +180,7 @@ impl From<i64> for ContractError {
             SMT_PUT_FAILED => Self::SmtPutFailed,
             GET_SYSTEM_TIME_FAILED => Self::GetSystemTimeFailed,
             DATA_TOO_LARGE => Self::DataTooLarge,
+            HEX_FMT_ERR => Self::HexFmtErr,
             _ => Self::Custom(error as u32),
         }
     }