Skip to content

Commit

Permalink
feat(connector-fabric): add WatchBlocks endpoint
Browse files Browse the repository at this point in the history
- Add new endpoint for monitoring new blocks. Response type is determined from input argument.
  Monitor endpoints are stored in connector.runningWatchBlocksMonitors.
- Add `FabricApiClient` which supports new socketio endpoint.
- Add functional test to check the new endpoint.
- Upgrade fabric-sdk-node to solve test hand issue.
- Loose up `ISocketApiClient` to allow interface monitor options.
- Update readme with WatchBlocks usage.

Regarding fabric-sdk-node upgrade: with sdk2.3, the test used to hang occasionally.
I've ensured that all resources are being cleaned-up, and investigated the problem. The issue was
infinite recurse dns resolution (of fabric peer address) in grpc-js `ResolvingLoadBalancer`.
Could not find exact cause of this strange behaviour, the code there is pretty complex
and didn't want to dig to deep, but I've noticed that recently there were some fixes
in the functions I suspected so I've updated the fabric-sdk, which uses newer grpc-js,
and the problem went away, so I assume this was in fact bug in gprc-js. Run this test on repeat for
hours and didn't notice any error.

Closes: hyperledger#2118

Signed-off-by: Michal Bajer <michal.bajer@fujitsu.com>
  • Loading branch information
outSH committed Dec 15, 2022
1 parent 53f9a7b commit 1f7b676
Show file tree
Hide file tree
Showing 16 changed files with 1,509 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ export interface ISocketApiClient<BlockType> {
baseConfig?: any,
): Promise<any>;

watchBlocksV1?(
monitorOptions?: Record<string, unknown>,
): Observable<BlockType>;
watchBlocksV1?(monitorOptions?: any): Observable<BlockType>;

watchBlocksAsyncV1?(
monitorOptions?: Record<string, unknown>,
): Promise<Observable<BlockType>>;
watchBlocksAsyncV1?(monitorOptions?: any): Promise<Observable<BlockType>>;
}
64 changes: 60 additions & 4 deletions packages/cactus-plugin-ledger-connector-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
- [1.4.1. Identity Providers](#141-identity-providers)
- [1.4.2. Setting up a WS-X.509 provider](#142-setting-up-a-ws-x509-provider)
- [1.4.3. Building the ws-identity docker image](#143-building-the-ws-identity-docker-image)
- [1.5 Monitoring new blocks (WatchBlocks)](#15-monitoring-new-blocks-watchblocks)
- [1.5.1 Example](#151-example)
- [1.5.2 Listener Type](#152-listener-type)
- [2. Architecture](#2-architecture)
- [2.1. run-transaction-endpoint](#21-run-transaction-endpoint)
- [3. Containerization](#3-containerization)
Expand All @@ -27,9 +30,10 @@
- [6. License](#6-license)
- [7. Acknowledgments](#7-acknowledgments)


## 1. Usage

This plugin provides a way to interact with Fabric networks.
This plugin provides a way to interact with Fabric networks.
Using this one can perform:
* Deploy smart contracts (chaincode).
* Execute transactions on the ledger.
Expand Down Expand Up @@ -81,7 +85,7 @@ try {

### 1.3. Using Via The API Client

**Prerequisites**
**Prerequisites**
- A running Fabric ledger (network)
- You have a running Cactus API server on `$HOST:$PORT` with the Fabric connector plugin installed on it (and the latter configured to have access to the Fabric ledger from point 1)

Expand Down Expand Up @@ -250,7 +254,7 @@ await connector.rotateKey(
Identity providers allows client to manage their private more effectively and securely. Cactus Fabric Connector support multiple type of providers. Each provider differ based upon where the private are stored. On High level certificate credential are stored as

```typescript
{
{
type: FabricSigningCredentialType;
credentials: {
certificate: string;
Expand All @@ -277,6 +281,58 @@ The following packages are used to access private keys (via web-socket) stored

TBD

### 1.5 Monitoring new blocks (WatchBlocks)
- Use `ApiClient` to receive new blocks from a fabric ledger.
- Type of the response can be configured.
- Credentials must be configured using `gatewayOptions` argument (you can either send them directly in request or use wallet stored in keychain).

#### 1.5.1 Example
For more detailed example check [fabric-watch-blocks-v1-endpoint.test.ts](./src/test/typescript/integration/fabric-v2-2-x/fabric-watch-blocks-v1-endpoint.test.ts)

``` typescript
// Setup
const signingCredential = {
keychainId: uuidv4(),
keychainRef: "user2",
};

// Create RxJS Observable.
// This will connect to the fabric connector and start the monitoring operation.
const watchObservable = apiClient.watchBlocksV1({
channelName: "mychannel", // fabric channel name
gatewayOptions: { // use signing credential from keychain
identity: signingCredential.keychainRef,
wallet: {
keychain: signingCredential,
},
},
WatchBlocksListenerTypeV1.Full, // return full block data
});

// Subscribe to the observable to receive new blocks
const subscription = watchObservable.subscribe({
next(event) {
// Handle new event
},
error(err) {
// Handle error from connector
},
});
```

#### 1.5.2 Listener Type
There are two types of listener type - original and cactus ones.

##### Original
Corresponds directly to `BlockType` from `fabric-common`:
- `WatchBlocksListenerTypeV1.Filtered`,
- `WatchBlocksListenerTypeV1.Full`,
- `WatchBlocksListenerTypeV1.Private`,

##### Cactus (custom)
Parses the data and returns custom formatted block.
- `WatchBlocksListenerTypeV1.CactusTransactions`: Returns transactions summary. Compatible with legacy `fabric-socketio` monitoring operation.

## 2. Architecture
The sequence diagrams for various endpoints are mentioned below

Expand All @@ -292,7 +348,7 @@ The above diagram shows the sequence diagram of transact() method of the PluginL

![run-transaction-endpoint-enroll](docs/architecture/images/run-transaction-endpoint-enroll.png)

The above diagram shows the sequence diagram of enroll() method of the PluginLedgerConnectorFabric class. The caller to this function, which in reference to the above sequence diagram is API server, sends Signer object along with EnrollmentRequest as an argument to the enroll() method. Based on the singerType (FabricSigningCredentialType.X509, FabricSigningCredentialType.VaultX509, FabricSigningCredentialType.WsX509), corresponding identity is enrolled and stored inside keychain.
The above diagram shows the sequence diagram of enroll() method of the PluginLedgerConnectorFabric class. The caller to this function, which in reference to the above sequence diagram is API server, sends Signer object along with EnrollmentRequest as an argument to the enroll() method. Based on the singerType (FabricSigningCredentialType.X509, FabricSigningCredentialType.VaultX509, FabricSigningCredentialType.WsX509), corresponding identity is enrolled and stored inside keychain.



Expand Down
9 changes: 5 additions & 4 deletions packages/cactus-plugin-ledger-connector-fabric/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@
"bl": "5.0.0",
"bn.js": "4.12.0",
"express": "4.17.1",
"fabric-ca-client": "2.3.0-snapshot.62",
"fabric-common": "2.3.0-snapshot.63",
"fabric-network": "2.3.0-snapshot.62",
"fabric-protos": "2.3.0-snapshot.63",
"fabric-ca-client": "2.5.0-snapshot.8",
"fabric-common": "2.5.0-snapshot.8",
"fabric-network": "2.5.0-snapshot.8",
"fabric-protos": "2.5.0-snapshot.8",
"fast-safe-stringify": "2.1.1",
"form-data": "4.0.0",
"http-status-codes": "2.1.4",
Expand All @@ -75,6 +75,7 @@
"node-vault": "0.9.22",
"openapi-types": "9.1.0",
"prom-client": "13.2.0",
"rxjs": "7.3.0",
"sanitize-filename": "1.6.3",
"sanitize-html": "2.7.0",
"secp256k1": "4.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,210 @@
"PrometheusExporterMetricsResponse": {
"type": "string",
"nullable": false
},
"WatchBlocksV1": {
"type": "string",
"description": "Websocket requests for monitoring new blocks.",
"enum": [
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Subscribe",
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Next",
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Unsubscribe",
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Error",
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Complete"
],
"x-enum-varnames": [
"Subscribe",
"Next",
"Unsubscribe",
"Error",
"Complete"
]
},
"WatchBlocksListenerTypeV1": {
"type": "string",
"description": "Response type from WatchBlocks. 'Cactus*' are custom views, others correspond to fabric SDK call.",
"enum": [
"filtered",
"full",
"private",
"cactus:transactions"
],
"x-enum-varnames": [
"Filtered",
"Full",
"Private",
"CactusTransactions"
]
},
"WatchBlocksOptionsV1": {
"type": "object",
"description": "Options passed when subscribing to block monitoring.",
"required": [
"channelName",
"gatewayOptions",
"type"
],
"properties": {
"channelName": {
"type": "string",
"description": "Hyperledger Fabric channel to connect to.",
"minLength": 1,
"maxLength": 100,
"nullable": false
},
"gatewayOptions": {
"$ref": "#/components/schemas/GatewayOptions",
"description": "Options to Hyperledger Fabric Node SDK Gateway",
"nullable": false
},
"type": {
"$ref": "#/components/schemas/WatchBlocksListenerTypeV1",
"description": "Type of response block to return.",
"nullable": false
},
"startBlock": {
"type": "string",
"description": "From which block start monitoring. Defaults to latest.",
"minLength": 1,
"maxLength": 100,
"nullable": false
}
}
},
"WatchBlocksCactusTransactionsEventV1": {
"type": "object",
"description": "Transaction summary from commited block.",
"required": [
"chaincodeId",
"transactionId",
"functionName",
"functionArgs"
],
"properties": {
"chaincodeId": {
"description": "ChainCode containing function that was executed.",
"nullable": false,
"type": "string"
},
"transactionId": {
"description": "Transaction identifier.",
"nullable": false,
"type": "string"
},
"functionName": {
"description": "Function name that was executed.",
"nullable": false,
"type": "string"
},
"functionArgs": {
"description": "List of function arguments.",
"type": "array",
"items": {
"type": "string",
"minLength": 0,
"nullable": false
}
}
}
},
"WatchBlocksCactusTransactionsResponseV1": {
"type": "object",
"description": "Custom response containing block transactions summary. Compatible with legacy fabric-socketio connector monitoring.",
"required": [
"cactusTransactionsEvents"
],
"properties": {
"cactusTransactionsEvents": {
"description": "List of transactions summary",
"type": "array",
"items": {
"$ref": "#/components/schemas/WatchBlocksCactusTransactionsEventV1",
"nullable": false
}
}
}
},
"WatchBlocksFullResponseV1": {
"type": "object",
"description": "Response that corresponds to Fabric SDK 'full' EventType.",
"required": [
"fullBlock"
],
"properties": {
"fullBlock": {
"description": "Full commited block.",
"nullable": false
}
}
},
"WatchBlocksFilteredResponseV1": {
"type": "object",
"description": "Response that corresponds to Fabric SDK 'filtered' EventType.",
"required": [
"filteredBlock"
],
"properties": {
"filteredBlock": {
"description": "Filtered commited block.",
"nullable": false
}
}
},
"WatchBlocksPrivateResponseV1": {
"type": "object",
"description": "Response that corresponds to Fabric SDK 'private' EventType.",
"required": [
"privateBlock"
],
"properties": {
"privateBlock": {
"description": "Private commited block.",
"nullable": false
}
}
},
"WatchBlocksCactusErrorResponseV1": {
"type": "object",
"description": "Error response from WatchBlocks operation.",
"required": [
"code",
"errorMessage"
],
"properties": {
"code": {
"description": "Error code.",
"type": "number"
},
"errorMessage": {
"description": "Description of the error.",
"type": "string"
}
}
},
"WatchBlocksResponseV1": {
"description": "Response block from WatchBlocks endpoint. Depends on 'type' passed in subscription options.",
"oneOf": [
{
"$ref": "#/components/schemas/WatchBlocksCactusTransactionsResponseV1",
"nullable": false
},
{
"$ref": "#/components/schemas/WatchBlocksFullResponseV1",
"nullable": false
},
{
"$ref": "#/components/schemas/WatchBlocksFilteredResponseV1",
"nullable": false
},
{
"$ref": "#/components/schemas/WatchBlocksPrivateResponseV1",
"nullable": false
},
{
"$ref": "#/components/schemas/WatchBlocksCactusErrorResponseV1",
"nullable": false
}
]
}
}
},
Expand Down
Loading

0 comments on commit 1f7b676

Please sign in to comment.