Skip to content

Commit

Permalink
feat(core-manager): dispatch wallet events (#3776)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastijankuzner authored Jun 8, 2020
1 parent 7c348e1 commit 7cb916a
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 12 deletions.
16 changes: 16 additions & 0 deletions __tests__/unit/core-manager/service-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Application, Container, Providers } from "@packages/core-kernel";
import { defaults } from "@packages/core-manager/src/defaults";
import { Identifiers } from "@packages/core-manager/src/ioc";
import { ServiceProvider } from "@packages/core-manager/src/service-provider";
import { WatcherWallet } from "@packages/core-manager/src/watcher-wallet";
import path from "path";
import { dirSync, setGracefulCleanup } from "tmp";

Expand Down Expand Up @@ -33,6 +34,7 @@ beforeEach(() => {
app.bind(Container.Identifiers.PluginConfiguration).to(Providers.PluginConfiguration).inSingletonScope();
app.bind(Container.Identifiers.FilesystemService).toConstantValue({});
app.bind(Container.Identifiers.EventDispatcherService).toConstantValue(mockEventDispatcher);
app.bind(Container.Identifiers.WalletAttributes).toConstantValue({});

defaults.watcher.storage = dirSync().name + "/events.sqlite";
defaults.server.https.tls.key = path.resolve(__dirname, "./__fixtures__/key.pem");
Expand Down Expand Up @@ -114,4 +116,18 @@ describe("ServiceProvider", () => {
it("should not be required", async () => {
await expect(serviceProvider.required()).resolves.toBeFalse();
});

it("should create wallet", async () => {
const usedDefaults = { ...defaults };

setPluginConfiguration(app, serviceProvider, usedDefaults);

await expect(serviceProvider.register()).toResolve();

// @ts-ignore
const wallet = app.get(Container.Identifiers.WalletFactory)("123");
expect(wallet).toBeInstanceOf(WatcherWallet);

await expect(serviceProvider.dispose()).toResolve();
});
});
89 changes: 89 additions & 0 deletions __tests__/unit/core-manager/watcher-wallet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import "jest-extended";

import { Container, Services } from "@arkecosystem/core-kernel";
import { WatcherWallet } from "@arkecosystem/core-manager/src/watcher-wallet";
import { Utils } from "@arkecosystem/crypto";
import { Sandbox } from "@packages/core-test-framework";
import { getWalletAttributeSet } from "@packages/core-test-framework/src/internal/wallet-attributes";

let sandbox: Sandbox;
let wallet: WatcherWallet;
const mockEventDispatcher = {
dispatchSync: jest.fn(),
};

beforeEach(() => {
sandbox = new Sandbox();
sandbox.app.bind(Container.Identifiers.EventDispatcherService).toConstantValue(mockEventDispatcher);

const attributeMap = new Services.Attributes.AttributeMap(getWalletAttributeSet());

wallet = new WatcherWallet(sandbox.app, "123", attributeMap);
});

afterEach(() => {
jest.clearAllMocks();
});

describe("WatcherWallet", () => {
describe("Original", () => {
it("should emit on property set", async () => {
wallet.nonce = Utils.BigNumber.make("3");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(1);
});

it("should emit on setAttribute", async () => {
wallet.setAttribute("delegate.username", "dummy");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(1);
});

it("should emit on forgetAttribute", async () => {
wallet.setAttribute("delegate.username", "dummy");
wallet.forgetAttribute("delegate.username");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(2);
});

it("should clone", async () => {
const clone = wallet.clone();

expect(clone).toEqual(wallet);
});
});

describe("Clone", () => {
let clone: WatcherWallet;

beforeEach(() => {
clone = wallet.clone();
jest.clearAllMocks();
});

it("should emit on property set", async () => {
clone.nonce = Utils.BigNumber.make("3");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(1);
});

it("should emit on setAttribute", async () => {
clone.setAttribute("delegate.username", "dummy");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(1);
});

it("should emit on forgetAttribute", async () => {
clone.setAttribute("delegate.username", "dummy");
clone.forgetAttribute("delegate.username");

expect(mockEventDispatcher.dispatchSync).toHaveBeenCalledTimes(2);
});

it("should clone", async () => {
const anotherClone = clone.clone();

expect(anotherClone).toEqual(wallet);
});
});
});
1 change: 1 addition & 0 deletions packages/core-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@arkecosystem/core-cli": "^3.0.0-next.0",
"@arkecosystem/core-database": "^3.0.0-next.0",
"@arkecosystem/core-kernel": "^3.0.0-next.0",
"@arkecosystem/core-state": "^3.0.0-next.0",
"@hapi/basic": "^6.0.0",
"@hapi/boom": "^9.0.0",
"@hapi/hapi": "^19.0.0",
Expand Down
5 changes: 5 additions & 0 deletions packages/core-manager/src/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum WalletEvent {
PropertySet = "wallet.property.set",
AttributeSet = "wallet.attribute.set",
AttributeForget = "wallet.attribute.forget",
}
19 changes: 16 additions & 3 deletions packages/core-manager/src/service-provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApplicationFactory } from "@arkecosystem/core-cli";
import { Container, Contracts, Providers, Types } from "@arkecosystem/core-kernel";
import { Container, Contracts, Providers, Services, Types } from "@arkecosystem/core-kernel";

import { ActionReader } from "./action-reader";
import { DatabaseLogger } from "./database-logger";
Expand All @@ -12,6 +12,7 @@ import { PluginFactory } from "./server/plugins";
import { Server } from "./server/server";
import { Argon2id, SimpleTokenValidator } from "./server/validators";
import { SnapshotsManager } from "./snapshots/snapshots-manager";
import { WatcherWallet } from "./watcher-wallet";

export class ServiceProvider extends Providers.ServiceProvider {
public async register(): Promise<void> {
Expand Down Expand Up @@ -40,6 +41,18 @@ export class ServiceProvider extends Providers.ServiceProvider {

const pkg: Types.PackageJson = require("../package.json");
this.app.bind(Identifiers.CLI).toConstantValue(ApplicationFactory.make(new Container.Container(), pkg));

this.app
.bind(Container.Identifiers.WalletFactory)
.toFactory<Contracts.State.Wallet>((context: Container.interfaces.Context) => (address: string) =>
new WatcherWallet(
context.container.get(Container.Identifiers.Application),
address,
new Services.Attributes.AttributeMap(
context.container.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes),
),
),
);
}

/**
Expand All @@ -63,11 +76,11 @@ export class ServiceProvider extends Providers.ServiceProvider {
}

public async dispose(): Promise<void> {
if (this.config().get("server.http.enabled")) {
if (this.app.isBound(Identifiers.HTTP)) {
await this.app.get<Server>(Identifiers.HTTP).dispose();
}

if (this.config().get("server.https.enabled")) {
if (this.app.isBound(Identifiers.HTTPS)) {
await this.app.get<Server>(Identifiers.HTTPS).dispose();
}

Expand Down
75 changes: 75 additions & 0 deletions packages/core-manager/src/watcher-wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Contracts, Services } from "@arkecosystem/core-kernel";
import { Wallets } from "@arkecosystem/core-state";
import { cloneDeep } from "@arkecosystem/utils";

import { WalletEvent } from "./events";

export class WatcherWallet extends Wallets.Wallet {
public constructor(
private app: Contracts.Kernel.Application,
address: string,
attributes: Services.Attributes.AttributeMap,
) {
super(address, attributes);

const handler: ProxyHandler<WatcherWallet> = {
set(target, key, value) {
target.app?.events.dispatchSync(WalletEvent.PropertySet, {
publicKey: target.publicKey,
key: key,
value: value,
previousValue: target[key],
wallet: target,
});

target[key] = value;
return true;
},
};

return new Proxy(this, handler);
}

public setAttribute<T = any>(key: string, value: T): boolean {
const isSet = super.setAttribute(key, value);

this.app?.events.dispatchSync(WalletEvent.AttributeSet, {
publicKey: this.publicKey,
isSet: isSet,
key: key,
value: value,
wallet: this,
});

return isSet;
}

public forgetAttribute(key: string): boolean {
const previousValue = super.getAttribute(key);
const isForget = super.forgetAttribute(key);

this.app?.events.dispatchSync(WalletEvent.AttributeForget, {
publicKey: this.publicKey,
isForget: isForget,
key: key,
previousValue: previousValue,
wallet: this,
});

return isForget;
}

public clone(): WatcherWallet {
const clone = new WatcherWallet(this.app, this.address, cloneDeep(this.attributes));

for (const key of Object.keys(this)) {
if (key === "app") {
continue;
}

clone[key] = cloneDeep(this[key]);
}

return clone;
}
}
18 changes: 10 additions & 8 deletions packages/core-state/src/wallets/indexers/wallet-indexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ export const registerIndexers = (app: Contracts.Kernel.Application): void => {
};

export const registerFactories = (app: Contracts.Kernel.Application): void => {
app.bind(Container.Identifiers.WalletFactory).toFactory<Contracts.State.Wallet>(
(context: Container.interfaces.Context) => (address: string) =>
new Wallet(
address,
new Services.Attributes.AttributeMap(
context.container.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes),
if (!app.isBound(Container.Identifiers.WalletFactory)) {
app.bind(Container.Identifiers.WalletFactory).toFactory<Contracts.State.Wallet>(
(context: Container.interfaces.Context) => (address: string) =>
new Wallet(
address,
new Services.Attributes.AttributeMap(
context.container.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes),
),
),
),
);
);
}
};
2 changes: 1 addition & 1 deletion packages/core-state/src/wallets/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class Wallet implements Contracts.State.Wallet {
*/
public constructor(
public readonly address: string,
private readonly attributes: Services.Attributes.AttributeMap,
protected readonly attributes: Services.Attributes.AttributeMap,
) {}

/**
Expand Down

0 comments on commit 7cb916a

Please sign in to comment.