Add NIP-13 proof-of-work policy
This commit is contained in:
parent
3ab9364622
commit
d652bf7a94
11
deno.lock
11
deno.lock
@ -210,5 +210,16 @@
|
||||
"https://esm.sh/v113/nostr-tools@1.8.1/lib/references.d.ts": "b2e39f5c439380a1dc8e578b471ba3992f33e3936aa0eecf625734c7ba669098",
|
||||
"https://esm.sh/v113/nostr-tools@1.8.1/lib/relay.d.ts": "9bc6f2897a95ec12d128e94b2e7d6161d30a6bfe8d4350e217fbf4b710988db3",
|
||||
"https://esm.sh/v113/nostr-tools@1.8.1/lib/utils.d.ts": "6f37b09db0dce09f17ff7b70b921b9bb809bcb18c3bc058cb914afdf3c0e774e"
|
||||
},
|
||||
"npm": {
|
||||
"specifiers": {
|
||||
"@noble/secp256k1@^1.7.1": "@noble/secp256k1@1.7.1"
|
||||
},
|
||||
"packages": {
|
||||
"@noble/secp256k1@1.7.1": {
|
||||
"integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
mod.ts
1
mod.ts
@ -4,6 +4,7 @@ export { default as hellthreadPolicy, type Hellthread } from './src/policies/hel
|
||||
export { default as keywordPolicy } from './src/policies/keyword-policy.ts';
|
||||
export { default as noopPolicy } from './src/policies/noop-policy.ts';
|
||||
export { default as openaiPolicy, type OpenAI, type OpenAIHandler } from './src/policies/openai-policy.ts';
|
||||
export { default as powPolicy } from './src/policies/pow-policy.ts';
|
||||
export { default as pubkeyBanPolicy } from './src/policies/pubkey-ban-policy.ts';
|
||||
export { default as rateLimitPolicy, type RateLimit } from './src/policies/rate-limit-policy.ts';
|
||||
export { default as readOnlyPolicy } from './src/policies/read-only-policy.ts';
|
||||
|
@ -1,3 +1,4 @@
|
||||
export { readLines } from 'https://deno.land/std@0.181.0/io/mod.ts';
|
||||
export { assert, assertEquals } from 'https://deno.land/std@0.181.0/testing/asserts.ts';
|
||||
export { Keydb } from 'https://deno.land/x/keydb@1.0.0/sqlite.ts';
|
||||
export * as secp from 'npm:@noble/secp256k1@^1.7.1';
|
||||
|
20
src/policies/pow-policy.test.ts
Normal file
20
src/policies/pow-policy.test.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { assertEquals } from '../deps.ts';
|
||||
import { buildEvent, buildInputMessage } from '../test.ts';
|
||||
|
||||
import powPolicy from './pow-policy.ts';
|
||||
|
||||
Deno.test('blocks events without a nonce', async () => {
|
||||
const msg = buildInputMessage();
|
||||
assertEquals((await powPolicy(msg)).action, 'reject');
|
||||
});
|
||||
|
||||
Deno.test('accepts event with sufficient POW', async () => {
|
||||
const msg = buildInputMessage({
|
||||
event: buildEvent({
|
||||
id: '000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358',
|
||||
tags: [['nonce', '776797', '20']],
|
||||
}),
|
||||
});
|
||||
|
||||
assertEquals((await powPolicy(msg, { difficulty: 20 })).action, 'accept');
|
||||
});
|
74
src/policies/pow-policy.ts
Normal file
74
src/policies/pow-policy.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { secp } from '../deps.ts';
|
||||
|
||||
import type { Policy } from '../types.ts';
|
||||
|
||||
/** Policy options for `powPolicy`. */
|
||||
interface POW {
|
||||
/** Events will be rejected if their `id` does not contain at least this many leading `0`'s. Default: `1` */
|
||||
difficulty?: number;
|
||||
}
|
||||
|
||||
/** Reject events which don't meet Proof-of-Work ([NIP-13](https://github.com/nostr-protocol/nips/blob/master/13.md)) criteria. */
|
||||
const powPolicy: Policy<POW> = ({ event }, opts = {}) => {
|
||||
const { difficulty = 1 } = opts;
|
||||
|
||||
const pow = getPow(event.id);
|
||||
const nonce = event.tags.find((t) => t[0] === 'nonce');
|
||||
|
||||
if (pow >= difficulty && nonce && Number(nonce[2]) >= difficulty) {
|
||||
return {
|
||||
id: event.id,
|
||||
action: 'accept',
|
||||
msg: '',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: event.id,
|
||||
action: 'reject',
|
||||
msg: `pow: insufficient proof-of-work (difficulty ${difficulty})`,
|
||||
};
|
||||
};
|
||||
|
||||
/** Get POW difficulty from a Nostr hex ID. */
|
||||
function getPow(id: string): number {
|
||||
return getLeadingZeroBits(secp.utils.hexToBytes(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of leading 0 bits. Adapted from nostream.
|
||||
* https://github.com/Cameri/nostream/blob/fb6948fd83ca87ce552f39f9b5eb780ea07e272e/src/utils/proof-of-work.ts
|
||||
*/
|
||||
function getLeadingZeroBits(hash: Uint8Array): number {
|
||||
let total: number, i: number, bits: number;
|
||||
|
||||
for (i = 0, total = 0; i < hash.length; i++) {
|
||||
bits = msb(hash[i]);
|
||||
total += bits;
|
||||
if (bits !== 8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from nostream.
|
||||
* https://github.com/Cameri/nostream/blob/fb6948fd83ca87ce552f39f9b5eb780ea07e272e/src/utils/proof-of-work.ts
|
||||
*/
|
||||
function msb(b: number) {
|
||||
let n = 0;
|
||||
|
||||
if (b === 0) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-cond-assign
|
||||
while (b >>= 1) {
|
||||
n++;
|
||||
}
|
||||
|
||||
return 7 - n;
|
||||
}
|
||||
|
||||
export default powPolicy;
|
Loading…
Reference in New Issue
Block a user