Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions packages/wasm-privacy-coin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,14 @@ Implements `AutoCloseable`. Always use in try-with-resources.

#### Instance methods

| Method | Returns | Description |
| ------------------------------------------------------------------------------------------------------ | ---------------- | --------------------------------------------------------------- |
| `ping()` | `void` | Verifies the WASM module is alive |
| `appendCommitments(long blockHeight, List<ShieldedCommitment> commitments)` | `ShieldedRoot` | Append cmx values, checkpoint the tree, return the new root |
| `appendCommitments(long blockHeight, List<ShieldedCommitment> commitments, ShieldedRoot expectedRoot)` | `ShieldedRoot` | Same, with optional root verification |
| `truncateToCheckpoint(long blockHeight)` | `ShieldedRoot` | Roll back to a prior checkpoint, return the root at that height |
| `save()` | `TreeState` | Serialize tree state for persistence |
| `getInfo()` | `MerkleTreeInfo` | Return tip height, leaf count, checkpoint count |
| `close()` | `void` | Drop the in-WASM tree and release the Chicory instance |
| Method | Returns | Description |
| --------------------------------------------------------------------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ping()` | `void` | Verifies the WASM module is alive |
| `appendCommitments(long blockHeight, List<ShieldedCommitment> commitments, List<Boolean> owned, ShieldedRoot expectedRoot)` | `ShieldedRoot` | Append cmx values, checkpoint the tree; `owned` marks which commitments belong to this wallet (pass `List.of()` if unused); `expectedRoot` is optional root verification (pass `null` to skip) |
| `truncateToCheckpoint(long blockHeight)` | `ShieldedRoot` | Roll back to a prior checkpoint, return the root at that height |
| `save()` | `TreeState` | Serialize tree state for persistence |
| `getInfo()` | `MerkleTreeInfo` | Return tip height, leaf count, checkpoint count |
| `close()` | `void` | Drop the in-WASM tree and release the Chicory instance |

**`blockHeight`** must be in the range `[0, 4_294_967_295]` (Rust `u32`). Passing a
negative value or a value above `0xFFFFFFFFL` throws `IllegalArgumentException`
Expand Down Expand Up @@ -275,9 +274,9 @@ ShieldedCommitment cmx = ShieldedCommitment.of(HexFormat.of().parseHex(
"0100000000000000000000000000000000000000000000000000000000000000"));

try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(savedState)) {
ShieldedRoot root = tree.appendCommitments(2_500_001L, List.of(cmx));
ShieldedRoot root = tree.appendCommitments(2_500_001L, List.of(cmx), List.of(), null);
// Empty block — still creates a checkpoint
tree.appendCommitments(2_500_002L, Collections.emptyList());
tree.appendCommitments(2_500_002L, Collections.emptyList(), List.of(), null);
}
```

Expand All @@ -287,7 +286,7 @@ try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(savedState)) {
ShieldedRoot expected = ShieldedRoot.of(expectedRootBytes);

try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(savedState)) {
ShieldedRoot root = tree.appendCommitments(2_500_001L, cmxList, expected);
ShieldedRoot root = tree.appendCommitments(2_500_001L, cmxList, List.of(), expected);
// root.equals(expected) is guaranteed
}
```
Expand All @@ -299,7 +298,7 @@ try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(savedState)) {
```java
byte[] snapshot;
try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(emptyState)) {
tree.appendCommitments(100L, List.of(cmx));
tree.appendCommitments(100L, List.of(cmx), List.of(), null);
snapshot = tree.save().bytes();
}

Expand All @@ -313,8 +312,8 @@ try (ShieldedMerkleTree restored = ShieldedMerkleTree.fromState(TreeState.of(sna

```java
try (ShieldedMerkleTree tree = ShieldedMerkleTree.fromState(savedState)) {
ShieldedRoot root100 = tree.appendCommitments(100L, List.of(cmx));
tree.appendCommitments(101L, List.of(cmx));
ShieldedRoot root100 = tree.appendCommitments(100L, List.of(cmx), List.of(), null);
tree.appendCommitments(101L, List.of(cmx), List.of(), null);

ShieldedRoot restoredRoot = tree.truncateToCheckpoint(100L);
// restoredRoot.equals(root100)
Expand Down
6 changes: 6 additions & 0 deletions packages/wasm-privacy-coin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
<version>5.10.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
<scope>test</scope>
</dependency>
Comment on lines +44 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually now you don't need this any longer

</dependencies>

<distributionManagement>
Expand Down
1 change: 1 addition & 0 deletions packages/wasm-privacy-coin/proto/privacy_coin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ message AppendCommitmentsRequest {
uint32 block_height = 1;
repeated bytes commitments = 2; // each exactly 32 bytes (cmx)
optional bytes expected_root = 3; // 32 bytes; absent = skip verification
repeated bool owned = 4; // per-commitment ownership flag; absent/empty = all not-owned
}

message TruncateRequest {
Expand Down
3 changes: 2 additions & 1 deletion packages/wasm-privacy-coin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ pub unsafe extern "C" fn append_commitments(ptr: *const u8, len: u32) -> i32 {
Some(tree) => {
let commitments: Vec<Vec<u8>> =
req.commitments.iter().map(|b| b.to_vec()).collect();
let owned = req.owned;
let exp = expected_root.as_deref();
match tree.append_commitments(req.block_height, commitments, exp) {
match tree.append_commitments(req.block_height, commitments, owned, exp) {
Ok(root) => write_ok_bytes(root),
Err(e) => {
let (code, msg) = split_error(&e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ public void ping() {
*
* @param blockHeight block height (u32 range)
* @param commitments shielded note commitment values (cmx) for this block
* @param owned per-commitment ownership flags; {@code null} or shorter list → remaining are false
* @param expectedRoot root to verify against; {@code null} to skip
* @return computed root after appending
* @throws WasmException with code {@code ROOT_MISMATCH} if verification fails
*/
public ShieldedRoot appendCommitments(
long blockHeight, List<ShieldedCommitment> commitments, ShieldedRoot expectedRoot) {
long blockHeight, List<ShieldedCommitment> commitments, List<Boolean> owned,
ShieldedRoot expectedRoot) {
requireU32(blockHeight, "blockHeight");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the 2-arg and 3-arg appendCommitments overloads is a breaking API change for any external caller. Worth calling out explicitly in the PR description (not just implied by the diff), and confirming downstream consumers are updated in lockstep.

Separately: packages/wasm-privacy-coin/README.md (lines ~163-164, 278, 280, 290, 302, 316-317) still documents and uses the old 2-arg/3-arg signatures — those examples no longer compile against this new signature and should be updated in this PR.


Generated by Claude Code

Objects.requireNonNull(commitments, "commitments must not be null");

Expand All @@ -115,6 +117,9 @@ public ShieldedRoot appendCommitments(
.addAllCommitments(commitments.stream()
.map(c -> ByteString.copyFrom(c.bytes()))
.toList());
if (owned != null && !owned.isEmpty()) {
req.addAllOwned(owned);
}
if (expectedRoot != null) {
req.setExpectedRoot(ByteString.copyFrom(expectedRoot.bytes()));
}
Expand All @@ -123,11 +128,6 @@ public ShieldedRoot appendCommitments(
return ShieldedRoot.of(unwrap(r, resp -> resp.getBytesValue().toByteArray()));
}

/** Convenience overload — appends without root verification. */
public ShieldedRoot appendCommitments(long blockHeight, List<ShieldedCommitment> commitments) {
return appendCommitments(blockHeight, commitments, null);
}

/**
* Rolls the tree back to the checkpoint at the given block height.
*
Expand Down
Loading
Loading