Skip to content

Commit

Permalink
Merge pull request sandwichfarm#180 from dskvr/develop
Browse files Browse the repository at this point in the history
Update release branch
  • Loading branch information
dskvr authored Jan 10, 2023
2 parents d35fc55 + e65274e commit 2705805
Show file tree
Hide file tree
Showing 29 changed files with 1,113 additions and 206 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ public/main.js
lib/nostr-relay-inspector
dist
yarn-error.log
.env
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ COPY . /app/

RUN yarn && yarn build

RUN yarn global add yaml2json
#RUN yarn global add yaml2json

RUN yaml2json relays.yaml > dist/relays.json
#RUN yaml2json relays.yaml > dist/relays.json

FROM nginx:stable-alpine as nginx-nostr-relay-registry

Expand Down
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
> this project is alpha! changes are fast and hard. branching model and tests will come with beta.
# nostr-watch
# nostr-watch 0.1.0

A client-side nostr network status built with Vue3, [nostr-js](https://github.com/jb55/nostr-js) and [nostr-relay-inspector](https://github.com/dskvr/nostr-relay-inspector). Goal is to produce a client-side app that collects detailed information about nostr relays and the network in general to assist users, developers and relay operators alike
A client-side nostr network status built with Vue3, Pinia, [nostr-tools](https://github.com/fiatjaf/nostr-tools), [nostr-js](https://github.com/jb55/nostr-js) and [nostr-relay-inspector](https://github.com/dskvr/nostr-relay-inspector).

nostr.watch aggregates data on nostr relays and the network in general to assist users, developers and relay operators.

Develop branch is deployed to https://next.nostr.watch

## Features
- [x] Real-time relay status
Expand All @@ -13,25 +17,35 @@ A client-side nostr network status built with Vue3, [nostr-js](https://github.co
- [x] Relay Behavior Analysis
- [x] NIP Checks
- [x] Geo Checks (build-time)
- [ ] Optional Companion Backend for historical data and relay relief, front-end gracefully degrades if inaccessible.
- [ ] "Best Relays for a User" and "Best Relays for a Developer" dynamic aggregate, the former based largely on a balance of Latency and NIP support, the latter based largely on NIP support. Unique results for each visitor
- [x] Favorite Relays
- [x] Extension Support
- [x] Nostr signing, individualized relay testing
- [ ] Lighting Tips to Relay Operators
- [ ] Relay Statistics
- [ ] Relay Historical Data
- [ ] Discover relays at runtime (currently buildtime, ready to move to runtime with 0.1)
- [ ] Discover geo at runtime

## Todo [Road to Beta]
- [ ] Expose all features in frontend
- [ ] Clean codebase
- [ ] Tests

## Project setup
```
yarn install
```

## Pre-build


### Compiles and hot-reloads for development
Run once or whenever you want to update geo/discover relays
```
yarn prebuild
```
```
yarn serve
```

### Compiles and minifies for production
_This runs prebuild every time_
```
yarn build
```
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nostr-watch",
"version": "0.1.0",
"version": "0.1.1",
"private": true,
"scripts": {
"build": "vue-cli-service build",
Expand All @@ -25,14 +25,15 @@
"array-timsort": "1.0.3",
"country-code-emoji": "2.3.0",
"cross-fetch": "3.1.5",
"dotenv": "16.0.3",
"fast-safe-stringify": "2.1.1",
"js-yaml": "4.1.0",
"leaflet": "1.9.3",
"node-emoji": "1.11.0",
"node-fetch": "3.3.0",
"nostr": "0.2.5",
"nostr-relay-inspector": "0.0.23",
"nostr-tools": "1.0.1",
"nostr-relay-inspector": "0.0.25",
"nostr-tools": "1.1.1",
"object-sizeof": "1.6.3",
"onion-regex": "2.0.8",
"path-segments": "0.1.1",
Expand Down
5 changes: 3 additions & 2 deletions relays.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ relays:
- wss://relay.nostr.lu.ke
- wss://relay.nostrgraph.net
- wss://private-nostr.v0l.io
- wss://relay.nvote.co:443
- "wss://relay.nvote.co:443"
- wss://relay.nostrprotocol.net
- wss://nostr.itssilvestre.com
- wss://nostr.whoop.ph
Expand All @@ -198,5 +198,6 @@ relays:
- wss://relay.nostrprotocol.net
- wss://nostr.easydns.ca
- wss://nostr.bostonbtc.com
- "wss://knostr.neutrine.com:8880"
- wss://no-str.org
- wss://nostr.thesimplekid.com
- wss://knostr.neutrine.com
211 changes: 211 additions & 0 deletions scripts/canonicals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
require('dotenv').config()

const fs = require('fs'),
yaml= require('js-yaml'),
crypto = require('crypto'),
nostrTools = require('nostr-tools'),
{ RelayPool } = require('nostr'),
{ validateEvent, verifySignature, signEvent, getEventHash, getPublicKey } = nostrTools,

uniques = new Set()

let relays = yaml.load(fs.readFileSync('./relays.yaml', 'utf8')).relays,
canonicals = new Object(),
missing = new Array(),
hashes = new Object(),
discovered = true,
totalSent = 0,
oks = 0,
notices = 0

const pool = RelayPool(relays, {reconnect: false})

pool
.on('ok', (Relay) => {
oks++
// console.log('OK', Relay.url)
})
.on('notice', (Relay, notice) => {
notices++
// console.log('NOTICE', Relay.url, notice)
})

async function run(){
// setup()
// deletions()
// await process.exit()
await discover()
// process.exit()
// console.log(`wtf`, relays.length)
// console.log(`hashes`, Object.keys(hashes).length)
await sieve()
await broadcast()
process.exit()

}

function setup(){
const event = {
"id": "a2640e8a6640c595942ccf290eae404ac58569b59af5c8c8e3334d9cf809fff6",
"pubkey": "b3b0d247f66bf40c4c9f4ce721abfe1fd3b7529fbc1ea5e64d5f0f8df3a4b6e6",
"created_at": 1673275222,
"kind": 1,
"tags": [],
"content": "<3 to all the relays",
"sig": "e536be52a04f95c54e5cc82caafb9b25c8d47e00182c0eac0b6b678b482710288cc7fd85c62b0f97f5ed33dfbd7e15555c9bfeac059794767e414666d807f9cf"
}

pool.send(['EVENT', event])
}

async function discover(){


console.log('relays', relays.length)

return new Promise(resolve => {
const subid = crypto.randomBytes(40).toString('hex')

pool
.on('open', Relay => {
console.log('open', Relay.url)
Relay.subscribe(subid, {limit: relays.length, kinds:[1], "#t": ['canonical'], authors:[ getPublicKey(process.env.PRIVATE_KEY) ] }, )
relays.forEach( relay => {
hashes[hash(relay)] = relay
// Relay.subscribe(`subid_${relay}`, {limit: 1, kinds:[1], authors:[ getPublicKey(process.env.PRIVATE_KEY) ] }, )
})

})
.on('event', (Relay, _subid, event) => {
if(!discovered){
// console.log('published event found', event.id)
}
if(_subid.includes(subid) && discovered) {
// console.log('event', event.content, event.id)

if(uniques.has(event.id))
return

const relayHash = event.tags.map( tag => tag[0]=='h' ? tag[1] : false )[0]

if(!relayHash)
return

// console.log('relay hash', Relay.url, relayHash)

const relay = hashes?.[relayHash]

uniques.add(event.id)
canonicals[relay] = event
}
})

setTimeout( () => {
// pool.close()
discovered = false
resolve(relays)
}, 10*1000 )
})
}

async function sieve(){
console.log('filtering relays', relays.length)
checkMissing()
console.log('missing', missing.length)
return
}

function checkMissing(){
missing = new Array()
relays.forEach( relay => {
// console.log('check missing', relay, (canonicals?.[relay] instanceof Object) )
if( !(canonicals?.[relay] instanceof Object) )
missing.push(relay)
})
}

async function broadcast(){
setInterval( ()=> {
console.log('status', '\ntotal sent:', totalSent, '\noks:', oks, '\nnotices:', notices, '\n\n')
}, 60000)

for(let i=0;i<missing.length;i++){
const relay = missing[i]
const event = {
created_at: Math.floor(Date.now()/1000),
content: `<3 ${relay}, canonical note for https://nostr.watch/relay/${relay.replace('wss://', '')}`,
kind: 1,
tags: [
['h', hash(relay)],
['t', 'canonical'],
['e', process.env.CANONICAL_NOTE, 'wss://nostr.sandwich.farm']
]
}
const signedEvent = await sign(event, relay)

if(!signedEvent)
return

// console.log("sending to pool", signedEvent)
pool.send(['EVENT', signedEvent])
totalSent++
console.log('total sent, backlog', totalSent)
await delay(10*1000)
}
console.log('finished.')
}

async function sign(event, relay){
// console.log('event to sign', event)
event.pubkey = getPublicKey(process.env.PRIVATE_KEY)
event.id = getEventHash(event)
event.sig = await signEvent(event, process.env.PRIVATE_KEY)

let ok = validateEvent(event)
let veryOk = await verifySignature(event)

// if(relay)
// console.log('sign valid', relay, ':', ok, veryOk)
// else
// console.log('sign valid', ':', ok, veryOk)

if( ok && veryOk )
return event
else
console.error('event is invalid', event)
}

async function deletions(){
const tags = [
["e", "8e68215676f0bfcc386e3cc0d9e975e7fab1aed91d781c4ec3aac5f4c2c11e24"],
["e", "00834b0779cd0a87b6eeb5d25e22e887b007a30239a1e75cb567324a687e000b"],
["e", "783f57bfbeb3c4e1cd13cc493b021cb0c353ab98c1d02f5378dfdfb0afcc77fd"]
]

const event = {
"kind": 5,
created_at: Math.floor(Date.now()/1000),
"tags": tags,
"content": "delete dev posts"
}
const signedEvent = await sign(event)

if(!signedEvent)
return

console.log("sending to pool", signedEvent)

pool.send(['EVENT', signedEvent])

return
}

async function delay(ms) {
return new Promise( resolve => setTimeout(resolve, ms) )
}

function hash(relay){
return crypto.createHash('md5').update(relay).digest('hex');
}

run()
Empty file removed src/components/TaskManager.vue
Empty file.
Loading

0 comments on commit 2705805

Please sign in to comment.