Skip to content

Commit 33eb19a

Browse files
more work
1 parent 09ea11a commit 33eb19a

File tree

8 files changed

+913
-89
lines changed

8 files changed

+913
-89
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# verify_elements Function in WASM
2+
3+
## Summary
4+
5+
The `verify_elements` function from Drive cannot be fully implemented in the WASM environment due to fundamental architectural limitations.
6+
7+
## The Issue
8+
9+
1. **Element Type Not Exposed**: The `grovedb::Element` enum is only available when the "server" feature is enabled, not with the "verify" feature that wasm-drive-verify uses.
10+
11+
2. **Security by Design**: This limitation appears intentional - the verify feature is designed to work with serialized data only, preventing exposure of internal tree structures across the WASM boundary.
12+
13+
3. **Type Dependencies**: The Element enum contains variants like:
14+
- `Item(Vec<u8>)` - raw data
15+
- `Tree(Option<Vec<u8>>)` - tree reference
16+
- `SumTree(Option<Vec<u8>>, i64)` - sum tree with value
17+
- `SumItem(Vec<u8>, i64)` - item with sum
18+
19+
These types reference internal GroveDB structures that cannot be safely exposed in WASM.
20+
21+
## Alternative Approaches
22+
23+
Instead of `verify_elements`, use the specialized verification functions:
24+
25+
### 1. For Documents
26+
```rust
27+
// Use DriveDocumentQuery
28+
let query = DriveDocumentQuery::new(...);
29+
let (root_hash, documents) = query.verify_proof_keep_serialized(proof, platform_version)?;
30+
```
31+
32+
### 2. For Identities
33+
```rust
34+
// Use identity-specific functions
35+
let (root_hash, identity) = verify_full_identity_by_identity_id(proof, identity_id, platform_version)?;
36+
```
37+
38+
### 3. For Contracts
39+
```rust
40+
// Use contract verification
41+
let (root_hash, contract) = verify_contract(proof, contract_id, platform_version)?;
42+
```
43+
44+
## Current Implementation
45+
46+
The `verify_elements` function in wasm-drive-verify returns an error explaining this limitation and directing users to the appropriate alternative functions.
47+
48+
## Future Considerations
49+
50+
If raw element access is absolutely needed in WASM:
51+
52+
1. **Custom Serialization**: Implement a custom serialization format for Elements on the server side that can be deserialized in WASM
53+
2. **Proof Format Changes**: Modify the proof format to include pre-serialized elements
54+
3. **Feature Flag**: Add a new feature flag that exposes a limited, WASM-safe version of Element
55+
56+
However, these approaches would require significant changes to the core Drive/GroveDB architecture and may compromise the security model.

packages/wasm-drive-verify/src/group/verify_active_action_infos.rs

Lines changed: 470 additions & 8 deletions
Large diffs are not rendered by default.

packages/wasm-drive-verify/src/identity/verify_identity_keys_by_identity_id.rs

Lines changed: 113 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use dpp::identity::identity_public_key::IdentityPublicKey;
12
use dpp::identity::PartialIdentity;
23
use dpp::version::PlatformVersion;
34
use drive::drive::identity::key::fetch::{IdentityKeysRequest, KeyRequestType};
@@ -6,7 +7,7 @@ use js_sys::{Array, Object, Reflect, Uint8Array};
67
use wasm_bindgen::prelude::*;
78

89
// Helper function to convert PartialIdentity to JS object
9-
fn partial_identity_to_js(identity: &PartialIdentity) -> Result<JsValue, JsValue> {
10+
pub fn partial_identity_to_js(identity: &PartialIdentity) -> Result<JsValue, JsValue> {
1011
let obj = Object::new();
1112

1213
// Set id
@@ -19,22 +20,18 @@ fn partial_identity_to_js(identity: &PartialIdentity) -> Result<JsValue, JsValue
1920
for (key_id, _public_key) in &identity.loaded_public_keys {
2021
let key_obj = Object::new();
2122

22-
// Set key properties
23-
Reflect::set(
24-
&key_obj,
25-
&JsValue::from_str("id"),
26-
&JsValue::from_str(&key_id.to_string()),
27-
)
28-
.map_err(|_| JsValue::from_str("Failed to set key id"))?;
29-
30-
// For now, we'll add a placeholder for the full key data
31-
// TODO: Implement full IdentityPublicKey serialization
32-
Reflect::set(
33-
&key_obj,
34-
&JsValue::from_str("data"),
35-
&JsValue::from_str("[Key data not yet implemented]"),
36-
)
37-
.map_err(|_| JsValue::from_str("Failed to set key data"))?;
23+
// Serialize the full IdentityPublicKey
24+
let serialized_key = serialize_identity_public_key(_public_key)?;
25+
26+
// Merge the serialized key properties into the key object
27+
let key_keys = Object::keys(&serialized_key);
28+
for i in 0..key_keys.length() {
29+
let prop_name = key_keys.get(i);
30+
let prop_value = Reflect::get(&serialized_key, &prop_name)
31+
.map_err(|_| JsValue::from_str("Failed to get key property"))?;
32+
Reflect::set(&key_obj, &prop_name, &prop_value)
33+
.map_err(|_| JsValue::from_str("Failed to set key property"))?;
34+
}
3835

3936
Reflect::set(&keys_obj, &JsValue::from_str(&key_id.to_string()), &key_obj)
4037
.map_err(|_| JsValue::from_str("Failed to set key in map"))?;
@@ -174,3 +171,102 @@ pub fn verify_identity_keys_by_identity_id(
174171
identity: identity_js,
175172
})
176173
}
174+
175+
// Helper function to serialize IdentityPublicKey to JS object
176+
fn serialize_identity_public_key(key: &IdentityPublicKey) -> Result<Object, JsValue> {
177+
let obj = Object::new();
178+
179+
match key {
180+
IdentityPublicKey::V0(key_v0) => {
181+
// Set id
182+
Reflect::set(&obj, &JsValue::from_str("id"), &JsValue::from(key_v0.id))
183+
.map_err(|_| JsValue::from_str("Failed to set key id"))?;
184+
185+
// Set purpose (as number)
186+
Reflect::set(
187+
&obj,
188+
&JsValue::from_str("purpose"),
189+
&JsValue::from(key_v0.purpose as u8),
190+
)
191+
.map_err(|_| JsValue::from_str("Failed to set purpose"))?;
192+
193+
// Set security level (as number)
194+
Reflect::set(
195+
&obj,
196+
&JsValue::from_str("securityLevel"),
197+
&JsValue::from(key_v0.security_level as u8),
198+
)
199+
.map_err(|_| JsValue::from_str("Failed to set security level"))?;
200+
201+
// Set contract bounds (optional)
202+
match &key_v0.contract_bounds {
203+
Some(bounds) => {
204+
let bounds_obj = Object::new();
205+
match bounds {
206+
dpp::identity::identity_public_key::contract_bounds::ContractBounds::SingleContract { id } => {
207+
Reflect::set(&bounds_obj, &JsValue::from_str("type"), &JsValue::from_str("SingleContract"))
208+
.map_err(|_| JsValue::from_str("Failed to set bounds type"))?;
209+
let id_array = Uint8Array::from(id.as_slice());
210+
Reflect::set(&bounds_obj, &JsValue::from_str("id"), &id_array)
211+
.map_err(|_| JsValue::from_str("Failed to set bounds id"))?;
212+
}
213+
dpp::identity::identity_public_key::contract_bounds::ContractBounds::SingleContractDocumentType { id, document_type_name } => {
214+
Reflect::set(&bounds_obj, &JsValue::from_str("type"), &JsValue::from_str("SingleContractDocumentType"))
215+
.map_err(|_| JsValue::from_str("Failed to set bounds type"))?;
216+
let id_array = Uint8Array::from(id.as_slice());
217+
Reflect::set(&bounds_obj, &JsValue::from_str("id"), &id_array)
218+
.map_err(|_| JsValue::from_str("Failed to set bounds id"))?;
219+
Reflect::set(&bounds_obj, &JsValue::from_str("documentTypeName"), &JsValue::from_str(document_type_name))
220+
.map_err(|_| JsValue::from_str("Failed to set document type name"))?;
221+
}
222+
}
223+
Reflect::set(&obj, &JsValue::from_str("contractBounds"), &bounds_obj)
224+
.map_err(|_| JsValue::from_str("Failed to set contract bounds"))?;
225+
}
226+
None => {
227+
Reflect::set(&obj, &JsValue::from_str("contractBounds"), &JsValue::NULL)
228+
.map_err(|_| JsValue::from_str("Failed to set contract bounds to null"))?;
229+
}
230+
}
231+
232+
// Set key type (as number)
233+
Reflect::set(
234+
&obj,
235+
&JsValue::from_str("type"),
236+
&JsValue::from(key_v0.key_type as u8),
237+
)
238+
.map_err(|_| JsValue::from_str("Failed to set key type"))?;
239+
240+
// Set read only flag
241+
Reflect::set(
242+
&obj,
243+
&JsValue::from_str("readOnly"),
244+
&JsValue::from_bool(key_v0.read_only),
245+
)
246+
.map_err(|_| JsValue::from_str("Failed to set read only"))?;
247+
248+
// Set key data (as Uint8Array)
249+
let data_array = Uint8Array::from(key_v0.data.as_slice());
250+
Reflect::set(&obj, &JsValue::from_str("data"), &data_array)
251+
.map_err(|_| JsValue::from_str("Failed to set key data"))?;
252+
253+
// Set disabled_at (optional timestamp)
254+
match key_v0.disabled_at {
255+
Some(timestamp) => {
256+
Reflect::set(
257+
&obj,
258+
&JsValue::from_str("disabledAt"),
259+
&JsValue::from_str(&timestamp.to_string()),
260+
)
261+
.map_err(|_| JsValue::from_str("Failed to set disabled at"))?;
262+
}
263+
None => {
264+
Reflect::set(&obj, &JsValue::from_str("disabledAt"), &JsValue::NULL)
265+
.map_err(|_| JsValue::from_str("Failed to set disabled at to null"))?;
266+
}
267+
}
268+
}
269+
}
270+
271+
Ok(obj)
272+
}

packages/wasm-drive-verify/src/state_transition/verify_state_transition_was_executed_with_proof.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ use dpp::version::PlatformVersion;
77
use drive::drive::Drive;
88
use drive::query::ContractLookupFn;
99
use js_sys::{Object, Reflect, Uint8Array};
10-
use serde_wasm_bindgen::from_value;
10+
use serde_wasm_bindgen::{from_value, to_value};
1111
use std::collections::HashMap;
1212
use std::sync::Arc;
1313
use wasm_bindgen::prelude::*;
1414

15+
// Import the partial identity serialization function from the identity module
16+
use crate::identity::verify_identity_keys_by_identity_id::partial_identity_to_js;
17+
1518
#[wasm_bindgen]
1619
pub struct VerifyStateTransitionWasExecutedWithProofResult {
1720
root_hash: Vec<u8>,
@@ -144,13 +147,11 @@ fn convert_proof_result_to_js(
144147
)
145148
.map_err(|_| JsValue::from_str("Failed to set type"))?;
146149

147-
// TODO: Add serialization of the data contract
148-
Reflect::set(
149-
&obj,
150-
&JsValue::from_str("dataContract"),
151-
&JsValue::from_str("Data contract serialization not yet implemented"),
152-
)
153-
.map_err(|_| JsValue::from_str("Failed to set dataContract"))?;
150+
let contract_js = to_value(_contract).map_err(|e| {
151+
JsValue::from_str(&format!("Failed to serialize data contract: {:?}", e))
152+
})?;
153+
Reflect::set(&obj, &JsValue::from_str("dataContract"), &contract_js)
154+
.map_err(|_| JsValue::from_str("Failed to set dataContract"))?;
154155
}
155156
StateTransitionProofResult::VerifiedIdentity(_identity) => {
156157
Reflect::set(
@@ -160,13 +161,11 @@ fn convert_proof_result_to_js(
160161
)
161162
.map_err(|_| JsValue::from_str("Failed to set type"))?;
162163

163-
// TODO: Add serialization of the identity
164-
Reflect::set(
165-
&obj,
166-
&JsValue::from_str("identity"),
167-
&JsValue::from_str("Identity serialization not yet implemented"),
168-
)
169-
.map_err(|_| JsValue::from_str("Failed to set identity"))?;
164+
let identity_js = to_value(_identity).map_err(|e| {
165+
JsValue::from_str(&format!("Failed to serialize identity: {:?}", e))
166+
})?;
167+
Reflect::set(&obj, &JsValue::from_str("identity"), &identity_js)
168+
.map_err(|_| JsValue::from_str("Failed to set identity"))?;
170169
}
171170
StateTransitionProofResult::VerifiedDocuments(_documents) => {
172171
Reflect::set(
@@ -176,13 +175,11 @@ fn convert_proof_result_to_js(
176175
)
177176
.map_err(|_| JsValue::from_str("Failed to set type"))?;
178177

179-
// TODO: Add serialization of the documents
180-
Reflect::set(
181-
&obj,
182-
&JsValue::from_str("documents"),
183-
&JsValue::from_str("Documents serialization not yet implemented"),
184-
)
185-
.map_err(|_| JsValue::from_str("Failed to set documents"))?;
178+
let documents_js = to_value(_documents).map_err(|e| {
179+
JsValue::from_str(&format!("Failed to serialize documents: {:?}", e))
180+
})?;
181+
Reflect::set(&obj, &JsValue::from_str("documents"), &documents_js)
182+
.map_err(|_| JsValue::from_str("Failed to set documents"))?;
186183
}
187184
StateTransitionProofResult::VerifiedPartialIdentity(_partial_identity) => {
188185
Reflect::set(
@@ -192,11 +189,11 @@ fn convert_proof_result_to_js(
192189
)
193190
.map_err(|_| JsValue::from_str("Failed to set type"))?;
194191

195-
// TODO: Add serialization of the partial identity
192+
let partial_identity_js = partial_identity_to_js(_partial_identity)?;
196193
Reflect::set(
197194
&obj,
198195
&JsValue::from_str("partialIdentity"),
199-
&JsValue::from_str("Partial identity serialization not yet implemented"),
196+
&partial_identity_js,
200197
)
201198
.map_err(|_| JsValue::from_str("Failed to set partialIdentity"))?;
202199
}

packages/wasm-drive-verify/src/system/verify_elements.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Element type is not exposed through drive's verify feature
2-
// We'll work with the serialized format instead
2+
// This is a placeholder implementation that demonstrates the limitation
33
use js_sys::{Array, Uint8Array};
44
use wasm_bindgen::prelude::*;
55

@@ -22,16 +22,36 @@ impl VerifyElementsResult {
2222
}
2323
}
2424

25+
/// Verifies elements at a specific path with given keys
26+
///
27+
/// **Note**: This function is currently not fully implemented due to limitations in the
28+
/// WASM environment. The Element type from grovedb is not exposed through the verify
29+
/// feature, making it impossible to properly serialize and return element data.
30+
///
31+
/// For document verification, please use the document-specific verification functions
32+
/// such as `verify_proof_keep_serialized` which are designed to work within these
33+
/// limitations.
34+
///
35+
/// # Alternative Approaches:
36+
///
37+
/// 1. For document queries: Use `DriveDocumentQuery.verify_proof_keep_serialized()`
38+
/// 2. For identity queries: Use the identity-specific verification functions
39+
/// 3. For contract queries: Use `verify_contract()`
40+
///
41+
/// This limitation exists because:
42+
/// - The Element enum from grovedb contains references to internal tree structures
43+
/// - These structures cannot be safely exposed across the WASM boundary
44+
/// - The verify feature intentionally excludes server-side types for security
2545
#[wasm_bindgen(js_name = "verifyElements")]
2646
pub fn verify_elements(
2747
_proof: &Uint8Array,
2848
_path: &Array,
2949
_keys: &Array,
3050
_platform_version_number: u32,
3151
) -> Result<VerifyElementsResult, JsValue> {
32-
// This function requires Element type from grovedb which is not available in wasm context
33-
// TODO: Implement a version that works with serialized elements
3452
Err(JsValue::from_str(
35-
"verify_elements is not yet implemented for WASM due to grovedb dependency",
53+
"verify_elements is not available in WASM due to grovedb Element type limitations. \
54+
Please use document, identity, or contract-specific verification functions instead. \
55+
See the function documentation for alternatives.",
3656
))
3757
}

0 commit comments

Comments
 (0)