Skip to content

Commit 6c62de4

Browse files
committed
feat(connector-fabric): add WatchBlocks endpoint
- 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: #2118 Signed-off-by: Michal Bajer <[email protected]>
1 parent 53f9a7b commit 6c62de4

File tree

16 files changed

+1537
-47
lines changed

16 files changed

+1537
-47
lines changed

packages/cactus-core-api/src/main/typescript/plugin/ledger-connector/i-socket-api-client.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ export interface ISocketApiClient<BlockType> {
2121
baseConfig?: any,
2222
): Promise<any>;
2323

24-
watchBlocksV1?(
25-
monitorOptions?: Record<string, unknown>,
26-
): Observable<BlockType>;
24+
watchBlocksV1?(monitorOptions?: any): Observable<BlockType>;
2725

28-
watchBlocksAsyncV1?(
29-
monitorOptions?: Record<string, unknown>,
30-
): Promise<Observable<BlockType>>;
26+
watchBlocksAsyncV1?(monitorOptions?: any): Promise<Observable<BlockType>>;
3127
}

packages/cactus-plugin-ledger-connector-fabric/README.md

+60-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
- [1.4.1. Identity Providers](#141-identity-providers)
1111
- [1.4.2. Setting up a WS-X.509 provider](#142-setting-up-a-ws-x509-provider)
1212
- [1.4.3. Building the ws-identity docker image](#143-building-the-ws-identity-docker-image)
13+
- [1.5 Monitoring new blocks (WatchBlocks)](#15-monitoring-new-blocks-watchblocks)
14+
- [1.5.1 Example](#151-example)
15+
- [1.5.2 Listener Type](#152-listener-type)
1316
- [2. Architecture](#2-architecture)
1417
- [2.1. run-transaction-endpoint](#21-run-transaction-endpoint)
1518
- [3. Containerization](#3-containerization)
@@ -27,9 +30,10 @@
2730
- [6. License](#6-license)
2831
- [7. Acknowledgments](#7-acknowledgments)
2932

33+
3034
## 1. Usage
3135

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

8286
### 1.3. Using Via The API Client
8387

84-
**Prerequisites**
88+
**Prerequisites**
8589
- A running Fabric ledger (network)
8690
- 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)
8791

@@ -250,7 +254,7 @@ await connector.rotateKey(
250254
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
251255

252256
```typescript
253-
{
257+
{
254258
type: FabricSigningCredentialType;
255259
credentials: {
256260
certificate: string;
@@ -277,6 +281,58 @@ The following packages are used to access private keys (via web-socket) stored
277281

278282
TBD
279283

284+
### 1.5 Monitoring new blocks (WatchBlocks)
285+
- Use `ApiClient` to receive new blocks from a fabric ledger.
286+
- Type of the response can be configured.
287+
- Credentials must be configured using `gatewayOptions` argument (you can either send them directly in request or use wallet stored in keychain).
288+
289+
#### 1.5.1 Example
290+
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)
291+
292+
``` typescript
293+
// Setup
294+
const signingCredential = {
295+
keychainId: uuidv4(),
296+
keychainRef: "user2",
297+
};
298+
299+
// Create RxJS Observable.
300+
// This will connect to the fabric connector and start the monitoring operation.
301+
const watchObservable = apiClient.watchBlocksV1({
302+
channelName: "mychannel", // fabric channel name
303+
gatewayOptions: { // use signing credential from keychain
304+
identity: signingCredential.keychainRef,
305+
wallet: {
306+
keychain: signingCredential,
307+
},
308+
},
309+
WatchBlocksListenerTypeV1.Full, // return full block data
310+
});
311+
312+
// Subscribe to the observable to receive new blocks
313+
const subscription = watchObservable.subscribe({
314+
next(event) {
315+
// Handle new event
316+
},
317+
error(err) {
318+
// Handle error from connector
319+
},
320+
});
321+
```
322+
323+
#### 1.5.2 Listener Type
324+
There are two types of listener type - original and cactus ones.
325+
326+
##### Original
327+
Corresponds directly to `BlockType` from `fabric-common`:
328+
- `WatchBlocksListenerTypeV1.Filtered`,
329+
- `WatchBlocksListenerTypeV1.Full`,
330+
- `WatchBlocksListenerTypeV1.Private`,
331+
332+
##### Cactus (custom)
333+
Parses the data and returns custom formatted block.
334+
- `WatchBlocksListenerTypeV1.CactusTransactions`: Returns transactions summary. Compatible with legacy `fabric-socketio` monitoring operation.
335+
280336
## 2. Architecture
281337
The sequence diagrams for various endpoints are mentioned below
282338

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

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

295-
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.
351+
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.
296352

297353

298354

packages/cactus-plugin-ledger-connector-fabric/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@
6161
"bl": "5.0.0",
6262
"bn.js": "4.12.0",
6363
"express": "4.17.1",
64-
"fabric-ca-client": "2.3.0-snapshot.62",
65-
"fabric-common": "2.3.0-snapshot.63",
66-
"fabric-network": "2.3.0-snapshot.62",
67-
"fabric-protos": "2.3.0-snapshot.63",
64+
"fabric-ca-client": "2.5.0-snapshot.8",
65+
"fabric-common": "2.5.0-snapshot.8",
66+
"fabric-network": "2.5.0-snapshot.8",
67+
"fabric-protos": "2.5.0-snapshot.8",
6868
"fast-safe-stringify": "2.1.1",
6969
"form-data": "4.0.0",
7070
"http-status-codes": "2.1.4",
@@ -75,6 +75,7 @@
7575
"node-vault": "0.9.22",
7676
"openapi-types": "9.1.0",
7777
"prom-client": "13.2.0",
78+
"rxjs": "7.3.0",
7879
"sanitize-filename": "1.6.3",
7980
"sanitize-html": "2.7.0",
8081
"secp256k1": "4.0.3",

packages/cactus-plugin-ledger-connector-fabric/src/main/json/openapi.json

+204
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,210 @@
10851085
"PrometheusExporterMetricsResponse": {
10861086
"type": "string",
10871087
"nullable": false
1088+
},
1089+
"WatchBlocksV1": {
1090+
"type": "string",
1091+
"description": "Websocket requests for monitoring new blocks.",
1092+
"enum": [
1093+
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Subscribe",
1094+
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Next",
1095+
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Unsubscribe",
1096+
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Error",
1097+
"org.hyperledger.cactus.api.async.hlfabric.WatchBlocksV1.Complete"
1098+
],
1099+
"x-enum-varnames": [
1100+
"Subscribe",
1101+
"Next",
1102+
"Unsubscribe",
1103+
"Error",
1104+
"Complete"
1105+
]
1106+
},
1107+
"WatchBlocksListenerTypeV1": {
1108+
"type": "string",
1109+
"description": "Response type from WatchBlocks. 'Cactus*' are custom views, others correspond to fabric SDK call.",
1110+
"enum": [
1111+
"filtered",
1112+
"full",
1113+
"private",
1114+
"cactus:transactions"
1115+
],
1116+
"x-enum-varnames": [
1117+
"Filtered",
1118+
"Full",
1119+
"Private",
1120+
"CactusTransactions"
1121+
]
1122+
},
1123+
"WatchBlocksOptionsV1": {
1124+
"type": "object",
1125+
"description": "Options passed when subscribing to block monitoring.",
1126+
"required": [
1127+
"channelName",
1128+
"gatewayOptions",
1129+
"type"
1130+
],
1131+
"properties": {
1132+
"channelName": {
1133+
"type": "string",
1134+
"description": "Hyperledger Fabric channel to connect to.",
1135+
"minLength": 1,
1136+
"maxLength": 100,
1137+
"nullable": false
1138+
},
1139+
"gatewayOptions": {
1140+
"$ref": "#/components/schemas/GatewayOptions",
1141+
"description": "Options to Hyperledger Fabric Node SDK Gateway",
1142+
"nullable": false
1143+
},
1144+
"type": {
1145+
"$ref": "#/components/schemas/WatchBlocksListenerTypeV1",
1146+
"description": "Type of response block to return.",
1147+
"nullable": false
1148+
},
1149+
"startBlock": {
1150+
"type": "string",
1151+
"description": "From which block start monitoring. Defaults to latest.",
1152+
"minLength": 1,
1153+
"maxLength": 100,
1154+
"nullable": false
1155+
}
1156+
}
1157+
},
1158+
"WatchBlocksCactusTransactionsEventV1": {
1159+
"type": "object",
1160+
"description": "Transaction summary from commited block.",
1161+
"required": [
1162+
"chaincodeId",
1163+
"transactionId",
1164+
"functionName",
1165+
"functionArgs"
1166+
],
1167+
"properties": {
1168+
"chaincodeId": {
1169+
"description": "ChainCode containing function that was executed.",
1170+
"nullable": false,
1171+
"type": "string"
1172+
},
1173+
"transactionId": {
1174+
"description": "Transaction identifier.",
1175+
"nullable": false,
1176+
"type": "string"
1177+
},
1178+
"functionName": {
1179+
"description": "Function name that was executed.",
1180+
"nullable": false,
1181+
"type": "string"
1182+
},
1183+
"functionArgs": {
1184+
"description": "List of function arguments.",
1185+
"type": "array",
1186+
"items": {
1187+
"type": "string",
1188+
"minLength": 0,
1189+
"nullable": false
1190+
}
1191+
}
1192+
}
1193+
},
1194+
"WatchBlocksCactusTransactionsResponseV1": {
1195+
"type": "object",
1196+
"description": "Custom response containing block transactions summary. Compatible with legacy fabric-socketio connector monitoring.",
1197+
"required": [
1198+
"cactusTransactionsEvents"
1199+
],
1200+
"properties": {
1201+
"cactusTransactionsEvents": {
1202+
"description": "List of transactions summary",
1203+
"type": "array",
1204+
"items": {
1205+
"$ref": "#/components/schemas/WatchBlocksCactusTransactionsEventV1",
1206+
"nullable": false
1207+
}
1208+
}
1209+
}
1210+
},
1211+
"WatchBlocksFullResponseV1": {
1212+
"type": "object",
1213+
"description": "Response that corresponds to Fabric SDK 'full' EventType.",
1214+
"required": [
1215+
"fullBlock"
1216+
],
1217+
"properties": {
1218+
"fullBlock": {
1219+
"description": "Full commited block.",
1220+
"nullable": false
1221+
}
1222+
}
1223+
},
1224+
"WatchBlocksFilteredResponseV1": {
1225+
"type": "object",
1226+
"description": "Response that corresponds to Fabric SDK 'filtered' EventType.",
1227+
"required": [
1228+
"filteredBlock"
1229+
],
1230+
"properties": {
1231+
"filteredBlock": {
1232+
"description": "Filtered commited block.",
1233+
"nullable": false
1234+
}
1235+
}
1236+
},
1237+
"WatchBlocksPrivateResponseV1": {
1238+
"type": "object",
1239+
"description": "Response that corresponds to Fabric SDK 'private' EventType.",
1240+
"required": [
1241+
"privateBlock"
1242+
],
1243+
"properties": {
1244+
"privateBlock": {
1245+
"description": "Private commited block.",
1246+
"nullable": false
1247+
}
1248+
}
1249+
},
1250+
"WatchBlocksCactusErrorResponseV1": {
1251+
"type": "object",
1252+
"description": "Error response from WatchBlocks operation.",
1253+
"required": [
1254+
"code",
1255+
"errorMessage"
1256+
],
1257+
"properties": {
1258+
"code": {
1259+
"description": "Error code.",
1260+
"type": "number"
1261+
},
1262+
"errorMessage": {
1263+
"description": "Description of the error.",
1264+
"type": "string"
1265+
}
1266+
}
1267+
},
1268+
"WatchBlocksResponseV1": {
1269+
"description": "Response block from WatchBlocks endpoint. Depends on 'type' passed in subscription options.",
1270+
"oneOf": [
1271+
{
1272+
"$ref": "#/components/schemas/WatchBlocksCactusTransactionsResponseV1",
1273+
"nullable": false
1274+
},
1275+
{
1276+
"$ref": "#/components/schemas/WatchBlocksFullResponseV1",
1277+
"nullable": false
1278+
},
1279+
{
1280+
"$ref": "#/components/schemas/WatchBlocksFilteredResponseV1",
1281+
"nullable": false
1282+
},
1283+
{
1284+
"$ref": "#/components/schemas/WatchBlocksPrivateResponseV1",
1285+
"nullable": false
1286+
},
1287+
{
1288+
"$ref": "#/components/schemas/WatchBlocksCactusErrorResponseV1",
1289+
"nullable": false
1290+
}
1291+
]
10881292
}
10891293
}
10901294
},

0 commit comments

Comments
 (0)