Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

web-wallet: Fix creation of a new wallet causing the sync to start fr… #3376

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions web-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Fix creation of a new wallet causing the sync to start from genesis [#3362]

## [1.2.0] - 2025-01-16

### Added
Expand Down Expand Up @@ -537,6 +539,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#3339]: https://github.com/dusk-network/rusk/issues/3339
[#3354]: https://github.com/dusk-network/rusk/issues/3354
[#3356]: https://github.com/dusk-network/rusk/issues/3356
[#3362]: https://github.com/dusk-network/rusk/issues/3362

<!-- VERSIONS -->

Expand Down
15 changes: 15 additions & 0 deletions web-wallet/src/routes/(welcome)/setup/create/+page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { error } from "@sveltejs/kit";

import { networkStore } from "$lib/stores";

/** @type {import("./$types").PageLoad} */
export async function load() {
return {
currentBlockHeight: await networkStore.getCurrentBlockHeight().catch(() => {
error(
500,
"Unable to retrieve current block height: the network may be down."
);
}),
};
}
8 changes: 6 additions & 2 deletions web-wallet/src/routes/(welcome)/setup/create/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import PasswordSetup from "../PasswordSetup.svelte";
import { goto } from "$lib/navigation";

/** @type {import("./$types").PageData} */
export let data;

/** @type {boolean} */
let notice = false;

Expand Down Expand Up @@ -47,6 +50,7 @@
let enteredMnemonicPhrase = [];

const { userId } = $settingsStore;
const { currentBlockHeight } = data;

$: if (showPasswordSetup) {
password = showPasswordSetup ? password : "";
Expand Down Expand Up @@ -141,7 +145,7 @@
}}
nextButton={{
action: async () => {
await initializeWallet(mnemonicPhrase.join(" "));
await initializeWallet(mnemonicPhrase.join(" "), currentBlockHeight);
mnemonicPhrase = [];
},
disabled: false,
Expand All @@ -151,7 +155,7 @@
Network<br />
<mark>Syncing</mark>
</h2>
<NetworkSync />
<NetworkSync {currentBlockHeight} />
</WizardStep>
<WizardStep
step={5}
Expand Down
13 changes: 4 additions & 9 deletions web-wallet/src/routes/(welcome)/setup/create/NetworkSync.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@
import { DOCUMENTATION_LINKS } from "$lib/constants";
import { IconHeadingCard } from "$lib/containers/Cards";
import { createNumberFormatter } from "$lib/dusk/number";
import { networkStore, settingsStore } from "$lib/stores";
import { settingsStore } from "$lib/stores";
import { mdiCubeOutline } from "@mdi/js";
$: ({ language } = $settingsStore);

/** @type {bigint} */
let currentBlock;

networkStore.getCurrentBlockHeight().then((block) => {
currentBlock = block;
});
export let currentBlockHeight;

const numberFormatter = createNumberFormatter(language);
</script>
Expand All @@ -27,9 +23,8 @@

<CopyField
name="Block Height"
displayValue={currentBlock ? numberFormatter(currentBlock) : "Loading..."}
rawValue={String(currentBlock)}
disabled={!currentBlock}
displayValue={numberFormatter(currentBlockHeight)}
rawValue={String(currentBlockHeight)}
/>

<small>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1787,7 +1787,6 @@ exports[`Create > ensures the Network Syncing step renders as expected 1`] = `
<button
aria-label="Copy Address"
class="dusk-button dusk-button--type--button dusk-button--variant--primary dusk-button--size--default dusk-icon-button copy-field__button"
disabled=""
type="button"
>
<svg
Expand Down
90 changes: 65 additions & 25 deletions web-wallet/src/routes/(welcome)/setup/create/__tests__/page.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render } from "@testing-library/svelte";
import { tick } from "svelte";
import * as SvelteKit from "@sveltejs/kit";
import { getKey, setKey } from "lamb";
import * as bip39 from "bip39";
import { ProfileGenerator } from "$lib/vendor/w3sper.js/src/mod";
Expand All @@ -12,6 +13,7 @@ import loginInfoStorage from "$lib/services/loginInfoStorage";
import * as shuffleArray from "$lib/dusk/array";

import Create from "../+page.svelte";
import { load } from "../+page.js";

/**
* @param {HTMLElement} input
Expand Down Expand Up @@ -53,9 +55,14 @@ describe("Create", async () => {
.then(getKey("address"))
.then(String);

const blockHeightSpy = vi
.spyOn(networkStore, "getCurrentBlockHeight")
.mockResolvedValue(currentBlockHeight);
const baseProps = {
data: { currentBlockHeight },
};
const baseOptions = {
props: baseProps,
target: document.body,
};

const generateMnemonicSpy = vi
.spyOn(bip39, "generateMnemonic")
.mockReturnValue(mnemonic);
Expand All @@ -72,7 +79,7 @@ describe("Create", async () => {
afterEach(() => {
cleanup();
settingsStore.reset();
blockHeightSpy.mockClear();

generateMnemonicSpy.mockClear();
shuffleArraySpy.mockClear();
clearAndInitSpy.mockClear();
Expand All @@ -82,7 +89,6 @@ describe("Create", async () => {
});

afterAll(() => {
blockHeightSpy.mockRestore();
generateMnemonicSpy.mockRestore();
shuffleArraySpy.mockRestore();
clearAndInitSpy.mockRestore();
Expand All @@ -91,22 +97,55 @@ describe("Create", async () => {
initWalletSpy.mockRestore();
});

describe("Create page load", () => {
const blockHeightSpy = vi
.spyOn(networkStore, "getCurrentBlockHeight")
.mockResolvedValue(currentBlockHeight);

afterEach(() => {
blockHeightSpy.mockClear();
});

afterAll(() => {
blockHeightSpy.mockRestore();
});

it('should retrieve the current block height and pass it to the page as "page data"', async () => {
// @ts-ignore we don't care for load parameters here
await expect(load()).resolves.toStrictEqual({ currentBlockHeight });
});

it("should call the svelte kit error if the current block height can't be retrieved", async () => {
const svelteKitErrorSpy = vi.spyOn(SvelteKit, "error");

blockHeightSpy.mockRejectedValueOnce(new Error("some error"));

// @ts-ignore we don't care for load parameters here
await expect(load()).rejects.toThrow();

expect(svelteKitErrorSpy).toHaveBeenCalledTimes(1);
expect(svelteKitErrorSpy).toHaveBeenCalledWith(500, expect.any(String));

svelteKitErrorSpy.mockRestore();
});
});

it("should render the Existing Wallet notice step of the Create flow if there is a userId saved in localStorage", () => {
settingsStore.update(setKey("userId", userId));

const { container } = render(Create);
const { container } = render(Create, baseOptions);

expect(container.firstChild).toMatchSnapshot();
});

it("should render the Terms of Service step of the Create flow if there is no userId saved in localStorage", () => {
const { container } = render(Create);
const { container } = render(Create, baseOptions);

expect(container.firstChild).toMatchSnapshot();
});

it("should render the `Securely store your mnemonic phrase!` agreement step after the ToS", async () => {
const { container, getByRole } = render(Create);
const { container, getByRole } = render(Create, baseOptions);

const mathRandomSpy = vi.spyOn(Math, "random").mockReturnValue(42);

Expand All @@ -118,7 +157,7 @@ describe("Create", async () => {
});

it("should not allow the user proceed unless both agreement checks are selected on the `Securely store your mnemonic phrase!` step", async () => {
const { getByRole, getAllByRole } = render(Create);
const { getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand Down Expand Up @@ -149,7 +188,7 @@ describe("Create", async () => {
});

it("correctly renders the Mnemonic Preview page", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand All @@ -162,7 +201,7 @@ describe("Create", async () => {
});

it("correctly renders the Mnemonic Verification page", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand All @@ -176,7 +215,7 @@ describe("Create", async () => {
});

it("doesn't let the user proceed if they have entered mismatching Mnemonic", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand Down Expand Up @@ -204,7 +243,7 @@ describe("Create", async () => {
});

it("lets the user proceed if they have entered a matching Mnemonic", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand All @@ -228,7 +267,7 @@ describe("Create", async () => {
});

it("ensures that the Undo button on the Mnemonic Validate step works as expected", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand All @@ -254,7 +293,7 @@ describe("Create", async () => {
});

it("ensures the Password step renders as expected", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand Down Expand Up @@ -284,7 +323,7 @@ describe("Create", async () => {
});

it("ensures the Network Syncing step renders as expected", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand All @@ -303,13 +342,11 @@ describe("Create", async () => {
await fireEvent.click(getByRole("button", { name: "Next" }));
await fireEvent.click(getByRole("button", { name: "Next" }));

expect(blockHeightSpy).toHaveBeenCalledTimes(1);

expect(container.firstChild).toMatchSnapshot();
});

it("ensures the All Done step renders as expected", async () => {
const { container, getByRole, getAllByRole } = render(Create);
const { container, getByRole, getAllByRole } = render(Create, baseOptions);

await fireEvent.click(getByRole("button", { name: "Accept" }));

Expand Down Expand Up @@ -340,7 +377,7 @@ describe("Create", async () => {
});

it("should initialize the wallet without setting a password", async () => {
const { getByRole, getAllByRole } = render(Create);
const { getByRole, getAllByRole } = render(Create, baseOptions);

// ToS step
await fireEvent.click(getByRole("button", { name: "Accept" }));
Expand Down Expand Up @@ -375,14 +412,14 @@ describe("Create", async () => {

expect(settingsResetSpy).toHaveBeenCalledTimes(1);
expect(initWalletSpy).toHaveBeenCalledTimes(1);
expect(initWalletSpy).toHaveBeenCalledWith(mnemonic);
expect(initWalletSpy).toHaveBeenCalledWith(mnemonic, currentBlockHeight);

await vi.waitUntil(() => clearAndInitSpy.mock.calls.length === 1);

expect(clearAndInitSpy).toHaveBeenCalledTimes(1);
expect(clearAndInitSpy).toHaveBeenCalledWith(
expect.any(ProfileGenerator),
undefined
currentBlockHeight
);

// All Done step
Expand All @@ -395,7 +432,10 @@ describe("Create", async () => {
});

it("should initialize the wallet encrypted mnemonic saved in localStorage", async () => {
const { getByPlaceholderText, getByRole, getAllByRole } = render(Create);
const { getByPlaceholderText, getByRole, getAllByRole } = render(
Create,
baseOptions
);

// ToS step
await fireEvent.click(getByRole("button", { name: "Accept" }));
Expand Down Expand Up @@ -436,14 +476,14 @@ describe("Create", async () => {

expect(settingsResetSpy).toHaveBeenCalledTimes(1);
expect(initWalletSpy).toHaveBeenCalledTimes(1);
expect(initWalletSpy).toHaveBeenCalledWith(mnemonic);
expect(initWalletSpy).toHaveBeenCalledWith(mnemonic, currentBlockHeight);

await vi.waitUntil(() => clearAndInitSpy.mock.calls.length === 1);

expect(clearAndInitSpy).toHaveBeenCalledTimes(1);
expect(clearAndInitSpy).toHaveBeenCalledWith(
expect.any(ProfileGenerator),
undefined
currentBlockHeight
);

// All Done step
Expand Down
Loading