From 427f5545f20d4c12244f2d9504c0fc87a754405a Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 20 Mar 2023 17:56:48 +0000 Subject: [PATCH 1/3] added custom errors provider and tests --- light-sdk-ts/package.json | 1 + light-sdk-ts/src/constants.ts | 4 + light-sdk-ts/src/errors.ts | 10 ++ .../src/test-utils/functionalCircuit.ts | 2 +- light-sdk-ts/src/transaction.ts | 10 +- light-sdk-ts/src/wallet/provider.ts | 96 ++++++----- light-sdk-ts/src/wallet/user.ts | 15 +- light-sdk-ts/tests/provider.test.ts | 156 ++++++++++++++++++ light-sdk-ts/tests/transaction.test.ts | 6 +- .../tests/transactionParameters.test.ts | 10 +- light-system-programs/package.json | 2 + light-system-programs/tests/provider.test.ts | 142 ++++++++++++++++ 12 files changed, 389 insertions(+), 65 deletions(-) create mode 100644 light-sdk-ts/tests/provider.test.ts create mode 100644 light-system-programs/tests/provider.test.ts diff --git a/light-sdk-ts/package.json b/light-sdk-ts/package.json index 7d2e2ebe4d..256855ee1d 100644 --- a/light-sdk-ts/package.json +++ b/light-sdk-ts/package.json @@ -12,6 +12,7 @@ "test-account": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/account.test.ts --exit", "test-relayer": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/relayer.test.ts --exit", "test-createOutUtxos": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/createOutUtxos.test.ts --exit", + "test-provider": "ts-mocha --resolveJsonModule ./tsconfig.json -t 100000000 tests/provider.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/constants.ts b/light-sdk-ts/src/constants.ts index 9a66fd28a2..a180a96cfa 100644 --- a/light-sdk-ts/src/constants.ts +++ b/light-sdk-ts/src/constants.ts @@ -87,6 +87,10 @@ export const DEFAULT_PROGRAMS = { clock: SYSVAR_CLOCK_PUBKEY, }; +// TODO: adjust according to relayer fees +// recommented minimum amount of lamports to be able to pay for transaction fees +export const MINIMUM_LAMPORTS = 50_000; + // TODO: make account object with important accounts export const MERKLE_TREE_KEY = new PublicKey( "DCxUdYgqjE6AR9m13VvqpkxJqGJYnk8jn8NEeD3QY3BU", diff --git a/light-sdk-ts/src/errors.ts b/light-sdk-ts/src/errors.ts index cb4777aec2..a1dabe479a 100644 --- a/light-sdk-ts/src/errors.ts +++ b/light-sdk-ts/src/errors.ts @@ -45,6 +45,14 @@ export enum ProviderErrorCode { SOL_MERKLE_TREE_UNDEFINED = "SOL_MERKLE_TREE_UNDEFINED", ANCHOR_PROVIDER_UNDEFINED = "ANCHOR_PROVIDER_UNDEFINED", PROVIDER_UNDEFINED = "PROVIDER_UNDEFINED", + NODE_WALLET_AND_BROWSER_WALLET_UNDEFINED = "NODE_WALLET_AND_BROWSER_WALLET_UNDEFINED", + NODE_WALLET_UNDEFINED = "NODE_WALLET_UNDEFINED", + URL_UNDEFINED = "URL_UNDEFINED", + CONNECTION_UNDEFINED = "CONNECTION_UNDEFINED", + CONNECTION_DEFINED = "CONNECTION_DEFINED", + KEYPAIR_UNDEFINED = "KEYPAIR_UNDEFINED", + NODE_WALLET_AND_BROWSER_WALLET_DEFINED = "NODE_WALLET_AND_BROWSER_WALLET_DEFINED", + MERKLE_TREE_NOT_INITIALIZED = "MERKLE_TREE_NOT_INITIALIZED", } export enum VerifierErrorCode { @@ -146,3 +154,5 @@ export class AccountError extends MetaError {} export class RelayerError extends MetaError {} export class CreateUtxoError extends MetaError {} + +export class ProviderError extends MetaError {} diff --git a/light-sdk-ts/src/test-utils/functionalCircuit.ts b/light-sdk-ts/src/test-utils/functionalCircuit.ts index 3c48d2b801..7ca8713453 100644 --- a/light-sdk-ts/src/test-utils/functionalCircuit.ts +++ b/light-sdk-ts/src/test-utils/functionalCircuit.ts @@ -35,7 +35,7 @@ export async function functionalCircuitTest( }); let mockPubkey = SolanaKeypair.generate().publicKey; - let lightProvider = await LightProvider.loadMock(mockPubkey); + let lightProvider = await LightProvider.loadMock(); let txParams = new TransactionParameters({ outputUtxos: [deposit_utxo1], merkleTreePubkey: mockPubkey, diff --git a/light-sdk-ts/src/transaction.ts b/light-sdk-ts/src/transaction.ts index c7b45aa2c3..f591c0d882 100644 --- a/light-sdk-ts/src/transaction.ts +++ b/light-sdk-ts/src/transaction.ts @@ -1556,15 +1556,7 @@ export class Transaction { await this.provider.provider.connection.getRecentBlockhash("confirmed") ).blockhash; const txMsg = new TransactionMessage({ - payerKey: - this.params.relayer.accounts.relayerPubkey !== - this.provider.browserWallet?.publicKey && - this.params.relayer.accounts.relayerPubkey !== - this.provider.nodeWallet?.publicKey - ? this.params.relayer.accounts.relayerPubkey - : this.provider.browserWallet - ? this.provider.browserWallet.publicKey - : this.provider.nodeWallet!.publicKey, + payerKey: this.params.relayer.accounts.relayerPubkey, instructions: [ ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), ix, diff --git a/light-sdk-ts/src/wallet/provider.ts b/light-sdk-ts/src/wallet/provider.ts index 33957a368f..9bca296713 100644 --- a/light-sdk-ts/src/wallet/provider.ts +++ b/light-sdk-ts/src/wallet/provider.ts @@ -1,9 +1,4 @@ -import { - AnchorError, - AnchorProvider, - setProvider, - Wallet, -} from "@coral-xyz/anchor"; +import { AnchorProvider, setProvider } from "@coral-xyz/anchor"; import { SolMerkleTree } from "../merkleTree"; import { PublicKey, @@ -11,14 +6,10 @@ import { Connection, ConfirmOptions, } from "@solana/web3.js"; -import { - ADMIN_AUTH_KEYPAIR, - initLookUpTable, - initLookUpTableFromFile, - setUpMerkleTree, -} from "../test-utils"; +import { ADMIN_AUTH_KEYPAIR, initLookUpTableFromFile } from "../test-utils"; import { MERKLE_TREE_HEIGHT, MERKLE_TREE_KEY } from "../constants"; import { MerkleTree } from "../merkleTree/merkleTree"; +import { ProviderError, ProviderErrorCode } from "../errors"; const axios = require("axios"); const circomlibjs = require("circomlibjs"); @@ -46,8 +37,8 @@ export class Provider { solMerkleTree?: SolMerkleTree; provider?: AnchorProvider | { connection: Connection }; // temp -? url?: string; - minimumLamports: number; - + // TODO: refactor by streamlining everything towards using connection and not anchor provider + // - get rid of url /** * Init either with nodeWallet or browserWallet. Feepayer is the provided wallet * Optionally provide confirmConfig, Default = 'confirmed'. @@ -58,26 +49,38 @@ export class Provider { confirmConfig, connection, url = "http://127.0.0.1:8899", - minimumLamports = 5000 * 30, }: { nodeWallet?: SolanaKeypair; browserWallet?: BrowserWallet; confirmConfig?: ConfirmOptions; connection?: Connection; url?: string; - minimumLamports?: number; }) { if (nodeWallet && browserWallet) - throw new Error("Both node and browser environments provided."); - if (!nodeWallet && !browserWallet) throw new Error("No wallet provided."); - if (nodeWallet && !url) throw new Error("No url provided."); + throw new ProviderError( + ProviderErrorCode.NODE_WALLET_AND_BROWSER_WALLET_DEFINED, + "constructor", + "Both node and browser environments provided chose one.", + ); + if (!nodeWallet && !browserWallet) + throw new ProviderError( + ProviderErrorCode.NODE_WALLET_AND_BROWSER_WALLET_UNDEFINED, + "constructor", + "No wallet provided.", + ); if (browserWallet && !connection) - throw new Error("No connection provided."); + throw new ProviderError( + ProviderErrorCode.CONNECTION_UNDEFINED, + "constructor", + "No connection provided with browser wallet.", + ); if (nodeWallet && connection) - throw new Error( + throw new ProviderError( + ProviderErrorCode.CONNECTION_DEFINED, + "constructor", "Connection provided in node environment. Provide a url instead", ); - this.minimumLamports = minimumLamports; + this.confirmConfig = confirmConfig || { commitment: "confirmed" }; if (nodeWallet) { @@ -98,7 +101,7 @@ export class Provider { } } - static async loadMock(mockPubkey: PublicKey) { + static async loadMock() { let mockProvider = new Provider({ nodeWallet: ADMIN_AUTH_KEYPAIR, url: "mock", @@ -107,7 +110,7 @@ export class Provider { mockProvider.lookUpTable = SolanaKeypair.generate().publicKey; mockProvider.solMerkleTree = new SolMerkleTree({ poseidon: mockProvider.poseidon, - pubkey: mockPubkey, + pubkey: MERKLE_TREE_KEY, }); return mockProvider; @@ -121,6 +124,7 @@ export class Provider { return; } if (!this.provider) throw new Error("No provider set."); + // TODO: remove this should not exist this.lookUpTable = await initLookUpTableFromFile(this.provider); } catch (err) { console.error(err); @@ -128,7 +132,7 @@ export class Provider { } } - private async fetchMerkleTree() { + private async fetchMerkleTree(merkleTreePubkey: PublicKey) { try { if (this.browserWallet) { const response = await axios.get("http://localhost:3331/merkletree"); @@ -145,20 +149,24 @@ export class Provider { this.solMerkleTree = { ...response.data.data, merkleTree, pubkey }; } - // TODO: move to a seperate function + const merkletreeIsInited = await this.provider!.connection.getAccountInfo( - MERKLE_TREE_KEY, + merkleTreePubkey, ); if (!merkletreeIsInited) { - await setUpMerkleTree(this.provider!); - // TODO: throw error + throw new ProviderError( + ProviderErrorCode.MERKLE_TREE_NOT_INITIALIZED, + "fetchMerkleTree", + `Merkle tree is not initialized if on local host run test utils setUpMerkleTree before initin the provider, on other networks check your merkle tree pubkey ${merkleTreePubkey}`, + ); } const mt = await SolMerkleTree.build({ - pubkey: MERKLE_TREE_KEY, + pubkey: merkleTreePubkey, poseidon: this.poseidon, provider: this.provider, }); + console.log("✔️ building merkletree done"); this.solMerkleTree = mt; } catch (err) { @@ -172,7 +180,7 @@ export class Provider { this.poseidon = poseidon; } async latestMerkleTree() { - await this.fetchMerkleTree(); + await this.fetchMerkleTree(MERKLE_TREE_KEY); } // TODO: add loadEddsa @@ -183,18 +191,28 @@ export class Provider { * @param connection get from useConnection() hook */ static async browser( - walletContext?: BrowserWallet, - connection?: Connection, + browserWallet: BrowserWallet, + connection: Connection, confirmConfig?: ConfirmOptions, ): Promise { + if (!browserWallet) { + throw new ProviderError(ProviderErrorCode.KEYPAIR_UNDEFINED, "browser"); + } + if (!connection) { + throw new ProviderError( + ProviderErrorCode.CONNECTION_UNDEFINED, + "browser", + ); + } + const provider = new Provider({ - browserWallet: walletContext, + browserWallet, confirmConfig, connection, }); await provider.loadPoseidon(); await provider.fetchLookupTable(); - await provider.fetchMerkleTree(); + await provider.fetchMerkleTree(MERKLE_TREE_KEY); return provider; } @@ -205,10 +223,14 @@ export class Provider { * @param url full-node rpc endpoint to instantiate a Connection */ static async native( - keypair?: SolanaKeypair, + keypair: SolanaKeypair, url?: string, confirmConfig?: ConfirmOptions, ): Promise { + if (!keypair) { + throw new ProviderError(ProviderErrorCode.KEYPAIR_UNDEFINED, "native"); + } + const provider = new Provider({ nodeWallet: keypair, confirmConfig, @@ -216,7 +238,7 @@ export class Provider { }); await provider.loadPoseidon(); await provider.fetchLookupTable(); - await provider.fetchMerkleTree(); + await provider.fetchMerkleTree(MERKLE_TREE_KEY); return provider; } } diff --git a/light-sdk-ts/src/wallet/user.ts b/light-sdk-ts/src/wallet/user.ts index 471e966265..e327c7a2ae 100644 --- a/light-sdk-ts/src/wallet/user.ts +++ b/light-sdk-ts/src/wallet/user.ts @@ -21,6 +21,7 @@ import { MERKLE_TREE_KEY, TOKEN_REGISTRY, merkleTreeProgramId, + MINIMUM_LAMPORTS, } from "../constants"; import { ADMIN_AUTH_KEY, @@ -511,9 +512,7 @@ export class User { // TODO: implement browserWallet support; for UI throw new Error("Browser wallet support not implemented yet!"); } - extraSolAmount = extraSolAmount - ? extraSolAmount * 1e9 - : this.provider.minimumLamports; + extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; } else { // amount = amount * tokenCtx.decimals; extraSolAmount = amount * tokenCtx.decimals; @@ -562,7 +561,7 @@ export class User { * @params token: string * @params amount: number - in base units (e.g. lamports for 'SOL') * @params recipient: PublicKey - Solana address - * @params extraSolAmount: number - optional, if not set, will use provider minimumLamports + * @params extraSolAmount: number - optional, if not set, will use MINIMUM_LAMPORTS */ async unshield({ token, @@ -587,9 +586,7 @@ export class User { recipient, ); - extraSolAmount = extraSolAmount - ? extraSolAmount * 1e9 - : this.provider.minimumLamports; + extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; } else { extraSolAmount = amount; amount = 0; @@ -658,9 +655,7 @@ export class User { amount = amount * tokenCtx.decimals; if (!tokenCtx.isSol) { - extraSolAmount = extraSolAmount - ? extraSolAmount * 1e9 - : this.provider.minimumLamports; + extraSolAmount = extraSolAmount ? extraSolAmount * 1e9 : MINIMUM_LAMPORTS; } else { extraSolAmount = amount; amount = 0; diff --git a/light-sdk-ts/tests/provider.test.ts b/light-sdk-ts/tests/provider.test.ts new file mode 100644 index 0000000000..923956336c --- /dev/null +++ b/light-sdk-ts/tests/provider.test.ts @@ -0,0 +1,156 @@ +import { assert, expect } from "chai"; +let circomlibjs = require("circomlibjs"); +import { + SystemProgram, + Keypair as SolanaKeypair, + PublicKey, + Connection, +} from "@solana/web3.js"; +import * as anchor from "@coral-xyz/anchor"; +import { it } from "mocha"; +import { buildPoseidonOpt } from "circomlibjs"; +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +// Load chai-as-promised support +chai.use(chaiAsPromised); +import { Account } from "../src/account"; +import { Utxo } from "../src/utxo"; +import { + FEE_ASSET, + hashAndTruncateToCircuit, + Provider as LightProvider, + MINT, + Transaction, + TransactionParameters, + VerifierZero, + TransactionErrorCode, + Action, + TransactioParametersError, + TransactionParametersErrorCode, + Relayer, + FIELD_SIZE, + merkleTreeProgramId, + VerifierTwo, + VerifierOne, + AUTHORITY, + TransactionError, + ProviderErrorCode, + SolMerkleTreeErrorCode, + ADMIN_AUTH_KEYPAIR, + MERKLE_TREE_KEY, + DEFAULT_ZERO, + ProviderError, +} from "../src"; +import { MerkleTree } from "../src/merkleTree/merkleTree"; + +process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; +process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; +const verifiers = [new VerifierZero(), new VerifierOne(), new VerifierTwo()]; + +describe("Test Provider Functional", () => { + let seed32 = new Uint8Array(32).fill(1).toString(); + let depositAmount = 20_000; + let depositFeeAmount = 10_000; + + let mockPubkey = SolanaKeypair.generate().publicKey; + let mockPubkey1 = SolanaKeypair.generate().publicKey; + let mockPubkey2 = SolanaKeypair.generate().publicKey; + let mockPubkey3 = SolanaKeypair.generate().publicKey; + let poseidon, lightProvider: LightProvider; + + before(async () => { + poseidon = await circomlibjs.buildPoseidonOpt(); + }); + + it("Mock Provider", async () => { + const lightProviderMock = await LightProvider.loadMock(); + assert.equal(lightProviderMock.browserWallet, undefined); + assert.equal( + lightProviderMock.nodeWallet?.publicKey.toBase58(), + ADMIN_AUTH_KEYPAIR.publicKey.toBase58(), + ); + assert.equal(lightProviderMock.url, "mock"); + assert(lightProviderMock.poseidon); + assert(lightProviderMock.lookUpTable); + assert.equal( + lightProviderMock.solMerkleTree?.pubkey.toBase58(), + MERKLE_TREE_KEY.toBase58(), + ); + assert.equal(lightProviderMock.solMerkleTree?.merkleTree.levels, 18); + assert.equal( + lightProviderMock.solMerkleTree?.merkleTree.zeroElement, + DEFAULT_ZERO, + ); + }); + + it("KEYPAIR_UNDEFINED native", async () => { + await chai.assert.isRejected( + // @ts-ignore + LightProvider.native(), + ProviderErrorCode.KEYPAIR_UNDEFINED, + ); + }); + + it("CONNECTION_DEFINED", async () => { + expect(() => { + // @ts-ignore + new LightProvider({ nodeWallet: ADMIN_AUTH_KEYPAIR, connection: {} }); + }) + .to.throw(ProviderError) + .includes({ + code: ProviderErrorCode.CONNECTION_DEFINED, + functionName: "constructor", + }); + }); + + it("NODE_WALLET_AND_BROWSER_WALLET_UNDEFINED", async () => { + expect(() => { + // @ts-ignore + new LightProvider({}); + }) + .to.throw(ProviderError) + .includes({ + code: ProviderErrorCode.NODE_WALLET_AND_BROWSER_WALLET_UNDEFINED, + functionName: "constructor", + }); + }); + + it("CONNECTION_UNDEFINED", async () => { + expect(() => { + // @ts-ignore + new LightProvider({ browserWallet: {} }); + }) + .to.throw(ProviderError) + .includes({ + code: ProviderErrorCode.CONNECTION_UNDEFINED, + functionName: "constructor", + }); + }); + + it("KEYPAIR_UNDEFINED browser", async () => { + await chai.assert.isRejected( + // @ts-ignore + LightProvider.browser(), + ProviderErrorCode.KEYPAIR_UNDEFINED, + ); + }); + + it("KEYPAIR_UNDEFINED browser", async () => { + await chai.assert.isRejected( + // @ts-ignore + LightProvider.browser(), + ProviderErrorCode.KEYPAIR_UNDEFINED, + ); + }); + + it("CONNECTION_UNDEFINED browser", async () => { + const mockKeypair = SolanaKeypair.generate(); + + await chai.assert.isRejected( + // @ts-ignore + LightProvider.browser(mockKeypair), + ProviderErrorCode.CONNECTION_UNDEFINED, + ); + }); +}); diff --git a/light-sdk-ts/tests/transaction.test.ts b/light-sdk-ts/tests/transaction.test.ts index 611eb34970..dbd564f5cd 100644 --- a/light-sdk-ts/tests/transaction.test.ts +++ b/light-sdk-ts/tests/transaction.test.ts @@ -69,7 +69,7 @@ describe("Transaction Error Tests", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey3); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -308,7 +308,7 @@ describe("Transaction Functional Tests", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey3); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -382,7 +382,7 @@ describe("Transaction Functional Tests", () => { const poseidon = await circomlibjs.buildPoseidonOpt(); let mockPubkey = SolanaKeypair.generate().publicKey; - let lightProvider = await LightProvider.loadMock(mockPubkey); + let lightProvider = await LightProvider.loadMock(); var deposit_utxo1 = new Utxo({ poseidon, diff --git a/light-sdk-ts/tests/transactionParameters.test.ts b/light-sdk-ts/tests/transactionParameters.test.ts index e4b539404f..0400f31f78 100644 --- a/light-sdk-ts/tests/transactionParameters.test.ts +++ b/light-sdk-ts/tests/transactionParameters.test.ts @@ -51,7 +51,7 @@ describe("Transaction Parameters Functional", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey3); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -468,7 +468,7 @@ describe("Test General TransactionParameters Errors", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey3); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -581,7 +581,7 @@ describe("Test TransactionParameters Transfer Errors", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -794,7 +794,7 @@ describe("Test TransactionParameters Deposit Errors", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], @@ -1149,7 +1149,7 @@ describe("Test TransactionParameters Withdrawal Errors", () => { new anchor.BN(5000), ); keypair = new Account({ poseidon: poseidon, seed: seed32 }); - lightProvider = await LightProvider.loadMock(mockPubkey); + lightProvider = await LightProvider.loadMock(); deposit_utxo1 = new Utxo({ poseidon: poseidon, assets: [FEE_ASSET, MINT], diff --git a/light-system-programs/package.json b/light-system-programs/package.json index ba6d2433ed..500b92c9c4 100644 --- a/light-system-programs/package.json +++ b/light-system-programs/package.json @@ -6,6 +6,8 @@ "test-user": "sh runTest.sh 'ts-mocha -t 2000000 tests/user_tests.ts --exit'", "test-verifiers": "sh runTest.sh 'ts-mocha -t 2000000 tests/verifier_tests.ts --exit'", "test-merkle-tree": "sh runTest.sh 'ts-mocha -t 2000000 tests/merkle_tree_tests.ts --exit'", + "test-provider": "sh runTest.sh 'ts-mocha -t 2000000 tests/provider.test.ts --exit'", + "format": "prettier --write \"src/**/*.{ts,js}\"" }, "dependencies": { diff --git a/light-system-programs/tests/provider.test.ts b/light-system-programs/tests/provider.test.ts new file mode 100644 index 0000000000..702c731b75 --- /dev/null +++ b/light-system-programs/tests/provider.test.ts @@ -0,0 +1,142 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Connection, Keypair as SolanaKeypair, PublicKey, SystemProgram } from "@solana/web3.js"; +import _ from "lodash"; +import { assert } from "chai"; +import {Account, Action, KEYPAIR_PRIVKEY, Provider as LightProvider, Transaction, TransactionParameters, userTokenAccount, VerifierZero } from "light-sdk"; + +let circomlibjs = require("circomlibjs"); + +// TODO: add and use namespaces in SDK +import { + Utxo, + setUpMerkleTree, + initLookUpTableFromFile, + merkleTreeProgramId, + ADMIN_AUTH_KEYPAIR, + MINT, + Provider, + newAccountWithTokens, + createTestAccounts, + confirmConfig, + Relayer, + User, + strToArr, + TOKEN_REGISTRY, + updateMerkleTreeForTest, + DEFAULT_ZERO, + MERKLE_TREE_KEY, +} from "light-sdk"; + +import { BN } from "@coral-xyz/anchor"; + +var LOOK_UP_TABLE; +var POSEIDON, KEYPAIR; + +// TODO: remove deprecated function calls +describe("verifier_program", () => { + // Configure the client to use the local cluster. + process.env.ANCHOR_WALLET = process.env.HOME + "/.config/solana/id.json"; + process.env.ANCHOR_PROVIDER_URL = "http://127.0.0.1:8899"; + + const provider = anchor.AnchorProvider.local( + "http://127.0.0.1:8899", + confirmConfig, + ); + anchor.setProvider(provider); + console.log("merkleTreeProgram: ", merkleTreeProgramId.toBase58()); + + const userKeypair = ADMIN_AUTH_KEYPAIR; //new SolanaKeypair(); + + before("init test setup Merkle tree lookup table etc ", async () => { + let initLog = console.log; + // console.log = () => {}; + await createTestAccounts(provider.connection, ); + LOOK_UP_TABLE = await initLookUpTableFromFile(provider); + await setUpMerkleTree(provider); + // console.log = initLog; + POSEIDON = await circomlibjs.buildPoseidonOpt(); + KEYPAIR = new Account({ + poseidon: POSEIDON, + seed: KEYPAIR_PRIVKEY.toString(), + }); + }); + + it("Native Provider", async ()=> { + let connection = new Connection("http://127.0.0.1:8899", "confirmed"); + await connection.confirmTransaction(await connection.requestAirdrop(ADMIN_AUTH_KEYPAIR.publicKey, 10_000_000_0000), "confirmed"); + const mockKeypair = SolanaKeypair.generate(); + const lightProviderMock = await LightProvider.native(mockKeypair); + assert.equal(lightProviderMock.browserWallet, undefined); + assert.equal(lightProviderMock.nodeWallet?.publicKey.toBase58(), mockKeypair.publicKey.toBase58()); + assert.equal(lightProviderMock.url, "http://127.0.0.1:8899"); + assert(lightProviderMock.poseidon); + assert(lightProviderMock.lookUpTable); + assert.equal(lightProviderMock.solMerkleTree?.pubkey.toBase58(), MERKLE_TREE_KEY.toBase58()); + assert.equal(lightProviderMock.solMerkleTree?.merkleTree.levels, 18); + assert.equal(lightProviderMock.solMerkleTree?.merkleTree.zeroElement, DEFAULT_ZERO); + assert.equal(lightProviderMock.solMerkleTree?.merkleTree._layers[0].length, 0); + }); + + it("Fetch latestMerkleTree", async () => { + const lightProvider = await Provider.native(ADMIN_AUTH_KEYPAIR); + + let depositFeeAmount = 10000; + let depositAmount = 0; + + let deposit_utxo1 = new Utxo({ + poseidon: POSEIDON, + assets: [SystemProgram.programId, MINT], + amounts: [ + new anchor.BN(depositFeeAmount), + new anchor.BN(depositAmount), + ], + account: KEYPAIR, + }); + let deposit_utxo2 = new Utxo({poseidon: POSEIDON}); + + let txParams = new TransactionParameters({ + outputUtxos: [deposit_utxo1, deposit_utxo2], + merkleTreePubkey: MERKLE_TREE_KEY, + sender: userTokenAccount, + senderFee: ADMIN_AUTH_KEYPAIR.publicKey, + verifier: new VerifierZero(), + poseidon: POSEIDON, + lookUpTable: LOOK_UP_TABLE, + action: Action.DEPOSIT + }); + let tx = new Transaction({ + provider: lightProvider, + params: txParams + }); + await tx.compileAndProve(); + + try { + let res = await tx.sendAndConfirmTransaction(); + console.log(res); + } catch (e) { + console.log(e); + } + // TODO: add random amount and amount checks + try { + console.log("updating merkle tree..."); + let initLog = console.log; + console.log = () => {}; + await updateMerkleTreeForTest( + lightProvider.provider?.connection!, + // provider.provider, + ); + console.log = initLog; + console.log("✔️updated merkle tree!"); + } catch (e) { + console.log(e); + throw new Error("Failed to update merkle tree!"); + } + assert.equal(lightProvider.solMerkleTree.merkleTree.indexOf(deposit_utxo1.getCommitment()), -1); + assert.equal(lightProvider.solMerkleTree.merkleTree.indexOf(deposit_utxo2.getCommitment()), -1); + + await lightProvider.latestMerkleTree(); + assert.equal(lightProvider.solMerkleTree.merkleTree.indexOf(deposit_utxo1.getCommitment()), 0); + assert.equal(lightProvider.solMerkleTree.merkleTree.indexOf(deposit_utxo2.getCommitment()), 1); + }) + +}) \ No newline at end of file From 863ab09df213b11929574389156fc67bf56e484d Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 20 Mar 2023 19:12:15 +0000 Subject: [PATCH 2/3] fixed rebase conflicts and added a check in createUtxos --- light-sdk-ts/src/constants.ts | 3 ++- light-sdk-ts/src/wallet/createOutUtxos.ts | 11 +++++++++++ light-system-programs/tests/provider.test.ts | 2 +- light-system-programs/tests/user_tests.ts | 2 +- test.sh | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/light-sdk-ts/src/constants.ts b/light-sdk-ts/src/constants.ts index a180a96cfa..4c00ae6b0e 100644 --- a/light-sdk-ts/src/constants.ts +++ b/light-sdk-ts/src/constants.ts @@ -89,7 +89,8 @@ export const DEFAULT_PROGRAMS = { // TODO: adjust according to relayer fees // recommented minimum amount of lamports to be able to pay for transaction fees -export const MINIMUM_LAMPORTS = 50_000; +// needs to be more than 100k to be rentexempt +export const MINIMUM_LAMPORTS = 150_000; // TODO: make account object with important accounts export const MERKLE_TREE_KEY = new PublicKey( diff --git a/light-sdk-ts/src/wallet/createOutUtxos.ts b/light-sdk-ts/src/wallet/createOutUtxos.ts index 4798a68a4f..f96e85a8f6 100644 --- a/light-sdk-ts/src/wallet/createOutUtxos.ts +++ b/light-sdk-ts/src/wallet/createOutUtxos.ts @@ -61,6 +61,11 @@ const getRecipientsAmount = (mint: PublicKey, recipients: Recipient[]) => { // transfer // check no publics +// shield +// sumInSol +sumSolAmount +// sumInSpl +sumSplAmount +// publicMint + // create via recipients requested utxos and subtract amounts from sums // beforeEach utxo check that no amount is negative @@ -220,6 +225,12 @@ export function createOutUtxos({ "createOutUtxos", "Shield and relayer fee defined", ); + if (!publicSolAmount && !publicSplAmount) + throw new CreateUtxoError( + CreateUtxoErrorCode.NO_PUBLIC_AMOUNTS_PROVIDED, + "createOutUtxos", + "publicSolAmount not initialized for unshield", + ); if (!publicSplAmount) publicSplAmount = new BN(0); if (!publicSolAmount) publicSolAmount = new BN(0); let publicSplAssetIndex = assets.findIndex( diff --git a/light-system-programs/tests/provider.test.ts b/light-system-programs/tests/provider.test.ts index 702c731b75..5c6101443e 100644 --- a/light-system-programs/tests/provider.test.ts +++ b/light-system-programs/tests/provider.test.ts @@ -102,7 +102,7 @@ describe("verifier_program", () => { verifier: new VerifierZero(), poseidon: POSEIDON, lookUpTable: LOOK_UP_TABLE, - action: Action.DEPOSIT + action: Action.SHIELD }); let tx = new Transaction({ provider: lightProvider, diff --git a/light-system-programs/tests/user_tests.ts b/light-system-programs/tests/user_tests.ts index 058753f6e6..01dc78f846 100644 --- a/light-system-programs/tests/user_tests.ts +++ b/light-system-programs/tests/user_tests.ts @@ -210,7 +210,7 @@ describe("verifier_program", () => { ); }); - it.skip("(user class) unshield SPL", async () => { + it("(user class) unshield SPL", async () => { let amount = 1; let token = "USDC"; let solRecipient = SolanaKeypair.generate(); diff --git a/test.sh b/test.sh index 467be19057..3b13cc0775 100755 --- a/test.sh +++ b/test.sh @@ -10,6 +10,7 @@ yarn test yarn run test-merkle-tree yarn run test-verifiers yarn run test-user +yarn run test-provider popd pushd light-sdk-ts From 08ffd63e88731842febfdc50aec9082b7c22c83f Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 20 Mar 2023 19:22:55 +0000 Subject: [PATCH 3/3] unskipped a test --- light-system-programs/tests/functional_tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/light-system-programs/tests/functional_tests.ts b/light-system-programs/tests/functional_tests.ts index 021b822f71..225eb8bacb 100644 --- a/light-system-programs/tests/functional_tests.ts +++ b/light-system-programs/tests/functional_tests.ts @@ -170,7 +170,7 @@ describe("verifier_program", () => { assert.equal(accountInfo, null); }); - it.skip("shielded transfer 1 & 2", async () => { + it("shielded transfer 1 & 2", async () => { await provider.connection.confirmTransaction( await provider.connection.requestAirdrop(verifierState, 1_000_000_000), "confirmed",