Skip to content

Commit 5a61333

Browse files
nyc432claude
andcommitted
feat: Enforce secure image ID verification (v0.3.0)
BREAKING CHANGE: Receipt.verify() now requires image_id parameter Security improvements: - verify() method now requires external image_id parameter - Prevents deriving image ID from untrusted receipt data - Follows security best practice: never trust data from unverified sources Test improvements: - Added comprehensive test suite with zero-tolerance policy - Created run_all_tests.sh master test runner - All tests and demos return proper exit codes - Test runner aborts immediately on first failure API refinements: - Simplified API with consistent naming conventions - Removed legacy code and unnecessary complexity - Better error messages and validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7735cfe commit 5a61333

19 files changed

+754
-504
lines changed

CLAUDE.md

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,101 @@ print(dir(pyr0)) # Should show 'serialization' if properly installed
171171
- **0.x.x** - Alpha/Beta releases
172172
- **1.x.x** - Stable releases (future)
173173

174-
**Remember**: ALWAYS ask "What version number should this be?" before pushing to GitHub.
174+
**Remember**: ALWAYS ask "What version number should this be?" before pushing to GitHub.
175+
176+
## Test and Demo Requirements
177+
178+
**CRITICAL**: All tests and demos MUST follow strict error handling guidelines to ensure reliability and maintainability.
179+
180+
### Mandatory Requirements for ALL Tests and Demos:
181+
182+
1. **Exit Codes**:
183+
- MUST exit with code 0 on success, 1 on ANY failure
184+
- MUST track failures throughout execution (e.g., `test_passed = False`)
185+
- MUST NOT return success if any part failed
186+
187+
2. **No Silent Failures**:
188+
- NEVER treat missing dependencies as optional or acceptable
189+
- NEVER use warning symbols (⚠️) for actual failures - use error symbols (✗)
190+
- NEVER skip tests silently - missing prerequisites are failures
191+
- NEVER return success when errors occurred but were caught
192+
193+
3. **Exception Handling**:
194+
- MUST fail the test if an unexpected exception occurs
195+
- MUST NOT catch and ignore exceptions that indicate real problems
196+
- MUST NOT continue execution after critical failures
197+
198+
4. **Dependencies**:
199+
- ALL dependencies (like PyR0, merkle_py) are REQUIRED, not optional
200+
- If a dependency is missing, the test/demo MUST fail with a clear error
201+
- NEVER print "install X to enable feature" - if X is needed, its absence is a failure
202+
203+
5. **Validation**:
204+
- MUST verify ALL expected outcomes, not just print them
205+
- MUST fail if outputs don't match expectations (wrong size, format, values)
206+
- MUST check return codes of called functions and demos
207+
208+
### Integration with Master Test Runner:
209+
210+
All new tests and demos MUST be added to `run_all_tests.sh`:
211+
212+
```bash
213+
# Add your test/demo to the appropriate section:
214+
run_test "Your Test Name" "uv run path/to/your_test.py"
215+
```
216+
217+
The master test runner (`./run_all_tests.sh`):
218+
- Runs tests and demos in sequence, aborting immediately on first failure
219+
- Shows colored output with clear failure messages
220+
- Exits with code 1 immediately upon ANY test failure
221+
- Exits with code 0 only if ALL tests pass (zero tolerance)
222+
- Does NOT skip tests for missing dependencies - all are required
223+
224+
### Example of CORRECT Test Structure:
225+
226+
```python
227+
#!/usr/bin/env python3
228+
import sys
229+
230+
test_passed = True
231+
232+
try:
233+
# Test something
234+
result = do_something()
235+
if not result:
236+
print("✗ Test failed: unexpected result")
237+
test_passed = False
238+
except Exception as e:
239+
print(f"✗ Test failed with error: {e}")
240+
test_passed = False
241+
242+
if test_passed:
243+
print("✓ All tests passed")
244+
sys.exit(0)
245+
else:
246+
print("✗ Some tests failed")
247+
sys.exit(1)
248+
```
249+
250+
### Example of INCORRECT Patterns (NEVER DO THESE):
251+
252+
```python
253+
# WRONG - Silent failure
254+
try:
255+
import required_module
256+
except ImportError:
257+
print("⚠️ Module not available, skipping test") # NO!
258+
return # This hides a failure!
259+
260+
# WRONG - Treating errors as acceptable
261+
if not dependency_exists:
262+
print("Continuing without dependency...") # NO!
263+
# Should fail here instead
264+
265+
# WRONG - Success despite errors
266+
except Exception as e:
267+
print(f"Error: {e}")
268+
# No test_passed = False here! # NO!
269+
```
270+
271+
**Remember**: The goal is ZERO TOLERANCE for failures. If something should work, it MUST work, or the test MUST fail visibly with exit code 1.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "pyr0"
5-
version = "0.2.0"
5+
version = "0.3.0"
66
edition = "2021"
77

88
[lib]

README.md

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# PyR0 - Python Interface for RISC Zero zkVM
22

3-
[![Version](https://img.shields.io/badge/version-0.2.0-orange)](https://github.com/garyrob/PyR0/releases)
3+
[![Version](https://img.shields.io/badge/version-0.3.0-orange)](https://github.com/garyrob/PyR0/releases)
44
**⚠️ Experimental Alpha - Apple Silicon Only**
55

66
Python bindings for [RISC Zero](https://www.risczero.com/) zkVM, enabling zero-knowledge proof generation and verification from Python.
77

8-
> **Note**: This is an experimental alpha release (v0.2.0) currently targeting Apple Silicon (M1/M2/M3) Macs only.
8+
> **Note**: This is an experimental alpha release (v0.3.0) currently targeting Apple Silicon (M1/M2/M3) Macs only.
99
1010
## Overview
1111

@@ -29,6 +29,65 @@ The typical workflow:
2929
3. Use PyR0 to load the ELF, provide input, and generate a proof
3030
4. Share the proof and journal (public outputs) for verification
3131

32+
## Guest Program Development
33+
34+
### Basic Guest Setup
35+
36+
A minimal RISC Zero guest program requires proper configuration:
37+
38+
**Cargo.toml:**
39+
```toml
40+
[package]
41+
name = "my-guest"
42+
version = "0.1.0"
43+
edition = "2021"
44+
45+
[dependencies]
46+
# Basic setup - works for simple guests
47+
risc0-zkvm = { version = "1.2", default-features = false, features = ["std"] }
48+
```
49+
50+
### Memory Management
51+
52+
⚠️ **Critical for complex guests**: The default bump allocator never frees memory, causing "Out of memory!" errors even with moderate data.
53+
54+
Enable the heap allocator for guests that:
55+
- Process variable-length data
56+
- Use collections (Vec, HashMap, etc.)
57+
- Perform string operations
58+
- Run iterative algorithms
59+
60+
```toml
61+
[dependencies]
62+
risc0-zkvm = { version = "1.2", default-features = false, features = ["std", "heap-embedded-alloc"] }
63+
```
64+
65+
*Note: The heap allocator adds ~5% cycle overhead but prevents memory exhaustion.*
66+
67+
### Common Guest Patterns
68+
69+
**Reading Input:**
70+
```rust
71+
// For raw bytes (most efficient, works with pyr0.prove(image, raw_bytes))
72+
use std::io::Read;
73+
let mut buffer = Vec::new();
74+
env::stdin().read_to_end(&mut buffer).unwrap();
75+
76+
// For serialized data (when using PyR0 serialization helpers)
77+
let data: MyStruct = env::read();
78+
```
79+
80+
**Writing to Journal:**
81+
```rust
82+
// Simple values
83+
env::commit(&my_u32);
84+
85+
// Complex structures (using Borsh)
86+
use borsh::BorshSerialize;
87+
let output = MyOutput { /* ... */ };
88+
env::commit_slice(&borsh::to_vec(&output).unwrap());
89+
```
90+
3291
## Installation
3392

3493
### System Requirements

demo/ed25519_demo.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142
print(f"✓ Program ID verified: {receipt_program_id[:16]}...{receipt_program_id[-16:]}")
143143

144144
# Verify the cryptographic proof
145-
receipt.verify()
145+
receipt.verify(image.id) # Pass the trusted image ID
146146
print("✓ Proof verified")
147147

148148
# Test with invalid signature
@@ -152,7 +152,7 @@
152152
input_data = serialization.ed25519_input(pk_bytes, sig_bytes, msg_bytes)
153153

154154
# For the second test, we just execute, no need for proof
155-
segments, info = pyr0.execute_with_input(image, input_data)
155+
info = pyr0.dry_run(image, input_data)
156156
journal = info.journal
157157

158158
print(f"Journal: {len(journal)} bytes")
@@ -167,13 +167,20 @@
167167

168168
import struct
169169
result = struct.unpack('<I', journal[16:20])[0]
170+
test_passed = False
170171
if result == 0:
171172
print("✅ Signature INVALID - Test PASSED")
173+
test_passed = True
172174
elif result == 1:
173175
print("❌ Signature reported as VALID - Test FAILED")
174176
else:
175177
print(f"❌ Unexpected result: {result}")
176178

177179
print("\n=== Summary ===")
178-
print("If both tests passed, the zkVM correctly validates Ed25519 signatures!")
179-
print("These are real cryptographic proofs in production mode.")
180+
if test_passed:
181+
print("✓ Both tests passed! The zkVM correctly validates Ed25519 signatures!")
182+
print("These are real cryptographic proofs in production mode.")
183+
sys.exit(0)
184+
else:
185+
print("✗ Some tests failed")
186+
sys.exit(1)

demo/merkle_demo.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Poseidon hash over BN254, compatible with Noir/Barretenberg ZK circuits.
77
"""
88

9+
import sys
910
import merkle_py
1011

1112
def test_basic_operations():
@@ -144,19 +145,26 @@ def main():
144145
print("\nThis test suite demonstrates a sparse Merkle tree implementation")
145146
print("compatible with zero-knowledge proof circuits (Noir/Barretenberg).")
146147

147-
tree = test_basic_operations()
148-
test_poseidon_hash()
149-
test_batch_operations()
150-
151-
print("\n" + "╔" + "=" * 58 + "╗")
152-
print("║" + " " * 20 + "ALL TESTS PASSED ✅" + " " * 19 + "║")
153-
print("╚" + "=" * 58 + "╝")
154-
155-
print("\nThis implementation provides:")
156-
print(" • Sparse Merkle tree supporting 2^256 keys")
157-
print(" • Poseidon hash function over BN254 field")
158-
print(" • 16-level Merkle proofs for ZK circuits")
159-
print(" • Python interface via PyO3 bindings")
148+
try:
149+
tree = test_basic_operations()
150+
test_poseidon_hash()
151+
test_batch_operations()
152+
153+
print("\n" + "╔" + "=" * 58 + "╗")
154+
print("║" + " " * 20 + "ALL TESTS PASSED ✅" + " " * 19 + "║")
155+
print("╚" + "=" * 58 + "╝")
156+
157+
print("\nThis implementation provides:")
158+
print(" • Sparse Merkle tree supporting 2^256 keys")
159+
print(" • Poseidon hash function over BN254 field")
160+
print(" • 16-level Merkle proofs for ZK circuits")
161+
print(" • Python interface via PyO3 bindings")
162+
return 0
163+
except Exception as e:
164+
print(f"\n✗ Test failed: {e}")
165+
import traceback
166+
traceback.print_exc()
167+
return 1
160168

161169
if __name__ == "__main__":
162-
main()
170+
sys.exit(main())

0 commit comments

Comments
 (0)