From deafb776bbe5ee32244e452ac114030617b1c8c4 Mon Sep 17 00:00:00 2001 From: MSghais Date: Thu, 1 Aug 2024 17:42:43 +0200 Subject: [PATCH 1/2] simple config nip-89 creation and view + how it works --- askeladd-dvm-marketplace/.env.example | 4 +- .../src/app/components/EventCard.tsx | 37 +++ .../src/app/components/TagsCard.tsx | 24 ++ .../src/app/config-marketplace/page.tsx | 309 ++++++++++++++++++ .../src/app/utils/generateAppHandler.ts | 93 ++++++ .../src/constants/relay.ts | 3 + askeladd-dvm-marketplace/src/types/index.ts | 28 +- 7 files changed, 496 insertions(+), 2 deletions(-) create mode 100644 askeladd-dvm-marketplace/src/app/components/EventCard.tsx create mode 100644 askeladd-dvm-marketplace/src/app/components/TagsCard.tsx create mode 100644 askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx create mode 100644 askeladd-dvm-marketplace/src/app/utils/generateAppHandler.ts diff --git a/askeladd-dvm-marketplace/.env.example b/askeladd-dvm-marketplace/.env.example index 7234561..676644f 100644 --- a/askeladd-dvm-marketplace/.env.example +++ b/askeladd-dvm-marketplace/.env.example @@ -1,3 +1,5 @@ DEFAULT_NOSTR_USER_SK=9f...f4 NEXT_PUBLIC_DEFAULT_NOSTR_USER_SK=9f...f4 # We can generate Nostr private key for user after -NEXT_PUBLIC_DEFAULT_RELAYER="wss" \ No newline at end of file +NEXT_PUBLIC_DEFAULT_RELAYER="wss" +DVM_PUBKEY=1OM...lfg +NEXT_PUBLIC_DVM_PUBKEY=1OM...lfg \ No newline at end of file diff --git a/askeladd-dvm-marketplace/src/app/components/EventCard.tsx b/askeladd-dvm-marketplace/src/app/components/EventCard.tsx new file mode 100644 index 0000000..591e4ef --- /dev/null +++ b/askeladd-dvm-marketplace/src/app/components/EventCard.tsx @@ -0,0 +1,37 @@ +import { NDKEvent } from '@nostr-dev-kit/ndk'; +import { Event as NostrEvent } from 'nostr-tools'; +import React, { useState } from 'react'; +import TagsCard from './TagsCard'; +// Define the props for the component +interface TagsCardProps { + event: NDKEvent | NostrEvent; // Array of array of strings +} +const EventCard: React.FC = ({ event }) => { + + const [seeTag, setSeeTag] = useState(false) + + + const date:string|undefined = event?.created_at ? new Date(event?.created_at).toDateString() : undefined + return ( + //
+
+
+ {date && +

{date}

+ } +

Job id: {event?.kind}

+

{event?.content}

+ + {seeTag && + + } +
+ + +
+ ); +}; + +export default EventCard; diff --git a/askeladd-dvm-marketplace/src/app/components/TagsCard.tsx b/askeladd-dvm-marketplace/src/app/components/TagsCard.tsx new file mode 100644 index 0000000..906df29 --- /dev/null +++ b/askeladd-dvm-marketplace/src/app/components/TagsCard.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +// Define the props for the component +interface TagsCardProps { + tags: string[][]; // Array of array of strings +} +const TagsCard: React.FC = ({ tags }) => { + return ( +
+ {tags.map((tagGroup, index) => ( +
+ {index} + {tagGroup.map((tag, idx) => ( + // + + {tag} + + ))} +
+ ))} +
+ ); +}; + +export default TagsCard; diff --git a/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx b/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx new file mode 100644 index 0000000..b15024d --- /dev/null +++ b/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx @@ -0,0 +1,309 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; +import { useSendNote } from "@/hooks/useSendNote"; +import { useFetchEvents } from "@/hooks/useFetchEvents"; +import { APPLICATION_PUBKEY_DVM, ASKELADD_RELAY } from "@/constants/relay"; +import { Event as EventNostr, SimplePool } from "nostr-tools"; +import { ASKELADD_KINDS, ConfigHandle } from "@/types"; +import EventCard from "../components/EventCard"; +import { generateContentAndTags } from "../utils/generateAppHandler"; + +export default function Home() { + const [publicKey, setPublicKey] = useState(); + const [appKind, setAppKind] = useState(ASKELADD_KINDS.KIND_JOB_REQUEST) + const [configKind, setConfigKind] = useState(ConfigHandle.ALL_KIND) + const [jobId, setJobId] = useState(); + const [error, setError] = useState() + const [lastConfig, setLastConfig] = useState() + const [events, setEvents] = useState([]) + const [proofStatus, setProofStatus] = useState< + "idle" | "pending" | "received" | "verified" + >("idle"); + const [isLoading, setIsLoading] = useState(false); + const [isInitialized, setIsInitialized] = useState(false); + const [openHowItWork, setOpenHowItWork] = useState(false); + const [isNeedLoadEvents, setIsNeedLoadEvents] = useState(true); + const [isAdmin, setIsAdmin] = useState(false); + const [timestampJob, setTimestampJob] = useState(); + const { fetchEvents, fetchEventsTools, setupSubscriptionNostr } = useFetchEvents() + const { sendNote, publishNote } = useSendNote() + + useEffect(() => { + if (isNeedLoadEvents || !isInitialized) { + fetchEventsApp() + setIsNeedLoadEvents(false) + setIsInitialized(true) + } + }, [isNeedLoadEvents]) + + useEffect(() => { + + if (publicKey) { + + if (process.env.NEXT_PUBLIC_DVM_PUBKEY && process.env.NEXT_PUBLIC_DVM_PUBKEY != publicKey) { + setIsAdmin(true) + } + } + }, [publicKey]) + + const fetchEventsApp = async () => { + console.log("fetch events config"); + const { events } = await fetchEventsTools({ + kinds: [NDKKind.AppHandler], + limit: 100, + }) + console.log("events config NIP-89", events); + setLastConfig(events[0]) + setEvents(events); + setIsNeedLoadEvents(false) + } + + + /** Connect you */ + const connectExtension = async () => { + try { + + if (typeof window !== "undefined" && window.nostr) { + const pubkey = await window.nostr.getPublicKey(); + let created_at = new Date().getTime(); + setPublicKey(pubkey) + } + + } catch (e) { + console.log("connect extension error", e) + } finally { + setIsLoading(false); + } + + }; + + /** Submit Recommended App Handler for: + * JOB_REQUEST 5600: Prove + * JOB_RESULT 6600: Result + */ + const submitJob = async () => { + try { + setIsLoading(true); + setJobId(undefined) + setProofStatus("pending"); + setError(undefined); + + submitApplicationHandler() + setIsNeedLoadEvents(true) + + } catch (e) { + } finally { + setIsLoading(false); + + } + + }; + + const submitApplicationHandler = async () => { + try { + setIsLoading(true); + setProofStatus("pending"); + setLastConfig(undefined); + setError(undefined); + + setTimestampJob(new Date().getTime()) + /** Use Nostr extension to send event */ + const pool = new SimplePool(); + let pubkey; + if (typeof window !== "undefined" && window.nostr) { + console.log("pubkey", pubkey) + if (!pubkey) return; + const { tags, content } = generateContentAndTags(configKind, appKind, pubkey) + console.log("tags", tags) + console.log("content", content) + if (!content || !tags) return; + + let created_at = new Date().getTime(); + const event = await window.nostr.signEvent({ + pubkey: publicKey ?? pubkey, + created_at: created_at, + kind: NDKKind.AppHandler, + tags: tags, + content: content + }) // takes an event object, adds `id`, `pubkey` and `sig` and returns it + // Setup job request to fetch job id + + // let eventID = await relay.publish(event as EventNostr); + const eventID = await Promise.any(pool.publish(ASKELADD_RELAY, event as EventNostr)); + console.log("eventID", eventID[0]) + setIsNeedLoadEvents(true) + + } else { + + /** @TODO flow is user doesn't have NIP-07 extension */ + // let { result, event } = await sendNote({ content, tags, kind: 5600 }) + // console.log("event", event) + // if (event?.sig) { + // setJobId(event?.sig); + // } + // setIsWaitingJob(true) + /** NDK event + * Generate or import private key after + */ + } + } catch (e) { + } finally { + setIsLoading(false); + } + + }; + + const handleSetAppKindSpecific = (kind: ASKELADD_KINDS, config: ConfigHandle) => { + if (kind) { + setAppKind(kind) + setConfigKind(ConfigHandle.SPECIFIC_KIND); + } + if (config == ConfigHandle?.ALL_KIND) { + setConfigKind(config); + setAppKind(undefined) + } + } + + return ( +
+
+
+
+ + + {publicKey && publicKey != APPLICATION_PUBKEY_DVM ? +
+
+
+
+ {publicKey && +

Connected: {publicKey}

} +

You can't config this if you are not an admin of ASKELADD

+
+
+
+
+ + : publicKey && publicKey == APPLICATION_PUBKEY_DVM && +
+
+

Askeladd DVM Marketplace

+
+ {publicKey == APPLICATION_PUBKEY_DVM && +

DVM admin

+ } + {publicKey && +

Connected: {publicKey}

} +
+ +
+ + +

Selected config: {configKind == ConfigHandle?.ALL_KIND ? "All" : "Specific"}

+

Change your config

+ + + {configKind == ConfigHandle.SPECIFIC_KIND && + <> + + +

Selected event: {appKind}

+
+ + +
+ + } + +
+ {isLoading &&
} +
+ +
+ } + + + + + + +
setOpenHowItWork(!openHowItWork)} + className="cursor-pointer my-5 p-5" + > +

How the ASKELADD DVM ZK works?

+ {!openHowItWork && + + } + {openHowItWork && + <> +
+

As an User

+

User send a JOB_REQUEST with different params on the Nostr event:

+

You need theses params on the Nostr event:

+

It can change with all STWO Prover enabled on the Marketplace

+

Request: {JSON.stringify({ + "claim": "413300", + "log_size":"5" + })} // The input of the Program

+

Tags: ["param", "input_name", "value"]

+
+ + + } +
+ +
+ +

Existing Config of DVM

+

Last config

+ {lastConfig && + <> + + + } + +

By Event KIND enabled on ASKELADD (WIP)

+

(5600, 6600 and more soon)

+ +

Admin config (WIP)

+

TODO

+ +

Others config

+ +
{events?.map((event, i) => { + return ( + + ) + + })} +
+
+ + +
+
+
+ ); +} diff --git a/askeladd-dvm-marketplace/src/app/utils/generateAppHandler.ts b/askeladd-dvm-marketplace/src/app/utils/generateAppHandler.ts new file mode 100644 index 0000000..449b092 --- /dev/null +++ b/askeladd-dvm-marketplace/src/app/utils/generateAppHandler.ts @@ -0,0 +1,93 @@ +import { APPLICATION_PUBKEY_DVM, ASKELADD_RELAY } from "@/constants/relay"; +import { ASKELADD_KINDS, ConfigHandle, KIND_JOB_REQUEST, KIND_JOB_RESULT } from "@/types"; + +const PARAMS_JOB_REQUEST_ZK = { + request: { + + }, + params: { + + }, + program: { + + } +} + +const PARAMS_JOB_RESULT_ZK = { + job_id: { + + }, + proof: { + + }, + result: { + + } +} + +const PARAMS_ALL_KIND = { + [KIND_JOB_REQUEST]:PARAMS_JOB_REQUEST_ZK, + [KIND_JOB_RESULT]:PARAMS_JOB_RESULT_ZK, +} +export const generateTagsByAppKind = (tags: string[][], appKind: ASKELADD_KINDS, config:ConfigHandle) => { + + const randomId = Math.random().toString(36).substring(7); + + if(config == ConfigHandle.SPECIFIC_KIND) { + tags = [ + ["d", randomId], + ["k", appKind?.toString()], + ["a", `31990:app1-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "ios"], + ["a", `31990:app2-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "web"], + ["a", `31990:app3-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "android"], + ['output', 'text/json'] + ]; + } else { + tags = [ + ["d", randomId], + ["k", ASKELADD_KINDS.KIND_JOB_REQUEST.toString()], + ["k", ASKELADD_KINDS.KIND_JOB_RESULT.toString()], + ["a", `31990:app1-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "ios"], + ["a", `31990:app2-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "web"], + ["a", `31990:app3-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "android"], + // ["web", "https://..../a/", "nevent"], + // ["web", "https://..../p/", "nprofile"], + // ["web", "https://..../e/"], + ['output', 'text/json'] + ]; + } + return tags + +} +export const generateContentAndTags = (configKind: ConfigHandle, appKind?: ASKELADD_KINDS, pubkey?:string): { tags?: string[][], content?: string } => { + let tags: string[][] = [] + let content = ""; + if (configKind == ConfigHandle?.ALL_KIND) { + content = JSON.stringify(PARAMS_ALL_KIND); + tags = [ + ["d",], + ["k", ASKELADD_KINDS.KIND_JOB_REQUEST.toString()], + ["k", ASKELADD_KINDS.KIND_JOB_RESULT.toString()], + ["a", `31990:app1-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "ios"], + ["a", `31990:app2-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "web"], + ["a", `31990:app3-pubkey:${APPLICATION_PUBKEY_DVM}`, ASKELADD_RELAY[0], "android"], + // ["web", "https://..../a/", "nevent"], + // ["web", "https://..../p/", "nprofile"], + // ["web", "https://..../e/"], + ['output', 'text/json'] + ]; + + } else if (configKind == ConfigHandle?.SPECIFIC_KIND) { + if (!appKind) return { tags, content } + tags = generateTagsByAppKind(tags, appKind, configKind) + if (appKind && appKind?.toString() == ASKELADD_KINDS.KIND_JOB_REQUEST.toString()) { + content = JSON.stringify(PARAMS_JOB_REQUEST_ZK) + } + else if (appKind && appKind == ASKELADD_KINDS.KIND_JOB_RESULT) { + content = JSON.stringify(PARAMS_JOB_RESULT_ZK) + } + + } + + return { tags, content } +} diff --git a/askeladd-dvm-marketplace/src/constants/relay.ts b/askeladd-dvm-marketplace/src/constants/relay.ts index 7b9a182..39ca656 100644 --- a/askeladd-dvm-marketplace/src/constants/relay.ts +++ b/askeladd-dvm-marketplace/src/constants/relay.ts @@ -4,3 +4,6 @@ export const ASKELADD_RELAY= [ // "wss://relay.nostr.net", process.env.NEXT_PUBLIC_DEFAULT_RELAYER ?? 'wss://nostr-relay-nestjs-production.up.railway.app',// AFK relayer ] + + +export const APPLICATION_PUBKEY_DVM= process.env.NEXT_PUBLIC_DVM_PUBKEY diff --git a/askeladd-dvm-marketplace/src/types/index.ts b/askeladd-dvm-marketplace/src/types/index.ts index 0a99b42..a35ecf2 100644 --- a/askeladd-dvm-marketplace/src/types/index.ts +++ b/askeladd-dvm-marketplace/src/types/index.ts @@ -1,6 +1,7 @@ export const KIND_JOB_RESULT = 6600 export const KIND_JOB_REQUEST = 5600 + export interface JobResultProver { job_id: string; response: { @@ -39,7 +40,32 @@ export interface CommitmentSchemeProof { fri_proof: { inner_layers: ProofInnerLayer[] | any[]; last_layer_poly: { - coeffs:any[]; + coeffs: any[]; } }; } + +export enum ASKELADD_KINDS { + KIND_JOB_REQUEST = 5600, + KIND_JOB_RESULT = 6600, + // KIND_SUBMIT_PROGRAM +} + +export enum ASKELADD_KINDS_NAME { + KIND_JOB_REQUEST = "Job request", + KIND_JOB_RESULT = "Job result", + KIND_SUBMIT_PROGRAM = "Submit result", +} +// export const ASKELADD_KINDS= { +// KIND_JOB_REQUEST, +// KIND_JOB_RESULT +// } + +export interface IFormRecommendedApplicationHandlerEvent { + +} + +export enum ConfigHandle { + SPECIFIC_KIND, + ALL_KIND +} \ No newline at end of file From c516479f815192dee5767d91a07ee906d3ce654e Mon Sep 17 00:00:00 2001 From: MSghais Date: Thu, 1 Aug 2024 17:57:02 +0200 Subject: [PATCH 2/2] fix build no comment text node --- .../src/app/config-marketplace/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx b/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx index b15024d..c9ac42f 100644 --- a/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx +++ b/askeladd-dvm-marketplace/src/app/config-marketplace/page.tsx @@ -186,7 +186,7 @@ export default function Home() {
{publicKey &&

Connected: {publicKey}

} -

You can't config this if you are not an admin of ASKELADD

+

You can't config this if you are not an admin of ASKELADD

@@ -266,8 +266,8 @@ export default function Home() {

Request: {JSON.stringify({ "claim": "413300", "log_size":"5" - })} // The input of the Program

-

Tags: ["param", "input_name", "value"]

+ })} " The input of the Program

+

Tags: {`["param", "input_name", "value"]`}