From 20df7f850759b714555f1f439b71d5e689275340 Mon Sep 17 00:00:00 2001 From: smolgrrr Date: Sun, 25 Aug 2024 14:43:01 +1000 Subject: [PATCH] sort by work feed --- .DS_Store | Bin 0 -> 6148 bytes README.md | 11 +- .../src/components/modals/CardContainer.tsx | 2 +- client/src/components/modals/PostCard.tsx | 109 +++++---- client/src/components/modals/RepostCard.tsx | 119 --------- client/src/components/routes/HashtagPage.tsx | 13 +- client/src/components/routes/Home.tsx | 38 ++- .../src/components/routes/Notifications.tsx | 11 - client/src/components/routes/Thread.tsx | 2 +- pow_server/.gitignore | 2 - pow_server/Dockerfile | 23 -- pow_server/go.mod | 27 --- pow_server/go.sum | 54 ----- pow_server/main.go | 225 ------------------ 14 files changed, 104 insertions(+), 532 deletions(-) create mode 100644 .DS_Store delete mode 100644 client/src/components/modals/RepostCard.tsx delete mode 100644 pow_server/.gitignore delete mode 100644 pow_server/Dockerfile delete mode 100644 pow_server/go.mod delete mode 100644 pow_server/go.sum delete mode 100644 pow_server/main.go diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9cd5d4c253a05603a7c1ea2073a985349de1876c GIT binary patch literal 6148 zcmeHKJ5Iwu5S;-RmT1yJxhFtM57H8u6XXIYiDZysIYsD~iUZ{e5j98R%?Ge$g^~hh zq}k`4ndh}%;qed=FP=AZq8Sm@a6x*NWonWapV*m4Dj>@}w(Wem?z`Qj@^nDB6L zV)cb#tf4?C5DI)LAm4`s7tD^uP(K}5bOZp(Z@e0=b(R1o2>`QWF+>E$MgY#3`O5DJVHIJfOg`u{!r#QZ-d#V!;G1^$!* zTs6yPfmib0I(a$iwF&+J|1{KUITmfjKyAg?NGrY?)Frt_K06jer6aC%VEhP(E@7d- HZz%8qxJfCw literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 00e0bae..16003bf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# The Wire: unstoppable free speech +# The Wired: unstoppable free speech An app to facilitate unstoppable free speech on the internet. ## Overview @@ -6,10 +6,5 @@ Nostr at the core: censorship resistant, also stupid simple and permissionless < Anon only: Names and Nyms carry reputations/ego and risk doxxing - Anons speak freely
PoW: Spam and noise -### Wanting to add - -Ways to make Nostr notes impossible to stop?
-- Secure Scuttlebutt gossip and offline (?): Even in locked down countries, if we have a SSB-like medium, we only need one person to connect to a relay to share other ppl's messages
-- Extremely lightweight relays and gossiping
-- PWAs and bridges: A proliferation of similar, simple PWAs, and the sharing of notes through any means (telegram, email, pigeon) before getting to nostr
-- API connection to do PoW externally to app if wanted +## Back up +https://git.getwired.app/doot/TAO \ No newline at end of file diff --git a/client/src/components/modals/CardContainer.tsx b/client/src/components/modals/CardContainer.tsx index ab0e5ed..cfb1290 100644 --- a/client/src/components/modals/CardContainer.tsx +++ b/client/src/components/modals/CardContainer.tsx @@ -2,7 +2,7 @@ import { PropsWithChildren } from "react"; export default function CardContainer({ children }: PropsWithChildren) { return ( -
+
{children}
); diff --git a/client/src/components/modals/PostCard.tsx b/client/src/components/modals/PostCard.tsx index bca6dfa..a92c6ad 100644 --- a/client/src/components/modals/PostCard.tsx +++ b/client/src/components/modals/PostCard.tsx @@ -31,10 +31,30 @@ const PostCard = ({ const icon = getIconFromHash(event.pubkey); const metadataParsed = metadata ? getMetadata(metadata) : null; const [relatedEvents, setRelatedEvents] = useState([]); + const [sumReplyPow, setReplySumPow] = useState(0); + const [repostedEvent, setRepostedEvent] = useState(); + const [parsedEvent, setParsedEvent] = useState(event); useEffect(() => { const allRelatedEvents = [event, ...(replies || [])]; setRelatedEvents(allRelatedEvents); + + if (event.kind === 6) { + setRepostedEvent(event) + setParsedEvent(JSON.parse(event.content)); + } + + // Adjusting the sum calculation to account for exponential growth in work + const sum = replies.reduce((acc, reply) => { + const difficulty = verifyPow(reply); + // Skip adding to the sum if difficulty is 0, assuming 0 means no work was done. + // Adjust this logic if verifyPow uses a different scale or interpretation. + return difficulty > 0 ? acc + Math.pow(2, difficulty) : acc; + }, 0); + + // Check if sum is greater than 0 to avoid -Infinity in log2 calculation + const equivalentDifficulty = sum > 0 ? Math.log2(sum) : 0; + setReplySumPow(equivalentDifficulty); }, [event, replies]); const handleClick = () => { @@ -44,52 +64,61 @@ const PostCard = ({ } }; + return ( -
-
- -
- {repliedTo &&
- Reply to: - {uniqBy(repliedTo, 'pubkey').map((event, index) => ( -
- {event.kind === 0 ? ( - - ) : ( -
- )} -
- ))} -
} -
- {metadataParsed ? - - : -
- } -
-
- {verifyPow(event)} -
- · -
- {timeAgo(event.created_at)} -
- · -
- - {replies.length} +
+
+ +
+ {repliedTo &&
+ Reply to: + {uniqBy(repliedTo, 'pubkey').map((parsedEvent, index) => ( +
+ {event.kind === 0 ? ( + + ) : ( +
+ )} +
+ ))} +
} +
+ {metadataParsed ? + + : +
+ } +
+
+ {verifyPow(parsedEvent)} +
+ {repostedEvent && +
+ + {verifyPow(repostedEvent)}
+ } + · +
+ + {replies.length} + ( + + {sumReplyPow.toFixed(0)}) +
+ · +
+ {timeAgo(event.created_at)}
+
); }; diff --git a/client/src/components/modals/RepostCard.tsx b/client/src/components/modals/RepostCard.tsx deleted file mode 100644 index 82eb466..0000000 --- a/client/src/components/modals/RepostCard.tsx +++ /dev/null @@ -1,119 +0,0 @@ -// import CardContainer from "./CardContainer"; -import { CpuChipIcon } from "@heroicons/react/24/outline"; -// import { parseContent } from "../../utils/content"; -import { Event, nip19 } from "nostr-tools"; -import { getMetadata, Metadata } from "../../utils/getMetadata"; -// import { renderMedia } from "../../utils/FileUpload"; -import { getIconFromHash, timeAgo } from "../../utils/cardUtils"; -import { verifyPow } from "../../utils/mine"; -import { useNavigate } from 'react-router-dom'; -import { subNoteOnce } from "../../utils/subscriptions"; -import { useEffect, useState } from "react"; -import ContentPreview from "./CardModals/TextModal"; - -interface RepostProps { - key?: string | number; - event: Event; -} - -const RepostCard = ({ - key, - event -}: RepostProps) => { - const repostedEvent = JSON.parse(event.content); - // const { files } = parseContent(repostedEvent); - const icon = getIconFromHash(event.pubkey); - const navigate = useNavigate(); - const [cachedMetadataEvents, setCachedMetadataEvents] = useState( - JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]") - ); - const [metadata, setMetadata] = useState() - - // Define your callback function for subGlobalFeed - const onEvent = (event: Event, relay: string) => { - const existingEvent = cachedMetadataEvents.find((e) => e.pubkey === event.pubkey) - if (existingEvent) { - setMetadata(getMetadata(existingEvent)) - } - else if (!existingEvent && event.kind === 0 && event.pubkey === repostedEvent.pubkey && metadata == null) { - setMetadata(getMetadata(event)) - - setCachedMetadataEvents((prevMetadataEvents) => { - // Check if the event already exists in the cached metadata events - const existingEvent = prevMetadataEvents.find((e) => e.id === event.id || e.pubkey === event.pubkey) - if (!existingEvent) { - // If the event doesn't exist, add it to the cached metadata events - return [...prevMetadataEvents, event]; - } else if (existingEvent && existingEvent.created_at < event.created_at) { - // Remove any existing metadata event with the same pubkey and id - const updatedMetadataEvents = prevMetadataEvents.filter( - (e) => e.id !== existingEvent.id - ); - // Add the new metadata event - return [...updatedMetadataEvents, event]; - } - // If the event already exists, return the previous cached metadata events - return prevMetadataEvents; - }); - } - }; - - useEffect(() => { - subNoteOnce(repostedEvent.id, onEvent); - }, [repostedEvent.id]); - - // Save the cached metadataEvents to localStorage - useEffect(() => { - localStorage.setItem("cachedMetadataEvents", JSON.stringify(cachedMetadataEvents)); - }, [cachedMetadataEvents]); - - const handleClick = () => { - navigate(`/thread/${nip19.noteEncode(repostedEvent.id)}`); - }; - - return ( -
- -
- Repost - @ - {verifyPow(event)} -
-
-
-
-
-
- -
-
- {metadata ? - - : -
- } -
-
- {verifyPow(repostedEvent)} -
- · -
- {timeAgo(repostedEvent.created_at)} -
-
-
-
-
-
-
-
- ); -}; - -export default RepostCard; \ No newline at end of file diff --git a/client/src/components/routes/HashtagPage.tsx b/client/src/components/routes/HashtagPage.tsx index 1037355..204d0e8 100644 --- a/client/src/components/routes/HashtagPage.tsx +++ b/client/src/components/routes/HashtagPage.tsx @@ -2,7 +2,6 @@ import PostCard from "../modals/PostCard"; import { verifyPow } from "../../utils/mine"; import { Event } from "nostr-tools"; import NewNoteCard from "../forms/PostFormCard"; -import RepostCard from "../modals/RepostCard"; import { useParams } from "react-router-dom"; import { useFetchEvents } from "../../hooks/useFetchEvents"; @@ -36,19 +35,15 @@ const HashtagPage = () => {
-
- {sortedEvents.map((event) => ( - event.kind === 1 ? +
+ {sortedEvents.map((event) => e.pubkey === event.pubkey && e.kind === 0) || null} replies={sortedEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))} /> - : - - ))} + + )}
); diff --git a/client/src/components/routes/Home.tsx b/client/src/components/routes/Home.tsx index 94048b7..cc2b57f 100644 --- a/client/src/components/routes/Home.tsx +++ b/client/src/components/routes/Home.tsx @@ -1,7 +1,6 @@ import { verifyPow } from "../../utils/mine"; import { Event } from "nostr-tools"; import NewNoteCard from "../forms/PostFormCard"; -import RepostCard from "../modals/RepostCard"; import { DEFAULT_DIFFICULTY } from "../../config"; import PostCard from "../modals/PostCard"; import { useFetchEvents } from "../../hooks/useFetchEvents"; @@ -14,10 +13,29 @@ const Home = () => { .filter((event) => verifyPow(event) >= Number(filterDifficulty) && event.kind !== 0 && - (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "a")) + (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "p")) ) - const sortedEvents = postEvents.sort((a, b) => b.created_at - a.created_at); + const postEventsWithReplies = postEvents.map((event) => { + const totalWork = Math.pow(2, verifyPow(event)) + + noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)) + .reduce((acc, reply) => acc + Math.pow(2, verifyPow(reply)), 0); + return { + postEvent: event, + replies: noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)), + totalWork: totalWork, // Add total work here + }; + }); + + const sortedEvents = postEventsWithReplies + .sort((a, b) => { + // Sort by total work in descending order + const workDiff = b.totalWork - a.totalWork; + if (workDiff !== 0) return workDiff; + + // If total work is the same, sort by created_at in descending order + return b.postEvent.created_at - a.postEvent.created_at; + }); // Render the component return ( @@ -25,17 +43,13 @@ const Home = () => {
-
+
{sortedEvents.map((event) => ( - event.kind === 1 ? e.pubkey === event.pubkey && e.kind === 0) || null} - replies={noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))} - /> - : - e.pubkey === event.postEvent.pubkey && e.kind === 0) || null} + replies={event.replies} /> ))}
diff --git a/client/src/components/routes/Notifications.tsx b/client/src/components/routes/Notifications.tsx index 6fbbafc..5bcb88a 100644 --- a/client/src/components/routes/Notifications.tsx +++ b/client/src/components/routes/Notifications.tsx @@ -1,7 +1,6 @@ import { useState, useCallback } from "react"; import PostCard from "../modals/PostCard"; import { Event } from "nostr-tools"; -import RepostCard from "../modals/RepostCard"; import { useFetchEvents } from "../../hooks/useFetchEvents"; const Notifications = () => { @@ -65,31 +64,21 @@ const Notifications = () => {
Your Recent Posts {sortedEvents.map((event) => ( - event.kind === 1 ? e.pubkey === event.pubkey && e.kind === 0) || null} replies={countReplies(event)} /> - : - ))}
Mentions {sortedMentions.map((event) => ( - event.kind === 1 ? e.pubkey === event.pubkey && e.kind === 0) || null} replies={countReplies(event)} /> - : - ))}
diff --git a/client/src/components/routes/Thread.tsx b/client/src/components/routes/Thread.tsx index 3bef26c..ec8471f 100644 --- a/client/src/components/routes/Thread.tsx +++ b/client/src/components/routes/Thread.tsx @@ -67,7 +67,7 @@ const Thread = () => { .sort((a, b) => a.created_at - b.created_at).map((event, index) => ( e.pubkey === event.pubkey && e.kind === 0) || null} replies={countReplies(event)} /> ))} - e.pubkey === OPEvent.pubkey && e.kind === 0) || null} replies={countReplies(OPEvent)} type={'OP'}/> + e.pubkey === OPEvent.pubkey && e.kind === 0) || null} replies={replyEvents} type={'OP'}/>
{/* This is the white line separator */} diff --git a/pow_server/.gitignore b/pow_server/.gitignore deleted file mode 100644 index eff50e2..0000000 --- a/pow_server/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -go.sum -main \ No newline at end of file diff --git a/pow_server/Dockerfile b/pow_server/Dockerfile deleted file mode 100644 index 946e8a2..0000000 --- a/pow_server/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# Start from the latest golang base image -FROM golang:latest - -# Set the Current Working Directory inside the container -WORKDIR /app - -# Copy go mod and sum files -COPY go.mod go.sum ./ - -# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed -RUN go mod download - -# Copy the source from the current directory to the Working Directory inside the container -COPY . . - -# Build the Go app -RUN go build -o main . - -# Expose port 8080 to the outside world -EXPOSE 8080 - -# Command to run the executable -CMD ["./main"] \ No newline at end of file diff --git a/pow_server/go.mod b/pow_server/go.mod deleted file mode 100644 index beb7970..0000000 --- a/pow_server/go.mod +++ /dev/null @@ -1,27 +0,0 @@ -module pow_server - -go 1.21.4 - -require ( - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/dgraph-io/ristretto v0.1.1 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/gobwas/httphead v0.1.0 // indirect - github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.2.0 // indirect - github.com/golang/glog v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/nbd-wtf/go-nostr v0.25.7 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect - github.com/tidwall/gjson v1.14.4 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.0 // indirect - golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect - golang.org/x/sys v0.8.0 // indirect -) diff --git a/pow_server/go.sum b/pow_server/go.sum deleted file mode 100644 index a3820bb..0000000 --- a/pow_server/go.sum +++ /dev/null @@ -1,54 +0,0 @@ -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I= -github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/nbd-wtf/go-nostr v0.25.7 h1:DcGOSgKVr/L6w62tRtKeV2t46sRyFcq9pWcyIFkh0eM= -github.com/nbd-wtf/go-nostr v0.25.7/go.mod h1:bkffJI+x914sPQWum9ZRUn66D7NpDnAoWo1yICvj3/0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= -github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= -golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pow_server/main.go b/pow_server/main.go deleted file mode 100644 index 01504e0..0000000 --- a/pow_server/main.go +++ /dev/null @@ -1,225 +0,0 @@ -package main - -import ( - "encoding/hex" - "encoding/json" - "errors" - "log" - "math/bits" - "net/http" - "runtime" - "strconv" - "time" - - "github.com/nbd-wtf/go-nostr" -) - -var ( - ErrDifficultyTooLow = errors.New("nip13: insufficient difficulty") - ErrGenerateTimeout = errors.New("nip13: generating proof of work took too long") -) - -// Difficulty counts the number of leading zero bits in an event ID. -// It returns a negative number if the event ID is malformed. -func Difficulty(eventID string) int { - if len(eventID) != 64 { - return -1 - } - var zeros int - for i := 0; i < 64; i += 2 { - if eventID[i:i+2] == "00" { - zeros += 8 - continue - } - var b [1]byte - if _, err := hex.Decode(b[:], []byte{eventID[i], eventID[i+1]}); err != nil { - return -1 - } - zeros += bits.LeadingZeros8(b[0]) - break - } - return zeros -} - -// Generate performs proof of work on the specified event until either the target -// difficulty is reached or the function runs for longer than the timeout. -// The latter case results in ErrGenerateTimeout. -// -// Upon success, the returned event always contains a "nonce" tag with the target difficulty -// commitment, and an updated event.CreatedAt. -func Generate(event *nostr.Event, targetDifficulty int, nonceStart int, nonceStep int) (*nostr.Event, error) { - nonce := nonceStart - tag := nostr.Tag{"nonce", strconv.Itoa(nonceStep), strconv.Itoa(targetDifficulty)} - event.Tags = append(event.Tags, tag) - - for { - nonce += nonceStep - tag[1] = strconv.Itoa(nonce) - event.CreatedAt = nostr.Now() - if Difficulty(event.GetID()) >= targetDifficulty { - return event, nil - } - } -} - -func generatePOW(event *nostr.Event, difficulty int, numCores int) (*nostr.Event, error) { - resultChan := make(chan *nostr.Event) - errorChan := make(chan error) - - for i := 0; i < numCores; i++ { - go func(nonceStart int, nonceStep int) { - generatedEvent, err := Generate(event, difficulty, nonceStart, nonceStep) - if err != nil { - errorChan <- err - return - } - resultChan <- generatedEvent - }(i, numCores) - } - - select { - case result := <-resultChan: - return result, nil - case err := <-errorChan: - return nil, err - } -} - -// PowRequest struct for the POST request -type PowRequest struct { - ReqEvent *nostr.Event `json:"req_event"` - Difficulty string `json:"difficulty"` -} - -// handlePOW is the handler function for the "/powgen" endpoint -func handlePOW(w http.ResponseWriter, r *http.Request, numCores int) { - if r.Method != "POST" { - http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) - return - } - - var powReq PowRequest - err := json.NewDecoder(r.Body).Decode(&powReq) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - difficulty, err := strconv.Atoi(powReq.Difficulty) - if err != nil { - // handle error - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Generate proof of work for the event - generatedEvent, err := generatePOW(powReq.ReqEvent, difficulty, numCores) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Create a response struct - type Response struct { - Event *nostr.Event `json:"event"` - } - - // Respond with the generated event and the time taken - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(Response{Event: generatedEvent}) -} - -// PowRequest struct for the POST request -type TestRequest struct { - Difficulty string `json:"difficulty"` -} - -// handlePOW is the handler function for the "/powgen" endpoint -func handleTest(w http.ResponseWriter, r *http.Request, numCores int) { - if r.Method != "POST" { - http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) - return - } - - var powReq PowRequest - err := json.NewDecoder(r.Body).Decode(&powReq) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - difficulty, err := strconv.Atoi(powReq.Difficulty) - if err != nil { - // handle error - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Start the timer - start := time.Now() - - event := &nostr.Event{ - Kind: nostr.KindTextNote, - Content: "It's just me mining my own business", - PubKey: "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", - } - pow, err := generatePOW(event, difficulty, numCores) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Calculate the duration in milliseconds - iterations, _ := strconv.ParseFloat(pow.Tags[0][1], 64) - timeTaken := time.Since(start).Seconds() - hashrate := iterations / time.Since(start).Seconds() - - // Create a response struct - type Response struct { - TimeTaken float64 `json:"timeTaken"` - Hashrate float64 `json:"hashrate"` - } - - // Respond with the generated event and the time taken - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(Response{TimeTaken: timeTaken, Hashrate: hashrate}) -} - -func corsMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Set headers - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") - - // If it's a preflight request, respond with 200 - if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - - // Next - next.ServeHTTP(w, r) - }) -} - -func handlePOWWithCores(numCores int) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - handlePOW(w, r, numCores) - } -} - -func handleTestWithCores(numCores int) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - handleTest(w, r, numCores) - } -} - -func main() { - numCores := runtime.NumCPU() - - http.Handle("/powgen", corsMiddleware(handlePOWWithCores(numCores))) - http.Handle("/test", corsMiddleware(handleTestWithCores(numCores))) - - log.Fatal(http.ListenAndServe("0.0.0.0:8080", nil)) -}