-
Notifications
You must be signed in to change notification settings - Fork 3
Autogenerate the activity claim's rKey #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
60c3950
feat(sdk-core): implement deterministic rKey generation for activity …
s-adamantine 6094d85
fix: limit rKey hash input to claim-relevant fields only
s-adamantine b292b51
fix: restructure create flow to embed location in claim and include i…
s-adamantine 403d31a
fix: embed contributors in claim record and include in rKey hash
s-adamantine fa74a68
fix: re-throw location errors to ensure rKey consistency across retries
s-adamantine b25245a
feat: support detailed contribution records with StrongRefs
s-adamantine ca69500
fix: remove duplicate comment in HypercertOperationsImpl.ts
s-adamantine 97fff24
docs: add missing parameters locationRef and contributorsData to crea…
s-adamantine df68722
test: add unit tests for stableStringify utility
s-adamantine 3290202
fix: sha256Hash throws ValidationError on non-serializable input
s-adamantine a8a270f
refactor: extract create method logic to helpers
s-adamantine cdd9d2b
fix: make rKey hashing explicitly deterministic by using input params
s-adamantine 3b58749
fix: normalize hashInput to use resolved StrongRefs instead of raw pa…
s-adamantine 750cd44
feat(sdk-core): support multiple locations for activity claims
aspiers fb29a89
fix(sdk-react): use HypercertProject instead of HypercertProjectWithM…
aspiers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| --- | ||
| "@hypercerts-org/sdk-core": minor | ||
| --- | ||
|
|
||
| Support multiple locations for hypercert activity claims | ||
|
|
||
| **Breaking Changes:** | ||
|
|
||
| - `CreateHypercertParams.location` is now `CreateHypercertParams.locations` (plural, array) | ||
| - `CreateHypercertResult.locationUri` is now `CreateHypercertResult.locationUris` (plural, array) | ||
| - `CreateHypercertResult.locationCid` is now `CreateHypercertResult.locationCids` (plural, array) | ||
|
|
||
| **New Functionality:** | ||
|
|
||
| - Hypercerts can now have multiple locations to support activities spanning multiple places | ||
| - Each location can be a StrongRef, string URI, or location object | ||
| - `attachLocation()` now appends to existing locations array instead of replacing | ||
|
|
||
| **Migration:** | ||
|
|
||
| ```typescript | ||
| // Before (v0.10.0-beta.5 and earlier) | ||
| await repo.hypercerts.create({ | ||
| ...params, | ||
| location: { | ||
| lpVersion: "1.0.0", | ||
| srs: "EPSG:4326", | ||
| locationType: "coordinate-decimal", | ||
| location: "https://example.com/location", | ||
| }, | ||
| }); | ||
|
|
||
| // After (v0.10.0-beta.6+) | ||
| await repo.hypercerts.create({ | ||
| ...params, | ||
| locations: [ | ||
| { | ||
| lpVersion: "1.0.0", | ||
| srs: "EPSG:4326", | ||
| locationType: "coordinate-decimal", | ||
| location: "https://example.com/location", | ||
| }, | ||
| ], | ||
| }); | ||
|
|
||
| // Now supports multiple locations | ||
| await repo.hypercerts.create({ | ||
| ...params, | ||
| locations: [ | ||
| { location: "https://example.com/location1", ... }, | ||
| { location: "https://example.com/location2", ... }, | ||
| ], | ||
| }); | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| "@hypercerts-org/sdk-core": minor | ||
| --- | ||
|
|
||
| feat: implement pre-generation of rKeys for activity claims using deterministic content hashing (SHA-256). | ||
|
|
||
| fix: normalize hashInput in `createHypercertRecord()` to use resolved StrongRefs (`locationRef`, `contributorsData`) | ||
| instead of raw params which may contain non-serializable Blobs or inconsistent formats, ensuring stable rKey generation. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /** | ||
| * Crypto utilities for the SDK. | ||
| */ | ||
|
|
||
| import { NetworkError, ValidationError } from "../core/errors.js"; | ||
|
|
||
| /** | ||
| * Deterministically stringifies an object by sorting keys recursively. | ||
| * Handles deeply nested objects and null values correctly. | ||
| */ | ||
| export function stableStringify(obj: unknown): string | undefined { | ||
| if (obj === undefined || typeof obj === "function" || typeof obj === "symbol") { | ||
| return undefined; | ||
| } | ||
| if (obj === null || typeof obj !== "object") { | ||
| return JSON.stringify(obj); | ||
| } | ||
|
|
||
| if (Array.isArray(obj)) { | ||
| return JSON.stringify(obj.map((item) => { | ||
| const val = stableStringify(item); | ||
| return val === undefined ? null : JSON.parse(val); | ||
| })); | ||
| } | ||
|
|
||
| const sortedKeys = Object.keys(obj as object).sort(); | ||
| const sortedObj: Record<string, unknown> = {}; | ||
|
|
||
| for (const key of sortedKeys) { | ||
| const value = (obj as Record<string, unknown>)[key]; | ||
| const str = stableStringify(value); | ||
| // Skip undefined or non-serializable values | ||
| if (str === undefined) continue; | ||
| sortedObj[key] = JSON.parse(str); | ||
| } | ||
|
|
||
| return JSON.stringify(sortedObj); | ||
| } | ||
|
|
||
| /** | ||
| * Computes the SHA-256 hash of a JSON-serializable object. | ||
| * Returns the hash as a hexadecimal string. | ||
| * | ||
| * @param content - The content to hash (will be JSON serialized) | ||
| * @returns The SHA-256 hash of the content | ||
| * @throws {ValidationError} If content is not serializable (e.g. undefined, function, symbol) | ||
| */ | ||
| export async function sha256Hash(content: unknown): Promise<string> { | ||
| // Use stable stringification to ensure deterministic output | ||
| const jsonString = stableStringify(content); | ||
|
|
||
| if (jsonString === undefined) { | ||
| throw new ValidationError(`Content illegal: not serializable (type: ${typeof content})`); | ||
| } | ||
|
|
||
| const msgBuffer = new TextEncoder().encode(jsonString); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| if (typeof crypto !== "undefined" && crypto.subtle) { | ||
| // Browser / Modern Node.js | ||
| const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer); | ||
| const hashArray = Array.from(new Uint8Array(hashBuffer)); | ||
| return hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); | ||
| } else { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| // Fallback for older environments or specific setups if global crypto isn't available | ||
| try { | ||
| // Dynamic import to avoid breaking browser builds if bundler doesn't handle it | ||
|
|
||
| const { createHash } = await import("node:crypto"); | ||
| const hash = createHash("sha256").update(jsonString).digest("hex"); | ||
| return hash; | ||
| } catch (e) { | ||
| throw new NetworkError("SHA-256 hashing not supported in this environment", e); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a 0.x breaking-change note to the changeset.
Since
@hypercerts-org/sdk-coreis still 0.x, this behavioral change (deterministic rKeys replacing server-assigned keys) can be breaking for clients depending on random rKeys or duplicate-creation behavior. Please call that out explicitly. Based on learnings.✍️ Suggested wording update
feat: implement pre-generation of rKeys for activity claims using deterministic content hashing (SHA-256). +Note: `@hypercerts-org/sdk-core` is 0.x; this changes rKey generation from server-assigned keys to deterministic `hc:<hash>`, which may affect clients relying on random rKeys or duplicate-creation behavior.📝 Committable suggestion
🤖 Prompt for AI Agents