Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"wrapped-keys/nodejs",
"hacker-guides/encryption/encrypt-file",
"hacker-guides/encryption/encrypt-large-file",
"hacker-guides/encryption/encrypt-string"
"hacker-guides/encryption/encrypt-string",
"sign-as-action/nodejs"
],
"scripts": {
"install:all": "yarn workspaces focus --all",
Expand Down
1 change: 1 addition & 0 deletions sign-as-action/nodejs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ETHEREUM_PRIVATE_KEY=
4 changes: 4 additions & 0 deletions sign-as-action/nodejs/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://json.schemastore.org/mocharc.json",
"require": "tsx"
}
Empty file added sign-as-action/nodejs/README.md
Empty file.
30 changes: 30 additions & 0 deletions sign-as-action/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "sign-as-action-nodejs",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module",
"scripts": {
"test": "npx @dotenvx/dotenvx run -- mocha test/**/*.spec.ts"
},
"dependencies": {
"@dotenvx/dotenvx": "^0.44.1",
"@lit-protocol/auth-helpers": "^7.0.4",
"@lit-protocol/constants": "^7.0.4",
"@lit-protocol/contracts-sdk": "^7.0.4",
"@lit-protocol/lit-node-client": "^7.0.4",
"ethers": "5.7.2"
},
"devDependencies": {
"@types/chai": "^4.3.16",
"@types/chai-json-schema": "^1.4.10",
"@types/mocha": "^10.0.6",
"chai": "4.5.0",
"chai-json-schema": "^1.5.1",
"ipfs-only-hash": "^4.0.0",
"mocha": "^10.4.0",
"tsc": "^2.0.4",
"tsx": "^4.12.0",
"typescript": "^5.4.5"
}
}
12 changes: 12 additions & 0 deletions sign-as-action/nodejs/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "sign-as-action-nodejs",
"root": "sign-as-action/nodejs",
"targets": {
"test-lit": {
"executor": "nx:run-commands",
"options": {
"command": "cd sign-as-action/nodejs && yarn && yarn test"
}
}
}
}
4 changes: 4 additions & 0 deletions sign-as-action/nodejs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./signAsAction";
export * from "./verifyActionSignature";
export * from './signAsActionLitAction';
export * from './verifyActionSignatureLitAction';
47 changes: 47 additions & 0 deletions sign-as-action/nodejs/src/signAsAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as ethers from "ethers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LIT_RPC, LIT_NETWORK } from "@lit-protocol/constants";

import { getEnv, getSessionSigs } from "./utils";
import { signAsActionLitAction } from "./signAsActionLitAction";

const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");

export const signAsAction = async ({ toSign }: { toSign: Uint8Array }) => {
let litNodeClient: LitNodeClient;

try {
const ethersSigner = new ethers.Wallet(
ETHEREUM_PRIVATE_KEY,
new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE)
);

console.log("🔄 Connecting to Lit network...");
litNodeClient = new LitNodeClient({
litNetwork: LIT_NETWORK.DatilDev,
debug: false,
});
await litNodeClient.connect();
console.log("✅ Connected to Lit network");

const sessionSigs = await getSessionSigs({ litNodeClient, ethersSigner });

console.log("🔄 Executing signAsAction Lit Action...");
const litActionSignature = await litNodeClient.executeJs({
sessionSigs,
code: signAsActionLitAction,
jsParams: {
toSign,
sigName: "sig",
signingScheme: "EcdsaK256Sha256",
},
});
console.log("✅ Executed signAsAction Lit Action");

return litActionSignature;
} catch (error) {
console.error(error);
} finally {
litNodeClient!.disconnect();
}
};
11 changes: 11 additions & 0 deletions sign-as-action/nodejs/src/signAsActionLitAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-nocheck
const _signAsActionLitAction = async () => {
const signature = await Lit.Actions.signAsAction({
toSign,
sigName,
signingScheme,
});
Lit.Actions.setResponse({ response: signature });
};

export const signAsActionLitAction = `(${_signAsActionLitAction.toString()})();`;
66 changes: 66 additions & 0 deletions sign-as-action/nodejs/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as ethers from "ethers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LIT_ABILITY } from "@lit-protocol/constants";
import {
createSiweMessageWithRecaps,
generateAuthSig,
LitActionResource,
} from "@lit-protocol/auth-helpers";

export const getEnv = (name: string): string => {
// Browser environment
if (typeof globalThis !== 'undefined' && 'window' in globalThis) {
const envMap: Record<string, string | undefined> = {
'ETHEREUM_PRIVATE_KEY': process.env.NEXT_PUBLIC_ETHEREUM_PRIVATE_KEY
};
const env = envMap[name];
if (env === undefined || env === "")
throw new Error(
`Browser: ${name} ENV is not defined, please define it in the .env file`
);
return env;
}

// Node environment
const env = process.env[name];
if (env === undefined || env === "")
throw new Error(
`Node: ${name} ENV is not defined, please define it in the .env file`
);
return env;
};

export const getSessionSigs = async ({ litNodeClient, ethersSigner }: { litNodeClient: LitNodeClient, ethersSigner: ethers.Wallet }) => {
console.log("🔄 Getting Session Signatures...");
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours
resourceAbilityRequests: [
{
resource: new LitActionResource("*"),
ability: LIT_ABILITY.LitActionExecution,
},
],
authNeededCallback: async ({
resourceAbilityRequests,
expiration,
uri,
}) => {
const toSign = await createSiweMessageWithRecaps({
uri: uri!,
expiration: expiration!,
resources: resourceAbilityRequests!,
walletAddress: ethersSigner.address,
nonce: await litNodeClient.getLatestBlockhash(),
litNodeClient,
});

return await generateAuthSig({
signer: ethersSigner,
toSign,
});
},
});
console.log("✅ Got Session Signatures");
return sessionSigs;
};
48 changes: 48 additions & 0 deletions sign-as-action/nodejs/src/verifyActionSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as ethers from "ethers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LIT_RPC, LIT_NETWORK } from "@lit-protocol/constants";

import { getEnv, getSessionSigs } from "./utils";
import { verifyActionSignatureLitAction } from "./verifyActionSignatureLitAction";

const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");

export const verifyActionSignature = async ({ actionIpfsCid, toSign, signOutput }: { actionIpfsCid: string, toSign: Uint8Array, signOutput: string }) => {
let litNodeClient: LitNodeClient;

try {
const ethersSigner = new ethers.Wallet(
ETHEREUM_PRIVATE_KEY,
new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE)
);

console.log("🔄 Connecting to Lit network...");
litNodeClient = new LitNodeClient({
litNetwork: LIT_NETWORK.DatilDev,
debug: false,
});
await litNodeClient.connect();
console.log("✅ Connected to Lit network");

const sessionSigs = await getSessionSigs({ litNodeClient, ethersSigner });

console.log("🔄 Executing verifyActionSignature Lit Action...");
const response = await litNodeClient.executeJs({
sessionSigs,
code: verifyActionSignatureLitAction,
jsParams: {
actionIpfsCid,
toSign,
signOutput,
signingScheme: "EcdsaK256Sha256",
},
});
console.log("✅ Executed verifyActionSignature Lit Action");

return response;
} catch (error) {
console.error(error);
} finally {
litNodeClient!.disconnect();
}
};
12 changes: 12 additions & 0 deletions sign-as-action/nodejs/src/verifyActionSignatureLitAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @ts-nocheck
const _verifyActionSignatureLitAction = async () => {
const result = await Lit.Actions.verifyActionSignature({
signingScheme,
actionIpfsCid,
toSign,
signOutput,
});
Lit.Actions.setResponse({ response: JSON.stringify(result) });
};

export const verifyActionSignatureLitAction = `(${_verifyActionSignatureLitAction.toString()})();`;
60 changes: 60 additions & 0 deletions sign-as-action/nodejs/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { expect, use } from "chai";
import chaiJsonSchema from "chai-json-schema";
import * as ethers from "ethers";
import IpfsHash from "ipfs-only-hash";

import { signAsAction, verifyActionSignature, signAsActionLitAction } from "../src";

use(chaiJsonSchema);

describe("Testing signAsAction and verifyActionSignature", () => {
let litActionSignatureString: string;

it("should sign and verify an action signature", async () => {
const signAsActionLitActionIpfsCid = await IpfsHash.of(signAsActionLitAction);
const message = new Uint8Array(
await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode("Hello world")
)
);
const messageHash = ethers.utils.hexlify(message);

const responseSchema = {
type: "object",
properties: {
response: { type: "string" },
success: { type: "boolean", const: true },
signedData: { type: "object" },
decryptedData: { type: "object" },
claimData: { type: "object" },
},
required: ["response", "success", "signedData", "decryptedData", "claimData"],
};

const result = await signAsAction({ toSign: message });
expect(result).to.be.jsonSchema(responseSchema);

litActionSignatureString = result!.response as string;

const litActionSignatureObject = JSON.parse(litActionSignatureString);
expect(litActionSignatureObject).to.be.jsonSchema(
{
type: "object",
properties: {
r: { type: "string", pattern: "^[a-f0-9]{64,66}$" },
s: { type: "string", pattern: "^[a-f0-9]{64,66}$" },
v: { type: "integer" },
},
required: ["r", "s", "v"],
}
);

const response = await verifyActionSignature({
actionIpfsCid: signAsActionLitActionIpfsCid,
toSign: message,
signOutput: litActionSignatureString
});
expect(response?.response).to.be.equal('true');
}).timeout(120_000);
});
Loading
Loading