Skip to content

Commit 8edf340

Browse files
authored
chore(vapp): unit tests (#71)
* chore(vapp): unit tests * chore: unit tests * more unit tests * fix stuff * rm spec * merge main * fix utils * fmt * state
1 parent 975a92f commit 8edf340

File tree

12 files changed

+4791
-850
lines changed

12 files changed

+4791
-850
lines changed

crates/vapp/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub enum VAppPanic {
4949
OnlyOwnerCanDelegate,
5050

5151
#[error("Request id mismatch in clear")]
52-
RequestIdMismatch { found: Address, expected: Address },
52+
RequestIdMismatch { found: Vec<u8>, expected: Vec<u8> },
5353

5454
#[error("Invalid bid amount in clear: {amount}")]
5555
InvalidU256Amount { amount: String },

crates/vapp/src/state.rs

Lines changed: 10 additions & 587 deletions
Large diffs are not rendered by default.

crates/vapp/src/transactions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ pub struct ClearTransaction {
9393
pub fulfill: Option<FulfillProofRequest>,
9494
/// The verifier signature.
9595
pub verify: Option<Vec<u8>>,
96-
/// The vk request.
96+
/// The verifying key.
97+
///
98+
/// Note: This is only used as a hint outside the zkVM so that we can get the verifying key.
99+
#[serde(skip)]
97100
pub vk: Option<Vec<u8>>,
98101
}

crates/vapp/src/utils.rs

Lines changed: 0 additions & 261 deletions
Original file line numberDiff line numberDiff line change
@@ -21,264 +21,3 @@ pub fn bytes_to_words_be(bytes: &[u8; 32]) -> Result<[u32; 8], VAppPanic> {
2121
pub fn address(bytes: &[u8]) -> Result<Address, VAppPanic> {
2222
Address::try_from(bytes).map_err(|_| VAppPanic::AddressDeserializationFailed)
2323
}
24-
25-
/// Test utilities for creating and verifying signatures.
26-
#[cfg(test)]
27-
#[allow(clippy::missing_panics_doc)]
28-
pub mod tests {
29-
/// Signing utilities for creating and verifying signatures.
30-
///
31-
/// This module provides helper functions for creating signers, signing protobuf messages,
32-
/// and building `VApp` events for testing and network operations.
33-
#[cfg(any(test, feature = "network"))]
34-
pub mod signers {
35-
use alloy::signers::{local::PrivateKeySigner, SignerSync};
36-
use alloy_primitives::{keccak256, Signature, U256};
37-
use prost::Message;
38-
use spn_network_types::{
39-
BidRequest, BidRequestBody, ExecuteProofRequest, ExecuteProofRequestBody,
40-
ExecutionStatus, FulfillProofRequest, FulfillProofRequestBody, HashableWithSender,
41-
MessageFormat, RequestProofRequest, RequestProofRequestBody, SetDelegationRequest,
42-
SetDelegationRequestBody, SettleRequest, SettleRequestBody,
43-
};
44-
use spn_utils::SPN_SEPOLIA_V1_DOMAIN;
45-
46-
use crate::transactions::{ClearTransaction, DelegateTransaction, VAppTransaction};
47-
use alloy_primitives::Address;
48-
49-
/// Creates a signer from a string key.
50-
///
51-
/// The key is hashed using keccak256 to generate the private key.
52-
#[must_use]
53-
pub fn signer(key: &str) -> PrivateKeySigner {
54-
PrivateKeySigner::from_bytes(&keccak256(key)).unwrap()
55-
}
56-
57-
/// Signs a protobuf message using a signer.
58-
///
59-
/// The message is encoded to bytes and then signed using the provided signer.
60-
pub fn proto_sign<T: Message>(signer: &PrivateKeySigner, message: &T) -> Signature {
61-
let mut buf = Vec::new();
62-
message.encode(&mut buf).unwrap();
63-
signer.sign_message_sync(&buf).unwrap()
64-
}
65-
66-
/// Builds a complete [`VAppEvent::Clear`] for testing.
67-
///
68-
/// This function creates a full clear event with all required signatures and data,
69-
/// including request, bid, fulfill, and execute components.
70-
#[allow(clippy::too_many_arguments)]
71-
pub fn clear_vapp_event(
72-
requester: &PrivateKeySigner,
73-
prover: &PrivateKeySigner,
74-
delegated_prover: &PrivateKeySigner,
75-
settle_signer: &PrivateKeySigner,
76-
executor: &PrivateKeySigner,
77-
verifier: &PrivateKeySigner,
78-
request: RequestProofRequestBody,
79-
bid_nonce: u64,
80-
bid_amount: U256,
81-
settle_nonce: u64,
82-
fulfill_nonce: u64,
83-
proof: Vec<u8>,
84-
execute_nonce: u64,
85-
execution_status: ExecutionStatus,
86-
_vk_digest_array: Option<[u32; 8]>,
87-
pv_digest_array: Option<[u8; 32]>,
88-
) -> VAppTransaction {
89-
// Generate request ID and signature.
90-
let request_id = request.hash_with_signer(requester.address().as_ref()).unwrap();
91-
let request_signature = proto_sign(requester, &request);
92-
93-
// Create and sign bid.
94-
let bid = BidRequestBody {
95-
nonce: bid_nonce,
96-
request_id: request_id.to_vec(),
97-
amount: bid_amount.to_string(),
98-
domain: SPN_SEPOLIA_V1_DOMAIN.to_vec(),
99-
prover: prover.address().to_vec(),
100-
};
101-
let bid_signature = proto_sign(delegated_prover, &bid);
102-
103-
// Create and sign settle.
104-
let settle = SettleRequestBody {
105-
nonce: settle_nonce,
106-
request_id: request_id.to_vec(),
107-
winner: prover.address().to_vec(),
108-
domain: SPN_SEPOLIA_V1_DOMAIN.to_vec(),
109-
};
110-
let settle_signature = proto_sign(settle_signer, &settle);
111-
112-
// Create and sign fulfill.
113-
let fulfill = FulfillProofRequestBody {
114-
nonce: fulfill_nonce,
115-
request_id: request_id.to_vec(),
116-
proof,
117-
reserved_metadata: None,
118-
domain: spn_utils::SPN_SEPOLIA_V1_DOMAIN.to_vec(),
119-
};
120-
let fulfill_signature = proto_sign(delegated_prover, &fulfill);
121-
122-
// Create and sign execute.
123-
let execute = ExecuteProofRequestBody {
124-
nonce: execute_nonce,
125-
request_id: request_id.to_vec(),
126-
execution_status: execution_status.into(),
127-
public_values_hash: Some(
128-
pv_digest_array.map(|arr| arr.to_vec()).unwrap_or(vec![0; 32]),
129-
),
130-
cycles: None,
131-
pgus: Some(1),
132-
domain: spn_utils::SPN_SEPOLIA_V1_DOMAIN.to_vec(),
133-
punishment: None,
134-
failure_cause: None,
135-
};
136-
let execute_signature = proto_sign(executor, &execute);
137-
138-
// Sign the request ID with the verifier's private key.
139-
let fulfill_id = fulfill.hash_with_signer(delegated_prover.address().as_ref()).unwrap();
140-
let verifier_signature = verifier.sign_message_sync(&fulfill_id).unwrap();
141-
142-
VAppTransaction::Clear(ClearTransaction {
143-
request: RequestProofRequest {
144-
format: MessageFormat::Binary.into(),
145-
body: Some(request),
146-
signature: request_signature.as_bytes().to_vec(),
147-
},
148-
bid: BidRequest {
149-
format: MessageFormat::Binary.into(),
150-
body: Some(bid),
151-
signature: bid_signature.as_bytes().to_vec(),
152-
},
153-
settle: SettleRequest {
154-
format: MessageFormat::Binary.into(),
155-
body: Some(settle),
156-
signature: settle_signature.as_bytes().to_vec(),
157-
},
158-
fulfill: Some(FulfillProofRequest {
159-
format: MessageFormat::Binary.into(),
160-
body: Some(fulfill),
161-
signature: fulfill_signature.as_bytes().to_vec(),
162-
}),
163-
execute: ExecuteProofRequest {
164-
format: MessageFormat::Binary.into(),
165-
body: Some(execute),
166-
signature: execute_signature.as_bytes().to_vec(),
167-
},
168-
verify: Some(verifier_signature.as_bytes().to_vec()),
169-
vk: None,
170-
})
171-
}
172-
173-
/// Builds a complete [`VAppEvent::Delegate`] for testing.
174-
///
175-
/// This function creates a delegation event with proper signature verification,
176-
/// simulating the process where a prover owner delegates authority to another account.
177-
pub fn delegate_vapp_event(
178-
prover_owner: &PrivateKeySigner,
179-
prover_address: Address,
180-
delegate_address: Address,
181-
nonce: u64,
182-
) -> VAppTransaction {
183-
// Create the delegation request body.
184-
let body = SetDelegationRequestBody {
185-
nonce,
186-
delegate: delegate_address.to_vec(),
187-
domain: SPN_SEPOLIA_V1_DOMAIN.to_vec(),
188-
prover: prover_address.to_vec(),
189-
};
190-
191-
// Sign the delegation request with the prover owner's key.
192-
let signature = proto_sign(prover_owner, &body);
193-
194-
VAppTransaction::Delegate(DelegateTransaction {
195-
delegation: SetDelegationRequest {
196-
format: MessageFormat::Binary.into(),
197-
body: Some(body),
198-
signature: signature.as_bytes().to_vec(),
199-
},
200-
})
201-
}
202-
}
203-
204-
/// Test utilities for setting up test environments.
205-
///
206-
/// This module provides common test setup functions and utilities for creating
207-
/// test states, domains, and signers.
208-
#[cfg(any(test, feature = "network"))]
209-
pub mod test_utils {
210-
use std::time::{SystemTime, UNIX_EPOCH};
211-
212-
use alloy::signers::local::PrivateKeySigner;
213-
use alloy_primitives::Address;
214-
use spn_utils::SPN_SEPOLIA_V1_DOMAIN;
215-
216-
use crate::{merkle::MerkleStorage, sol::Account, state::VAppState, storage::RequestId};
217-
218-
use super::signers::signer;
219-
220-
/// Test environment containing state, domain, and signers.
221-
pub struct TestEnvironment {
222-
/// The state of the vApp.
223-
pub state: VAppState<MerkleStorage<Address, Account>, MerkleStorage<RequestId, bool>>,
224-
/// The auctioneer signer.
225-
pub auctioneer: PrivateKeySigner,
226-
/// The executor signer.
227-
pub executor: PrivateKeySigner,
228-
/// The verifier signer.
229-
pub verifier: PrivateKeySigner,
230-
/// The requester signer.
231-
pub requester: PrivateKeySigner,
232-
/// The prover signer.
233-
pub prover: PrivateKeySigner,
234-
/// The signers for the test environment.
235-
pub signers: Vec<PrivateKeySigner>,
236-
}
237-
238-
/// Gets the current timestamp as seconds since Unix epoch.
239-
#[must_use]
240-
pub fn timestamp() -> i64 {
241-
SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_secs()
242-
as i64
243-
}
244-
245-
/// Sets up a test environment with initialized state and signers.
246-
///
247-
/// Creates a new state with a local domain and 10 test signers.
248-
#[must_use]
249-
pub fn setup() -> TestEnvironment {
250-
let domain = *SPN_SEPOLIA_V1_DOMAIN;
251-
let treasury = signer("treasury");
252-
let auctioneer = signer("auctioneer");
253-
let executor = signer("executor");
254-
let verifier = signer("verifier");
255-
let state = VAppState::new(
256-
domain,
257-
treasury.address(),
258-
auctioneer.address(),
259-
executor.address(),
260-
verifier.address(),
261-
);
262-
TestEnvironment {
263-
state,
264-
auctioneer,
265-
executor,
266-
verifier,
267-
requester: signer("requester"),
268-
prover: signer("prover"),
269-
signers: vec![
270-
signer("1"),
271-
signer("2"),
272-
signer("3"),
273-
signer("4"),
274-
signer("5"),
275-
signer("6"),
276-
signer("7"),
277-
signer("8"),
278-
signer("9"),
279-
signer("10"),
280-
],
281-
}
282-
}
283-
}
284-
}

crates/vapp/src/verifier.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,17 @@ impl VAppVerifier for MockVerifier {
3535
Ok(())
3636
}
3737
}
38+
39+
/// A reject verifier for testing.
40+
#[derive(Debug, Clone, Default)]
41+
pub struct RejectVerifier;
42+
43+
impl VAppVerifier for RejectVerifier {
44+
fn verify(
45+
&self,
46+
_vk_digest_array: [u32; 8],
47+
_pv_digest_array: [u8; 32],
48+
) -> Result<(), VAppVerifierError> {
49+
Err(VAppVerifierError::InvalidProof)
50+
}
51+
}

0 commit comments

Comments
 (0)