zescrow_core/
condition.rs1use bincode::{Decode, Encode};
12#[cfg(feature = "json")]
13use serde::{Deserialize, Serialize};
14
15use crate::error::ConditionError;
16use crate::Result;
17
18pub mod ed25519;
20pub mod hashlock;
22pub mod secp256k1;
24pub mod threshold;
26
27use ed25519::Ed25519;
28use hashlock::Hashlock;
29use secp256k1::Secp256k1;
30use threshold::Threshold;
31
32#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
34#[cfg_attr(
35 feature = "json",
36 serde(tag = "condition", content = "fulfillment", rename_all = "lowercase")
37)]
38#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
39pub enum Condition {
40 Hashlock(Hashlock),
42 Ed25519(Ed25519),
44 Secp256k1(Secp256k1),
46 Threshold(Threshold),
48}
49
50impl Condition {
51 #[inline]
61 pub fn verify(&self) -> Result<()> {
62 match self {
63 Self::Hashlock(hashlock) => hashlock.verify().map_err(ConditionError::Hashlock)?,
64 Self::Ed25519(ed25519) => ed25519.verify().map_err(ConditionError::Ed25519)?,
65 Self::Secp256k1(secp256k1) => secp256k1.verify().map_err(ConditionError::Secp256k1)?,
66 Self::Threshold(threshold) => threshold.verify().map_err(ConditionError::Threshold)?,
67 }
68 Ok(())
69 }
70
71 pub fn hashlock(hash: [u8; 32], preimage: Vec<u8>) -> Self {
73 Self::Hashlock(Hashlock { hash, preimage })
74 }
75
76 pub fn ed25519(public_key: [u8; 32], message: Vec<u8>, signature: Vec<u8>) -> Self {
78 Self::Ed25519(Ed25519 {
79 public_key,
80 signature,
81 message,
82 })
83 }
84
85 pub fn secp256k1(public_key: Vec<u8>, message: Vec<u8>, signature: Vec<u8>) -> Self {
87 Self::Secp256k1(Secp256k1 {
88 public_key,
89 signature,
90 message,
91 })
92 }
93
94 pub fn threshold(threshold: usize, subconditions: Vec<Self>) -> Self {
96 Self::Threshold(Threshold {
97 threshold,
98 subconditions,
99 })
100 }
101}
102
103#[cfg(feature = "json")]
104impl std::fmt::Display for Condition {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 let json = serde_json::to_string(self).map_err(|_| std::fmt::Error)?;
108 write!(f, "{json}")
109 }
110}
111
112#[cfg(test)]
113mod tests {
114
115 use sha2::{Digest, Sha256};
116
117 use super::*;
118
119 #[test]
120 fn preimage() {
121 let preimage = b"secret".to_vec();
122 let hash = Sha256::digest(&preimage).into();
123 let cond = Condition::hashlock(hash, preimage);
124 assert!(cond.verify().is_ok());
125
126 let cond = Condition::hashlock(hash, b"wrong-secret".to_vec());
128 assert!(cond.verify().is_err());
129 }
130
131 #[test]
132 fn ed25519() {
133 use ed25519_dalek::ed25519::signature::rand_core::OsRng;
134 use ed25519_dalek::{Signer, SigningKey};
135
136 let mut csprng = OsRng;
137 let sk: SigningKey = SigningKey::generate(&mut csprng);
138
139 let message = b"zkEscrow".to_vec();
140 let signature = sk.sign(&message).to_bytes().to_vec();
141 let public_key = sk.verifying_key().to_bytes();
142 let cond = Condition::ed25519(public_key, message.clone(), signature.clone());
143 assert!(cond.verify().is_ok());
144
145 let mut signature = signature;
147 signature[0] ^= 0xFF;
148 let cond = Condition::ed25519(public_key, message, signature);
149 assert!(cond.verify().is_err());
150 }
151
152 #[test]
153 fn secp256k1() {
154 use k256::ecdsa::signature::Signer;
155 use k256::ecdsa::{Signature, SigningKey};
156 use k256::elliptic_curve::rand_core::OsRng;
157
158 let sk = SigningKey::random(&mut OsRng);
159 let vk = sk.verifying_key();
160 let message = b"zkEscrow".to_vec();
161 let signature: Signature = sk.sign(&message);
162
163 let sig_bytes = signature.to_der().as_bytes().to_vec();
164 let pk_bytes = vk.to_encoded_point(false).as_bytes().to_vec();
165
166 let cond = Condition::secp256k1(pk_bytes.clone(), message, sig_bytes.clone());
167 assert!(cond.verify().is_ok());
168
169 let cond = Condition::secp256k1(pk_bytes, b"tampered".to_vec(), sig_bytes);
171 assert!(cond.verify().is_err());
172 }
173
174 #[test]
175 fn nonzero_threshold() {
176 let hash = Sha256::digest(b"zkEscrow").into();
178 let correct = Condition::hashlock(hash, b"zkEscrow".to_vec());
179 let wrong = Condition::hashlock(hash, b"wrong-preimage".to_vec());
180
181 let cond = Condition::threshold(1, vec![correct.clone(), wrong.clone()]);
183 assert!(cond.verify().is_ok());
184
185 let cond = Condition::threshold(2, vec![correct, wrong]);
187 assert!(cond.verify().is_err());
188
189 let cond = Condition::threshold(1, vec![]);
191 assert!(cond.verify().is_err());
192 }
193
194 #[test]
195 fn zero_threshold() {
196 let cond = Condition::threshold(0, vec![]);
198 assert!(cond.verify().is_ok());
199
200 let preimage = b"zkEscrow".to_vec();
202 let hash = Sha256::digest(&preimage).into();
203 let subcond = Condition::hashlock(hash, preimage);
204 let cond = Condition::threshold(0, vec![subcond]);
205 assert!(cond.verify().is_ok());
206 }
207
208 #[test]
209 fn nested_thresholds() {
210 let preimage = b"zkEscrow".to_vec();
211 let hash = Sha256::digest(&preimage).into();
212 let leaf = Condition::hashlock(hash, preimage);
213
214 let inner = Condition::threshold(1, vec![leaf.clone()]);
216 let outer = Condition::threshold(1, vec![inner]);
218 assert!(outer.verify().is_ok());
219
220 let wrong_leaf = Condition::hashlock(hash, b"wrong-preimage".to_vec());
222 let inner2 = Condition::threshold(1, vec![wrong_leaf]);
223 let outer2 = Condition::threshold(1, vec![inner2]);
224 assert!(outer2.verify().is_err());
225 }
226
227 #[cfg(feature = "json")]
228 #[test]
229 fn json_roundtrip_hashlock() {
230 let preimage = b"secret".to_vec();
231 let hash = Sha256::digest(&preimage).into();
232 let cond = Condition::hashlock(hash, preimage);
233 let json = serde_json::to_string(&cond).unwrap();
234 let decoded: Condition = serde_json::from_str(&json).unwrap();
235 assert_eq!(decoded, cond);
236 }
237
238 #[cfg(feature = "json")]
239 #[test]
240 fn json_roundtrip_threshold() {
241 let preimage = b"nested".to_vec();
242 let hash = Sha256::digest(&preimage).into();
243 let inner = Condition::hashlock(hash, preimage);
244 let cond = Condition::threshold(1, vec![inner]);
245 let json = serde_json::to_string(&cond).unwrap();
246 let decoded: Condition = serde_json::from_str(&json).unwrap();
247 assert_eq!(decoded, cond);
248 }
249}