Skip to content

Commit

Permalink
Added Serial Num Parameter to Note Recipient Constructor in the Web C…
Browse files Browse the repository at this point in the history
…lient
  • Loading branch information
Dennis Garcia committed Jan 17, 2025
1 parent 2bac040 commit 1dca3e1
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* Web Store InsertChainMmrNodes Duplicate Ids Causes Error (#627).
* Fixed client bugs where some note metadata was not being updated (#625).
* Added Sync Loop to Integration Tests for Small Speedup (#590).
* Added Serial Num Parameter to Note Recipient Constructor in the Web Client (#671).

### Changes

Expand Down
2 changes: 2 additions & 0 deletions crates/web-client/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const {
TransactionRequestBuilder,
TransactionScriptInputPair,
TransactionScriptInputPairArray,
Word,
WebClient,
} = await wasm({
importHook: () => {
Expand Down Expand Up @@ -79,5 +80,6 @@ export {
TransactionRequestBuilder,
TransactionScriptInputPair,
TransactionScriptInputPairArray,
Word,
WebClient,
};
1 change: 1 addition & 0 deletions crates/web-client/js/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ export {
TransactionRequestBuilder,
TransactionScriptInputPair,
TransactionScriptInputPairArray,
Word,
WebClient,
} from "./crates/miden_client_web";
2 changes: 1 addition & 1 deletion crates/web-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@demox-labs/miden-sdk",
"version": "0.0.19",
"version": "0.6.1-next.3",
"description": "Polygon Miden Wasm SDK",
"collaborators": [
"Polygon Miden",
Expand Down
11 changes: 5 additions & 6 deletions crates/web-client/src/models/note_recipient.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use miden_objects::{
crypto::rand::{FeltRng, RpoRandomCoin},
notes::{
NoteInputs as NativeNoteInputs, NoteRecipient as NativeNoteRecipient,
NoteScript as NativeNoteScript,
},
Word as NativeWord,
};
use wasm_bindgen::prelude::*;

use super::{note_inputs::NoteInputs, note_script::NoteScript, rpo_digest::RpoDigest};
use super::{note_inputs::NoteInputs, note_script::NoteScript, rpo_digest::RpoDigest, word::Word};

#[derive(Clone)]
#[wasm_bindgen]
Expand All @@ -16,13 +16,12 @@ pub struct NoteRecipient(NativeNoteRecipient);
#[wasm_bindgen]
impl NoteRecipient {
#[wasm_bindgen(constructor)]
pub fn new(note_script: &NoteScript, inputs: &NoteInputs) -> NoteRecipient {
let mut random_coin = RpoRandomCoin::new(Default::default());
let serial_num = random_coin.draw_word();
pub fn new(serial_num: &Word, note_script: &NoteScript, inputs: &NoteInputs) -> NoteRecipient {
let native_serial_num: NativeWord = serial_num.into();
let native_note_script: NativeNoteScript = note_script.into();
let native_note_inputs: NativeNoteInputs = inputs.into();
let native_note_recipient =
NativeNoteRecipient::new(serial_num, native_note_script, native_note_inputs);
NativeNoteRecipient::new(native_serial_num, native_note_script, native_note_inputs);

NoteRecipient(native_note_recipient)
}
Expand Down
2 changes: 2 additions & 0 deletions crates/web-client/test/global.test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
TransactionRequestBuilder,
TransactionScriptInputPair,
TransactionScriptInputPairArray,
Word,
WebClient,
} from "../dist/index";

Expand Down Expand Up @@ -72,6 +73,7 @@ declare global {
TransactionRequestBuilder: typeof TransactionRequestBuilder;
TransactionScriptInputPair: typeof TransactionScriptInputPair;
TransactionScriptInputPairArray: typeof TransactionScriptInputPairArray;
Word: typeof Word;
create_client: () => Promise<void>;

// Add the helpers namespace
Expand Down
2 changes: 2 additions & 0 deletions crates/web-client/test/mocha.global.setup.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ before(async () => {
TransactionRequestBuilder,
TransactionScriptInputPair,
TransactionScriptInputPairArray,
Word,
WebClient,
} = await import("./index.js");
let rpc_url = `http://localhost:${rpc_port}`;
Expand Down Expand Up @@ -130,6 +131,7 @@ before(async () => {
window.TransactionRequestBuilder = TransactionRequestBuilder;
window.TransactionScriptInputPair = TransactionScriptInputPair;
window.TransactionScriptInputPairArray = TransactionScriptInputPairArray;
window.Word = Word;

// Create a namespace for helper functions
window.helpers = window.helpers || {};
Expand Down
249 changes: 239 additions & 10 deletions crates/web-client/test/new_transactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
mintTransaction,
setupWalletAndFaucet,
} from "./webClientTestUtils";
import { setupConsumedNote } from "./notes.test";

// NEW_MINT_TRANSACTION TESTS
// =======================================================================================================
Expand Down Expand Up @@ -328,7 +329,11 @@ export const customTransaction = async (
])
);

const serialNum = window.Word.new_from_u64s(
new BigUint64Array([BigInt(1), BigInt(2), BigInt(3), BigInt(4)])
);
let noteRecipient = new window.NoteRecipient(
serialNum,
compiledNoteScript,
noteInputs
);
Expand Down Expand Up @@ -414,19 +419,243 @@ export const customTransaction = async (
}, asserted_value);
};

const customTxWithMultipleNotes = async (
isSerialNumSame: boolean,
senderAccountId: string,
faucetAccountId: string
) => {
return await testingPage.evaluate(
async (_isSerialNumSame, _senderAccountId, _faucetAccountId) => {
const client = window.client;
const amount = BigInt(10);
const targetAccount = await client.new_wallet(
window.AccountStorageMode.private(),
true
);
const targetAccountId = targetAccount.id();
const senderAccountId = window.AccountId.from_hex(_senderAccountId);
const faucetAccountId = window.AccountId.from_hex(_faucetAccountId);

// Create custom note with multiple assets to send to target account
// Error should happen if serial numbers are the same in each set of
// note assets. Otherwise, the transaction should go through.
await client.fetch_and_cache_account_auth_by_pub_key(senderAccountId);

let noteAssets_1 = new window.NoteAssets([
new window.FungibleAsset(faucetAccountId, amount),
]);
let noteAssets_2 = new window.NoteAssets([
new window.FungibleAsset(faucetAccountId, amount),
]);

let noteMetadata = new window.NoteMetadata(
senderAccountId,
window.NoteType.public(),
window.NoteTag.from_account_id(
targetAccountId,
window.NoteExecutionMode.new_local()
),
window.NoteExecutionHint.none(),
undefined
);

let serialNum1 = window.Word.new_from_u64s(
new BigUint64Array([BigInt(1), BigInt(2), BigInt(3), BigInt(4)])
);
let serialNum2 = window.Word.new_from_u64s(
new BigUint64Array([BigInt(5), BigInt(6), BigInt(7), BigInt(8)])
);

const p2id_script = `
use.miden::account
use.miden::note
use.miden::contracts::wallets::basic->wallet
# ERRORS
# =================================================================================================
# P2ID script expects exactly 2 note inputs
const.ERR_P2ID_WRONG_NUMBER_OF_INPUTS=0x00020050
# P2ID's target account address and transaction address do not match
const.ERR_P2ID_TARGET_ACCT_MISMATCH=0x00020051
#! Helper procedure to add all assets of a note to an account.
#!
#! Inputs: []
#! Outputs: []
proc.add_note_assets_to_account
push.0 exec.note::get_assets
# => [num_of_assets, 0 = ptr, ...]
# compute the pointer at which we should stop iterating
dup.1 add
# => [end_ptr, ptr, ...]
# pad the stack and move the pointer to the top
padw movup.5
# => [ptr, 0, 0, 0, 0, end_ptr, ...]
# compute the loop latch
dup dup.6 neq
# => [latch, ptr, 0, 0, 0, 0, end_ptr, ...]
while.true
# => [ptr, 0, 0, 0, 0, end_ptr, ...]
# save the pointer so that we can use it later
dup movdn.5
# => [ptr, 0, 0, 0, 0, ptr, end_ptr, ...]
# load the asset
mem_loadw
# => [ASSET, ptr, end_ptr, ...]
# pad the stack before call
padw swapw padw padw swapdw
# => [ASSET, pad(12), ptr, end_ptr, ...]
# add asset to the account
call.wallet::receive_asset
# => [pad(16), ptr, end_ptr, ...]
# clean the stack after call
dropw dropw dropw
# => [0, 0, 0, 0, ptr, end_ptr, ...]
# increment the pointer and compare it to the end_ptr
movup.4 add.1 dup dup.6 neq
# => [latch, ptr+1, ASSET, end_ptr, ...]
end
# clear the stack
drop dropw drop
end
#! Pay-to-ID script: adds all assets from the note to the account, assuming ID of the account
#! matches target account ID specified by the note inputs.
#!
#! Requires that the account exposes:
#! - miden::contracts::wallets::basic::receive_asset procedure.
#!
#! Inputs: []
#! Outputs: []
#!
#! Note inputs are assumed to be as follows:
#! - target_account_id is the ID of the account for which the note is intended.
#!
#! Panics if:
#! - Account does not expose miden::contracts::wallets::basic::receive_asset procedure.
#! - Account ID of executing account is not equal to the Account ID specified via note inputs.
#! - The same non-fungible asset already exists in the account.
#! - Adding a fungible asset would result in amount overflow, i.e., the total amount would be
#! greater than 2^63.
begin
# store the note inputs to memory starting at address 0
push.0 exec.note::get_inputs
# => [num_inputs, inputs_ptr]
# make sure the number of inputs is 2
eq.2 assert.err=ERR_P2ID_WRONG_NUMBER_OF_INPUTS
# => [inputs_ptr]
# read the target account ID from the note inputs
padw movup.4 mem_loadw drop drop
# => [target_account_id_prefix, target_account_id_suffix]
exec.account::get_id
# => [account_id_prefix, account_id_suffix, target_account_id_prefix, target_account_id_suffix, ...]
# ensure account_id = target_account_id, fails otherwise
exec.account::is_id_eq assert.err=ERR_P2ID_TARGET_ACCT_MISMATCH
# => []
exec.add_note_assets_to_account
# => []
end
`;

let compiledNoteScript = await client.compile_note_script(p2id_script);

let noteInputs = new window.NoteInputs(
new window.FeltArray([
targetAccount.id().suffix(),
targetAccount.id().prefix(),
])
);

let noteRecipient1 = new window.NoteRecipient(
serialNum1,
compiledNoteScript,
noteInputs
);
let noteRecipient2 = new window.NoteRecipient(
_isSerialNumSame ? serialNum1 : serialNum2,
compiledNoteScript,
noteInputs
);

let note1 = new window.Note(noteAssets_1, noteMetadata, noteRecipient1);
let note2 = new window.Note(noteAssets_2, noteMetadata, noteRecipient2);

let transaction_request = new window.TransactionRequestBuilder()
.with_own_output_notes(
new window.OutputNotesArray([
window.OutputNote.full(note1),
window.OutputNote.full(note2),
])
)
.build();

let transactionResult = await client.new_transaction(
senderAccountId,
transaction_request
);

await client.submit_transaction(transactionResult);

await window.helpers.waitForTransaction(
transactionResult.executed_transaction().id().to_hex()
);
},
isSerialNumSame,
senderAccountId,
faucetAccountId
);
};

describe("custom transaction tests", () => {
it("custom transaction completes successfully", async () => {
const result = await customTransaction("0");

expect(1).to.equal(1);
await expect(customTransaction("0")).to.be.fulfilled;
});

// TODO: Need better error handling throughout the new_transaction
// and submit_transaction web-client call stacks to actually detect
// this. Otherwise it hangs the test.
// it.only("custom transaction fails", async () => {
// const result = await customTransaction("1");
it("custom transaction fails", async () => {
await expect(customTransaction("1")).to.be.rejected;
});
});

// expect(1).to.equal(1);
// });
describe("custom transaction with multiple output notes", () => {
const testCases = [
{
description: "does not fail when output note serial numbers are unique",
shouldFail: false,
},
{
description: "fails when output note serial numbers are the same",
shouldFail: true,
},
];

testCases.forEach(({ description, shouldFail }) => {
it(description, async () => {
const { accountId, faucetId } = await setupConsumedNote();
if (shouldFail) {
await expect(customTxWithMultipleNotes(shouldFail, accountId, faucetId))
.to.be.rejected;
} else {
await expect(customTxWithMultipleNotes(shouldFail, accountId, faucetId))
.to.be.fulfilled;
}
});
});
});
Loading

0 comments on commit 1dca3e1

Please sign in to comment.