From ba8bc5d9da5d896ada7ff08fa5f326b3be06d5d6 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Tue, 14 Mar 2023 17:32:39 +0000 Subject: [PATCH 1/2] added account custom errors --- light-macros | 2 +- light-sdk-ts/package.json | 1 + light-sdk-ts/src/account.ts | 165 ++++++++---- light-sdk-ts/src/errors.ts | 13 + light-sdk-ts/tests/account.test.ts | 342 +++++++++++++++++++++++++ light-sdk-ts/tests/tests.ts | 221 ---------------- light-sdk-ts/tests/transaction.test.ts | 1 - 7 files changed, 479 insertions(+), 266 deletions(-) create mode 100644 light-sdk-ts/tests/account.test.ts delete mode 100644 light-sdk-ts/tests/tests.ts diff --git a/light-macros b/light-macros index b988296653..5215de03fb 160000 --- a/light-macros +++ b/light-macros @@ -1 +1 @@ -Subproject commit b9882966536da239fd045cf2d2de5fec09d48a8f +Subproject commit 5215de03fbc7b39326ec502ac788005c93391eed diff --git a/light-sdk-ts/package.json b/light-sdk-ts/package.json index 5b93162477..75d4d6a50a 100644 --- a/light-sdk-ts/package.json +++ b/light-sdk-ts/package.json @@ -9,6 +9,7 @@ "test-utxo": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/utxo.test.ts --exit", "test-verifiers": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/verifiers.test.ts --exit", "test-transaction": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/transaction.test.ts --exit", + "test-account": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/account.test.ts --exit", "build": "yarn tsc", "format": "prettier --write \"src/**/*.{ts,js}\"", "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", diff --git a/light-sdk-ts/src/account.ts b/light-sdk-ts/src/account.ts index ada30d2773..36e9a2bff5 100644 --- a/light-sdk-ts/src/account.ts +++ b/light-sdk-ts/src/account.ts @@ -1,8 +1,14 @@ const nacl = require("tweetnacl"); import { BN } from "@coral-xyz/anchor"; +import { + AccountError, + AccountErrorCode, + TransactionParametersErrorCode, +} from "./errors"; const { blake2b } = require("@noble/hashes/blake2b"); const b2params = { dkLen: 32 }; const ffjavascript = require("ffjavascript"); +import { buildEddsa } from "circomlibjs"; export class Account { /** @@ -17,7 +23,7 @@ export class Account { poseidon: any; burnerSeed: Uint8Array; // keypair for eddsa poseidon signatures - poseidonEddsa?: { + poseidonEddsaKeypair?: { publicKey?: [Uint8Array, Uint8Array]; privateKey: Uint8Array; }; @@ -41,6 +47,7 @@ export class Account { poseidonEddsaPrivateKey, eddsa, encryptionPublicKey, + encryptionPrivateKey, }: { poseidon?: any; seed?: string; @@ -50,16 +57,33 @@ export class Account { poseidonEddsaPrivateKey?: Uint8Array; eddsa?: any; encryptionPublicKey?: Uint8Array; + encryptionPrivateKey?: Uint8Array; }) { - if (seed.length < 32) { - throw "seed too short length less than 32"; - } - if (poseidon) { - this.poseidon = poseidon; + if (!poseidon) { + throw new AccountError( + TransactionParametersErrorCode.NO_POSEIDON_HASHER_PROVIDED, + "constructor", + ); } + + this.poseidon = poseidon; this.burnerSeed = new Uint8Array(); // creates a burner utxo by using the index for domain separation if (burner) { + if (!seed) { + throw new AccountError( + AccountErrorCode.SEED_UNDEFINED, + "constructor", + "seed is required to create a burner account", + ); + } + if (seed.length < 32) { + throw new AccountError( + AccountErrorCode.INVALID_SEED_SIZE, + "constructor", + "seed too short length less than 32", + ); + } // burnerSeed can be shared since hash cannot be inverted - only share this for app utxos // sharing the burnerSeed saves 32 bytes in onchain data if it is require to share both // the encryption and private key of a utxo @@ -70,22 +94,26 @@ export class Account { this.privkey, this.poseidon, ); - this.poseidonEddsa = Account.getEddsaPrivateKey( + this.poseidonEddsaKeypair = Account.getEddsaPrivateKey( this.burnerSeed.toString(), ); } else if (privateKey) { + if (!encryptionPrivateKey) { + throw new AccountError( + AccountErrorCode.ENCRYPTION_PRIVATE_KEY_UNDEFINED, + "constructor", + ); + } this.privkey = privateKey; this.pubkey = Account.generateShieldedPublicKey( this.privkey, this.poseidon, ); if (poseidonEddsaPrivateKey) { - this.poseidonEddsa = { privateKey: poseidonEddsaPrivateKey }; + this.poseidonEddsaKeypair = { privateKey: poseidonEddsaPrivateKey }; } - this.encryptionKeypair = { - publicKey: encryptionPublicKey ? encryptionPublicKey : new Uint8Array(), - secretKey: new Uint8Array(), - }; + this.encryptionKeypair = + nacl.box.keyPair.fromSecretKey(encryptionPrivateKey); } else if (publicKey) { this.pubkey = publicKey; this.privkey = new BN("0"); @@ -94,34 +122,69 @@ export class Account { secretKey: new Uint8Array(), }; } else { + if (!seed) { + throw new AccountError( + AccountErrorCode.SEED_UNDEFINED, + "constructor", + "seed is required to create an account", + ); + } + if (seed.length < 32) { + throw new AccountError( + AccountErrorCode.INVALID_SEED_SIZE, + "constructor", + "seed too short length less than 32", + ); + } this.encryptionKeypair = Account.getEncryptionKeyPair(seed); this.privkey = Account.generateShieldedPrivateKey(seed); this.pubkey = Account.generateShieldedPublicKey( this.privkey, this.poseidon, ); - this.poseidonEddsa = Account.getEddsaPrivateKey(seed); + this.poseidonEddsaKeypair = Account.getEddsaPrivateKey(seed); } this.eddsa = eddsa; } - async getEddsaPublicKey(): Promise<[Uint8Array, Uint8Array]> { - if (this.poseidonEddsa && this.eddsa) { - this.poseidonEddsa.publicKey = this.eddsa.prv2pub( - this.poseidonEddsa.privateKey, + async getEddsaPublicKey(eddsa?: any): Promise<[Uint8Array, Uint8Array]> { + if (!this.poseidonEddsaKeypair) { + throw new AccountError( + AccountErrorCode.POSEIDON_EDDSA_KEYPAIR_UNDEFINED, + "getEddsaPublicKey", + "poseidonEddsaKeypair.privateKey undefined", + ); + } + if (!this.poseidonEddsaKeypair.privateKey) { + throw new AccountError( + AccountErrorCode.POSEIDON_EDDSA_KEYPAIR_UNDEFINED, + "getEddsaPublicKey", + "poseidonEddsaKeypair.privateKey undefined", ); - if (this.poseidonEddsa.publicKey) { - return this.poseidonEddsa.publicKey; + } + if (!this.eddsa) { + if (eddsa) { + this.eddsa = eddsa; } else { - throw new Error("get poseidonEddsa.publicKey failed"); + this.eddsa = await buildEddsa(); } - } else { - throw new Error("poseidonEddsa.privateKey undefined"); } + + this.poseidonEddsaKeypair.publicKey = this.eddsa.prv2pub( + this.poseidonEddsaKeypair.privateKey, + ); + if (!this.poseidonEddsaKeypair.publicKey) { + throw new AccountError( + AccountErrorCode.POSEIDON_EDDSA_GET_PUBKEY_FAILED, + "signEddsa", + "poseidonEddsaKeypair.privateKey undefined", + ); + } + return this.poseidonEddsaKeypair.publicKey; } static getEddsaPrivateKey(seed: string) { - const privkeySeed = seed + "poseidonEddsa"; + const privkeySeed = seed + "poseidonEddsaKeypair"; return { publicKey: undefined, privateKey: blake2b.create(b2params).update(privkeySeed).digest(), @@ -132,31 +195,35 @@ export class Account { return new BN(this.encryptionKeypair.publicKey).toBuffer("be", 32); } - // TODO: make eddsa wrapper class - // TODO: include eddsa into static from methods + // TODO: Add check for uint8array to be well formed async signEddsa(msg: string | Uint8Array, eddsa?: any): Promise { + if (!this.poseidonEddsaKeypair) { + throw new AccountError( + AccountErrorCode.POSEIDON_EDDSA_KEYPAIR_UNDEFINED, + "signEddsa", + "poseidonEddsaKeypair.privateKey undefined", + ); + } + if (!this.eddsa) { - if (!eddsa) { + if (eddsa) { this.eddsa = eddsa; } else { - throw new Error("Eddsa is not provided"); + this.eddsa = await buildEddsa(); } } - if (this.poseidonEddsa) { - if (typeof msg == "string") { - return this.eddsa.packSignature( - this.eddsa.signPoseidon( - this.poseidonEddsa.privateKey, - this.poseidon.F.e(ffjavascript.Scalar.e(msg)), - ), - ); - } else { - return this.eddsa.packSignature( - this.eddsa.signPoseidon(this.poseidonEddsa.privateKey, msg), - ); - } + + if (typeof msg == "string") { + return this.eddsa.packSignature( + this.eddsa.signPoseidon( + this.poseidonEddsaKeypair.privateKey, + this.poseidon.F.e(ffjavascript.Scalar.e(msg)), + ), + ); } else { - throw new Error("poseidonEddsa.privateKey undefined"); + return this.eddsa.packSignature( + this.eddsa.signPoseidon(this.poseidonEddsaKeypair.privateKey, msg), + ); } } @@ -178,6 +245,13 @@ export class Account { } static createBurner(poseidon: any, seed: String, index: BN): Account { + if (seed.length < 32) { + throw new AccountError( + AccountErrorCode.INVALID_SEED_SIZE, + "constructor", + "seed too short length less than 32", + ); + } const burnerSeed = blake2b .create(b2params) .update(seed + "burnerSeed" + index.toString("hex")) @@ -192,10 +266,15 @@ export class Account { return new Account({ poseidon, seed: burnerSeedString, burner: true }); } - static fromPrivkey(poseidon: any, privateKey: Uint8Array): Account { + static fromPrivkey( + poseidon: any, + privateKey: Uint8Array, + encryptionPrivateKey: Uint8Array, + ): Account { const privkey = new BN(privateKey); - return new Account({ poseidon, privateKey: privkey }); + return new Account({ poseidon, privateKey: privkey, encryptionPrivateKey }); } + static fromPubkey( publicKey: Uint8Array, encPubkey: Uint8Array, diff --git a/light-sdk-ts/src/errors.ts b/light-sdk-ts/src/errors.ts index e3c0f43db3..118140e30e 100644 --- a/light-sdk-ts/src/errors.ts +++ b/light-sdk-ts/src/errors.ts @@ -10,6 +10,17 @@ export enum UtxoErrorCode { ASSET_NOT_FOUND = "ASSET_NOT_FOUND", } +export enum AccountErrorCode { + INVALID_SEED_SIZE = "INVALID_SEED_SIZE", + SEED_UNDEFINED = "SEED_UNDEFINED", + SEED_DEFINED = "SEED_DEFINED", + ENCRYPTION_PRIVATE_KEY_UNDEFINED = "ENCRYPTION_PRIVATE_KEY_UNDEFINED", + PRIVATE_KEY_UNDEFINED = "PRIVATE_KEY_UNDEFINED", + POSEIDON_EDDSA_KEYPAIR_UNDEFINED = "POSEIDON_EDDSA_KEYPAIR_UNDEFINED", + POSEIDON_EDDSA_GET_PUBKEY_FAILED = "POSEIDON_EDDSA_GET_PUBKEY_FAILED", + PUBLIC_KEY_UNDEFINED = "PUBLIC_KEY_UNDEFINED", +} + export enum ProviderErrorCode { SOL_MERKLE_TREE_UNDEFINED = "SOL_MERKLE_TREE_UNDEFINED", ANCHOR_PROVIDER_UNDEFINED = "ANCHOR_PROVIDER_UNDEFINED", @@ -109,3 +120,5 @@ export class TransactioParametersError extends MetaError {} export class UtxoError extends MetaError {} export class VerifierError extends MetaError {} + +export class AccountError extends MetaError {} diff --git a/light-sdk-ts/tests/account.test.ts b/light-sdk-ts/tests/account.test.ts new file mode 100644 index 0000000000..3bb6bcc6bd --- /dev/null +++ b/light-sdk-ts/tests/account.test.ts @@ -0,0 +1,342 @@ +import { assert, expect } from "chai"; +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +// Load chai-as-promised support +chai.use(chaiAsPromised); +let circomlibjs = require("circomlibjs"); +import { SystemProgram, Keypair as SolanaKeypair } from "@solana/web3.js"; +import * as anchor from "@coral-xyz/anchor"; +import { it } from "mocha"; +import { buildPoseidonOpt, buildBabyjub, buildEddsa } from "circomlibjs"; +import { Scalar } from "ffjavascript"; + +import { Account } from "../src/account"; +import { + FEE_ASSET, + functionalCircuitTest, + hashAndTruncateToCircuit, + Provider as LightProvider, + MINT, + Transaction, + UtxoError, + UtxoErrorCode, + TransactionError, + TransactionErrorCode, + ProviderErrorCode, + Provider, + TransactionParameters, + VerifierZero, + Action, + Relayer, + AccountError, + AccountErrorCode, + TransactionParametersErrorCode, +} from "../src"; +const { blake2b } = require("@noble/hashes/blake2b"); +const b2params = { dkLen: 32 }; +process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; +process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; +let seed32 = new Uint8Array(32).fill(1).toString(); + +describe("Test Account Functional", () => { + var poseidon, eddsa, babyJub, F, k0: Account, k00: Account, kBurner: Account; + before(async () => { + poseidon = await circomlibjs.buildPoseidonOpt(); + eddsa = await buildEddsa(); + babyJub = await buildBabyjub(); + F = babyJub.F; + k0 = new Account({ poseidon, seed: seed32 }); + k00 = new Account({ poseidon, seed: seed32 }); + kBurner = Account.createBurner(poseidon, seed32, new anchor.BN("0")); + }); + + it("Test blake2 Domain separation", () => { + let seed = "123"; + let seedHash = blake2b.create(b2params).update(seed).digest(); + let encSeed = seedHash + "encryption"; + let privkeySeed = seedHash + "privkey"; + let privkeyHash = blake2b.create(b2params).update(privkeySeed).digest(); + let encHash = blake2b.create(b2params).update(encSeed).digest(); + + assert.notEqual(encHash, seedHash); + assert.notEqual(privkeyHash, seedHash); + assert.notEqual(encHash, privkeyHash); + }); + + it("Test poseidon", async () => { + let x = new Array(30).fill(1); + let y = new Array(30).fill(2); + + let hash = poseidon.F.toString( + poseidon([new anchor.BN(x).toString(), new anchor.BN(y).toString()]), + ); + + x = new Array(29).fill(1); + y = new Array(31).fill(2); + y[30] = 1; + + const hash1 = poseidon.F.toString( + poseidon([new anchor.BN(x).toString(), new anchor.BN(y).toString()]), + ); + assert.notEqual(hash, hash1); + }); + + it("Test Poseidon Eddsa Keypair", async () => { + let seed32 = new Uint8Array(32).fill(1).toString(); + let k0 = new Account({ poseidon, seed: seed32, eddsa }); + + const prvKey = blake2b + .create(b2params) + .update(seed32 + "poseidonEddsaKeypair") + .digest(); + const pubKey = eddsa.prv2pub(prvKey); + k0.getEddsaPublicKey(); + if (k0.poseidonEddsaKeypair && k0.poseidonEddsaKeypair.publicKey) { + assert.equal( + prvKey.toString(), + k0.poseidonEddsaKeypair.privateKey.toString(), + ); + assert.equal( + pubKey[0].toString(), + k0.poseidonEddsaKeypair.publicKey[0].toString(), + ); + assert.equal( + pubKey[1].toString(), + k0.poseidonEddsaKeypair.publicKey[1].toString(), + ); + } else { + throw new Error("k0.poseidonEddsaKeypair undefined"); + } + + const msg = "12321"; + const sigK0 = await k0.signEddsa(msg); + assert.equal( + sigK0.toString(), + eddsa.packSignature(eddsa.signPoseidon(prvKey, F.e(Scalar.e(msg)))), + ); + assert(eddsa.verifyPoseidon(msg, eddsa.unpackSignature(sigK0), pubKey)); + }); + const compareKeypairsEqual = ( + k0: Account, + k1: Account, + fromPrivkey: Boolean = false, + ) => { + assert.equal(k0.privkey.toString(), k1.privkey.toString()); + assert.equal(k0.pubkey.toString(), k1.pubkey.toString()); + assert.equal(k0.burnerSeed.toString(), k1.burnerSeed.toString()); + if (!fromPrivkey) { + assert.equal( + k0.encryptionKeypair.publicKey.toString(), + k1.encryptionKeypair.publicKey.toString(), + ); + } + }; + + const compareKeypairsNotEqual = ( + k0: Account, + k1: Account, + burner = false, + ) => { + assert.notEqual(k0.privkey.toString(), k1.privkey.toString()); + assert.notEqual( + k0.encryptionKeypair.publicKey.toString(), + k1.encryptionKeypair.publicKey.toString(), + ); + assert.notEqual(k0.pubkey.toString(), k1.pubkey.toString()); + if (burner) { + assert.notEqual(k0.burnerSeed.toString(), k1.burnerSeed.toString()); + } + }; + + it("Functional", async () => { + // generate the same keypair from seed + compareKeypairsEqual(k0, k00); + + // functional reference + assert.equal( + k0.encryptionKeypair.publicKey.toString(), + "79,88,143,40,214,78,70,137,196,5,122,152,24,73,163,196,183,217,173,186,135,188,91,113,160,128,183,111,110,245,183,96", + ); + assert.equal( + k0.privkey.toString(), + "72081772318062199533713901017818635304770734661701934546410527310990294418314", + ); + assert.equal( + k0.pubkey.toString(), + "17617449169454204288593541557256537870126094878332671558512052528902373564643", + ); + assert.equal( + (await k0.signEddsa("12321")).toString(), + "212,157,228,136,102,128,200,55,198,76,182,145,197,253,21,162,44,1,96,155,169,90,154,102,119,222,224,151,18,121,71,15,96,116,148,29,69,204,94,22,119,89,152,185,128,45,25,73,227,245,247,13,19,51,95,1,86,67,111,212,63,92,213,0", + ); + + assert.equal( + (await k0.signEddsa("12321", eddsa)).toString(), + "212,157,228,136,102,128,200,55,198,76,182,145,197,253,21,162,44,1,96,155,169,90,154,102,119,222,224,151,18,121,71,15,96,116,148,29,69,204,94,22,119,89,152,185,128,45,25,73,227,245,247,13,19,51,95,1,86,67,111,212,63,92,213,0", + ); + + assert.equal( + (await k0.signEddsa("12321", eddsa)).toString(), + "212,157,228,136,102,128,200,55,198,76,182,145,197,253,21,162,44,1,96,155,169,90,154,102,119,222,224,151,18,121,71,15,96,116,148,29,69,204,94,22,119,89,152,185,128,45,25,73,227,245,247,13,19,51,95,1,86,67,111,212,63,92,213,0", + ); + + let seedDiff32 = new Uint8Array(32).fill(2).toString(); + let k1 = new Account({ poseidon, seed: seedDiff32 }); + // keypairs from different seeds are not equal + compareKeypairsNotEqual(k0, k1); + }); + + it("Burner functional", async () => { + // functional reference burner + assert.equal( + kBurner.encryptionKeypair.publicKey.toString(), + "118,44,67,51,130,2,17,15,16,119,197,218,27,218,191,249,95,51,193,62,252,27,59,71,151,12,244,206,103,244,155,13", + ); + assert.equal( + kBurner.privkey.toString(), + "81841610170886826015335465607758273107896278528010278185780510216694719969226", + ); + assert.equal( + kBurner.pubkey.toString(), + "3672531747475455051184163226139092471034744667609536681047180780320195966514", + ); + assert.equal( + Array.from(kBurner.burnerSeed).toString(), + "142,254,65,39,85,90,174,142,146,117,207,76,115,140,59,91,85,155,236,166,1,144,219,206,240,188,218,10,215,93,41,213", + ); + + assert.equal( + (await kBurner.signEddsa("12321")).toString(), + "79,54,246,128,173,120,190,144,139,170,213,115,226,103,155,253,214,137,30,177,186,67,128,53,164,240,81,55,138,98,181,34,121,204,42,16,191,189,18,169,230,169,65,46,94,168,211,137,21,79,175,171,187,86,59,162,202,118,45,229,189,84,146,2", + ); + + assert.equal( + (await kBurner.signEddsa("12321", eddsa)).toString(), + "79,54,246,128,173,120,190,144,139,170,213,115,226,103,155,253,214,137,30,177,186,67,128,53,164,240,81,55,138,98,181,34,121,204,42,16,191,189,18,169,230,169,65,46,94,168,211,137,21,79,175,171,187,86,59,162,202,118,45,229,189,84,146,2", + ); + + // burners and regular keypair from the same seed are not equal + compareKeypairsNotEqual(k0, kBurner, true); + + let kBurner2 = Account.fromBurnerSeed(poseidon, kBurner.burnerSeed); + compareKeypairsEqual(kBurner2, kBurner); + compareKeypairsNotEqual(k0, kBurner2, true); + }); + + it("Burner same index & keypair eq", () => { + let kBurner0 = Account.createBurner(poseidon, seed32, new anchor.BN("0")); + // burners with the same index from the same seed are the equal + compareKeypairsEqual(kBurner0, kBurner); + }); + + it("Burner diff index & keypair neq", () => { + let kBurner0 = Account.createBurner(poseidon, seed32, new anchor.BN("0")); + // burners with the same index from the same seed are the equal + compareKeypairsEqual(kBurner0, kBurner); + let kBurner1 = Account.createBurner(poseidon, seed32, new anchor.BN("1")); + // burners with incrementing index are not equal + compareKeypairsNotEqual(kBurner1, kBurner0, true); + }); + + it("fromPrivkey", () => { + let k0Privkey = Account.fromPrivkey( + poseidon, + k0.privkey.toBuffer("be", 32), + k0.encryptionKeypair.secretKey, + ); + compareKeypairsEqual(k0Privkey, k0, true); + }); + + it("fromPubkey", () => { + let k0Pubkey = Account.fromPubkey( + k0.pubkey.toBuffer("be", 32), + k0.encryptionKeypair.publicKey, + poseidon, + ); + assert.equal(k0Pubkey.pubkey.toString(), k0.pubkey.toString()); + assert.notEqual(k0Pubkey.privkey, k0.privkey); + }); +}); + +describe("Test Account Errors", () => { + var poseidon, eddsa, babyJub, F, k0: Account, k00: Account, kBurner: Account; + before(async () => { + poseidon = await circomlibjs.buildPoseidonOpt(); + eddsa = await buildEddsa(); + babyJub = await buildBabyjub(); + F = babyJub.F; + k0 = new Account({ poseidon, seed: seed32 }); + k00 = new Account({ poseidon, seed: seed32 }); + kBurner = Account.createBurner(poseidon, seed32, new anchor.BN("0")); + }); + + it("INVALID_SEED_SIZE", async () => { + expect(() => { + new Account({ poseidon, seed: "123" }); + }) + .to.throw(AccountError) + .includes({ + code: AccountErrorCode.INVALID_SEED_SIZE, + functionName: "constructor", + }); + }); + + it("INVALID_SEED_SIZE burner", async () => { + expect(() => { + new Account({ poseidon, seed: "123", burner: true }); + }) + .to.throw(AccountError) + .includes({ + code: AccountErrorCode.INVALID_SEED_SIZE, + functionName: "constructor", + }); + }); + + it("NO_POSEIDON_HASHER_PROVIDED", async () => { + expect(() => { + new Account({ seed: "123" }); + }) + .to.throw(AccountError) + .includes({ + code: TransactionParametersErrorCode.NO_POSEIDON_HASHER_PROVIDED, + functionName: "constructor", + }); + }); + + it("ENCRYPTION_PRIVATE_KEY_UNDEFINED", async () => { + expect(() => { + // @ts-ignore + Account.fromPrivkey(poseidon, k0.privkey.toBuffer("be", 32)); + }) + .to.throw(AccountError) + .includes({ + code: AccountErrorCode.ENCRYPTION_PRIVATE_KEY_UNDEFINED, + functionName: "constructor", + }); + }); + + it("POSEIDON_EDDSA_KEYPAIR_UNDEFINED getEddsaPublicKey", async () => { + const account = Account.fromPubkey( + k0.pubkey.toBuffer("be", 32), + k0.encryptionKeypair.publicKey, + poseidon, + ); + await chai.assert.isRejected( + account.getEddsaPublicKey(), + AccountErrorCode.POSEIDON_EDDSA_KEYPAIR_UNDEFINED, + ); + }); + + it("POSEIDON_EDDSA_KEYPAIR_UNDEFINED signEddsa", async () => { + const account = Account.fromPubkey( + k0.pubkey.toBuffer("be", 32), + k0.encryptionKeypair.publicKey, + poseidon, + ); + await chai.assert.isRejected( + account.signEddsa("123123"), + AccountErrorCode.POSEIDON_EDDSA_KEYPAIR_UNDEFINED, + ); + }); +}); diff --git a/light-sdk-ts/tests/tests.ts b/light-sdk-ts/tests/tests.ts deleted file mode 100644 index c4dac3d900..0000000000 --- a/light-sdk-ts/tests/tests.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { assert, expect } from "chai"; -var chaiAsPromised = require("chai-as-promised"); -let circomlibjs = require("circomlibjs"); -import { SystemProgram, Keypair as SolanaKeypair } from "@solana/web3.js"; -import * as anchor from "@coral-xyz/anchor"; -import { it } from "mocha"; -import { buildPoseidonOpt, buildBabyjub, buildEddsa } from "circomlibjs"; -import { Scalar } from "ffjavascript"; - -import { Account } from "../src/account"; -import { Utxo } from "../src/utxo"; -import { - FEE_ASSET, - functionalCircuitTest, - hashAndTruncateToCircuit, - Provider as LightProvider, - MINT, - Transaction, - UtxoError, - UtxoErrorCode, - TransactionError, - TransactionErrorCode, - ProviderErrorCode, - Provider, - TransactionParameters, - VerifierZero, - Action, - Relayer, -} from "../src"; -const { blake2b } = require("@noble/hashes/blake2b"); -const b2params = { dkLen: 32 }; - -describe("verifier_program", () => { - process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; - process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; - - it("Test poseidon", async () => { - const poseidon = await circomlibjs.buildPoseidonOpt(); - - let x = new Array(32).fill(1); - let y = new Array(32).fill(2); - - let hash = poseidon.F.toString( - poseidon([new anchor.BN(x).toString(), new anchor.BN(y).toString()]), - ); - console.log(new anchor.BN(hash).toArray("le", 32)); - - x = new Array(32).fill(3); - y = new Array(32).fill(3); - - hash = poseidon.F.toString( - poseidon([new anchor.BN(x).toString(), new anchor.BN(y).toString()]), - ); - console.log(new anchor.BN(hash).toArray("be", 32)); - }); - - it("Test Keypair Poseidon Eddsa", async () => { - const poseidon = await circomlibjs.buildPoseidonOpt(); - let eddsa = await buildEddsa(); - const babyJub = await buildBabyjub(); - const F = babyJub.F; - let seed32 = new Uint8Array(32).fill(1).toString(); - let k0 = new Account({ poseidon, seed: seed32, eddsa }); - - const prvKey = blake2b - .create(b2params) - .update(seed32 + "poseidonEddsa") - .digest(); - const pubKey = eddsa.prv2pub(prvKey); - k0.getEddsaPublicKey(); - if (k0.poseidonEddsa && k0.poseidonEddsa.publicKey) { - assert.equal(prvKey.toString(), k0.poseidonEddsa.privateKey.toString()); - assert.equal( - pubKey[0].toString(), - k0.poseidonEddsa.publicKey[0].toString(), - ); - assert.equal( - pubKey[1].toString(), - k0.poseidonEddsa.publicKey[1].toString(), - ); - } else { - throw new Error("k0.poseidonEddsa undefined"); - } - - const msg = "12321"; - const sigK0 = await k0.signEddsa(msg); - assert.equal( - sigK0.toString(), - eddsa.packSignature(eddsa.signPoseidon(prvKey, F.e(Scalar.e(msg)))), - ); - assert(eddsa.verifyPoseidon(msg, eddsa.unpackSignature(sigK0), pubKey)); - }); - - // TODO: rename to 'Test Account' - it("Test Keypair", async () => { - const poseidon = await circomlibjs.buildPoseidonOpt(); - - let seed = "123"; - let seedHash = blake2b.create(b2params).update(seed).digest(); - let encSeed = seed + "encryption"; - let encHash = blake2b.create(b2params).update(encSeed).digest(); - let privkeySeed = seed + "privkey"; - let privkeyHash = blake2b.create(b2params).update(privkeySeed).digest(); - - assert.notEqual(encHash, seedHash); - assert.notEqual(privkeyHash, seedHash); - assert.notEqual(encHash, privkeyHash); - try { - expect(new Account({ poseidon, seed: "123" })).to.throw(); - } catch (e) { - assert.isTrue( - e.toString().includes("seed too short length less than 32"), - ); - } - - const compareKeypairsEqual = ( - k0: Account, - k1: Account, - fromPrivkey: Boolean = false, - ) => { - assert.equal(k0.privkey.toString(), k1.privkey.toString()); - assert.equal(k0.pubkey.toString(), k1.pubkey.toString()); - assert.equal(k0.burnerSeed.toString(), k1.burnerSeed.toString()); - if (!fromPrivkey) { - assert.equal( - k0.encryptionKeypair.publicKey.toString(), - k1.encryptionKeypair.publicKey.toString(), - ); - } - }; - - const compareKeypairsNotEqual = ( - k0: Account, - k1: Account, - burner = false, - ) => { - assert.notEqual(k0.privkey.toString(), k1.privkey.toString()); - assert.notEqual( - k0.encryptionKeypair.publicKey.toString(), - k1.encryptionKeypair.publicKey.toString(), - ); - assert.notEqual(k0.pubkey.toString(), k1.pubkey.toString()); - if (burner) { - assert.notEqual(k0.burnerSeed.toString(), k1.burnerSeed.toString()); - } - }; - - let seed32 = new Uint8Array(32).fill(1).toString(); - let k0 = new Account({ poseidon, seed: seed32 }); - let k00 = new Account({ poseidon, seed: seed32 }); - // generate the same keypair from seed - compareKeypairsEqual(k0, k00); - - // functional reference - assert.equal( - k0.encryptionKeypair.publicKey.toString(), - "79,88,143,40,214,78,70,137,196,5,122,152,24,73,163,196,183,217,173,186,135,188,91,113,160,128,183,111,110,245,183,96", - ); - assert.equal( - k0.privkey.toString(), - "72081772318062199533713901017818635304770734661701934546410527310990294418314", - ); - assert.equal( - k0.pubkey.toString(), - "17617449169454204288593541557256537870126094878332671558512052528902373564643", - ); - - let seedDiff32 = new Uint8Array(32).fill(2).toString(); - let k1 = new Account({ poseidon, seed: seedDiff32 }); - // keypairs from different seeds are not equal - compareKeypairsNotEqual(k0, k1); - - // functional reference burner - let kBurner = Account.createBurner(poseidon, seed32, new anchor.BN("0")); - assert.equal( - kBurner.encryptionKeypair.publicKey.toString(), - "118,44,67,51,130,2,17,15,16,119,197,218,27,218,191,249,95,51,193,62,252,27,59,71,151,12,244,206,103,244,155,13", - ); - assert.equal( - kBurner.privkey.toString(), - "81841610170886826015335465607758273107896278528010278185780510216694719969226", - ); - assert.equal( - kBurner.pubkey.toString(), - "3672531747475455051184163226139092471034744667609536681047180780320195966514", - ); - assert.equal( - Array.from(kBurner.burnerSeed).toString(), - "142,254,65,39,85,90,174,142,146,117,207,76,115,140,59,91,85,155,236,166,1,144,219,206,240,188,218,10,215,93,41,213", - ); - - // burners and regular keypair from the same seed are not equal - compareKeypairsNotEqual(k0, kBurner, true); - let kBurner0 = Account.createBurner(poseidon, seed32, new anchor.BN("0")); - // burners with the same index from the same seed are the equal - compareKeypairsEqual(kBurner0, kBurner); - let kBurner1 = Account.createBurner(poseidon, seed32, new anchor.BN("1")); - // burners with incrementing index are not equal - compareKeypairsNotEqual(kBurner1, kBurner0, true); - - let kBurner2 = Account.fromBurnerSeed(poseidon, kBurner.burnerSeed); - compareKeypairsEqual(kBurner2, kBurner); - compareKeypairsNotEqual(k0, kBurner2, true); - - // fromPrivkey - let k0Privkey = Account.fromPrivkey( - poseidon, - k0.privkey.toBuffer("be", 32), - ); - compareKeypairsEqual(k0Privkey, k0, true); - - // fromPubkey - let k0Pubkey = Account.fromPubkey( - k0.pubkey.toBuffer("be", 32), - k0.encryptionKeypair.publicKey, - poseidon, - ); - assert.equal(k0Pubkey.pubkey.toString(), k0.pubkey.toString()); - assert.notEqual(k0Pubkey.privkey, k0.privkey); - }); -}); diff --git a/light-sdk-ts/tests/transaction.test.ts b/light-sdk-ts/tests/transaction.test.ts index 7eb8c885b1..55566c2b1d 100644 --- a/light-sdk-ts/tests/transaction.test.ts +++ b/light-sdk-ts/tests/transaction.test.ts @@ -88,7 +88,6 @@ describe("Transaction Error Tests", () => { }); }); - it("Constructor PROVIDER_UNDEFINED", async () => { expect(() => { // @ts-ignore: From 5514f7e41dabf7be501f45436faa44232d0976b8 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Tue, 14 Mar 2023 17:51:29 +0000 Subject: [PATCH 2/2] removed sdk build errors --- light-circuits/yarn.lock | 42 ++++++++------------------------- light-sdk-ts/src/account.ts | 1 + light-sdk-ts/src/utxo.ts | 2 +- light-system-programs/yarn.lock | 4 ++-- mock-app-verifier/yarn.lock | 9 ++----- 5 files changed, 16 insertions(+), 42 deletions(-) diff --git a/light-circuits/yarn.lock b/light-circuits/yarn.lock index e8cc637739..070c8cb519 100644 --- a/light-circuits/yarn.lock +++ b/light-circuits/yarn.lock @@ -464,29 +464,7 @@ "@solana/buffer-layout-utils" "^0.2.0" buffer "^6.0.3" -"@solana/web3.js@1.73.2": - version "1.73.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.73.2.tgz#4b30cd402b35733dae3a7d0b638be26a7742b395" - integrity sha512-9WACF8W4Nstj7xiDw3Oom22QmrhBh0VyZyZ7JvvG3gOxLWLlX3hvm5nPVJOGcCE/9fFavBbCUb5A6CIuvMGdoA== - dependencies: - "@babel/runtime" "^7.12.5" - "@noble/ed25519" "^1.7.0" - "@noble/hashes" "^1.1.2" - "@noble/secp256k1" "^1.6.3" - "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.2.1" - bigint-buffer "^1.1.5" - bn.js "^5.0.0" - borsh "^0.7.0" - bs58 "^4.0.1" - buffer "6.0.1" - fast-stable-stringify "^1.0.0" - jayson "^3.4.4" - node-fetch "2" - rpc-websockets "^7.5.0" - superstruct "^0.14.2" - -"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0": +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2": version "1.73.3" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.73.3.tgz#60e6bd68f6f364d4be360b1e0a03a0a68468a029" integrity sha512-vHRMo589XEIpoujpE2sZZ1aMZvfA1ImKfNxobzEFyMb+H5j6mRRUXfdgWD0qJ0sm11e5BcBC7HPeRXJB+7f3Lg== @@ -1401,7 +1379,7 @@ jsonparse@^1.2.0: "@noble/hashes" "^1.1.5" "@solana/spl-account-compression" "^0.1.5" "@solana/spl-token" "0.3.7" - "@solana/web3.js" "1.73.2" + "@solana/web3.js" "^1.73.2" axios "^1.3.4" circomlib "^2.0.5" circomlibjs "^0.1.7" @@ -1569,13 +1547,6 @@ node-addon-api@^3.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-fetch@2, node-fetch@^2.6.7: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - dependencies: - whatwg-url "^5.0.0" - node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -1583,6 +1554,13 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.7: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: version "4.6.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" @@ -1687,7 +1665,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -rpc-websockets@^7.5.0, rpc-websockets@^7.5.1: +rpc-websockets@^7.5.1: version "7.5.1" resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.1.tgz#e0a05d525a97e7efc31a0617f093a13a2e10c401" integrity sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w== diff --git a/light-sdk-ts/src/account.ts b/light-sdk-ts/src/account.ts index 36e9a2bff5..efb6165121 100644 --- a/light-sdk-ts/src/account.ts +++ b/light-sdk-ts/src/account.ts @@ -8,6 +8,7 @@ import { const { blake2b } = require("@noble/hashes/blake2b"); const b2params = { dkLen: 32 }; const ffjavascript = require("ffjavascript"); +// @ts-ignore: import { buildEddsa } from "circomlibjs"; export class Account { diff --git a/light-sdk-ts/src/utxo.ts b/light-sdk-ts/src/utxo.ts index ccda3a2fab..3199066bcd 100644 --- a/light-sdk-ts/src/utxo.ts +++ b/light-sdk-ts/src/utxo.ts @@ -333,7 +333,7 @@ export class Utxo { const verifierAddress = new PublicKey(bytes.slice(95, 127)); // ...new Array(1), separator is otherwise 0 const appData = bytes.slice(128, bytes.length); - const burnerAccount = Account.fromPrivkey( + const burnerAccount = Account.fromBurnerSeed( poseidon, appData.slice(72, 104), ); diff --git a/light-system-programs/yarn.lock b/light-system-programs/yarn.lock index a47765bdf1..f4a915215b 100644 --- a/light-system-programs/yarn.lock +++ b/light-system-programs/yarn.lock @@ -486,7 +486,7 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" -"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0": +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2": version "1.73.3" resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.73.3.tgz" integrity sha512-vHRMo589XEIpoujpE2sZZ1aMZvfA1ImKfNxobzEFyMb+H5j6mRRUXfdgWD0qJ0sm11e5BcBC7HPeRXJB+7f3Lg== @@ -1425,7 +1425,7 @@ jsonparse@^1.2.0: "@noble/hashes" "^1.1.5" "@solana/spl-account-compression" "^0.1.5" "@solana/spl-token" "0.3.7" - "@solana/web3.js" "1.73.2" + "@solana/web3.js" "^1.73.2" axios "^1.3.4" circomlib "^2.0.5" circomlibjs "^0.1.7" diff --git a/mock-app-verifier/yarn.lock b/mock-app-verifier/yarn.lock index 4db65574c5..3dcace2978 100644 --- a/mock-app-verifier/yarn.lock +++ b/mock-app-verifier/yarn.lock @@ -519,7 +519,7 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" -"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0": +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2": version "1.73.3" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.73.3.tgz#60e6bd68f6f364d4be360b1e0a03a0a68468a029" integrity sha512-vHRMo589XEIpoujpE2sZZ1aMZvfA1ImKfNxobzEFyMb+H5j6mRRUXfdgWD0qJ0sm11e5BcBC7HPeRXJB+7f3Lg== @@ -1488,7 +1488,7 @@ jsonparse@^1.2.0: "@noble/hashes" "^1.1.5" "@solana/spl-account-compression" "^0.1.5" "@solana/spl-token" "0.3.7" - "@solana/web3.js" "1.73.2" + "@solana/web3.js" "^1.73.2" axios "^1.3.4" circomlib "^2.0.5" circomlibjs "^0.1.7" @@ -1736,11 +1736,6 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - r1csfile@0.0.40: version "0.0.40" resolved "https://registry.yarnpkg.com/r1csfile/-/r1csfile-0.0.40.tgz#410e10e3cec8daf62ff87b022460cf180fd51d58"