Skip to content

Commit

Permalink
feat(core-transactions): register wallet attributes before accessing …
Browse files Browse the repository at this point in the history
…them (#2867)
  • Loading branch information
spkjp authored and faustbrian committed Aug 7, 2019
1 parent 79dcbe0 commit 3c39180
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 6 deletions.
9 changes: 9 additions & 0 deletions __tests__/unit/core-state/wallets/wallet-manager-htlc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { database } from "../mocks/database";
import { state } from "../mocks/state";

import { State } from "@arkecosystem/core-interfaces";
import { Handlers } from "@arkecosystem/core-transactions";
import { Crypto, Identities, Transactions, Utils } from "@arkecosystem/crypto";
import { Wallet, WalletManager } from "../../../../packages/core-state/src/wallets";
import { TransactionFactory } from "../../../helpers/transaction-factory";
Expand All @@ -15,6 +16,14 @@ let walletManager: State.IWalletManager;

const makeTimestamp = (secondsRelativeToNow = 0) => Math.floor((Date.now() + secondsRelativeToNow * 1000) / 1000);

beforeAll(() => {
jest.spyOn(Handlers.Registry, "isKnownWalletAttribute").mockReturnValue(true);
});

afterAll(() => {
jest.restoreAllMocks();
});

describe("Wallet Manager", () => {
describe("HTLC claim", () => {
const lockPassphrase = "craft imitate step mixture patch forest volcano business charge around girl confirm";
Expand Down
10 changes: 9 additions & 1 deletion __tests__/unit/core-state/wallets/wallet-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import "../../core-database/mocks/core-container";

import { State } from "@arkecosystem/core-interfaces";
import { Handlers } from "@arkecosystem/core-transactions";
import { InsufficientBalanceError } from "@arkecosystem/core-transactions/src/errors";
import { Blocks, Constants, Identities, Interfaces, Transactions, Utils } from "@arkecosystem/crypto";
import { Address } from "@arkecosystem/crypto/src/identities";
Expand Down Expand Up @@ -409,10 +410,17 @@ describe("Wallet Manager", () => {
});

const wallet = new Wallet(walletData1.address);
wallet.setAttribute("custom.attribute", "something");
expect(() => wallet.setAttribute("custom.attribute", "something")).toThrow();

const spy = jest.spyOn(Handlers.Registry, "isKnownWalletAttribute").mockReturnValue(true);

expect(() => wallet.setAttribute("custom.attribute", "something")).not.toThrow();

walletManager.reindex(wallet);

expect(walletManager.findById("something")).toBe(wallet);

spy.mockRestore();
});

it("should unregister an index", () => {
Expand Down
6 changes: 5 additions & 1 deletion __tests__/unit/core-transactions/handler-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class TestTransaction extends Transactions.Transaction {

// tslint:disable-next-line:max-classes-per-file
class TestTransactionHandler extends TransactionHandler {
public dependencies(): TransactionHandlerConstructor[] {
public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return [];
}

Expand Down
11 changes: 10 additions & 1 deletion packages/core-state/src/wallets/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { State } from "@arkecosystem/core-interfaces";
import { Errors } from "@arkecosystem/core-transactions";
import { Errors, Handlers } from "@arkecosystem/core-transactions";
import { Crypto, Enums, Identities, Interfaces, Transactions, Utils } from "@arkecosystem/crypto";
import assert from "assert";
import dottie from "dottie";

export class Wallet implements State.IWallet {
Expand All @@ -20,18 +21,22 @@ export class Wallet implements State.IWallet {
}

public hasAttribute(key: string): boolean {
this.assertKnownAttribute(key);
return dottie.exists(this.attributes, key);
}

public getAttribute<T>(key: string, defaultValue?: T): T {
this.assertKnownAttribute(key);
return dottie.get(this.attributes, key, defaultValue);
}

public setAttribute<T = any>(key: string, value: T): void {
this.assertKnownAttribute(key);
dottie.set(this.attributes, key, value);
}

public forgetAttribute(key: string): void {
this.assertKnownAttribute(key);
this.setAttribute(key, undefined);
}

Expand Down Expand Up @@ -232,4 +237,8 @@ export class Wallet implements State.IWallet {
public toString(): string {
return `${this.address} (${Utils.formatSatoshi(this.balance)})`;
}

private assertKnownAttribute(key: string): void {
assert(Handlers.Registry.isKnownWalletAttribute(key), `Tried to access unknown attribute: ${key}`);
}
}
13 changes: 13 additions & 0 deletions packages/core-transactions/src/handlers/delegate-registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ export class DelegateRegistrationTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return [
"delegate",
"delegate.lastBlock",
"delegate.rank",
"delegate.round",
"delegate.username",
"delegate.voteBalance",
"delegate.forgedTotal",
"delegate.approval",
];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);
const forgedBlocks = await connection.blocksRepository.getDelegatesForgedBlocks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class DelegateResignationTransactionHandler extends TransactionHandler {
return [DelegateRegistrationTransactionHandler];
}

public walletAttributes(): ReadonlyArray<string> {
return ["delegate.resigned"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
23 changes: 20 additions & 3 deletions packages/core-transactions/src/handlers/handler-registry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Enums, Errors, Transactions } from "@arkecosystem/crypto";

import assert from "assert";
import { InvalidTransactionTypeError } from "../errors";
import { DelegateRegistrationTransactionHandler } from "./delegate-registration";
import { DelegateResignationTransactionHandler } from "./delegate-resignation";
import { HtlcClaimTransactionHandler } from "./htlc-claim";
Expand All @@ -9,18 +11,18 @@ import { IpfsTransactionHandler } from "./ipfs";
import { MultiPaymentTransactionHandler } from "./multi-payment";
import { MultiSignatureTransactionHandler } from "./multi-signature";
import { SecondSignatureTransactionHandler } from "./second-signature";
import { TransactionHandler, TransactionHandlerConstructor } from "./transaction";
import { TransferTransactionHandler } from "./transfer";
import { VoteTransactionHandler } from "./vote";

import { InvalidTransactionTypeError } from "../errors";
import { TransactionHandler, TransactionHandlerConstructor } from "./transaction";

export class TransactionHandlerRegistry {
private readonly registeredTransactionHandlers: Map<
Transactions.InternalTransactionType,
TransactionHandler
> = new Map();

private readonly knownWalletAttributes: Map<string, boolean> = new Map();

constructor() {
this.registerTransactionHandler(TransferTransactionHandler);
this.registerTransactionHandler(SecondSignatureTransactionHandler);
Expand Down Expand Up @@ -80,6 +82,12 @@ export class TransactionHandlerRegistry {
Transactions.TransactionRegistry.registerTransactionType(transactionConstructor);
}

const walletAttributes: ReadonlyArray<string> = service.walletAttributes();
for (const attribute of walletAttributes) {
assert(!this.knownWalletAttributes.has(attribute), `Wallet attribute is already known: ${attribute}`);
this.knownWalletAttributes.set(attribute, true);
}

this.registeredTransactionHandlers.set(internalType, service);
}

Expand All @@ -100,9 +108,18 @@ export class TransactionHandlerRegistry {
throw new InvalidTransactionTypeError(internalType.toString());
}

const walletAttributes: ReadonlyArray<string> = service.walletAttributes();
for (const attribute of walletAttributes) {
this.knownWalletAttributes.delete(attribute);
}

Transactions.TransactionRegistry.deregisterTransactionType(transactionConstructor);
this.registeredTransactionHandlers.delete(internalType);
}

public isKnownWalletAttribute(attribute: string): boolean {
return this.knownWalletAttributes.has(attribute);
}
}

export const transactionHandlerRegistry = new TransactionHandlerRegistry();
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/htlc-claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export class HtlcClaimTransactionHandler extends TransactionHandler {
return [HtlcLockTransactionHandler];
}

public walletAttributes(): ReadonlyArray<string> {
return [];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);
for (const transaction of transactions) {
Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/htlc-lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export class HtlcLockTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return ["htlc.locks", "htlc.lockedBalance"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const lockTransactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);
for (const transaction of lockTransactions) {
Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/htlc-refund.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export class HtlcRefundTransactionHandler extends TransactionHandler {
return [HtlcLockTransactionHandler];
}

public walletAttributes(): ReadonlyArray<string> {
return [];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);
for (const transaction of transactions) {
Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/ipfs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export class IpfsTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return ["ipfs", "ipfs.hashes"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/multi-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export class MultiPaymentTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return [];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/multi-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export class MultiSignatureTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return ["multiSignature"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/second-signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export class SecondSignatureTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return ["secondPublicKey"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
2 changes: 2 additions & 0 deletions packages/core-transactions/src/handlers/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export abstract class TransactionHandler implements ITransactionHandler {

public abstract dependencies(): ReadonlyArray<TransactionHandlerConstructor>;

public abstract walletAttributes(): ReadonlyArray<string>;

/**
* Wallet logic
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export class TransferTransactionHandler extends TransactionHandler {
return [];
}

public walletAttributes(): ReadonlyArray<string> {
return [];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getReceivedTransactions();

Expand Down
4 changes: 4 additions & 0 deletions packages/core-transactions/src/handlers/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export class VoteTransactionHandler extends TransactionHandler {
return [DelegateRegistrationTransactionHandler];
}

public walletAttributes(): ReadonlyArray<string> {
return ["vote"];
}

public async bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void> {
const transactions = await connection.transactionsRepository.getAssetsByType(this.getConstructor().type);

Expand Down
2 changes: 2 additions & 0 deletions packages/core-transactions/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface ITransactionHandler {

dependencies(): ReadonlyArray<TransactionHandlerConstructor>;

walletAttributes(): ReadonlyArray<string>;

bootstrap(connection: Database.IConnection, walletManager: State.IWalletManager): Promise<void>;

isActivated(): Promise<boolean>;
Expand Down

0 comments on commit 3c39180

Please sign in to comment.