From 7518507e339f46a68a9958a52caf48d46053dfee Mon Sep 17 00:00:00 2001 From: Mike Turner Date: Wed, 15 Jan 2025 08:20:03 -0600 Subject: [PATCH] Add updated typescript models to reflect structure of API responses + add Raw method to API client allowing non-formatted data to be returned from the API. --- sdk/src/browser.ts | 13 +- sdk/src/models/blockJSON.ts | 4 +- sdk/src/models/confirmed_transaction.ts | 7 +- sdk/src/models/deploy.ts | 6 - sdk/src/models/deployment/deploymentJSON.ts | 7 + sdk/src/models/deployment/deploymentObject.ts | 7 + sdk/src/models/executionJSON.ts | 5 +- sdk/src/models/feeJSON.ts | 7 + sdk/src/models/finalizeJSON.ts | 6 + sdk/src/models/functionObject.ts | 1 + sdk/src/models/output/outputJSON.ts | 2 +- sdk/src/models/ownerJSON.ts | 4 + sdk/src/models/transaction/transactionJSON.ts | 10 +- .../models/transaction/transactionObject.ts | 4 +- sdk/src/network-client.ts | 78 +++++------ sdk/src/utils.ts | 1 - sdk/tests/network-client.test.ts | 121 ++++++++++++++---- wasm/Cargo.lock | 2 +- wasm/src/ledger/transaction.rs | 20 ++- 19 files changed, 212 insertions(+), 93 deletions(-) delete mode 100644 sdk/src/models/deploy.ts create mode 100644 sdk/src/models/deployment/deploymentJSON.ts create mode 100644 sdk/src/models/deployment/deploymentObject.ts create mode 100644 sdk/src/models/feeJSON.ts create mode 100644 sdk/src/models/finalizeJSON.ts create mode 100644 sdk/src/models/ownerJSON.ts diff --git a/sdk/src/browser.ts b/sdk/src/browser.ts index 0f91580f2..ae5d746c7 100644 --- a/sdk/src/browser.ts +++ b/sdk/src/browser.ts @@ -3,13 +3,17 @@ import "./polyfill/shared"; import { Account } from "./account"; import { AleoNetworkClient, ProgramImports } from "./network-client"; import { BlockJSON, Header, Metadata } from "./models/blockJSON"; -import { DeploymentMetadata } from "./models/deploy"; +import { ConfirmedTransactionJSON } from "./models/confirmed_transaction"; +import { DeploymentJSON, VerifyingKeys } from "./models/deployment/deploymentJSON"; +import { DeploymentObject } from "./models/deployment/deploymentObject"; import { ExecutionJSON } from "./models/executionJSON"; +import { FeeJSON} from "./models/feeJSON"; import { FunctionObject } from "./models/functionObject"; import { InputJSON } from "./models/input/inputJSON"; import { InputObject } from "./models/input/inputObject"; import { OutputJSON } from "./models/output/outputJSON"; import { OutputObject } from "./models/output/outputObject"; +import { OwnerJSON } from "./models/ownerJSON"; import { PlaintextArray} from "./models/plaintext/array"; import { PlaintextLiteral} from "./models/plaintext/literal"; import { PlaintextObject } from "./models/plaintext/plaintext"; @@ -99,8 +103,11 @@ export { BlockJSON, BlockHeightSearch, CachedKeyPair, - DeploymentMetadata, + ConfirmedTransactionJSON, + DeploymentJSON, + DeploymentObject, ExecutionJSON, + FeeJSON, FunctionObject, FunctionKeyPair, FunctionKeyProvider, @@ -113,6 +120,7 @@ export { ProgramImports, OfflineKeyProvider, OfflineSearchParams, + OwnerJSON, PlaintextArray, PlaintextLiteral, PlaintextObject, @@ -125,4 +133,5 @@ export { TransactionObject, TransitionJSON, TransitionObject, + VerifyingKeys, }; diff --git a/sdk/src/models/blockJSON.ts b/sdk/src/models/blockJSON.ts index 77b6ffd1f..51bacc1ed 100644 --- a/sdk/src/models/blockJSON.ts +++ b/sdk/src/models/blockJSON.ts @@ -1,10 +1,10 @@ -import { ConfirmedTransaction } from "./confirmed_transaction"; +import { ConfirmedTransactionJSON } from "./confirmed_transaction"; export type BlockJSON = { block_hash: string; previous_hash: string; header: Header; - transactions?: (ConfirmedTransaction)[]; + transactions?: (ConfirmedTransactionJSON)[]; signature: string; } export type Header = { diff --git a/sdk/src/models/confirmed_transaction.ts b/sdk/src/models/confirmed_transaction.ts index 9595947fa..af350ad6f 100644 --- a/sdk/src/models/confirmed_transaction.ts +++ b/sdk/src/models/confirmed_transaction.ts @@ -1,7 +1,10 @@ import { TransactionJSON } from "./transaction/transactionJSON"; +import { FinalizeJSON } from "./finalizeJSON"; -export interface ConfirmedTransaction { +export interface ConfirmedTransactionJSON { + status: string type: string; - id: string; + index: number; transaction: TransactionJSON; + finalize: FinalizeJSON[]; } diff --git a/sdk/src/models/deploy.ts b/sdk/src/models/deploy.ts deleted file mode 100644 index 57e8e89c6..000000000 --- a/sdk/src/models/deploy.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FunctionObject } from "./functionObject"; - -export interface DeploymentMetadata { - "programId" : string, - "functions" : FunctionObject[] -} \ No newline at end of file diff --git a/sdk/src/models/deployment/deploymentJSON.ts b/sdk/src/models/deployment/deploymentJSON.ts new file mode 100644 index 000000000..ac7321125 --- /dev/null +++ b/sdk/src/models/deployment/deploymentJSON.ts @@ -0,0 +1,7 @@ +export type VerifyingKeys = [string, [string, string]]; + +export interface DeploymentJSON { + "edition" : number, + "program" : string, + "verifying_keys" : VerifyingKeys, +} \ No newline at end of file diff --git a/sdk/src/models/deployment/deploymentObject.ts b/sdk/src/models/deployment/deploymentObject.ts new file mode 100644 index 000000000..27c88238b --- /dev/null +++ b/sdk/src/models/deployment/deploymentObject.ts @@ -0,0 +1,7 @@ +import { FunctionObject } from "../functionObject"; + +export interface DeploymentObject { + "edition" : number, + "program" : string, + "functions" : FunctionObject[], +} \ No newline at end of file diff --git a/sdk/src/models/executionJSON.ts b/sdk/src/models/executionJSON.ts index 1088d0652..f5ae29cb8 100644 --- a/sdk/src/models/executionJSON.ts +++ b/sdk/src/models/executionJSON.ts @@ -1,6 +1,7 @@ import { TransitionJSON } from "./transition/transitionJSON"; export interface ExecutionJSON { - edition: number; - transitions?: (TransitionJSON)[]; + transitions: TransitionJSON[]; + proof: string[]; + global_state_root: string; } diff --git a/sdk/src/models/feeJSON.ts b/sdk/src/models/feeJSON.ts new file mode 100644 index 000000000..1bb39dc6d --- /dev/null +++ b/sdk/src/models/feeJSON.ts @@ -0,0 +1,7 @@ +import { TransitionJSON } from "./transition/transitionJSON"; + +export interface FeeJSON { + transition: TransitionJSON; + global_state_root: string; + proof: string; +} \ No newline at end of file diff --git a/sdk/src/models/finalizeJSON.ts b/sdk/src/models/finalizeJSON.ts new file mode 100644 index 000000000..f81b78bea --- /dev/null +++ b/sdk/src/models/finalizeJSON.ts @@ -0,0 +1,6 @@ +export interface FinalizeJSON { + "type": string; + "mapping_id": string; + "key_id": string; + "value_id": string; +} diff --git a/sdk/src/models/functionObject.ts b/sdk/src/models/functionObject.ts index 7ad9d68ff..cef5a2f1e 100644 --- a/sdk/src/models/functionObject.ts +++ b/sdk/src/models/functionObject.ts @@ -5,4 +5,5 @@ export interface FunctionObject { "constraints" : number, "variables" : number, "verifyingKey" : string | VerifyingKey, + "certificate" : string, } \ No newline at end of file diff --git a/sdk/src/models/output/outputJSON.ts b/sdk/src/models/output/outputJSON.ts index 45098f64b..3537f0484 100644 --- a/sdk/src/models/output/outputJSON.ts +++ b/sdk/src/models/output/outputJSON.ts @@ -1,6 +1,6 @@ export interface OutputJSON { type: string; id: string; - checksum: string; + checksum?: string; value: string; } diff --git a/sdk/src/models/ownerJSON.ts b/sdk/src/models/ownerJSON.ts new file mode 100644 index 000000000..b167b6aee --- /dev/null +++ b/sdk/src/models/ownerJSON.ts @@ -0,0 +1,4 @@ +export interface OwnerJSON { + address: string; + signature: string; +} \ No newline at end of file diff --git a/sdk/src/models/transaction/transactionJSON.ts b/sdk/src/models/transaction/transactionJSON.ts index 58bcadc53..5c94f4780 100644 --- a/sdk/src/models/transaction/transactionJSON.ts +++ b/sdk/src/models/transaction/transactionJSON.ts @@ -1,7 +1,13 @@ +import { DeploymentJSON } from "../deployment/deploymentJSON"; import { ExecutionJSON } from "../executionJSON"; +import { FeeJSON } from "../feeJSON"; +import { OwnerJSON } from "../ownerJSON"; export interface TransactionJSON { type: string; id: string; - execution: ExecutionJSON; -} + deployment?: DeploymentJSON; + execution?: ExecutionJSON; + fee: FeeJSON; + owner?: OwnerJSON; +} \ No newline at end of file diff --git a/sdk/src/models/transaction/transactionObject.ts b/sdk/src/models/transaction/transactionObject.ts index f92627fdb..5c2cceb6f 100644 --- a/sdk/src/models/transaction/transactionObject.ts +++ b/sdk/src/models/transaction/transactionObject.ts @@ -1,5 +1,5 @@ import { TransitionObject } from "../transition/transitionObject"; -import { DeploymentMetadata } from "../deploy"; +import { DeploymentObject } from "../deployment/deploymentObject"; export interface TransactionObject { id : string; @@ -8,5 +8,5 @@ export interface TransactionObject { baseFee : bigint; priorityFee : bigint; transitions : TransitionObject[]; - deployment?: DeploymentMetadata; + deployment? : DeploymentObject; } diff --git a/sdk/src/network-client.ts b/sdk/src/network-client.ts index 2d3b7c25c..1443bbe2b 100644 --- a/sdk/src/network-client.ts +++ b/sdk/src/network-client.ts @@ -10,6 +10,7 @@ import { PrivateKey, Transaction, } from "./wasm"; +import { ConfirmedTransactionJSON } from "./models/confirmed_transaction"; type ProgramImports = { [key: string]: string | Program }; @@ -80,17 +81,37 @@ class AleoNetworkClient { this.host = host + "/%%NETWORK%%"; } + /** + * Fetches data from the Aleo network and returns it as a JSON object. + * + * @param url + */ async fetchData( url = "/", ): Promise { + try { + return parseJSON(await this.fetchRaw(url)); + } catch (error) { + throw new Error("Error fetching data."); + } + } + + /** + * Fetches data from the Aleo network and returns it as an unparsed string. + * + * This method should be used when it is desired to reconstitute data returned + * from the network into a WASM object. + * + * @param url + */ + async fetchRaw( + url = "/", + ): Promise { try { const response = await get(this.host + url, { headers: this.headers }); - - const text = await response.text(); - return parseJSON(text); - + return await response.text(); } catch (error) { throw new Error("Error fetching data."); } @@ -612,10 +633,10 @@ class AleoNetworkClient { async getProgramMappingPlaintext(programId: string, mappingName: string, key: string | Plaintext): Promise { try { const keyString = key instanceof Plaintext ? key.toString() : key; - const value = await this.fetchData<string>("/program/" + programId + "/mapping/" + mappingName + "/" + keyString); + const value = await this.fetchRaw("/program/" + programId + "/mapping/" + mappingName + "/" + keyString); return Plaintext.fromString(value); } catch (error) { - throw new Error("Failed to fetch mapping value"); + throw new Error("Failed to fetch mapping value." + error); } } @@ -676,7 +697,7 @@ class AleoNetworkClient { */ async getTransactionObject(transactionId: string): Promise<Transaction> { try { - const transaction = await this.fetchData<string>("/transaction/" + transactionId); + const transaction = await this.fetchRaw("/transaction/" + transactionId); return Transaction.fromString(transaction); } catch (error) { throw new Error("Error fetching transaction."); @@ -690,35 +711,16 @@ class AleoNetworkClient { * @example * const transactions = networkClient.getTransactions(654); */ - async getTransactions(height: number): Promise<Array<TransactionJSON>> { - try { - return await this.fetchData<Array<TransactionJSON>>("/block/" + height.toString() + "/transactions"); - } catch (error) { - throw new Error("Error fetching transactions."); - } - } - - /** - * Returns an array of transactions as wasm objects present at the specified block height. - * - * @param {number} height - * @example - * const transactions = networkClient.getTransactionObjects(654); - * - * let transaction_summaries = transactions.map(transaction => transaction.summary()); - */ - async getTransactionObjects(height: number): Promise<Array<Transaction>> { + async getTransactions(height: number): Promise<Array<ConfirmedTransactionJSON>> { try { - const transactionStrings = await this.fetchData<Array<string>>(`/block/${height}/transactions`); - return transactionStrings.map(transaction => Transaction.fromString(transaction)); + return await this.fetchData<Array<ConfirmedTransactionJSON>>("/block/" + height.toString() + "/transactions"); } catch (error) { - throw new Error("Error fetching transactions."); + throw new Error("Error fetching transactions. " + error); } } /** - * Returns the transactions in the memory pool. This method will only work with a validator node with its REST API - * enabled. + * Returns the transactions in the memory pool. This method requires access to a validator's REST API. * * @example * const transactions = networkClient.getTransactionsInMempool(); @@ -731,22 +733,6 @@ class AleoNetworkClient { } } - /** - * Returns the transactions in the memory pool as wasm objects. This method will only work with a validator node with - * its REST API enabled. - * - * @example - * const transactions = networkClient.getTransactionsInMempool(); - */ - async getTransactionObjectsInMempool(): Promise<Array<Transaction>> { - try { - const transactionStrings = await this.fetchData<Array<string>>("/memoryPool/transactions"); - return transactionStrings.map(transaction => Transaction.fromString(transaction)); - } catch (error) { - throw new Error("Error fetching transactions from mempool."); - } - } - /** * Returns the transition ID of the transition corresponding to the ID of the input or output. * @param {string} inputOrOutputID - ID of the input or output. diff --git a/sdk/src/utils.ts b/sdk/src/utils.ts index d92420a45..f2f6740db 100644 --- a/sdk/src/utils.ts +++ b/sdk/src/utils.ts @@ -8,7 +8,6 @@ export function parseJSON(json: string): any { function revive(key: string, value: any, context: any) { if (Number.isInteger(value)) { return BigInt(context.source); - } else { return value; } diff --git a/sdk/tests/network-client.test.ts b/sdk/tests/network-client.test.ts index 0c3afe229..cc008995d 100644 --- a/sdk/tests/network-client.test.ts +++ b/sdk/tests/network-client.test.ts @@ -2,7 +2,7 @@ import sinon from "sinon"; import { expect } from "chai"; import { Account, BlockJSON, AleoNetworkClient, TransactionObject, InputObject, OutputObject } from "../src/node"; import { beaconPrivateKeyString } from "./data/account-data"; -import { Plaintext, PlaintextObject, Transition, TransitionObject } from "../src/node"; +import { DeploymentObject, ExecutionJSON, InputJSON, OutputJSON, Plaintext, PlaintextObject, Transition, TransitionObject } from "../src/node"; async function catchError(f: () => Promise<any>): Promise<Error | null> { try { @@ -15,12 +15,15 @@ async function catchError(f: () => Promise<any>): Promise<Error | null> { } -async function expectThrows(f: () => Promise<any>, message: string): Promise<void> { +async function expectThrowsMessage(f: () => Promise<any>, message: string): Promise<void> { const error = await catchError(f); expect(error).not.equal(null); expect(error!.message).equal(message); } - +async function expectThrows(f: () => Promise<any>): Promise<void> { + const error = await catchError(f); + expect(error).not.equal(null); +} describe('NodeConnection', () => { let connection: AleoNetworkClient; @@ -51,7 +54,7 @@ describe('NodeConnection', () => { }); it('should throw an error if the request fails', async () => { - await expectThrows( + await expectThrowsMessage( () => connection.getBlock(99999999), "Error fetching block.", ); @@ -68,7 +71,7 @@ describe('NodeConnection', () => { }); it('should throw an error if the request fails', async () => { - await expectThrows( + await expectThrowsMessage( () => connection.getBlockRange(999999999, 1000000000), "Error fetching blocks between 999999999 and 1000000000.", ); @@ -84,7 +87,7 @@ describe('NodeConnection', () => { it('should throw an error if the request fails', async () => { const program_id = "a" + (Math.random()).toString(32).substring(2) + ".aleo"; - await expectThrows( + await expectThrowsMessage( () => connection.getProgram(program_id), "Error fetching program", ); @@ -142,7 +145,6 @@ describe('NodeConnection', () => { it('should throw an error if the request fails', async () => { await expectThrows( () => connection.getTransactions(999999999), - "Error fetching transactions.", ); }); }); @@ -186,22 +188,22 @@ describe('NodeConnection', () => { describe('findUnspentRecords', () => { it('should fail if block heights or private keys are incorrectly specified', async () => { - await expectThrows( + await expectThrowsMessage( () => connection.findUnspentRecords(5, 0, beaconPrivateKeyString, undefined, undefined, []), "Start height must be less than or equal to end height.", ); - await expectThrows( + await expectThrowsMessage( () => connection.findUnspentRecords(-5, 5, beaconPrivateKeyString, undefined, undefined, []), "Start height must be greater than or equal to 0", ); - await expectThrows( + await expectThrowsMessage( () => connection.findUnspentRecords(0, 5, "definitelynotaprivatekey", undefined, undefined, []), "Error parsing private key provided.", ); - await expectThrows( + await expectThrowsMessage( () => connection.findUnspentRecords(0, 5, undefined, undefined, undefined, []), "Private key must be specified in an argument to findOwnedRecords or set in the AleoNetworkClient", ); @@ -226,11 +228,9 @@ describe('NodeConnection', () => { }); describe('Test API methods that return wasm objects', () => { - it('should return a struct whose object representation matches the wasm representation', async () => { - // Ensure we're on testnet. - const transactions = await connection.getTransactionObjects(27400); - if (transactions.length > 1) { - // Check the credits.aleo mapping. + it('Plaintext mapping content should match that of the plaintext object', async () => { + const transactions = await connection.getTransactions(27400); + if (transactions.length > 0) { const plaintext = await connection.getProgramMappingPlaintext("credits.aleo", "committee", "aleo17m3l8a4hmf3wypzkf5lsausfdwq9etzyujd0vmqh35ledn2sgvqqzqkqal"); const text = await connection.getProgramMappingValue("credits.aleo", "committee", "aleo17m3l8a4hmf3wypzkf5lsausfdwq9etzyujd0vmqh35ledn2sgvqqzqkqal"); @@ -246,16 +246,15 @@ describe('NodeConnection', () => { } }); - it('should have a correct transaction summary', async () => { - // Get the transactions at block 27400 on testnet. - const transactions = await connection.getTransactionObjects(27400); - if (transactions.length > 1) { - const transaction = transactions[0]; + it('should have correct data within the wasm object and summary object for an execution transaction', async () => { + // Get the first transaction at block 24700 on testnet. + const transactions = await connection.getTransactions(27400); + if (transactions.length > 0) { + const transaction = await connection.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); const transition = <Transition>transaction.transitions()[0]; - const summary = <TransactionObject>transactions[0].summary(true); + const summary = <TransactionObject>transaction.summary(true); // Ensure the transaction metadata was correctly computed. - expect(transactions.length).equal(3); expect(transaction.id()).equal("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); expect(transaction.isExecute()).equal(true); expect(transaction.isFee()).equals(false); @@ -279,7 +278,7 @@ describe('NodeConnection', () => { expect(<string>summary.transitions[0].tcm).equals("5140704971235445395514301730284508935687584564904251867869912904008739082032field"); expect(<string>summary.transitions[0].scm).equals("4048085747685910464005835076598544744404883618916202014212851266936759881218field"); expect(summary.transitions[0].functionName).equals("transfer_public"); - expect(summary.id).equals(transaction.transactionId()); + expect(summary.id).equals(transaction.id()); expect(summary.fee).equals(transaction.feeAmount()); expect(summary.baseFee).equals(transaction.baseFeeAmount()); expect(summary.priorityFee).equals(transaction.priorityFeeAmount()); @@ -315,5 +314,79 @@ describe('NodeConnection', () => { expect(<bigint>transition_future_arguments[1]).equals(BigInt(1449)); } }); + + it('should have correct data within the wasm object and summary object for a deployment transaction', async () => { + // Get the deployment transaction for token_registry.aleo + const transactions = await connection.getTransactions(27400); + if (transactions.length === 0) { + const transaction = await connection.getTransactionObject("at15mwg0jyhvpjjrfxwrlwzn8puusnmy7r3xzvpjht4e5gzgnp68q9qd0qqec"); + const summary = <TransactionObject>transaction.summary(true); + const deployment = <DeploymentObject>summary.deployment; + + // Ensure the transaction metadata was correctly computed. + expect(transaction.id()).equal("at15mwg0jyhvpjjrfxwrlwzn8puusnmy7r3xzvpjht4e5gzgnp68q9qd0qqec"); + expect(transaction.isExecute()).equal(false); + expect(transaction.isFee()).equals(false); + expect(transaction.isDeploy()).equals(true); + expect(transaction.records().length).equals( 0); + + // Ensure the object summary returns the correct general transaction metadata. + expect(summary.type).equals(transaction.transactionType()); + expect(summary.fee).equals(transaction.feeAmount()); + expect(summary.transitions.length).equals(0); + expect(summary.id).equals(transaction.id()); + expect(summary.fee).equals(transaction.feeAmount()); + expect(summary.baseFee).equals(transaction.baseFeeAmount()); + expect(summary.priorityFee).equals(transaction.priorityFeeAmount()); + expect(deployment.program).equals("token_registry.aleo"); + expect(deployment.functions.length).equals(22); + } + }); + + it('Should give the correct JSON response when requesting multiple transactions', async () => { + const transactions = await connection.getTransactions(27400); + if (transactions.length > 0) { + expect(transactions.length).equal(4); + expect(transactions[0].status).equal("accepted"); + expect(transactions[0].type).equal("execute"); + expect(transactions[0].index).equal(0); + expect(transactions[0].finalize.length).equal(1); + expect(transactions[0].transaction.id).equal("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); + expect(transactions[0].transaction.type).equal(true); + + const execution = <ExecutionJSON>transactions[0].transaction.execution; + expect(execution.transitions.length).equal(1); + expect(execution.transitions[0].id).equal("au1j529auupdhlc2jcl802fm2uf3khcwm7xt7wxkrl2jv3pvj7zhu8qvna5pp"); + expect(execution.transitions[0].program).equal("puzzle_arcade_coin_v001.aleo"); + expect(execution.transitions[0].function).equal("mint"); + expect((<InputJSON[]>execution.transitions[0].inputs).length).equal(2); + + // Get outputs and ensure they have the correct values. + const outputs = <OutputJSON[]>execution.transitions[0].outputs + expect(outputs.length).equal(1); + expect(outputs[0].type).equal("record"); + expect(outputs[0].id).equal("5572694414900952602617575422938223520445524449912062453787036904987112234218field"); + expect(outputs[0].value).equal("record1qyqsqkh52naqk7l62m9rw3emwm0swuttcs7qs6lqqeu2czc34mv7h2c8qyrxzmt0w4h8ggcqqgqsq5de236vn6h0z3cvecduyzmqmaw0ue8wk4a9a5z89r7qxv53cwgg4xqpmzq9tv35fzyt6xfem9f74qcx9qsj90e6gzajfetc874q6g9snrzkgw"); + expect(<string>outputs[0].checksum).equal("976841447793933847827686780055501802433867163411662643088590532874495495156field"); + expect(transactions[0].transaction.fee.proof).equal("proof1qyqsqqqqqqqqqqqpqqqqqqqqqqq89k9zdaf38ssnk3znaue2xv0uax8dcvydrrssgfxwuvm7w0h7wtcwu4qdkvhc4yxy73m8m4z0hssqq8xn4t5qyj88zvhfausgzez7w0e8fhhdj6gnfgpw20k46ml4thyzjc9xj3rhuzn6qdrsvlm05amd0q96plngd7txlcjykyspyja0qnvljtte2s9v23s3x7rekhc29khqrmrp2hl9pwv8ckyx7z8p4uh64zq8lgkum20sk6klu462ahnnrerqzaaksvtk64wdsj5dwr0wcg8e63pxcfjucch3224xwkxppwtcwsqqkdw07r53e2zt402jekqmlju3c66kmzcaz0lh5ctacluce39rycg8g3x9gs8vgrxnmjzgqu95ntqqqwdhjhpx5ztvk0x35559prjlm3a54wcd6tk9k3m2fs0xkx7pty9v4urlzg6029l9mfp0tsafxq72szzhhaymx0hqztu4pr2gvqfg2nqjpmqv9c8wkrjgsm7mcpnhxdu703xkzc2tz2l4hljlv7jdpdgvyqr4pf80tzp6w0vdtupfhfj2nhmmc9s38g0na5698p8eg0n6vtcdzksfp64rveukf8yq4pk2re2zujqpy0rfr65rd3l4s63hj78nqfft70r463ym8sfan7sa2nypeyy7urkgxwsjv5n5924dc67jtm28upypvpllg4sceam4jfv603s2hgfz4dwgeuswc258uq6teftsxf7xdvrll68rqz7xtpjzl7qkaq028x44g3m52k9csrv8mlfewrklavyg7rcf4qh6htcxe39vcd63790s4s4phmfc940uhhshd7hnxrp6p7dyp73686zpa7hv59k7xrztjmer4h7yrdwd06zgwe97p7as955vafqsrlg26jt675nj4cs7z733y7gr9qtfj42j33clkcup5g3k8ww3pug25vmdqcdmrz07a6azf7h6ugj6wuvs3y5c6gjmxy3ks94ny20fd5gwmahweegq73t4nhytu55nc5xa0jy8qchstu7r23psfjk7day55p4yyejrhh89hlc93zlcnu48tqjul35wn0us20w7rmwe7cwgy2q9quaaxz5n4rueeey4hm9cxlu828h2y29wkwr3p8h6nxfqdu905txsr604gnzurkz7rjnuy44a2a8afgplfgt4wd77tlfg8ec7tacjyqgrqvqqqqqqqqqqpqg7sm3jcaeqajsucveqam4mjajv0hps5lvqe7hhg93psqnjpu8aqpn0x8t92c3fjcke385h2yuvsqq2zvx9t6g9y3qnzza9k4tje8yzxzsmpaaf5un7vc78syypyplm5psr3rs5vp5n6ru4zm5vlukkf2vqqyhl84enh2p3q35t2c5n9ve4fdpa9pcg4pjms7j3alm3ctvv55e3qqmjv7lccnkh00utzhjwv2hglqaqmuh84kz5wug0pmwn0n8ua373ttqjdksd3eq7e4z3f69yvnn0sqqqcnztcq"); + expect(transactions[0].transaction.fee.global_state_root).equal("sr1uwx36xp95j7p2w7yadnj5ups6n8ktf0uwnvq0yauk2fefa2lsqysj4ydym"); + expect(transactions[0].transaction.fee.transition.function).equal("fee_public"); + expect(transactions[0].transaction.fee.transition.program).equal("credits.aleo"); + + // Check the fee inputs. + const feeInputs = <InputJSON[]>transactions[0].transaction.fee.transition.inputs; + expect(feeInputs.length).equal(3); + expect(feeInputs[0].type).equal("public"); + expect(feeInputs[0].value).equal("1449u64"); + expect(feeInputs[0].id).equal("4386982425102730230159169800986934827054209772356695389341764668009606015212field"); + + // Check the fee outputs. + const feeOutputs = <OutputJSON[]>transactions[0].transaction.fee.transition.outputs; + expect(feeOutputs.length).equal(1); + expect(feeOutputs[0].type).equal("future"); + expect(feeOutputs[0].id).equal("5266202420911953477603237216561767366202408116662663021354607932182034937240field"); + expect(feeOutputs[0].value).equal("{\n program_id: credits.aleo,\n function_name: fee_public,\n arguments: [\n aleo193cgzzpr5lcwq6rmzq4l2ctg5f4mznead080mclfgrc0e5k0w5pstfdfps,\n 1449u64\n ]\n}"); + } + }); }) }); diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index a8057ca79..d5ea9531b 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -94,7 +94,7 @@ checksum = "7e4f181fc1a372e8ceff89612e5c9b13f72bff5b066da9f8d6827ae65af492c4" [[package]] name = "aleo-wasm" -version = "0.7.1" +version = "0.7.6" dependencies = [ "anyhow", "async-trait", diff --git a/wasm/src/ledger/transaction.rs b/wasm/src/ledger/transaction.rs index 02896f293..eac57060b 100644 --- a/wasm/src/ledger/transaction.rs +++ b/wasm/src/ledger/transaction.rs @@ -224,18 +224,20 @@ impl Transaction { // If the transaction is a deployment, summarize the deployment. let deployment = if let Some(deployment) = self.0.deployment() { - let functions = deployment.verifying_keys().iter().map(|(function_name, (verifying_key, _))| { + let functions = deployment.verifying_keys().iter().map(|(function_name, (verifying_key, certificate))| { // Create the initial function object. object! { "name" : function_name.to_string(), "constraints" : verifying_key.circuit_info.num_constraints as u32, "variables" : verifying_key.num_variables() as u32, "verifyingKey": if convert_to_js { JsValue::from_str(&verifying_key.to_string()) } else { JsValue::from(VerifyingKey::from(verifying_key)) }, + "certificate" : certificate.to_string(), } }).collect::<Array>(); // Create the deployment object. JsValue::from(object! { - "programId" : deployment.program_id().to_string(), + "edition" : deployment.edition(), + "program" : deployment.program_id().to_string(), "functions" : functions, }) } else { @@ -282,6 +284,20 @@ impl Transaction { pub fn transitions(&self) -> Array { self.0.transitions().map(|transition| JsValue::from(Transition::from(transition))).collect::<Array>() } + + /// Get the verifying keys in a transaction. + pub fn verifying_keys(&self) -> Array { + self.0.deployment().map(|deployment| { + deployment.verifying_keys().iter().map(|(function_name, (verifying_key, certificate))| { + object! { + "program" : deployment.program_id().to_string(), + "functionName" : function_name.to_string(), + "verifyingKey" : verifying_key.to_string(), + "certificate" : certificate.to_string(), + } + }).collect::<Array>() + }).unwrap_or_else(|| Array::new()) + } } impl Deref for Transaction {