Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/infrastructure/BlockHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class BlockHttp extends Http implements BlockRepository {
* @returns Observable<BlockInfo>
*/
public getBlockByHeight(height: UInt64): Observable<BlockInfo> {
return this.call(this.blockRoutesApi.getBlockByHeight(height.toString()), (body) => BlockHttp.toBlockInfo(body));
return this.call(this.blockRoutesApi.getBlockByHeight(height.toString()), (body) => this.toBlockInfo(body));
}

/**
Expand Down Expand Up @@ -87,7 +87,7 @@ export class BlockHttp extends Http implements BlockRepository {
*/
public getBlocksByHeightWithLimit(height: UInt64, limit: number): Observable<BlockInfo[]> {
return this.call(this.blockRoutesApi.getBlocksByHeightWithLimit(height.toString(), limit), (body) =>
body.map((blockDTO) => BlockHttp.toBlockInfo(blockDTO)),
body.map((blockDTO) => this.toBlockInfo(blockDTO)),
);
}

Expand All @@ -98,12 +98,13 @@ export class BlockHttp extends Http implements BlockRepository {
* @param {BlockInfoDTO} dto the dto object from rest.
* @returns {BlockInfo} a BlockInfo model
*/
public static toBlockInfo(dto: BlockInfoDTO): BlockInfo {
private toBlockInfo(dto: BlockInfoDTO): BlockInfo {
const networkType = dto.block.network.valueOf();
return new BlockInfo(
dto.meta.hash,
dto.meta.generationHash,
UInt64.fromNumericString(dto.meta.totalFee),
dto.meta.stateHashSubCacheMerkleRoots,
dto.meta.numTransactions,
dto.block.signature,
PublicAccount.createFromPublicKey(dto.block.signerPublicKey, networkType),
Expand Down
10 changes: 7 additions & 3 deletions src/infrastructure/IListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@

import { Observable } from 'rxjs';
import { Address } from '../model/account/Address';
import { BlockInfo } from '../model/blockchain/BlockInfo';
import { AggregateTransaction } from '../model/transaction/AggregateTransaction';
import { CosignatureSignedTransaction } from '../model/transaction/CosignatureSignedTransaction';
import { Transaction } from '../model/transaction/Transaction';
import { TransactionStatusError } from '../model/transaction/TransactionStatusError';
import { NewBlock } from '../model/blockchain/NewBlock';

/**
* Listener service
*/
export interface IListener {
/**
* Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws)
*/
url: string;
/**
* Open web socket connection.
* @returns Promise<Void>
Expand All @@ -49,9 +53,9 @@ export interface IListener {
* Each time a new Block is added into the blockchain,
* it emits a new BlockInfo in the event stream.
*
* @return an observable stream of BlockInfo
* @return an observable stream of NewBlock
*/
newBlock(): Observable<BlockInfo>;
newBlock(): Observable<NewBlock>;

/**
* Returns an observable stream of Transaction for a specific address.
Expand Down
61 changes: 47 additions & 14 deletions src/infrastructure/Listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ import { Observable, of, OperatorFunction, Subject } from 'rxjs';
import { filter, flatMap, map, share } from 'rxjs/operators';
import * as WebSocket from 'ws';
import { Address } from '../model/account/Address';
import { BlockInfo } from '../model/blockchain/BlockInfo';
import { NamespaceName } from '../model/namespace/NamespaceName';
import { AggregateTransaction } from '../model/transaction/AggregateTransaction';
import { CosignatureSignedTransaction } from '../model/transaction/CosignatureSignedTransaction';
import { Deadline } from '../model/transaction/Deadline';
import { Transaction } from '../model/transaction/Transaction';
import { TransactionStatusError } from '../model/transaction/TransactionStatusError';
import { BlockHttp } from './BlockHttp';
import { IListener } from './IListener';
import { NamespaceRepository } from './NamespaceRepository';
import { CreateTransactionFromDTO } from './transaction/CreateTransactionFromDTO';
import { BlockInfoDTO } from 'symbol-openapi-typescript-node-client/dist/model/blockInfoDTO';
import { NewBlock } from '../model/blockchain/NewBlock';
import { PublicAccount } from '../model/account/PublicAccount';
import { UInt64 } from '../model/UInt64';

export enum ListenerChannelName {
block = 'block',
Expand All @@ -44,14 +46,13 @@ export enum ListenerChannelName {

interface ListenerMessage {
readonly channelName: ListenerChannelName;
readonly message: Transaction | string | BlockInfo | TransactionStatusError | CosignatureSignedTransaction;
readonly message: Transaction | string | NewBlock | TransactionStatusError | CosignatureSignedTransaction;
}

/**
* Listener service
*/
export class Listener implements IListener {
public readonly url: string;
/**
* @internal
* WebSocket connector
Expand All @@ -70,14 +71,15 @@ export class Listener implements IListener {

/**
* Constructor
* @param config - Listener configuration
* @param websocketInjected - (Optional) WebSocket injected when using listeners in client
* @param url - Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws).
* @param namespaceRepository - NamespaceRepository interface for resolving alias.
* @param websocketInjected - (Optional) WebSocket injected when using listeners in client.
*/
constructor(
/**
* Listener configuration.
* Listener websocket server url. default: rest-gateway's url with ''/ws'' suffix. (e.g. http://localhost:3000/ws)
*/
private config: string,
public readonly url: string,
/**
* Namespace repository for resolving account alias
*/
Expand All @@ -87,8 +89,7 @@ export class Listener implements IListener {
*/
private websocketInjected?: any,
) {
this.config = config.replace(/\/$/, '');
this.url = `${this.config}/ws`;
this.url = url.replace(/\/$/, '');
this.messageSubject = new Subject();
}

Expand Down Expand Up @@ -139,7 +140,7 @@ export class Listener implements IListener {
} else if (message.block) {
this.messageSubject.next({
channelName: ListenerChannelName.block,
message: BlockHttp.toBlockInfo(message),
message: this.toNewBlock(message),
});
} else if (message.code) {
this.messageSubject.next({
Expand Down Expand Up @@ -192,13 +193,13 @@ export class Listener implements IListener {
*
* @return an observable stream of BlockInfo
*/
public newBlock(): Observable<BlockInfo> {
public newBlock(): Observable<NewBlock> {
this.subscribeTo('block');
return this.messageSubject.asObservable().pipe(
share(),
filter((_) => _.channelName === ListenerChannelName.block),
filter((_) => _.message instanceof BlockInfo),
map((_) => _.message as BlockInfo),
filter((_) => _.message instanceof NewBlock),
map((_) => _.message as NewBlock),
);
}

Expand Down Expand Up @@ -406,4 +407,36 @@ export class Listener implements IListener {
};
this.webSocket.send(JSON.stringify(subscriptionMessage));
}

/**
* This method maps a BlockInfoDTO from rest to the SDK's BlockInfo model object.
*
* @internal
* @param {BlockInfoDTO} dto the dto object from rest.
* @returns {NewBlock} a BlockInfo model
*/
private toNewBlock(dto: BlockInfoDTO): NewBlock {
const networkType = dto.block.network.valueOf();
return new NewBlock(
dto.meta.hash,
dto.meta.generationHash,
dto.block.signature,
PublicAccount.createFromPublicKey(dto.block.signerPublicKey, networkType),
networkType,
dto.block.version,
dto.block.type,
UInt64.fromNumericString(dto.block.height),
UInt64.fromNumericString(dto.block.timestamp),
UInt64.fromNumericString(dto.block.difficulty),
dto.block.feeMultiplier,
dto.block.previousBlockHash,
dto.block.transactionsHash,
dto.block.receiptsHash,
dto.block.stateHash,
dto.block.proofGamma,
dto.block.proofScalar,
dto.block.proofVerificationHash,
dto.block.beneficiaryPublicKey ? PublicAccount.createFromPublicKey(dto.block.beneficiaryPublicKey, networkType) : undefined,
);
}
}
36 changes: 36 additions & 0 deletions src/infrastructure/RepositoryFactoryConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2020 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { NetworkType } from '../model/network/NetworkType';

export interface RepositoryFactoryConfig {
/**
* optional network type if you don't want to load it from the server.
*/
networkType?: NetworkType;
/**
* optional node generation hash if you don't want to load it from the server.
*/
generationHash?: string;
/**
* optional websocket url. If not provided, Default: Rest-Gateway url with ''/ws'' suffix (e.g. http://localhost:3000/ws).
*/
websocketUrl?: string;
/**
* optional injected websocket instance when using listeners in client.
*/
websocketInjected?: any;
}
20 changes: 13 additions & 7 deletions src/infrastructure/RepositoryFactoryHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { RestrictionMosaicHttp } from './RestrictionMosaicHttp';
import { RestrictionMosaicRepository } from './RestrictionMosaicRepository';
import { TransactionHttp } from './TransactionHttp';
import { TransactionRepository } from './TransactionRepository';
import { RepositoryFactoryConfig } from './RepositoryFactoryConfig';

/**
* Receipt http repository.
Expand All @@ -55,22 +56,27 @@ export class RepositoryFactoryHttp implements RepositoryFactory {
private readonly url: string;
private readonly networkType: Observable<NetworkType>;
private readonly generationHash: Observable<string>;
private readonly websocketUrl: string;
private readonly websocketInjected?: any;

/**
* Constructor
* @param url the server url.
* @param networkType optional network type if you don't want to load it from the server.
* @param generationHash optional node generation hash if you don't want to load it from the server.
* @param configs optional repository factory configs
*/
constructor(url: string, networkType?: NetworkType, generationHash?: string) {
constructor(url: string, configs?: RepositoryFactoryConfig) {
this.url = url;
this.networkType = networkType ? observableOf(networkType) : this.createNetworkRepository().getNetworkType().pipe(shareReplay(1));
this.generationHash = generationHash
? observableOf(generationHash)
this.networkType = configs?.networkType
? observableOf(configs.networkType)
: this.createNetworkRepository().getNetworkType().pipe(shareReplay(1));
this.generationHash = configs?.generationHash
? observableOf(configs?.generationHash)
: this.createNodeRepository()
.getNodeInfo()
.pipe(map((b) => b.networkGenerationHashSeed))
.pipe(shareReplay(1));
this.websocketUrl = configs?.websocketUrl ? configs?.websocketUrl : `${url.replace(/\/$/, '')}/ws`;
this.websocketInjected = configs?.websocketInjected;
}

createAccountRepository(): AccountRepository {
Expand Down Expand Up @@ -134,6 +140,6 @@ export class RepositoryFactoryHttp implements RepositoryFactory {
}

createListener(): IListener {
return new Listener(this.url, this.createNamespaceRepository());
return new Listener(this.websocketUrl, this.createNamespaceRepository(), this.websocketInjected);
}
}
5 changes: 5 additions & 0 deletions src/model/blockchain/BlockInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class BlockInfo {
* @param hash
* @param generationHash
* @param totalFee
* @param stateHashSubCacheMerkleRoots
* @param numTransactions
* @param signature
* @param signer
Expand Down Expand Up @@ -59,6 +60,10 @@ export class BlockInfo {
* The sum of all transaction fees included in the block.
*/
public readonly totalFee: UInt64,
/**
* State hash sub cache merkle roots
*/
public readonly stateHashSubCacheMerkleRoots: string[],
/**
* The number of transactions included.
*/
Expand Down
Loading