diff --git a/package.json b/package.json index d0f48e8..352c27b 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,6 @@ ], "license": "MIT", "dependencies": { - "@types/crypto-js": "^3.1.43", - "@types/lodash": "^4.14.113", - "commitizen": "3.0.4", "crypto-js": "^3.1.9-1", "js-sha3": "0.8.0", "lodash": "^4.17.10", @@ -57,9 +54,12 @@ "rxjs": "^6.2.2" }, "devDependencies": { + "@types/crypto-js": "^3.1.43", + "@types/lodash": "^4.14.113", "@types/jest": "^23.3.9", "@types/sinon": "5.0.7", "codecov": "^3.0.4", + "commitizen": "3.0.4", "commitlint": "7.2.1", "cz-conventional-changelog": "^2.1.0", "ghooks": "^2.0.4", diff --git a/src/infrastructure/ApostilleHttp.ts b/src/infrastructure/ApostilleHttp.ts index 2aebb93..8e7d8c3 100644 --- a/src/infrastructure/ApostilleHttp.ts +++ b/src/infrastructure/ApostilleHttp.ts @@ -1,19 +1,50 @@ import { sortBy } from 'lodash'; import { AccountHttp, Address, AggregateTransaction, Listener, PublicAccount, QueryParams, SignedTransaction, Transaction, TransactionAnnounceResponse, TransactionHttp, TransactionInfo, TransferTransaction } from 'nem2-sdk'; -import { filter, mergeMap } from 'rxjs/operators'; +import { EMPTY, Observable } from 'rxjs'; +import { expand, filter, mergeMap } from 'rxjs/operators'; import { Errors } from '../types/Errors'; +import { Initiator } from './Initiator'; +export interface IReadyTransaction { + transaction: Transaction; + initiator?: Initiator; +} export class ApostilleHttp { private transactionHttp: TransactionHttp; private accountHttp: AccountHttp; + private listener: Listener; + + private unannouncedTransactions: IReadyTransaction[] = []; public constructor(url: string) { this.transactionHttp = new TransactionHttp(url); this.accountHttp = new AccountHttp(url); + this.listener = new Listener(url); + } + + public addTransaction(transaction: Transaction, initiator?: Initiator): void { + this.unannouncedTransactions.push({transaction, initiator}); + } + + public getIncompleteTransactions(): Transaction[] { + return this.unannouncedTransactions + .filter((readyTransaction) => { + return readyTransaction.initiator === undefined; + }) + .map((readyTransaction) => { + return readyTransaction.transaction; + }); + } + + public hasIncompleteTransactions(): boolean { + return this.unannouncedTransactions.some((readyTransaction) => { + return readyTransaction.initiator === undefined; + }); } /** + * @internal * Generic announce method * * @param {SignedTransaction} signedTransaction @@ -41,10 +72,10 @@ export class ApostilleHttp { cosignatoryAccount: PublicAccount, signedAggregateBondedTransaction: SignedTransaction, signedLockFundsTransaction: SignedTransaction, - listener: Listener): Promise { + ): Promise { return new Promise((resolve, reject) => { - listener.open().then(() => { + this.listener.open().then(() => { // Announce lock funds first this.transactionHttp @@ -52,13 +83,14 @@ export class ApostilleHttp { .subscribe((x) => console.log(x), (err) => reject(err)); // Watch for lock funds confirmation before announcing aggregate bonded - listener + this.listener .confirmed(cosignatoryAccount.address) .pipe( - filter((transaction) => transaction.transactionInfo !== undefined - && transaction.transactionInfo.hash === signedLockFundsTransaction.hash), - mergeMap((ignored) => this.transactionHttp.announceAggregateBonded( - signedAggregateBondedTransaction)), + filter((transaction) => { + return transaction.transactionInfo !== undefined + && transaction.transactionInfo.hash === signedLockFundsTransaction.hash; + }), + mergeMap((ignored) => this.transactionHttp.announceAggregateBonded(signedAggregateBondedTransaction)), ) .subscribe((announcedAggregateBonded) => { resolve(announcedAggregateBonded); @@ -78,13 +110,13 @@ export class ApostilleHttp { */ public async isCreated(publicAccount: PublicAccount): Promise { try { - const unconfirmedTransactions = await this._unconfirmedTransactions(publicAccount); + const unconfirmedTransactions = await this._unconfirmedTransactions(publicAccount).toPromise(); if (unconfirmedTransactions.length) { // the apostille has been sent to the network return true; } else { // then check transactions - const transactions = await this._transactions(publicAccount); + const transactions = await this._transactions(publicAccount).toPromise(); if (transactions.length > 0) { return true; } else { @@ -162,7 +194,7 @@ export class ApostilleHttp { */ public getCreationTransaction(publicAccount: PublicAccount): Promise { return new Promise((resolve, reject) => { - this._fetchAllTransactions(publicAccount).then((transactions: Transaction[]) => { + this.fetchAllTransactions(publicAccount).subscribe((transactions: Transaction[]) => { if (transactions.length > 0) { const firstTransaction = transactions[transactions.length - 1]; if (firstTransaction instanceof TransferTransaction) { @@ -179,73 +211,35 @@ export class ApostilleHttp { } } reject(Errors[Errors.CREATION_TRANSACTIONS_NOT_FOUND]); - }).catch((err) => { - reject(err); }); }); } - /** - * Fetch all transactions pertaining to this publicAccount - * - * @returns {Promise} - * @memberof CertificateHistory - */ - public _fetchAllTransactions(publicAccount: PublicAccount): Promise { - return new Promise(async (resolve, reject) => { - let nextId: string = ''; - const pageSize: number = 100; - let lastPageSize: number = 100; - const allTransactions: Transaction[] = []; - while (lastPageSize === pageSize) { - const queryParams = new QueryParams(pageSize, nextId !== '' ? nextId : undefined); - await this._transactions(publicAccount, queryParams).then((transactions) => { - lastPageSize = transactions.length; - if (lastPageSize < 1) { return; } - nextId = transactions[transactions.length - 1].transactionInfo!.id; - allTransactions.push(...transactions); - }).catch((err) => { - reject(err); - }); - } - resolve(allTransactions); - }); + public fetchAllTransactions(publicAccount: PublicAccount): Observable { + let nextId: string = ''; + const pageSize: number = 100; + let lastPageSize: number = 100; + let queryParams = new QueryParams(pageSize); + return this._transactions(publicAccount, queryParams).pipe( + expand((transactions) => { + lastPageSize = transactions.length; + if (lastPageSize < pageSize) { return EMPTY; } + nextId = transactions[transactions.length - 1].transactionInfo!.id; + queryParams = new QueryParams(pageSize, nextId !== '' ? nextId : undefined); + return this._transactions(publicAccount, queryParams); + }), + ); } - private _unconfirmedTransactions( + public _transactions( publicAccount: PublicAccount, - queryParams ?: QueryParams | undefined): Promise { - - return new Promise((resolve, reject) => { - this.accountHttp.unconfirmedTransactions( - publicAccount, - queryParams, - ).subscribe((unconfirmedTransactions) => { - resolve(unconfirmedTransactions); - }, (err) => { - // network or comunication problem - reject(err); - }); - }); - + queryParams ?: QueryParams | undefined): Observable { + return this.accountHttp.transactions(publicAccount, queryParams); } - private _transactions( + public _unconfirmedTransactions( publicAccount: PublicAccount, - queryParams ?: QueryParams | undefined): Promise { - - return new Promise((resolve, reject) => { - this.accountHttp.transactions( - publicAccount, - queryParams, - ).subscribe((transactions) => { - resolve(transactions); - }, (err) => { - // network or comunication problem - reject(err); - }); - }); - + queryParams ?: QueryParams | undefined): Observable { + return this.accountHttp.unconfirmedTransactions(publicAccount, queryParams); } - } diff --git a/src/infrastructure/Initiator.ts b/src/infrastructure/Initiator.ts new file mode 100644 index 0000000..470ecb5 --- /dev/null +++ b/src/infrastructure/Initiator.ts @@ -0,0 +1,3 @@ +export class Initiator { + +} diff --git a/src/model/apostille/ApostillePublicAccount.ts b/src/model/apostille/ApostillePublicAccount.ts index e85503c..38dfca1 100644 --- a/src/model/apostille/ApostillePublicAccount.ts +++ b/src/model/apostille/ApostillePublicAccount.ts @@ -38,16 +38,16 @@ export class ApostillePublicAccount { * @description - modify ownership of the apostille account by modifying the multisig contract * @param {PublicAccount[]} newOwners - array of owners to add * @param {PublicAccount[]} OwnersToRemove - array of owners to remove - * @param {number} quorum - relative quorum (refer to http://bit.ly/2Jnff1r) - * @param {number} minRemoval - relative number of minimum owners necessary to agree to remove 1/n owners + * @param {number} quorumDelta - relative quorum (refer to http://bit.ly/2Jnff1r) + * @param {number} minRemovalDelta - relative number of minimum owners necessary to agree to remove 1/n owners * @returns {ModifyMultisigAccountTransaction} * @memberof ApostilleAccount */ public transfer( newOwners: PublicAccount[], ownersToRemove: PublicAccount[], - quorum: number, - minRemoval: number, + quorumDelta: number, + minRemovalDelta: number, ): ModifyMultisigAccountTransaction { // the initiator must be a multisig account const modifications: MultisigCosignatoryModification[] = []; @@ -67,8 +67,8 @@ export class ApostillePublicAccount { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), - quorum, - minRemoval, + quorumDelta, + minRemovalDelta, modifications, this.publicAccount.address.networkType, ); @@ -94,17 +94,17 @@ export class ApostillePublicAccount { } /** - * @description sign normal transaction (can sign update(if normal transaction) and lockFundsTransaction) + * @description sign normal transaction (can sign update(if normal transaction) and lockFundsTransaction * @param {TransferTransaction} transferTransaction * @param {Account} initiatorAccount * @param {HashFunction} [hashFunction] - only hash transfer transaction message - * @returns + * @returns {SignedTransaction} * @memberof ApostillePublicAccount */ public sign( transaction: TransferTransaction | Transaction, initiatorAccount: Account, - hashFunction?: HashFunction) { + hashFunction?: HashFunction): SignedTransaction { if (initiatorAccount.address.networkType !== this.publicAccount.address.networkType) { throw new Error(Errors[Errors.NETWORK_TYPE_MISMATCHED]); } @@ -136,12 +136,12 @@ export class ApostillePublicAccount { * @description signed aggregate transaction (can sign update(if multisig transaction) and transfer transaction) * @param {Transaction} transaction * @param {Account[]} signers - * @param {boolean} isCompleteCosignatories + * @param {boolean} isComplete * @returns * @memberof ApostillePublicAccount */ - public signAggregate(transaction: Transaction, signers: Account[], isCompleteCosignatories: boolean) { - if (isCompleteCosignatories) { + public signAggregate(transaction: Transaction, signers: Account[], isComplete: boolean): SignedTransaction { + if (isComplete) { return this._signTransferTransactionAgregateComplete(transaction, signers); } else { return this._signTransferTransactionAggregateBonded(transaction, signers); diff --git a/tests/e2e/PublicApostille/PublicApostille.spec.ts b/tests/e2e/PublicApostille/PublicApostille.spec.ts index 7264569..a24d4e3 100644 --- a/tests/e2e/PublicApostille/PublicApostille.spec.ts +++ b/tests/e2e/PublicApostille/PublicApostille.spec.ts @@ -16,7 +16,7 @@ const hashFn = new SHA256(); describe('announce function should work properly', () => { test('updating an public apostille should be allowed as many times as we want', async () => { - const sinkAddress = Address.createFromRawAddress( 'SCKPEZ-5ZAPYO-PXVF6U-YLHINF-CLYZHO-YCIO3P-KGVV'); + const sinkAddress = Address.createFromRawAddress('SCKPEZ-5ZAPYO-PXVF6U-YLHINF-CLYZHO-YCIO3P-KGVV'); const publicApostille = new PublicApostille(fileName, sinkAddress); let updateTransaction;