Skip to content

Commit

Permalink
refactor(core-transactions): IoC TransactionHandlerRegistry
Browse files Browse the repository at this point in the history
* Transition TransactionHandlerRegistry to IoC

To isolate pool and core states separate stacks of transaction
handlers need to be instantiated. And so TransactionHandlerRegistry
instantiation has to be native to IoC

I removed initialization routines during registration, instead it is
performed during instantiation through postConstruct decorator.
All available transaction handlers are discovered through @multiInject
decorator. During registration handler instances are accepted Instead
of instantiating them with new keyword. Dependencies are also handled
through instances and normal injection.

Due to circular references on One and Two namespaces concrete types
are required in specific handlers that need to define dependencies.

* Leave registry in singletone scope for  now

Will have to leave TransactionHandlerRegistry in singletone scope for now
because of global TransactionRegistry object wich raises errors when
transaction registered twice (due to handler registry re-instantiation).
  • Loading branch information
rainydio authored and faustbrian committed Dec 17, 2019
1 parent ff8975a commit c11af3a
Show file tree
Hide file tree
Showing 25 changed files with 173 additions and 133 deletions.
2 changes: 2 additions & 0 deletions packages/core-kernel/src/ioc/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export const Identifiers = {
TransactionPoolWalletRepository: Symbol.for("TransactionPool<WalletRepository>"),
// Transactions - @todo: better names that won't clash
WalletAttributes: Symbol.for("Wallet<Attributes>"),
// TransactionHandler
TransactionHandler: Symbol.for("TransactionHandler"),
// Registries
TransactionHandlerRegistry: Symbol.for("Registry<TransactionHandler>"),
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BridgechainRegistrationTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainRegistrationTransaction;
@Container.inject(BusinessRegistrationTransactionHandler)
private readonly businessRegistrationTransactionHandler!: BusinessRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [this.businessRegistrationTransactionHandler];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [BusinessRegistrationTransactionHandler];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainRegistrationTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BridgechainResignationTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainResignationTransaction;
@Container.inject(BridgechainRegistrationTransactionHandler)
private readonly bridgechainRegistrationTransactionHandler!: BridgechainRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [this.bridgechainRegistrationTransactionHandler];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [BridgechainRegistrationTransactionHandler];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainResignationTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BridgechainUpdateTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainUpdateTransaction;
@Container.inject(BridgechainRegistrationTransactionHandler)
private readonly bridgechainRegistrationTransactionHandler!: BridgechainRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [this.bridgechainRegistrationTransactionHandler];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [BridgechainRegistrationTransactionHandler];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BridgechainUpdateTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BusinessRegistrationTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessRegistrationTransaction;
public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessRegistrationTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BusinessResignationTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessResignationTransaction;
@Container.inject(BusinessRegistrationTransactionHandler)
private readonly businessRegistrationTransactionHandler!: BusinessRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [this.businessRegistrationTransactionHandler];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [BusinessRegistrationTransactionHandler];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessResignationTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import { MagistrateTransactionHandler } from "./magistrate-handler";

@Container.injectable()
export class BusinessUpdateTransactionHandler extends MagistrateTransactionHandler {
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessUpdateTransaction;
@Container.inject(BusinessRegistrationTransactionHandler)
private readonly businessRegistrationTransactionHandler!: BusinessRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<Handlers.TransactionHandler> {
return [this.businessRegistrationTransactionHandler];
}

public dependencies(): ReadonlyArray<Handlers.TransactionHandlerConstructor> {
return [BusinessRegistrationTransactionHandler];
public getConstructor(): Transactions.TransactionConstructor {
return MagistrateTransactions.BusinessUpdateTransaction;
}

public walletAttributes(): ReadonlyArray<string> {
Expand Down
23 changes: 13 additions & 10 deletions packages/core-magistrate-transactions/src/service-provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Container, Contracts, Providers } from "@arkecosystem/core-kernel";
import { Handlers } from "@arkecosystem/core-transactions";

import {
BridgechainRegistrationTransactionHandler,
Expand All @@ -13,15 +12,19 @@ import { bridgechainIndexer, businessIndexer, MagistrateIndex } from "./wallet-i

export class ServiceProvider extends Providers.ServiceProvider {
public async register(): Promise<void> {
const transactionHandlerRegistry = this.app.get<Handlers.Registry>(
Container.Identifiers.TransactionHandlerRegistry,
);
transactionHandlerRegistry.registerTransactionHandler(BusinessRegistrationTransactionHandler);
transactionHandlerRegistry.registerTransactionHandler(BusinessResignationTransactionHandler);
transactionHandlerRegistry.registerTransactionHandler(BusinessUpdateTransactionHandler);
transactionHandlerRegistry.registerTransactionHandler(BridgechainRegistrationTransactionHandler);
transactionHandlerRegistry.registerTransactionHandler(BridgechainResignationTransactionHandler);
transactionHandlerRegistry.registerTransactionHandler(BridgechainUpdateTransactionHandler);
this.app.bind(BusinessRegistrationTransactionHandler).toSelf();
this.app.bind(BusinessResignationTransactionHandler).toSelf();
this.app.bind(BusinessUpdateTransactionHandler).toSelf();
this.app.bind(BridgechainRegistrationTransactionHandler).toSelf();
this.app.bind(BridgechainResignationTransactionHandler).toSelf();
this.app.bind(BridgechainUpdateTransactionHandler).toSelf();

this.app.bind(Container.Identifiers.TransactionHandler).to(BusinessRegistrationTransactionHandler);
this.app.bind(Container.Identifiers.TransactionHandler).to(BusinessResignationTransactionHandler);
this.app.bind(Container.Identifiers.TransactionHandler).to(BusinessUpdateTransactionHandler);
this.app.bind(Container.Identifiers.TransactionHandler).to(BridgechainRegistrationTransactionHandler);
this.app.bind(Container.Identifiers.TransactionHandler).to(BridgechainResignationTransactionHandler);
this.app.bind(Container.Identifiers.TransactionHandler).to(BridgechainUpdateTransactionHandler);
}

public async boot(): Promise<void> {
Expand Down
65 changes: 19 additions & 46 deletions packages/core-transactions/src/handlers/handler-registry.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,28 @@
import { Container, Contracts, Services, Utils } from "@arkecosystem/core-kernel";
import { Container, Services, Utils } from "@arkecosystem/core-kernel";
import { Enums, Transactions } from "@arkecosystem/crypto";

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

// todo: review the implementation
@Container.injectable()
export class TransactionHandlerRegistry {
/**
* @private
* @type {Contracts.Kernel.Application}
* @memberof Server
*/
@Container.inject(Container.Identifiers.Application)
private readonly app!: Contracts.Kernel.Application;
@Container.inject(Container.Identifiers.WalletAttributes)
private readonly walletAttributes!: Services.Attributes.AttributeSet;

@Container.multiInject(Container.Identifiers.TransactionHandler)
private readonly allHandlers!: TransactionHandler[];

private readonly registeredTransactionHandlers: Map<
Transactions.InternalTransactionType,
Map<number, TransactionHandler>
> = new Map();

public initialize(): void {
this.registerTransactionHandler(One.TransferTransactionHandler);
this.registerTransactionHandler(Two.TransferTransactionHandler);

this.registerTransactionHandler(One.SecondSignatureRegistrationTransactionHandler);
this.registerTransactionHandler(Two.SecondSignatureRegistrationTransactionHandler);

this.registerTransactionHandler(One.DelegateRegistrationTransactionHandler);
this.registerTransactionHandler(Two.DelegateRegistrationTransactionHandler);

this.registerTransactionHandler(One.VoteTransactionHandler);
this.registerTransactionHandler(Two.VoteTransactionHandler);

this.registerTransactionHandler(One.MultiSignatureRegistrationTransactionHandler);
this.registerTransactionHandler(Two.MultiSignatureRegistrationTransactionHandler);

this.registerTransactionHandler(Two.IpfsTransactionHandler);

this.registerTransactionHandler(Two.MultiPaymentTransactionHandler);

this.registerTransactionHandler(Two.DelegateResignationTransactionHandler);

this.registerTransactionHandler(Two.HtlcLockTransactionHandler);
this.registerTransactionHandler(Two.HtlcClaimTransactionHandler);
this.registerTransactionHandler(Two.HtlcRefundTransactionHandler);
@Container.postConstruct()
public registerAllHandlersPostConstruct() {
for (const handler of this.allHandlers) {
this.registerTransactionHandler(handler);
}
}

public getAll(): Map<number, TransactionHandler>[] {
Expand Down Expand Up @@ -106,8 +83,7 @@ export class TransactionHandlerRegistry {
return activatedTransactionHandlers;
}

public registerTransactionHandler(constructor: TransactionHandlerConstructor) {
const handler: TransactionHandler = this.app.resolve(constructor);
public registerTransactionHandler(handler: TransactionHandler) {
const transactionConstructor = handler.getConstructor();

Utils.assert.defined<number>(transactionConstructor.type);
Expand All @@ -132,10 +108,8 @@ export class TransactionHandlerRegistry {

for (const attribute of handler.walletAttributes()) {
// TODO: scope attribute by `handler.getConstructor().key` to distinguish between duplicate attributes ?
if (
!this.app.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes).has(attribute)
) {
this.app.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes).set(attribute);
if (!this.walletAttributes.has(attribute)) {
this.walletAttributes.set(attribute);
}
}

Expand All @@ -146,9 +120,8 @@ export class TransactionHandlerRegistry {
this.registeredTransactionHandlers.get(internalType)?.set(transactionConstructor.version, handler);
}

public deregisterTransactionHandler(constructor: TransactionHandlerConstructor): void {
const service: TransactionHandler = new constructor();
const transactionConstructor = service.getConstructor();
public deregisterTransactionHandler(handler: TransactionHandler): void {
const transactionConstructor = handler.getConstructor();

Utils.assert.defined<number>(transactionConstructor.type);
Utils.assert.defined<number>(transactionConstructor.typeGroup);
Expand All @@ -169,8 +142,8 @@ export class TransactionHandlerRegistry {
throw new InvalidTransactionTypeError(internalType.toString());
}

for (const attribute of service.walletAttributes()) {
this.app.get<Services.Attributes.AttributeSet>(Container.Identifiers.WalletAttributes).forget(attribute);
for (const attribute of handler.walletAttributes()) {
this.walletAttributes.forget(attribute);
}

Transactions.TransactionRegistry.deregisterTransactionType(transactionConstructor);
Expand Down
4 changes: 2 additions & 2 deletions packages/core-transactions/src/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TransactionHandlerRegistry } from "./handler-registry";
import * as One from "./one";
import { TransactionHandler, TransactionHandlerConstructor } from "./transaction";
import { TransactionHandler } from "./transaction";
import * as Two from "./two";

export { One, Two, TransactionHandler, TransactionHandlerConstructor, TransactionHandlerRegistry as Registry };
export { One, Two, TransactionHandler, TransactionHandlerRegistry as Registry };
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import {
WalletNotADelegateError,
WalletUsernameAlreadyRegisteredError,
} from "../../errors";
import { TransactionHandler, TransactionHandlerConstructor } from "../transaction";
import { TransactionHandler } from "../transaction";

const { TransactionType, TransactionTypeGroup } = Enums;

// todo: revisit the implementation, container usage and arguments after core-database rework
// todo: replace unnecessary function arguments with dependency injection to avoid passing around references
@Container.injectable()
export class DelegateRegistrationTransactionHandler extends TransactionHandler {
public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
public dependencies(): ReadonlyArray<TransactionHandler> {
return [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { Interfaces, Transactions, Utils } from "@arkecosystem/crypto";

import { LegacyMultiSignatureError, MultiSignatureAlreadyRegisteredError } from "../../errors";
import { TransactionReader } from "../../transaction-reader";
import { TransactionHandler, TransactionHandlerConstructor } from "../transaction";
import { TransactionHandler } from "../transaction";

// todo: revisit the implementation, container usage and arguments after core-database rework
// todo: replace unnecessary function arguments with dependency injection to avoid passing around references
@Container.injectable()
export class MultiSignatureRegistrationTransactionHandler extends TransactionHandler {
public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
public dependencies(): ReadonlyArray<TransactionHandler> {
return [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Container, Contracts, Utils } from "@arkecosystem/core-kernel";
import { Interfaces, Transactions } from "@arkecosystem/crypto";

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

// todo: revisit the implementation, container usage and arguments after core-database rework
// todo: replace unnecessary function arguments with dependency injection to avoid passing around references
@Container.injectable()
export class SecondSignatureRegistrationTransactionHandler extends TransactionHandler {
public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
public dependencies(): ReadonlyArray<TransactionHandler> {
return [];
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core-transactions/src/handlers/one/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Container, Contracts, Utils } from "@arkecosystem/core-kernel";
import { Interfaces, Managers, Transactions } from "@arkecosystem/crypto";

import { isRecipientOnActiveNetwork } from "../../utils";
import { TransactionHandler, TransactionHandlerConstructor } from "../transaction";
import { TransactionHandler } from "../transaction";

// todo: revisit the implementation, container usage and arguments after core-database rework
// todo: replace unnecessary function arguments with dependency injection to avoid passing around references
@Container.injectable()
export class TransferTransactionHandler extends TransactionHandler {
public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
public dependencies(): ReadonlyArray<TransactionHandler> {
return [];
}

Expand Down
15 changes: 9 additions & 6 deletions packages/core-transactions/src/handlers/one/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import {
VotedForNonDelegateError,
VotedForResignedDelegateError,
} from "../../errors";
import { One } from "../index";
import { TransactionHandler, TransactionHandlerConstructor } from "../transaction";
import { TransactionHandler } from "../transaction";
import { DelegateRegistrationTransactionHandler } from "./delegate-registration";

// todo: revisit the implementation, container usage and arguments after core-database rework
// todo: replace unnecessary function arguments with dependency injection to avoid passing around references
@Container.injectable()
export class VoteTransactionHandler extends TransactionHandler {
@Container.inject(DelegateRegistrationTransactionHandler)
private readonly delegateRegistrationTransactionHandlerOne!: DelegateRegistrationTransactionHandler;

public dependencies(): ReadonlyArray<TransactionHandler> {
return [this.delegateRegistrationTransactionHandlerOne];
}

public walletAttributes(): ReadonlyArray<string> {
return ["vote"];
}
Expand All @@ -23,10 +30,6 @@ export class VoteTransactionHandler extends TransactionHandler {
return Transactions.One.VoteTransaction;
}

public dependencies(): ReadonlyArray<TransactionHandlerConstructor> {
return [One.DelegateRegistrationTransactionHandler];
}

public async bootstrap(): Promise<void> {
return;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/core-transactions/src/handlers/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import {
} from "../errors";
import { TransactionReader } from "../transaction-reader";

export type TransactionHandlerConstructor = new () => TransactionHandler;

// todo: revisit the implementation, container usage and arguments after core-database rework
@Container.injectable()
export abstract class TransactionHandler {
Expand All @@ -35,7 +33,7 @@ export abstract class TransactionHandler {

public abstract getConstructor(): Transactions.TransactionConstructor;

public abstract dependencies(): ReadonlyArray<TransactionHandlerConstructor>;
public abstract dependencies(): ReadonlyArray<TransactionHandler>;

public abstract walletAttributes(): ReadonlyArray<string>;

Expand Down
Loading

0 comments on commit c11af3a

Please sign in to comment.