Skip to content

Commit

Permalink
refactor(core-transaction-pool): add type, interfaces and sort out st…
Browse files Browse the repository at this point in the history
…ructural issues
  • Loading branch information
faustbrian committed Apr 19, 2019
1 parent c497f93 commit ea79251
Show file tree
Hide file tree
Showing 38 changed files with 1,443 additions and 1,892 deletions.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion __tests__/unit/core-transaction-pool/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import cloneDeep from "lodash.clonedeep";
import randomSeed from "random-seed";
import { Connection } from "../../../packages/core-transaction-pool/src";
import { defaults } from "../../../packages/core-transaction-pool/src/defaults";
import { MemPoolTransaction } from "../../../packages/core-transaction-pool/src/mem-pool-transaction";
import { MemPoolTransaction } from "../../../packages/core-transaction-pool/src/pool-transaction";
import { TransactionFactory } from "../../helpers/transaction-factory";
import { block2, delegates } from "../../utils/fixtures/unitnet";
import { transactions as mockData } from "./__fixtures__/transactions";
Expand Down
550 changes: 0 additions & 550 deletions __tests__/unit/core-transaction-pool/guard.test.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import { Enums, Interfaces } from "@arkecosystem/crypto";
import { MemPoolTransaction } from "../../../packages/core-transaction-pool/src/mem-pool-transaction";
import { MemPoolTransaction } from "../../../packages/core-transaction-pool/src/pool-transaction";
import { transactions } from "./__fixtures__/transactions";

describe("MemPoolTransaction", () => {
describe("expireAt", () => {
describe("expiresAt", () => {
it("should return transaction expiration when it is set", () => {
const transaction: Interfaces.ITransaction = transactions.dummy1;
transaction.data.expiration = 1123;
const memPoolTransaction = new MemPoolTransaction(transaction);
expect(memPoolTransaction.expireAt(1)).toBe(1123);
expect(memPoolTransaction.expiresAt(1)).toBe(1123);
});

it("should return timestamp + maxTransactionAge when expiration is not set", () => {
const transaction: Interfaces.ITransaction = transactions.dummy2;
transaction.data.timestamp = 344;
const memPoolTransaction = new MemPoolTransaction(transaction);
expect(memPoolTransaction.expireAt(131)).toBe(transaction.data.timestamp + 131);
expect(memPoolTransaction.expiresAt(131)).toBe(transaction.data.timestamp + 131);
});

it("should return null for timelock transfer with no expiration set", () => {
const transaction: Interfaces.ITransaction = transactions.dummy3;
transaction.data.type = Enums.TransactionTypes.TimelockTransfer;
const memPoolTransaction = new MemPoolTransaction(transaction);
expect(memPoolTransaction.expireAt(1)).toBe(null);
expect(memPoolTransaction.expiresAt(1)).toBe(null);
});
});
});
2 changes: 1 addition & 1 deletion packages/core-api/src/repositories/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ export class TransactionsRepository extends Repository implements IRepository {
* @return {String}
*/
public __publicKeyFromAddress(senderId): string {
if (this.databaseService.walletManager.exists(senderId)) {
if (this.databaseService.walletManager.has(senderId)) {
return this.databaseService.walletManager.findByAddress(senderId).publicKey;
}

Expand Down
23 changes: 5 additions & 18 deletions packages/core-api/src/versions/2/transactions/controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { app } from "@arkecosystem/core-container";
import { P2P, TransactionPool } from "@arkecosystem/core-interfaces";
import { TransactionGuard } from "@arkecosystem/core-transaction-pool";
import { Enums } from "@arkecosystem/crypto";
import { Enums, Interfaces } from "@arkecosystem/crypto";
import Boom from "boom";
import Hapi from "hapi";
import { Controller } from "../shared/controller";
Expand All @@ -22,18 +21,13 @@ export class TransactionsController extends Controller {

public async store(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
if (!this.transactionPool.options.enabled) {
return Boom.serverUnavailable("Transaction pool is disabled.");
}

const guard = new TransactionGuard(this.transactionPool);

const result = await guard.validate((request.payload as any).transactions);
const processor: TransactionPool.IProcessor = this.transactionPool.makeProcessor();
const result = await processor.validate((request.payload as any).transactions);

if (result.broadcast.length > 0) {
app.resolvePlugin<P2P.IPeerService>("p2p")
.getMonitor()
.broadcastTransactions(guard.getBroadcastTransactions());
.broadcastTransactions(processor.getBroadcastTransactions());
}

return {
Expand Down Expand Up @@ -63,10 +57,6 @@ export class TransactionsController extends Controller {

public async unconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
if (!this.transactionPool.options.enabled) {
return Boom.serverUnavailable("Transaction pool is disabled.");
}

const pagination = super.paginate(request);

const transactions = this.transactionPool.getTransactions(pagination.offset, pagination.limit);
Expand All @@ -89,11 +79,8 @@ export class TransactionsController extends Controller {

public async showUnconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
if (!this.transactionPool.options.enabled) {
return Boom.serverUnavailable("Transaction pool is disabled.");
}
const transaction: Interfaces.ITransaction = this.transactionPool.getTransaction(request.params.id);

const transaction = this.transactionPool.getTransaction(request.params.id);
if (!transaction) {
return Boom.notFound("Transaction not found");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class AcceptBlockHandler extends BlockHandler {
this.logger.error(`Refused new block ${JSON.stringify(this.block.data)}`);
this.logger.debug(error.stack);

this.blockchain.transactionPool.purgeBlock(this.block);
this.blockchain.transactionPool.purgeByBlock(this.block);
this.blockchain.forkBlock(this.block);

return super.execute();
Expand Down
2 changes: 1 addition & 1 deletion packages/core-database/src/database-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ export class DatabaseService implements Database.IDatabaseService {
public async verifyTransaction(transaction: Interfaces.ITransaction): Promise<boolean> {
const senderId: string = Identities.Address.fromPublicKey(transaction.data.senderPublicKey);

const sender: Database.IWallet = this.walletManager.findByAddress(senderId); // should exist
const sender: Database.IWallet = this.walletManager.findByAddress(senderId);
const transactionHandler: TransactionHandler = TransactionHandlerRegistry.get(transaction.type);

if (!sender.publicKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus
private getPublicKeyFromAddress(senderId: string): string {
const { walletManager }: Database.IDatabaseService = this.databaseServiceProvider();

return walletManager.exists(senderId) ? walletManager.findByAddress(senderId).publicKey : null;
return walletManager.has(senderId) ? walletManager.findByAddress(senderId).publicKey : null;
}

private async mapBlocksToTransactions(rows): Promise<Interfaces.ITransactionData[]> {
Expand Down
18 changes: 10 additions & 8 deletions packages/core-database/src/wallet-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import pluralize from "pluralize";
import { Wallet } from "./wallet";

export class WalletManager implements Database.IWalletManager {
public logger = app.resolvePlugin<Logger.ILogger>("logger");
public config = app.getConfig();

// @TODO: make this private and read-only
public byAddress: { [key: string]: Database.IWallet };
// @TODO: make this private and read-only
public byPublicKey: { [key: string]: Database.IWallet };
// @TODO: make this private and read-only
public byUsername: { [key: string]: Database.IWallet };
// @TODO: make this private and read-only
public logger: Logger.ILogger = app.resolvePlugin<Logger.ILogger>("logger");

/**
* Create a new wallet manager instance.
Expand Down Expand Up @@ -83,6 +85,10 @@ export class WalletManager implements Database.IWalletManager {
}
}

public has(addressOrPublicKey: string): boolean {
return this.hasByAddress(addressOrPublicKey) || this.hasByPublicKey(addressOrPublicKey);
}

public hasByAddress(address: string): boolean {
return !!this.byAddress[address];
}
Expand All @@ -107,10 +113,6 @@ export class WalletManager implements Database.IWalletManager {
delete this.byUsername[username];
}

public exists(addressOrPublicKey: string): boolean {
return this.hasByAddress(addressOrPublicKey) || this.hasByPublicKey(addressOrPublicKey);
}

public index(wallets: Database.IWallet[]): void {
for (const wallet of wallets) {
this.reindex(wallet);
Expand Down Expand Up @@ -389,7 +391,7 @@ export class WalletManager implements Database.IWalletManager {
const { type, data } = transaction;

const transactionHandler: TransactionHandler = TransactionHandlerRegistry.get(transaction.type);
const sender: Database.IWallet = this.findByPublicKey(data.senderPublicKey); // Should exist
const sender: Database.IWallet = this.findByPublicKey(data.senderPublicKey);
const recipient: Database.IWallet = this.byAddress[data.recipientId];

transactionHandler.revertForSender(transaction, sender);
Expand Down
4 changes: 1 addition & 3 deletions packages/core-interfaces/src/core-database/wallet-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export type IDelegateWallet = IWallet & { rate: number; round: number };
export interface IWalletManager {
logger: Logger.ILogger;

config: any;

reset(): void;

allByAddress(): IWallet[];
Expand All @@ -48,7 +46,7 @@ export interface IWalletManager {

findByAddress(address: string): IWallet;

exists(addressOrPublicKey: string): boolean;
has(addressOrPublicKey: string): boolean;

findByPublicKey(publicKey: string): IWallet;

Expand Down
172 changes: 18 additions & 154 deletions packages/core-interfaces/src/core-transaction-pool/connection.ts
Original file line number Diff line number Diff line change
@@ -1,180 +1,44 @@
import { Enums, Interfaces } from "@arkecosystem/crypto";
import { Dato } from "@faustbrian/dato";
import { IProcessor } from "./processor";

export interface IAddTransactionResponse {
success: boolean;
}

export interface IAddTransactionErrorResponse extends IAddTransactionResponse {
transaction: Interfaces.ITransaction;
type: string;
message: string;
success: boolean;
transaction?: Interfaces.ITransaction;
type?: string;
message?: string;
}

export interface IConnection {
options: any;
loggedAllowedSenders: string[];
walletManager: any;
makeProcessor(): IProcessor;

make(): Promise<this>;

/**
* Get a driver instance.
*/
driver(): () => any;

/**
* Disconnect from transaction pool.
* @return {void}
*/
disconnect(): void;

/**
* Get the number of transactions in the pool.
*/
getPoolSize(): number;

/**
* Get the number of transactions in the pool from a specific sender\
*/
getSenderSize(senderPublicKey: string): number;

/**
* Add many transactions to the pool.
* @param {Array} transactions, already transformed and verified
* by transaction guard - must have serialized field
* @return {Object} like
* {
* added: [ ... successfully added transactions ... ],
* notAdded: [ { transaction: Transaction, type: String, message: String }, ... ]
* }
*/
addTransactions(
transactions: Interfaces.ITransaction[],
): {
added: Interfaces.ITransaction[];
notAdded: IAddTransactionErrorResponse[];
notAdded: IAddTransactionResponse[];
};

/**
* Add a transaction to the pool.
*/
addTransaction(transaction: Interfaces.ITransaction): IAddTransactionResponse;

/**
* Remove a transaction from the pool by transaction object.
* @param {Transaction} transaction
* @return {void}
*/
removeTransaction(transaction: Interfaces.ITransaction): void;

/**
* Remove a transaction from the pool by id.
*/
removeTransactionById(id: string, senderPublicKey?: string): void;

/**
* Get all transactions that are ready to be forged.
*/
getTransactionsForForging(blockSize: number): string[];

/**
* Get a transaction by transaction id.
*/
acceptChainedBlock(block: Interfaces.IBlock): void;
blockSender(senderPublicKey: string): Dato;
buildWallets(): Promise<void>;
flush(): void;
getTransaction(id: string): Interfaces.ITransaction;

/**
* Get all transactions within the specified range [start, start + size), ordered by fee.
* @return {(Array|void)} array of serialized transaction hex strings
*/
getTransactions(start: number, size: number, maxBytes?: number): Buffer[];

/**
* Get all transactions within the specified range [start, start + size).
* @return {Array} array of transactions IDs in the specified range
*/
getTransactionIdsForForging(start: number, size: number): string[];

/**
* Get data from all transactions within the specified range [start, start + size).
* Transactions are ordered by fee (highest fee first) or by
* insertion time, if fees equal (earliest transaction first).
* @return {Array} array of transaction[property]
*/
getTransactionsData(start: number, size: number, property: string, maxBytes?: number): string[] | Buffer[];

/**
* Get all transactions of a given type from the pool.
*/
getTransactions(start: number, size: number, maxBytes?: number): Buffer[];
getTransactionsByType(type: any): any;

/**
* Remove all transactions from the transaction pool belonging to specific sender.
*/
removeTransactionsForSender(senderPublicKey: string): void;

/**
* Check whether sender of transaction has exceeded max transactions in queue.
*/
getTransactionsData<T>(start: number, size: number, property: string, maxBytes?: number): T[];
getTransactionsForForging(blockSize: number): string[];
has(transactionId: string): any;
hasExceededMaxTransactions(transaction: Interfaces.ITransactionData): boolean;

/**
* Flush the pool (delete all transactions from it).
*/
flush(): void;

/**
* Checks if a transaction exists in the pool.
*/
transactionExists(transactionId: string): any;

/**
* Check if transaction sender is blocked
* @return {Boolean}
*/
isSenderBlocked(senderPublicKey: string): boolean;

/**
* Blocks sender for a specified time
*/
blockSender(senderPublicKey: string): Dato;

/**
* Processes recently accepted block by the blockchain.
* It removes block transaction from the pool and adjusts
* pool wallets for non existing transactions.
*
* @param {Object} block
* @return {void}
*/
acceptChainedBlock(block: Interfaces.IBlock): void;

/**
* Rebuild pool manager wallets
* Removes all the wallets from pool manager and applies transaction from pool - if any
* It waits for the node to sync, and then check the transactions in pool
* and validates them and apply to the pool manager.
*/
buildWallets(): Promise<void>;

purgeByBlock(block: Interfaces.IBlock): void;
purgeByPublicKey(senderPublicKey: string): void;

/**
* Purges all transactions from senders with at least one
* invalid transaction.
*/
purgeSendersWithInvalidTransactions(block: Interfaces.IBlock): void;

/**
* Purges all transactions from the block.
* Purges if transaction exists. It assumes that if trx exists that also wallet exists in pool
*/
purgeBlock(block: Interfaces.IBlock): void;

/**
* Check whether a given sender has any transactions of the specified type
* in the pool.
*/
removeTransaction(transaction: Interfaces.ITransaction): void;
removeTransactionById(id: string, senderPublicKey?: string): void;
removeTransactionsForSender(senderPublicKey: string): void;
senderHasTransactionsOfType(senderPublicKey: string, transactionType: Enums.TransactionTypes): boolean;
}
Loading

0 comments on commit ea79251

Please sign in to comment.