From b551837ee16883307b072dbc430672cfe2af1c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Hern=C3=A1ndez?= Date: Tue, 21 Jan 2025 19:13:22 +0000 Subject: [PATCH 1/5] test: update a signature test to use UpdatePermission instead of ValuesPermission --- client-vms/tests/signature.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/client-vms/tests/signature.test.ts b/client-vms/tests/signature.test.ts index c3702d6..04a0e2c 100644 --- a/client-vms/tests/signature.test.ts +++ b/client-vms/tests/signature.test.ts @@ -8,7 +8,7 @@ import { type Uuid, ValuesPermissionsBuilder, } from "#/types"; -import { type VmClient, VmClientBuilder } from "#/vm"; +import { UpdatePermissionsBuilder, type VmClient, VmClientBuilder } from "#/vm"; import { Env, PrivateKeyPerSuite } from "./helpers"; describe("Signature", () => { @@ -44,19 +44,23 @@ describe("Signature", () => { let privateKeyStoreId: Uuid; it("store private key", async () => { - const permissions = ValuesPermissionsBuilder.defaultOwner(client.id) - .grantCompute(client.id, tecdsaProgramId) - .build(); privateKeyStoreId = await client .storeValues() .ttl(1) .value(tecdsaKeyName, NadaValue.new_ecdsa_private_key(privateKey)) - .permissions(permissions) .build() .invoke(); expect(privateKeyStoreId).toHaveLength(36); }); + it("update private key permissions", async () => { + const permissions = UpdatePermissionsBuilder.init(client) + .valuesId(privateKeyStoreId) + .grantCompute(client.id, tecdsaProgramId) + .build(); + await permissions.invoke(); + }); + it("retrieve private key", async () => { const data = await client .retrieveValues() From b67287443f2372423dfba420a524799f586b8809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Hern=C3=A1ndez?= Date: Tue, 21 Jan 2025 19:14:18 +0000 Subject: [PATCH 2/5] fix: assign store id when use update permissions --- client-react-hooks/src/use-nil-update-permissions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client-react-hooks/src/use-nil-update-permissions.ts b/client-react-hooks/src/use-nil-update-permissions.ts index b4cbc25..353e120 100644 --- a/client-react-hooks/src/use-nil-update-permissions.ts +++ b/client-react-hooks/src/use-nil-update-permissions.ts @@ -3,6 +3,7 @@ import { useMutation } from "@tanstack/react-query"; import { type UseNilHook, nilHookBaseResult } from "./nil-hook-base"; interface ExecuteArgs { + id?: Uuid; permissions: UpdatePermissionsBuilder; } type ExecuteResult = Uuid; @@ -11,7 +12,7 @@ type useNilUpdatePermissions = UseNilHook; export const useNilUpdatePermissions = (): useNilUpdatePermissions => { const mutationFn = async (args: ExecuteArgs): Promise => { - return args.permissions.build().invoke(); + return args.permissions.valuesId(args.id).build().invoke(); }; const mutate = useMutation({ From f63a76f8fc564aea8f7f334d16451b68e9c51475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Hern=C3=A1ndez?= Date: Tue, 21 Jan 2025 19:15:59 +0000 Subject: [PATCH 3/5] chore: add examples for signatures --- examples-nextjs/app/components/ecdsa-sign.tsx | 57 ++++++++++++++++ .../app/components/retrieve-values.tsx | 7 +- .../app/components/store-ecdsa-values.tsx | 65 +++++++++++++++++++ .../components/update-ecdsa-permissions.tsx | 49 ++++++++++++++ examples-nextjs/app/page.tsx | 6 ++ examples-nextjs/package.json | 3 + pnpm-lock.yaml | 21 +++++- 7 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 examples-nextjs/app/components/ecdsa-sign.tsx create mode 100644 examples-nextjs/app/components/store-ecdsa-values.tsx create mode 100644 examples-nextjs/app/components/update-ecdsa-permissions.tsx diff --git a/examples-nextjs/app/components/ecdsa-sign.tsx b/examples-nextjs/app/components/ecdsa-sign.tsx new file mode 100644 index 0000000..50206cd --- /dev/null +++ b/examples-nextjs/app/components/ecdsa-sign.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useNilInvokeCompute, useNillion } from "@nillion/client-react-hooks"; +import { Uuid } from "@nillion/client-vms"; +import { type ChangeEvent, type FC, useState } from "react"; + +export const EcdsaSign: FC = () => { + const { client } = useNillion(); + const mutation = useNilInvokeCompute(); + const [id, setId] = useState(""); + const isValidUuid = Uuid.safeParse(id).success; + + const tecdsaProgramId = "builtin/tecdsa_sign"; + + function handleChange(event: ChangeEvent): void { + setId(event.target.value); + } + + function handleClick(): void { + // Assumes addition_division.py + const options = { + programId: tecdsaProgramId, + inputBindings: [ + { party: "tecdsa_key_party", user: client.id }, + { party: "tecdsa_digest_message_party", user: client.id }, + ], + outputBindings: [{ party: "tecdsa_output_party", users: [client.id] }], + valueIds: [id], + computeTimeValues: [], + }; + mutation.execute(options); + } + + let resultId = ""; + if (mutation.isSuccess) { + resultId = mutation.data; + } else if (mutation.isError) { + resultId = mutation.error.message; + } + + return ( +
+

Invoke Compute

+
    +
  1. Status: {mutation.status}
  2. +
  3. Program id: {tecdsaProgramId}
  4. +
  5. + Id: +
  6. +
  7. Compute result id: {resultId}
  8. +
+ +
+ ); +}; diff --git a/examples-nextjs/app/components/retrieve-values.tsx b/examples-nextjs/app/components/retrieve-values.tsx index c6ba7b1..40166bc 100644 --- a/examples-nextjs/app/components/retrieve-values.tsx +++ b/examples-nextjs/app/components/retrieve-values.tsx @@ -2,6 +2,7 @@ import { useNilRetrieveValues } from "@nillion/client-react-hooks"; import { Uuid } from "@nillion/client-vms"; +import { bytesToHex } from "@noble/hashes/utils"; import { type ChangeEvent, type FC, useState } from "react"; export const RetrieveValues: FC = () => { @@ -15,7 +16,11 @@ export const RetrieveValues: FC = () => { if (mutation.isSuccess) { // stringify cannot handle BigInts data = data = JSON.stringify(mutation.data, (_, v) => - typeof v === "bigint" ? v.toString() : v, + typeof v === "bigint" + ? v.toString() + : v instanceof Uint8Array + ? bytesToHex(v) + : v, ); } else if (mutation.isError) { data = mutation.error.message; diff --git a/examples-nextjs/app/components/store-ecdsa-values.tsx b/examples-nextjs/app/components/store-ecdsa-values.tsx new file mode 100644 index 0000000..1ae0899 --- /dev/null +++ b/examples-nextjs/app/components/store-ecdsa-values.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { useNilStoreValues } from "@nillion/client-react-hooks"; +import { NadaValue } from "@nillion/client-vms"; +import { secp256k1 } from "@noble/curves/secp256k1"; +import { sha256 } from "@noble/hashes/sha2"; +import { bytesToHex } from "@noble/hashes/utils"; +import type { FC } from "react"; + +export const StoreSignatureValues: FC = () => { + const storeValuesMutation = useNilStoreValues(); + + let id = ""; + if (storeValuesMutation.isSuccess) { + id = storeValuesMutation.data; + } else if (storeValuesMutation.isError) { + id = storeValuesMutation.error.message; + } + + const privateKey: Uint8Array = secp256k1.utils.randomPrivateKey(); + const digestMessage = sha256("A deep message with a deep number: 42"); + + const options = { + values: [ + { + name: "tecdsa_private_key", + value: NadaValue.new_ecdsa_private_key(privateKey), + }, + { + name: "tecdsa_digest_message", + value: NadaValue.new_ecdsa_digest_message(digestMessage), + }, + ], + ttl: 1, + }; + + const stringifiedOptions = JSON.stringify({ + ...options, + values: options.values.map(({ name, value }) => ({ + name, + value: { + type: value.type_name(), + value: bytesToHex(value.to_byte_array()), + }, + })), + }); + + function handleClick(): void { + storeValuesMutation.execute(options); + } + + return ( +
+

Store Signature Values

+
    +
  1. Status: {storeValuesMutation.status}
  2. +
  3. Options: {stringifiedOptions}
  4. +
  5. Store Id: {id}
  6. +
+ +
+ ); +}; diff --git a/examples-nextjs/app/components/update-ecdsa-permissions.tsx b/examples-nextjs/app/components/update-ecdsa-permissions.tsx new file mode 100644 index 0000000..53be775 --- /dev/null +++ b/examples-nextjs/app/components/update-ecdsa-permissions.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { + useNilUpdatePermissions, + useNillion, +} from "@nillion/client-react-hooks"; +import { UpdatePermissionsBuilder, Uuid } from "@nillion/client-vms"; +import { type ChangeEvent, type FC, useMemo, useState } from "react"; + +export const UpdateEcdsaPermissions: FC = () => { + const { client } = useNillion(); + const mutation = useNilUpdatePermissions(); + const [id, setId] = useState(""); + const isValidUuid = Uuid.safeParse(id).success; + + const tecdsaProgramId = "builtin/tecdsa_sign"; + + const permissions = useMemo(() => { + return UpdatePermissionsBuilder.init(client).grantCompute( + client.id, + tecdsaProgramId, + ); + }, [client.id]); + + function handleChange(event: ChangeEvent): void { + setId(event.target.value); + } + + function handleClick(): void { + const options = { id, permissions }; + mutation.execute(options); + } + + return ( +
+

Update ECDSA Sign Permissions

+
    +
  1. Status: {mutation.status}
  2. +
  3. New permissions: {JSON.stringify(permissions.toObject())}
  4. +
  5. + Id: +
  6. +
+ +
+ ); +}; diff --git a/examples-nextjs/app/page.tsx b/examples-nextjs/app/page.tsx index 2e4a8ad..7518414 100644 --- a/examples-nextjs/app/page.tsx +++ b/examples-nextjs/app/page.tsx @@ -10,14 +10,17 @@ import { useEffect, useState } from "react"; import { AccountBalance } from "#/components/account-balance"; import { AddFunds } from "#/components/add-funds"; import { DeleteValues } from "#/components/delete-values"; +import { EcdsaSign } from "#/components/ecdsa-sign"; import { InvokeCompute } from "#/components/invoke-compute"; import { OverwritePermissions } from "#/components/overwrite-permissions"; import { PoolStatus } from "#/components/pool-status"; import { RetrieveComputeResults } from "#/components/retrieve-compute-results"; import { RetrievePermissions } from "#/components/retrieve-permissions"; import { RetrieveValues } from "#/components/retrieve-values"; +import { StoreSignatureValues } from "#/components/store-ecdsa-values"; import { StoreProgram } from "#/components/store-program"; import { StoreValues } from "#/components/store-values"; +import { UpdateEcdsaPermissions } from "#/components/update-ecdsa-permissions"; import { UpdatePermissions } from "#/components/update-permissions"; import { UpdateValues } from "#/components/update-values"; @@ -55,6 +58,9 @@ export default function Home() { + + + ); } diff --git a/examples-nextjs/package.json b/examples-nextjs/package.json index bec6355..3e27ede 100644 --- a/examples-nextjs/package.json +++ b/examples-nextjs/package.json @@ -12,6 +12,9 @@ "dependencies": { "@nillion/client-react-hooks": "workspace:*", "@nillion/client-vms": "workspace:*", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@noble/secp256k1": "^2.1.0", "next": "^15.0.3", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f61de83..c1e4318 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -174,6 +174,15 @@ importers: '@nillion/client-vms': specifier: workspace:* version: link:../client-vms + '@noble/curves': + specifier: ^1.6.0 + version: 1.6.0 + '@noble/hashes': + specifier: ^1.5.0 + version: 1.7.1 + '@noble/secp256k1': + specifier: ^2.1.0 + version: 2.1.0 next: specifier: ^15.0.3 version: 15.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -953,6 +962,10 @@ packages: resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + '@noble/secp256k1@2.1.0': resolution: {integrity: sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==} @@ -2677,7 +2690,7 @@ snapshots: '@confio/ics23@0.6.8': dependencies: - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.1 protobufjs: 6.11.4 '@connectrpc/connect-web@2.0.0(@bufbuild/protobuf@2.2.2)(@connectrpc/connect@2.0.0(@bufbuild/protobuf@2.2.2))': @@ -2701,7 +2714,7 @@ snapshots: '@cosmjs/encoding': 0.32.4 '@cosmjs/math': 0.32.4 '@cosmjs/utils': 0.32.4 - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.1 bn.js: 5.2.1 elliptic: 6.6.0 libsodium-wrappers-sumo: 0.7.15 @@ -3078,6 +3091,8 @@ snapshots: '@noble/hashes@1.5.0': {} + '@noble/hashes@1.7.1': {} + '@noble/secp256k1@2.1.0': {} '@pkgjs/parseargs@0.11.0': @@ -4282,7 +4297,7 @@ snapshots: starknet@6.11.0: dependencies: '@noble/curves': 1.4.2 - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.1 '@scure/base': 1.1.9 '@scure/starknet': 1.0.0 abi-wan-kanabi: 2.2.3 From ec7963de8f42b5bcbeb4ff72795b01d9a1653039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Hern=C3=A1ndez?= Date: Wed, 22 Jan 2025 08:39:59 +0000 Subject: [PATCH 4/5] chore: bump client-react-hooks version --- client-react-hooks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-react-hooks/package.json b/client-react-hooks/package.json index ac3ccaf..fccaa7c 100644 --- a/client-react-hooks/package.json +++ b/client-react-hooks/package.json @@ -2,7 +2,7 @@ "name": "@nillion/client-react-hooks", "license": "MIT", "author": "devsupport@nillion.com", - "version": "0.3.0-rc.0", + "version": "0.3.0-rc.1", "homepage": "https://nillion.pub/client-ts", "repository": { "type": "git", From a7aac2ac2200a48761b70d49a0d4137d94870892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Hern=C3=A1ndez?= Date: Wed, 22 Jan 2025 08:40:12 +0000 Subject: [PATCH 5/5] chore: bump examples-nextjs version --- examples-nextjs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples-nextjs/package.json b/examples-nextjs/package.json index 3e27ede..0e721ec 100644 --- a/examples-nextjs/package.json +++ b/examples-nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@nillion/examples-nextjs", - "version": "0.3.0", + "version": "0.3.1", "private": true, "scripts": { "dev": "next dev",