diff --git a/example/history.ts b/example/history.ts new file mode 100644 index 0000000..6822fff --- /dev/null +++ b/example/history.ts @@ -0,0 +1,46 @@ +import { NetworkType, PublicAccount } from 'nem2-sdk'; +import { map, reduce, shareReplay } from 'rxjs/operators'; +import { ApostilleHttp } from './../src/infrastructure/ApostilleHttp'; +import { HistoricalEndpoints } from './../src/model/repository/HistoricalEndpoints'; + +// const accountLarge = PublicAccount.createFromPublicKey( +// 'FE9F2C724D0E0360A20B9ED7591E4E25CF25D6F4A4E8E52C491C62D2452397F8', +// NetworkType.MIJIN_TEST); + +// const accountSmall = PublicAccount.createFromPublicKey( +// '38FD27361DB3B12953AF6C3813F740ADB7C980789DCB3D2486B3B654CBC91B72', +// NetworkType.MIJIN_TEST); +const accountSmall = PublicAccount.createFromPublicKey( + '95361ED8C94048BD5B0BDB229C19DF817DB7D66B59F4162E3F3A1D0D813B2AB9', +NetworkType.MIJIN_TEST); + +const http = new ApostilleHttp(HistoricalEndpoints[NetworkType.MIJIN_TEST]); + +const subscription = http.fetchAllTransactions(accountSmall).pipe( + map((txs) => { + return txs.map((tx) => { + if (!tx.transactionInfo) { return {}; } + console.log(tx.transactionInfo.id, tx.transactionInfo.height.compact()); + return { + height: tx.transactionInfo.height, + id: tx.transactionInfo.id, + }; + }); + }), + reduce((acc, txs) => { + return acc.concat(txs); + }), + shareReplay(), +); + +subscription.subscribe((txs) => { + console.log('First', txs.length); +}); +subscription.subscribe((txs) => { + console.log('Second', txs.length); +}); +setTimeout(() => { + subscription.subscribe((txs) => { + console.log('Third', txs.length); + }); +}, 10000); diff --git a/example/index.ts b/example/index.ts new file mode 100644 index 0000000..47d01a5 --- /dev/null +++ b/example/index.ts @@ -0,0 +1,80 @@ +import fs from 'fs'; +import { Account, NetworkType, PublicAccount } from 'nem2-sdk'; +import path from 'path'; +import { SHA256 } from './../src/hash/sha256'; +import { SHA3_256 } from './../src/hash/sha3-256'; +import { ApostilleHttp } from './../src/infrastructure/ApostilleHttp'; +import { Initiator } from './../src/infrastructure/Initiator'; +import { Apostille } from './../src/model/apostille/Apostille'; +import { HistoricalEndpoints } from './../src/model/repository/HistoricalEndpoints'; + +// ApostilleHttp +const network = NetworkType.MIJIN_TEST; +const apostilleHttp = new ApostilleHttp(HistoricalEndpoints[network]); + +// Create Apostille function +function createApostille(pk, fileName) { + const file = fs.readFileSync(`${path.basename(__dirname)}/${fileName}`); + const fileData = file.toString('hex'); + + // Account info + const generatorAccount = Account.createFromPrivateKey(pk, network); + + // FOR DEMO PURPOSES ONLY - DO NOT USE IN PRODUCTION! + const sinkPublicKey = SHA256.hash('PublicApostilleSinkAddress'); + const sinkAddress = PublicAccount.createFromPublicKey(sinkPublicKey, network).address; + + // Create apostille + const apostille = Apostille.initFromSeed(fileName, generatorAccount); + + const fileHash = new SHA3_256().signedHashing(fileData, pk, network); + + console.log(fileHash); + + const apostilleTx = apostille.update(fileHash); + apostilleHttp.addTransaction({ + initiator: new Initiator(generatorAccount), + transaction: apostilleTx, + }); + + const sinkTx = apostille.update(fileHash, sinkAddress); + apostilleHttp.addTransaction({ + initiator: new Initiator(apostille.HDAccount), + transaction: sinkTx, + }); + + const multisigTx = apostille.associate([generatorAccount.publicAccount], 1, 1); + apostilleHttp.addTransaction(multisigTx); + + const metadata = { + filename: fileName, + tags: ['apostille', 'sample', 'LuxTag'], + // tslint:disable-next-line:object-literal-sort-keys + description: 'LuxTag logo', + originFileURL: 'https://luxtag.io/wp-content/uploads/2018/04/logo-Luxtag-uai-720x269.png', + }; + const metadataTx = apostille.update(JSON.stringify(metadata)); + apostilleHttp.addTransaction({ + initiator: new Initiator(generatorAccount), + transaction: metadataTx, + }); +} + +// File info +const f = 'luxtag1.png'; +const accounts = [ + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae1', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae2', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae3', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae4', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae5', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae6', + 'aaaaaaaaaaeeeeeeeeeebbbbbbbbbb5555555555dddddddddd1111111111aae7', +]; + +accounts.forEach((a) => createApostille(a, f)); + +// Announce +apostilleHttp.announceAll().subscribe( + (signedTxs) => console.log(signedTxs), +); diff --git a/example/luxtag1.png b/example/luxtag1.png new file mode 100644 index 0000000..62f41a9 Binary files /dev/null and b/example/luxtag1.png differ diff --git a/src/infrastructure/ApostilleHttp.ts b/src/infrastructure/ApostilleHttp.ts index e003b10..928d621 100644 --- a/src/infrastructure/ApostilleHttp.ts +++ b/src/infrastructure/ApostilleHttp.ts @@ -1,7 +1,7 @@ import { chain, remove, sortBy, uniq } from 'lodash'; -import { Account, AccountHttp, Address, AggregateTransaction, Deadline, InnerTransaction, Listener, LockFundsTransaction, Mosaic, NamespaceId, PublicAccount, QueryParams, SignedTransaction, Transaction, TransactionAnnounceResponse, TransactionHttp, TransactionInfo, TransactionType, TransferTransaction, UInt64 } from 'nem2-sdk'; -import { EMPTY, forkJoin, Observable, of } from 'rxjs'; -import { expand, filter, mergeMap, reduce, shareReplay } from 'rxjs/operators'; +import { Account, AccountHttp, Address, AggregateTransaction, Deadline, InnerTransaction, Listener, LockFundsTransaction, Mosaic, NamespaceId, PublicAccount, QueryParams, SignedTransaction, Transaction, TransactionAnnounceResponse, TransactionHttp, TransactionInfo, TransactionStatus, TransactionType, TransferTransaction, UInt64 } from 'nem2-sdk'; +import { concat, EMPTY, from, Observable, of } from 'rxjs'; +import { expand, filter, finalize, map, mergeMap, reduce, shareReplay, switchMap, takeWhile } from 'rxjs/operators'; import { Errors } from '../types/Errors'; import { Initiator, initiatorAccountType } from './Initiator'; @@ -40,6 +40,7 @@ export class ApostilleHttp { } public announceList: SignedTransaction[] = []; + public return; private transactionHttp: TransactionHttp; private accountHttp: AccountHttp; @@ -320,7 +321,11 @@ export class ApostilleHttp { * @returns {Promise} * @memberof ApostilleAccount */ - public announceAll(): Observable<[SignedTransaction[], Observable]> { + public announceAll(): Observable< + SignedTransaction[] | + TransactionAnnounceResponse | + TransactionStatus[] + > { let innerTransactionsList: IAnnounceTransactionList[] = []; let readyTx; while (this.unannouncedTransactions.length > 0) { @@ -348,7 +353,7 @@ export class ApostilleHttp { const innerTransaction = refreshedTransaction.toAggregate(initiator.publicAccount); innerTransactionsList.push({initiator, innerTransaction}); } else { - this.announceList.concat(this.aggregateAndSign(innerTransactionsList)); + this.announceList = this.announceList.concat(this.aggregateAndSign(innerTransactionsList)); innerTransactionsList = []; // Clear buffer const aggregateBondedTransaction = initiator.sign(transaction); @@ -367,39 +372,55 @@ export class ApostilleHttp { } } - this.announceList.concat(this.aggregateAndSign(innerTransactionsList)); + this.announceList = this.announceList.concat(this.aggregateAndSign(innerTransactionsList)); innerTransactionsList = []; // Clear buffer - // Start listener - this.confirmedListener(); - // Announce all signed transactions and push to network - return forkJoin( + return concat( of(this.announceList), - this.announceList.map((signedTx) => { - return this.transactionHttp.announce(signedTx); - }), + from(this.announceList).pipe( + switchMap((signedTx) => { + return this.transactionHttp.announce(signedTx); + }), + ), + this.confirmedListener(), ); } - private confirmedListener(): void { - const newBlock$ = this.listener.newBlock().subscribe(() => { - if (this.announceList.length > 0) { - const transactionHashes = this.announceList.map((signedTx) => signedTx.hash); - this.transactionHttp.getTransactionsStatuses(transactionHashes).subscribe( - (txs) => { - for (const tx of txs) { - if (tx.group === 'confirmed') { - remove(this.announceList, (signedTx) => { - return signedTx.hash === tx.hash; - }); - } - } - }, - ); - } else { - newBlock$.unsubscribe(); - } - }); + private confirmedListener(): Observable { + return from( + this.listener.open(), + ).pipe( + switchMap(() => { + return this.listener.newBlock() + .pipe( + finalize(() => this.listener.close()), + takeWhile(() => this.announceList.length > 0), + switchMap( + () => { + const transactionHashes = this.announceList.map((signedTx) => signedTx.hash); + return this.transactionHttp.getTransactionsStatuses(transactionHashes); + }, + ), + map( + (txs) => { + for (const tx of txs) { + if (tx.group === 'confirmed') { + remove(this.announceList, (signedTx) => { + return signedTx.hash === tx.hash; + }); + } + if (tx.group === 'failed') { + remove(this.announceList, (signedTx) => { + return signedTx.hash === tx.hash; + }); + } + } + return txs; + }, + ), + ); + }), + ); } } diff --git a/src/model/apostille/ApostillePublicAccount.ts b/src/model/apostille/ApostillePublicAccount.ts index 3842ce1..c79a6ff 100644 --- a/src/model/apostille/ApostillePublicAccount.ts +++ b/src/model/apostille/ApostillePublicAccount.ts @@ -1,4 +1,4 @@ -import { Deadline, ModifyMultisigAccountTransaction, Mosaic, MultisigCosignatoryModification, MultisigCosignatoryModificationType, NetworkType, PlainMessage, PublicAccount, TransferTransaction } from 'nem2-sdk'; +import { Address, Deadline, ModifyMultisigAccountTransaction, MultisigCosignatoryModification, MultisigCosignatoryModificationType, NetworkType, PlainMessage, PublicAccount, TransferTransaction } from 'nem2-sdk'; export class ApostillePublicAccount { /** @@ -25,17 +25,16 @@ export class ApostillePublicAccount { /** * @description - returning a transfer transaction for the apostille account * @param {string} rawData - the raw data to send in the payload - * @param {(Mosaic[] | Mosaic[])} [mosaics=[]] - array of mosiacs to attach * @returns {TransferTransaction} * @memberof ApostillePublicAccount */ - public update(rawData: string, mosaics: Mosaic[] = []): TransferTransaction { + public update(rawData: string, destAddress?: Address): TransferTransaction { const plainMessage = PlainMessage.create(rawData); const creationTransaction = TransferTransaction.create( Deadline.create(), - this.publicAccount.address, - mosaics, + destAddress || this.publicAccount.address, + [], plainMessage, this.publicAccount.address.networkType, );