From 1e94dc62398bcc017272dfecba6965f08d25f9a1 Mon Sep 17 00:00:00 2001
From: "Gomez Goiri, Aitor" <aitor.gomez@tecnalia.com>
Date: Fri, 1 Apr 2022 15:20:30 +0200
Subject: [PATCH] Allowing to define a custom affiliation attribute and adding
 new SDK client

The SDK client differs in creating an actual "hf.Affiliation" too  (with the deployment restrictions specified).
---
 scripts/README.md         |   23 +-
 scripts/create-users.js   |   97 ++-
 scripts/package-lock.json | 1436 ++++++++++++++++++++++++++++++++++++-
 scripts/package.json      |    9 +-
 scripts/sample.env        |    5 +-
 scripts/sdk.js            |  260 +++++++
 6 files changed, 1792 insertions(+), 38 deletions(-)
 create mode 100644 scripts/sdk.js

diff --git a/scripts/README.md b/scripts/README.md
index 9dec428..13f0763 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -1,12 +1,27 @@
 # Create sample users for organizations
 
+## Configuration
+
+Edit admin users and users to be created in _users.json_.
+You can provide connection details and admin passwords in _.env_ (you can use _sample.env_ as a template).
+
+### Create/update users with the API
+
 The script _config_users.js_ will take care of creating the necessary users in all the organizations.
 
 ```bash
-node config_users.js
+npm run users
+# or node config-users.js api
 ```
 
-## Configuration
+### Create/update users with the SDK
 
-Edit admin users and users to be created in _users.json_.
-You can provide connection details and admin passwords in _.env_ (you can use _sample.env_ as a template).
+We must use this version to create users with a customized affiliation.
+
+```bash
+npm run users-sdk
+# or node config-users.js sdk
+```
+
+**Note**: By default Fabric only defines [3 affiliations](https://hyperledger-fabric-ca.readthedocs.io/en/release-1.4/deployguide/ca-config.html#affiliations) and admins belong to _org1.department1_ (which means that they can only create subaffiliations).
+Since I didn't learn how to modify them in Minifab, new affiliations are prefixed by this prefix making it ugly and confusing :(
diff --git a/scripts/create-users.js b/scripts/create-users.js
index c3066f9..f5bbafb 100644
--- a/scripts/create-users.js
+++ b/scripts/create-users.js
@@ -3,6 +3,7 @@
 require("dotenv").config()
 
 const ApiClient = require("@traceblock/api-client")
+const SDKHelper = require("./sdk")
 
 const usersByOrg = require("./users")
 
@@ -11,18 +12,26 @@ const readPassword = (user, org) =>
   "secret"
 
 const modifyUser = async (
-  apiClient,
+  client,
   username,
   password,
   organization,
-  role
+  role,
+  email,
+  affiliation
 ) => {
-  const oldCert = await apiClient.getUser(username)
-  const modUser = await apiClient.modifyUser(
+  const oldCert = await client.getUser(username, organization)
+  oldCert.attrs = [
+    ...oldCert.attrs,
+    { name: "affiliation", value: affiliation, ecert: true }
+  ]
+
+  const modUser = await client.modifyUser(
     username,
     password,
     organization,
     role,
+    email,
     oldCert
   )
 
@@ -34,50 +43,107 @@ const modifyUser = async (
 }
 
 const createOrModifyUserIfNeeded = async (
-  apiClient,
+  client,
   username,
   password,
   organization,
-  role
+  role,
+  affiliation
 ) => {
+  const email = `${username}@${organization}`
+
   try {
     // Await is needed here to force the catch clausule if something goes wrong
-    return await apiClient.createUser(
+    return await client.createUser(
       username,
       password,
       organization,
       role,
-      `${username}@${organization}`
+      email,
+      "en",
+      [{ name: "affiliation", value: affiliation, ecert: true }]
     )
   } catch (err) {
-    return modifyUser(apiClient, username, password, organization, role)
+    return modifyUser(
+      client,
+      username,
+      password,
+      organization,
+      role,
+      email,
+      affiliation
+    )
   }
 }
 
-const createUsers = async () => {
-  const apiClient = new ApiClient(
+const printAffiliations = async (sdkClient, organization) => {
+  console.log(`\nAffiliations for ${organization}`)
+
+  const existingAffiliations = await sdkClient.getAffiliations(organization)
+  existingAffiliations.forEach(({ name, affiliations: affs = [] }) =>
+    console.log(`\t${name}: [ ${affs.map(a => a.name).join(", ")} ] `)
+  )
+}
+
+const createAffiliations = async (
+  sdkClient,
+  organization,
+  affiliations = []
+) => {
+  if (affiliations.length > 0) {
+    for (let affiliation of affiliations) {
+      try {
+        await sdkClient.addAffiliation(organization, affiliation)
+      } catch (err) {
+        err.errors.forEach(({ message }) => console.error("Error:", message))
+      }
+    }
+
+    await printAffiliations(sdkClient, organization)
+  }
+}
+
+const createUsers = async (useSdk = false) => {
+  let client = new ApiClient(
     process.env.TRACEBLOCK_API,
     process.env.TRACEBLOCK_NETWORK,
     process.env.TRACEBLOCK_CHANNEL,
     process.env.TRACEBLOCK_CHAINCODE
   )
 
+  if (useSdk) {
+    // CA nodes must be accesible.
+    // If this script is not run from a container inside the network, expose the ports.
+    // Append "--expose-endpoints true" to deployment/network/up.sh
+    client = new SDKHelper(process.env.CONNECTION_CONFIG)
+  }
+
   for (let el of Object.entries(usersByOrg)) {
     const [org, values] = el
     const admin = values.find(v => v.role === "admin")
 
-    await apiClient.signIn(admin.user, readPassword(admin.user, org), org)
+    await client.signIn(admin.user, readPassword(admin.user, org), org)
+
+    if (useSdk) {
+      // The API does not include an option to create new affiliations
+      await createAffiliations(
+        client,
+        org,
+        values.filter(u => !!u.affiliation).map(u => u.affiliation)
+      )
+    }
 
     const prom = values
       //.filter(v => v.role !== "admin")
       // Admin roles also need to be configured
       .map(u =>
         createOrModifyUserIfNeeded(
-          apiClient,
+          client,
           u.user,
           readPassword(u.user, org),
           org,
-          u.role
+          u.role,
+          u.affiliation
         )
       )
 
@@ -87,4 +153,5 @@ const createUsers = async () => {
   console.log("Users created")
 }
 
-createUsers()
+const [clientType = ""] = process.argv.slice(2)
+createUsers(clientType.toLowerCase() === "sdk")
diff --git a/scripts/package-lock.json b/scripts/package-lock.json
index af4535f..743a85d 100644
--- a/scripts/package-lock.json
+++ b/scripts/package-lock.json
@@ -8,24 +8,241 @@
       "name": "hypercog-config-scripts",
       "version": "0.0.1",
       "dependencies": {
-        "@traceblock/api-client": "^0.2.9",
-        "dotenv": "^10.0.0"
+        "@traceblock/api-client": "^0.2.10",
+        "dotenv": "^16.0.0",
+        "fabric-ca-client": "^2.2.11",
+        "fabric-network": "^2.2.11"
       }
     },
+    "node_modules/@grpc/grpc-js": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.10.tgz",
+      "integrity": "sha512-++oAubX/7rJzlqH0ShyzDENNNDHYrlttdc3NM40KlaVQDcgGqQknuPoavmyTC+oNUDyxPCX5dHceKhfcgN3tiw==",
+      "dependencies": {
+        "@grpc/proto-loader": "^0.6.4",
+        "@types/node": ">=12.12.47"
+      },
+      "engines": {
+        "node": "^8.13.0 || >=10.10.0"
+      }
+    },
+    "node_modules/@grpc/proto-loader": {
+      "version": "0.6.9",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz",
+      "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==",
+      "dependencies": {
+        "@types/long": "^4.0.1",
+        "lodash.camelcase": "^4.3.0",
+        "long": "^4.0.0",
+        "protobufjs": "^6.10.0",
+        "yargs": "^16.2.0"
+      },
+      "bin": {
+        "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+    },
+    "node_modules/@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "node_modules/@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "node_modules/@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+    },
+    "node_modules/@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "node_modules/@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+    },
+    "node_modules/@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+    },
+    "node_modules/@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+    },
+    "node_modules/@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+    },
+    "node_modules/@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+    },
     "node_modules/@traceblock/api-client": {
-      "version": "0.2.9",
-      "resolved": "https://artifact.tecnalia.com:443/artifactory/api/npm/traceblock-npm-release/@traceblock/api-client/-/api-client-0.2.9.tgz",
-      "integrity": "sha1-AhqCJayB7TK8tJ6LzvGzJszp1E8=",
+      "version": "0.2.10",
+      "resolved": "https://artifact.tecnalia.com:443/artifactory/api/npm/traceblock-npm-release/@traceblock/api-client/-/api-client-0.2.10.tgz",
+      "integrity": "sha1-bSsmHfpspbqofKPI1ON1Mi9pROU=",
       "dependencies": {
         "cross-fetch": "^3.1.4",
         "jsonwebtoken": "^8.5.1"
       }
     },
+    "node_modules/@types/long": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+    },
+    "node_modules/@types/node": {
+      "version": "17.0.23",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+      "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
+    },
+    "node_modules/@types/tough-cookie": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+      "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/async": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+    },
+    "node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/axios-cookiejar-support": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-1.0.1.tgz",
+      "integrity": "sha512-IZJxnAJ99XxiLqNeMOqrPbfR7fRyIfaoSLdPUf4AMQEGkH8URs0ghJK/xtqBsD+KsSr3pKl4DEQjCn834pHMig==",
+      "dependencies": {
+        "is-redirect": "^1.0.0",
+        "pify": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "peerDependencies": {
+        "@types/tough-cookie": ">=2.3.3",
+        "axios": ">=0.16.2",
+        "tough-cookie": ">=2.3.3"
+      }
+    },
+    "node_modules/bn.js": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+      "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+    },
+    "node_modules/brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+    },
     "node_modules/buffer-equal-constant-time": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
       "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
     },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "node_modules/colors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+      "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
     "node_modules/cross-fetch": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
@@ -34,12 +251,20 @@
         "node-fetch": "2.6.1"
       }
     },
+    "node_modules/cycle": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+      "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/dotenv": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
-      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "version": "16.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
+      "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==",
       "engines": {
-        "node": ">=10"
+        "node": ">=12"
       }
     },
     "node_modules/ecdsa-sig-formatter": {
@@ -50,6 +275,235 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "node_modules/elliptic": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+      "dependencies": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eyes": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+      "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
+      "engines": {
+        "node": "> 0.1.90"
+      }
+    },
+    "node_modules/fabric-ca-client": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-ca-client/-/fabric-ca-client-2.2.11.tgz",
+      "integrity": "sha512-Tl6ktoYw5ouvz256E/blz+0mWoA6r9L8Pc1BbRffi6ROxuSewdaJEffQjLGAcLG+gJpH2aeviXljWNASqvPIUQ==",
+      "dependencies": {
+        "fabric-common": "2.2.11",
+        "jsrsasign": "^10.4.1",
+        "url": "^0.11.0",
+        "winston": "^2.4.5"
+      },
+      "engines": {
+        "node": "^10.15.3 || ^12.13.1 || ^14.13.1",
+        "npm": "^6.4.1"
+      }
+    },
+    "node_modules/fabric-common": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-common/-/fabric-common-2.2.11.tgz",
+      "integrity": "sha512-VDZUXSoTkgXA4axOONCF93DkFzDaqN0wfKnUTa36Y1Dkk5iSZFdH5iqCroAmerrKg71yGlkiBN7DsD7QWl9McA==",
+      "dependencies": {
+        "callsite": "^1.0.0",
+        "elliptic": "^6.5.4",
+        "fabric-protos": "2.2.11",
+        "js-sha3": "^0.8.0",
+        "jsrsasign": "^10.4.1",
+        "long": "^4.0.0",
+        "nconf": "^0.11.2",
+        "promise-settle": "^0.3.0",
+        "sjcl": "^1.0.8",
+        "winston": "^2.4.5",
+        "yn": "^4.0.0"
+      },
+      "engines": {
+        "node": "^10.15.3 || ^12.13.1 || ^14.13.1",
+        "npm": "^6.4.1"
+      },
+      "optionalDependencies": {
+        "pkcs11js": "^1.0.6"
+      }
+    },
+    "node_modules/fabric-network": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-network/-/fabric-network-2.2.11.tgz",
+      "integrity": "sha512-YikiBMK0O9Ve+5vZkEOkiz/eXCBzJFh0Ik9MigpjRam86E64NOpNzjMZbCfeas91+O5HPJrO5+zfjEHw7vWsIg==",
+      "dependencies": {
+        "fabric-common": "2.2.11",
+        "fabric-protos": "2.2.11",
+        "long": "^4.0.0",
+        "nano": "^9.0.3"
+      },
+      "engines": {
+        "node": "^10.15.3 || ^12.13.1 || ^14.13.1",
+        "npm": "^6.4.1"
+      }
+    },
+    "node_modules/fabric-protos": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-protos/-/fabric-protos-2.2.11.tgz",
+      "integrity": "sha512-haWUubqMLccIyBdmhx4ueKCe7rJgr/zln7iDF+BM7Im48K/a76VmWdddm+QGgWI0Bbk1RA1KfLOdwdb/gSGiSg==",
+      "dependencies": {
+        "@grpc/grpc-js": "^1.3.4",
+        "@grpc/proto-loader": "^0.6.2",
+        "protobufjs": "^6.11.2"
+      },
+      "engines": {
+        "node": "^10.15.3 || ^12.13.1 || ^14.13.1",
+        "npm": "^6.4.1"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.14.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
+      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "node_modules/hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "dependencies": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/ini": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+      "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-redirect": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+      "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+    },
+    "node_modules/js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+    },
     "node_modules/jsonwebtoken": {
       "version": "8.5.1",
       "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
@@ -71,6 +525,14 @@
         "npm": ">=1.4.28"
       }
     },
+    "node_modules/jsrsasign": {
+      "version": "10.5.14",
+      "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.5.14.tgz",
+      "integrity": "sha512-W49L90gR5E8lOMfZJzhq/JHM2Am6slJWsaI/aiyDZurK1CQly9/FN9ZCvM5ez8Svbw5+CwVEH25iIARLoh4Viw==",
+      "funding": {
+        "url": "https://github.com/kjur/jsrsasign#donations"
+      }
+    },
     "node_modules/jwa": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
@@ -90,6 +552,11 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+    },
     "node_modules/lodash.includes": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -125,11 +592,61 @@
       "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
       "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
     },
+    "node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+    },
+    "node_modules/minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
+    "node_modules/nan": {
+      "version": "2.15.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+      "optional": true
+    },
+    "node_modules/nano": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/nano/-/nano-9.0.5.tgz",
+      "integrity": "sha512-fEAhwAdXh4hDDnC8cYJtW6D8ivOmpvFAqT90+zEuQREpRkzA/mJPcI4EKv15JUdajaqiLTXNoKK6PaRF+/06DQ==",
+      "dependencies": {
+        "@types/tough-cookie": "^4.0.0",
+        "axios": "^0.21.1",
+        "axios-cookiejar-support": "^1.0.1",
+        "qs": "^6.9.4",
+        "tough-cookie": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/nconf": {
+      "version": "0.11.3",
+      "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.11.3.tgz",
+      "integrity": "sha512-iYsAuDS9pzjVMGIzJrGE0Vk3Eh8r/suJanRAnWGBd29rVS2XtSgzcAo5l6asV3e4hH2idVONHirg1efoBOslBg==",
+      "dependencies": {
+        "async": "^1.4.0",
+        "ini": "^2.0.0",
+        "secure-keys": "^1.0.0",
+        "yargs": "^16.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/node-fetch": {
       "version": "2.6.1",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
@@ -138,6 +655,116 @@
         "node": "4.x || >=6.0.0"
       }
     },
+    "node_modules/object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/pify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
+      "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/pkcs11js": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/pkcs11js/-/pkcs11js-1.3.0.tgz",
+      "integrity": "sha512-owI+M6Gpw0cEU47cTt2eWQs4Iqm9zRyobiJ0q37wIgOrK8BcXVuRM3eVGH58QxYWhItMcRiEBUTE8HUHZX+beQ==",
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "nan": "^2.15.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/PeculiarVentures"
+      }
+    },
+    "node_modules/promise-settle": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/promise-settle/-/promise-settle-0.3.0.tgz",
+      "integrity": "sha1-tO/VcqHrdM95T4KM00naQKCOTpY=",
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/protobufjs": {
+      "version": "6.11.2",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+      "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+    },
+    "node_modules/punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.10.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+      "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+      "dependencies": {
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+      "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+      "engines": {
+        "node": ">=0.4.x"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -157,6 +784,11 @@
         }
       ]
     },
+    "node_modules/secure-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz",
+      "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o="
+    },
     "node_modules/semver": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -164,23 +796,366 @@
       "bin": {
         "semver": "bin/semver"
       }
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/sjcl": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz",
+      "integrity": "sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/tough-cookie": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+      "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+      "dependencies": {
+        "psl": "^1.1.33",
+        "punycode": "^2.1.1",
+        "universalify": "^0.1.2"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "dependencies": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      }
+    },
+    "node_modules/url/node_modules/punycode": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+      "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+    },
+    "node_modules/winston": {
+      "version": "2.4.5",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz",
+      "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==",
+      "dependencies": {
+        "async": "~1.0.0",
+        "colors": "1.0.x",
+        "cycle": "1.0.x",
+        "eyes": "0.1.x",
+        "isstream": "0.1.x",
+        "stack-trace": "0.0.x"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/winston/node_modules/async": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
+      "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz",
+      "integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg==",
+      "engines": {
+        "node": ">=10"
+      }
     }
   },
   "dependencies": {
+    "@grpc/grpc-js": {
+      "version": "1.5.10",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.5.10.tgz",
+      "integrity": "sha512-++oAubX/7rJzlqH0ShyzDENNNDHYrlttdc3NM40KlaVQDcgGqQknuPoavmyTC+oNUDyxPCX5dHceKhfcgN3tiw==",
+      "requires": {
+        "@grpc/proto-loader": "^0.6.4",
+        "@types/node": ">=12.12.47"
+      }
+    },
+    "@grpc/proto-loader": {
+      "version": "0.6.9",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz",
+      "integrity": "sha512-UlcCS8VbsU9d3XTXGiEVFonN7hXk+oMXZtoHHG2oSA1/GcDP1q6OUgs20PzHDGizzyi8ufGSUDlk3O2NyY7leg==",
+      "requires": {
+        "@types/long": "^4.0.1",
+        "lodash.camelcase": "^4.3.0",
+        "long": "^4.0.0",
+        "protobufjs": "^6.10.0",
+        "yargs": "^16.2.0"
+      }
+    },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+    },
     "@traceblock/api-client": {
-      "version": "0.2.9",
-      "resolved": "https://artifact.tecnalia.com:443/artifactory/api/npm/traceblock-npm-release/@traceblock/api-client/-/api-client-0.2.9.tgz",
-      "integrity": "sha1-AhqCJayB7TK8tJ6LzvGzJszp1E8=",
+      "version": "0.2.10",
+      "resolved": "https://artifact.tecnalia.com:443/artifactory/api/npm/traceblock-npm-release/@traceblock/api-client/-/api-client-0.2.10.tgz",
+      "integrity": "sha1-bSsmHfpspbqofKPI1ON1Mi9pROU=",
       "requires": {
         "cross-fetch": "^3.1.4",
         "jsonwebtoken": "^8.5.1"
       }
     },
+    "@types/long": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
+      "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
+    },
+    "@types/node": {
+      "version": "17.0.23",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+      "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw=="
+    },
+    "@types/tough-cookie": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+      "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "async": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+    },
+    "axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "requires": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "axios-cookiejar-support": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-1.0.1.tgz",
+      "integrity": "sha512-IZJxnAJ99XxiLqNeMOqrPbfR7fRyIfaoSLdPUf4AMQEGkH8URs0ghJK/xtqBsD+KsSr3pKl4DEQjCn834pHMig==",
+      "requires": {
+        "is-redirect": "^1.0.0",
+        "pify": "^5.0.0"
+      }
+    },
+    "bn.js": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+      "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+    },
+    "brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
+    },
     "buffer-equal-constant-time": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
       "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
     },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "callsite": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
+    },
+    "cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "requires": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+    },
+    "colors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+      "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+    },
     "cross-fetch": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
@@ -189,10 +1164,15 @@
         "node-fetch": "2.6.1"
       }
     },
+    "cycle": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+      "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
+    },
     "dotenv": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
-      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
+      "version": "16.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
+      "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q=="
     },
     "ecdsa-sig-formatter": {
       "version": "1.0.11",
@@ -202,6 +1182,173 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "elliptic": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+      "requires": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+    },
+    "eyes": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+      "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
+    },
+    "fabric-ca-client": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-ca-client/-/fabric-ca-client-2.2.11.tgz",
+      "integrity": "sha512-Tl6ktoYw5ouvz256E/blz+0mWoA6r9L8Pc1BbRffi6ROxuSewdaJEffQjLGAcLG+gJpH2aeviXljWNASqvPIUQ==",
+      "requires": {
+        "fabric-common": "2.2.11",
+        "jsrsasign": "^10.4.1",
+        "url": "^0.11.0",
+        "winston": "^2.4.5"
+      }
+    },
+    "fabric-common": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-common/-/fabric-common-2.2.11.tgz",
+      "integrity": "sha512-VDZUXSoTkgXA4axOONCF93DkFzDaqN0wfKnUTa36Y1Dkk5iSZFdH5iqCroAmerrKg71yGlkiBN7DsD7QWl9McA==",
+      "requires": {
+        "callsite": "^1.0.0",
+        "elliptic": "^6.5.4",
+        "fabric-protos": "2.2.11",
+        "js-sha3": "^0.8.0",
+        "jsrsasign": "^10.4.1",
+        "long": "^4.0.0",
+        "nconf": "^0.11.2",
+        "pkcs11js": "^1.0.6",
+        "promise-settle": "^0.3.0",
+        "sjcl": "^1.0.8",
+        "winston": "^2.4.5",
+        "yn": "^4.0.0"
+      }
+    },
+    "fabric-network": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-network/-/fabric-network-2.2.11.tgz",
+      "integrity": "sha512-YikiBMK0O9Ve+5vZkEOkiz/eXCBzJFh0Ik9MigpjRam86E64NOpNzjMZbCfeas91+O5HPJrO5+zfjEHw7vWsIg==",
+      "requires": {
+        "fabric-common": "2.2.11",
+        "fabric-protos": "2.2.11",
+        "long": "^4.0.0",
+        "nano": "^9.0.3"
+      }
+    },
+    "fabric-protos": {
+      "version": "2.2.11",
+      "resolved": "https://registry.npmjs.org/fabric-protos/-/fabric-protos-2.2.11.tgz",
+      "integrity": "sha512-haWUubqMLccIyBdmhx4ueKCe7rJgr/zln7iDF+BM7Im48K/a76VmWdddm+QGgWI0Bbk1RA1KfLOdwdb/gSGiSg==",
+      "requires": {
+        "@grpc/grpc-js": "^1.3.4",
+        "@grpc/proto-loader": "^0.6.2",
+        "protobufjs": "^6.11.2"
+      }
+    },
+    "follow-redirects": {
+      "version": "1.14.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
+      "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+    },
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+    },
+    "hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+      "requires": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "ini": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+      "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+    },
+    "is-redirect": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+      "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+    },
+    "isstream": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+    },
+    "js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+    },
     "jsonwebtoken": {
       "version": "8.5.1",
       "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
@@ -219,6 +1366,11 @@
         "semver": "^5.6.0"
       }
     },
+    "jsrsasign": {
+      "version": "10.5.14",
+      "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.5.14.tgz",
+      "integrity": "sha512-W49L90gR5E8lOMfZJzhq/JHM2Am6slJWsaI/aiyDZurK1CQly9/FN9ZCvM5ez8Svbw5+CwVEH25iIARLoh4Viw=="
+    },
     "jwa": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
@@ -238,6 +1390,11 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+    },
     "lodash.includes": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -273,25 +1430,274 @@
       "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
       "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
     },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+    },
+    "minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
+    },
     "ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
+    "nan": {
+      "version": "2.15.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+      "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+      "optional": true
+    },
+    "nano": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/nano/-/nano-9.0.5.tgz",
+      "integrity": "sha512-fEAhwAdXh4hDDnC8cYJtW6D8ivOmpvFAqT90+zEuQREpRkzA/mJPcI4EKv15JUdajaqiLTXNoKK6PaRF+/06DQ==",
+      "requires": {
+        "@types/tough-cookie": "^4.0.0",
+        "axios": "^0.21.1",
+        "axios-cookiejar-support": "^1.0.1",
+        "qs": "^6.9.4",
+        "tough-cookie": "^4.0.0"
+      }
+    },
+    "nconf": {
+      "version": "0.11.3",
+      "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.11.3.tgz",
+      "integrity": "sha512-iYsAuDS9pzjVMGIzJrGE0Vk3Eh8r/suJanRAnWGBd29rVS2XtSgzcAo5l6asV3e4hH2idVONHirg1efoBOslBg==",
+      "requires": {
+        "async": "^1.4.0",
+        "ini": "^2.0.0",
+        "secure-keys": "^1.0.0",
+        "yargs": "^16.1.1"
+      }
+    },
     "node-fetch": {
       "version": "2.6.1",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
       "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
     },
+    "object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+    },
+    "pify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz",
+      "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA=="
+    },
+    "pkcs11js": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/pkcs11js/-/pkcs11js-1.3.0.tgz",
+      "integrity": "sha512-owI+M6Gpw0cEU47cTt2eWQs4Iqm9zRyobiJ0q37wIgOrK8BcXVuRM3eVGH58QxYWhItMcRiEBUTE8HUHZX+beQ==",
+      "optional": true,
+      "requires": {
+        "nan": "^2.15.0"
+      }
+    },
+    "promise-settle": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/promise-settle/-/promise-settle-0.3.0.tgz",
+      "integrity": "sha1-tO/VcqHrdM95T4KM00naQKCOTpY="
+    },
+    "protobufjs": {
+      "version": "6.11.2",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+      "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      }
+    },
+    "psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
+    "qs": {
+      "version": "6.10.3",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+      "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+      "requires": {
+        "side-channel": "^1.0.4"
+      }
+    },
+    "querystring": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+    },
     "safe-buffer": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
     },
+    "secure-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz",
+      "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o="
+    },
     "semver": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
       "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+    },
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "requires": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      }
+    },
+    "sjcl": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz",
+      "integrity": "sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ=="
+    },
+    "stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "tough-cookie": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+      "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+      "requires": {
+        "psl": "^1.1.33",
+        "punycode": "^2.1.1",
+        "universalify": "^0.1.2"
+      }
+    },
+    "universalify": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+    },
+    "url": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+      "requires": {
+        "punycode": "1.3.2",
+        "querystring": "0.2.0"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+        }
+      }
+    },
+    "winston": {
+      "version": "2.4.5",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz",
+      "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==",
+      "requires": {
+        "async": "~1.0.0",
+        "colors": "1.0.x",
+        "cycle": "1.0.x",
+        "eyes": "0.1.x",
+        "isstream": "0.1.x",
+        "stack-trace": "0.0.x"
+      },
+      "dependencies": {
+        "async": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
+          "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
+        }
+      }
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+    },
+    "yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "requires": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      }
+    },
+    "yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+    },
+    "yn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz",
+      "integrity": "sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg=="
     }
   }
 }
diff --git a/scripts/package.json b/scripts/package.json
index a464e86..dfdb92a 100644
--- a/scripts/package.json
+++ b/scripts/package.json
@@ -3,10 +3,13 @@
   "version": "0.0.1",
   "private": true,
   "dependencies": {
-    "@traceblock/api-client": "^0.2.9",
-    "dotenv": "^10.0.0"
+    "@traceblock/api-client": "^0.2.10",
+    "dotenv": "^16.0.0",
+    "fabric-ca-client": "^2.2.11",
+    "fabric-network": "^2.2.11"
   },
   "scripts": {
-    "users": "node create-users.js"
+    "users": "node create-users.js api",
+    "users-sdk": "node create-users.js sdk"
   }
 }
diff --git a/scripts/sample.env b/scripts/sample.env
index 2e363c3..ae6db47 100644
--- a/scripts/sample.env
+++ b/scripts/sample.env
@@ -9,4 +9,7 @@ TRACEBLOCK_NETWORK=main
 PASSWD-ADMIN-SIDENOR.COM=password
 PASSWD-ADMIN-CEMENT-COMPANY1.COM=password
 PASSWD-ADMIN-CEMENT-COMPANY2.COM=password
-PASSWD-ADMIN-PUBLIC-ADMINISTRATION.COM=password
\ No newline at end of file
+PASSWD-ADMIN-PUBLIC-ADMINISTRATION.COM=password
+
+# Necessary for create-users-with-sdk.js
+CONNECTION_CONFIG=../deployment/network/vars/profiles/hypercog_connection_for_nodesdk.json
\ No newline at end of file
diff --git a/scripts/sdk.js b/scripts/sdk.js
new file mode 100644
index 0000000..62828e8
--- /dev/null
+++ b/scripts/sdk.js
@@ -0,0 +1,260 @@
+// COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+
+require("dotenv").config()
+
+const fs = require("fs")
+const { Wallets } = require("fabric-network")
+const FabricCAServices = require("fabric-ca-client")
+
+const loadProfile = connectionConfigPath => {
+  if (fs.existsSync(connectionConfigPath)) {
+    return JSON.parse(fs.readFileSync(connectionConfigPath, "utf8"))
+  }
+  throw new Error(`no such file or directory: ${connectionConfigPath}`)
+}
+
+const updateAttrs = (newAttrs = [], oldAttrs = []) => [
+  ...oldAttrs
+    // Reserved prefix, Registrar is not authorized to register it
+    .filter(attr => !attr.name.startsWith("hf."))
+    // Remove old values of the new attrs to be added
+    .filter(attr => !newAttrs.map(a => a.name).find(e => e === attr.name)),
+  ...newAttrs
+]
+
+const createAttrs = (organization, role, affiliation, email, lang = "es") => [
+  // Attrs used in https://git.code.tecnalia.com/ledgerbuilder/sdk/-/blob/develop/core/stub/stub.go
+  { name: "org", value: organization, ecert: true },
+  { name: "role", value: role, ecert: true }, // Also used in frontend
+  { name: "email", value: email },
+  { name: "lang", value: lang, ecert: true }
+]
+
+// By default Fabric only defines these 3 affiliations:
+// https://hyperledger-fabric-ca.readthedocs.io/en/release-1.4/deployguide/ca-config.html#affiliations
+// Since I didn't learn how to modify them in Minifab,
+// new affiliations must be prefixed by one of those making it ugly and confusing :(
+const toValidHfAffiliation = affiliation =>
+  affiliation ? "org1.department1." + affiliation : "org1.department1"
+
+class SDKHelper {
+  constructor(connectionProfilePath) {
+    this.connectionProfile = loadProfile(connectionProfilePath)
+    this.caClient = null
+    this.wallet = {}
+  }
+
+  async _getWallet(org) {
+    // setup the wallet to hold the credentials of the application user
+    if (!this.wallet[org]) {
+      this.wallet[org] = await Wallets.newInMemoryWallet()
+    }
+
+    return this.wallet[org]
+  }
+
+  _getMspId(org) {
+    const orgProfile = this.connectionProfile.organizations[org]
+    if (!orgProfile) {
+      throw new Error(`no such organization in the connection profile: ${org}`)
+    }
+    return orgProfile.mspid
+  }
+
+  _setCAClient(org) {
+    const orgProfile = this.connectionProfile.organizations[org]
+    if (!orgProfile) {
+      throw new Error(`no such organization in the connection profile: ${org}`)
+    }
+
+    const caHostName = orgProfile.certificateAuthorities[0]
+    const caInfo = this.connectionProfile.certificateAuthorities[caHostName]
+    const caTLSCACerts = caInfo.tlsCACerts.pem
+    this.caClient = new FabricCAServices(
+      caInfo.url,
+      { trustedRoots: caTLSCACerts, verify: false },
+      caInfo.caName
+    )
+  }
+
+  async _getAdminUser(organization) {
+    const wallet = await this._getWallet(organization)
+
+    const adminIdentity = await wallet.get("admin")
+    if (!adminIdentity) {
+      throw new Error(
+        "An identity for the admin user does not exist in the wallet"
+      )
+    }
+
+    const provider = wallet
+      .getProviderRegistry()
+      .getProvider(adminIdentity.type)
+    return provider.getUserContext(adminIdentity, "admin")
+  }
+
+  async _enrollUser(userId, password, org) {
+    const mspId = this._getMspId(org)
+    const enrollment = await this.caClient.enroll({
+      enrollmentID: userId,
+      enrollmentSecret: password
+    })
+    // x509Identity
+    return {
+      credentials: {
+        certificate: enrollment.certificate,
+        privateKey: enrollment.key.toBytes()
+      },
+      mspId: mspId,
+      type: "X.509"
+    }
+  }
+
+  async signIn(userId, password, org) {
+    const wallet = await this._getWallet(org)
+
+    // Check to see if we've already enrolled the admin user.
+    const identity = await wallet.get(userId)
+    if (identity) {
+      console.log(
+        `Signing: An identity for the user ${userId} already exists in the wallet`
+      )
+      return
+    }
+
+    this._setCAClient(org)
+
+    const x509Identity = await this._enrollUser(userId, password, org)
+    await wallet.put(userId, x509Identity)
+  }
+
+  async createUser(
+    userId,
+    password,
+    organization,
+    role,
+    email,
+    language,
+    extraAttrs = []
+  ) {
+    const adminUser = await this._getAdminUser(organization)
+
+    // returns secret
+    await this.caClient.register(
+      {
+        enrollmentID: userId,
+        enrollmentSecret: password,
+        role: "client", // "hf.Type" in the certificate
+        affiliation: toValidHfAffiliation(affiliation),
+        maxEnrollments: -1, // unlimited
+        attrs: updateAttrs(
+          createAttrs(organization, role, email, language),
+          extraAttrs
+        )
+      },
+      adminUser
+    )
+
+    // Register the user, enroll the user, and import the new identity into the wallet.
+    // if affiliation is specified by client, the affiliation value must be configured in CA
+    const secret = await this._register(organization)
+    await this._enrollUser(userId, secret, organization)
+  }
+
+  /**
+   * Modifies the information of a user.
+   * Note that only a user with an `admin` role can do this.
+   *
+   * @param {string} username Username of the user.
+   * @param {string} password New password of the user.
+   * @param {string} organization New organization of the user.
+   * @param {string} role New role for the user.
+   * @param {string} email E-mail of the new user.
+   * @param {object} [oldCert] X.509 certificate fields
+   * @param {Array.<AttributeX509>} [oldCert.attrs=[]] Attributes of the certificate.
+   */
+  async modifyUser(userId, password, organization, role, email, oldCert = {}) {
+    const wallet = await this._getWallet(organization)
+
+    // Must use an admin to register a new user
+    const adminIdentity = await wallet.get("admin")
+    if (!adminIdentity) {
+      console.log("An identity for the admin user does not exist in the wallet")
+      console.log("Enroll the admin user before retrying")
+      return
+    }
+
+    const provider = wallet
+      .getProviderRegistry()
+      .getProvider(adminIdentity.type)
+    const adminUser = await provider.getUserContext(adminIdentity, "admin")
+    const identityService = await this.caClient.newIdentityService()
+
+    const affiliationAttr = oldCert.attrs.find(
+      attr => attr.name === "affiliation"
+    )
+    const affiliation = affiliationAttr
+      ? toValidHfAffiliation(affiliationAttr.value)
+      : undefined
+
+    return identityService.update(
+      userId,
+      {
+        enrollmentID: userId,
+        secret: password,
+        affiliation,
+        role: "client", // "hf.Type" in the certificate
+        attrs: updateAttrs(
+          createAttrs(organization, role, email),
+          oldCert.attrs
+        )
+      },
+      adminUser
+    )
+  }
+
+  // Order important since "organization" param is not in API client
+  async getUser(userId, organization) {
+    const identityService = await this.caClient.newIdentityService()
+    const { success, result } = await identityService.getOne(
+      userId,
+      await this._getAdminUser(organization)
+    )
+    return success ? result : null
+  }
+
+  async getAffiliations(organization) {
+    const affiliationService = await this.caClient.newAffiliationService()
+    const affResp = await affiliationService.getAll(
+      await this._getAdminUser(organization)
+    )
+
+    if (!affResp.success) {
+      console.error(affResp.errors)
+      return []
+    }
+
+    return affResp.result.affiliations
+  }
+
+  async updateAffiliation(organization, oldAffiliation, affiliation) {
+    const affiliationService = await this.caClient.newAffiliationService()
+    await affiliationService.update(
+      oldAffiliation,
+      // Force: If any of the parent affiliations do not exist, create all parent affiliations also.
+      { name: toValidHfAffiliation(affiliation), force: true },
+      await this._getAdminUser(organization)
+    )
+  }
+
+  async addAffiliation(organization, affiliation) {
+    const affiliationService = await this.caClient.newAffiliationService()
+    await affiliationService.create(
+      // Force: If any of the parent affiliations do not exist, create all parent affiliations also.
+      { name: toValidHfAffiliation(affiliation), force: true },
+      await this._getAdminUser(organization)
+    )
+  }
+}
+
+module.exports = SDKHelper
-- 
GitLab