Skip to content

Commit

Permalink
Merge pull request #24 from pinax-network/optional-private-key
Browse files Browse the repository at this point in the history
make privateKey optional
  • Loading branch information
DenisCarriere authored Feb 15, 2024
2 parents f31f24e + c286cca commit c95a95d
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 34 deletions.
6 changes: 2 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Webhook
PRIVATE_KEY=<Ed25519 Private Key>
WEBHOOK_URL=http://127.0.0.1:3000
PORT=9102
WEBHOOK_URL=http://127.0.0.1:3000

# Get Substreams API Key
# https://app.pinax.network
Expand All @@ -16,7 +15,6 @@ START_BLOCK=-10
PRODUCTION_MODE=true

# Webhook (Optional)
DISABLE_PING=false
DISABLE_SIGNATURE=false
PRIVATE_KEY=<Ed25519 Private Key>
VERBOSE=true
MAXIMUM_ATTEMPTS=100
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,8 @@ if (!isVerified) {

```env
# Webhook
PRIVATE_KEY=<Ed25519 Private Key>
WEBHOOK_URL=http://127.0.0.1:3000
PORT=9102
WEBHOOK_URL=http://127.0.0.1:3000
# Get Substreams API Key
# https://app.pinax.network
Expand All @@ -124,10 +123,9 @@ START_BLOCK=-10
PRODUCTION_MODE=true
# Webhook (Optional)
DISABLE_PING=false
DISABLE_SIGNATURE=false
VERBOSE=true
PRIVATE_KEY=<Ed25519 Private Key>
MAXIMUM_ATTEMPTS=100
VERBOSE=true
```

## Help
Expand Down Expand Up @@ -161,8 +159,6 @@ Options:
--verbose <boolean> Enable verbose logging (default: "false", env: VERBOSE)
--webhook-url <string> Webhook URL to send POST (env: WEBHOOK_URL)
--private-key <string> Ed25519 private key to sign POST data payload (env: PRIVATE_KEY)
--disable-ping <boolean> Disable ping on init (choices: "true", "false", default: false, env: DISABLE_PING)
--disable-signature <boolean> Disable Ed25519 signature (choices: "true", "false", default: false, env: DISABLE_SIGNATURE)
--maximum-attempts <number> Maximum attempts to retry POST (default: 100, env: MAXIMUM_ATTEMPTS)
-h, --help display help for command
```
Expand Down
9 changes: 2 additions & 7 deletions bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,18 @@ import { ping } from "../src/ping.js";

export interface WebhookRunOptions extends commander.RunOptions {
webhookUrl: string;
privateKey: string;
expiryTime: number;
privateKey?: string;
maximumAttempts: number;
disablePing: string;
disableSignature: string;
}

const webhookUrlOption = new Option("--webhook-url <string>", "Webhook URL to send POST").makeOptionMandatory().env("WEBHOOK_URL");
const privateKeyOption = new Option("--private-key <string>", "Ed25519 private key to sign POST data payload").makeOptionMandatory().env("PRIVATE_KEY");
const privateKeyOption = new Option("--private-key <string>", "Ed25519 private key to sign POST data payload").env("PRIVATE_KEY");

// Run Webhook Sink
const program = commander.program(pkg);
const command = commander.run(program, pkg);
command.addOption(webhookUrlOption);
command.addOption(privateKeyOption);
command.addOption(new Option("--disable-ping <boolean>", "Disable ping on init").choices(["true", "false"]).env("DISABLE_PING").default(false));
command.addOption(new Option("--disable-signature <boolean>", "Disable Ed25519 signature").choices(["true", "false"]).env("DISABLE_SIGNATURE").default(false));
command.addOption(new Option("--maximum-attempts <number>", "Maximum attempts to retry POST").env("MAXIMUM_ATTEMPTS").default(100));
command.action(action);

Expand Down
8 changes: 3 additions & 5 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ export async function action(options: WebhookRunOptions) {

// Ping URL to check if it's valid
const privateKey = parsePrivateKey(options.privateKey);
if (options.disablePing === "false") {
if (!(await ping(options.webhookUrl, privateKey))) {
logger.error("exiting from invalid PING response");
process.exit(1);
}
if (!(await ping(options.webhookUrl, privateKey))) {
logger.error("exiting from invalid PING response");
process.exit(1);
}
let session: SessionInit;
emitter.on("session", (data) => {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.8.5",
"version": "0.8.6",
"name": "substreams-sink-webhook",
"description": "Substreams Sink Webhook",
"type": "module",
Expand Down
3 changes: 2 additions & 1 deletion src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export function checkKey(key: Hex, type: "public" | "private") {

// TweetNaCl.js private key (includes public key)
// split the private key from the public key
export function parsePrivateKey(privateKey: string) {
export function parsePrivateKey(privateKey?: string) {
if (!privateKey) return;
if (typeof privateKey === "string" && privateKey.length === 128) {
return privateKey.slice(0, 64);
}
Expand Down
7 changes: 6 additions & 1 deletion src/ping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Hex } from "@noble/curves/abstract/utils";
import { keyPair } from "./auth.js";
import { postWebhook } from "./postWebhook.js";

export async function ping(url: string, privateKey: Hex) {
export async function ping(url: string, privateKey?: Hex) {
const body = JSON.stringify({ message: "PING" });
const invalidprivateKey = keyPair().privateKey;

Expand All @@ -13,6 +13,11 @@ export async function ping(url: string, privateKey: Hex) {
return false;
}

// if no private key is provided, we can't send an invalid signature
// so we can't test the response to an invalid signature
// so we return true
if (!privateKey) return true;

// send invalid signature (must NOT respond with 200)
try {
await postWebhook(url, body, invalidprivateKey, { maximumAttempts: 0 });
Expand Down
19 changes: 11 additions & 8 deletions src/postWebhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ interface PostWebhookOptions {
disableSignature?: string;
}

export async function postWebhook(url: string, body: string, secretKey: Hex, options: PostWebhookOptions = {}) {
export async function postWebhook(url: string, body: string, secretKey?: Hex, options: PostWebhookOptions = {}) {
// Retry Policy
const initialInterval = 1000; // 1s
const maximumAttempts = options.maximumAttempts ?? 100 * initialInterval;
const maximumInterval = 100 * initialInterval;
const backoffCoefficient = 2;
const disableSignature = options.disableSignature === "true";

let attempts = 0;
while (true) {
Expand All @@ -40,16 +39,20 @@ export async function postWebhook(url: string, body: string, secretKey: Hex, opt

try {
const timestamp = createTimestamp();
const signature = disableSignature ? "" : sign(timestamp, body, secretKey);
const headers = new Headers([
["content-type", "application/json"],
["x-signature-timestamp", String(timestamp)],
]);

// optional signature
if (secretKey) {
headers.set("x-signature-ed25519", sign(timestamp, body, secretKey));
}

const response = await fetch(url, {
body,
method: "POST",
headers: {
"content-type": "application/json",
"x-signature-ed25519": signature,
"x-signature-timestamp": String(timestamp),
},
headers,
});

const status = response.status;
Expand Down

0 comments on commit c95a95d

Please sign in to comment.