darkfi_sdk/crypto/
util.rs1use darkfi_serial::ReadExt;
20use halo2_gadgets::poseidon::primitives as poseidon;
21use pasta_curves::{
22 group::ff::{FromUniformBytes, PrimeField},
23 pallas,
24};
25use std::io::Cursor;
26use subtle::CtOption;
27
28use crate::{
29 error::{ContractError, GenericResult},
30 hex::{decode_hex_arr, hex_from_iter},
31};
32
33#[inline]
34fn hash_to_field_elem<F: FromUniformBytes<64>>(persona: &[u8], vals: &[&[u8]]) -> F {
35 let mut hasher = blake2b_simd::Params::new().hash_length(64).personal(persona).to_state();
36
37 for v in vals {
38 hasher.update(v);
39 }
40
41 F::from_uniform_bytes(hasher.finalize().as_array())
42}
43
44pub fn hash_to_scalar(persona: &[u8], vals: &[&[u8]]) -> pallas::Scalar {
47 hash_to_field_elem(persona, vals)
48}
49
50pub fn hash_to_base(persona: &[u8], vals: &[&[u8]]) -> pallas::Base {
53 hash_to_field_elem(persona, vals)
54}
55
56pub fn fp_mod_fv(val: pallas::Base) -> pallas::Scalar {
61 pallas::Scalar::from_repr(val.to_repr()).unwrap()
62}
63
64pub fn fv_mod_fp_unsafe(val: pallas::Scalar) -> CtOption<pallas::Base> {
69 pallas::Base::from_repr(val.to_repr())
70}
71
72pub fn poseidon_hash<const N: usize>(messages: [pallas::Base; N]) -> pallas::Base {
74 poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<N>, 3, 2>::init()
80 .hash(messages)
81}
82
83pub fn fp_to_u64(value: pallas::Base) -> Option<u64> {
84 let repr = value.to_repr();
85 if !repr[8..].iter().all(|&b| b == 0u8) {
86 return None
87 }
88 let mut cur = Cursor::new(&repr[0..8]);
89 let uint = ReadExt::read_u64(&mut cur).ok()?;
90 Some(uint)
91}
92
93pub trait FieldElemAsStr: PrimeField<Repr = [u8; 32]> {
95 fn to_string(&self) -> String {
96 "0x".to_string() + &hex_from_iter(self.to_repr().iter().cloned().rev())
98 }
99
100 fn from_str(hex: &str) -> GenericResult<Self> {
101 if hex.len() != 33 * 2 {
102 return Err(ContractError::HexFmtErr)
103 }
104
105 let hex = hex.strip_prefix("0x").ok_or(ContractError::HexFmtErr)?;
106
107 let mut bytes = decode_hex_arr(hex)?;
108 bytes.reverse();
109
110 Option::from(Self::from_repr(bytes)).ok_or(ContractError::HexFmtErr)
111 }
112}
113
114impl FieldElemAsStr for pallas::Base {}
115impl FieldElemAsStr for pallas::Scalar {}
116
117#[test]
118fn test_fp_to_u64() {
119 use super::pasta_prelude::Field;
120
121 let fp = pallas::Base::from(u64::MAX);
122 assert_eq!(fp_to_u64(fp), Some(u64::MAX));
123 assert_eq!(fp_to_u64(fp + pallas::Base::ONE), None);
124}
125
126#[test]
127fn test_fp_to_str() {
128 use self::FieldElemAsStr;
129 let fpstr = "0x227ae0da79929f3e23f8d5bc9992f5f140f5198932378731e1b49b67fdc296c8";
130 assert_eq!(pallas::Base::from_str(fpstr).unwrap().to_string(), fpstr);
131
132 let fpstr = "0x000000000000000000000000000000000000000000000000ffffffffffffffff";
133 let fp = pallas::Base::from(u64::MAX);
134 assert_eq!(fp.to_string(), fpstr);
135 assert_eq!(pallas::Base::from_str(fpstr).unwrap(), fp);
136}