68 | where
69 | Self: Sized;
70 | fn to_base64(&self) -> (String, String);
71 | // Checks that the public key is included in the config
72 | fn confirm_in_config(&self, config: &Config) -> Result<(), Error>;
73 | }
74 |
75 | struct VerificationParameters {
76 | pub_key: P,
77 | signature: S,
78 | }
79 |
80 | type Ecdsa = VerificationParameters;
81 | type Sr25519 = VerificationParameters;
82 | type Ed25519 = VerificationParameters;
83 |
84 | impl DeviceKey for Ecdsa {
85 | type PublicKey = EcdsaPublicKey;
86 | type Signature = EcdsaSignature;
87 |
88 | fn verify_signature(&self, message: &[u8], _context: &[u8]) -> Result<(), Error> {
89 | self.pub_key.verify(message, &self.signature).map_err(|_| {
90 | Error::InvalidSignatureRequest("Unable to verify ecdsa signature".to_string())
91 | })
92 | }
93 |
94 | fn from_base64(pub_key_encoded: &[u8], signature_encoded: &[u8]) -> Result {
95 | let pub_key = Ecdsa::pub_key_from_base64(pub_key_encoded)?;
96 | let signature = EcdsaSignature::from_slice(
97 | BASE64_STANDARD
98 | .decode(signature_encoded)
99 | .map_err(|_| Error::InvalidSignatureRequest("ecdsa from_base64 error".to_string()))?
100 | .as_slice(),
101 | )
102 | .map_err(|_| Error::InvalidSignatureRequest("Invalid ecdsa signature".to_string()))?;
103 | Ok(Ecdsa { pub_key, signature })
104 | }
105 |
106 | fn pub_key_from_base64(pub_key_encoded: &[u8]) -> Result {
107 | let pub_key = EcdsaPublicKey::from_sec1_bytes(
108 | BASE64_STANDARD
109 | .decode(pub_key_encoded)
110 | .map_err(|_| {
111 | Error::InvalidSignatureRequest("ecdsa pub_key_from_base64 error".to_string())
112 | })?
113 | .as_slice(),
114 | )
115 | .map_err(|_| Error::InvalidSignatureRequest("Invalid ecdsa public key".to_string()))?;
116 | Ok(pub_key)
117 | }
118 |
119 | fn to_base64(&self) -> (String, String) {
120 | let pub_key_encoded = BASE64_STANDARD.encode(self.pub_key.to_encoded_point(true));
121 | let signature_encoded = BASE64_STANDARD.encode(self.signature.to_bytes());
122 | (pub_key_encoded, signature_encoded)
123 | }
124 |
125 | fn confirm_in_config(&self, config: &Config) -> Result<(), Error> {
126 | if !config.ecdsa_public_keys.contains(&self.pub_key) {
127 | return Err(Error::InvalidSignatureRequest(
128 | "ECDSA Public key not in config".to_string(),
129 | ));
130 | }
131 | Ok(())
132 | }
133 | }
134 |
135 | impl DeviceKey for Ed25519 {
136 | type PublicKey = Ed25519PublicKey;
137 | type Signature = Ed25519Signature;
138 |
139 | fn verify_signature(&self, message: &[u8], _context: &[u8]) -> Result<(), Error> {
140 | self.pub_key.verify(message, &self.signature).map_err(|_| {
141 | Error::InvalidSignatureRequest("Unable to verify ed25519 signature".to_string())
142 | })
143 | }
144 |
145 | fn pub_key_from_base64(public_key: &[u8]) -> Result
146 | where
147 | Self: Sized,
148 | {
149 | let pub_key = Ed25519PublicKey::try_from(
150 | BASE64_STANDARD
151 | .decode(public_key)
152 | .map_err(|_| {
153 | Error::InvalidSignatureRequest("ed25519 pub_key_from_base64 error".to_string())
154 | })?
155 | .as_slice(),
156 | )
157 | .map_err(|_| Error::InvalidSignatureRequest("Invalid ed25519 public key".to_string()))?;
158 | Ok(pub_key)
159 | }
160 |
161 | fn from_base64(pub_key_encoded: &[u8], signature_encoded: &[u8]) -> Result {
162 | let pub_key = Ed25519::pub_key_from_base64(pub_key_encoded)?;
163 | let signature = Ed25519Signature::try_from(
164 | BASE64_STANDARD
165 | .decode(signature_encoded)
166 | .map_err(|_| {
167 | Error::InvalidSignatureRequest("ed25519 from_base64 error".to_string())
168 | })?
169 | .as_slice(),
170 | )
171 | .unwrap();
172 | Ok(Ed25519 { pub_key, signature })
173 | }
174 |
175 | fn to_base64(&self) -> (String, String) {
176 | let pub_key_encoded = BASE64_STANDARD.encode(self.pub_key.to_bytes());
177 | let signature_encoded = BASE64_STANDARD.encode(self.signature.to_bytes());
178 | (pub_key_encoded, signature_encoded)
179 | }
180 |
181 | fn confirm_in_config(&self, config: &Config) -> Result<(), Error> {
182 | if !config.ed25519_public_keys.contains(&self.pub_key) {
183 | return Err(Error::InvalidSignatureRequest(
184 | "Ed25519 Public key not in config".to_string(),
185 | ));
186 | }
187 | Ok(())
188 | }
189 | }
190 |
191 | impl DeviceKey for Sr25519 {
192 | type PublicKey = Sr25519PublicKey;
193 | type Signature = Sr25519Signature;
194 |
195 | fn verify_signature(&self, message: &[u8], context: &[u8]) -> Result<(), Error> {
196 | let context = signing_context(context);
197 | self.pub_key
198 | .verify(context.bytes(message), &self.signature)
199 | .map_err(|_| {
200 | Error::InvalidSignatureRequest("Unable to verify sr25519 signature".to_string())
201 | })
202 | }
203 |
204 | fn from_base64(pub_key_encoded: &[u8], signature_encoded: &[u8]) -> Result {
205 | let pub_key = Sr25519::pub_key_from_base64(pub_key_encoded)?;
206 | let signature = Sr25519Signature::from_bytes(
207 | BASE64_STANDARD
208 | .decode(signature_encoded)
209 | .map_err(|_| {
210 | Error::InvalidSignatureRequest("sr25519 from_base64 error".to_string())
211 | })?
212 | .as_slice(),
213 | )
214 | .map_err(|_| Error::InvalidSignatureRequest("Invalid sr25519 signature".to_string()))?;
215 | Ok(Sr25519 { pub_key, signature })
216 | }
217 |
218 | fn pub_key_from_base64(pub_key_encoded: &[u8]) -> Result {
219 | let pub_key = Sr25519PublicKey::from_bytes(
220 | BASE64_STANDARD
221 | .decode(pub_key_encoded)
222 | .map_err(|_| {
223 | Error::InvalidSignatureRequest("sr25519 pub_key_from_base64 error".to_string())
224 | })?
225 | .as_slice(),
226 | )
227 | .map_err(|_| Error::InvalidSignatureRequest("Invalid sr25519 public key".to_string()))?;
228 | Ok(pub_key)
229 | }
230 |
231 | fn to_base64(&self) -> (String, String) {
232 | let pub_key_encoded = BASE64_STANDARD.encode(self.pub_key);
233 | let signature_encoded = BASE64_STANDARD.encode(self.signature.to_bytes());
234 | (pub_key_encoded, signature_encoded)
235 | }
236 |
237 | fn confirm_in_config(&self, config: &Config) -> Result<(), Error> {
238 | if !config.sr25519_public_keys.contains(&self.pub_key) {
239 | return Err(Error::InvalidSignatureRequest(
240 | "Sr25519 Public key not in config".to_string(),
241 | ));
242 | }
243 | Ok(())
244 | }
245 | }
246 |
247 | pub struct DeviceKeyProxy;
248 |
249 | impl Program for DeviceKeyProxy {
250 | fn evaluate(
251 | signature_request: SignatureRequest,
252 | raw_config: Option>,
253 | _oracle_data: Option>>,
254 | ) -> Result<(), Error> {
255 | let config_json = serde_json::from_slice::(
256 | raw_config
257 | .ok_or(Error::Evaluation("No config provided.".to_string()))?
258 | .as_slice(),
259 | )
260 | .map_err(|e| Error::Evaluation(format!("Failed to parse config: {}", e)))?;
261 | let aux_data_json = serde_json::from_slice::(
262 | signature_request
263 | .auxilary_data
264 | .ok_or(Error::InvalidSignatureRequest(
265 | "No auxilary_data provided".to_string(),
266 | ))?
267 | .as_slice(),
268 | )
269 | .map_err(|e| {
270 | Error::InvalidSignatureRequest(format!("Failed to parse auxilary_data: {}", e))
271 | })?;
272 |
273 | let config = Config::try_from(config_json)?;
274 |
275 | // assert that the key in the aux data is in the config, and verify signature
276 | match aux_data_json.public_key_type.as_str() {
277 | "ecdsa" => {
278 | let verification_parameters = Ecdsa::from_base64(
279 | aux_data_json.public_key.as_bytes(),
280 | aux_data_json.signature.as_bytes(),
281 | )?;
282 | verification_parameters.confirm_in_config(&config)?;
283 | verification_parameters
284 | .verify_signature(signature_request.message.as_slice(), b"")?;
285 | }
286 | "sr25519" => {
287 | let verification_parameters = Sr25519::from_base64(
288 | aux_data_json.public_key.as_bytes(),
289 | aux_data_json.signature.as_bytes(),
290 | )?;
291 | verification_parameters.confirm_in_config(&config)?;
292 | verification_parameters.verify_signature(
293 | signature_request.message.as_slice(),
294 | aux_data_json.context.as_bytes(),
295 | )?;
296 | }
297 | "ed25519" => {
298 | let verification_parameters = Ed25519::from_base64(
299 | aux_data_json.public_key.as_bytes(),
300 | aux_data_json.signature.as_bytes(),
301 | )?;
302 | verification_parameters.confirm_in_config(&config)?;
303 | verification_parameters
304 | .verify_signature(signature_request.message.as_slice(), b"")?;
305 | }
306 | _ => {
307 | return Err(Error::InvalidSignatureRequest(
308 | "Invalid public key type".to_string(),
309 | ))
310 | }
311 | }
312 |
313 | Ok(())
314 | }
315 |
316 | fn custom_hash(_data: Vec) -> Option> {
317 | None
318 | }
319 | }
320 |
321 | impl TryFrom for Config {
322 | type Error = Error;
323 |
324 | fn try_from(config_json: UserConfig) -> Result {
325 | let mut config = Config::default();
326 |
327 | if let Some(ecdsa_pub_keys) = config_json.ecdsa_public_keys {
328 | for encoded_key in ecdsa_pub_keys {
329 | config.ecdsa_public_keys.push(
330 | Ecdsa::pub_key_from_base64(encoded_key.as_bytes()).map_err(|_| {
331 | Error::InvalidSignatureRequest("config conversion ecdsa".to_string())
332 | })?,
333 | );
334 | }
335 | }
336 |
337 | if let Some(sr25519_pub_keys) = config_json.sr25519_public_keys {
338 | for encoded_key in sr25519_pub_keys {
339 | let public_key =
340 | Sr25519::pub_key_from_base64(encoded_key.as_bytes()).map_err(|_| {
341 | Error::InvalidSignatureRequest("config conversion sr25519".to_string())
342 | })?;
343 | config.sr25519_public_keys.push(public_key);
344 | }
345 | }
346 |
347 | if let Some(ed25519_pub_keys) = config_json.ed25519_public_keys {
348 | for encoded_key in ed25519_pub_keys {
349 | let public_key =
350 | Ed25519::pub_key_from_base64(encoded_key.as_bytes()).map_err(|_| {
351 | Error::InvalidSignatureRequest("config conversion ed25519".to_string())
352 | })?;
353 | config.ed25519_public_keys.push(public_key);
354 | }
355 | }
356 |
357 | Ok(config)
358 | }
359 | }
360 |
361 | impl From for UserConfig {
362 | fn from(config: Config) -> UserConfig {
363 | let ecdsa_public_keys = config
364 | .ecdsa_public_keys
365 | .iter()
366 | .map(|key| {
367 | let encoded_key = BASE64_STANDARD.encode(key.to_encoded_point(true).as_bytes());
368 | encoded_key
369 | })
370 | .collect();
371 | let sr25519_public_keys = config
372 | .sr25519_public_keys
373 | .iter()
374 | .map(|key| {
375 | let encoded_key = BASE64_STANDARD.encode(key);
376 | encoded_key
377 | })
378 | .collect();
379 | let ed25519_public_keys = config
380 | .ed25519_public_keys
381 | .iter()
382 | .map(|key| {
383 | let encoded_key = BASE64_STANDARD.encode(key.as_bytes());
384 | encoded_key
385 | })
386 | .collect();
387 |
388 | UserConfig {
389 | ecdsa_public_keys: Some(ecdsa_public_keys),
390 | sr25519_public_keys: Some(sr25519_public_keys),
391 | ed25519_public_keys: Some(ed25519_public_keys),
392 | }
393 | }
394 | }
395 |
396 | export_program!(DeviceKeyProxy);
397 |
--------------------------------------------------------------------------------
/examples/device-key-proxy/src/tests.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | use ed25519_dalek::{Signature as Ed25519Signature, SigningKey as Ed25519Keypair};
4 | use k256::ecdsa::{signature::Signer, Signature as EcdsaSignature, SigningKey as EcdsaKeypair};
5 | use rand_core::OsRng;
6 | use schnorrkel::{signing_context, Keypair as Sr25519Keypair, Signature as Sr25519Signature};
7 |
8 | struct TestKeys {
9 | ecdsa_keys: Vec,
10 | sr25519_keys: Vec,
11 | ed25519_keys: Vec,
12 | }
13 |
14 | #[test]
15 | fn test_ok_for_only_device_key_signatures() {
16 | let device_keys = generate_test_keys();
17 |
18 | let config = Config {
19 | ecdsa_public_keys: device_keys
20 | .ecdsa_keys
21 | .iter()
22 | .map(|key| EcdsaPublicKey::from(key))
23 | .collect(),
24 | sr25519_public_keys: device_keys
25 | .sr25519_keys
26 | .iter()
27 | .map(|key| key.public)
28 | .collect(),
29 | ed25519_public_keys: device_keys
30 | .ed25519_keys
31 | .iter()
32 | .map(|key| key.verifying_key())
33 | .collect(),
34 | };
35 | let json_config = UserConfig::from(config.clone());
36 |
37 | let message: &str = "this is some message that we want to sign if its from a valid device key";
38 |
39 | // constrtuct signature request from device key (for positive test)
40 | let ecdsa_device_key_signature: EcdsaSignature = device_keys.ecdsa_keys[0]
41 | .try_sign(message.as_bytes())
42 | .unwrap();
43 | let device_key_aux_data_json_edcsa = AuxData {
44 | public_key_type: "ecdsa".to_string(),
45 | public_key: BASE64_STANDARD.encode(
46 | device_keys.ecdsa_keys[0]
47 | .verifying_key()
48 | .to_encoded_point(true)
49 | .as_bytes(),
50 | ),
51 | signature: BASE64_STANDARD.encode(ecdsa_device_key_signature.to_bytes()),
52 | context: "".to_string(),
53 | };
54 | let mut request_from_device_key = SignatureRequest {
55 | message: message.to_string().into_bytes(),
56 | auxilary_data: Some(
57 | serde_json::to_string(&device_key_aux_data_json_edcsa)
58 | .unwrap()
59 | .into_bytes(),
60 | ),
61 | };
62 |
63 | let config_bytes = serde_json::to_vec(&json_config).unwrap();
64 | // positive for edcsa
65 | assert!(DeviceKeyProxy::evaluate(
66 | request_from_device_key.clone(),
67 | Some(config_bytes.clone()),
68 | None
69 | )
70 | .is_ok());
71 | // positive for sr25519
72 | let context = signing_context(b"");
73 |
74 | let sr25519_device_key_signature: Sr25519Signature =
75 | device_keys.sr25519_keys[0].sign(context.bytes(message.as_bytes()));
76 |
77 | let device_key_aux_data_json_sr25519 = AuxData {
78 | public_key_type: "sr25519".to_string(),
79 | public_key: BASE64_STANDARD.encode(device_keys.sr25519_keys[0].public),
80 | signature: BASE64_STANDARD.encode(sr25519_device_key_signature.to_bytes()),
81 | context: "".to_string(),
82 | };
83 | request_from_device_key.auxilary_data = Some(
84 | serde_json::to_string(&device_key_aux_data_json_sr25519.clone())
85 | .unwrap()
86 | .into_bytes(),
87 | );
88 | assert!(DeviceKeyProxy::evaluate(
89 | request_from_device_key.clone(),
90 | Some(config_bytes.clone()),
91 | None
92 | )
93 | .is_ok());
94 | // positive for ed25519
95 | let ed25519_device_key_signature: Ed25519Signature =
96 | device_keys.ed25519_keys[0].sign(message.as_bytes());
97 | let device_key_aux_data_json_ed25519 = AuxData {
98 | public_key_type: "ed25519".to_string(),
99 | public_key: BASE64_STANDARD.encode(device_keys.ed25519_keys[0].verifying_key()),
100 | signature: BASE64_STANDARD.encode(ed25519_device_key_signature.to_bytes()),
101 | context: "".to_string(),
102 | };
103 | request_from_device_key.auxilary_data = Some(
104 | serde_json::to_string(&device_key_aux_data_json_ed25519)
105 | .unwrap()
106 | .into_bytes(),
107 | );
108 | DeviceKeyProxy::evaluate(request_from_device_key, Some(config_bytes.clone()), None).unwrap();
109 | }
110 |
111 | #[test]
112 | fn test_fail_bad_signatures() {
113 | let device_keys = generate_test_keys();
114 | let non_device_keys = generate_test_keys();
115 |
116 | let config = Config {
117 | ecdsa_public_keys: device_keys
118 | .ecdsa_keys
119 | .iter()
120 | .map(|key| EcdsaPublicKey::from(key))
121 | .collect(),
122 | sr25519_public_keys: device_keys
123 | .sr25519_keys
124 | .iter()
125 | .map(|key| key.public)
126 | .collect(),
127 | ed25519_public_keys: device_keys
128 | .ed25519_keys
129 | .iter()
130 | .map(|key| key.verifying_key())
131 | .collect(),
132 | };
133 | let json_config = UserConfig::from(config.clone());
134 |
135 | let message: &str = "this is some message that we want to sign if its from a valid device key";
136 | let context = signing_context(b"");
137 |
138 | // constrtuct signature request from device key (for positive test)
139 | let ecdsa_non_device_key_signature: EcdsaSignature = non_device_keys.ecdsa_keys[0]
140 | .try_sign(message.as_bytes())
141 | .unwrap();
142 |
143 | let device_key_aux_data_json_edcsa = AuxData {
144 | public_key_type: "ecdsa".to_string(),
145 | public_key: BASE64_STANDARD.encode(
146 | device_keys.ecdsa_keys[0]
147 | .verifying_key()
148 | .to_encoded_point(true)
149 | .as_bytes(),
150 | ),
151 | signature: BASE64_STANDARD.encode(ecdsa_non_device_key_signature.to_bytes()),
152 | context: "".to_string(),
153 | };
154 | let mut request_from_device_key = SignatureRequest {
155 | message: message.to_string().into_bytes(),
156 | auxilary_data: Some(
157 | serde_json::to_string(&device_key_aux_data_json_edcsa)
158 | .unwrap()
159 | .into_bytes(),
160 | ),
161 | };
162 |
163 | let config_bytes = serde_json::to_vec(&json_config).unwrap();
164 | // fail for edcsa
165 | assert_eq!(
166 | DeviceKeyProxy::evaluate(
167 | request_from_device_key.clone(),
168 | Some(config_bytes.clone()),
169 | None
170 | )
171 | .unwrap_err()
172 | .to_string(),
173 | "Error::InvalidSignatureRequest(\"Unable to verify ecdsa signature\")"
174 | );
175 | let sr25519_non_device_key_signature: Sr25519Signature =
176 | non_device_keys.sr25519_keys[0].sign(context.bytes(message.as_bytes()));
177 | // fail for sr25519
178 | let device_key_aux_data_json_sr25519 = AuxData {
179 | public_key_type: "sr25519".to_string(),
180 | public_key: BASE64_STANDARD.encode(device_keys.sr25519_keys[0].public),
181 | signature: BASE64_STANDARD.encode(sr25519_non_device_key_signature.to_bytes()),
182 | context: "".to_string(),
183 | };
184 | request_from_device_key.auxilary_data = Some(
185 | serde_json::to_string(&device_key_aux_data_json_sr25519.clone())
186 | .unwrap()
187 | .into_bytes(),
188 | );
189 | assert_eq!(
190 | DeviceKeyProxy::evaluate(
191 | request_from_device_key.clone(),
192 | Some(config_bytes.clone()),
193 | None
194 | )
195 | .unwrap_err()
196 | .to_string(),
197 | "Error::InvalidSignatureRequest(\"Unable to verify sr25519 signature\")"
198 | );
199 | // fail for ed25519
200 | let ed25519_non_device_key_signature: Ed25519Signature =
201 | non_device_keys.ed25519_keys[0].sign(message.as_bytes());
202 | let device_key_aux_data_json_ed25519 = AuxData {
203 | public_key_type: "ed25519".to_string(),
204 | public_key: BASE64_STANDARD.encode(device_keys.ed25519_keys[0].verifying_key()),
205 | signature: BASE64_STANDARD.encode(ed25519_non_device_key_signature.to_bytes()),
206 | context: "".to_string(),
207 | };
208 | request_from_device_key.auxilary_data = Some(
209 | serde_json::to_string(&device_key_aux_data_json_ed25519)
210 | .unwrap()
211 | .into_bytes(),
212 | );
213 | assert_eq!(
214 | DeviceKeyProxy::evaluate(
215 | request_from_device_key.clone(),
216 | Some(config_bytes.clone()),
217 | None
218 | )
219 | .unwrap_err()
220 | .to_string(),
221 | "Error::InvalidSignatureRequest(\"Unable to verify ed25519 signature\")"
222 | );
223 | }
224 |
225 | #[test]
226 | fn test_fails_pub_key_not_found() {
227 | let device_keys = generate_test_keys();
228 | let non_device_keys = generate_test_keys();
229 |
230 | let config = Config {
231 | ecdsa_public_keys: device_keys
232 | .ecdsa_keys
233 | .iter()
234 | .map(|key| EcdsaPublicKey::from(key))
235 | .collect(),
236 | sr25519_public_keys: device_keys
237 | .sr25519_keys
238 | .iter()
239 | .map(|key| key.public)
240 | .collect(),
241 | ed25519_public_keys: device_keys
242 | .ed25519_keys
243 | .iter()
244 | .map(|key| key.verifying_key())
245 | .collect(),
246 | };
247 | let json_config = UserConfig::from(config.clone());
248 | let config_bytes = serde_json::to_vec(&json_config).unwrap();
249 |
250 | let message: &str = "this is some message that we want to sign if its from a valid device key";
251 | // construct signature request from non-device key (for negative test)
252 | let ecdsa_non_device_key_signature: EcdsaSignature = non_device_keys.ecdsa_keys[0]
253 | .try_sign(message.as_bytes())
254 | .unwrap();
255 | let non_device_key_aux_data_json = AuxData {
256 | public_key_type: "ecdsa".to_string(),
257 | public_key: BASE64_STANDARD.encode(
258 | non_device_keys.ecdsa_keys[0]
259 | .verifying_key()
260 | .to_encoded_point(true)
261 | .as_bytes(),
262 | ),
263 | signature: BASE64_STANDARD.encode(ecdsa_non_device_key_signature.to_bytes()),
264 | context: "".to_string(),
265 | };
266 | let mut request_from_non_device_key = SignatureRequest {
267 | message: message.to_string().into_bytes(),
268 | auxilary_data: Some(
269 | serde_json::to_string(&non_device_key_aux_data_json)
270 | .unwrap()
271 | .into_bytes(),
272 | ),
273 | };
274 | assert_eq!(
275 | DeviceKeyProxy::evaluate(
276 | request_from_non_device_key.clone(),
277 | Some(config_bytes.clone()),
278 | None
279 | )
280 | .unwrap_err()
281 | .to_string(),
282 | "Error::InvalidSignatureRequest(\"ECDSA Public key not in config\")"
283 | );
284 | //sr25519 fail
285 | let context = signing_context(b"");
286 |
287 | let sr25519_device_key_signature: Sr25519Signature =
288 | non_device_keys.sr25519_keys[0].sign(context.bytes(message.as_bytes()));
289 |
290 | let non_device_key_aux_data_json_sr25519 = AuxData {
291 | public_key_type: "sr25519".to_string(),
292 | public_key: BASE64_STANDARD.encode(non_device_keys.sr25519_keys[0].public),
293 | signature: BASE64_STANDARD.encode(sr25519_device_key_signature.to_bytes()),
294 | context: "".to_string(),
295 | };
296 | request_from_non_device_key.auxilary_data = Some(
297 | serde_json::to_string(&non_device_key_aux_data_json_sr25519.clone())
298 | .unwrap()
299 | .into_bytes(),
300 | );
301 | assert_eq!(
302 | DeviceKeyProxy::evaluate(
303 | request_from_non_device_key.clone(),
304 | Some(config_bytes.clone()),
305 | None
306 | )
307 | .unwrap_err()
308 | .to_string(),
309 | "Error::InvalidSignatureRequest(\"Sr25519 Public key not in config\")"
310 | );
311 |
312 | //ed25519 fail
313 | let ed25519_device_key_signature: Ed25519Signature =
314 | non_device_keys.ed25519_keys[0].sign(message.as_bytes());
315 | let device_key_aux_data_json_ed25519 = AuxData {
316 | public_key_type: "ed25519".to_string(),
317 | public_key: BASE64_STANDARD.encode(non_device_keys.ed25519_keys[0].verifying_key()),
318 | signature: BASE64_STANDARD.encode(ed25519_device_key_signature.to_bytes()),
319 | context: "".to_string(),
320 | };
321 | request_from_non_device_key.auxilary_data = Some(
322 | serde_json::to_string(&device_key_aux_data_json_ed25519)
323 | .unwrap()
324 | .into_bytes(),
325 | );
326 | assert_eq!(
327 | DeviceKeyProxy::evaluate(request_from_non_device_key, Some(config_bytes), None)
328 | .unwrap_err()
329 | .to_string(),
330 | "Error::InvalidSignatureRequest(\"Ed25519 Public key not in config\")"
331 | );
332 | }
333 | #[test]
334 | fn test_fails_with_no_aux_or_config() {
335 | let device_keys = generate_test_keys();
336 |
337 | let config = UserConfig {
338 | ecdsa_public_keys: Some(
339 | device_keys
340 | .ecdsa_keys
341 | .iter()
342 | .map(|key| {
343 | let public_key = EcdsaPublicKey::from(key);
344 | let encoded_key =
345 | BASE64_STANDARD.encode(public_key.to_encoded_point(true).as_bytes());
346 | encoded_key
347 | })
348 | .collect(),
349 | ),
350 | sr25519_public_keys: None,
351 | ed25519_public_keys: None,
352 | };
353 | let config_bytes = serde_json::to_vec(&config).unwrap();
354 |
355 | let message = "this is some message that we want to sign if its from a valid device key";
356 | let _device_key_signature: EcdsaSignature = device_keys.ecdsa_keys[0]
357 | .try_sign(message.as_bytes())
358 | .unwrap();
359 |
360 | let request_from_device_key_no_aux = SignatureRequest {
361 | message: message.to_string().into_bytes(),
362 | auxilary_data: None,
363 | };
364 |
365 | assert_eq!(
366 | DeviceKeyProxy::evaluate(
367 | request_from_device_key_no_aux.clone(),
368 | Some(config_bytes.clone()),
369 | None
370 | )
371 | .unwrap_err()
372 | .to_string(),
373 | "Error::InvalidSignatureRequest(\"No auxilary_data provided\")"
374 | );
375 |
376 | let ecdsa_device_key_signature: EcdsaSignature = device_keys.ecdsa_keys[0]
377 | .try_sign(message.as_bytes())
378 | .unwrap();
379 |
380 | let mut device_key_aux_data_json = AuxData {
381 | public_key_type: "ecdsa".to_string(),
382 | public_key: BASE64_STANDARD.encode(
383 | device_keys.ecdsa_keys[0]
384 | .verifying_key()
385 | .to_encoded_point(true)
386 | .as_bytes(),
387 | ),
388 | signature: BASE64_STANDARD.encode(ecdsa_device_key_signature.to_bytes()),
389 | context: "".to_string(),
390 | };
391 | let mut request_from_device_key = SignatureRequest {
392 | message: message.to_string().into_bytes(),
393 | auxilary_data: Some(
394 | serde_json::to_string(&device_key_aux_data_json)
395 | .unwrap()
396 | .into_bytes(),
397 | ),
398 | };
399 | assert_eq!(
400 | DeviceKeyProxy::evaluate(request_from_device_key.clone(), None, None)
401 | .unwrap_err()
402 | .to_string(),
403 | "Error::Evaluation(\"No config provided.\")"
404 | );
405 |
406 | device_key_aux_data_json.public_key_type = "phish".to_string();
407 | request_from_device_key.auxilary_data = Some(
408 | serde_json::to_string(&device_key_aux_data_json)
409 | .unwrap()
410 | .into_bytes(),
411 | );
412 | assert_eq!(
413 | DeviceKeyProxy::evaluate(request_from_device_key, Some(config_bytes.clone()), None)
414 | .unwrap_err()
415 | .to_string(),
416 | "Error::InvalidSignatureRequest(\"Invalid public key type\")"
417 | );
418 | }
419 |
420 | /// Generates keys that can be used for testing
421 | fn generate_test_keys() -> TestKeys {
422 | let ecdsa_keys: Vec = (0..3).map(|_| EcdsaKeypair::random(&mut OsRng)).collect();
423 | let sr25519_keys: Vec = (0..3)
424 | .map(|_| Sr25519Keypair::generate_with(&mut OsRng))
425 | .collect();
426 | let ed25519_keys: Vec = (0..3)
427 | .map(|_| Ed25519Keypair::generate(&mut OsRng))
428 | .collect();
429 |
430 | TestKeys {
431 | ecdsa_keys,
432 | sr25519_keys,
433 | ed25519_keys,
434 | }
435 | }
436 |
--------------------------------------------------------------------------------
/examples/infinite-loop/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "infinite-loop"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Unlicense"
7 | repository = "https://github.com/entropyxyz/constraints"
8 | edition = "2021"
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | # This is required to compile programs to a wasm module and for use in rust libs
13 | [lib]
14 | crate-type = ["cdylib", "rlib"]
15 |
16 | [dependencies]
17 | entropy-programs-core = { workspace = true }
18 | schemars = {version = "0.8.16", optional = true}
19 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
20 |
21 | # These are used by `cargo component`
22 | [package.metadata.component]
23 | package = "entropy:infinite-loop"
24 |
25 | [package.metadata.component.target]
26 | path = "../../wit"
27 |
28 | [package.metadata.component.dependencies]
29 |
30 | [features]
31 | std = ["schemars"]
--------------------------------------------------------------------------------
/examples/infinite-loop/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 |
3 | extern crate alloc;
4 | use alloc::vec::Vec;
5 | use entropy_programs_core::{bindgen::Error, bindgen::*, export_program, prelude::*};
6 |
7 | use serde::{Deserialize, Serialize};
8 |
9 | /// JSON-deserializable struct that will be used to derive the program-JSON interface.
10 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
11 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
12 | pub struct UserConfig {}
13 |
14 | /// JSON representation of the auxiliary data
15 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
16 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
17 | pub struct AuxData {}
18 |
19 | // TODO confirm this isn't an issue for audit
20 | register_custom_getrandom!(always_fail);
21 |
22 | pub struct InfiniteLoop;
23 |
24 | impl Program for InfiniteLoop {
25 | /// This is the only function required by the program runtime. `message` is the preimage of the curve element to be
26 | /// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
27 | fn evaluate(
28 | _signature_request: SignatureRequest,
29 | _config: Option>,
30 | _oracle_data: Option>>,
31 | ) -> Result<(), Error> {
32 | loop {}
33 | #[allow(unreachable_code)]
34 | Ok(())
35 | }
36 |
37 | /// Since we don't use a custom hash function, we can just return `None` here.
38 | fn custom_hash(_data: Vec) -> Option> {
39 | None
40 | }
41 | }
42 |
43 | export_program!(InfiniteLoop);
44 |
--------------------------------------------------------------------------------
/examples/oracle-example/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "oracle-example"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Unlicense"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 |
11 | # This is required to compile programs to a wasm module and for use in rust libs
12 | [lib]
13 | crate-type = ["cdylib", "rlib"]
14 |
15 | [dependencies]
16 | entropy-programs-core = { workspace = true }
17 | schemars = {version = "0.8.16", optional = true}
18 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
19 | codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false }
20 | # These are used by `cargo component`
21 | [package.metadata.component]
22 | package = "entropy:oracle-example"
23 |
24 | [package.metadata.component.target]
25 | path = "../../wit"
26 |
27 | [package.metadata.component.dependencies]
28 |
29 |
30 | [features]
31 | std = ["schemars"]
--------------------------------------------------------------------------------
/examples/oracle-example/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! This example shows how to write a contrieved and basic program: checking the length of the data to be signed.
2 |
3 | #![cfg_attr(not(feature = "std"), no_std)]
4 |
5 | extern crate alloc;
6 |
7 | use alloc::{
8 | string::{String, ToString},
9 | vec,
10 | vec::Vec,
11 | };
12 | use codec::{Decode, Encode};
13 | use entropy_programs_core::{bindgen::Error, bindgen::*, export_program, prelude::*};
14 | use serde::{Deserialize, Serialize};
15 | /// JSON-deserializable struct that will be used to derive the program-JSON interface.
16 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
17 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
18 | pub struct UserConfig {}
19 |
20 | /// JSON representation of the auxiliary data
21 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
22 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
23 | pub struct AuxData {}
24 |
25 | // Oracle data is heading block_number_entropy as I expect that to be passed below to the evaluate function.
26 | pub const ORACLE_DATA: [&str; 1] = ["block_number_entropy"];
27 |
28 | // TODO confirm this isn't an issue for audit
29 | register_custom_getrandom!(always_fail);
30 |
31 | pub struct OracleExample;
32 |
33 | impl Program for OracleExample {
34 | fn evaluate(
35 | _signature_request: SignatureRequest,
36 | _config: Option>,
37 | oracle_data: Option>>,
38 | ) -> Result<(), Error> {
39 | let data = oracle_data.ok_or(Error::Evaluation("No oracle data provided.".to_string()))?;
40 | let block_number = u32::decode(&mut data[0].as_ref())
41 | .map_err(|_| Error::Evaluation("Unable to decode oracle data".to_string()))?;
42 | // our program just checks that the block number is greater than 100
43 | if block_number > 100 {
44 | return Err(Error::Evaluation("Block Number too large".to_string()));
45 | }
46 |
47 | Ok(())
48 | }
49 |
50 | /// Since we don't use a custom hash function, we can just return `None` here.
51 | fn custom_hash(_data: Vec) -> Option> {
52 | None
53 | }
54 | }
55 |
56 | export_program!(OracleExample);
57 |
58 | // write a test that calls evaluate and passes it the proper parameters
59 | #[cfg(test)]
60 | mod tests {
61 | use super::*;
62 | use alloc::vec;
63 | use codec::Encode;
64 |
65 | #[test]
66 | fn test_should_sign() {
67 | let signature_request = SignatureRequest {
68 | message: "".to_string().into_bytes(),
69 | auxilary_data: None,
70 | };
71 |
72 | assert!(
73 | OracleExample::evaluate(signature_request, None, Some(vec![99u32.encode()])).is_ok()
74 | );
75 | }
76 |
77 | #[test]
78 | fn test_should_error() {
79 | let signature_request = SignatureRequest {
80 | message: "".to_string().into_bytes(),
81 | auxilary_data: None,
82 | };
83 |
84 | assert_eq!(
85 | OracleExample::evaluate(signature_request, None, Some(vec![101u32.encode()]))
86 | .unwrap_err()
87 | .to_string(),
88 | "Error::Evaluation(\"Block Number too large\")"
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/examples/private-acl/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "example-private-acl"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # This is required to compile programs to a wasm module and for use in rust libs
7 | [lib]
8 | crate-type = ["cdylib", "rlib"]
9 |
10 | [dependencies]
11 | blake2 = "0.10.6"
12 | entropy-programs = { workspace = true }
13 | schemars = {version = "0.8.16", optional = true}
14 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
15 |
16 | [build-dependencies]
17 | hex = "0.4.3"
18 | blake2 = "0.10.6"
19 |
20 | # These are used by `cargo component`
21 | [package.metadata.component]
22 | package = "entropy:example-private-acl"
23 |
24 | [package.metadata.component.target]
25 | path = "../../wit"
26 |
27 | [package.metadata.component.dependencies]
28 |
29 | [features]
30 | std = ["schemars"]
--------------------------------------------------------------------------------
/examples/private-acl/addresses.txt:
--------------------------------------------------------------------------------
1 |
2 | 772b9a9e8aa1c9db861c6611a82d251db4fac990
3 | 772b9a9e8aa1c9db861c6611a82d251db4fac9aa
4 |
--------------------------------------------------------------------------------
/examples/private-acl/build.rs:
--------------------------------------------------------------------------------
1 | //! This reads a text file of hex-encoded addresses, one per line,
2 | //! hashes them, and puts them in the constant ADDRESSES
3 | use blake2::{Blake2s256, Digest};
4 | use std::env;
5 | use std::fs::File;
6 | use std::io::BufWriter;
7 | use std::io::Write;
8 | use std::io::{BufRead, BufReader};
9 | use std::path::Path;
10 |
11 | fn main() {
12 | let out_dir = env::var_os("OUT_DIR").unwrap();
13 | let dest_path = Path::new(&out_dir).join("addresses.rs");
14 | let out_file = File::create(dest_path).unwrap();
15 | let mut writer = BufWriter::new(out_file);
16 |
17 | // First count the number of non-empty lines in the file
18 | let length = {
19 | let file = File::open(format!("addresses.txt")).unwrap();
20 | let reader = BufReader::new(file);
21 | let mut number_lines = 0;
22 | for line in reader.lines() {
23 | if !line.unwrap().is_empty() {
24 | number_lines += 1;
25 | }
26 | }
27 | number_lines
28 | };
29 |
30 | let file = File::open(format!("addresses.txt")).unwrap();
31 | let reader = BufReader::new(file);
32 | writer
33 | .write(format!("const ADDRESSES: [[u8; 32]; {}] = [", length).as_bytes())
34 | .unwrap();
35 |
36 | for line in reader.lines() {
37 | let line = line.unwrap();
38 | if line.is_empty() {
39 | continue;
40 | }
41 | let address: [u8; 20] = hex::decode(line).unwrap().try_into().unwrap();
42 |
43 | let hashed_address: [u8; 32] = {
44 | let mut hasher = Blake2s256::new();
45 | hasher.update(&address);
46 | hasher.finalize().into()
47 | };
48 |
49 | writer
50 | .write(format!(" {:?},", hashed_address).as_bytes())
51 | .unwrap();
52 | }
53 | writer.write("];".as_bytes()).unwrap();
54 |
55 | println!("cargo:rerun-if-changed=build.rs");
56 | println!("cargo:rerun-if-changed=addresses.txt");
57 | }
58 |
--------------------------------------------------------------------------------
/examples/private-acl/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! This is an allow list where the allowed addresses are hashed to improve privacy
2 | //! It is still possible for anyone to check whether a given address is in the list, using the
3 | //! on-chain bytecode. But you cannot just read the allowed addresses from it.
4 | #![no_std]
5 |
6 | extern crate alloc;
7 |
8 | use alloc::string::ToString;
9 | use alloc::vec::Vec;
10 | use blake2::{Blake2s256, Digest};
11 | use entropy_programs::{
12 | arch::evm::NameOrAddress,
13 | core::{bindgen::*, export_program, prelude::*, TryParse},
14 | programs::acl::*,
15 | };
16 | use serde::{Deserialize, Serialize};
17 |
18 | /// JSON-deserializable struct that will be used to derive the program-JSON interface.
19 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
20 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
21 | pub struct UserConfig {}
22 |
23 | /// JSON representation of the auxiliary data
24 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
25 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
26 | pub struct AuxData {}
27 |
28 | pub struct PrivateTransactionAcl;
29 |
30 | include!(concat!(env!("OUT_DIR"), "/addresses.rs"));
31 |
32 | // TODO confirm this isn't an issue for audit
33 | register_custom_getrandom!(always_fail);
34 |
35 | impl Program for PrivateTransactionAcl {
36 | /// Allow any address given in the pre-defined list (addresses.txt)
37 | // #[no_mangle]
38 | fn evaluate(
39 | signature_request: SignatureRequest,
40 | _config: Option>,
41 | _oracle_data: Option>>,
42 | ) -> Result<(), CoreError> {
43 | // parse the raw tx into some type
44 | let parsed_tx = ::TransactionRequest::try_parse(
45 | signature_request.message.as_slice(),
46 | )?;
47 |
48 | let name_or_address: NameOrAddress = parsed_tx.to.ok_or(Error::Evaluation(
49 | "No recipient given in transaction".to_string(),
50 | ))?;
51 |
52 | match name_or_address {
53 | NameOrAddress::Name(_) => Err(Error::Evaluation("ENS names not supported".to_string())),
54 | NameOrAddress::Address(address) => {
55 | let hashed_address = {
56 | let mut hasher = Blake2s256::new();
57 | hasher.update(&address.0);
58 | hasher.finalize().into()
59 | };
60 | if ADDRESSES.contains(&hashed_address) {
61 | Ok(())
62 | } else {
63 | Err(Error::Evaluation("Address not in allow list".to_string()))
64 | }
65 | }
66 | }
67 | }
68 |
69 | fn custom_hash(_data: Vec) -> Option> {
70 | None
71 | }
72 | }
73 |
74 | export_program!(PrivateTransactionAcl);
75 |
76 | // write a test that calls evaluate and passes it the proper parameters
77 | #[cfg(test)]
78 | mod tests {
79 | use super::*;
80 | use alloc::string::ToString;
81 |
82 | #[test]
83 | fn test_evaluate() {
84 | let signature_request = SignatureRequest {
85 | // `data` is an RLP serialized ETH transaction with the recipient set to `0x772b9a9e8aa1c9db861c6611a82d251db4fac990`
86 | message: "0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac990019243726561746564204f6e20456e74726f7079018080".to_string().into_bytes(),
87 | auxilary_data: None,
88 | };
89 |
90 | assert!(PrivateTransactionAcl::evaluate(signature_request, None, None).is_ok());
91 | }
92 |
93 | #[test]
94 | fn test_start_fail() {
95 | let signature_request = SignatureRequest {
96 | // `data` is the same as previous test, but recipient address ends in `1` instead of `0`, so it should fail
97 | message: "0xef01808094772b9a9e8aa1c9db861c6611a82d251db4fac991019243726561746564204f6e20456e74726f7079018080".to_string().into_bytes(),
98 | auxilary_data: None,
99 | };
100 |
101 | assert!(PrivateTransactionAcl::evaluate(signature_request, None, None).is_err());
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "example-risc0"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Apache-2.0"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | # This is required to compile programs to a wasm module and for use in rust libs
11 | [lib]
12 | crate-type = ["cdylib", "rlib"]
13 |
14 | [dependencies]
15 | entropy-programs-core = { workspace = true }
16 | serde = { version = "1.0", default-features = false, features = ["derive"] }
17 | bincode = "1.3.3"
18 | # json-example = { path = "json" }
19 | risc0-zkvm = { workspace = true }
20 | schemars = {version = "0.8.16", optional = true}
21 |
22 | [dev-dependencies]
23 | json-core = { path = "json/core" }
24 | json-methods = { path = "json/methods" }
25 | risc0-zkvm = { git = "https://github.com/risc0/risc0", tag = "v0.18.0", default-features = true }
26 |
27 | [features]
28 | std = ["risc0-zkvm/std", "schemars"]
29 |
30 | # These are used by `cargo component`
31 | [package.metadata.component]
32 | package = "entropy:example-risc0"
33 |
34 | [package.metadata.component.target]
35 | path = "../../wit"
36 |
37 | [package.metadata.component.dependencies]
38 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "json-example"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Apache-2.0"
7 | repository = "https://github.com/entropyxyz/constraints"
8 | edition = "2021"
9 |
10 | [dependencies]
11 | json-core = { path = "core" }
12 | json-methods = { path = "methods" }
13 | risc0-zkvm = { git = "https://github.com/risc0/risc0", tag = "v0.18.0", default-features = false }
14 | serde = "1.0"
15 |
16 | [features]
17 | cuda = ["risc0-zkvm/cuda"]
18 | default = []
19 | metal = ["risc0-zkvm/metal"]
20 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/README.md:
--------------------------------------------------------------------------------
1 | # JSON Example
2 |
3 | This code demonstrates how to prove that a JSON file contains a specific field and value using the RISC Zero zkVM. The JSON file is identified by SHA-256 hash, allowing users to commit to a specific JSON file and then prove some of its contents without revealing the full file.
4 |
5 | ## Quick Start
6 |
7 | First, [install Rust] if you don't already have it.
8 |
9 | Next, install the `cargo-risczero` tool and install the toolchain with:
10 | ```bash
11 | cargo install cargo-risczero
12 | cargo risczero install
13 | ```
14 |
15 | Then, run the example with:
16 | ```bash
17 | cargo run --release
18 | ```
19 |
20 | [install Rust]: https://doc.rust-lang.org/cargo/getting-started/installation.html
21 |
22 | ## Video Tutorial
23 |
24 | For a walk-through of this example, check out this [excerpt from our workshop at ZK HACK III](https://www.youtube.com/watch?v=6vIgBHx61vc&list=PLcPzhUaCxlCgig7ofeARMPwQ8vbuD6hC5&index=7).
25 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "json-core"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Apache-2.0"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | [dependencies]
11 | risc0-zkvm = { git = "https://github.com/risc0/risc0", tag = "v0.18.0", default-features = false }
12 | serde = "1.0"
13 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/core/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 RISC Zero, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use risc0_zkvm::sha::Digest;
16 | use serde::{Deserialize, Serialize};
17 |
18 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19 | pub struct Outputs {
20 | pub data: u32,
21 | pub hash: Digest,
22 | }
23 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "json-methods"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Apache-2.0"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | [build-dependencies]
11 | risc0-build = { workspace = true }
12 |
13 | [package.metadata.risc0]
14 | methods = ["guest"]
15 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/build.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 RISC Zero, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | fn main() {
16 | risc0_build::embed_methods();
17 | }
18 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/guest/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "anyhow"
7 | version = "1.0.69"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
10 |
11 | [[package]]
12 | name = "autocfg"
13 | version = "1.1.0"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
16 |
17 | [[package]]
18 | name = "bitflags"
19 | version = "1.3.2"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
22 |
23 | [[package]]
24 | name = "bitflags"
25 | version = "2.4.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
28 |
29 | [[package]]
30 | name = "blake2"
31 | version = "0.10.6"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
34 | dependencies = [
35 | "digest",
36 | ]
37 |
38 | [[package]]
39 | name = "block-buffer"
40 | version = "0.10.3"
41 | source = "registry+https://github.com/rust-lang/crates.io-index"
42 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
43 | dependencies = [
44 | "generic-array",
45 | ]
46 |
47 | [[package]]
48 | name = "bytemuck"
49 | version = "1.13.0"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
52 | dependencies = [
53 | "bytemuck_derive",
54 | ]
55 |
56 | [[package]]
57 | name = "bytemuck_derive"
58 | version = "1.4.0"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322"
61 | dependencies = [
62 | "proc-macro2",
63 | "quote",
64 | "syn 1.0.107",
65 | ]
66 |
67 | [[package]]
68 | name = "cfg-if"
69 | version = "1.0.0"
70 | source = "registry+https://github.com/rust-lang/crates.io-index"
71 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
72 |
73 | [[package]]
74 | name = "const-oid"
75 | version = "0.9.4"
76 | source = "registry+https://github.com/rust-lang/crates.io-index"
77 | checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
78 |
79 | [[package]]
80 | name = "cpufeatures"
81 | version = "0.2.5"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
84 | dependencies = [
85 | "libc",
86 | ]
87 |
88 | [[package]]
89 | name = "crypto-common"
90 | version = "0.1.6"
91 | source = "registry+https://github.com/rust-lang/crates.io-index"
92 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
93 | dependencies = [
94 | "generic-array",
95 | "typenum",
96 | ]
97 |
98 | [[package]]
99 | name = "digest"
100 | version = "0.10.6"
101 | source = "registry+https://github.com/rust-lang/crates.io-index"
102 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
103 | dependencies = [
104 | "block-buffer",
105 | "const-oid",
106 | "crypto-common",
107 | "subtle",
108 | ]
109 |
110 | [[package]]
111 | name = "elf"
112 | version = "0.7.2"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "e2b183d6ce6ca4cf30e3db37abf5b52568b5f9015c97d9fbdd7026aa5dcdd758"
115 |
116 | [[package]]
117 | name = "errno"
118 | version = "0.3.5"
119 | source = "registry+https://github.com/rust-lang/crates.io-index"
120 | checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
121 | dependencies = [
122 | "libc",
123 | "windows-sys",
124 | ]
125 |
126 | [[package]]
127 | name = "fastrand"
128 | version = "2.0.1"
129 | source = "registry+https://github.com/rust-lang/crates.io-index"
130 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
131 |
132 | [[package]]
133 | name = "generic-array"
134 | version = "0.14.6"
135 | source = "registry+https://github.com/rust-lang/crates.io-index"
136 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
137 | dependencies = [
138 | "typenum",
139 | "version_check",
140 | ]
141 |
142 | [[package]]
143 | name = "getrandom"
144 | version = "0.2.8"
145 | source = "registry+https://github.com/rust-lang/crates.io-index"
146 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
147 | dependencies = [
148 | "cfg-if",
149 | "libc",
150 | "wasi",
151 | ]
152 |
153 | [[package]]
154 | name = "hex"
155 | version = "0.4.3"
156 | source = "registry+https://github.com/rust-lang/crates.io-index"
157 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
158 |
159 | [[package]]
160 | name = "json"
161 | version = "0.12.4"
162 | source = "registry+https://github.com/rust-lang/crates.io-index"
163 | checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
164 |
165 | [[package]]
166 | name = "json-core"
167 | version = "0.1.0"
168 | dependencies = [
169 | "risc0-zkvm",
170 | "serde",
171 | ]
172 |
173 | [[package]]
174 | name = "libc"
175 | version = "0.2.147"
176 | source = "registry+https://github.com/rust-lang/crates.io-index"
177 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
178 |
179 | [[package]]
180 | name = "libm"
181 | version = "0.2.6"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
184 |
185 | [[package]]
186 | name = "linux-raw-sys"
187 | version = "0.4.10"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
190 |
191 | [[package]]
192 | name = "log"
193 | version = "0.4.17"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
196 | dependencies = [
197 | "cfg-if",
198 | ]
199 |
200 | [[package]]
201 | name = "num-derive"
202 | version = "0.4.0"
203 | source = "registry+https://github.com/rust-lang/crates.io-index"
204 | checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e"
205 | dependencies = [
206 | "proc-macro2",
207 | "quote",
208 | "syn 2.0.26",
209 | ]
210 |
211 | [[package]]
212 | name = "num-traits"
213 | version = "0.2.15"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
216 | dependencies = [
217 | "autocfg",
218 | ]
219 |
220 | [[package]]
221 | name = "paste"
222 | version = "1.0.11"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
225 |
226 | [[package]]
227 | name = "pin-project-lite"
228 | version = "0.2.9"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
231 |
232 | [[package]]
233 | name = "proc-macro2"
234 | version = "1.0.63"
235 | source = "registry+https://github.com/rust-lang/crates.io-index"
236 | checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
237 | dependencies = [
238 | "unicode-ident",
239 | ]
240 |
241 | [[package]]
242 | name = "quote"
243 | version = "1.0.29"
244 | source = "registry+https://github.com/rust-lang/crates.io-index"
245 | checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
246 | dependencies = [
247 | "proc-macro2",
248 | ]
249 |
250 | [[package]]
251 | name = "rand_core"
252 | version = "0.6.4"
253 | source = "registry+https://github.com/rust-lang/crates.io-index"
254 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
255 |
256 | [[package]]
257 | name = "redox_syscall"
258 | version = "0.3.5"
259 | source = "registry+https://github.com/rust-lang/crates.io-index"
260 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
261 | dependencies = [
262 | "bitflags 1.3.2",
263 | ]
264 |
265 | [[package]]
266 | name = "risc0-binfmt"
267 | version = "0.18.0"
268 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
269 | dependencies = [
270 | "anyhow",
271 | "elf",
272 | "log",
273 | "risc0-zkp",
274 | "risc0-zkvm-platform",
275 | "serde",
276 | ]
277 |
278 | [[package]]
279 | name = "risc0-circuit-rv32im"
280 | version = "0.18.0"
281 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
282 | dependencies = [
283 | "anyhow",
284 | "log",
285 | "risc0-core",
286 | "risc0-zkp",
287 | "risc0-zkvm-platform",
288 | "tracing",
289 | ]
290 |
291 | [[package]]
292 | name = "risc0-core"
293 | version = "0.18.0"
294 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
295 | dependencies = [
296 | "bytemuck",
297 | "rand_core",
298 | ]
299 |
300 | [[package]]
301 | name = "risc0-zkp"
302 | version = "0.18.0"
303 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
304 | dependencies = [
305 | "anyhow",
306 | "blake2",
307 | "bytemuck",
308 | "digest",
309 | "hex",
310 | "log",
311 | "paste",
312 | "rand_core",
313 | "risc0-core",
314 | "risc0-zkvm-platform",
315 | "serde",
316 | "sha2",
317 | "tracing",
318 | ]
319 |
320 | [[package]]
321 | name = "risc0-zkvm"
322 | version = "0.18.0"
323 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
324 | dependencies = [
325 | "anyhow",
326 | "bytemuck",
327 | "cfg-if",
328 | "getrandom",
329 | "hex",
330 | "libm",
331 | "log",
332 | "num-derive",
333 | "num-traits",
334 | "risc0-binfmt",
335 | "risc0-circuit-rv32im",
336 | "risc0-core",
337 | "risc0-zkp",
338 | "risc0-zkvm-platform",
339 | "serde",
340 | "tempfile",
341 | "tracing",
342 | ]
343 |
344 | [[package]]
345 | name = "risc0-zkvm-platform"
346 | version = "0.18.0"
347 | source = "git+https://github.com/risc0/risc0?tag=v0.18.0#29cc16f84e1f5f2af34120528b1f18a33e00ce71"
348 |
349 | [[package]]
350 | name = "rustix"
351 | version = "0.38.13"
352 | source = "registry+https://github.com/rust-lang/crates.io-index"
353 | checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
354 | dependencies = [
355 | "bitflags 2.4.0",
356 | "errno",
357 | "libc",
358 | "linux-raw-sys",
359 | "windows-sys",
360 | ]
361 |
362 | [[package]]
363 | name = "search_json"
364 | version = "0.12.0"
365 | dependencies = [
366 | "json",
367 | "json-core",
368 | "risc0-zkvm",
369 | ]
370 |
371 | [[package]]
372 | name = "serde"
373 | version = "1.0.171"
374 | source = "registry+https://github.com/rust-lang/crates.io-index"
375 | checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
376 | dependencies = [
377 | "serde_derive",
378 | ]
379 |
380 | [[package]]
381 | name = "serde_derive"
382 | version = "1.0.171"
383 | source = "registry+https://github.com/rust-lang/crates.io-index"
384 | checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
385 | dependencies = [
386 | "proc-macro2",
387 | "quote",
388 | "syn 2.0.26",
389 | ]
390 |
391 | [[package]]
392 | name = "sha2"
393 | version = "0.10.6"
394 | source = "registry+https://github.com/rust-lang/crates.io-index"
395 | checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
396 | dependencies = [
397 | "cfg-if",
398 | "cpufeatures",
399 | "digest",
400 | ]
401 |
402 | [[package]]
403 | name = "subtle"
404 | version = "2.4.1"
405 | source = "registry+https://github.com/rust-lang/crates.io-index"
406 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
407 |
408 | [[package]]
409 | name = "syn"
410 | version = "1.0.107"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
413 | dependencies = [
414 | "proc-macro2",
415 | "quote",
416 | "unicode-ident",
417 | ]
418 |
419 | [[package]]
420 | name = "syn"
421 | version = "2.0.26"
422 | source = "registry+https://github.com/rust-lang/crates.io-index"
423 | checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
424 | dependencies = [
425 | "proc-macro2",
426 | "quote",
427 | "unicode-ident",
428 | ]
429 |
430 | [[package]]
431 | name = "tempfile"
432 | version = "3.8.0"
433 | source = "registry+https://github.com/rust-lang/crates.io-index"
434 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
435 | dependencies = [
436 | "cfg-if",
437 | "fastrand",
438 | "redox_syscall",
439 | "rustix",
440 | "windows-sys",
441 | ]
442 |
443 | [[package]]
444 | name = "tracing"
445 | version = "0.1.37"
446 | source = "registry+https://github.com/rust-lang/crates.io-index"
447 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
448 | dependencies = [
449 | "cfg-if",
450 | "pin-project-lite",
451 | "tracing-attributes",
452 | "tracing-core",
453 | ]
454 |
455 | [[package]]
456 | name = "tracing-attributes"
457 | version = "0.1.23"
458 | source = "registry+https://github.com/rust-lang/crates.io-index"
459 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
460 | dependencies = [
461 | "proc-macro2",
462 | "quote",
463 | "syn 1.0.107",
464 | ]
465 |
466 | [[package]]
467 | name = "tracing-core"
468 | version = "0.1.30"
469 | source = "registry+https://github.com/rust-lang/crates.io-index"
470 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
471 |
472 | [[package]]
473 | name = "typenum"
474 | version = "1.16.0"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
477 |
478 | [[package]]
479 | name = "unicode-ident"
480 | version = "1.0.6"
481 | source = "registry+https://github.com/rust-lang/crates.io-index"
482 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
483 |
484 | [[package]]
485 | name = "version_check"
486 | version = "0.9.4"
487 | source = "registry+https://github.com/rust-lang/crates.io-index"
488 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
489 |
490 | [[package]]
491 | name = "wasi"
492 | version = "0.11.0+wasi-snapshot-preview1"
493 | source = "registry+https://github.com/rust-lang/crates.io-index"
494 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
495 |
496 | [[package]]
497 | name = "windows-sys"
498 | version = "0.48.0"
499 | source = "registry+https://github.com/rust-lang/crates.io-index"
500 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
501 | dependencies = [
502 | "windows-targets",
503 | ]
504 |
505 | [[package]]
506 | name = "windows-targets"
507 | version = "0.48.5"
508 | source = "registry+https://github.com/rust-lang/crates.io-index"
509 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
510 | dependencies = [
511 | "windows_aarch64_gnullvm",
512 | "windows_aarch64_msvc",
513 | "windows_i686_gnu",
514 | "windows_i686_msvc",
515 | "windows_x86_64_gnu",
516 | "windows_x86_64_gnullvm",
517 | "windows_x86_64_msvc",
518 | ]
519 |
520 | [[package]]
521 | name = "windows_aarch64_gnullvm"
522 | version = "0.48.5"
523 | source = "registry+https://github.com/rust-lang/crates.io-index"
524 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
525 |
526 | [[package]]
527 | name = "windows_aarch64_msvc"
528 | version = "0.48.5"
529 | source = "registry+https://github.com/rust-lang/crates.io-index"
530 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
531 |
532 | [[package]]
533 | name = "windows_i686_gnu"
534 | version = "0.48.5"
535 | source = "registry+https://github.com/rust-lang/crates.io-index"
536 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
537 |
538 | [[package]]
539 | name = "windows_i686_msvc"
540 | version = "0.48.5"
541 | source = "registry+https://github.com/rust-lang/crates.io-index"
542 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
543 |
544 | [[package]]
545 | name = "windows_x86_64_gnu"
546 | version = "0.48.5"
547 | source = "registry+https://github.com/rust-lang/crates.io-index"
548 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
549 |
550 | [[package]]
551 | name = "windows_x86_64_gnullvm"
552 | version = "0.48.5"
553 | source = "registry+https://github.com/rust-lang/crates.io-index"
554 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
555 |
556 | [[package]]
557 | name = "windows_x86_64_msvc"
558 | version = "0.48.5"
559 | source = "registry+https://github.com/rust-lang/crates.io-index"
560 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
561 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/guest/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "search_json"
3 | version = "0.12.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Apache-2.0"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | [workspace]
11 |
12 | [dependencies]
13 | json = "0.12"
14 | json-core = { path = "../../core" }
15 | risc0-zkvm = { git = "https://github.com/risc0/risc0", tag = "v0.18.0", default-features = false, features = [
16 | "std",
17 | ] }
18 |
19 |
20 | # TODO copy the json project into the programs example and then resolve the deps via crates registry or
21 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/guest/src/main.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 RISC Zero, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #![no_main]
16 |
17 | use json::parse;
18 | use json_core::Outputs;
19 | use risc0_zkvm::{
20 | guest::env,
21 | sha::{Impl, Sha256},
22 | };
23 |
24 | risc0_zkvm::guest::entry!(main);
25 |
26 | pub fn main() {
27 | let data: String = env::read();
28 | let sha = *Impl::hash_bytes(&data.as_bytes());
29 | let data = parse(&data).unwrap();
30 | let proven_val = data["critical_data"].as_u32().unwrap();
31 | let out = Outputs {
32 | data: proven_val,
33 | hash: sha,
34 | };
35 | env::commit(&out);
36 | }
37 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/methods/src/lib.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 RISC Zero, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | include!(concat!(env!("OUT_DIR"), "/methods.rs"));
16 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/res/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "boolean_field": true,
3 | "critical_data": 47,
4 | "obj_field": {
5 | "string_subfield": "hello world",
6 | "array_subfield": [
7 | "more",
8 | "example",
9 | "text"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/json/src/main.rs:
--------------------------------------------------------------------------------
1 | // Copyright 2023 RISC Zero, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | use json_core::Outputs;
16 | use json_methods::{SEARCH_JSON_ELF, SEARCH_JSON_ID};
17 | use risc0_zkvm::{
18 | default_prover,
19 | serde::{from_slice, to_vec},
20 | ExecutorEnv,
21 | };
22 |
23 | fn main() {
24 | let data = include_str!("../res/example.json");
25 | let outputs = search_json(data);
26 | println!();
27 | println!(" {:?}", outputs.hash);
28 | println!(
29 | "provably contains a field 'critical_data' with value {}",
30 | outputs.data
31 | );
32 | }
33 |
34 | fn search_json(data: &str) -> Outputs {
35 | let env = ExecutorEnv::builder()
36 | .add_input(&to_vec(&data).unwrap())
37 | .build()
38 | .unwrap();
39 |
40 | // Obtain the default prover.
41 | let prover = default_prover();
42 |
43 | // Produce a receipt by proving the specified ELF binary.
44 | let receipt = prover.prove_elf(env, SEARCH_JSON_ELF).unwrap();
45 |
46 | // Intentionally change the ID (commitment) to something else.
47 | let mut different_id = SEARCH_JSON_ID.clone();
48 | different_id[0] = 0x00;
49 |
50 | let _verified = receipt.verify(different_id);
51 | let _ = dbg!(_verified);
52 |
53 | from_slice(&receipt.journal).unwrap()
54 | }
55 |
56 | #[cfg(test)]
57 | mod tests {
58 | #[test]
59 | fn main() {
60 | let data = include_str!("../res/example.json");
61 | let outputs = super::search_json(data);
62 | assert_eq!(
63 | outputs.data, 47,
64 | "Did not find the expected value in the critical_data field"
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! This example shows how to write a contrieved and basic program: checking the length of the data to be signed.
2 |
3 | #![cfg_attr(not(test), no_std)]
4 |
5 | extern crate alloc;
6 |
7 | use alloc::{string::ToString, vec::Vec};
8 |
9 | use entropy_programs_core::{bindgen::Error, bindgen::*, export_program, prelude::*};
10 |
11 | use bincode;
12 | use risc0_zkvm::Receipt;
13 |
14 | // TODO confirm this isn't an issue for audit
15 | register_custom_getrandom!(always_fail);
16 |
17 | pub struct ZkVmVerificationProgram;
18 |
19 | impl Program for ZkVmVerificationProgram {
20 | fn evaluate(signature_request: SignatureRequest, _config: Option>, _oracle_data: Option>>) -> Result<(), Error> {
21 | let image_id: [u32; 8] = bincode::deserialize(&signature_request.message)
22 | .map_err(|_| Error::InvalidSignatureRequest("Could not parse image_id".to_string()))?;
23 |
24 | let receipt: Receipt = match signature_request.auxilary_data {
25 | Some(serialized_receipt) => {
26 | bincode::deserialize(&serialized_receipt).map_err(|_| {
27 | Error::InvalidSignatureRequest("Could not parse receipt".to_string())
28 | })?
29 | }
30 | None => {
31 | return Err(Error::InvalidSignatureRequest(
32 | "No receipt provided".to_string(),
33 | ))
34 | }
35 | };
36 |
37 | receipt
38 | .verify(image_id)
39 | .map_err(|_| Error::Evaluation("Proof verification failed".to_string()))?;
40 |
41 | Ok(())
42 | }
43 |
44 | /// Since we don't use a custom hash function, we can just return `None` here.
45 | fn custom_hash(_data: Vec) -> Option> {
46 | None
47 | }
48 | }
49 |
50 | export_program!(ZkVmVerificationProgram);
51 |
52 | // write a test that calls evaluate and passes it the proper parameters
53 | #[cfg(test)]
54 | mod tests {
55 | use super::*;
56 |
57 | use std::fs::{create_dir_all, File};
58 | use std::io::{Read, Write};
59 | use std::path::Path;
60 |
61 | use json_methods::{SEARCH_JSON_ELF, SEARCH_JSON_ID};
62 | use risc0_zkvm::{default_prover, serde::to_vec, ExecutorEnv};
63 |
64 | use helpers::*;
65 |
66 | #[ignore = "this test is only used to reproducibly generate test data"]
67 | #[test]
68 | fn generate_and_save_receipt_for_test() {
69 | let json_data = read_json_data();
70 |
71 | // Generate receipt
72 | let receipt = generate_receipt(&json_data);
73 |
74 | // Write receipt to file
75 | let receipt_path = Path::new("test_data").join("zkvm_receipt.bin");
76 | write_struct_to_file(&receipt_path, &receipt);
77 |
78 | // Write commitment to file
79 | let commitment_path = Path::new("test_data").join("zkvm_image_id.bin");
80 | write_struct_to_file(&commitment_path, &SEARCH_JSON_ID);
81 |
82 | // Write an erroneous commitment to file
83 | let wrong_commitment = create_erroneous_commitment();
84 | let wrong_commitment_path = Path::new("test_data").join("zkvm_wrong_image_id.bin");
85 | write_struct_to_file(&wrong_commitment_path, &wrong_commitment);
86 | }
87 |
88 | #[test]
89 | fn test_should_pass_valid_receipt_and_image_pair() {
90 | let signature_request = SignatureRequest {
91 | message: bincode::serialize(&read_test_image_id()).unwrap(),
92 | auxilary_data: Some(bincode::serialize(&read_test_receipt()).unwrap()),
93 | };
94 |
95 | assert!(ZkVmVerificationProgram::evaluate(signature_request, None, None).is_ok());
96 | }
97 |
98 | #[test]
99 | fn test_should_error_with_incorrect_image_id_for_receipt_image_pair() {
100 | let signature_request = SignatureRequest {
101 | message: bincode::serialize(&read_erronous_test_image_id()).unwrap(),
102 | auxilary_data: Some(bincode::serialize(&read_test_receipt()).unwrap()),
103 | };
104 |
105 | assert!(ZkVmVerificationProgram::evaluate(signature_request, None, None).is_err());
106 | }
107 |
108 | // Test helper functions
109 | mod helpers {
110 | use super::*;
111 |
112 | /// Read a file from disk and deserialize it using bincode
113 | pub fn read_struct_from_file(filename: &str) -> T
114 | where
115 | T: serde::de::DeserializeOwned,
116 | {
117 | let dest_path = std::path::Path::new("test_data").join(filename);
118 |
119 | // Read the serialized data from disk
120 | let mut f = File::open(dest_path).expect("Failed to open file");
121 | let mut serialized = Vec::new();
122 | f.read_to_end(&mut serialized).expect("Failed to read data");
123 |
124 | // Deserialize using bincode
125 | bincode::deserialize(&serialized).expect("Failed to deserialize data")
126 | }
127 |
128 | /// Read the test Receipt from disk
129 | pub fn read_test_receipt() -> Receipt {
130 | read_struct_from_file("zkvm_receipt.bin")
131 | }
132 |
133 | /// Read the test image ID from disk
134 | pub fn read_test_image_id() -> [u32; 8] {
135 | read_struct_from_file("zkvm_image_id.bin")
136 | }
137 |
138 | // Read zkvm_wrong_image_id.bin from disk
139 | pub fn read_erronous_test_image_id() -> [u32; 8] {
140 | read_struct_from_file("zkvm_wrong_image_id.bin")
141 | }
142 |
143 | pub fn read_json_data() -> String {
144 | include_str!("../json/res/example.json").to_string()
145 | }
146 |
147 | pub fn generate_receipt(json_data: &str) -> Receipt {
148 | let env = ExecutorEnv::builder()
149 | .add_input(&to_vec(&json_data).unwrap())
150 | .build()
151 | .unwrap();
152 | let prover = default_prover();
153 | prover.prove_elf(env, SEARCH_JSON_ELF).unwrap()
154 | }
155 |
156 | pub fn write_struct_to_file(path: &Path, data: &T) {
157 | create_dir_all(&path.parent().unwrap()).expect("Failed to create directory");
158 | let serialized_data = bincode::serialize(data).expect("Failed to serialize data");
159 | let mut file = File::create(path).expect("Failed to create file");
160 | file.write_all(&serialized_data)
161 | .expect("Failed to write data");
162 | }
163 |
164 | pub fn create_erroneous_commitment() -> [u32; 8] {
165 | let mut wrong_commitment = SEARCH_JSON_ID.clone();
166 | wrong_commitment[0] = 0x00;
167 | wrong_commitment
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/test_data/zkvm_image_id.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/entropyxyz/programs/c351230c7eac77b7b9a86610cb88f4d724882ff7/examples/risczero-zkvm-verification/test_data/zkvm_image_id.bin
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/test_data/zkvm_receipt.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/entropyxyz/programs/c351230c7eac77b7b9a86610cb88f4d724882ff7/examples/risczero-zkvm-verification/test_data/zkvm_receipt.bin
--------------------------------------------------------------------------------
/examples/risczero-zkvm-verification/test_data/zkvm_wrong_image_id.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/entropyxyz/programs/c351230c7eac77b7b9a86610cb88f4d724882ff7/examples/risczero-zkvm-verification/test_data/zkvm_wrong_image_id.bin
--------------------------------------------------------------------------------
/examples/siwe/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "template-siwe"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "Unlicense"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | # This is required to compile programs to a wasm module and for use in rust libs
11 | [lib]
12 | crate-type = ["cdylib", "rlib"]
13 |
14 | [dependencies]
15 | entropy-programs-core = { workspace = true }
16 | siwe = "0.6.0"
17 | schemars = {version = "0.8.16", optional = true}
18 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
19 |
20 | # These are used by `cargo component`
21 | [package.metadata.component]
22 | package = "entropy:template-siwe"
23 |
24 | [package.metadata.component.target]
25 | path = "../../wit"
26 |
27 | [package.metadata.component.dependencies]
28 |
29 |
30 | [features]
31 | std = ["schemars"]
--------------------------------------------------------------------------------
/examples/siwe/README.md:
--------------------------------------------------------------------------------
1 | # 'Sign-in with Ethereum' example template
2 |
3 | A template program which allows only valid Sign-in with Ethereum messages (EIP 4361)
4 | which sign-in to a given service.
5 |
6 | This could be used to share an account for a specific service whilst not allowing transactions
7 | to be signed.
8 |
--------------------------------------------------------------------------------
/examples/siwe/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! A template program which allows only valid Sign-in with Ethereum messages (EIP 4361)
2 | //! which sign-in to a given service.
3 | //!
4 | //! This could be used to share an account for a specific service whilst not allowing transactions
5 | //! to be signed.
6 | #![no_std]
7 |
8 | extern crate alloc;
9 |
10 | use alloc::{
11 | string::{String, ToString},
12 | vec::Vec,
13 | };
14 |
15 | use entropy_programs_core::{bindgen::Error, bindgen::*, export_program, prelude::*};
16 | use serde::{Deserialize, Serialize};
17 | use siwe::Message;
18 |
19 | /// JSON-deserializable struct that will be used to derive the program-JSON interface.
20 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
21 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
22 | pub struct UserConfig {}
23 |
24 | /// JSON representation of the auxiliary data
25 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
26 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
27 | pub struct AuxData {}
28 |
29 | // TODO confirm this isn't an issue for audit
30 | register_custom_getrandom!(always_fail);
31 |
32 | // The domain we allow a user to sign-in to. Change this to the desired service
33 | const ALLOWED_DOMAIN: &str = "localhost";
34 |
35 | pub struct Siwe;
36 |
37 | impl Program for Siwe {
38 | fn evaluate(
39 | signature_request: SignatureRequest,
40 | _config: Option>,
41 | _oracle_data: Option>>,
42 | ) -> Result<(), Error> {
43 | let string_message = String::from_utf8(signature_request.message)
44 | .map_err(|err| Error::Evaluation(err.to_string()))?;
45 | let siwe_message = string_message
46 | .parse::()
47 | .map_err(|err| Error::Evaluation(err.to_string()))?;
48 |
49 | if siwe_message.domain == ALLOWED_DOMAIN {
50 | Ok(())
51 | } else {
52 | Err(Error::Evaluation(
53 | "You may not sign-in to this domain".to_string(),
54 | ))
55 | }
56 | }
57 |
58 | /// Since we don't use a custom hash function, we can just return `None` here.
59 | fn custom_hash(_data: Vec) -> Option> {
60 | None
61 | }
62 | }
63 |
64 | export_program!(Siwe);
65 |
66 | // write a test that calls evaluate and passes it the proper parameters
67 | #[cfg(test)]
68 | mod tests {
69 | use super::*;
70 |
71 | #[test]
72 | fn test_should_sign() {
73 | let signature_request = SignatureRequest {
74 | message: "localhost wants you to sign in with your Ethereum account:
75 | 0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94
76 |
77 | This is a test statement.
78 |
79 | URI: https://localhost/login
80 | Version: 1
81 | Chain ID: 1
82 | Nonce: oNCEHm5jzQU2WvuBB
83 | Issued At: 2022-01-28T23:28:16.013Z"
84 | .to_string()
85 | .into_bytes(),
86 | auxilary_data: None,
87 | };
88 |
89 | assert!(Siwe::evaluate(signature_request, None, None).is_ok());
90 | }
91 |
92 | #[test]
93 | fn test_bad_siwe_message() {
94 | let signature_request = SignatureRequest {
95 | message: "localhost does not want you to sign in with your Ethereum account:
96 | 0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94
97 |
98 | This is a test statement.
99 |
100 | URI: https://localhost/login
101 | Version: 1
102 | Chain ID: 1
103 | Nonce: oNCEHm5jzQU2WvuBB
104 | Issued At: 2022-01-28T23:28:16.013Z"
105 | .to_string()
106 | .into_bytes(),
107 | auxilary_data: None,
108 | };
109 |
110 | assert!(Siwe::evaluate(signature_request, None, None).is_err());
111 | }
112 |
113 | #[test]
114 | fn test_bad_domain() {
115 | let signature_request = SignatureRequest {
116 | message: "google.com does not want you to sign in with your Ethereum account:
117 | 0x6Ee9894c677EFa1c56392e5E7533DE76004C8D94
118 |
119 | This is a test statement.
120 |
121 | URI: https://google.com/login
122 | Version: 1
123 | Chain ID: 1
124 | Nonce: oNCEHm5jzQU2WvuBB
125 | Issued At: 2022-01-28T23:28:16.013Z"
126 | .to_string()
127 | .into_bytes(),
128 | auxilary_data: None,
129 | };
130 |
131 | assert!(Siwe::evaluate(signature_request, None, None).is_err());
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/programs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "entropy-programs"
3 | version = "0.1.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "AGPL-3.0-or-later"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 |
10 | # [lib]
11 | # crate-type = ["cdylib"]
12 |
13 | [dependencies]
14 | entropy-programs-acl = { path = "../acl", default-features = false, features = ["evm"] }
15 | entropy-programs-evm = { path = "../evm", default-features = false }
16 | entropy-programs-core = { path = "../core", default-features = false }
17 |
18 | [dev-dependencies]
19 | # entropy-programs-runtime = { path = "../runtime", default-features = false }
20 |
21 | [features]
22 | default = ["std"]
23 | std = ["entropy-programs-acl/std", "entropy-programs-evm/std"]
24 |
--------------------------------------------------------------------------------
/programs/README.md:
--------------------------------------------------------------------------------
1 | # `entropy-programs`
2 |
3 | This is the main library for writing programs. Currently, it is boilerplate and will re-export all programs and architectures.
4 |
--------------------------------------------------------------------------------
/programs/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! Includes types and interfaces that are foundational to the core of programs.
2 |
3 | #![cfg_attr(not(feature = "std"), no_std)]
4 |
5 | extern crate alloc;
6 |
7 | pub use entropy_programs_core as core;
8 | /// All architecture-agnostic programs should be re-exported from this module
9 | pub mod programs {
10 | pub use entropy_programs_acl as acl;
11 | }
12 | /// All architectures that implement the `ec_core::Architecture` trait should be re-exported from here.
13 | pub mod arch {
14 | pub use entropy_programs_evm as evm;
15 | }
16 |
17 | /// Dynamic parsing allows for easily hooking transactions into
18 | pub mod parsing {}
19 |
--------------------------------------------------------------------------------
/runtime/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "entropy-programs-runtime"
3 | version = "0.11.0"
4 | authors = ["Entropy Cryptography "]
5 | homepage = "https://entropy.xyz/"
6 | license = "AGPL-3.0-or-later"
7 | repository = "https://github.com/entropyxyz/programs"
8 | edition = "2021"
9 | description = "Webassembly runtime for running Entropy programs"
10 |
11 | [dependencies]
12 | wasmtime = { version = "12.0.1", features = ["component-model"] }
13 | entropy-programs-core = { version = "0.11.0", path = "../core" }
14 | thiserror = "1.0.47"
15 |
16 | [dev-dependencies]
17 | blake3 = "1.5.0"
18 |
--------------------------------------------------------------------------------
/runtime/README.md:
--------------------------------------------------------------------------------
1 | # `entropy-programs-runtime`
2 |
3 | This contains the Wasm runtime for evaluaing, testing, and simulating programs as *Wasm Components*.
4 |
5 | ## Running Tests
6 |
7 | Before running the tests, you need to build the `template-barebones` and `infinite-loop` components. Be sure to have `cargo component` installed, and run `cargo component build --release -p template-barebones -p infinite-loop --target wasm32-unknown-unknown`. This will create the files needed for testing at `target/wasm32-unknown-unknown/release/`.
8 |
--------------------------------------------------------------------------------
/runtime/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! Contains the Wasm runtime and related types for evaluating programs.
2 |
3 | use thiserror::Error;
4 | use wasmtime::{
5 | component::{bindgen, Component, Linker},
6 | Config as WasmtimeConfig, Engine, Result, Store,
7 | };
8 |
9 | /// Note, this is wasmtime's bindgen, not wit-bindgen (modules)
10 | mod bindgen {
11 | use super::bindgen;
12 |
13 | bindgen!({
14 | world: "program",
15 | });
16 | }
17 | pub use bindgen::{Error as ProgramError, Program, SignatureRequest};
18 |
19 | /// Runtime `Error` type
20 | #[derive(Debug, Error)]
21 | pub enum RuntimeError {
22 | /// Program bytecode is of zero length (core-side runtime error; Programs should probably not return this)
23 | #[error("Bytecode length is zero")]
24 | EmptyBytecode,
25 | /// Program bytecode is not a valid WebAssembly component.
26 | #[error("Invalid bytecode")]
27 | InvalidBytecode,
28 | /// Program error during execution.
29 | #[error("Runtime error: {0}")]
30 | Runtime(ProgramError),
31 | /// Program exceeded fuel limits. Execute fewer instructions.
32 | #[error("Out of fuel")]
33 | OutOfFuel,
34 | }
35 |
36 | /// Config is for runtime parameters (eg instructions per program, additional runtime interfaces, etc).
37 | pub struct Config {
38 | /// Max number of instructions the runtime will execute before returning an error.
39 | pub fuel: u64,
40 | }
41 |
42 | impl Default for Config {
43 | fn default() -> Self {
44 | Self { fuel: 10_000 }
45 | }
46 | }
47 |
48 | /// Runtime allows for the execution of programs. Instantiate with `Runtime::new()`.
49 | pub struct Runtime {
50 | engine: Engine,
51 | linker: Linker<()>,
52 | store: Store<()>,
53 | }
54 |
55 | impl Default for Runtime {
56 | fn default() -> Self {
57 | Self::new(Config::default())
58 | }
59 | }
60 |
61 | impl Runtime {
62 | pub fn new(config: Config) -> Self {
63 | let mut wasmtime_config = WasmtimeConfig::new();
64 | wasmtime_config
65 | .wasm_component_model(true)
66 | .consume_fuel(true);
67 |
68 | let engine = Engine::new(&wasmtime_config).unwrap();
69 | let linker = Linker::new(&engine);
70 | let mut store = Store::new(&engine, ());
71 |
72 | store.add_fuel(config.fuel).unwrap();
73 | Self {
74 | engine,
75 | linker,
76 | store,
77 | }
78 | }
79 | }
80 |
81 | impl Runtime {
82 | /// Evaluate a program with a given initial state.
83 | pub fn evaluate(
84 | &mut self,
85 | program: &[u8],
86 | signature_request: &SignatureRequest,
87 | config: Option<&[u8]>,
88 | oracle_data: Option<&[Vec]>,
89 | ) -> Result<(), RuntimeError> {
90 | if program.len() == 0 {
91 | return Err(RuntimeError::EmptyBytecode);
92 | }
93 |
94 | let component = Component::from_binary(&self.engine, program)
95 | .map_err(|_| RuntimeError::InvalidBytecode)?;
96 | let (bindings, _) = Program::instantiate(&mut self.store, &component, &self.linker)
97 | .map_err(|_| RuntimeError::InvalidBytecode)?;
98 |
99 | bindings
100 | .call_evaluate(&mut self.store, signature_request, config, oracle_data)
101 | .map_err(|_| RuntimeError::OutOfFuel)?
102 | .map_err(RuntimeError::Runtime)
103 | }
104 |
105 | /// Compute the `custom-hash` of a `message` from the program.
106 | pub fn custom_hash(
107 | &mut self,
108 | program: &[u8],
109 | message: &[u8],
110 | ) -> Result<[u8; 32], RuntimeError> {
111 | if program.len() == 0 {
112 | return Err(RuntimeError::EmptyBytecode);
113 | }
114 |
115 | let component = Component::from_binary(&self.engine, program)
116 | .map_err(|_| RuntimeError::InvalidBytecode)?;
117 | let (bindings, _) = Program::instantiate(&mut self.store, &component, &self.linker)
118 | .map_err(|_| RuntimeError::InvalidBytecode)?;
119 |
120 | let hash_as_vec = bindings
121 | .call_custom_hash(&mut self.store, message)
122 | .unwrap().ok_or(RuntimeError::Runtime(ProgramError::InvalidSignatureRequest("`custom-hash` returns `None`. Implement the hash function in your program, or select a predefined `hash` in your signature request.".to_string())))?;
123 | if hash_as_vec.len() != 32 {
124 | return Err(RuntimeError::Runtime(
125 | ProgramError::InvalidSignatureRequest(format!(
126 | "`custom-hash` must returns a Vec of length 32, not {}.",
127 | hash_as_vec.len()
128 | )),
129 | ));
130 | }
131 |
132 | let mut hash = [0u8; 32];
133 | hash.copy_from_slice(&hash_as_vec);
134 | Ok(hash)
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/runtime/tests/runtime.rs:
--------------------------------------------------------------------------------
1 | /// Points to the `template-barebones` program binary.
2 | const BAREBONES_COMPONENT_WASM: &[u8] =
3 | include_bytes!("../../target/wasm32-unknown-unknown/release/template_barebones.wasm");
4 | const CUSTOM_HASH_COMPONENT_WASM: &[u8] =
5 | include_bytes!("../../target/wasm32-unknown-unknown/release/example_custom_hash.wasm");
6 | /// Points to the `infinite-loop` program binary.
7 | const INFINITE_LOOP_WASM: &[u8] =
8 | include_bytes!("../../target/wasm32-unknown-unknown/release/infinite_loop.wasm");
9 |
10 | use blake3;
11 | use entropy_programs_runtime::{Runtime, SignatureRequest};
12 |
13 | #[test]
14 | fn test_barebones_component() {
15 | let mut runtime = Runtime::default();
16 |
17 | // The barebones example simply validates that the length of the data to be signed is greater than 10.
18 | let longer_than_10 = "asdfasdfasdfasdf".to_string();
19 | let signature_request = SignatureRequest {
20 | message: longer_than_10.into_bytes(),
21 | auxilary_data: None,
22 | };
23 |
24 | let res = runtime.evaluate(BAREBONES_COMPONENT_WASM, &signature_request, None, None);
25 | assert!(res.is_ok());
26 | }
27 |
28 | #[test]
29 | fn test_barebones_component_fails_with_data_length_less_than_10() {
30 | let mut runtime = Runtime::default();
31 |
32 | // Since the barebones example verifies that the length of the data to be signed is greater than 10, this should fail.
33 | let shorter_than_10 = "asdf".to_string();
34 | let signature_request = SignatureRequest {
35 | message: shorter_than_10.into_bytes(),
36 | auxilary_data: None,
37 | };
38 |
39 | let res = runtime.evaluate(BAREBONES_COMPONENT_WASM, &signature_request, None, None);
40 | assert!(res.is_err());
41 | }
42 |
43 | #[test]
44 | fn test_empty_bytecode_fails() {
45 | let mut runtime = Runtime::default();
46 |
47 | let signature_request = SignatureRequest {
48 | message: vec![],
49 | auxilary_data: None,
50 | };
51 |
52 | let res = runtime.evaluate(&[], &signature_request, None, None);
53 | assert_eq!(res.unwrap_err().to_string(), "Bytecode length is zero");
54 | }
55 |
56 | #[test]
57 | fn test_infinite_loop() {
58 | let mut runtime = Runtime::default();
59 |
60 | let signature_request = SignatureRequest {
61 | message: vec![],
62 | auxilary_data: None,
63 | };
64 |
65 | let res = runtime.evaluate(INFINITE_LOOP_WASM, &signature_request, None, None);
66 | assert_eq!(res.unwrap_err().to_string(), "Out of fuel");
67 | }
68 |
69 | #[test]
70 | fn test_custom_hash() {
71 | let mut runtime = Runtime::default();
72 |
73 | let message = "some_data_to_be_hashed".to_string().into_bytes();
74 |
75 | let mut expected_hash = [0u8; 32];
76 | let expected_hash_as_vec = blake3::hash(&message).as_bytes().to_vec();
77 | expected_hash.copy_from_slice(&expected_hash_as_vec);
78 |
79 | let actual_hash = runtime
80 | .custom_hash(CUSTOM_HASH_COMPONENT_WASM, message.as_slice())
81 | .unwrap();
82 |
83 | assert_eq!(actual_hash, expected_hash);
84 | }
85 |
86 | #[test]
87 | fn test_custom_hash_errors_when_returning_none() {
88 | let mut runtime = Runtime::default();
89 |
90 | let message = "some_data_to_be_hashed".to_string().into_bytes();
91 |
92 | let res = runtime.custom_hash(
93 | // Remember, barebones component doesn't define a custom hash function
94 | BAREBONES_COMPONENT_WASM,
95 | message.as_slice(),
96 | );
97 | assert!(res.is_err());
98 | assert_eq!(
99 | res.unwrap_err().to_string(),
100 | "Runtime error: Error::InvalidSignatureRequest(\"`custom-hash` returns `None`. Implement the hash function in your program, or select a predefined `hash` in your signature request.\")"
101 | );
102 | }
103 |
104 | // TODO add test for custom hash returning a vec of length != 32
105 |
--------------------------------------------------------------------------------
/runtime/wit:
--------------------------------------------------------------------------------
1 | ../wit
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "stable"
3 | components = ["rustfmt", "clippy"]
4 | targets = ["wasm32-unknown-unknown"]
5 |
--------------------------------------------------------------------------------
/templates/basic-template/.dockerignore:
--------------------------------------------------------------------------------
1 | target
2 | .git
3 |
--------------------------------------------------------------------------------
/templates/basic-template/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | {{project-name}}_serialized_config_type.txt
3 | {{project-name}}_serialized_aux_data_type.txt
4 | .env
--------------------------------------------------------------------------------
/templates/basic-template/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "{{project-name}}"
3 | version = "0.1.0"
4 | authors = ["{{authors}}"]
5 | license = "Unlicense"
6 | edition = "2021"
7 |
8 | [workspace]
9 | members = ["generate-types", ".", "cli"]
10 | resolver = "2"
11 |
12 | # strip debug info since that makes up a major part of Wasm blobs, see Wasm's `twiggy`
13 | [profile.release]
14 | strip = "debuginfo"
15 |
16 | # This is required to compile programs to a wasm module and for use in rust libs
17 | [lib]
18 | crate-type = ["cdylib", "rlib"]
19 |
20 | [dependencies]
21 | entropy-programs-core = { git = "https://github.com/entropyxyz/programs.git", branch = "master" }
22 | schemars = { version = "0.8.16", optional = true }
23 | serde = { version = "1.0", default-features = false, features = [
24 | "alloc",
25 | "derive",
26 | ] }
27 |
28 | # These are used by `cargo component`
29 | [package.metadata.component]
30 | package = "entropy:{{project-name}}"
31 |
32 | [package.metadata.component.dependencies]
33 |
34 | # Metadata related to an Entropy program
35 | [package.metadata.entropy-program]
36 |
37 | # The docker image used to build this program
38 | docker-image = "entropyxyz/build-entropy-programs:v0.0.1"
39 |
40 | # Configuration interface description
41 | # interface-description = ""
42 |
43 | [features]
44 | std = ["schemars"]
45 |
--------------------------------------------------------------------------------
/templates/basic-template/Dockerfile:
--------------------------------------------------------------------------------
1 | # To build this program, and put the .wasm binary in the directory 'output':
2 | # docker build --output=binary-dir .
3 | FROM entropyxyz/build-entropy-programs:v0.0.1 AS base
4 |
5 | WORKDIR /usr/src/programs
6 | COPY . .
7 |
8 | RUN cargo component build --release --target wasm32-unknown-unknown
9 |
10 | FROM scratch AS binary
11 | COPY --from=base /usr/src/programs/target/wasm32-unknown-unknown/release/*.wasm /
12 |
--------------------------------------------------------------------------------
/templates/basic-template/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # {{project-name}}
4 |
5 | ## Running tests
6 |
7 | `cargo test`
8 |
9 | ## Building the program
10 |
11 | Get the necessary build tools with:
12 |
13 | ```shell
14 | cargo install cargo-component --version 0.2.0 &&
15 | cargo install wasm-tools
16 | ```
17 |
18 | Then build with:
19 |
20 | ```shell
21 | cargo component build --release --target wasm32-unknown-unknown
22 | ```
23 |
24 | The `.wasm` binary can be found in `./target/wasm32-unknown-unknown/release`
25 |
26 | ## Building with docker
27 |
28 | If you want to make your program publicly available and open source, and make it possible for others to verify that the source code corresponds to the on-chain binary, you can build it with the included Dockerfile:
29 |
30 | ```shell
31 | docker build --output=binary-dir .
32 | ```
33 |
34 | This will compile your program and put the `.wasm` binary file in `./binary-dir`.
35 |
36 | ## Generate Types
37 |
38 | Types are meant top be posted with the program, it is how people know how to interact with your program
39 |
40 | They will be autogenerated when running store-programs, or you can run it manually
41 |
42 | ```shell
43 | cargo run -p generate-types
44 | ```
45 |
46 | Will generate two files that will hold both the aux_data_schema and config_schema
47 |
48 | ## Upload program
49 |
50 | The basic template is shipped with a cli to upload a program, after compiling the program then generating the types
51 | you upload the program to chain.
52 |
53 | Create a .env file with two variables
54 |
55 | ```env
56 | DEPLOYER_MNEMONIC=""
57 | ENTROPY_DEVNET=""
58 | ```
59 |
60 | Then run:
61 |
62 | ```shell
63 | cargo run -p cli -- store-program
64 | ```
65 |
--------------------------------------------------------------------------------
/templates/basic-template/cli/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cli"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | clap = { version = "4.5.20", features = ["derive"] }
8 | subxt = { version = "0.35.3", features = ["substrate-compat"] }
9 | colored = "2.0.4"
10 | tokio = { version = "1.16", features = [
11 | "macros",
12 | "rt-multi-thread",
13 | "io-util",
14 | "process",
15 | ] }
16 | anyhow = "1.0.82"
17 | dotenv = "0.15.0"
18 | generate-types = { path = "../generate-types" }
19 | project-root = "0.2.2"
20 | entropy-test-cli = { git = "https://github.com/entropyxyz/entropy-core.git", branch = "master" }
21 |
--------------------------------------------------------------------------------
/templates/basic-template/cli/entropy_metadata.scale:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/entropyxyz/programs/c351230c7eac77b7b9a86610cb88f4d724882ff7/templates/basic-template/cli/entropy_metadata.scale
--------------------------------------------------------------------------------
/templates/basic-template/cli/src/main.rs:
--------------------------------------------------------------------------------
1 | use clap::Parser;
2 | use colored::Colorize;
3 | use dotenv::dotenv;
4 | use entropy_test_cli::{run_command, Cli, PROGRAM_VERSION_NUMBER};
5 | use generate_types::generate_types;
6 | use project_root::get_project_root;
7 | use std::fs;
8 |
9 | #[tokio::main]
10 | async fn main() -> anyhow::Result<()> {
11 | dotenv().ok();
12 | let program = format!(
13 | "{}/target/wasm32-unknown-unknown/release/{{project-name}}.wasm",
14 | get_project_root()?.to_string_lossy()
15 | );
16 | generate_types();
17 | let config_interface = format!(
18 | "{}/{{project-name}}_serialized_config_type.txt",
19 | get_project_root()?.to_string_lossy()
20 | );
21 | let aux_data_interface = format!(
22 | "{}/{{project-name}}_serialized_aux_data_type.txt",
23 | get_project_root()?.to_string_lossy()
24 | );
25 |
26 | let oracle_data = format!(
27 | "{}/{{project-name}}_serialized_oracle_data_type.txt",
28 | get_project_root()?.to_string_lossy()
29 | );
30 |
31 | // length is 1 if empty and can ignore, scale codec length
32 | let decoded_oracle_data = fs::read(oracle_data.clone()).unwrap();
33 | let oracle_option = if decoded_oracle_data.len() == 1 {
34 | None
35 | } else {
36 | Some(oracle_data.into())
37 | };
38 |
39 | let cli = Cli::parse();
40 | let json_ouput = cli.json;
41 | match run_command(
42 | cli,
43 | Some(program.into()),
44 | Some(config_interface.into()),
45 | Some(aux_data_interface.into()),
46 | oracle_option,
47 | Some(PROGRAM_VERSION_NUMBER),
48 | )
49 | .await
50 | {
51 | Ok(output) => {
52 | if json_ouput {
53 | println!("{}", output);
54 | } else {
55 | println!("Success: {}", output.green());
56 | }
57 | Ok(())
58 | }
59 | Err(err) => {
60 | if !json_ouput {
61 | eprintln!("{}", "Failed!".red());
62 | }
63 | Err(err)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/templates/basic-template/generate-types/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "generate-types"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | program = { package = "{{project-name}}", path = "..", default-features = false, features = [
10 | 'std',
11 | ] }
12 | schemars = "0.8.16"
13 | serde_json = { version = "1.0" }
14 | codec = { package = "parity-scale-codec", version = "3.6.8", default-features = false }
15 |
--------------------------------------------------------------------------------
/templates/basic-template/generate-types/src/lib.rs:
--------------------------------------------------------------------------------
1 | use schemars::schema_for;
2 | use std::fs;
3 | use program::{UserConfig, AuxData, ORACLE_DATA};
4 | use codec::Encode;
5 |
6 | pub fn generate_types() {
7 | let schema_config = schema_for!(UserConfig);
8 | fs::write(
9 | "./tests_serialized_config_type.txt",
10 | format!(
11 | "{:?}",
12 | serde_json::to_vec(&schema_config)
13 | .expect("error converting user config")
14 | ),
15 | )
16 | .expect("Failed to write to config");
17 |
18 | let schema_aux_data = schema_for!(AuxData);
19 | fs::write(
20 | "./tests_serialized_aux_data_type.txt",
21 | format!(
22 | "{:?}",
23 | serde_json::to_vec(&schema_aux_data)
24 | .expect("error converting user aux_data")
25 | ),
26 | )
27 | .expect("Failed to write to proxy aux_data");
28 |
29 | let oracle_data = ORACLE_DATA.iter().map(|x| x.encode()).collect::>();
30 | fs::write(
31 | "./tests_serialized_oracle_data_type.txt",
32 | oracle_data.encode()
33 | )
34 | .expect("Failed to write oracle_data");
35 | }
--------------------------------------------------------------------------------
/templates/basic-template/generate-types/src/main.rs:
--------------------------------------------------------------------------------
1 | use generate_types::generate_types;
2 |
3 | fn main() {
4 | generate_types();
5 | }
--------------------------------------------------------------------------------
/templates/basic-template/src/lib.rs:
--------------------------------------------------------------------------------
1 | //! No-op program
2 |
3 | #![cfg_attr(not(feature = "std"), no_std)]
4 |
5 | extern crate alloc;
6 |
7 | use alloc::{string::ToString, vec::Vec};
8 |
9 | use entropy_programs_core::{bindgen::Error, bindgen::*, export_program, prelude::*};
10 | use serde::{Deserialize, Serialize};
11 |
12 | #[cfg(test)]
13 | mod tests;
14 |
15 | // TODO confirm this isn't an issue for audit
16 | register_custom_getrandom!(always_fail);
17 |
18 | /// JSON-deserializable struct that will be used to derive the program-JSON interface.
19 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
20 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
21 | pub struct UserConfig {
22 | }
23 |
24 | /// JSON representation of the auxiliary data
25 | #[cfg_attr(feature = "std", derive(schemars::JsonSchema))]
26 | #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
27 | pub struct AuxData {
28 | }
29 |
30 | /// Oracle data used, (change 0 to amount of items in vector)
31 | pub const ORACLE_DATA: [&str; 0] = [];
32 |
33 | pub struct {{project-name | upper_camel_case}};
34 |
35 | impl Program for {{project-name | upper_camel_case}} {
36 | fn evaluate(
37 | signature_request: SignatureRequest,
38 | _config: Option>,
39 | _oracle_data: Option>>,
40 | ) -> Result<(), Error> {
41 | if signature_request.message.is_empty() {
42 | return Err(Error::Evaluation(
43 | "Message must have a length greater than zero".to_string(),
44 | ));
45 | }
46 | Ok(())
47 | }
48 |
49 | /// Since we don't use a custom hash function, we can just return `None` here.
50 | fn custom_hash(_data: Vec) -> Option> {
51 | None
52 | }
53 | }
54 |
55 | export_program!({{project-name | upper_camel_case}});
56 |
--------------------------------------------------------------------------------
/templates/basic-template/src/tests.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | #[test]
4 | fn test_should_sign() {
5 | let signature_request = SignatureRequest {
6 | message: b"some_message".to_vec(),
7 | auxilary_data: None,
8 | };
9 |
10 | assert!({{project-name | upper_camel_case}}::evaluate(signature_request, None, None).is_ok());
11 | }
12 |
13 | #[test]
14 | fn test_should_fail() {
15 | let signature_request = SignatureRequest {
16 | message: Vec::new(),
17 | auxilary_data: None,
18 | };
19 |
20 | assert!({{project-name | upper_camel_case}}::evaluate(signature_request, None, None).is_err());
21 | }
--------------------------------------------------------------------------------
/wit/application.wit:
--------------------------------------------------------------------------------
1 | package entropy:core
2 |
3 | world program {
4 | // similar to `variant`, but no type payloads
5 | variant error {
6 | /// The signature request is invalid (ie. the `initial-state` )
7 | invalid-signature-request(string),
8 | evaluation(string)
9 | }
10 | /// Evaluates the program given the user's signature request and the program's configuration.
11 | export evaluate: func(signature-request: signature-request, config: option>, oracle-data: option>>) -> result<_, error>
12 |
13 | /// Programs that use custom hash functions can a custom 32-byte curve point to be signed.
14 | export custom-hash: func(data: list) -> option>
15 |
16 | record signature-request {
17 | /// Preimage of the user's data that will be signed (eg. RLP-encoded ETH transaction request).
18 | message: list,
19 | /// Auxiliary data optionally required for program evaluation; this won't be signed (eg. zero-knowledge proof, third party signature)
20 | auxilary-data: option>
21 | }
22 | }
23 |
--------------------------------------------------------------------------------