From 57d427160b113a94564bdd1ad8ab75a1c1ca9fe8 Mon Sep 17 00:00:00 2001 From: Marina Fedyantceva Date: Tue, 12 Mar 2024 17:57:53 +0300 Subject: [PATCH 01/27] updated packages versions --- package.json | 54 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 71dadf893..c6161402b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "@material-ui/lab": "^3.0.0-alpha.30", "@material-ui/styles": "^4.11.2", "@sentry/browser": "^5.29.0", - "aws-amplify": "^1.1.28", + "aws-amplify": "^4.3.46", "clsx": "^1.1.1", "color": "^3.1.3", "css-loader": "^5.0.1", @@ -32,37 +32,40 @@ "notistack": "^0.8.9", "papaparse": "^5.3.0", "query-string": "^6.13.7", - "react": "^16.14.0", + "react": "^18.2.0", + "react-app-rewired": "^2.2.1", "react-async-script-loader": "^0.3.0", "react-copy-to-clipboard": "^5.0.1", - "react-dom": "^16.14.0", - "react-dropzone": "^8.0.4", + "react-dom": "^18.2.0", + "react-dropzone": "^10.2.1", "react-file-drop": "^0.2.8", "react-ga": "^2.6.0", - "react-google-charts": "^3.0.10", + "react-google-charts": "^4.0.1", "react-helmet": "^6.1.0", - "react-image-gallery": "^1.0.8", - "react-json-view": "^1.19.1", - "react-redux": "^7.2.2", + "react-image-gallery": "^1.3.0", + "react-json-view": "^1.21.3", + "react-redux": "^9.1.0", "react-router-dom": "^5.2.0", "react-router-sitemap": "^1.2.0", - "react-scripts": "^3.4.4", - "react-slick": "^0.28.0", + "react-scripts": "^5.0.1", + "react-slick": "^0.30.2", "react-star-rating-component": "^1.4.1", - "react-swipeable-views": "^0.13.1", - "redux": "^4.0.1", - "redux-thunk": "^2.3.0", + "react-swipeable-views": "^0.14.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", "scrypt": "github:barrysteyn/node-scrypt#fb60a8d3c158fe115a624b5ffa7480f3a24b03fb", "singularitynet-platform-contracts": "1.0.2", "slick-carousel": "^1.8.1", "snet-sdk-web": "3.0.0", - "style-loader": "^2.0.0", "utf8": "^3.0.0", "validate.js": "^0.13.1", - "web3": "1.0.0-beta.52" + "web3": "^4.5.0" + }, + "resolutions": { + "concat-stream": "github:singnet/concat-stream" }, "scripts": { - "start": "react-scripts --openssl-legacy-provider start", + "start": "react-app-rewired --openssl-legacy-provider start", "build": "npm run sitemap && react-scripts --max_old_space_size=8192 build", "test": "react-scripts test", "eject": "react-scripts eject", @@ -97,23 +100,28 @@ ] }, "devDependencies": { - "@babel/core": "^7.12.10", + "@babel/preset-env": "^7.24.0", "@storybook/addon-actions": "^5.3.21", "@storybook/addon-links": "^5.3.21", "@storybook/addons": "^5.3.21", "@storybook/react": "^5.3.21", "adm-zip": "0.5.5", "archiver": "5.3.0", - "babel-cli": "^6.26.0", - "babel-preset-es2015": "^6.24.1", - "babel-preset-react": "^6.24.1", - "babel-register": "^6.26.0", "chalk": "4.1.1", "dotenv": "8.2.0", + "eslint": "^8.57.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-config-react-app": "^7.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.34.0", + "eslint-plugin-react-hooks": "^4.6.0", "husky": "^2.7.0", + "prettier": "^3.2.5", "lint-staged": "^8.2.0", - "prettier": "^1.18.2", - "pretty-quick": "^1.11.0", "prompts": "^2.4.1", "rimraf": "^3.0.2", "storybook-addon-react-docgen": "^1.2.42", From 7d1684a2050ef05c2f61e5e4f492583dafa2aae7 Mon Sep 17 00:00:00 2001 From: Marina Fedyantceva Date: Tue, 12 Mar 2024 17:58:30 +0300 Subject: [PATCH 02/27] added updated sdk --- config-overrides.js | 27 + package.json | 5 +- snet-sdk-js/.babelrc | 12 + snet-sdk-js/.env.example | 7 + snet-sdk-js/.eslintrc | 42 + snet-sdk-js/.gitignore | 88 + snet-sdk-js/LICENSE | 21 + snet-sdk-js/README.md | 84 + .../design-docs/class-diagram.plantuml | 274 +++ snet-sdk-js/design-docs/class-diagram.png | Bin 0 -> 486045 bytes snet-sdk-js/jsdoc.config.json | 28 + snet-sdk-js/package.json | 32 + snet-sdk-js/packages/nodejs/.babelrc | 14 + snet-sdk-js/packages/nodejs/README.md | 127 + snet-sdk-js/packages/nodejs/package.json | 54 + .../packages/nodejs/src/ConcurrencyManager.js | 146 ++ snet-sdk-js/packages/nodejs/src/NodeSdk.js | 46 + .../packages/nodejs/src/ServiceClient.js | 123 + snet-sdk-js/packages/nodejs/src/core | 1 + snet-sdk-js/packages/nodejs/src/index.js | 6 + .../BasePaidPaymentStrategy.js | 85 + .../DefaultPaymentStrategy.js | 58 + .../FreeCallPaymentStrategy.js | 173 ++ .../PaidCallPaymentStrategy.js | 52 + .../PrepaidPaymentStrategy.js | 53 + .../nodejs/src/payment_strategies/index.js | 1 + .../nodejs/src/proto/state_service.proto | 92 + .../nodejs/src/proto/state_service_grpc_pb.js | 90 + .../nodejs/src/proto/state_service_pb.js | 1231 ++++++++++ .../nodejs/src/proto/token_service.proto | 63 + .../nodejs/src/proto/token_service_grpc_pb.js | 60 + .../nodejs/src/proto/token_service_pb.js | 609 +++++ snet-sdk-js/packages/nodejs/src/sdk-core.js | 5 + .../packages/nodejs/src/utils/logger.js | 17 + snet-sdk-js/packages/web/.babelrc | 13 + snet-sdk-js/packages/web/README.md | 95 + snet-sdk-js/packages/web/package.json | 57 + .../packages/web/src/ConcurrencyManager.js | 108 + .../packages/web/src/RegistryContract.js | 134 ++ snet-sdk-js/packages/web/src/WebSdk.js | 43 + .../packages/web/src/WebServiceClient.js | 128 + snet-sdk-js/packages/web/src/core/index.js | 10 + .../packages/web/src/core/src/Account.js | 172 ++ .../web/src/core/src/BaseServiceClient.js | 598 +++++ .../web/src/core/src/IPFSMetadataProvider.js | 86 + .../packages/web/src/core/src/MPEContract.js | 246 ++ .../web/src/core/src/PaymentChannel.js | 133 ++ .../core/src/identities/MetaMaskIdentity.js | 67 + .../core/src/identities/PrivateKeyIdentity.js | 70 + .../web/src/core/src/identities/index.js | 2 + ...DefaultPaymentChannelManagementStrategy.js | 83 + ...PrepaidPaymentChannelManagementStrategy.js | 49 + .../index.js | 1 + snet-sdk-js/packages/web/src/core/src/sdk.js | 94 + .../src/core/src/types/grpc_type_defs.jsdoc | 5 + .../src/core/src/types/snet_type_defs.jsdoc | 185 ++ .../src/core/src/types/web3_type_defs.jsdoc | 5 + .../src/core/src/utils/bignumber_helper.js | 5 + .../src/core/src/utils/blockchainEvents.js | 6 + .../web/src/core/src/utils/encodingUtils.js | 17 + .../web/src/core/src/utils/ethereumUtils.js | 3 + .../packages/web/src/core/src/utils/logger.js | 9 + snet-sdk-js/packages/web/src/index.js | 8 + .../BasePaidPaymentStrategy.js | 79 + .../DefaultPaymentStrategy.js | 33 + .../FreeCallPaymentStrategy.js | 135 ++ .../PaidCallPaymentStrategy.js | 49 + .../PrepaidPaymentStrategy.js | 42 + .../web/src/payment_strategies/index.js | 1 + .../web/src/proto/state_service.proto | 92 + .../web/src/proto/state_service_pb.js | 1229 ++++++++++ .../web/src/proto/state_service_pb_service.js | 116 + .../web/src/proto/token_service.proto | 63 + .../web/src/proto/token_service_pb.js | 607 +++++ .../web/src/proto/token_service_pb_service.js | 61 + .../packages/web/src/proto/training.proto | 112 + .../packages/web/src/proto/training_pb.js | 2116 +++++++++++++++++ .../web/src/proto/training_pb_service.js | 221 ++ snet-sdk-js/packages/web/src/sdk-core.js | 5 + .../packages/web/src/utils/BrowserConsole.js | 48 + snet-sdk-js/packages/web/src/utils/logger.js | 6 + 81 files changed, 11171 insertions(+), 2 deletions(-) create mode 100644 config-overrides.js create mode 100644 snet-sdk-js/.babelrc create mode 100644 snet-sdk-js/.env.example create mode 100644 snet-sdk-js/.eslintrc create mode 100644 snet-sdk-js/.gitignore create mode 100644 snet-sdk-js/LICENSE create mode 100644 snet-sdk-js/README.md create mode 100644 snet-sdk-js/design-docs/class-diagram.plantuml create mode 100644 snet-sdk-js/design-docs/class-diagram.png create mode 100644 snet-sdk-js/jsdoc.config.json create mode 100644 snet-sdk-js/package.json create mode 100644 snet-sdk-js/packages/nodejs/.babelrc create mode 100644 snet-sdk-js/packages/nodejs/README.md create mode 100644 snet-sdk-js/packages/nodejs/package.json create mode 100644 snet-sdk-js/packages/nodejs/src/ConcurrencyManager.js create mode 100644 snet-sdk-js/packages/nodejs/src/NodeSdk.js create mode 100644 snet-sdk-js/packages/nodejs/src/ServiceClient.js create mode 100644 snet-sdk-js/packages/nodejs/src/core create mode 100644 snet-sdk-js/packages/nodejs/src/index.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/BasePaidPaymentStrategy.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/DefaultPaymentStrategy.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/FreeCallPaymentStrategy.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/PaidCallPaymentStrategy.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/PrepaidPaymentStrategy.js create mode 100644 snet-sdk-js/packages/nodejs/src/payment_strategies/index.js create mode 100644 snet-sdk-js/packages/nodejs/src/proto/state_service.proto create mode 100644 snet-sdk-js/packages/nodejs/src/proto/state_service_grpc_pb.js create mode 100644 snet-sdk-js/packages/nodejs/src/proto/state_service_pb.js create mode 100644 snet-sdk-js/packages/nodejs/src/proto/token_service.proto create mode 100644 snet-sdk-js/packages/nodejs/src/proto/token_service_grpc_pb.js create mode 100644 snet-sdk-js/packages/nodejs/src/proto/token_service_pb.js create mode 100644 snet-sdk-js/packages/nodejs/src/sdk-core.js create mode 100644 snet-sdk-js/packages/nodejs/src/utils/logger.js create mode 100644 snet-sdk-js/packages/web/.babelrc create mode 100644 snet-sdk-js/packages/web/README.md create mode 100644 snet-sdk-js/packages/web/package.json create mode 100644 snet-sdk-js/packages/web/src/ConcurrencyManager.js create mode 100644 snet-sdk-js/packages/web/src/RegistryContract.js create mode 100644 snet-sdk-js/packages/web/src/WebSdk.js create mode 100644 snet-sdk-js/packages/web/src/WebServiceClient.js create mode 100644 snet-sdk-js/packages/web/src/core/index.js create mode 100644 snet-sdk-js/packages/web/src/core/src/Account.js create mode 100644 snet-sdk-js/packages/web/src/core/src/BaseServiceClient.js create mode 100644 snet-sdk-js/packages/web/src/core/src/IPFSMetadataProvider.js create mode 100644 snet-sdk-js/packages/web/src/core/src/MPEContract.js create mode 100644 snet-sdk-js/packages/web/src/core/src/PaymentChannel.js create mode 100644 snet-sdk-js/packages/web/src/core/src/identities/MetaMaskIdentity.js create mode 100644 snet-sdk-js/packages/web/src/core/src/identities/PrivateKeyIdentity.js create mode 100644 snet-sdk-js/packages/web/src/core/src/identities/index.js create mode 100644 snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/DefaultPaymentChannelManagementStrategy.js create mode 100644 snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/PrepaidPaymentChannelManagementStrategy.js create mode 100644 snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/index.js create mode 100644 snet-sdk-js/packages/web/src/core/src/sdk.js create mode 100644 snet-sdk-js/packages/web/src/core/src/types/grpc_type_defs.jsdoc create mode 100644 snet-sdk-js/packages/web/src/core/src/types/snet_type_defs.jsdoc create mode 100644 snet-sdk-js/packages/web/src/core/src/types/web3_type_defs.jsdoc create mode 100644 snet-sdk-js/packages/web/src/core/src/utils/bignumber_helper.js create mode 100644 snet-sdk-js/packages/web/src/core/src/utils/blockchainEvents.js create mode 100644 snet-sdk-js/packages/web/src/core/src/utils/encodingUtils.js create mode 100644 snet-sdk-js/packages/web/src/core/src/utils/ethereumUtils.js create mode 100644 snet-sdk-js/packages/web/src/core/src/utils/logger.js create mode 100644 snet-sdk-js/packages/web/src/index.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/BasePaidPaymentStrategy.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/DefaultPaymentStrategy.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/FreeCallPaymentStrategy.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/PaidCallPaymentStrategy.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/PrepaidPaymentStrategy.js create mode 100644 snet-sdk-js/packages/web/src/payment_strategies/index.js create mode 100644 snet-sdk-js/packages/web/src/proto/state_service.proto create mode 100644 snet-sdk-js/packages/web/src/proto/state_service_pb.js create mode 100644 snet-sdk-js/packages/web/src/proto/state_service_pb_service.js create mode 100644 snet-sdk-js/packages/web/src/proto/token_service.proto create mode 100644 snet-sdk-js/packages/web/src/proto/token_service_pb.js create mode 100644 snet-sdk-js/packages/web/src/proto/token_service_pb_service.js create mode 100644 snet-sdk-js/packages/web/src/proto/training.proto create mode 100644 snet-sdk-js/packages/web/src/proto/training_pb.js create mode 100644 snet-sdk-js/packages/web/src/proto/training_pb_service.js create mode 100644 snet-sdk-js/packages/web/src/sdk-core.js create mode 100644 snet-sdk-js/packages/web/src/utils/BrowserConsole.js create mode 100644 snet-sdk-js/packages/web/src/utils/logger.js diff --git a/config-overrides.js b/config-overrides.js new file mode 100644 index 000000000..df0f21710 --- /dev/null +++ b/config-overrides.js @@ -0,0 +1,27 @@ +const webpack = require("webpack"); + +module.exports = function override(config) { + const fallback = config.resolve.fallback || {}; + Object.assign(fallback, { + os: require.resolve("os-browserify"), + url: require.resolve("url"), + path: require.resolve("path-browserify"), + }); + config.resolve.fallback = fallback; + config.plugins = (config.plugins || []).concat([ + new webpack.ProvidePlugin({ + process: "process/browser", + Buffer: ["buffer", "Buffer"], + }), + ]); + config.ignoreWarnings = [/Failed to parse source map/]; + config.module.rules.push({ + test: /\.(js|mjs|jsx)$/, + enforce: "pre", + loader: require.resolve("source-map-loader"), + resolve: { + fullySpecified: false, + }, + }); + return config; +}; diff --git a/package.json b/package.json index c6161402b..93b818e08 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "scrypt": "github:barrysteyn/node-scrypt#fb60a8d3c158fe115a624b5ffa7480f3a24b03fb", "singularitynet-platform-contracts": "1.0.2", "slick-carousel": "^1.8.1", - "snet-sdk-web": "3.0.0", + "snet-sdk-web": "file:./snet-sdk-js/packages/web", "utf8": "^3.0.0", "validate.js": "^0.13.1", "web3": "^4.5.0" @@ -76,7 +76,8 @@ "build-storybook": "build-storybook", "sitemap": "babel-node scripts/sitemap-generator.js", "zip-components": "REACT_APP_SANDBOX=true babel-node scripts/zip-components.js", - "generate-stubs": "babel-node scripts/generate-stubs.js" + "generate-stubs": "babel-node scripts/generate-stubs.js", + "preinstall": "cd snet-sdk-js/packages/web && yarn install && npm run build && yarn link" }, "lint-staged": { "*.{js,jsx}": [ diff --git a/snet-sdk-js/.babelrc b/snet-sdk-js/.babelrc new file mode 100644 index 000000000..394c5435b --- /dev/null +++ b/snet-sdk-js/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ] + ] +} diff --git a/snet-sdk-js/.env.example b/snet-sdk-js/.env.example new file mode 100644 index 000000000..0deaa5dd3 --- /dev/null +++ b/snet-sdk-js/.env.example @@ -0,0 +1,7 @@ +PRIVATE_KEY= +SIGNER_PRIVATE_KEY= +NETWORK_ID=3 +PROVIDER_HOST=https://goerli.infura.io/v3/xxxxxx +IPFS_ENDPOINT=http://ipfs.singularitynet.io:80 +DEFAULT_GAS_PRICE=4700000 +DEFAULT_GAS_LIMIT=210000 diff --git a/snet-sdk-js/.eslintrc b/snet-sdk-js/.eslintrc new file mode 100644 index 000000000..fa2a6d654 --- /dev/null +++ b/snet-sdk-js/.eslintrc @@ -0,0 +1,42 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true, + "node": true + }, + "extends": "airbnb-base", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + "max-len": [ + "warn", + 120 + ], + "no-underscore-dangle": [ + "error", + { + "allowAfterThis": true + } + ], + "keyword-spacing": [ + "error", + { + "overrides": { + "if": { + "after": false + }, + "while": { + "after": false + } + } + } + ] + } +} diff --git a/snet-sdk-js/.gitignore b/snet-sdk-js/.gitignore new file mode 100644 index 000000000..2ef342d90 --- /dev/null +++ b/snet-sdk-js/.gitignore @@ -0,0 +1,88 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.kovan +.env.ropsten +.env.dev +.env.local + +# next.js build output +.next + +### macOS +# General +.DS_Store +.AppleDouble +.LSOverride + +### JetBrains +.idea/ +.vscode/ + +### istanbul +/.nyc_output/ +/coverage/ + +### build +dist +out +docs + +### misc +tmp +_example diff --git a/snet-sdk-js/LICENSE b/snet-sdk-js/LICENSE new file mode 100644 index 000000000..6df4da825 --- /dev/null +++ b/snet-sdk-js/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 SingularityNET + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/snet-sdk-js/README.md b/snet-sdk-js/README.md new file mode 100644 index 000000000..75b74569b --- /dev/null +++ b/snet-sdk-js/README.md @@ -0,0 +1,84 @@ +# snet-sdk-js +SingularityNET SDK for JavaScript + +## Getting Started +This repo hosts multiple SDKs for JavaScript. Currently supported platforms +1. Node.js using [grpc-node](https://github.com/grpc/grpc-node) +2. Browser (Web) using [grpc-web](https://github.com/improbable-eng/grpc-web) + +You can find more details about each sdk within the respective package folders. +1. Node.js under `packages/nodejs` directory +2. Web under `packages/web` directory + +**These SDKs are under active development and not ready for production use yet. If you find any bug or something doesn't work as expected, please create an issue.** + +## Usage +All the SDKs assume that there is enough `eth` balance to cover the `gas` cost and `AGI` tokens in the wallet to cover the service execution cost. + +The SDKs chose a default `PaymentChannelManagementStrategy` which is the simplest form of picking an existing `Payment Channel` if any or creates a new `Payment Channel` if no channel is found. This can be easily overridden by providing your own strategy to the SDK at the time of construction. Documentation on creating custom strategies will be available soon. + +## Development +This is a monorepo which is setup a little differently. It does not use any external tools like [lerna](https://github.com/lerna/lerna) or any other popular tool. + +There are 3 packages out of which only 2 of them are published to npm +1. core +2. nodejs (published) +3. web (published) + +The way the `core` package is shared across `nodejs` and `web` is by creating a symlink to core under each package. This setup has been tested on `macOS` and should work on any standard `Linux` distribution but it has not been tested on `Windows` OS. + +### Build +Navigate to the specific package which needs to be build and then run the following command +```shell +npm run build +``` + +### Publish +Navigate to the specific package which needs to be published and then run the following command +```bash +npm run publish +``` + +# Handling Signature / Binary +The grpc API metadata is a map of key, value pair to store the headers. +The value can be either String or Buffer. + +All binary headers should have `-bin` suffix in their names. Vice versa. +A String header's name must not end with this. + +NodeSDK and WebSDK use [grpc](https://www.npmjs.com/package/grpc) and [@improbable-eng/grpc-web +](https://www.npmjs.com/package/@improbable-eng/grpc-web) respectively to make grpc API calls. + +## NodeSDK +The grpc package used in node allows to use buffer in the raw binary format without any modification. +So the signature returned by the `signData` method in the `Account` class can be directly passed on to the grpc metadata. + e.g. + ``` + async getPaymentMetadata() { + const signature = await this._generateSignature(currentBlockNumber); + const metadata = [{ + 'snet-payment-channel-signature-bin': signature + }]; + return metadata; + } +``` +## WebSDK +Browsers don't have native support for http2.0 which is the base for grpc. +The package @improbable-eng/grpc-web acts as a wrapper around grpc and allows developers to make grpc calls from the browser. + +In order to transfer buffer signatures in the headers, the value has to converted first to `base64 string`. +A buffer can be converted to base64 string using the javascript function `Array.prototype.toString("base64")`. + + e.g. + ``` +async getPaymentMetadata() { + const signature = await this._generateSignature(channel.channelId, channel.state.nonce, amount); + const metadata = [{ + 'snet-payment-channel-signature-bin': signature.toString('base64') + }]; + return metadata; + } +``` + +
+[LICENSE](https://github.com/singnet/snet-sdk-js/blob/master/LICENSE) file for details. diff --git a/snet-sdk-js/design-docs/class-diagram.plantuml b/snet-sdk-js/design-docs/class-diagram.plantuml new file mode 100644 index 000000000..c7a63adcf --- /dev/null +++ b/snet-sdk-js/design-docs/class-diagram.plantuml @@ -0,0 +1,274 @@ +@startuml + +title SingularityNet Javascript SDK + +!define ABSTRACT <abstract> + +class Configuration { + + web3Provider: String + + privateKey: String + + networkId: Number + + ipfsEndpoint: String + + defaultGasPrice: Number + + defaultGasLimit: Number +} + +class Pricing { + + pricing_in_cogs: Number + + pricing_model: String + + default: Boolean +} + +class GroupPayment { + + payment_address: String + + payment_expiration_threshold: Number + + payment_channel_storage_type: String + + payment_channel_storage_client: Object +} + +class Group { + + group_id: String + + group_name: String + + payment: GroupPayment + + pricing: Pricing[] + + endpoints: String[] +} + +Group *-- GroupPayment +Group *-- Pricing + +class ServiceMetadata { + + display_name: String + + encoding: String + + service_type: String + + model_ipfs_hash: String + + mpe_address: String + + assets: Object + + groups: Group[] + + service_description: Object +} + +ServiceMetadata *-- Group + +interface MetadataProvider { + + metadata(orgId: String, serviceId: String): ServiceMetadata +} + +class IPFSMetadataProvider { + + metadata(): ServiceMetadataWithGroups + - _fetchOrgMetadata(Bytes orgId): OrgMetadata + - _fetchServiceMetadata(Bytes orgId,Bytes serviceId): ServiceMetadata + - _enhanceServiceGroupDetails(serviceMetadata, orgMetadata): ServiceMetadataWithGroups + - _constructIpfsClient(): IPFSClient +} + +IPFSMetadataProvider o-- Web3 + +MetadataProvider <|-- IPFSMetadataProvider + +class MPEContract { + + address: string + + networkId: Number + + contract: Web3.Eth.Contract + + balance(address): double + + channelAddFunds(account, channelId, amount): Transaction + + channelClaimTimeout(account, channelId): Transaction + + channelExtend(account, channelId, expiration): Transaction + + channelExtendAndAddFunds(account, channelId, expiration, amount): Transaction + + channels(channelId): + + deposit(account, double cogs): Transaction + + depositAndOpenChannel(account, recipientAddress, groupId, amount, expiration) + + getPastOpenChannels(account, recipient, startingBlockNumber): PaymentChannel[] + + openChannel(account, recipientAddress, groupId, amount, expiration) + + withdraw(account, amount): Transaction + - _fundEscrowAccount(account, amountInCogs): Void + - _deploymentBlockNumber(): Number +} + +MPEContract o-- Web3 + +class TransactionObject { + nonce: String + gas: String + gasPrice: String + to: String + data: String +} + +class Account { + + address(): String + + allowance(): BigNumber + + approveTransfer(amountInCogs): Transaction + + balance(): BigNumber + + depositToEscrowAccount(amountInCogs): Transaction + + escrowBalance(): BigNumber + + sendTransaction(to, contractFn, ...contractFnArgs): Transaction + + signData(message): Buffer + + signerAddress(): String + + withdrawFromEscrowAccount(amountInCogs): Transaction + - _waitForTransaction(String hash): Transaction + - _getTokenContract(): Contract + - _generateTokenContract(): Contract + - _baseTransactionObject(operation, to): Transaction + - _getGas(operation) : {gasLimit, gasPrice} + - _transactionCount(): Number +} + +Account o-- Web3 +Account o-- MPEContract +Account o-- Identity + +interface Identity { + + address() + + signData(message: String) + + sendTransaction(transactionObject) +} + +class PrivateKeyIdentity { + + address(): String + + signData(sha3Message): Bytes + + sendTransaction(transactionObject): String + - _setupAccount() + - sendTransaction(transactionObject) +} + +PrivateKeyIdentity o-- Configuration +PrivateKeyIdentity o-- Web3 + +Identity <|-- PrivateKeyIdentity + +class MetaMaskIdentity { + + address(): String + + signData(sha3Message): Bytes + + sendTransaction(transactionObject): String + + setupAccount() +} + +MetaMaskIdentity o-- Configuration +MetaMaskIdentity o-- Web3 + +Identity <|-- MetaMaskIdentity + +interface PaymentChannelManagementStrategy { + + selectChannel(ServiceClient serviceClient): PaymentChannel +} + +class EnhancedGroupInfo { + + group_id_in_bytes: Buffer + + payment_address: String + + payment_expiration_threshold: Number +} + +Group <|-- EnhancedGroupInfo + +class ServiceOptions { + channelStateRequestSigner: Function + paidCallMetadataGenerator: Function + endpoint: String + disableBlockchainOperations: Boolean + metadataGenerator: Function + concurrency: Boolean +} + +class BaseServiceClient { + + metadata(): ServiceMetadata + + paymentChannelStateServiceClient(): GRPCClient + + group(): EnhancedGroupInfo + + paymentChannels(): PaymentChannel[] + + depositAndOpenChannel(amount, expiration): PaymentChannel + + getChannelState(channelId): ChannelStateReply + + loadOpenChannels(): PaymentChannel[] + + openChannel(amount, expiration): PaymentChannel + + updateChannelStates(): PaymentChannel[] + - _enhanceGroupInfo(group): Group + - _channelStateRequest(channelId) + - _channelStateRequestProperties(channelId): { currentBlockNumber, signatureBytes } + - _fetchPaymentMetadata(): { channelId, nonce, signingAmount, signatureBytes } + - _getNewlyOpenedChannel(receipt): PaymentChannel + - _getServiceEndpoint(): String + - _generatePaymentChannelStateServiceClient() ABSTRACT + - _getChannelStateRequestMethodDescriptor() ABSTRACT +} + +BaseServiceClient o-- SnetSDK +BaseServiceClient o-- MPEContract +BaseServiceClient o-- ServiceMetadata +BaseServiceClient o-- Group +BaseServiceClient o-- GRPCClient +BaseServiceClient o-- PaymentChannelManagementStrategy +BaseServiceClient o-- ServiceOptions +BaseServiceClient o-- ConcurrencyManager + +class ServiceClient { + + service: GRPCClient +} + +BaseServiceClient <|-- ServiceClient + +class WebServiceClient { + + invoke(GRPCMethodDescriptor, props) + + unary(GRPCMethodDescriptor, props) +} + +BaseServiceClient <|-- WebServiceClient + +class PaymentChannelState { + + nonce: String + + currentNonce: Number + + expiry: Number + + amountDeposited: Number + + currentSignedAmount: Number + + availableAmount: Number +} + +class PaymentChannel { + + addFunds(BigInt amount): Transaction + + channelId(): Number + + claimUnusedTokens(): Transaction + + extendAndAddFunds(Number expiration, Number amount): Transaction + + extendExpiry(Number expiration): Transaction + + state(): PaymentChannelState + + syncState(): PaymentChannel + - _currentChannelState(): PaymentChannelState + - _uint8ArrayToBN(UInt8Array uint8Array): BigNumber +} + +PaymentChannel o-- Web3 +PaymentChannel o-- Account +PaymentChannel o-- MPEContract +PaymentChannel o-- BaseServiceClient + +class ConcurrencyManager { + + concurrentCalls: ConcurrentCalls[] + + token: String + + plannedAmount: Number + + usedAmount: Number + - _serviceClient: ServiceClient + + getToken(channel, serviceCallPrice, newToken): String + - getNewToken(channel, amount): String + - getTokenForAmount(channel, amount): String + + recordSuccessfulCall() +} + +ConcurrencyManager *-- ConcurrencyStubs + +interface SnetSDK { + + account(): Account + + web3(): Web3 +} + +SnetSDK o-- Configuration +SnetSDK o-- MetadataProvider + +class NodeSDK { + + createServiceClient(): ServiceClient +} + +SnetSDK <|.. NodeSDK + +class WebSDK { + + createServiceClient(): ServiceClient +} + +SnetSDK <|.. WebSDK +@enduml diff --git a/snet-sdk-js/design-docs/class-diagram.png b/snet-sdk-js/design-docs/class-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..de35e4efc46218cd9c07bb692d4399a25083c7f5 GIT binary patch literal 486045 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 &6#ncItYjjhYW8LwX2Zz;R(HUtYIg*0NN zexpDDbJ4W0aA##D%l&G@1?XA2wf^+mU%%%0u7rp0U=oj8X=nvHdWA<2KdQ`H<36~i zn_fSle9$&=zBEheKc*pamCc?R9JsXlaJG*k>;tC!#l_3K@d%8N8W$EQw`%M(84~`DUX11Hb z!BKg6-C;Y|>u2}mLO19bSp?G0_Tu)D^nP`j71Uqw+N#z7W|~7~&3u zzT`}DxBq#%lap}Tkz7*Z^V-nFn$nde`-fZF6&Q7H=XGMMts#PzD5#+1ZmQT~W{x7? z?Msbo%l)xK5F_C7E&7cr9p2X843B^6oP~^K7vsVRzfOB3z5bTwE8nf+oqAVb4966D ztnwKb3R5WR>G&v6Ee_w-H;XJR0Q| zzo0b}KP#E2_Yg25R98^AZ#Gkt3Xizoc=@e`Bi4T}c7(R17gunwteX2Mz1^b@c*EKF z)BAkVC%?pkFX(VWe8L3F=h0{1AE9>@=Q7e!6!{){Hz(bm*VcB#t=h{1$bLtm_(ANc z{@L)T0KOurle^1qaLjCBYBws~@X~Fd_PQK${!8sCJ{d5dM7M-rHWU_&N%l6?&T~z& zOoojD6il2)N$kDF2g!%`h?T6v3_@@R`e*CU)3{G~g$Y93ni0lFs zCG9(F$&qz1$rOkE29}0KBM=&(5t9mhUBXRA4ofnFw)1N7pt6JGn#aiwc*dF0n-ln| zs7UBzd2MRqCrbe65O(Y$tb}Jvc>k?yvJ+KTgZBWf9EmQ%)>1Ur4O5EPUsaT<>-;*D?9$`ngyjoDu(6nnR zNG%{wRqD~>xuW@DC5nou6@nZAm)AcEv*nt$u9MWo3bm)mEEj+4x?Lt%x;%d=D*EZq zy^QqAeGr=PDNNS6p||)JXi1yiI#^$5*W%~rhrJ2RB;9Q*WVm*JZscz7Jb?q3Bd{nX zqgguiRp0wr>SHJu$F($=J@p@riY0h=(T}z3-QV>FB$EwXcYitP{w%3f>t zoC;&bUq1!?k%10~Q(|FQ<@>oTsd8F7kb>*4>p77g*ku@tt$ruhpWqdVP<2 z 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

u{&_hf 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

- -
+
{htmlTooltip ? ( - - {htmlTooltip} - - } - placement="top" - > - - + {htmlTooltip}} placement="top"> + + ) : null} {infoTitle ? ( {infoTitle} ) : null}
-
- {htmlValue ? htmlValue : ({value})} + {htmlValue ? htmlValue : {value}}
-
); } diff --git a/src/assets/thirdPartyServices/common/OutlinedTextArea.js b/src/assets/thirdPartyServices/common/OutlinedTextArea.js index 3303489e2..fce07bbcb 100644 --- a/src/assets/thirdPartyServices/common/OutlinedTextArea.js +++ b/src/assets/thirdPartyServices/common/OutlinedTextArea.js @@ -12,7 +12,7 @@ class OutlinedTextArea extends React.Component { // Fixing Firefox issue at TextFields with type="number" num_onChange(event, onChange) { - if(isNaN(parseInt(event.target.value))) return; + if (isNaN(parseInt(event.target.value))) return; onChange(event); } @@ -75,8 +75,8 @@ class OutlinedTextArea extends React.Component { helperText={helperTxt ? helperTxt : null} inputProps={{ maxLength: charLimit ? charLimit : 5000, - min: typeof(min) === "number" ? min : null, - max: typeof(max) === "number" ? max : null, + min: typeof min === "number" ? min : null, + max: typeof max === "number" ? max : null, }} ref={ref} /> diff --git a/src/assets/thirdPartyServices/mozi/gene_annotation_service/form/index.js b/src/assets/thirdPartyServices/mozi/gene_annotation_service/form/index.js index bcc1291a9..a8ef2eb42 100644 --- a/src/assets/thirdPartyServices/mozi/gene_annotation_service/form/index.js +++ b/src/assets/thirdPartyServices/mozi/gene_annotation_service/form/index.js @@ -65,7 +65,7 @@ const VirusGenes = [ "NSP15", ]; -const AnnotationForm = props => { +const AnnotationForm = (props) => { const { enqueueSnackbar } = useSnackbar(); const geneInputRef = React.createRef(); const [genes, setGenes] = useState([]); @@ -79,8 +79,6 @@ const AnnotationForm = props => { const [includeCodingRNA, setIncludeCodingRNA] = useState(false); const [includeNonCodingRNA, setIncludeNonCodingRNA] = useState(false); - const [includeProtiens, setIncludeProtiens] = useState(true); - const [includeCov, setIncludeCov] = useState(true); const [loading, setLoading] = useState(false); @@ -88,13 +86,13 @@ const AnnotationForm = props => { const [geneInputMethod, setGeneInputMethod] = useState(GeneInputMethods.Manual); const [annotatePathwayWithBiogrid, setAnnotatePathwayWithBiogrid] = useState(false); - const addGene = e => { + const addGene = (e) => { const gene = e .trim() .toUpperCase() .split(" ") - .filter(g => g); - gene.some(g => genes.includes(g)) + .filter((g) => g); + gene.some((g) => genes.includes(g)) ? enqueueSnackbar("Gene already exists", { variant: "warning", anchorOrigin: { @@ -110,22 +108,22 @@ const AnnotationForm = props => { if (annotation === "biogrid-interaction-annotation" && !geneLevel && e.target.checked) { e.target.checked = false; } else { - const updated = e.target.checked ? [...annotations, annotation] : annotations.filter(a => a !== annotation); + const updated = e.target.checked ? [...annotations, annotation] : annotations.filter((a) => a !== annotation); setAnnotations(updated); } }; const toggleGoSubgroup = (subgroup, e) => { - const updated = e.target.checked ? [...GOSubgroups, subgroup] : GOSubgroups.filter(s => s !== subgroup); + const updated = e.target.checked ? [...GOSubgroups, subgroup] : GOSubgroups.filter((s) => s !== subgroup); setGOSubgroups(updated); }; const togglePathways = (subgroup, e) => { - const updated = e.target.checked ? [...pathways, subgroup] : pathways.filter(s => s !== subgroup); + const updated = e.target.checked ? [...pathways, subgroup] : pathways.filter((s) => s !== subgroup); setPathways(updated); }; - const handleFileUpload = file => { + const handleFileUpload = (file) => { const reader = new FileReader(); reader.onload = () => { setGenes(reader.result.split("\n")); @@ -144,7 +142,7 @@ const AnnotationForm = props => { setLoading(true); const annotationRequest = new AnnotationRequest(); annotationRequest.setGenesList( - genes.map(g => { + genes.map((g) => { const gene = new Gene(); gene.setGenename(g); return gene; @@ -161,11 +159,10 @@ const AnnotationForm = props => { gl.setFilter("gene-level?"); gl.setValue(capitalizeFirstLetter(geneLevel.toString())); - const annList = annotations.map(sa => { + const annList = annotations.map((sa) => { const annotation = new Annotation(); annotation.setFunctionname(sa); if (sa === "gene-go-annotation") { - const ip = new Filter(); annotation.setFiltersList([namespace, nop, gl]); } else if (sa === "gene-pathway-annotation") { const ps = new Filter(); @@ -212,7 +209,6 @@ const AnnotationForm = props => { const noncoding = new Filter(); noncoding.setFilter("noncoding"); noncoding.setValue(capitalizeFirstLetter(includeNonCodingRNA.toString())); - const protein = new Filter(); includeRNA.setFiltersList([gl, coding, noncoding]); annotationRequest.setAnnotationsList(includeCodingRNA || includeNonCodingRNA ? [...annList, includeRNA] : annList); @@ -227,9 +223,9 @@ const AnnotationForm = props => { const invalidGenes = statusMessage .split("`")[1] .split(",") - .map(g => g.trim()) - .filter(g => g); - setGenes(genes.filter(g => !invalidGenes.includes(g))); + .map((g) => g.trim()) + .filter((g) => g); + setGenes(genes.filter((g) => !invalidGenes.includes(g))); enqueueSnackbar("Gene not found!", { variant: "warning", anchorOrigin: { @@ -287,8 +283,8 @@ const AnnotationForm = props => { fullWidth value={currentGene} variant="outlined" - onChange={e => setCurrentGene(e.target.value)} - onKeyPress={e => { + onChange={(e) => setCurrentGene(e.target.value)} + onKeyPress={(e) => { if (e.key === "Enter") addGene(currentGene); }} /> @@ -296,7 +292,7 @@ const AnnotationForm = props => { {geneInputMethod === GeneInputMethods.Import && ( { + onDrop={(files) => { const file = files[0]; handleFileUpload(file); }} @@ -330,18 +326,18 @@ const AnnotationForm = props => { )}
- {genes.map(g => ( + {genes.map((g) => ( setGenes(genes.filter(f => f !== g))} + onDelete={() => setGenes(genes.filter((f) => f !== g))} /> ))} {genes.length > 1 && ( - setGenes([])} /> + setGenes([])} /> )}
@@ -353,7 +349,7 @@ const AnnotationForm = props => {
  • toggleAnnotation("gene-go-annotation", e)} />} + control={ toggleAnnotation("gene-go-annotation", e)} />} label={} /> @@ -362,7 +358,7 @@ const AnnotationForm = props => {
    - {GeneGoOptions.map(o => ( + {GeneGoOptions.map((o) => ( { color="primary" value={o.value} defaultChecked={GOSubgroups.includes(o.value)} - onChange={e => toggleGoSubgroup(o.value, e)} + onChange={(e) => toggleGoSubgroup(o.value, e)} /> } label={o.label} @@ -383,7 +379,7 @@ const AnnotationForm = props => { label="Number of parents" value={parents} error={parents === "" || isNaN(parents)} - onChange={e => setParents(e.target.value)} + onChange={(e) => setParents(e.target.value)} type="number" variant="outlined" helperText={ @@ -401,7 +397,7 @@ const AnnotationForm = props => {
  • toggleAnnotation("gene-pathway-annotation", e)} />} + control={ toggleAnnotation("gene-pathway-annotation", e)} />} label="Curated Pathways" /> @@ -413,7 +409,7 @@ const AnnotationForm = props => { togglePathways("reactome", e)} + onChange={(e) => togglePathways("reactome", e)} defaultChecked={pathways.includes("reactome")} /> } @@ -426,7 +422,7 @@ const AnnotationForm = props => { control={ setIncludeSmallMolecules(e.target.checked)} + onChange={(e) => setIncludeSmallMolecules(e.target.checked)} value="smallMolecules" color="primary" /> @@ -441,7 +437,7 @@ const AnnotationForm = props => { control={ setAnnotatePathwayWithBiogrid(e.target.checked)} + onChange={(e) => setAnnotatePathwayWithBiogrid(e.target.checked)} color="primary" /> } @@ -456,7 +452,7 @@ const AnnotationForm = props => { toggleAnnotation("biogrid-interaction-annotation", e)} /> + toggleAnnotation("biogrid-interaction-annotation", e)} /> } label="Biogrid Protien Interaction" /> @@ -469,7 +465,7 @@ const AnnotationForm = props => {
  • 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 literal 0 HcmV?d00001 diff --git a/snet-sdk-js/jsdoc.config.json b/snet-sdk-js/jsdoc.config.json new file mode 100644 index 000000000..605be4c15 --- /dev/null +++ b/snet-sdk-js/jsdoc.config.json @@ -0,0 +1,28 @@ +{ + "opts": { + "template": "templates/default", + "encoding": "utf8", + "destination": "./docs/", + "recurse": true + }, + "plugins": [], + "recurseDepth": 10, + "source": { + "include": "./src", + "exclude": [ + "./src/payment_channel_state_service_grpc_pb.js", + "./src/payment_channel_state_service_pb.js" + ], + "includePattern": ".+\\.js(doc|x)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "sourceType": "module", + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc","closure"] + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false + } +} diff --git a/snet-sdk-js/package.json b/snet-sdk-js/package.json new file mode 100644 index 000000000..c4b7b9da1 --- /dev/null +++ b/snet-sdk-js/package.json @@ -0,0 +1,32 @@ +{ + "private": true, + "name": "snet-sdk-monorepo", + "version": "0.0.0-internal", + "description": "SingularityNET SDK for JS", + "keywords": [ + "snet-sdk", + "singularitynet-sdk", + "singularitynet" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/singnet/snet-sdk-js.git" + }, + "scripts": { + "docGen": "jsdoc -c ./jsdoc.config.json", + "lint": "eslint src test", + "test": "nyc mocha --require @babel/register --colors", + "test:watch": "mocha --require @babel/register --colors -w" + }, + "author": "SingularityNET Foundation", + "license": "MIT", + "devDependencies": { + "chai": "^4.2.0", + "eslint": "^5.15.1", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-plugin-import": "^2.16.0", + "jsdoc": "^3.6.2", + "mocha": "^6.0.2", + "nyc": "^13.3.0" + } +} diff --git a/snet-sdk-js/packages/nodejs/.babelrc b/snet-sdk-js/packages/nodejs/.babelrc new file mode 100644 index 000000000..bec1f81e4 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ] + ], + "ignore": ["./src/_example"], + "exclude": ["./src/proto"] +} diff --git a/snet-sdk-js/packages/nodejs/README.md b/snet-sdk-js/packages/nodejs/README.md new file mode 100644 index 000000000..623d56bd9 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/README.md @@ -0,0 +1,127 @@ +# snet-sdk +![npm](https://img.shields.io/npm/v/snet-sdk.svg) + +SingularityNET SDK for Node.js + +## Getting Started + +These instructions are for the development and use of the SingularityNET SDK for JavaScript on Node.js platform. +### Installation +```bash +npm install snet-sdk +``` +### Usage + +The SingularityNET SDK allows you to import compiled client libraries for your service or services of choice and make calls to those services programmatically from your application by setting up state channels with the providers of those services and making gRPC calls to the SingularityNET daemons for those services by selecting a channel with sufficient funding and supplying the appropriate metadata for authentication. + +```javascript +import SnetSDK from 'snet-sdk'; +import config from './config'; +const sdk = new SnetSDK(config); +``` + +You can find a sample config below + +```json +{ + "web3Provider": "https://ropsten.infura.io/v3/1234567890", + "privateKey": "", + "signerPrivateKey": "", + "networkId": "3", + "ipfsEndpoint": "http://ipfs.organization.io:80", + "defaultGasPrice": "4700000", + "defaultGasLimit": "210000" +} +``` + +Now, the instance of the sdk can be used to instantiate clients for SingularityNET services. To interact with those services, the sdk needs to be supplied with the compiled gRPC client libraries. + +To generate the gRPC client libraries, you need the SingularityNET Command Line Interface, or CLI, which you can download from PyPi, see [https://github.com/singnet/snet-cli#installing-with-pip](https://github.com/singnet/snet-cli#installing-with-pip) + +Once you have the CLI installed, run the following command: +```bash +$ snet sdk generate-client-library nodejs +``` +Optionally, you can specify an output path; otherwise it's going to be `./client_libraries/nodejs///` +Once you have the generated gRPC client libraries, you can create an instance of a SingularityNET service client: +```javascript +import services from '' +import messages from '' +const client = sdk.createServiceClient("", "", ">") +``` +This generates a service client which can be used to make gRPC calls to the desired service. +You can then invoke service specific calls as follows +```javascript +client.service.(, callback); +``` +--- + +### Concurrency +SDK exposes two methods to facilitate concurrent service calls. + - getConcurrencyTokenAndChannelId + - setConcurrencyTokenAndChannelId + + In the consumer, you should call the getConcurrencyTokenAndChannelId() in the master thread. + It will return the concurrency token and the channel id. + Pass both of them to worker threads and the set the same in the respective instances using setConcurrencyTokenAndChannelId. + + SDK also exposes the `class DefaultPaymentStrategy` to handle the payment metadata for concurrent calls. + Initialize the DefaultPaymentStrategy with the number of calls you would want to run concurrently. + + e.g + ``` +import SnetSDK, { DefaultPaymentStrategy } from "snet-sdk"; +const sdk = new SnetSDK(config); +import cluster from "cluster"; + +const main = async () => { +... +// Planning for four concurrent calls +const paymentStrategy = new DefaultPaymentStrategy(4); +const serviceClient = await sdk.createServiceClient( + orgId, + serviceId, + service.CalculatorClient, + groupName, + paymentStrategy, + serviceClientOptionsFreeCall + ); + +if(cluster.isMaster) { + const {concurrencyToken, channelId} = await serviceClient.getConcurrencyTokenAndChannelId() + const worker = cluster.fork() + worker.on("message", message=>{ + console.log(`worker:${worker.id}, message:${message}`) + worker.send({concurrencyToken,channelId, info:"master: sent you the concurrency token and channel id"}) + }) +}else { + process.send(`send me the token for concurrency`); + process.on("message", async (message) => { + const { concurrencyToken, info,channelId } = message; + console.log(info); + serviceClient.setConcurrencyTokenAndChannelId(concurrencyToken,channelId) + const numbers = new messages.Numbers(); + numbers.setA(6); + numbers.setB(7); + serviceClient.service.mul(numbers, (err, result)=>{ + if(err) { + console.error(`service failed with error ${err}`) + }else{ + console.log(`service response is ${result}`) + } + }); + }); +} +main() +``` + + +### Versioning + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the +[tags on this repository](https://github.com/singnet/snet-sdk-js/tags). + +## License + +This project is licensed under the MIT License - see the +[LICENSE](https://github.com/singnet/snet-sdk-js/blob/master/LICENSE) file for details. diff --git a/snet-sdk-js/packages/nodejs/package.json b/snet-sdk-js/packages/nodejs/package.json new file mode 100644 index 000000000..52b81e054 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/package.json @@ -0,0 +1,54 @@ +{ + "name": "snet-sdk", + "version": "2.0.0-beta.0", + "description": "SingularityNET SDK for Nodejs", + "main": "./dist/index.js", + "files": [ + "dist" + ], + "keywords": [ + "snet-sdk", + "singularitynet-sdk", + "singularitynet", + "nodejs" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/singnet/snet-sdk-js.git", + "directory": "packages/nodejs" + }, + "homepage": "https://github.com/singnet/snet-sdk-js/packages/nodejs", + "scripts": { + "build": "babel src --out-dir dist", + "clean": "rimraf dist", + "clean:build": "npm run clean && npm run build", + "prepublishOnly": "npm run clean && npm run build" + }, + "author": "SingularityNET Foundation", + "license": "MIT", + "dependencies": { + "@ethereumjs/tx": "^5.2.1", + "@grpc/grpc-js": "^1.10.0", + "axios": "^1.6.7", + "bignumber.js": "^9.1.2", + "es6-promise": "^4.2.8", + "lodash": "latest", + "singularitynet-platform-contracts": "^1.0.4", + "singularitynet-token-contracts": "^3.0.3", + "web3": "^4.4.0", + "winston": "^3.11.0" + }, + "peerDependencies": { + "google-protobuf": "^3.8.0" + }, + "devDependencies": { + "@babel/cli": "^7.23.9", + "@babel/core": "^7.23.9", + "@babel/node": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/register": "^7.23.7", + "google-protobuf": "^3.21.2", + "rimraf": "^5.0.5", + "verdaccio": "^5.29.2" + } +} diff --git a/snet-sdk-js/packages/nodejs/src/ConcurrencyManager.js b/snet-sdk-js/packages/nodejs/src/ConcurrencyManager.js new file mode 100644 index 000000000..6825b4381 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/ConcurrencyManager.js @@ -0,0 +1,146 @@ +import * as grpc from '@grpc/grpc-js'; +import { logger } from './core'; +import services from './proto/token_service_grpc_pb'; +import { toBNString } from './core/src/utils/bignumber_helper'; + +class ConcurrencyManager { + constructor(concurrentCalls = 1, serviceClient) { + this._concurrentCalls = concurrentCalls; + this._serviceClient = serviceClient; + this._tokenServiceClient = this._generateTokenServiceClient(); + this._token = ''; + this._usedAmount = 0; + this._plannedAmount = 0; + } + + get concurrentCalls() { + return this._concurrentCalls; + } + + set token(value) { + this._token = value; + } + + // set serviceClient(value) { + // this._serviceClient = value; + // } + + async getToken(channel, serviceCallPrice) { + if(this._token) { + return this._token; + } + const currentSignedAmount = channel.state.currentSignedAmount.toNumber(); + if(currentSignedAmount !== 0) { + const { plannedAmount, usedAmount, token } = await this._getTokenForAmount(channel, currentSignedAmount); + if(usedAmount < plannedAmount) { + return token; + } + } + const newAmountToBeSigned = currentSignedAmount + serviceCallPrice; + return this._getNewToken(channel, newAmountToBeSigned); + } + + /** + * @param {ServiceClient} serviceClient + * @param {PaymentChannel} channel + * @param {number} amount + * @returns {Promise} token + * @private + */ + async _getNewToken(channel, amount) { + const tokenResponse = await this._getTokenForAmount(channel, amount); + const { token } = tokenResponse; + return token; + } + + async _getTokenServiceRequest(channel, amount) { + const { nonce } = channel.state; + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + + const mpeSignature = await this._generateMpeSignature(parseInt(channel.channelId, 10), parseInt(nonce, 10), amount); + const tokenSignature = await this._generateTokenSignature(mpeSignature, currentBlockNumber); + const Request = this._tokenServiceClient.getToken.requestType; + const request = new Request(); + + request.setChannelId(parseInt(channel.channelId, 10)); + request.setCurrentNonce(parseInt(nonce, 10)); + request.setSignedAmount(amount); + request.setSignature(tokenSignature); + request.setCurrentBlock(toBNString(currentBlockNumber)); + request.setClaimSignature(mpeSignature); + return request; + } + + /** + * Get token for the given amount + * @param {ServiceClient} serviceClient + * @param {PaymentChannel} channel + * @param {number} amount + * @returns {Promise} token + * @private + */ + async _getTokenForAmount(channel, amount) { + const request = await this._getTokenServiceRequest(channel, amount); + return new Promise((resolve, reject) => { + this._tokenServiceClient.getToken(request, (error, responseMessage) => { + if(error) { + console.log('token grpc error', error); + reject(error); + } else { + this._plannedAmount = responseMessage.getPlannedAmount(); + this._usedAmount = responseMessage.getUsedAmount(); + this._token = responseMessage.getToken(); + resolve({ + plannedAmount: this._plannedAmount, + usedAmount: this._usedAmount, + token: this._token, + }); + } + }); + }); + } + + async _generateTokenSignature(mpeSignature, currentBlockNumber) { + const mpeSignatureHex = mpeSignature.toString('hex'); + return this._serviceClient.signData({ t: "bytes", v: mpeSignatureHex }, { t: "uint256", v: currentBlockNumber }); + } + + async _generateMpeSignature(channelId, nonce, signedAmount) { + return this._serviceClient.signData( + { t: 'string', v: '__MPE_claim_message' }, + { t: 'address', v: this._serviceClient.mpeContract.address }, + { t: 'uint256', v: channelId }, + { t: 'uint256', v: nonce }, + { t: 'uint256', v: signedAmount }, + ); + } + + _generateTokenServiceClient() { + const serviceEndpoint = this._serviceClient._getServiceEndpoint(); + const grpcCredentials = this._getGrpcCredentials(serviceEndpoint); + return new services.TokenServiceClient(serviceEndpoint.host, grpcCredentials); + } + + /** + * generate options for the grpc call for respective protocol + * @param {{host:String, protocol:String}} serviceEndpoint + * @returns {*} grpcOptions + * @private + */ + _getGrpcCredentials(serviceEndpoint) { + if(serviceEndpoint.protocol === 'https:') { + logger.debug('Channel credential created for https', { tags: ['gRPC'] }); + return grpc.credentials.createSsl(); + } + if(serviceEndpoint.protocol === 'http:') { + logger.debug('Channel credential created for http', { tags: ['gRPC'] }); + return grpc.credentials.createInsecure(); + } + + const errorMessage = `Protocol: ${serviceEndpoint.protocol} not supported`; + logger.error(errorMessage, { tags: ['gRPC'] }); + throw new Error(errorMessage); + } +} + +export default ConcurrencyManager; diff --git a/snet-sdk-js/packages/nodejs/src/NodeSdk.js b/snet-sdk-js/packages/nodejs/src/NodeSdk.js new file mode 100644 index 000000000..391472986 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/NodeSdk.js @@ -0,0 +1,46 @@ +import SnetSDK, { PrivateKeyIdentity } from './sdk-core'; +import ServiceClient from './ServiceClient'; + +class NodeSdk extends SnetSDK { + /** + * @param {string} orgId + * @param {string} serviceId + * @param {GRPCClient} ServiceStub GRPC service client constructor + * @param {string} [groupName] + * @param {PaymentChannelManagementStrategy} [paymentChannelManagementStrategy=DefaultPaymentChannelManagementStrategy] + * @param {ServiceClientOptions} [options] + * @param {number} concurrentCalls + * @returns {Promise} + */ + async createServiceClient( + orgId, + serviceId, + ServiceStub, + groupName = null, + paymentChannelManagementStrategy = null, + options = {}, + concurrentCalls = 1, + ) { + const serviceMetadata = await this._metadataProvider.metadata(orgId, serviceId); + const group = await this._serviceGroup(serviceMetadata, orgId, serviceId, groupName); + const paymentStrategy = this._constructStrategy(paymentChannelManagementStrategy, concurrentCalls || 1); + const serviceClient = new ServiceClient( + this, + orgId, + serviceId, + this._mpeContract, + serviceMetadata, + group, + ServiceStub, + paymentStrategy, + options, + ); + return serviceClient; + } + + _createIdentity() { + return new PrivateKeyIdentity(this._config, this._web3); + } +} + +export default NodeSdk; diff --git a/snet-sdk-js/packages/nodejs/src/ServiceClient.js b/snet-sdk-js/packages/nodejs/src/ServiceClient.js new file mode 100644 index 000000000..971339ace --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/ServiceClient.js @@ -0,0 +1,123 @@ +import * as grpc from '@grpc/grpc-js'; +import { BaseServiceClient, logger } from './sdk-core'; +import { PaymentChannelStateServiceClient } from './proto/state_service_grpc_pb'; +import ConcurrencyManager from './ConcurrencyManager'; + +class ServiceClient extends BaseServiceClient { + /** + * @param {SnetSDK} sdk + * @param {String} orgId + * @param {String} serviceId + * @param {MPEContract} mpeContract + * @param {ServiceMetadata} metadata + * @param {Group} group + * @param {GRPCClient} ServiceStub - GRPC service client constructor + * @param {DefaultPaymentChannelManagementStrategy} paymentChannelManagementStrategy + * @param {ServiceClientOptions} [options={}] + */ + constructor( + sdk, + orgId, + serviceId, + mpeContract, + metadata, + group, + ServiceStub, + paymentChannelManagementStrategy, + options, + ) { + super(sdk, orgId, serviceId, mpeContract, metadata, group, paymentChannelManagementStrategy, options); + this._grpcService = this._constructGrpcService(ServiceStub); + this._concurrencyManager = new ConcurrencyManager(paymentChannelManagementStrategy.concurrentCalls || 1, this); + } + + /** + * @type {GRPCClient} + */ + get service() { + return this._grpcService; + } + + get concurrencyManager() { + return this._concurrencyManager; + } + + async getConcurrencyTokenAndChannelId() { + return this._paymentChannelManagementStrategy.getConcurrencyTokenAndChannelId(this); + } + + setConcurrencyTokenAndChannelId(token, channelId) { + this.concurrencyManager.token = token; + this._paymentChannelManagementStrategy.channelId = channelId; + } + + _getChannelStateRequestMethodDescriptor() { + return this.paymentChannelStateServiceClient.getChannelState.requestType; + } + + _constructGrpcService(ServiceStub) { + logger.debug('Creating service client', { tags: ['gRPC'] }); + const serviceEndpoint = this._getServiceEndpoint(); + const grpcChannelCredentials = this._getGrpcChannelCredentials(serviceEndpoint); + const grpcOptions = this._generateGrpcOptions(); + logger.debug(`Service pointing to ${serviceEndpoint.host}, `, { tags: ['gRPC'] }); + return new ServiceStub(serviceEndpoint.host, grpcChannelCredentials, grpcOptions); + } + + _generateGrpcOptions() { + if(this._options.disableBlockchainOperations) { + return {}; + } + + return { + interceptors: [this._generateInterceptor()], + }; + } + + _generateInterceptor() { + return (options, nextCall) => { + const requester = { + start: async (metadata, listener, next) => { + if(!this._paymentChannelManagementStrategy) { + next(metadata, listener); + return; + } + const paymentMetadata = await this._fetchPaymentMetadata(); + paymentMetadata.forEach((paymentMeta) => { + Object.entries(paymentMeta).forEach(([key, value]) => { + metadata.add(key, value); + }); + }); + next(metadata, listener); + }, + }; + return new grpc.InterceptingCall(nextCall(options), requester); + }; + } + + _generatePaymentChannelStateServiceClient() { + logger.debug('Creating PaymentChannelStateService client', { tags: ['gRPC'] }); + const serviceEndpoint = this._getServiceEndpoint(); + const grpcChannelCredentials = this._getGrpcChannelCredentials(serviceEndpoint); + logger.debug(`PaymentChannelStateService pointing to ${serviceEndpoint.host}, `, { tags: ['gRPC'] }); + return new PaymentChannelStateServiceClient(serviceEndpoint.host, grpcChannelCredentials); + } + + _getGrpcChannelCredentials(serviceEndpoint) { + if(serviceEndpoint.protocol === 'https:') { + logger.debug('Channel credential created for https', { tags: ['gRPC'] }); + return grpc.credentials.createSsl(); + } + + if(serviceEndpoint.protocol === 'http:') { + logger.debug('Channel credential created for http', { tags: ['gRPC'] }); + return grpc.credentials.createInsecure(); + } + + const errorMessage = `Protocol: ${serviceEndpoint.protocol} not supported`; + logger.error(errorMessage, { tags: ['gRPC'] }); + throw new Error(errorMessage); + } +} + +export default ServiceClient; diff --git a/snet-sdk-js/packages/nodejs/src/core b/snet-sdk-js/packages/nodejs/src/core new file mode 100644 index 000000000..3d25ddeb2 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/core @@ -0,0 +1 @@ +../../core \ No newline at end of file diff --git a/snet-sdk-js/packages/nodejs/src/index.js b/snet-sdk-js/packages/nodejs/src/index.js new file mode 100644 index 000000000..8dcb79892 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/index.js @@ -0,0 +1,6 @@ +import './utils/logger'; +import NodeSdk from './NodeSdk'; + +export default NodeSdk; +export * from './sdk-core'; +export * from './payment_strategies'; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/BasePaidPaymentStrategy.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/BasePaidPaymentStrategy.js new file mode 100644 index 000000000..ee812b78f --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/BasePaidPaymentStrategy.js @@ -0,0 +1,85 @@ +import { logger } from '../sdk-core'; + +class BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, blockOffset = 240, callAllowance = 1) { + this._serviceClient = serviceClient; + this._blockOffset = blockOffset; + this._callAllowance = callAllowance; + } + + /** + * @returns {Promise} + * @protected + */ + async _selectChannel(preselectChannelId) { + const { account } = this._serviceClient; + await this._serviceClient.loadOpenChannels(); + await this._serviceClient.updateChannelStates(); + const { paymentChannels } = this._serviceClient; + const serviceCallPrice = this._getPrice(); + const extendedChannelFund = serviceCallPrice * this._callAllowance; + const mpeBalance = await account.escrowBalance(); + const defaultExpiration = await this._serviceClient.defaultChannelExpiration(); + const extendedExpiry = defaultExpiration + this._blockOffset; + + if(preselectChannelId) { + const foundPreselectChannel = paymentChannels.find((el) => el.channelId === preselectChannelId); + if(foundPreselectChannel) { + return foundPreselectChannel; + } + } + + let selectedPaymentChannel; + if(paymentChannels.length < 1) { + if(serviceCallPrice > mpeBalance) { + selectedPaymentChannel = await this._serviceClient.depositAndOpenChannel(serviceCallPrice, extendedExpiry); + } else { + selectedPaymentChannel = await this._serviceClient.openChannel(serviceCallPrice, extendedExpiry); + } + } else { + selectedPaymentChannel = paymentChannels[0]; + } + const hasSufficientFunds = this._doesChannelHaveSufficientFunds(selectedPaymentChannel, serviceCallPrice); + const isValid = this._isValidChannel(selectedPaymentChannel, defaultExpiration); + if(hasSufficientFunds && !isValid) { + await selectedPaymentChannel.extendExpiry(extendedExpiry); + } else if(!hasSufficientFunds && isValid) { + await selectedPaymentChannel.addFunds(extendedChannelFund); + } else if(!hasSufficientFunds && !isValid) { + await selectedPaymentChannel.extendAndAddFunds(extendedExpiry, extendedChannelFund); + } + return selectedPaymentChannel; + } + + _getPrice() { + logger.error('_getPrice must be implemented in the sub classes'); + } + + /** + * @param {PaymentChannel} channel + * @param {number} requiredAmount + * @returns {boolean} + * @private + */ + _doesChannelHaveSufficientFunds(channel, requiredAmount) { + return channel.state.availableAmount >= requiredAmount; + } + + /** + * + * @param {PaymentChannel} channel + * @param {number} expiry + * @returns {boolean} + * @private + */ + _isValidChannel(channel, expiry) { + return channel.state.expiry >= expiry; + } +} + +export default BasePaidPaymentStrategy; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/DefaultPaymentStrategy.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/DefaultPaymentStrategy.js new file mode 100644 index 000000000..04e9fddc9 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/DefaultPaymentStrategy.js @@ -0,0 +1,58 @@ +import FreeCallPaymentStrategy from './FreeCallPaymentStrategy'; +import PrepaidPaymentStrategy from './PrepaidPaymentStrategy'; +import PaidCallPaymentStrategy from './PaidCallPaymentStrategy'; + +class DefaultPaymentStrategy { + /** + * Initializing the payment strategy + * @param {number} concurrentCalls + */ + constructor(concurrentCalls = 1) { + this._concurrentCalls = concurrentCalls; + this._channelId = undefined; + } + + get concurrentCalls() { + return this._concurrentCalls; + } + + set channelId(value) { + this._channelId = value; + } + + /** + * map the metadata for the gRPC call + * @param {BaseServiceClient} serviceClient + * @returns {Promise<({'snet-payment-type': string}|{'snet-payment-channel-id': string}|{'snet-payment-channel-nonce': string}|{'snet-payment-channel-amount': string}|{'snet-payment-channel-signature-bin': Buffer})[]>} + */ + async getPaymentMetadata(serviceClient) { + const freeCallPaymentStrategy = new FreeCallPaymentStrategy(serviceClient); + const isFreeCallsAvailable = await freeCallPaymentStrategy.isFreeCallAvailable(); + let metadata; + if(isFreeCallsAvailable) { + metadata = await freeCallPaymentStrategy.getPaymentMetadata(); + } else if(serviceClient.concurrencyFlag) { + const paymentStrategy = new PrepaidPaymentStrategy(serviceClient); + metadata = await paymentStrategy.getPaymentMetadata(this._channelId); + } else { + const paymentStrategy = new PaidCallPaymentStrategy(serviceClient); + metadata = await paymentStrategy.getPaymentMetadata(); + } + + return metadata; + } + + /** + * retrieve the concurrency token and the channelID from the daemon + * @param {ServiceClient} serviceClient + * @returns {Promise<{channelId: BigNumber, concurrencyToken: String}>} + */ + async getConcurrencyTokenAndChannelId(serviceClient) { + const paymentStrategy = new PrepaidPaymentStrategy(serviceClient); + const channel = await paymentStrategy._selectChannel(); + const concurrencyToken = await paymentStrategy.getConcurrencyToken(channel); + return { channelId: channel.channelId, concurrencyToken }; + } +} + +export default DefaultPaymentStrategy; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/FreeCallPaymentStrategy.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/FreeCallPaymentStrategy.js new file mode 100644 index 000000000..ce11dee87 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/FreeCallPaymentStrategy.js @@ -0,0 +1,173 @@ +import * as grpc from '@grpc/grpc-js'; +import services from "../proto/state_service_grpc_pb"; +import { logger, EncodingUtils } from "../sdk-core"; +import { toBNString } from "../core/src/utils/bignumber_helper"; + +class FreeCallPaymentStrategy { + constructor(serviceClient) { + this._serviceClient = serviceClient; + this._freeCallStateServiceClient = this._generateFreeCallStateServiceClient(); + this._encodingUtils = new EncodingUtils(); + } + + /** + * Check if there is any freecalls left for x service. + * @returns {Promise} + */ + async isFreeCallAvailable() { + try { + const freeCallsAvailableReply = await this._getFreeCallsAvailable(); + // Bypassing free calls if the token is empty + const freeCallsAvailable = freeCallsAvailableReply ? freeCallsAvailableReply.getFreeCallsAvailable() : 0; + console.log("freeCallsAvailable", freeCallsAvailable); + return freeCallsAvailable > 0; + } catch (error) { + console.log("error", error); + return false; + } + } + + /** + * generate free call payment metadata + * @returns {Promise<({'snet-free-call-auth-token-bin': FreeCallConfig.tokenToMakeFreeCall}|{'snet-free-call-token-expiry-block': *}|{'snet-payment-type': string}|{'snet-free-call-user-id': *}|{'snet-current-block-number': *})[]>} + */ + async getPaymentMetadata() { + console.log("get payment metadat"); + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + const signature = await this._generateSignature(currentBlockNumber); + const tokenBytes = this._encodingUtils.hexStringToBytes(tokenToMakeFreeCall); + const metadata = [ + { "snet-free-call-auth-token-bin": tokenBytes }, + { "snet-free-call-token-expiry-block": `${tokenExpiryDateBlock}` }, + { "snet-payment-type": "free-call" }, + { "snet-free-call-user-id": email }, + { "snet-current-block-number": `${currentBlockNumber}` }, + { "snet-payment-channel-signature-bin": signature }, + ]; + + return metadata; + } + + /** + * fetch the free calls available data from daemon + * @returns {Promise} + * @private + */ + async _getFreeCallsAvailable() { + const freeCallStateRequest = await this._getFreeCallStateRequest(); + if (!freeCallStateRequest) { + // Bypassing free calls if the token is empty + return undefined; + } + return new Promise((resolve, reject) => { + this._freeCallStateServiceClient.getFreeCallsAvailable(freeCallStateRequest, (error, responseMessage) => { + if (error) { + console.log("freecalls error", error); + reject(error); + } else { + resolve(responseMessage); + } + }); + }); + } + + /** + * + * @returns {Promise>>} + * @private + */ + async _generateSignature(currentBlockNumber) { + const { orgId, serviceId, groupId } = this._serviceClient.getServiceDetails(); + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + if (tokenExpiryDateBlock === 0 || !email || email.length === 0) { + throw Error("invalid entries"); + } + const enhancedToken = /^0x/.test(tokenToMakeFreeCall.toLowerCase()) + ? tokenToMakeFreeCall.substring(2, tokenToMakeFreeCall.length) + : tokenToMakeFreeCall; + return this._serviceClient.signData( + { t: "string", v: "__prefix_free_trial" }, + { t: "string", v: email }, + { t: "string", v: orgId }, + { t: "string", v: serviceId }, + { t: "string", v: groupId }, + { t: "uint256", v: currentBlockNumber }, + { t: "bytes", v: enhancedToken } + ); + } + + /** + * create the request for the freecall state service grpc + * @returns {FreeCallStateRequest} + * @private + */ + async _getFreeCallStateRequest() { + const Request = this._freeCallStateServiceClient.getFreeCallsAvailable.requestType; + const request = new Request(); + + const { + userId, tokenForFreeCall, tokenExpiryDateBlock, signature, currentBlockNumber, + await this._getFreeCallStateRequestProperties(); + + // if the token for freecall is empty, then user is taken to paid call directly + if (!tokenForFreeCall) { + return undefined; + } + + const tokenBytes = this._encodingUtils.hexStringToBytes(tokenForFreeCall); + request.setUserId(userId); + request.setTokenForFreeCall(tokenBytes); + request.setTokenExpiryDateBlock(tokenExpiryDateBlock); + request.setSignature(signature); + request.setCurrentBlock(currentBlockNumber); + return request; + } + + async _getFreeCallStateRequestProperties() { + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + const signature = await this._generateSignature(currentBlockNumber); + return { + userId: email, + tokenForFreeCall: tokenToMakeFreeCall, + tokenExpiryDateBlock, + signature, + currentBlockNumber: toBNString(currentBlockNumber), + }; + } + + /** + * create the grpc client for free call state service + * @returns {module:grpc.Client} + * @private + */ + _generateFreeCallStateServiceClient() { + const serviceEndpoint = this._serviceClient._getServiceEndpoint(); + const grpcCredentials = this._getGrpcCredentials(serviceEndpoint); + return new services.FreeCallStateServiceClient(serviceEndpoint.host, grpcCredentials); + } + + /** + * generate options for the grpc call for respective protocol + * @param {{host:String, protocol:String}} serviceEndpoint + * @returns {*} grpcOptions + * @private + */ + _getGrpcCredentials(serviceEndpoint) { + if (serviceEndpoint.protocol === "https:") { + logger.debug("Channel credential created for https", { tags: ["gRPC"] }); + return grpc.credentials.createSsl(); + } + if (serviceEndpoint.protocol === "http:") { + logger.debug("Channel credential created for http", { tags: ["gRPC"] }); + return grpc.credentials.createInsecure(); + } + + const errorMessage = `Protocol: ${serviceEndpoint.protocol} not supported`; + logger.error(errorMessage, { tags: ["gRPC"] }); + throw new Error(errorMessage); + } +} + +export default FreeCallPaymentStrategy; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/PaidCallPaymentStrategy.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/PaidCallPaymentStrategy.js new file mode 100644 index 000000000..649429f08 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/PaidCallPaymentStrategy.js @@ -0,0 +1,52 @@ +import BasePaidPaymentStrategy from './BasePaidPaymentStrategy'; + +class PaidCallPaymentStrategy extends BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, blockOffset = 240, callAllowance = 1) { + super(serviceClient, blockOffset, callAllowance); + } + + /** + * @returns {Promise<[{'snet-payment-type': string}, {'snet-payment-channel-id': string}, {'snet-payment-channel-nonce': string}, {'snet-payment-channel-amount': string}, {'snet-payment-channel-signature-bin': Buffer}]>} + */ + async getPaymentMetadata() { + const channel = await this._selectChannel(); + const amount = channel.state.currentSignedAmount.toNumber() + this._getPrice(); + const signature = await this._generateSignature(channel.channelId, channel.state.nonce, amount); + + const metadata = [ + { 'snet-payment-type': 'escrow' }, + { 'snet-payment-channel-id': `${channel.channelId}` }, + { 'snet-payment-channel-nonce': `${channel.state.nonce}` }, + { 'snet-payment-channel-amount': `${amount}` }, + { 'snet-payment-channel-signature-bin': signature }, + ]; + + return metadata; + } + + async _generateSignature(channelId, nonce, amount) { + return this._serviceClient.signData( + { t: 'string', v: '__MPE_claim_message' }, + { t: 'address', v: this._serviceClient.mpeContract.address }, + { t: 'uint256', v: channelId }, + { t: 'uint256', v: nonce }, + { t: 'uint256', v: amount }, + ); + } + + /** + * total price for all the service calls + * @returns {number} + * @private + */ + _getPrice() { + return this._serviceClient._pricePerServiceCall.toNumber(); + } +} + +export default PaidCallPaymentStrategy; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/PrepaidPaymentStrategy.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/PrepaidPaymentStrategy.js new file mode 100644 index 000000000..e0c8a06f1 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/PrepaidPaymentStrategy.js @@ -0,0 +1,53 @@ +import BasePaidPaymentStrategy from './BasePaidPaymentStrategy'; +import { EncodingUtils } from '../core'; + +class PrepaidPaymentStrategy extends BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {ConcurrencyManager} concurrencyManager + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, blockOffset = 240, callAllowance = 1) { + super(serviceClient, blockOffset, callAllowance); + this._encodingUtils = new EncodingUtils(); + this._concurrencyManager = serviceClient.concurrencyManager; + } + + /** + * @returns {Promise<[{'snet-payment-type': string}, {'snet-payment-channel-id': string}, {'snet-payment-channel-nonce': string}, {'snet-prepaid-auth-token-bin': *}]>} + */ + async getPaymentMetadata(preselectChannelId) { + const channel = await this._selectChannel(preselectChannelId); + const concurrentCallsPrice = this._getPrice(); + const token = await this._concurrencyManager.getToken(channel, concurrentCallsPrice); + const tokenBytes = this._encodingUtils.utfStringToBytes(token); + const metadata = [ + { 'snet-payment-type': 'prepaid-call' }, + { 'snet-payment-channel-id': `${channel.channelId}` }, + { 'snet-payment-channel-nonce': `${channel.state.nonce}` }, + { 'snet-prepaid-auth-token-bin': tokenBytes }, + ]; + return metadata; + } + + /** + * @returns {Promise} concurrencyToken + */ + async getConcurrencyToken(channel) { + const concurrentCallsPrice = this._getPrice(); + const token = await this._concurrencyManager.getToken(channel, concurrentCallsPrice); + return token; + } + + /** + * total price for all the service calls + * @returns {number} + * @private + */ + _getPrice() { + return this._serviceClient._pricePerServiceCall.toNumber() * this._concurrencyManager.concurrentCalls; + } +} + +export default PrepaidPaymentStrategy; diff --git a/snet-sdk-js/packages/nodejs/src/payment_strategies/index.js b/snet-sdk-js/packages/nodejs/src/payment_strategies/index.js new file mode 100644 index 000000000..5c16ab83a --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/payment_strategies/index.js @@ -0,0 +1 @@ +export { default as DefaultPaymentStrategy } from "./DefaultPaymentStrategy"; diff --git a/snet-sdk-js/packages/nodejs/src/proto/state_service.proto b/snet-sdk-js/packages/nodejs/src/proto/state_service.proto new file mode 100644 index 000000000..aa45e53cf --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/state_service.proto @@ -0,0 +1,92 @@ +syntax = "proto3"; + +package escrow; + +option java_package = "io.singularitynet.daemon.escrow"; + +// PaymentChannelStateService contains methods to get the MultiPartyEscrow +// payment channel state. +// channel_id, channel_nonce, value and amount fields below in fact are +// Solidity uint256 values. Which are big-endian integers, see +// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#formal-specification-of-the-encoding +// These values may be or may be not padded by zeros, service supports both +// options. +service PaymentChannelStateService { + // GetChannelState method returns a channel state by channel id. + rpc GetChannelState(ChannelStateRequest) returns (ChannelStateReply) {} +} + +// ChanelStateRequest is a request for channel state. +message ChannelStateRequest { + // channel_id contains id of the channel which state is requested. + bytes channel_id = 1; + + // signature is a client signature of the message which contains + // channel_id. It is used for client authorization. + bytes signature = 2; + + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 3; +} + +// ChannelStateReply message contains a latest channel state. current_nonce and +// current_value fields can be different from ones stored in the blockchain if +// server started withdrawing funds froms channel but transaction is still not +// finished. +message ChannelStateReply { + // current_nonce is a latest nonce of the payment channel. + bytes current_nonce = 1; + + // current_signed_amount is a last amount which were signed by client with current_nonce + //it could be absent if none message was signed with current_nonce + bytes current_signed_amount = 2; + + // current_signature is a last signature sent by client with current_nonce + // it could be absent if none message was signed with current nonce + bytes current_signature = 3; + + // last amount which was signed by client with nonce=current_nonce - 1 + bytes old_nonce_signed_amount = 4; + + // last signature sent by client with nonce = current_nonce - 1 + bytes old_nonce_signature = 5; + + //If the client / user chooses to sign upfront , the planned amount in cogs will be indicative of this. + //For pay per use, this will be zero + uint64 planned_amount = 6; + + //If the client / user chooses to sign upfront , the usage amount in cogs will be indicative of how much of the + //planned amount has actually been used. + //For pay per use, this will be zero + uint64 used_amount = 7; + + } + +//Used to determine free calls available for a given user. +service FreeCallStateService { + rpc GetFreeCallsAvailable(FreeCallStateRequest) returns (FreeCallStateReply) {} +} + +message FreeCallStateRequest { + //Has the user email id + string user_id = 1; + //signer-token = (user@mail, user-public-key, token_issue_date), this is generated my Market place Dapp + //to leverage free calls from SDK/ snet-cli, you will need this signer-token to be downloaded from Dapp + bytes token_for_free_call = 2; + //Token expiration date in Block number + uint64 token_expiry_date_block = 3 ; + //Signature is made up of the below, user signs with the private key corresponding with the public key used to generate the authorized token + //free-call-metadata = ("__prefix_free_trial",user_id,organization_id,service_id,group_id,current_block,authorized_token) + bytes signature = 4; + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 5; + +} + +message FreeCallStateReply { + //Has the user email id + string user_id = 1; + //Balance number of free calls available + uint64 free_calls_available = 2; +} + diff --git a/snet-sdk-js/packages/nodejs/src/proto/state_service_grpc_pb.js b/snet-sdk-js/packages/nodejs/src/proto/state_service_grpc_pb.js new file mode 100644 index 000000000..b7719cffa --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/state_service_grpc_pb.js @@ -0,0 +1,90 @@ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +'use strict'; +var grpc = require('@grpc/grpc-js'); +var state_service_pb = require('./state_service_pb.js'); + +function serialize_escrow_ChannelStateReply(arg) { + if (!(arg instanceof state_service_pb.ChannelStateReply)) { + throw new Error('Expected argument of type escrow.ChannelStateReply'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_ChannelStateReply(buffer_arg) { + return state_service_pb.ChannelStateReply.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_escrow_ChannelStateRequest(arg) { + if (!(arg instanceof state_service_pb.ChannelStateRequest)) { + throw new Error('Expected argument of type escrow.ChannelStateRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_ChannelStateRequest(buffer_arg) { + return state_service_pb.ChannelStateRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_escrow_FreeCallStateReply(arg) { + if (!(arg instanceof state_service_pb.FreeCallStateReply)) { + throw new Error('Expected argument of type escrow.FreeCallStateReply'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_FreeCallStateReply(buffer_arg) { + return state_service_pb.FreeCallStateReply.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_escrow_FreeCallStateRequest(arg) { + if (!(arg instanceof state_service_pb.FreeCallStateRequest)) { + throw new Error('Expected argument of type escrow.FreeCallStateRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_FreeCallStateRequest(buffer_arg) { + return state_service_pb.FreeCallStateRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +// PaymentChannelStateService contains methods to get the MultiPartyEscrow +// payment channel state. +// channel_id, channel_nonce, value and amount fields below in fact are +// Solidity uint256 values. Which are big-endian integers, see +// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#formal-specification-of-the-encoding +// These values may be or may be not padded by zeros, service supports both +// options. +var PaymentChannelStateServiceService = exports.PaymentChannelStateServiceService = { + // GetChannelState method returns a channel state by channel id. +getChannelState: { + path: '/escrow.PaymentChannelStateService/GetChannelState', + requestStream: false, + responseStream: false, + requestType: state_service_pb.ChannelStateRequest, + responseType: state_service_pb.ChannelStateReply, + requestSerialize: serialize_escrow_ChannelStateRequest, + requestDeserialize: deserialize_escrow_ChannelStateRequest, + responseSerialize: serialize_escrow_ChannelStateReply, + responseDeserialize: deserialize_escrow_ChannelStateReply, + }, +}; + +exports.PaymentChannelStateServiceClient = grpc.makeGenericClientConstructor(PaymentChannelStateServiceService); +// Used to determine free calls available for a given user. +var FreeCallStateServiceService = exports.FreeCallStateServiceService = { + getFreeCallsAvailable: { + path: '/escrow.FreeCallStateService/GetFreeCallsAvailable', + requestStream: false, + responseStream: false, + requestType: state_service_pb.FreeCallStateRequest, + responseType: state_service_pb.FreeCallStateReply, + requestSerialize: serialize_escrow_FreeCallStateRequest, + requestDeserialize: deserialize_escrow_FreeCallStateRequest, + responseSerialize: serialize_escrow_FreeCallStateReply, + responseDeserialize: deserialize_escrow_FreeCallStateReply, + }, +}; + +exports.FreeCallStateServiceClient = grpc.makeGenericClientConstructor(FreeCallStateServiceService); diff --git a/snet-sdk-js/packages/nodejs/src/proto/state_service_pb.js b/snet-sdk-js/packages/nodejs/src/proto/state_service_pb.js new file mode 100644 index 000000000..c075c2060 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/state_service_pb.js @@ -0,0 +1,1231 @@ +// source: state_service.proto +/** + * @fileoverview + * @enhanceable + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.escrow.ChannelStateReply', null, global); +goog.exportSymbol('proto.escrow.ChannelStateRequest', null, global); +goog.exportSymbol('proto.escrow.FreeCallStateReply', null, global); +goog.exportSymbol('proto.escrow.FreeCallStateRequest', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.ChannelStateRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.ChannelStateRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.ChannelStateRequest.displayName = 'proto.escrow.ChannelStateRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.ChannelStateReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.ChannelStateReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.ChannelStateReply.displayName = 'proto.escrow.ChannelStateReply'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.FreeCallStateRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.FreeCallStateRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.FreeCallStateRequest.displayName = 'proto.escrow.FreeCallStateRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.FreeCallStateReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.FreeCallStateReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.FreeCallStateReply.displayName = 'proto.escrow.FreeCallStateReply'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.ChannelStateRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.ChannelStateRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.ChannelStateRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateRequest.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: msg.getChannelId_asB64(), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.ChannelStateRequest} + */ +proto.escrow.ChannelStateRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.ChannelStateRequest; + return proto.escrow.ChannelStateRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.ChannelStateRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.ChannelStateRequest} + */ +proto.escrow.ChannelStateRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.ChannelStateRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.ChannelStateRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } +}; + + +/** + * optional bytes channel_id = 1; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes channel_id = 1; + * This is a type-conversion wrapper around `getChannelId()` + * @return {string} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChannelId())); +}; + + +/** + * optional bytes channel_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChannelId()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChannelId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setChannelId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes signature = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes signature = 2; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint64 current_block = 3; + * @return {number} + */ +proto.escrow.ChannelStateRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.ChannelStateReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.ChannelStateReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.ChannelStateReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateReply.toObject = function(includeInstance, msg) { + var f, obj = { + currentNonce: msg.getCurrentNonce_asB64(), + currentSignedAmount: msg.getCurrentSignedAmount_asB64(), + currentSignature: msg.getCurrentSignature_asB64(), + oldNonceSignedAmount: msg.getOldNonceSignedAmount_asB64(), + oldNonceSignature: msg.getOldNonceSignature_asB64(), + plannedAmount: jspb.Message.getFieldWithDefault(msg, 6, 0), + usedAmount: jspb.Message.getFieldWithDefault(msg, 7, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.ChannelStateReply} + */ +proto.escrow.ChannelStateReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.ChannelStateReply; + return proto.escrow.ChannelStateReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.ChannelStateReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.ChannelStateReply} + */ +proto.escrow.ChannelStateReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentNonce(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentSignedAmount(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentSignature(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOldNonceSignedAmount(value); + break; + case 5: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOldNonceSignature(value); + break; + case 6: + var value = /** @type {number} */ (reader.readUint64()); + msg.setPlannedAmount(value); + break; + case 7: + var value = /** @type {number} */ (reader.readUint64()); + msg.setUsedAmount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.ChannelStateReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.ChannelStateReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCurrentNonce_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getCurrentSignedAmount_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getCurrentSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getOldNonceSignedAmount_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getOldNonceSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 5, + f + ); + } + f = message.getPlannedAmount(); + if (f !== 0) { + writer.writeUint64( + 6, + f + ); + } + f = message.getUsedAmount(); + if (f !== 0) { + writer.writeUint64( + 7, + f + ); + } +}; + + +/** + * optional bytes current_nonce = 1; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes current_nonce = 1; + * This is a type-conversion wrapper around `getCurrentNonce()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentNonce())); +}; + + +/** + * optional bytes current_nonce = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentNonce()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentNonce())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentNonce = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes current_signed_amount = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes current_signed_amount = 2; + * This is a type-conversion wrapper around `getCurrentSignedAmount()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentSignedAmount())); +}; + + +/** + * optional bytes current_signed_amount = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentSignedAmount()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentSignedAmount())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentSignedAmount = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes current_signature = 3; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes current_signature = 3; + * This is a type-conversion wrapper around `getCurrentSignature()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentSignature())); +}; + + +/** + * optional bytes current_signature = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * This is a type-conversion wrapper around `getOldNonceSignedAmount()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOldNonceSignedAmount())); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOldNonceSignedAmount()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOldNonceSignedAmount())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setOldNonceSignedAmount = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * This is a type-conversion wrapper around `getOldNonceSignature()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOldNonceSignature())); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOldNonceSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOldNonceSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setOldNonceSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 5, value); +}; + + +/** + * optional uint64 planned_amount = 6; + * @return {number} + */ +proto.escrow.ChannelStateReply.prototype.getPlannedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setPlannedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional uint64 used_amount = 7; + * @return {number} + */ +proto.escrow.ChannelStateReply.prototype.getUsedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setUsedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.FreeCallStateRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.FreeCallStateRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.FreeCallStateRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateRequest.toObject = function(includeInstance, msg) { + var f, obj = { + userId: jspb.Message.getFieldWithDefault(msg, 1, ""), + tokenForFreeCall: msg.getTokenForFreeCall_asB64(), + tokenExpiryDateBlock: jspb.Message.getFieldWithDefault(msg, 3, 0), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 5, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.FreeCallStateRequest} + */ +proto.escrow.FreeCallStateRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.FreeCallStateRequest; + return proto.escrow.FreeCallStateRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.FreeCallStateRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.FreeCallStateRequest} + */ +proto.escrow.FreeCallStateRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setUserId(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTokenForFreeCall(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setTokenExpiryDateBlock(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.FreeCallStateRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.FreeCallStateRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getTokenForFreeCall_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getTokenExpiryDateBlock(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } +}; + + +/** + * optional string user_id = 1; + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getUserId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setUserId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional bytes token_for_free_call = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes token_for_free_call = 2; + * This is a type-conversion wrapper around `getTokenForFreeCall()` + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTokenForFreeCall())); +}; + + +/** + * optional bytes token_for_free_call = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTokenForFreeCall()` + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTokenForFreeCall())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setTokenForFreeCall = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint64 token_expiry_date_block = 3; + * @return {number} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenExpiryDateBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setTokenExpiryDateBlock = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes signature = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes signature = 4; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint64 current_block = 5; + * @return {number} + */ +proto.escrow.FreeCallStateRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.FreeCallStateReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.FreeCallStateReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.FreeCallStateReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateReply.toObject = function(includeInstance, msg) { + var f, obj = { + userId: jspb.Message.getFieldWithDefault(msg, 1, ""), + freeCallsAvailable: jspb.Message.getFieldWithDefault(msg, 2, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.FreeCallStateReply} + */ +proto.escrow.FreeCallStateReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.FreeCallStateReply; + return proto.escrow.FreeCallStateReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.FreeCallStateReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.FreeCallStateReply} + */ +proto.escrow.FreeCallStateReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setUserId(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setFreeCallsAvailable(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.FreeCallStateReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.FreeCallStateReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getFreeCallsAvailable(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } +}; + + +/** + * optional string user_id = 1; + * @return {string} + */ +proto.escrow.FreeCallStateReply.prototype.getUserId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.FreeCallStateReply} returns this + */ +proto.escrow.FreeCallStateReply.prototype.setUserId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint64 free_calls_available = 2; + * @return {number} + */ +proto.escrow.FreeCallStateReply.prototype.getFreeCallsAvailable = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateReply} returns this + */ +proto.escrow.FreeCallStateReply.prototype.setFreeCallsAvailable = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +goog.object.extend(exports, proto.escrow); diff --git a/snet-sdk-js/packages/nodejs/src/proto/token_service.proto b/snet-sdk-js/packages/nodejs/src/proto/token_service.proto new file mode 100644 index 000000000..33a66563e --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/token_service.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +package escrow; + +option java_package = "io.singularitynet.daemon.escrow"; +//It is expected that the user would call the GetChannelState to Determine the Current state of the Channel +//Based on the usage forecast, the user/client will have to sign for an amount L + U , where L is the last amount Signed +//and U is the amount based on expected usage. +//Please be aware that the Signing up an amount upfront ( Pre Paid) does come with a risk and hence the +//user must exercise caution on the amount signed specially with new service providers. +//If there is no need of making concurrent calls then you may consider pay per mode. +//Using a Token, the Client can now make concurrent calls, which was not supported previously with the pay per mode. +//However the pay per mode is a lot secure than the pre-paid mode. +service TokenService { + // GetToken method checks the Signature sent and returns a Token + // 1) The Signature is valid and has to be signed in the below format + //"__MPE_claim_message"+MpeContractAddress+ChannelID+ChannelNonce+SignedAmount + //Signature is to let the Service Provider make a claim + // 2) Signed amount >= Last amount Signed. + // if Signed amount == Last Signed amount , then check if planned_amount < used_amount + // if Signed amount > Last Signed amount , then update the planned amount = Signed Amount + // GetToken method in a way behaves as a renew Token too!. + rpc GetToken(TokenRequest) returns (TokenReply) {} + + + +} + +// TokenRequest is a request for getting a valid token. +message TokenRequest { + // channel_id contains id of the channel which state is requested. + uint64 channel_id = 1; + // current_nonce is a latest nonce of the payment channel. + uint64 current_nonce = 2; + //signed_amount is the amount signed by client with current_nonce + uint64 signed_amount = 3; + // Signature is a client signature of the message which contains 2 parts + //Part 1 : MPE Signature "__MPE_claim_message"+MpeContractAddress+ChannelID+ChannelNonce+SignedAmount + //Part 2 : Current Block Number + bytes signature = 4; + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 5; + + bytes claim_signature = 6; + +} + +// TokenReply message contains a latest channel state. current_nonce and +message TokenReply { + // current_nonce is a latest nonce of the payment channel. + uint64 channel_id = 1; + + //it could be absent if none message was signed with current_nonce + string token = 2; + + //If the client / user chooses to sign upfront , the planned amount in cogs will be indicative of this. + uint64 planned_amount = 3; + + //If the client / user chooses to sign upfront , the used amount in cogs will be indicative of how much of the + //planned amount has actually been used. + uint64 used_amount = 4; + +} diff --git a/snet-sdk-js/packages/nodejs/src/proto/token_service_grpc_pb.js b/snet-sdk-js/packages/nodejs/src/proto/token_service_grpc_pb.js new file mode 100644 index 000000000..02a476d7c --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/token_service_grpc_pb.js @@ -0,0 +1,60 @@ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +'use strict'; +var grpc = require('@grpc/grpc-js'); +var token_service_pb = require('./token_service_pb.js'); + +function serialize_escrow_TokenReply(arg) { + if (!(arg instanceof token_service_pb.TokenReply)) { + throw new Error('Expected argument of type escrow.TokenReply'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_TokenReply(buffer_arg) { + return token_service_pb.TokenReply.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_escrow_TokenRequest(arg) { + if (!(arg instanceof token_service_pb.TokenRequest)) { + throw new Error('Expected argument of type escrow.TokenRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_escrow_TokenRequest(buffer_arg) { + return token_service_pb.TokenRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + + +// It is expected that the user would call the GetChannelState to Determine the Current state of the Channel +// Based on the usage forecast, the user/client will have to sign for an amount L + U , where L is the last amount Signed +// and U is the amount based on expected usage. +// Please be aware that the Signing up an amount upfront ( Pre Paid) does come with a risk and hence the +// user must exercise caution on the amount signed specially with new service providers. +// If there is no need of making concurrent calls then you may consider pay per mode. +// Using a Token, the Client can now make concurrent calls, which was not supported previously with the pay per mode. +// However the pay per mode is a lot secure than the pre-paid mode. +var TokenServiceService = exports.TokenServiceService = { + // GetToken method checks the Signature sent and returns a Token +// 1) The Signature is valid and has to be signed in the below format +// "__MPE_claim_message"+MpeContractAddress+ChannelID+ChannelNonce+SignedAmount +// Signature is to let the Service Provider make a claim +// 2) Signed amount >= Last amount Signed. +// if Signed amount == Last Signed amount , then check if planned_amount < used_amount +// if Signed amount > Last Signed amount , then update the planned amount = Signed Amount +// GetToken method in a way behaves as a renew Token too!. +getToken: { + path: '/escrow.TokenService/GetToken', + requestStream: false, + responseStream: false, + requestType: token_service_pb.TokenRequest, + responseType: token_service_pb.TokenReply, + requestSerialize: serialize_escrow_TokenRequest, + requestDeserialize: deserialize_escrow_TokenRequest, + responseSerialize: serialize_escrow_TokenReply, + responseDeserialize: deserialize_escrow_TokenReply, + }, +}; + +exports.TokenServiceClient = grpc.makeGenericClientConstructor(TokenServiceService); diff --git a/snet-sdk-js/packages/nodejs/src/proto/token_service_pb.js b/snet-sdk-js/packages/nodejs/src/proto/token_service_pb.js new file mode 100644 index 000000000..0fe864d8b --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/proto/token_service_pb.js @@ -0,0 +1,609 @@ +// source: token_service.proto +/** + * @fileoverview + * @enhanceable + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! +/* eslint-disable */ +// @ts-nocheck + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.escrow.TokenReply', null, global); +goog.exportSymbol('proto.escrow.TokenRequest', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.TokenRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.TokenRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.TokenRequest.displayName = 'proto.escrow.TokenRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.TokenReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.TokenReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.TokenReply.displayName = 'proto.escrow.TokenReply'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.TokenRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.TokenRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.TokenRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenRequest.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: jspb.Message.getFieldWithDefault(msg, 1, 0), + currentNonce: jspb.Message.getFieldWithDefault(msg, 2, 0), + signedAmount: jspb.Message.getFieldWithDefault(msg, 3, 0), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 5, 0), + claimSignature: msg.getClaimSignature_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.TokenRequest} + */ +proto.escrow.TokenRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.TokenRequest; + return proto.escrow.TokenRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.TokenRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.TokenRequest} + */ +proto.escrow.TokenRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentNonce(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSignedAmount(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setClaimSignature(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.TokenRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.TokenRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId(); + if (f !== 0) { + writer.writeUint64( + 1, + f + ); + } + f = message.getCurrentNonce(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getSignedAmount(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } + f = message.getClaimSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 6, + f + ); + } +}; + + +/** + * optional uint64 channel_id = 1; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getChannelId = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setChannelId = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint64 current_nonce = 2; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getCurrentNonce = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setCurrentNonce = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 signed_amount = 3; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getSignedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setSignedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes signature = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.TokenRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes signature = 4; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.TokenRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint64 current_block = 5; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional bytes claim_signature = 6; + * @return {!(string|Uint8Array)} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes claim_signature = 6; + * This is a type-conversion wrapper around `getClaimSignature()` + * @return {string} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getClaimSignature())); +}; + + +/** + * optional bytes claim_signature = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getClaimSignature()` + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getClaimSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setClaimSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 6, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.TokenReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.TokenReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.TokenReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenReply.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: jspb.Message.getFieldWithDefault(msg, 1, 0), + token: jspb.Message.getFieldWithDefault(msg, 2, ""), + plannedAmount: jspb.Message.getFieldWithDefault(msg, 3, 0), + usedAmount: jspb.Message.getFieldWithDefault(msg, 4, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.TokenReply} + */ +proto.escrow.TokenReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.TokenReply; + return proto.escrow.TokenReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.TokenReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.TokenReply} + */ +proto.escrow.TokenReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setToken(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setPlannedAmount(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint64()); + msg.setUsedAmount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.TokenReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.TokenReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.TokenReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId(); + if (f !== 0) { + writer.writeUint64( + 1, + f + ); + } + f = message.getToken(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getPlannedAmount(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getUsedAmount(); + if (f !== 0) { + writer.writeUint64( + 4, + f + ); + } +}; + + +/** + * optional uint64 channel_id = 1; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getChannelId = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setChannelId = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional string token = 2; + * @return {string} + */ +proto.escrow.TokenReply.prototype.getToken = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setToken = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional uint64 planned_amount = 3; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getPlannedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setPlannedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint64 used_amount = 4; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getUsedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setUsedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +goog.object.extend(exports, proto.escrow); diff --git a/snet-sdk-js/packages/nodejs/src/sdk-core.js b/snet-sdk-js/packages/nodejs/src/sdk-core.js new file mode 100644 index 000000000..39b395a8e --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/sdk-core.js @@ -0,0 +1,5 @@ +import SnetSdk from './core'; + +export default SnetSdk; + +export * from './core'; diff --git a/snet-sdk-js/packages/nodejs/src/utils/logger.js b/snet-sdk-js/packages/nodejs/src/utils/logger.js new file mode 100644 index 000000000..50effce07 --- /dev/null +++ b/snet-sdk-js/packages/nodejs/src/utils/logger.js @@ -0,0 +1,17 @@ +import { format, transports } from 'winston'; +import { logger } from '../sdk-core'; + +const combinedFormat = format.combine(format.timestamp(), format.json()); +const errorLogger = new transports.File({ + format: combinedFormat, + filename: './logs/error.log', + level: 'error', +}); +const combinedLogger = new transports.File({ + format: combinedFormat, + filename: './logs/combined.log', + level: 'info', +}); + +logger.add(new transports.Console({ level: "debug" })); +logger.add(combinedLogger).add(errorLogger); diff --git a/snet-sdk-js/packages/web/.babelrc b/snet-sdk-js/packages/web/.babelrc new file mode 100644 index 000000000..728700d79 --- /dev/null +++ b/snet-sdk-js/packages/web/.babelrc @@ -0,0 +1,13 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ] + ], + "exclude": ["./src/proto"] +} diff --git a/snet-sdk-js/packages/web/README.md b/snet-sdk-js/packages/web/README.md new file mode 100644 index 000000000..48de0d9fa --- /dev/null +++ b/snet-sdk-js/packages/web/README.md @@ -0,0 +1,95 @@ +# snet-sdk-web + +![npm](https://img.shields.io/npm/v/snet-sdk-web.svg) + +SingularityNET SDK for Browser (Web) + +## Getting Started + +These instructions are for the development and use of the SingularityNET SDK for JavaScript on web platform like browsers. +### Installation +```bash +npm install snet-sdk-web +``` +### Usage + +The SingularityNET SDK allows you to import compiled client libraries for your service or services of choice and make calls to those services programmatically from your application by setting up state channels with the providers of those services and making gRPC calls to the SingularityNET daemons for those services by selecting a channel with sufficient funding and supplying the appropriate metadata for authentication. + +```javascript +import SnetSDK from "snet-sdk-web"; + +import config from "./config"; + +const sdk = new SnetSDK(config); +``` + +Now, the instance of the sdk can be used to instantiate clients for SingularityNET services. To interact with those services, the sdk needs to be supplied with the compiled gRPC client libraries. + +This SDK uses [gRPC-web](https://github.com/improbable-eng/grpc-web) by improbable engineering. To generate the gRPC client libraries, follow the instructions given by the `gRPC-web` package [here](https://github.com/improbable-eng/grpc-web/tree/master/client/grpc-web). + +The api to invoke gRPC methods against a service is similar to that of the `gRPC-web` package used. + +```javascript + +import { } from '' + +import { } from '' + +const client = sdk.createServiceClient("", "") + +``` +This generates a service client which can be used to make gRPC calls to the desired service. +You can then invoke service specific calls as follows +```javascript +client.invoke(., ); +``` + +More details about can be found on the official [documentation](https://github.com/improbable-eng/grpc-web/blob/master/client/grpc-web/docs/invoke.md#invokerpcoptions). + +--- + +## WEBSDK SETUP LOCALLY + +If you want to setup WEB SDK locally please follow below steps + +First clone this repo to your local machine. + +Then open repo and go to web folder inside SNET-SDK-JS > packages > web + +```bash +npm install +``` + +If you are using **Windows** Then follow below steps first + +Copy **core** folder from **SNET-SDK-JS > packages > core** and paste or replace it inside **SNET-SDK-JS > packages > web > src** + +```bash +npm run build +``` + +```bash +npm link +``` + +Now go to the other project or repo where you want to connect SDK locally + +```bash +npm link snet-sdk-web +``` + +```bash +npm run start +``` + +**(NOTE)** If you change anything inside web sdk and you want to access the updated code inside your repo you need to re run all the above commands. + +### Versioning + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the +[tags on this repository](https://github.com/singnet/snet-sdk-js/tags). + +## License + +This project is licensed under the MIT License - see the +[LICENSE](https://github.com/singnet/snet-sdk-js/blob/master/LICENSE) file for details. diff --git a/snet-sdk-js/packages/web/package.json b/snet-sdk-js/packages/web/package.json new file mode 100644 index 000000000..b29dcaae3 --- /dev/null +++ b/snet-sdk-js/packages/web/package.json @@ -0,0 +1,57 @@ +{ + "name": "snet-sdk-web", + "version": "3.0.0", + "description": "SingularityNET SDK for Web", + "main": "./dist/index.js", + "files": [ + "dist" + ], + "keywords": [ + "snet-sdk", + "singularitynet-sdk", + "singularitynet", + "web" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/singnet/snet-sdk-js.git", + "directory": "packages/web" + }, + "homepage": "https://github.com/singnet/snet-sdk-js/packages/web", + "scripts": { + "build": "babel src --out-dir dist", + "clean": "rimraf dist", + "clean:build": "npm run clean && npm run build", + "prepublishOnly": "npm run clean:build", + "build:link": "npm run build && npm link" + }, + "author": "SingularityNET Foundation", + "license": "MIT", + "dependencies": { + "@ethereumjs/tx": "^5.2.1", + "@grpc/grpc-js": "^1.10.0", + "axios": "^0.28.0", + "bignumber.js": "^9.1.2", + "es6-promise": "^4.2.8", + "lodash": "latest", + "singularitynet-platform-contracts": "^1.0.4", + "singularitynet-token-contracts": "^3.0.3", + "web3": "^4.4.0", + "winston": "^3.2.1", + "winston-transport": "^4.7.0" + }, + "peerDependencies": { + "@improbable-eng/grpc-web": "^0.15.0", + "google-protobuf": "^3.8.0" + }, + "devDependencies": { + "@babel/cli": "^7.23.9", + "@babel/core": "^7.23.9", + "@babel/node": "^7.23.9", + "@babel/preset-env": "^7.23.9", + "@babel/register": "^7.23.7", + "@improbable-eng/grpc-web": "^0.15.0", + "google-protobuf": "^3.21.2", + "rimraf": "^5.0.5" + } +} diff --git a/snet-sdk-js/packages/web/src/ConcurrencyManager.js b/snet-sdk-js/packages/web/src/ConcurrencyManager.js new file mode 100644 index 000000000..17bba5103 --- /dev/null +++ b/snet-sdk-js/packages/web/src/ConcurrencyManager.js @@ -0,0 +1,108 @@ +import { logger } from './sdk-core'; +import { toBNString } from './core/src/utils/bignumber_helper'; +import { TokenService, TokenServiceClient } from './proto/token_service_pb_service'; + +class ConcurrencyManager { + constructor(concurrentCalls = 1, serviceClient) { + this._concurrentCalls = concurrentCalls; + this._serviceClient = serviceClient; + this._tokenServiceClient = this._generateTokenServiceClient(); + } + + get concurrentCalls() { + return this._concurrentCalls; + } + + async getToken(channel, serviceCallPrice) { + const currentSignedAmount = channel.state.currentSignedAmount.toNumber(); + if(currentSignedAmount !== 0) { + const { plannedAmount, usedAmount, token } = await this._getTokenForAmount(channel, currentSignedAmount); + if(usedAmount < plannedAmount) { + return token; + } + } + const newAmountToBeSigned = currentSignedAmount + serviceCallPrice; + return this._getNewToken(channel, newAmountToBeSigned); + } + + /** + * @param {ServiceClient} serviceClient + * @param {PaymentChannel} channel + * @param {number} amount + * @returns {Promise} token + * @private + */ + async _getNewToken(channel, amount) { + const tokenResponse = await this._getTokenForAmount(channel, amount); + const { token } = tokenResponse; + return token; + } + + async _getTokenServiceRequest(channel, amount) { + const { nonce } = channel.state; + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + + const mpeSignature = await this._generateMpeSignature(parseInt(channel.channelId, 10), parseInt(nonce, 10), amount); + const tokenSignature = await this._generateTokenSignature(mpeSignature, currentBlockNumber); + const GetTokenRequest = TokenService.GetToken.requestType; + const request = new GetTokenRequest(); + request.setChannelId(parseInt(channel.channelId, 10)); + request.setCurrentNonce(parseInt(nonce, 10)); + request.setSignedAmount(amount); + request.setSignature(tokenSignature); + request.setCurrentBlock(toBNString(currentBlockNumber)); + request.setClaimSignature(mpeSignature); + return request; + } + + /** + * Get token for the given amount + * @param {ServiceClient} serviceClient + * @param {PaymentChannel} channel + * @param {number} amount + * @returns {Promise} token + * @private + */ + async _getTokenForAmount(channel, amount) { + const request = await this._getTokenServiceRequest(channel, amount); + return new Promise((resolve, reject) => { + this._tokenServiceClient.getToken(request, (error, responseMessage) => { + if(error) { + console.log('token grpc error', error); + reject(error); + } else { + resolve({ + plannedAmount: responseMessage.getPlannedAmount(), + usedAmount: responseMessage.getUsedAmount(), + token: responseMessage.getToken(), + }); + } + }); + }); + } + + async _generateTokenSignature(mpeSignature, currentBlockNumber) { + const mpeSignatureHex = mpeSignature.toString('hex'); + return this._serviceClient.signData({ t: "bytes", v: mpeSignatureHex }, { t: "uint256", v: currentBlockNumber }); + } + + async _generateMpeSignature(channelId, nonce, signedAmount) { + return this._serviceClient.signData( + { t: 'string', v: '__MPE_claim_message' }, + { t: 'address', v: this._serviceClient.mpeContract.address }, + { t: 'uint256', v: channelId }, + { t: 'uint256', v: nonce }, + { t: 'uint256', v: signedAmount }, + ); + } + + _generateTokenServiceClient() { + logger.debug('Creating TokenService client', { tags: ['gRPC'] }); + const serviceEndpoint = this._serviceClient._getServiceEndpoint(); + logger.debug(`TokenService pointing to ${serviceEndpoint.host}, `, { tags: ['gRPC'] }); + const host = `${serviceEndpoint.protocol}//${serviceEndpoint.host}`; + return new TokenServiceClient(host); + } +} + +export default ConcurrencyManager; diff --git a/snet-sdk-js/packages/web/src/RegistryContract.js b/snet-sdk-js/packages/web/src/RegistryContract.js new file mode 100644 index 000000000..dd29d3dc3 --- /dev/null +++ b/snet-sdk-js/packages/web/src/RegistryContract.js @@ -0,0 +1,134 @@ +import RegistryAbi from "singularitynet-platform-contracts/abi/Registry"; +import RegistryNetworks from "singularitynet-platform-contracts/networks/Registry"; + +class RegistryContract { + constructor(web3, networkId) { + this._web3 = web3; + this._contract = new this._web3.eth.Contract(RegistryAbi, RegistryNetworks[networkId].address); + } + + /** + * Converts a string to padded hexadecimal format. + * @param {string} string - The string to convert. + * @param {number} [bit=64] - The number of bits to pad to (default is 64). + * @returns {string} The padded hexadecimal string. + */ + _toPaddedHex(string, bit = 64) { + const hex = this._web3.utils.fromAscii(string); + const hexLength = hex.length; + const additionalBit = 2; // Due 0x prepend + if(hexLength === bit + additionalBit) return hex; + const paddedHex = hex.padEnd(bit * (Math.floor(hexLength / bit) + 1) + additionalBit, '0'); + return paddedHex; + } + + /** + * Creates a new organization in the blockchain + * @param {string} orgId - The unique organization id + * @param {string} orgMetadataURI - The IPFS URI for the organization metadata + * @param {Array} members - List of etherum addresses of the members of the organization + */ + createOrganization(orgId, orgMetadataURI, members) { + const enhancedOrgId = this._toPaddedHex(orgId); + const enhancedOrgMetadataURI = this._toPaddedHex(orgMetadataURI); + return this._contract.methods.createOrganization(enhancedOrgId, enhancedOrgMetadataURI, [...members]); + } + + /** + * + * @param {string} orgId - Id of organization to update. + * @param {string} orgMetadataURI -- The IPFS URI for the updated organization metadata + */ + changeOrganizationMetadataURI(orgId, orgMetadataURI) { + const enhancedOrgId = this._toPaddedHex(orgId); + const enhancedOrgMetadataURI = this._toPaddedHex(orgMetadataURI); + return this._contract.methods.changeOrganizationMetadataURI(enhancedOrgId, enhancedOrgMetadataURI); + } + + /** + * Add new members to the organization + * @param {string} orgId - The unique organization id + * @param {Array} newMembers - List of ethereum addresses of the new members to be added to the organization + */ + addOrganizationMembers(orgId, newMembers) { + const enhancedOrgId = this._toPaddedHex(orgId); + return this._contract.methods.addOrganizationMembers(enhancedOrgId, newMembers); + } + + /** + * Remove the existing members from the organization + * @param {string} orgId - The unique organization id + * @param {Array} existingMembers - List of ethereum address of the members that has to be removed from the organization + */ + removeOrganizationMembers(orgId, existingMembers) { + const enhancedOrgId = this._toPaddedHex(orgId); + return this._contract.methods.removeOrganizationMembers(enhancedOrgId, existingMembers); + } + + /** + * + * @param {string} orgId - The unique organization id + * @param {string} serviceId - Id of the service to create, must be unique organization-wide. + * @param {string} serviceMetadataURI - Service metadata. metadataURI should contain information for data consistency + * validation (for example hash). We support: IPFS URI. + */ + createServiceRegistration(orgId, serviceId, serviceMetadataURI) { + const enhancedOrgId = this._toPaddedHex(orgId); + const enhancedServiceId = this._toPaddedHex(serviceId); + const enhancedServiceMetadataURI = this._toPaddedHex(serviceMetadataURI); + return this._contract.methods.createServiceRegistration( + enhancedOrgId, + enhancedServiceId, + enhancedServiceMetadataURI + ); + } + + /** + * + * @param {string} orgId - The unique organization id + * @param {string} serviceId - Id of the service to update. + * @param {string} serviceMetadataURI - Service metadata. metadataURI should contain information for data consistency + * validation (for example hash). We support: IPFS URI. + */ + updateServiceRegistration(orgId, serviceId, serviceMetadataURI) { + const enhancedOrgId = this._toPaddedHex(orgId); + const enhancedServiceId = this._toPaddedHex(serviceId); + const enhancedServiceMetadataURI = this._toPaddedHex(serviceMetadataURI); + return this._contract.methods.updateServiceRegistration( + enhancedOrgId, + enhancedServiceId, + enhancedServiceMetadataURI + ); + } + + listOrganizations() { + return this._contract.methods.listOrganizations(); + } + + /** + * + * @param {string} orgId - Id of the organization to look up. + */ + getOrganizationById(orgId) { + const enhancedOrgId = this._toPaddedHex(orgId); + return this._contract.methods.getOrganizationById(enhancedOrgId); + } + + /** + * + * @param {string} orgId Id of the organization that owns the service to look up. + * @param {string} serviceId Id of the service to look up. + * + * @returns {boolean} true if an organization and service with these ids exists, false otherwise. If false, all other + * returned fields should be ignored. + * @returns {string} Id of the service, should be the same as the serviceId parameter. + * @returns {string} metadataURI Service metadata URI + */ + getServiceRegistrationById(orgId, serviceId) { + const enhancedOrgId = this._toPaddedHex(orgId); + const enhancedServiceId = this._toPaddedHex(serviceId); + return this._contract.methods.getServiceRegistrationById(enhancedOrgId, enhancedServiceId); + } +} + +export default RegistryContract; diff --git a/snet-sdk-js/packages/web/src/WebSdk.js b/snet-sdk-js/packages/web/src/WebSdk.js new file mode 100644 index 000000000..bcfae0907 --- /dev/null +++ b/snet-sdk-js/packages/web/src/WebSdk.js @@ -0,0 +1,43 @@ +import SnetSDK, { MetaMaskIdentity } from './sdk-core'; +import WebServiceClient from './WebServiceClient'; +import RegistryContract from './RegistryContract'; + +class WebSdk extends SnetSDK { + constructor(...args) { + super(...args); + this._registryContract = new RegistryContract(this._web3, this._networkId); + } + + /** + * @param {string} orgId + * @param {string} serviceId + * @param {string} [groupName] + * @param {PaymentChannelManagementStrategy} [paymentChannelManagementStrategy=DefaultPaymentChannelManagementStrategy] + * @param {ServiceClientOptions} options + * @returns {Promise} + */ + async createServiceClient(orgId, serviceId, groupName = null, paymentChannelManagementStrategy = null, options = {}) { + const serviceMetadata = await this._metadataProvider.metadata(orgId, serviceId); + const group = await this._serviceGroup(serviceMetadata, orgId, serviceId, groupName); + return new WebServiceClient( + this, + orgId, + serviceId, + this._mpeContract, + serviceMetadata, + group, + this._constructStrategy(paymentChannelManagementStrategy), + options, + ); + } + + _createIdentity() { + return new MetaMaskIdentity(this._config, this._web3); + } + + async setupAccount() { + await this._account._identity.setupAccount(); + } +} + +export default WebSdk; diff --git a/snet-sdk-js/packages/web/src/WebServiceClient.js b/snet-sdk-js/packages/web/src/WebServiceClient.js new file mode 100644 index 000000000..c0c26a7e4 --- /dev/null +++ b/snet-sdk-js/packages/web/src/WebServiceClient.js @@ -0,0 +1,128 @@ +import { grpc } from "@improbable-eng/grpc-web"; +import { forOwn } from "lodash"; +import { PaymentChannelStateService, PaymentChannelStateServiceClient } from "./proto/state_service_pb_service"; +import { Model, ModelClient } from "./proto/training_pb_service"; +import training_pb from "./proto/training_pb"; +import { BaseServiceClient, logger } from "./sdk-core"; + +class WebServiceClient extends BaseServiceClient { + /** + * @param {MethodDescriptor} methodDescriptor + * @param {InvokeRpcOptions} props + * @returns {Request} + */ + async invoke(methodDescriptor, props) { + const requestProps = await this._generateRequestProps(methodDescriptor, props); + return grpc.invoke(methodDescriptor, requestProps); + } + + /** + * @param {UnaryMethodDefinition} methodDescriptor + * @param {UnaryRpcOptions} props + * @returns {Request} + */ + async unary(methodDescriptor, props) { + const requestProps = await this._generateRequestProps(methodDescriptor, props); + return grpc.unary(methodDescriptor, requestProps); + } + + /** + * + * @param {UnaryMethodDefinition} methodDescriptor + * @param {UnaryRpcOptions} props + * @returns {Promise} + * @private + */ + async _generateRequestProps(methodDescriptor, props) { + const serviceEndpoint = this._getServiceEndpoint(); + const host = `${serviceEndpoint.protocol}//${serviceEndpoint.host}`; + const metadata = await this._enhanceMetadata(props.metadata, methodDescriptor); + return { + ...props, + host, + metadata, + }; + } + + async _enhanceMetadata(metadata = new grpc.Metadata(), methodDescriptor) { + if (this._options.disableBlockchainOperations) { + return metadata; + } + + if (this._options.metadataGenerator) { + const { serviceName } = methodDescriptor.service; + const { methodName } = methodDescriptor; + const customMetadata = await this._options.metadataGenerator(this, serviceName, methodName); + forOwn(customMetadata, (value, key) => { + metadata.append(key, value); + }); + return metadata; + } + + // const { channelId, nonce, signingAmount, signatureBytes } = await this._fetchPaymentMetadata(); + // metadata.append('snet-payment-type', 'escrow'); + + const paymentMetadata = await this._fetchPaymentMetadata(); + paymentMetadata.forEach((paymentMeta) => { + Object.entries(paymentMeta).forEach(([key, value]) => { + metadata.append(key, value); + }); + }); + + // metadata.append('snet-payment-channel-id', `${channelId}`); + // metadata.append('snet-payment-channel-nonce', `${nonce}`); + // metadata.append('snet-payment-channel-amount', `${signingAmount}`); + // metadata.append('snet-payment-channel-signature-bin', signatureBytes.toString('base64')); + metadata.append("snet-payment-mpe-address", this._mpeContract.address); + console.log("metadata", metadata); + return metadata; + } + + _generatePaymentChannelStateServiceClient() { + logger.debug("Creating PaymentChannelStateService client", { + tags: ["gRPC"], + }); + const serviceEndpoint = this._getServiceEndpoint(); + logger.debug(`PaymentChannelStateService pointing to ${serviceEndpoint.host}, `, { tags: ["gRPC"] }); + const host = `${serviceEndpoint.protocol}//${serviceEndpoint.host}`; + return new PaymentChannelStateServiceClient(host); + } + + _getChannelStateRequestMethodDescriptor() { + return PaymentChannelStateService.GetChannelState.requestType; + } + + _generateModelServiceClient() { + logger.debug("Creating TrainingStateService client", { tags: ["gRPC"] }); + const serviceEndpoint = this._getServiceEndpoint(); + logger.debug(`TrainingChannelStateService pointing to ${serviceEndpoint.host}, `, { tags: ["gRPC"] }); + const host = `${serviceEndpoint.protocol}//${serviceEndpoint.host}`; + return new ModelClient(host); + } + + _getModelRequestMethodDescriptor() { + return Model.get_all_models.requestType; + } + + _getAuthorizationRequestMethodDescriptor() { + return training_pb.AuthorizationDetails; + } + + _getCreateModelRequestMethodDescriptor() { + return Model.create_model.requestType; + } + + _getDeleteModelRequestMethodDescriptor() { + return Model.delete_model.requestType; + } + + _getUpdateModelRequestMethodDescriptor() { + return Model.update_model_access.requestType; + } + + _getModelDetailsRequestMethodDescriptor() { + return training_pb.ModelDetails; + } +} + +export default WebServiceClient; diff --git a/snet-sdk-js/packages/web/src/core/index.js b/snet-sdk-js/packages/web/src/core/index.js new file mode 100644 index 000000000..eef101ee1 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/index.js @@ -0,0 +1,10 @@ +import SnetSDK from './src/sdk'; + +export default SnetSDK; + +export { default as logger } from './src/utils/logger'; +export { default as EncodingUtils } from './src/utils/encodingUtils'; +export { default as blockChainEvents } from './src/utils/blockchainEvents'; +export { default as BaseServiceClient } from './src/BaseServiceClient'; +export { default as PaymentChannel } from './src/PaymentChannel'; +export * from './src/identities'; diff --git a/snet-sdk-js/packages/web/src/core/src/Account.js b/snet-sdk-js/packages/web/src/core/src/Account.js new file mode 100644 index 000000000..050ed2b5e --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/Account.js @@ -0,0 +1,172 @@ +import AGITokenAbi from 'singularitynet-token-contracts/abi/SingularityNetToken'; +import AGITokenNetworks from 'singularitynet-token-contracts/networks/SingularityNetToken'; +import { BigNumber } from 'bignumber.js'; +import logger from './utils/logger'; + +import { toBNString } from './utils/bignumber_helper'; + +class Account { + /** + * @param {Web3} web3 + * @param {number} networkId + * @param {MPEContract} mpeContract + * @param {IdentityProvider} identity + */ + constructor(web3, networkId, mpeContract, identity) { + this._identity = identity; + this._web3 = web3; + this._networkId = networkId; + this._tokenContract = this._generateTokenContract(); + this._mpeContract = mpeContract; + } + + /** + * Returns the token balance available. + * @returns {Promise.} + */ + async balance() { + logger.debug('Fetching account balance', { tags: ['Account'] }); + const address = await this.getAddress(); + return this.tokenContract.methods.balanceOf(address).call(); + } + + /** + * Returns the balance for the current account in MPE Account. + * @returns {Promise.} + */ + async escrowBalance() { + const address = await this.getAddress(); + return this._mpeContract.balance(address); + } + + /** + * Approves the specified number of tokens for transfer if not already approved and deposits the tokens to the MPE Account. + * @param {BigNumber} amountInCogs - Tokens to transfer to MPE Account + * @returns {Promise.} + */ + async depositToEscrowAccount(amountInCogs) { + const alreadyApprovedAmount = await this.allowance(); + if(amountInCogs > alreadyApprovedAmount) { + await this.approveTransfer(amountInCogs); + } + + return this._mpeContract.deposit(this, amountInCogs); + } + + /** + * Approves the specified tokens for transfer to MPE Account + * @param {BigNumber} amountInCogs - Tokens for approval. + * @returns {Promise.} + */ + async approveTransfer(amountInCogs) { + const amount = toBNString(amountInCogs); + logger.info(`Approving ${amount}cogs transfer to MPE address`, { tags: ['Account'] }); + const approveOperation = this.tokenContract.methods.approve; + return this.sendTransaction(this.tokenAddress, approveOperation, this._mpeContract.address, amount); + } + + /** + * Returns the already approved tokens for transfer to MPE Account. + * @returns {Promise.} + */ + async allowance() { + logger.debug('Fetching already approved allowance', { tags: ['Account'] }); + const address = await this.getAddress(); + return this.tokenContract.methods.allowance(address, this._mpeContract.address).call(); + } + + /** + * Withdraws the specified tokens from the MPE account. + * @param {BigNumber} amountInCogs - Tokens to be withdrawn from the escrow account. + * @returns {Promise.} + */ + async withdrawFromEscrowAccount(amountInCogs) { + return this._mpeContract.withdraw(this, amountInCogs); + } + + /** + * @type {string} + */ + async getAddress() { + return this._identity.getAddress(); + } + + /** + * @type {string} + */ + async getSignerAddress() { + return this.getAddress(); + } + + /** + * @param {...(*|Object)} data + * @param {string} data.(t|type) - Type of data. One of the following (string|uint256|int256|bool|bytes) + * @param {string} data.(v|value) - Value + * @returns {Buffer} - Signed binary data + * @see {@link https://web3js.readthedocs.io/en/1.0/web3-utils.html#soliditysha3|data} + */ + async signData(...data) { + const sha3Message = this._web3.utils.soliditySha3(...data); + const signature = await this._identity.signData(sha3Message); + const stripped = signature.substring(2, signature.length); + const byteSig = Buffer.from(stripped, 'hex'); + return Buffer.from(byteSig); + } + + /** + * Sends a transaction for the transaction object to the contract address + * @param {string} to - The contract address to send the signed transaction to + * @param {function} contractFn - The contract function for which the transaction needs to be sent + * @param {...any} contractFnArgs - The args which will be sent to the contract function + * @returns {Promise} + */ + async sendTransaction(to, contractFn, ...contractFnArgs) { + const operation = contractFn(...contractFnArgs); + const txObject = await this._baseTransactionObject(operation, to); + return this._identity.sendTransaction(txObject); + } + + get tokenContract() { + return this._tokenContract; + } + + get tokenAddress() { + return this._tokenContract.options.address; + } + + _generateTokenContract() { + return new this._web3.eth.Contract(AGITokenAbi, AGITokenNetworks[this._networkId].address); + } + + async _baseTransactionObject(operation, to) { + const { gasLimit, gasPrice } = await this._getGas(operation); + const nonce = await this._transactionCount(); + const chainId = await this._getChainId(); + return { + nonce: this._web3.utils.toHex(nonce), + gas: this._web3.utils.toHex(gasLimit), + gasPrice: this._web3.utils.toHex(gasPrice), + to, + data: operation.encodeABI(), + chainId, + }; + } + + async _getGas(operation) { + const gasPrice = await this._web3.eth.getGasPrice(); + const address = await this.getAddress(); + const estimatedGas = await operation.estimateGas({ from: address }); + return { gasLimit: estimatedGas, gasPrice }; + } + + async _transactionCount() { + const address = await this.getAddress(); + return this._web3.eth.getTransactionCount(address); + } + + async _getChainId() { + return this._web3.eth.net.getId(); + } +} + +export default Account; diff --git a/snet-sdk-js/packages/web/src/core/src/BaseServiceClient.js b/snet-sdk-js/packages/web/src/core/src/BaseServiceClient.js new file mode 100644 index 000000000..34cd0829b --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/BaseServiceClient.js @@ -0,0 +1,598 @@ +import url from "url"; +import { BigNumber } from "bignumber.js"; +import { find, first, isEmpty, map } from "lodash"; +import logger from "./utils/logger"; + +import { toBNString } from "./utils/bignumber_helper"; + +class BaseServiceClient { + /** + * @param {SnetSDK} sdk + * @param {String} orgId + * @param {String} serviceId + * @param {MPEContract} mpeContract + * @param {ServiceMetadata} metadata + * @param {Group} group + * @param {DefaultPaymentStrategy} paymentChannelManagementStrategy + * @param {ServiceClientOptions} [options={}] + */ + constructor(sdk, orgId, serviceId, mpeContract, metadata, group, paymentChannelManagementStrategy, options = {}) { + this._sdk = sdk; + this._mpeContract = mpeContract; + this._options = options; + this._metadata = { orgId, serviceId, ...metadata }; + this._group = this._enhanceGroupInfo(group); + this._paymentChannelManagementStrategy = paymentChannelManagementStrategy; + this._paymentChannelStateServiceClient = this._generatePaymentChannelStateServiceClient(); + this._modelServiceClient = this._generateModelServiceClient(); + this._paymentChannels = []; + } + + /** + * @type {Group} + */ + get group() { + return this._group; + } + + /** + * @type {Array.} + */ + get paymentChannels() { + return this._paymentChannels; + } + + /** + * @type {ServiceMetadata} + */ + get metadata() { + return this._metadata; + } + + /** + * @type {GRPCClient} + */ + get paymentChannelStateServiceClient() { + return this._paymentChannelStateServiceClient; + } + + /** + * @type {MPEContract} + */ + get mpeContract() { + return this._mpeContract; + } + + /** + * @type {boolean} + */ + get concurrencyFlag() { + if (typeof this._options.concurrency === "undefined") { + return true; + } + return this._options.concurrency; + } + + async getExistingModel(params) { + const request = await this._trainingStateRequest(params); + return new Promise((resolve, reject) => { + this._modelServiceClient.get_all_models(request, (err, response) => { + const modelDetails = response.getListOfModelsList(); + const data = modelDetails.map((item) => ({ + modelId: item.getModelId(), + methodName: item.getGrpcMethodName(), + serviceName: item.getGrpcServiceName(), + description: item.getDescription(), + status: item.getStatus(), + updatedDate: item.getUpdatedDate(), + addressList: item.getAddressListList(), + modelName: item.getModelName(), + publicAccess: item.getIsPubliclyAccessible(), + })); + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); + } + + async _trainingStateRequest(params) { + const message = "__get_existing_model"; + const { currentBlockNumber, signatureBytes } = await this._requestSignForModel(params.address, message); + const ModelStateRequest = this._getModelRequestMethodDescriptor(); + const modelStateRequest = new ModelStateRequest(); + modelStateRequest.setGrpcMethodName(params.grpcMethod); + modelStateRequest.setGrpcServiceName(params.grpcService); + + const AuthorizationRequest = this._getAuthorizationRequestMethodDescriptor(); + const authorizationRequest = new AuthorizationRequest(); + + authorizationRequest.setCurrentBlock(currentBlockNumber); + authorizationRequest.setMessage(message); + authorizationRequest.setSignature(signatureBytes); + authorizationRequest.setSignerAddress(params.address); + modelStateRequest.setAuthorization(authorizationRequest); + return modelStateRequest; + } + + async _requestSignForModel(address, message) { + const currentBlockNumber = await this._web3.eth.getBlockNumber(); + const signatureBytes = await this.account.signData( + { t: "string", v: message }, + { t: "address", v: address }, + { t: "uint256", v: currentBlockNumber } + ); + + return { + currentBlockNumber, + signatureBytes, + }; + } + + async createModel(address, params) { + const request = await this._trainingCreateModel(address, params); + return new Promise((resolve, reject) => { + this._modelServiceClient.create_model(request, (err, response) => { + logger.debug(`create model ${err} ${response}`); + const modelDetails = response.getModelDetails(); + const data = { + modelId: modelDetails.getModelId(), + methodName: modelDetails.getGrpcMethodName(), + serviceName: modelDetails.getGrpcServiceName(), + description: modelDetails.getDescription(), + status: modelDetails.getStatus(), + updatedDate: modelDetails.getUpdatedDate(), + addressList: modelDetails.getAddressListList(), + modelName: modelDetails.getModelName(), + publicAccess: modelDetails.getIsPubliclyAccessible(), + }; + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); + } + + async _trainingCreateModel(address, params) { + const message = "__create_model"; + const { currentBlockNumber, signatureBytes } = await this._requestSignForModel(address, message); + const ModelStateRequest = this._getCreateModelRequestMethodDescriptor(); + const modelStateRequest = new ModelStateRequest(); + + const AuthorizationRequest = this._getAuthorizationRequestMethodDescriptor(); + const authorizationRequest = new AuthorizationRequest(); + const ModelDetailsRequest = this._getModelDetailsRequestMethodDescriptor(); + + const { orgId, serviceId, groupId } = this.getServiceDetails(); + const modelDetailsRequest = new ModelDetailsRequest(); + authorizationRequest.setCurrentBlock(currentBlockNumber); + authorizationRequest.setMessage(message); + authorizationRequest.setSignature(signatureBytes); + authorizationRequest.setSignerAddress(address); + + modelDetailsRequest.setModelName(params.modelName); + modelDetailsRequest.setGrpcMethodName(params.method); + modelDetailsRequest.setGrpcServiceName(params.serviceName); + modelDetailsRequest.setDescription(params.description); + modelDetailsRequest.setIsPubliclyAccessible(params.publicAccess); + modelDetailsRequest.setAddressListList(params.address); + modelDetailsRequest.setTrainingDataLink(""); + + modelDetailsRequest.setOrganizationId(orgId); + modelDetailsRequest.setServiceId(serviceId); + modelDetailsRequest.setGroupId(groupId); + + modelStateRequest.setAuthorization(authorizationRequest); + modelStateRequest.setModelDetails(modelDetailsRequest); + return modelStateRequest; + } + + async deleteModel(params) { + const request = await this._trainingDeleteModel(params); + return new Promise((resolve, reject) => { + this._modelServiceClient.delete_model(request, (err, response) => { + logger.debug(`delete model ${err} ${response}`); + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }); + } + + async _trainingDeleteModel(params) { + const message = "__delete_model"; + const { currentBlockNumber, signatureBytes } = await this._requestSignForModel(params.address, message); + + const ModelStateRequest = this._getUpdateModelRequestMethodDescriptor(); + const modelStateRequest = new ModelStateRequest(); + + const AuthorizationRequest = this._getAuthorizationRequestMethodDescriptor(); + const authorizationRequest = new AuthorizationRequest(); + const ModelDetailsRequest = this._getModelDetailsRequestMethodDescriptor(); + const modelDetailsRequest = new ModelDetailsRequest(); + + authorizationRequest.setCurrentBlock(currentBlockNumber); + authorizationRequest.setMessage(message); + authorizationRequest.setSignature(signatureBytes); + authorizationRequest.setSignerAddress(params.address); + modelDetailsRequest.setModelId(params.modelId); + modelDetailsRequest.setGrpcMethodName(params.method); + modelDetailsRequest.setGrpcServiceName(params.name); + + modelStateRequest.setAuthorization(authorizationRequest); + modelStateRequest.setUpdateModelDetails(modelDetailsRequest); + return modelStateRequest; + } + + async updateModel(params) { + const request = await this._trainingUpdateModel(params); + return new Promise((resolve, reject) => { + this._modelServiceClient.update_model_access(request, (err, response) => { + logger.debug(`update model ${err} ${response}`); + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }); + } + + async _trainingUpdateModel(params) { + const message = "__update_model"; + const { currentBlockNumber, signatureBytes } = await this._requestSignForModel(params.address, message); + + const ModelStateRequest = this._getUpdateModelRequestMethodDescriptor(); + const modelStateRequest = new ModelStateRequest(); + + const AuthorizationRequest = this._getAuthorizationRequestMethodDescriptor(); + const authorizationRequest = new AuthorizationRequest(); + const ModelDetailsRequest = this._getModelDetailsRequestMethodDescriptor(); + const modelDetailsRequest = new ModelDetailsRequest(); + + authorizationRequest.setCurrentBlock(currentBlockNumber); + authorizationRequest.setMessage(message); + authorizationRequest.setSignature(signatureBytes); + authorizationRequest.setSignerAddress(params.address); + modelDetailsRequest.setModelId(params.modelId); + modelDetailsRequest.setGrpcMethodName(params.method); + modelDetailsRequest.setGrpcServiceName(params.name); + modelDetailsRequest.setModelName(params.modelName); + modelDetailsRequest.setDescription(params.description); + modelDetailsRequest.setAddressListList(params.addressList); + modelDetailsRequest.setTrainingDataLink(""); + modelDetailsRequest.setStatus(params.status); + modelDetailsRequest.setUpdatedDate(params.updatedDate); + modelDetailsRequest.setIsPubliclyAccessible(params.publicAccess); + + const { orgId, serviceId, groupId } = this.getServiceDetails(); + modelDetailsRequest.setOrganizationId(orgId); + modelDetailsRequest.setServiceId(serviceId); + modelDetailsRequest.setGroupId(groupId); + + modelStateRequest.setAuthorization(authorizationRequest); + modelStateRequest.setUpdateModelDetails(modelDetailsRequest); + return modelStateRequest; + } + + /** + * Fetches the latest channel state from the ai service daemon + * @param channelId + * @returns {Promise} + */ + async getChannelState(channelId) { + const channelStateRequest = await this._channelStateRequest(channelId); + + return new Promise((resolve, reject) => { + this.paymentChannelStateServiceClient.getChannelState(channelStateRequest, (err, response) => { + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }); + } + + /** + * @returns {Promise.} + */ + async loadOpenChannels() { + const currentBlockNumber = await this._web3.eth.getBlockNumber(); + const newPaymentChannels = await this._mpeContract.getPastOpenChannels(this.account, this, this._lastReadBlock); + logger.debug(`Found ${newPaymentChannels.length} payment channel open events`, { tags: ["PaymentChannel"] }); + this._paymentChannels = [...this._paymentChannels, ...newPaymentChannels]; + this._lastReadBlock = currentBlockNumber; + return this._paymentChannels; + } + + /** + * @returns {Promise.} + */ + async updateChannelStates() { + logger.info("Updating payment channel states", { + tags: ["PaymentChannel"], + }); + const currentChannelStatesPromise = map(this._paymentChannels, (paymentChannel) => paymentChannel.syncState()); + await Promise.all(currentChannelStatesPromise); + return this._paymentChannels; + } + + /** + * + * @param {BigNumber} amount + * @param {BigNumber} expiry + * @returns {Promise.} + */ + async openChannel(amount, expiry) { + const newChannelReceipt = await this._mpeContract.openChannel(this.account, this, amount, expiry); + return this._getNewlyOpenedChannel(newChannelReceipt); + } + + /** + * @param {BigNumber} amount + * @param {BigNumber} expiry + * @returns {Promise.} + */ + async depositAndOpenChannel(amount, expiry) { + const newFundedChannelReceipt = await this._mpeContract.depositAndOpenChannel(this.account, this, amount, expiry); + return this._getNewlyOpenedChannel(newFundedChannelReceipt); + } + + /** + * get the details of the service + * @returns {ServiceDetails} + */ + getServiceDetails() { + return { + orgId: this._metadata.orgId, + serviceId: this._metadata.serviceId, + groupId: this._group.group_id, + groupIdInBytes: this._group.group_id_in_bytes, + daemonEndpoint: this._getServiceEndpoint(), + }; + } + + /** + * Get the configuration for the freecall + * @returns {FreeCallConfig} + */ + getFreeCallConfig() { + return { + email: this._options.email, + tokenToMakeFreeCall: this._options.tokenToMakeFreeCall, + tokenExpiryDateBlock: this._options.tokenExpirationBlock, + }; + } + + /** + * find the current blocknumber + * @returns {Promise} + */ + async getCurrentBlockNumber() { + return this._web3.eth.getBlockNumber(); + } + + /** + * @param {...(*|Object)} data + * @param {string} data.(t|type) - Type of data. One of the following (string|uint256|int256|bool|bytes) + * @param {string} data.(v|value) - Value + * @returns {Promise} - Signed binary data + * @see {@link https://web3js.readthedocs.io/en/1.0/web3-utils.html#soliditysha3|data} + */ + async signData(...data) { + return this.account.signData(...data); + } + + /** + * @returns {Promise} + */ + async defaultChannelExpiration() { + const currentBlockNumber = await this._web3.eth.getBlockNumber(); + const paymentExpirationThreshold = this._getPaymentExpiryThreshold(); + return toBNString(currentBlockNumber) + paymentExpirationThreshold; + } + + _getPaymentExpiryThreshold() { + if (isEmpty(this._group)) { + return 0; + } + const paymentExpirationThreshold = this._group.payment.payment_expiration_threshold; + return paymentExpirationThreshold || 0; + } + + _enhanceGroupInfo(group) { + if (isEmpty(group)) { + return group; + } + + const { payment_address, payment_expiration_threshold } = group.payment; + + return { + group_id_in_bytes: Buffer.from(group.group_id, "base64"), + ...group, + payment_address, + payment_expiration_threshold, + }; + } + + async _channelStateRequest(channelId) { + const { currentBlockNumber, signatureBytes } = await this._channelStateRequestProperties(toBNString(channelId)); + const channelIdBytes = Buffer.alloc(4); + channelIdBytes.writeUInt32BE(toBNString(channelId), 0); + + const ChannelStateRequest = this._getChannelStateRequestMethodDescriptor(); + const channelStateRequest = new ChannelStateRequest(); + channelStateRequest.setChannelId(channelIdBytes); + channelStateRequest.setSignature(signatureBytes); + channelStateRequest.setCurrentBlock(toBNString(currentBlockNumber)); + return channelStateRequest; + } + + async _channelStateRequestProperties(channelId) { + if (this._options.channelStateRequestSigner) { + const { currentBlockNumber, signatureBytes } = await this._options.channelStateRequestSigner(channelId); + return { currentBlockNumber, signatureBytes }; + } + const currentBlockNumber = await this._web3.eth.getBlockNumber(); + const channelIdStr = toBNString(channelId); + const signatureBytes = await this.account.signData( + { t: "string", v: "__get_channel_state" }, + { t: "address", v: this._mpeContract.address }, + { t: "uint256", v: channelIdStr }, + { t: "uint256", v: currentBlockNumber } + ); + + return { currentBlockNumber, signatureBytes }; + } + + async _fetchPaymentMetadata() { + if (this._options.paidCallMetadataGenerator) { + logger.debug("Selecting PaymentChannel using the given strategy", { + tags: ["PaypalPaymentMgmtStrategy, gRPC"], + }); + const channel = await this._paymentChannelManagementStrategy.selectChannel(this); + const { + channelId, + state: { nonce, currentSignedAmount }, + } = channel; + const signingAmount = currentSignedAmount.plus(this._pricePerServiceCall); + const channelIdStr = toBNString(channelId); + const nonceStr = toBNString(nonce); + const signingAmountStr = toBNString(signingAmount); + logger.info( + `Using PaymentChannel[id: ${channelIdStr}] with nonce: ${nonceStr} and amount: ${signingAmountStr} and `, + { tags: ["PaymentChannelManagementStrategy", "gRPC"] } + ); + const { signatureBytes } = await this._options.paidCallMetadataGenerator(channelId, signingAmount, nonce); + const metadata = [ + { "snet-payment-type": "escrow" }, + { "snet-payment-channel-id": `${channelId}` }, + { "snet-payment-channel-nonce": `${nonce}` }, + { "snet-payment-channel-amount": `${signingAmount}` }, + { + "snet-payment-channel-signature-bin": signatureBytes.toString("base64"), + }, + ]; + return metadata; + } + return this._paymentChannelManagementStrategy.getPaymentMetadata(this); + + // NOTE: Moved channel selection logic to payment strategy + // + // + // logger.debug('Selecting PaymentChannel using the given strategy', { tags: ['PaymentChannelManagementStrategy, gRPC'] }); + // const channel = await this._paymentChannelManagementStrategy.selectChannel(this); + // + // const { channelId, state: { nonce, currentSignedAmount } } = channel; + // const signingAmount = currentSignedAmount.plus(this._pricePerServiceCall); + // const channelIdStr = toBNString(channelId); + // const nonceStr = toBNString(nonce); + // const signingAmountStr = toBNString(signingAmount); + // logger.info(`Using PaymentChannel[id: ${channelIdStr}] with nonce: ${nonceStr} and amount: ${signingAmountStr} and `, { tags: ['PaymentChannelManagementStrategy', 'gRPC'] }); + // + // if(this._options.paidCallMetadataGenerator) { + // const { signatureBytes } = await this._options.paidCallMetadataGenerator(channelId, signingAmount, nonce); + // return { + // channelId, nonce, signingAmount, signatureBytes, + // }; + // } + // + // const signatureBytes = await this.account.signData( + // { t: 'string', v: '__MPE_claim_message' }, + // { t: 'address', v: this._mpeContract.address }, + // { t: 'uint256', v: channelIdStr }, + // { t: 'uint256', v: nonceStr }, + // { t: 'uint256', v: signingAmountStr }, + // ); + // + // return { + // channelId, nonce, signingAmount, signatureBytes, + // }; + } + + async _getNewlyOpenedChannel(receipt) { + const openChannels = await this._mpeContract.getPastOpenChannels(this.account, this, receipt.blockNumber, this); + const newPaymentChannel = openChannels[0]; + logger.info(`New PaymentChannel[id: ${newPaymentChannel.channelId}] opened`); + return newPaymentChannel; + } + + get _web3() { + return this._sdk.web3; + } + + /** + * @type {Account} + */ + get account() { + return this._sdk.account; + } + + get _pricePerServiceCall() { + const { pricing } = this.group; + const fixedPricing = find(pricing, ({ price_model }) => price_model === "fixed_price"); + + return new BigNumber(fixedPricing.price_in_cogs); + } + + _getServiceEndpoint() { + if (this._options.endpoint) { + return url.parse(this._options.endpoint); + } + + const { endpoints } = this.group; + const endpoint = first(endpoints); + logger.debug(`Service endpoint: ${endpoint}`, { tags: ["gRPC"] }); + + return endpoint && url.parse(endpoint); + } + + _generatePaymentChannelStateServiceClient() { + logger.error("_generatePaymentChannelStateServiceClient must be implemented in the sub classes"); + } + + _getChannelStateRequestMethodDescriptor() { + logger.error("_getChannelStateRequestMethodDescriptor must be implemented in the sub classes"); + } + + _generateModelServiceClient() { + logger.error("_generateTrainingStateServiceClient must be implemented in the sub classes"); + } + + _getModelRequestMethodDescriptor() { + logger.error("_getModelRequestMethodDescriptor must be implemented in the sub classes"); + } + + _getAuthorizationRequestMethodDescriptor() { + logger.error("_getAuthorizationRequestMethodDescriptor must be implemented in the sub classes"); + } + + _getCreateModelRequestMethodDescriptor() { + logger.error("_getCreateModelRequestMethodDescriptor must be implemented in the sub classes"); + } + + _getDeleteModelRequestMethodDescriptor() { + logger.error("_getDeleteModelRequestMethodDescriptor must be implemented in the sub classes"); + } + + _getUpdateModelRequestMethodDescriptor() { + logger.error("__getUpdateModelRequestMethodDescriptor must be implemented in the sub classes"); + } + + _getModelDetailsRequestMethodDescriptor() { + logger.error("_getModelDetailsRequestMethodDescriptor must be implemented in the sub classes"); + } + + get concurrencyManager() { + logger.error("concurrencyManager must be implemented in the sub classes"); + } +} + +export default BaseServiceClient; diff --git a/snet-sdk-js/packages/web/src/core/src/IPFSMetadataProvider.js b/snet-sdk-js/packages/web/src/core/src/IPFSMetadataProvider.js new file mode 100644 index 000000000..cb9c85ab2 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/IPFSMetadataProvider.js @@ -0,0 +1,86 @@ +import { find, map } from 'lodash'; +import url from 'url'; +import RegistryNetworks from 'singularitynet-platform-contracts/networks/Registry.json'; +import RegistryAbi from 'singularitynet-platform-contracts/abi/Registry.json'; +import { get } from 'axios'; + +import logger from './utils/logger'; + +export default class IPFSMetadataProvider { + constructor(web3, networkId, ipfsEndpoint) { + this._web3 = web3; + this._networkId = networkId; + this._ipfsEndpoint = ipfsEndpoint; + const registryAddress = RegistryNetworks[this._networkId].address; + this._registryContract = new this._web3.eth.Contract(RegistryAbi, registryAddress); + } + + /** + * @param {string} orgId + * @param {string} serviceId + * @returns {Promise.} + */ + async metadata(orgId, serviceId) { + logger.debug(`Fetching service metadata [org: ${orgId} | service: ${serviceId}]`); + let orgIdBytes = this._web3.utils.fromAscii(orgId); + orgIdBytes = orgIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters + + let serviceIdBytes = this._web3.utils.fromAscii(serviceId); + serviceIdBytes = serviceIdBytes.padEnd(66, '0'); // 66 = '0x' + 64 hex characters + + const orgMetadata = await this._fetchOrgMetadata(orgIdBytes); + const serviceMetadata = await this._fetchServiceMetadata(orgIdBytes, serviceIdBytes); + return Promise.resolve(this._enhanceServiceGroupDetails(serviceMetadata, orgMetadata)); + } + + async _fetchOrgMetadata(orgIdBytes) { + logger.debug('Fetching org metadata URI from registry contract'); + const { orgMetadataURI } = await this._registryContract.methods.getOrganizationById(orgIdBytes).call(); + + return this._fetchMetadataFromIpfs(orgMetadataURI); + } + + async _fetchServiceMetadata(orgIdBytes, serviceIdBytes) { + logger.debug('Fetching service metadata URI from registry contract'); + const { metadataURI: serviceMetadataURI } = await this._registryContract.methods + .getServiceRegistrationById(orgIdBytes, serviceIdBytes) + .call(); + return this._fetchMetadataFromIpfs(serviceMetadataURI); + } + + async _fetchMetadataFromIpfs(metadataURI) { + let ipfsCID = `${this._web3.utils.hexToUtf8(metadataURI).substring(7)}`; + ipfsCID = ipfsCID.replace(/\0/g, ''); + logger.debug(`Fetching metadata from IPFS[CID: ${ipfsCID}]`); + try { + const fetchUrl = `${this._ipfsEndpoint}/api/v0/cat?arg=${ipfsCID}`; + const response = await get(fetchUrl); + return response.data; + } catch (error) { + logger.debug(`Error fetching metadata from IPFS[CID: ${ipfsCID}]`); + throw error; + } + } + + _enhanceServiceGroupDetails(serviceMetadata, orgMetadata) { + const { groups: orgGroups } = orgMetadata; + const { groups: serviceGroups } = serviceMetadata; + + const groups = map(serviceGroups, (group) => { + const { group_name: serviceGroupName } = group; + const orgGroup = find(orgGroups, ({ group_name: orgGroupName }) => orgGroupName === serviceGroupName); + return { + ...group, + payment: orgGroup.payment, + }; + }); + + return { ...serviceMetadata, groups }; + } + + _constructIpfsClient() { + const { protocol = 'http', hostname: host, port = 5001 } = url.parse(this._ipfsEndpoint); + const ipfsHostOrMultiaddr = { protocol: protocol.replace(':', ''), host, port }; + return IPFSClient(ipfsHostOrMultiaddr); + } +} diff --git a/snet-sdk-js/packages/web/src/core/src/MPEContract.js b/snet-sdk-js/packages/web/src/core/src/MPEContract.js new file mode 100644 index 000000000..5f0776470 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/MPEContract.js @@ -0,0 +1,246 @@ +import MPEAbi from 'singularitynet-platform-contracts/abi/MultiPartyEscrow'; +import MPENetworks from 'singularitynet-platform-contracts/networks/MultiPartyEscrow'; +import { BigNumber } from 'bignumber.js'; +import { map } from 'lodash'; + +import PaymentChannel from './PaymentChannel'; +import logger from './utils/logger'; +import { toBNString } from './utils/bignumber_helper'; + +class MPEContract { + /** + * @param {Web3} web3 + * @param {number} networkId + */ + constructor(web3, networkId) { + this._web3 = web3; + this._networkId = networkId; + this._contract = new this._web3.eth.Contract(MPEAbi, MPENetworks[networkId].address); + } + + /** + * An instance of Multi Party Contract generated by Web3 + * @type {Contract} + * @see {@link https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html|Web3 Contract} + */ + get contract() { + return this._contract; + } + + /** + * The public address of the MPE account + * @type {string} + */ + get address() { + return this._contract.options.address; + } + + /** + * Returns the balance against the address in Multi Party Escrow Account + * @param {string} address - The public address of account + * @returns {Promise} + */ + async balance(address) { + logger.debug('Fetching MPE account balance', { tags: ['MPE'] }); + return this.contract.methods.balances(address).call(); + } + + /** + * Transfers tokens from the account to MPE account + * @param {Account} account - The account from which the tokens needs to be transferred. + * @param {BigNumber} amountInCogs - The amount to transfer in cogs + * @returns {Promise.} + */ + async deposit(account, amountInCogs) { + const amount = toBNString(amountInCogs); + logger.info(`Depositing ${amount}cogs to MPE account`, { tags: ['MPE'] }); + const depositOperation = this.contract.methods.deposit; + return account.sendTransaction(this.address, depositOperation, amount); + } + + /** + * Withdraws tokens from MPE account and deposits to the account + * @param {Account} account - The account to deposit tokens + * @param {BigNumber} amountInCogs - The amount to be withdrawn + * @returns {Promise.} + */ + async withdraw(account, amountInCogs) { + const amount = toBNString(amountInCogs); + logger.info(`Withdrawing ${amount}cogs from MPE account`, { tags: ['MPE'] }); + const withdrawOperation = this.contract.methods.withdraw; + return account.sendTransaction(this.address, withdrawOperation, amount); + } + + /** + * Opens a payment channel between an account and the given service with the specified tokens and expiry period + * @param {Account} account - The account to create payment channel for + * @param {ServiceClient} service - The AI service between which the payment channel needs to be opened + * @param {BigNumber} amountInCogs - The initial tokens with the which payment channel needs to be opened + * @param {BigNumber} expiry - The expiry of the payment channel in terms of block number + * @returns {Promise.} + */ + async openChannel(account, service, amountInCogs, expiry) { + const amount = toBNString(amountInCogs); + const expiryStr = toBNString(expiry); + const { payment_address: recipientAddress, group_id_in_bytes: groupId } = service.group; + + logger.info(`Opening new payment channel [amount: ${amount}, expiry: ${expiryStr}]`, { tags: ['MPE'] }); + const openChannelOperation = this.contract.methods.openChannel; + const signerAddress = await account.getSignerAddress(); + const openChannelFnArgs = [signerAddress, recipientAddress, groupId, amount, expiryStr]; + return account.sendTransaction(this.address, openChannelOperation, ...openChannelFnArgs); + } + + /** + * Deposits the specified tokens to MPE Account and opens a payment channel between an account and the given service + * with the specified tokens and expiry period + * @param {Account} account - The account against which the operations needs to be performed + * @param {ServiceClient} service - The AI service between which the payment channel needs to be opened + * @param {BigNumber} amountInCogs - The initial tokens with the which payment channel needs to be opened + * @param {BigNumber} expiry - The expiry of the payment channel in terms of block number + * @returns {Promise.} + */ + async depositAndOpenChannel(account, service, amountInCogs, expiry) { + const amount = toBNString(amountInCogs); + const expiryStr = toBNString(expiry); + const { payment_address: recipientAddress, group_id_in_bytes: groupId } = service.group; + const alreadyApprovedAmount = await account.allowance(); + if(amountInCogs > alreadyApprovedAmount) { + await account.approveTransfer(amountInCogs); + } + + const depositAndOpenChannelOperation = this.contract.methods.depositAndOpenChannel; + const signerAddress = await account.getSignerAddress(); + const operationArgs = [signerAddress, recipientAddress, groupId, amount, expiryStr]; + logger.info(`Depositing ${amount}cogs to MPE address and Opening new payment channel [expiry: ${expiryStr}]`, { + tags: ['MPE'], + }); + return account.sendTransaction(this.address, depositAndOpenChannelOperation, ...operationArgs); + } + + /** + * Funds an existing payment channel + * @param {Account} account - The account against which the operations needs to be performed + * @param {BigNumber} channelId - The payment channel id + * @param {BigNumber} amountInCogs - The number of tokens to fund the channel + * @returns {Promise.} + */ + async channelAddFunds(account, channelId, amountInCogs) { + const channelIdStr = toBNString(channelId); + const amount = toBNString(amountInCogs); + await this._fundEscrowAccount(account, amountInCogs); + + logger.info(`Funding PaymentChannel[id: ${channelIdStr}] with ${amount}cogs`, { tags: ['MPE'] }); + const channelAddFundsOperation = this.contract.methods.channelAddFunds; + return account.sendTransaction(this.address, channelAddFundsOperation, channelIdStr, amount); + } + + /** + * Extends an existing payment channel + * @param {Account} account - The account against which the operations needs to be performed + * @param {BigNumber} channelId - The payment channel id + * @param {BigNumber} expiry - The expiry in terms of block number to extend the channel + * @returns {Promise.} + */ + async channelExtend(account, channelId, expiry) { + const channelIdStr = toBNString(channelId); + const expiryStr = toBNString(expiry); + logger.info(`Extending PaymentChannel[id: ${channelIdStr}]. New expiry is block# ${expiryStr}`, { tags: ['MPE'] }); + const channelExtendOperation = this.contract.methods.channelExtend; + return account.sendTransaction(this.address, channelExtendOperation, channelIdStr, expiryStr); + } + + /** + * Extends and adds funds to an existing payment channel + * @param {Account} account - The account against which the operations needs to be performed + * @param {BigNumber} channelId - The payment channel id + * @param {BigNumber} expiry - The expiry in terms of block number to extend the channel + * @param {BigNumber} amountInCogs - The number of tokens to fund the channel + * @returns {Promise.} + */ + async channelExtendAndAddFunds(account, channelId, expiry, amountInCogs) { + const channelIdStr = toBNString(channelId); + const amount = toBNString(amountInCogs); + const expiryStr = toBNString(expiry); + await this._fundEscrowAccount(account, amountInCogs); + + logger.info( + `Extending and Funding PaymentChannel[id: ${channelIdStr}] with amount: ${amount} and expiry: ${expiryStr}`, + { tags: ['MPE'] }, + ); + const channelExtendAndAddFundsOperation = this.contract.methods.channelExtendAndAddFunds; + return account.sendTransaction(this.address, channelExtendAndAddFundsOperation, channelIdStr, expiryStr, amount); + } + + /** + * Claims unused tokens in a channel. + * @param {Account} account - The account against which the operations needs to be performed + * @param {BigNumber} channelId - Channel ID from which to claim the unused tokens + * @returns {Promise.} + */ + async channelClaimTimeout(account, channelId) { + const channelIdStr = toBNString(channelId); + logger.info(`Claiming unused funds from expired channel PaymentChannel[id: ${channelIdStr}]`, { tags: ['MPE'] }); + const channelClaimTimeoutOperation = this.contract.methods.channelClaimTimeout; + return account.sendTransaction(this.address, channelClaimTimeoutOperation, channelIdStr); + } + + /** + * Fetches the latest state of the payment channel + * @param {BigNumber} channelId - The payment channel id + * @returns {Promise} - The return value(s) of the smart contract method. If it returns a single value, it’s returned as is. If it has multiple return values they are returned as an object with properties and indices: + */ + async channels(channelId) { + const channelIdStr = toBNString(channelId); + logger.debug(`Fetch latest PaymentChannel[id: ${channelIdStr}] state`, { tags: ['MPE'] }); + return this.contract.methods.channels(channelIdStr).call(); + } + + /** + * Fetches all the payment channels opened between the account and the service starting from the given block number + * @param {Account} account + * @param {ServiceClient} service + * @param {number} [startingBlockNumber=MPE Contract deployment block number] - The starting block number to fetch the + * open channels from + * @returns {Promise.} + */ + async getPastOpenChannels(account, service, startingBlockNumber) { + const fromBlock = startingBlockNumber || (await this._deploymentBlockNumber()); + logger.debug(`Fetching all payment channel open events starting at block: ${fromBlock}`, { tags: ['MPE'] }); + + const address = await account.getAddress(); + const decodedData = Buffer.from(service.group.group_id, 'base64').toString('hex'); + const groupId = `0x${decodedData}`; + const options = { + filter: { + sender: address, + recipient: service.group.payment_address, + groupId, + }, + fromBlock, + toBlock: 'latest', + }; + const channelsOpened = await this.contract.getPastEvents('ChannelOpen', options); + return map(channelsOpened, (channelOpenEvent) => { + const { channelId } = channelOpenEvent.returnValues; + return new PaymentChannel(channelId, this._web3, account, service, this); + }); + } + + async _fundEscrowAccount(account, amountInCogs) { + const address = await account.getAddress(); + let currentEscrowBalance = await this.balance(address); + currentEscrowBalance = toBNString(currentEscrowBalance); + if(amountInCogs > currentEscrowBalance) { + await account.depositToEscrowAccount(amountInCogs - currentEscrowBalance); + } + } + + async _deploymentBlockNumber() { + const { transactionHash } = MPENetworks[this._networkId]; + const { blockNumber } = await this._web3.eth.getTransactionReceipt(transactionHash); + return blockNumber; + } +} + +export default MPEContract; diff --git a/snet-sdk-js/packages/web/src/core/src/PaymentChannel.js b/snet-sdk-js/packages/web/src/core/src/PaymentChannel.js new file mode 100644 index 000000000..02ad9b7a6 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/PaymentChannel.js @@ -0,0 +1,133 @@ +import BigNumber from 'bignumber.js'; +import isEmpty from 'lodash/isEmpty'; + +import logger from './utils/logger'; +import { toBNString } from './utils/bignumber_helper'; + +class PaymentChannel { + /** + * @param {BigNumber} channelId + * @param {Web3} web3 + * @param {Account} account + * @param {BaseServiceClient} service + * @param {MPEContract} mpeContract + */ + constructor(channelId, web3, account, service, mpeContract) { + this._channelId = channelId; + this._web3 = web3; + this._account = account; + this._mpeContract = mpeContract; + this._serviceClient = service; + this._state = { + nonce: new BigNumber(0), + currentSignedAmount: new BigNumber(0), + }; + } + + /** + * @type {BigNumber} + */ + get channelId() { + return this._channelId; + } + + /** + * @type {PaymentChannelState} + */ + get state() { + return this._state; + } + + /** + * Adds fund to the payment channel + * @param {BigNumber} amount + * @returns {Promise.} + */ + async addFunds(amount) { + return this._mpeContract.channelAddFunds(this._account, this.channelId, amount); + } + + /** + * Extends the expiry of the payment channel + * @param {BigNumber} expiry - Expiry in terms of block number + * @returns {Promise.} + */ + async extendExpiry(expiry) { + return this._mpeContract.channelExtend(this._account, this.channelId, expiry); + } + + /** + * Extends the expiry of the payment channel and add funds to it + * @param {BigNumber} expiry + * @param {BigNumber} amount + * @returns {Promise.} + */ + async extendAndAddFunds(expiry, amount) { + return this._mpeContract.channelExtendAndAddFunds(this._account, this.channelId, expiry, amount); + } + + /** + * Claims unused tokens in the channel from the MPE Account. + * @returns {Promise.} + */ + async claimUnusedTokens() { + return this._mpeContract.channelClaimTimeout(this._account, this.channelId); + } + + /** + * Updates the state of the payment channel by fetching latest info from the mpe contract and the ai service daemon + * @returns {Promise} + */ + async syncState() { + logger.debug(`Syncing PaymentChannel[id: ${this._channelId}] state`, { tags: ['PaymentChannel'] }); + const latestChannelInfoOnBlockchain = await this._mpeContract.channels(this.channelId); + const currentState = await this._currentChannelState(); + const { currentSignedAmount, nonce: currentNonce } = currentState; + const { nonce, expiration: expiry, value: amountDeposited } = latestChannelInfoOnBlockchain; + const availableAmount = toBNString(amountDeposited) - toBNString(currentSignedAmount); + this._state = { + nonce: nonce.toString(), + currentNonce, + expiry, + amountDeposited, + currentSignedAmount, + availableAmount, + }; + logger.debug(`Latest PaymentChannel[id: ${this.channelId}] state:`, this._state, { tags: ['PaymentChannel'] }); + return Promise.resolve(this); + } + + async _currentChannelState() { + logger.debug(`Fetching latest PaymentChannel[id: ${this.channelId}] state from service daemon`, { + tags: ['PaymentChannel'], + }); + try { + const response = await this._serviceClient.getChannelState(this.channelId); + const nonce = PaymentChannel._uint8ArrayToBN(response.getCurrentNonce()); + const currentSignedAmount = PaymentChannel._uint8ArrayToBN(response.getCurrentSignedAmount()); + const channelState = { + currentSignedAmount, + nonce, + }; + return Promise.resolve(channelState); + } catch (err) { + logger.error( + `Failed to fetch latest PaymentChannel[id: ${this.channelId}] state from service daemon. ${err.message}`, + { tags: ['PaymentChannel'] }, + ); + return Promise.reject(err); + } + } + + static _uint8ArrayToBN(uint8Array) { + if(isEmpty(uint8Array)) { + return new BigNumber(0); + } + + const buffer = Buffer.from(uint8Array); + const hex = `0x${buffer.toString('hex')}`; + return new BigNumber(hex); + } +} + +export default PaymentChannel; diff --git a/snet-sdk-js/packages/web/src/core/src/identities/MetaMaskIdentity.js b/snet-sdk-js/packages/web/src/core/src/identities/MetaMaskIdentity.js new file mode 100644 index 000000000..863391df9 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/identities/MetaMaskIdentity.js @@ -0,0 +1,67 @@ +import Web3 from 'web3'; +import { ethereumMethods } from '../utils/ethereumUtils'; + +import logger from '../utils/logger'; +import blockChainEvents from '../utils/blockchainEvents'; + +/** + * @implements Identity + */ +class MetaMaskIdentity { + /** + * @param {Config} config + * @param {Web3} web3 + */ + constructor(config, web3) { + this._eth = new Web3(config.web3Provider); + this._web3 = web3; + this.setupAccount(); + } + + async getAddress() { + const { ethereum } = window; + const accounts = await ethereum.request({ method: ethereumMethods.REQUEST_ACCOUNTS }); + return accounts[0]; + } + + async signData(sha3Message) { + const address = await this.getAddress(); + const { ethereum } = window; + + return ethereum.request({ + method: 'personal_sign', + params: [sha3Message, address], + }); + } + + async sendTransaction(transactionObject) { + return new Promise((resolve, reject) => { + const method = this._web3.eth + .sendTransaction(transactionObject) + .on(blockChainEvents.ERROR, (error) => { + logger.error(`Couldn't send transaction. ${error}`); + reject(error); + }) + .once(blockChainEvents.CONFIRMATION, async (_confirmationNumber, receipt) => { + if(_confirmationNumber.receipt.status) { + resolve(_confirmationNumber.receipt); + } else { + reject(_confirmationNumber.receipt); + } + // await method.off(); + }); + }); + } + + async setupAccount() { + const { ethereum } = window; + if(typeof ethereum !== 'undefined') { + const accounts = await ethereum.request({ method: ethereumMethods.REQUEST_ACCOUNTS }); + this._web3.eth.defaultAccount = accounts[0]; + } else { + logger.error('Metamask is not installed'); + } + } +} + +export default MetaMaskIdentity; diff --git a/snet-sdk-js/packages/web/src/core/src/identities/PrivateKeyIdentity.js b/snet-sdk-js/packages/web/src/core/src/identities/PrivateKeyIdentity.js new file mode 100644 index 000000000..328d59d1e --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/identities/PrivateKeyIdentity.js @@ -0,0 +1,70 @@ +import blockChainEvents from '../utils/blockchainEvents'; +/** + * @implements Identity + */ +class PrivateKeyIdentity { + /** + * @param {Config} config + * @param {Web3} web3 + */ + constructor(config, web3) { + this._web3 = web3; + this._pk = config.privateKey; + this._setupAccount(); + } + + get address() { + return this._web3.eth.defaultAccount; + } + + async getAddress() { + return this._web3.eth.defaultAccount; + } + + async signData(sha3Message) { + const { signature } = this._web3.eth.accounts.sign(sha3Message, this._pk); + return signature; + } + + async sendTransaction(transactionObject) { + const signedTransaction = await this._signTransaction(transactionObject); + return new Promise((resolve, reject) => { + const method = this._web3.eth.sendSignedTransaction(signedTransaction); + method.once(blockChainEvents.CONFIRMATION, async (_confirmationNumber, receipt) => { + console.log('blockchain confirmation count', _confirmationNumber); + console.log('blockchain confirmation receipt status', _confirmationNumber.receipt.status); + if(_confirmationNumber.receipt.status) { + resolve(_confirmationNumber.receipt); + } else { + reject(_confirmationNumber.receipt); + } + // await method.off(); + }); + method.on(blockChainEvents.ERROR, (error) => { + console.log('blockchain error', error); + reject(error); + }); + method.once(blockChainEvents.TRANSACTION_HASH, (hash) => { + console.log('waiting for blockchain txn', hash); + }); + method.once(blockChainEvents.RECEIPT, (receipt) => { + console.log('blockchain receipt', receipt.status); + }); + }); + } + + async _signTransaction(txObject) { + delete txObject.chainId; + const privateKey = Buffer.from(this._pk.slice(2), 'hex'); + const signedTx = await this._web3.eth.accounts.signTransaction(txObject, privateKey); + return signedTx.rawTransaction; + } + + _setupAccount() { + const account = this._web3.eth.accounts.privateKeyToAccount(this._pk); + this._web3.eth.accounts.wallet.add(account); + this._web3.eth.defaultAccount = account.address; + } +} + +export default PrivateKeyIdentity; diff --git a/snet-sdk-js/packages/web/src/core/src/identities/index.js b/snet-sdk-js/packages/web/src/core/src/identities/index.js new file mode 100644 index 000000000..1f9dbe29c --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/identities/index.js @@ -0,0 +1,2 @@ +export { default as PrivateKeyIdentity } from './PrivateKeyIdentity'; +export { default as MetaMaskIdentity } from './MetaMaskIdentity'; diff --git a/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/DefaultPaymentChannelManagementStrategy.js b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/DefaultPaymentChannelManagementStrategy.js new file mode 100644 index 000000000..477e8b33b --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/DefaultPaymentChannelManagementStrategy.js @@ -0,0 +1,83 @@ +import { find } from 'lodash'; +import { BigNumber } from 'bignumber.js'; + +/** + * @implements PaymentChannelManagementStrategy + */ +class DefaultPaymentChannelManagementStrategy { + /** + * @param {SnetSDK} sdkContext + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(sdkContext, blockOffset = 0, callAllowance = 1) { + this._sdkContext = sdkContext; + this._blockOffset = blockOffset; + this._callAllowance = callAllowance; + } + + async selectChannel(serviceClient) { + const { account } = this._sdkContext; + await serviceClient.loadOpenChannels(); + await serviceClient.updateChannelStates(); + const { paymentChannels } = serviceClient; + const serviceCallPrice = this._pricePerServiceCall(serviceClient) * this._callAllowance; + const mpeBalance = await account.escrowBalance(); + const defaultExpiry = await this._defaultChannelExpiry(serviceClient); + + if(paymentChannels.length === 0) { + if(mpeBalance >= serviceCallPrice) { + return serviceClient.openChannel(serviceCallPrice, defaultExpiry); + } + + return serviceClient.depositAndOpenChannel(serviceCallPrice, defaultExpiry); + } + + const firstFundedValidChannel = find( + paymentChannels, + (paymentChanel) => this._hasSufficientFunds(paymentChanel, serviceCallPrice) && this._isValid(paymentChanel, defaultExpiry), + ); + if(firstFundedValidChannel) { + return firstFundedValidChannel; + } + + const firstFundedChannel = find(paymentChannels, (paymentChanel) => this._hasSufficientFunds(paymentChanel, serviceCallPrice), + ); + if(firstFundedChannel) { + await firstFundedChannel.extendExpiry(defaultExpiry); + return firstFundedChannel; + } + + const firstValidChannel = find(paymentChannels, (paymentChanel) => this._isValid(paymentChanel, defaultExpiry)); + if(firstValidChannel) { + await firstValidChannel.addFunds(serviceCallPrice); + return firstValidChannel; + } + + const firstExpiredAndUnfundedChannel = paymentChannels[0]; + await firstExpiredAndUnfundedChannel.extendAndAddFunds(defaultExpiry, serviceCallPrice); + return firstExpiredAndUnfundedChannel; + } + + _pricePerServiceCall(serviceClient) { + const { pricing } = serviceClient.group; + const fixedPricing = find(pricing, ({ price_model }) => price_model === "fixed_price"); + + return new BigNumber(fixedPricing.price_in_cogs); + } + + _hasSufficientFunds(paymentChannel, amount) { + return paymentChannel.state.availableAmount >= amount; + } + + _isValid(paymentChannel, expiry) { + return paymentChannel.state.expiry > expiry; + } + + async _defaultChannelExpiry(serviceClient) { + const currentBlockNumber = await this._sdkContext.web3.eth.getBlockNumber(); + return currentBlockNumber + serviceClient.group.payment_expiration_threshold + this._blockOffset; + } +} + +export default DefaultPaymentChannelManagementStrategy; diff --git a/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/PrepaidPaymentChannelManagementStrategy.js b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/PrepaidPaymentChannelManagementStrategy.js new file mode 100644 index 000000000..e4d2104cd --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/PrepaidPaymentChannelManagementStrategy.js @@ -0,0 +1,49 @@ +import DefaultPaymentChannelManagementStrategy from './DefaultPaymentChannelManagementStrategy'; + +class PrepaidPaymentChannelManagementStrategy extends DefaultPaymentChannelManagementStrategy { + constructor(sdkContext, blockOffset = 240, callAllowance = 1, concurrencyManager) { + super(sdkContext, blockOffset, callAllowance); + this._sdkContext = sdkContext; + this._blockOffset = blockOffset; + this._callAllowance = callAllowance; + this._concurrencyManager = concurrencyManager; + } + + _priceForConcurrentCalls(serviceClient) { + return this._pricePerServiceCall(serviceClient) * this._concurrencyManager.concurrentCalls; + } + + async selectChannel(serviceClient) { + const { account } = serviceClient; + await serviceClient.loadOpenChannels(); + await serviceClient.updateChannelStates(); + const { paymentChannels } = serviceClient; + const concurrentCallsPrice = this._priceForConcurrentCalls(serviceClient); + const extendChannelFund = concurrentCallsPrice * this._callAllowance; + const mpeBalance = await account.escrowBalance(); + const defaultExpiry = await this._defaultChannelExpiry(serviceClient); + const offsetExpiry = defaultExpiry + this.blockOffset; + let paymentChannel; + if(paymentChannels.length < 1) { + if(concurrentCallsPrice > mpeBalance) { + paymentChannel = serviceClient.depositAndOpenChannel(concurrentCallsPrice, offsetExpiry); + } else { + paymentChannel = serviceClient.openChannel(concurrentCallsPrice, offsetExpiry); + } + } else { + paymentChannel = paymentChannels[0]; + } + + const hasSufficientFunds = this._hasSufficientFunds(paymentChannel, concurrentCallsPrice); + const isValid = this._isValid(paymentChannel, defaultExpiry); + + if(hasSufficientFunds && !isValid) { + await paymentChannel.extendExpiry(offsetExpiry); + } else if(!hasSufficientFunds && isValid) { + await paymentChannel.addFunds(extendChannelFund); + } else if(!hasSufficientFunds && !isValid) { + await paymentChannel.extendAndAddFunds(offsetExpiry, extendChannelFund); + } + return paymentChannel; + } +} diff --git a/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/index.js b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/index.js new file mode 100644 index 000000000..e3c4ac953 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/payment_channel_management_strategies/index.js @@ -0,0 +1 @@ +export { default as DefaultPaymentChannelManagementStrategy } from './DefaultPaymentChannelManagementStrategy'; diff --git a/snet-sdk-js/packages/web/src/core/src/sdk.js b/snet-sdk-js/packages/web/src/core/src/sdk.js new file mode 100644 index 000000000..7528ef648 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/sdk.js @@ -0,0 +1,94 @@ +import Web3 from 'web3'; +import { find, first } from 'lodash'; + +import Account from './Account'; +import MPEContract from './MPEContract'; +import logger from './utils/logger'; +import IPFSMetadataProvider from './IPFSMetadataProvider'; +import { DefaultPaymentStrategy } from '../../payment_strategies'; + +const DEFAULT_CONFIG = { + defaultGasLimit: 210000, + defaultGasPrice: 4700000, + ipfsEndpoint: 'http://ipfs.singularitynet.io:80', +}; + +class SnetSDK { + /** + * @param {Config} config + * @param {MetadataProvider} metadataProvider + */ + constructor(config, metadataProvider = undefined) { + this._config = { + ...DEFAULT_CONFIG, + ...config, + }; + const options = { + defaultGas: this._config.defaultGasLimit, + defaultGasPrice: this._config.defaultGasPrice, + }; + this._networkId = config.networkId; + this._web3 = new Web3(config.web3Provider, null, options); + const identity = this._createIdentity(); + this._mpeContract = new MPEContract(this._web3, this._networkId); + this._account = new Account(this._web3, this._networkId, this._mpeContract, identity); + this._metadataProvider = metadataProvider || new IPFSMetadataProvider(this._web3, this._networkId, this._config.ipfsEndpoint); + } + + /** + * @type {Account} + */ + get account() { + return this._account; + } + + /** + * @type {Web3} + */ + get web3() { + return this._web3; + } + + set paymentChannelManagementStrategy(paymentChannelManagementStrategy) { + this._paymentChannelManagementStrategy = paymentChannelManagementStrategy; + } + + async _serviceGroup(serviceMetadata, orgId, serviceId, groupName = undefined) { + const group = this._findGroup(serviceMetadata.groups, groupName); + if(!group) { + const errorMessage = `Group[name: ${groupName}] not found for orgId: ${orgId} and serviceId: ${serviceId}`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + return group; + } + + _findGroup(groups, groupName) { + if(!groupName) { + return first(groups); + } + + return find(groups, ({ group_name }) => group_name === groupName); + } + + _constructStrategy(paymentChannelManagementStrategy, concurrentCalls = 1) { + if (paymentChannelManagementStrategy) { + return paymentChannelManagementStrategy; + } + + if (this._paymentChannelManagementStrategy) { + return this._paymentChannelManagementStrategy; + } + + logger.debug('PaymentChannelManagementStrategy not provided, using DefaultPaymentChannelManagementStrategy'); + // return new DefaultPaymentChannelManagementStrategy(this); + return new DefaultPaymentStrategy(concurrentCalls); + } + + _createIdentity() { + logger.error('_createIdentity must be implemented in the sub classes'); + } +} + +export default SnetSDK; diff --git a/snet-sdk-js/packages/web/src/core/src/types/grpc_type_defs.jsdoc b/snet-sdk-js/packages/web/src/core/src/types/grpc_type_defs.jsdoc new file mode 100644 index 000000000..c2c23c81e --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/types/grpc_type_defs.jsdoc @@ -0,0 +1,5 @@ +/** + * GRPC client constructor + * @typedef GRPCClient + * @see {@link https://grpc.github.io/grpc/node/grpc.Client.html|GRPC Client} + */ diff --git a/snet-sdk-js/packages/web/src/core/src/types/snet_type_defs.jsdoc b/snet-sdk-js/packages/web/src/core/src/types/snet_type_defs.jsdoc new file mode 100644 index 000000000..05b6c0eaa --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/types/snet_type_defs.jsdoc @@ -0,0 +1,185 @@ +/** + * @typedef {Object} Config + * @property {string|object} web3Provider - A URL or one of the Web3 provider classes. + * @property {string} privateKey - The accounts private key for performing token operations. + * @property {string} signerPrivateKey - The accounts private key for signing grpc calls to service. + * @property {number} networkId - Ethereum network ID. + * @property {string} [ipfsEndpoint=http://ipfs.singularitynet.io:80] - A URL for fetching service related metadata. + * @property {number} [defaultGasPrice=4700000] - The gas price to be used in case of fetching the gas price form the blockchain fails. + * @property {number} [defaultGasLimit=210000] - The gas limit to be used in case of fetching the gas estimate from the blockchain fails. + */ + +/** + * @typedef {Object} ServiceClientOptions + * @property {string} email + * @property {string} tokenToMakeFreeCall + * @property {number} tokenExpirationBlock + * @property {boolean} disableBlockchainOperations + * @property {string} [endpoint] + * @property {boolean} concurrency + */ + +/** + * typedef {Object} StateRequestSignerResponse + * @property {Number} currentBlockNumber + * @property {String} signatureBytes + */ + +/** + * @function + * @name ServiceClientOptions#channelStateRequestSigner + * @param {Number} channelId + * @returns {StateRequestSignerResponse} The state request signer response + */ + +/** + * typedef {Object} FreeCallSignature + * @property {String} signatureBytes + */ + +/** + * @function + * @name ServiceClientOptions#paidCallMetadataGenerator + * @param {Number} channelId + * @param {Number} signingAmount + * @param {Number} nonce + * @returns {FreeCallSignature} Free call signature object + */ + +/** + * typedef {Object} FreeCallGRPCMetadata + * @property {String} snet-payment-type + * @property {String} snet-free-call-user-id + * @property {String} snet-current-block-number + * @property {String} snet-payment-channel-signature-bin + */ + + +/** + * @function + * @name ServiceClientOptions#metadataGenerator + * @param {ServiceClient} serviceClient + * @param {String} serviceName + * @param {String} method + * @returns {FreeCallGRPCMetadata} Free call metadata to be appended for service method invocation + */ + +/** + * Interface for implementing identity. + * + * @interface Identity + */ + +/** + * Returns the address for the given identity + * + * @function + * @name Identity#address + * @returns {String} The wallet address of the identity + */ + +/** + * Signs data using a specific account in the identity + * + * @function + * @name Identity#signData + * @param {String} message Data to sign + * @returns {String} the signature + */ + +/** + * @typedef {Object} TransactionObject + * @property {String} nonce + * @property {String} gas + * @property {String} gasPrice + * @property {String} to + * @property {String} data + */ + +/** + * Sends a transaction to the network + * + * @function + * @name Identity#sendTransaction + * @param {TransactionObject} transactionObject + * @returns {Promise.} the transaction hash + */ + +/** + * Interface for implementing payment channel management strategies. + * + * @interface PaymentChannelManagementStrategy + */ + +/** + * Select a payment channel for the given service + * + * @function + * @name PaymentChannelManagementStrategy#selectChannel + * @param {ServiceClient} serviceClient + * @returns {Promise.} The payment channel used for making a grpc call to the ai service + */ + +/** + * @typedef {Object} Pricing + * @property {Number} pricing_in_cogs + * @property {String} pricing_model + * @property {Boolean} default + */ + +/** + * @typedef {Object} GroupPayment + * @property {String} payment_address + * @property {Number} payment_expiration_threshold + * @property {String} payment_channel_storage_type + * @property {Object} payment_channel_storage_client + */ + +/** + * @typedef {Object} Group + * @property {String} group_id + * @property {String} group_name + * @property {GroupPayment} payment + * @property {Pricing[]} pricing + * @property {String[]} endpoints + */ + +/** + * @typedef {Object} ServiceMetadata + * @property {String} display_name + * @property {String} encoding + * @property {String} service_type + * @property {String} model_ipfs_hash + * @property {String} mpe_address + * @property {Object} assets + * @property {Group[]} groups + * @property {Object} service_description + */ + +/** + * @typedef {Object} PaymentChannelState + * @property {BigNumber} [nonce=0] + * @property {BigNumber} [currentNonce] + * @property {BigNumber} [expiry] + * @property {BigNumber} [amountDeposited] + * @property {BigNumber} [currentSignedAmount=0] + * @property {BigNumber} [availableAmount] + */ + +/** + * @typedef {Object} ServiceDetails + * @property {String} orgId + * @property {String} serviceId + * @property {String} groupId + * @property {String} daemonEndpoint + */ + + /** + * @typedef {Object} FreeCallConfig + * @property {String} email + * @property {String} tokenToMakeFreeCall + * @property {String} tokenExpirationBlock + */ + + + diff --git a/snet-sdk-js/packages/web/src/core/src/types/web3_type_defs.jsdoc b/snet-sdk-js/packages/web/src/core/src/types/web3_type_defs.jsdoc new file mode 100644 index 000000000..579f8e9be --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/types/web3_type_defs.jsdoc @@ -0,0 +1,5 @@ +/** + * The transaction receipt same as returned from Web3 sendTransaction + * @typedef {object} TransactionReceipt + * @see {@link https://web3js.readthedocs.io/en/1.0/web3-eth.html#eth-sendtransaction-return|Web3 TransactionReceipt} + */ diff --git a/snet-sdk-js/packages/web/src/core/src/utils/bignumber_helper.js b/snet-sdk-js/packages/web/src/core/src/utils/bignumber_helper.js new file mode 100644 index 000000000..02fa92258 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/utils/bignumber_helper.js @@ -0,0 +1,5 @@ +import { BigNumber } from 'bignumber.js'; + +export function toBNString(value) { + return new BigNumber(value).toFixed(); +} diff --git a/snet-sdk-js/packages/web/src/core/src/utils/blockchainEvents.js b/snet-sdk-js/packages/web/src/core/src/utils/blockchainEvents.js new file mode 100644 index 000000000..61023eb70 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/utils/blockchainEvents.js @@ -0,0 +1,6 @@ +export default { + TRANSACTION_HASH: 'transactionHash', + RECEIPT: 'receipt', + CONFIRMATION: 'confirmation', + ERROR: 'error', +}; diff --git a/snet-sdk-js/packages/web/src/core/src/utils/encodingUtils.js b/snet-sdk-js/packages/web/src/core/src/utils/encodingUtils.js new file mode 100644 index 000000000..a21845706 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/utils/encodingUtils.js @@ -0,0 +1,17 @@ +class EncodingUtils { + hexStringToBytes(hex) { + let strippedHex = hex; + if(strippedHex.substring(0, 2).toLowerCase() === '0x') { + strippedHex = strippedHex.substring(2, strippedHex.length); + } + const bytes = Buffer.from(strippedHex, 'hex'); + return Buffer.from(bytes); + } + + utfStringToBytes(string) { + const bytes = Buffer.from(string, 'UTF-8'); + return Buffer.from(bytes); + } +} + +export default EncodingUtils; diff --git a/snet-sdk-js/packages/web/src/core/src/utils/ethereumUtils.js b/snet-sdk-js/packages/web/src/core/src/utils/ethereumUtils.js new file mode 100644 index 000000000..a0feaa253 --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/utils/ethereumUtils.js @@ -0,0 +1,3 @@ +export const ethereumMethods = { + REQUEST_ACCOUNTS: "eth_requestAccounts", +}; diff --git a/snet-sdk-js/packages/web/src/core/src/utils/logger.js b/snet-sdk-js/packages/web/src/core/src/utils/logger.js new file mode 100644 index 000000000..5bfd102ed --- /dev/null +++ b/snet-sdk-js/packages/web/src/core/src/utils/logger.js @@ -0,0 +1,9 @@ +import createLogger from 'winston/dist/winston/create-logger'; +import { format } from 'logform'; + +const logger = createLogger({ + format: format.simple(), + level: process.env.DEBUG ? 'silly' : 'info', +}); + +export default logger; diff --git a/snet-sdk-js/packages/web/src/index.js b/snet-sdk-js/packages/web/src/index.js new file mode 100644 index 000000000..26eeef898 --- /dev/null +++ b/snet-sdk-js/packages/web/src/index.js @@ -0,0 +1,8 @@ +import WebSdk from './WebSdk'; +import './utils/logger'; + +export default WebSdk; + +export * from './sdk-core'; +export { default as WebServiceClient } from './WebServiceClient'; +export * from "./payment_strategies"; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/BasePaidPaymentStrategy.js b/snet-sdk-js/packages/web/src/payment_strategies/BasePaidPaymentStrategy.js new file mode 100644 index 000000000..273f64400 --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/BasePaidPaymentStrategy.js @@ -0,0 +1,79 @@ +import { logger } from '../sdk-core'; + +class BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, blockOffset = 240, callAllowance = 1) { + this._serviceClient = serviceClient; + this._blockOffset = blockOffset; + this._callAllowance = callAllowance; + } + + /** + * @returns {Promise} + * @protected + */ + async _selectChannel() { + const { account } = this._serviceClient; + await this._serviceClient.loadOpenChannels(); + await this._serviceClient.updateChannelStates(); + const { paymentChannels } = this._serviceClient; + const serviceCallPrice = this._getPrice(); + const extendedChannelFund = serviceCallPrice * this._callAllowance; + const mpeBalance = await account.escrowBalance(); + const defaultExpiration = await this._serviceClient.defaultChannelExpiration(); + const extendedExpiry = defaultExpiration + this._blockOffset; + + let selectedPaymentChannel; + if(paymentChannels.length < 1) { + if(serviceCallPrice > mpeBalance) { + selectedPaymentChannel = await this._serviceClient.depositAndOpenChannel(serviceCallPrice, extendedExpiry); + } else { + selectedPaymentChannel = await this._serviceClient.openChannel(serviceCallPrice, extendedExpiry); + } + } else { + selectedPaymentChannel = paymentChannels[0]; + } + + const hasSufficientFunds = this._doesChannelHaveSufficientFunds(selectedPaymentChannel, serviceCallPrice); + const isValid = this._isValidChannel(selectedPaymentChannel, defaultExpiration); + if(hasSufficientFunds && !isValid) { + await selectedPaymentChannel.extendExpiry(extendedExpiry); + } else if(!hasSufficientFunds && isValid) { + await selectedPaymentChannel.addFunds(extendedChannelFund); + } else if(!hasSufficientFunds && !isValid) { + await selectedPaymentChannel.extendAndAddFunds(extendedExpiry, extendedChannelFund); + } + return selectedPaymentChannel; + } + + _getPrice() { + logger.error('_getPrice must be implemented in the sub classes'); + } + + /** + * @param {PaymentChannel} channel + * @param {number} requiredAmount + * @returns {boolean} + * @private + */ + _doesChannelHaveSufficientFunds(channel, requiredAmount) { + return channel.state.availableAmount >= requiredAmount; + } + + /** + * + * @param {PaymentChannel} channel + * @param {number} expiry + * @returns {boolean} + * @private + */ + _isValidChannel(channel, expiry) { + return channel.state.expiry >= expiry; + } +} + +export default BasePaidPaymentStrategy; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/DefaultPaymentStrategy.js b/snet-sdk-js/packages/web/src/payment_strategies/DefaultPaymentStrategy.js new file mode 100644 index 000000000..914cefca9 --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/DefaultPaymentStrategy.js @@ -0,0 +1,33 @@ +import PrepaidPaymentStrategy from './PrepaidPaymentStrategy'; +import ConcurrencyManager from '../ConcurrencyManager'; +import PaidCallPaymentStrategy from './PaidCallPaymentStrategy'; + +class DefaultPaymentStrategy { + /** + * Initializing the payment strategy + * @param {number} concurrentCalls + */ + constructor(concurrentCalls = 1) { + this._concurrentCalls = concurrentCalls; + } + + /** + * map the metadata for the gRPC call + * @param {BaseServiceClient} serviceClient + * @returns {Promise<({'snet-payment-type': string}|{'snet-payment-channel-id': string}|{'snet-payment-channel-nonce': string}|{'snet-payment-channel-amount': string}|{'snet-payment-channel-signature-bin': string.base64})[]>} + */ + async getPaymentMetadata(serviceClient) { + let metadata; + if(serviceClient.concurrencyFlag) { + const concurrencyManager = new ConcurrencyManager(this._concurrentCalls, serviceClient); + const paymentStrategy = new PrepaidPaymentStrategy(serviceClient, concurrencyManager); + metadata = await paymentStrategy.getPaymentMetadata(); + } else { + const paymentStrategy = new PaidCallPaymentStrategy(serviceClient); + metadata = await paymentStrategy.getPaymentMetadata(); + } + return metadata; + } +} + +export default DefaultPaymentStrategy; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/FreeCallPaymentStrategy.js b/snet-sdk-js/packages/web/src/payment_strategies/FreeCallPaymentStrategy.js new file mode 100644 index 000000000..76119e9b3 --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/FreeCallPaymentStrategy.js @@ -0,0 +1,135 @@ +import { EncodingUtils } from '../sdk-core'; +import { FreeCallStateServiceClient } from '../proto/state_service_pb_service'; + +class FreeCallPaymentStrategy { + constructor(serviceClient) { + this._serviceClient = serviceClient; + this._freeCallStateServiceClient = this._generateFreeCallStateServiceClient(); + this._encodingUtils = new EncodingUtils(); + } + + /** + * Check if there is any freecalls left for x service. + * @returns {Promise} + */ + async isFreeCallAvailable() { + try { + const freeCallsAvailableReply = await this._getFreeCallsAvailable(); + const freeCallsAvailable = freeCallsAvailableReply.getFreeCallsAvailable(); + console.log('freeCallsAvailable', freeCallsAvailable); + return freeCallsAvailable > 0; + } catch (error) { + console.log('error', error); + return false; + } + } + + /** + * generate free call payment metadata + * @returns {Promise<({'snet-free-call-auth-token-bin': FreeCallConfig.tokenToMakeFreeCall}|{'snet-free-call-token-expiry-block': *}|{'snet-payment-type': string}|{'snet-free-call-user-id': *}|{'snet-current-block-number': *})[]>} + */ + async getPaymentMetadata() { + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + const signature = await this._generateSignature(currentBlockNumber); + + const tokenBytes = this._encodingUtils.hexStringToBytes(tokenToMakeFreeCall) + + const metadata = [ + { 'snet-free-call-auth-token-bin': tokenBytes.toString('base64') }, + { 'snet-free-call-token-expiry-block': `${tokenExpiryDateBlock}` }, + { 'snet-payment-type': 'free-call' }, + { 'snet-free-call-user-id': email }, + { 'snet-current-block-number': `${currentBlockNumber}` }, + { 'snet-payment-channel-signature-bin': signature.toString('base64') }]; + return metadata; + } + + /** + * fetch the free calls available data from daemon + * @returns {Promise} + * @private + */ + async _getFreeCallsAvailable() { + const freeCallStateRequest = await this._getFreeCallStateRequest(); + return new Promise((resolve, reject) => { + this._freeCallStateServiceClient.getFreeCallsAvailable(freeCallStateRequest, (error, responseMessage) => { + if(error) { + console.log('freecalls error', error); + reject(error); + } else { + resolve(responseMessage); + } + }); + }); + } + + /** + * + * @returns {Promise>>} + * @private + */ + async _generateSignature(currentBlockNumber) { + const { orgId, serviceId, groupId } = this._serviceClient.getServiceDetails(); + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + if(tokenExpiryDateBlock === 0 || !email || email.length === 0 || !tokenToMakeFreeCall || tokenToMakeFreeCall.length === 0) { + throw Error('invalid entries'); + } + + return this._serviceClient.signData( + { t: 'string', v: '__prefix_free_trial' }, + { t: 'string', v: email }, + { t: 'string', v: orgId }, + { t: 'string', v: serviceId }, + { t: 'string', v: groupId }, + { t: 'uint256', v: currentBlockNumber }, + { t: 'bytes', v: tokenToMakeFreeCall.substring(2, tokenToMakeFreeCall.length) }, + ); + } + + /** + * create the request for the freecall state service grpc + * @returns {FreeCallStateRequest} + * @private + */ + async _getFreeCallStateRequest() { + const Request = this._freeCallStateServiceClient.getFreeCallsAvailable.requestType; + const request = new Request(); + + const { + userId, tokenForFreeCall, tokenExpiryDateBlock, signature, currentBlockNumber, + } = await this._getFreeCallStateRequestProperties(); + + const tokenBytes = this._encodingUtils.hexStringToBytes(tokenForFreeCall) + + request.setUserId(userId); + request.setTokenForFreeCall(tokenBytes); + request.setTokenExpiryDateBlock(tokenExpiryDateBlock); + request.setSignature(signature); + request.setCurrentBlock(currentBlockNumber); + return request; + } + + async _getFreeCallStateRequestProperties() { + const { email, tokenToMakeFreeCall, tokenExpiryDateBlock } = this._serviceClient.getFreeCallConfig(); + const currentBlockNumber = await this._serviceClient.getCurrentBlockNumber(); + const signature = await this._generateSignature(currentBlockNumber); + return { + userId: email, tokenForFreeCall: tokenToMakeFreeCall, tokenExpiryDateBlock, signature, currentBlockNumber, + }; + } + + /** + * create the grpc client for free call state service + * @returns {FreeCallStateServiceClient} + * @private + */ + _generateFreeCallStateServiceClient() { + const serviceEndpoint = this._serviceClient._getServiceEndpoint(); + // const grpcCredentials = this._getGrpcCredentials(serviceEndpoint); + // return new services.FreeCallStateServiceClient(serviceEndpoint.host, grpcCredentials); + return new FreeCallStateServiceClient(serviceEndpoint.host); + } +} + +export default FreeCallPaymentStrategy; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/PaidCallPaymentStrategy.js b/snet-sdk-js/packages/web/src/payment_strategies/PaidCallPaymentStrategy.js new file mode 100644 index 000000000..3909df254 --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/PaidCallPaymentStrategy.js @@ -0,0 +1,49 @@ +import BasePaidPaymentStrategy from './BasePaidPaymentStrategy'; + +class PaidCallPaymentStrategy extends BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, blockOffset = 240, callAllowance = 1) { + super(serviceClient, blockOffset, callAllowance); + } + + /** + * @returns {Promise<[{'snet-payment-type': string}, {'snet-payment-channel-id': string}, {'snet-payment-channel-nonce': string}, {'snet-payment-channel-amount': string}, {'snet-payment-channel-signature-bin': Buffer}]>} + */ + async getPaymentMetadata() { + const channel = await this._selectChannel(); + const amount = channel.state.currentSignedAmount.toNumber() + this._getPrice(); + const signature = await this._generateSignature(channel.channelId, channel.state.nonce, amount); + + const metadata = [{ 'snet-payment-type': 'escrow' }, + { 'snet-payment-channel-id': `${channel.channelId}` }, + { 'snet-payment-channel-nonce': `${channel.state.nonce}` }, + { 'snet-payment-channel-amount': `${amount}` }, + { 'snet-payment-channel-signature-bin': signature.toString('base64') }, + ]; + + return metadata; + } + + async _generateSignature(channelId, nonce, amount) { + return this._serviceClient.signData({ t: 'string', v: '__MPE_claim_message' }, + { t: 'address', v: this._serviceClient.mpeContract.address }, + { t: 'uint256', v: channelId }, + { t: 'uint256', v: nonce }, + { t: 'uint256', v: amount }); + } + + /** + * total price for all the service calls + * @returns {number} + * @private + */ + _getPrice() { + return this._serviceClient._pricePerServiceCall.toNumber(); + } +} + +export default PaidCallPaymentStrategy; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/PrepaidPaymentStrategy.js b/snet-sdk-js/packages/web/src/payment_strategies/PrepaidPaymentStrategy.js new file mode 100644 index 000000000..682e99c9c --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/PrepaidPaymentStrategy.js @@ -0,0 +1,42 @@ +import BasePaidPaymentStrategy from './BasePaidPaymentStrategy'; +import { EncodingUtils } from '../core'; + +class PrepaidPaymentStrategy extends BasePaidPaymentStrategy { + /** + * @param {BaseServiceClient} serviceClient + * @param {ConcurrencyManager} concurrencyManager + * @param {number} blockOffset + * @param {number} callAllowance + */ + constructor(serviceClient, concurrencyManager, blockOffset = 240, callAllowance = 1) { + super(serviceClient, blockOffset, callAllowance); + this._encodingUtils = new EncodingUtils(); + this._concurrencyManager = concurrencyManager; + } + + /** + * @returns {Promise<[{'snet-payment-type': string}, {'snet-payment-channel-id': string}, {'snet-payment-channel-nonce': string}, {'snet-prepaid-auth-token-bin': *}]>} + */ + async getPaymentMetadata() { + const channel = await this._selectChannel(); + const concurrentCallsPrice = this._getPrice(); + const token = await this._concurrencyManager.getToken(channel, concurrentCallsPrice); + const tokenBytes = this._encodingUtils.utfStringToBytes(token); + const metadata = [{ 'snet-payment-type': 'prepaid-call' }, + { 'snet-payment-channel-id': `${channel.channelId}` }, + { 'snet-payment-channel-nonce': `${channel.state.nonce}` }, + { 'snet-prepaid-auth-token-bin': tokenBytes.toString('base64') }]; + return metadata; + } + + /** + * total price for all the service calls + * @returns {number} + * @private + */ + _getPrice() { + return this._serviceClient._pricePerServiceCall.toNumber() * this._concurrencyManager.concurrentCalls; + } +} + +export default PrepaidPaymentStrategy; diff --git a/snet-sdk-js/packages/web/src/payment_strategies/index.js b/snet-sdk-js/packages/web/src/payment_strategies/index.js new file mode 100644 index 000000000..eb6a7f83f --- /dev/null +++ b/snet-sdk-js/packages/web/src/payment_strategies/index.js @@ -0,0 +1 @@ +export {default as DefaultPaymentStrategy} from './DefaultPaymentStrategy'; diff --git a/snet-sdk-js/packages/web/src/proto/state_service.proto b/snet-sdk-js/packages/web/src/proto/state_service.proto new file mode 100644 index 000000000..aa45e53cf --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/state_service.proto @@ -0,0 +1,92 @@ +syntax = "proto3"; + +package escrow; + +option java_package = "io.singularitynet.daemon.escrow"; + +// PaymentChannelStateService contains methods to get the MultiPartyEscrow +// payment channel state. +// channel_id, channel_nonce, value and amount fields below in fact are +// Solidity uint256 values. Which are big-endian integers, see +// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#formal-specification-of-the-encoding +// These values may be or may be not padded by zeros, service supports both +// options. +service PaymentChannelStateService { + // GetChannelState method returns a channel state by channel id. + rpc GetChannelState(ChannelStateRequest) returns (ChannelStateReply) {} +} + +// ChanelStateRequest is a request for channel state. +message ChannelStateRequest { + // channel_id contains id of the channel which state is requested. + bytes channel_id = 1; + + // signature is a client signature of the message which contains + // channel_id. It is used for client authorization. + bytes signature = 2; + + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 3; +} + +// ChannelStateReply message contains a latest channel state. current_nonce and +// current_value fields can be different from ones stored in the blockchain if +// server started withdrawing funds froms channel but transaction is still not +// finished. +message ChannelStateReply { + // current_nonce is a latest nonce of the payment channel. + bytes current_nonce = 1; + + // current_signed_amount is a last amount which were signed by client with current_nonce + //it could be absent if none message was signed with current_nonce + bytes current_signed_amount = 2; + + // current_signature is a last signature sent by client with current_nonce + // it could be absent if none message was signed with current nonce + bytes current_signature = 3; + + // last amount which was signed by client with nonce=current_nonce - 1 + bytes old_nonce_signed_amount = 4; + + // last signature sent by client with nonce = current_nonce - 1 + bytes old_nonce_signature = 5; + + //If the client / user chooses to sign upfront , the planned amount in cogs will be indicative of this. + //For pay per use, this will be zero + uint64 planned_amount = 6; + + //If the client / user chooses to sign upfront , the usage amount in cogs will be indicative of how much of the + //planned amount has actually been used. + //For pay per use, this will be zero + uint64 used_amount = 7; + + } + +//Used to determine free calls available for a given user. +service FreeCallStateService { + rpc GetFreeCallsAvailable(FreeCallStateRequest) returns (FreeCallStateReply) {} +} + +message FreeCallStateRequest { + //Has the user email id + string user_id = 1; + //signer-token = (user@mail, user-public-key, token_issue_date), this is generated my Market place Dapp + //to leverage free calls from SDK/ snet-cli, you will need this signer-token to be downloaded from Dapp + bytes token_for_free_call = 2; + //Token expiration date in Block number + uint64 token_expiry_date_block = 3 ; + //Signature is made up of the below, user signs with the private key corresponding with the public key used to generate the authorized token + //free-call-metadata = ("__prefix_free_trial",user_id,organization_id,service_id,group_id,current_block,authorized_token) + bytes signature = 4; + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 5; + +} + +message FreeCallStateReply { + //Has the user email id + string user_id = 1; + //Balance number of free calls available + uint64 free_calls_available = 2; +} + diff --git a/snet-sdk-js/packages/web/src/proto/state_service_pb.js b/snet-sdk-js/packages/web/src/proto/state_service_pb.js new file mode 100644 index 000000000..abc2fcdd9 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/state_service_pb.js @@ -0,0 +1,1229 @@ +// source: state_service.proto +/** + * @fileoverview + * @enhanceable + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.escrow.ChannelStateReply', null, global); +goog.exportSymbol('proto.escrow.ChannelStateRequest', null, global); +goog.exportSymbol('proto.escrow.FreeCallStateReply', null, global); +goog.exportSymbol('proto.escrow.FreeCallStateRequest', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.ChannelStateRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.ChannelStateRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.ChannelStateRequest.displayName = 'proto.escrow.ChannelStateRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.ChannelStateReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.ChannelStateReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.ChannelStateReply.displayName = 'proto.escrow.ChannelStateReply'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.FreeCallStateRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.FreeCallStateRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.FreeCallStateRequest.displayName = 'proto.escrow.FreeCallStateRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.FreeCallStateReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.FreeCallStateReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.FreeCallStateReply.displayName = 'proto.escrow.FreeCallStateReply'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.ChannelStateRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.ChannelStateRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.ChannelStateRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateRequest.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: msg.getChannelId_asB64(), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 3, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.ChannelStateRequest} + */ +proto.escrow.ChannelStateRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.ChannelStateRequest; + return proto.escrow.ChannelStateRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.ChannelStateRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.ChannelStateRequest} + */ +proto.escrow.ChannelStateRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.ChannelStateRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.ChannelStateRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } +}; + + +/** + * optional bytes channel_id = 1; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes channel_id = 1; + * This is a type-conversion wrapper around `getChannelId()` + * @return {string} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getChannelId())); +}; + + +/** + * optional bytes channel_id = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getChannelId()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.getChannelId_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getChannelId())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setChannelId = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes signature = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes signature = 2; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint64 current_block = 3; + * @return {number} + */ +proto.escrow.ChannelStateRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateRequest} returns this + */ +proto.escrow.ChannelStateRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.ChannelStateReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.ChannelStateReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.ChannelStateReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateReply.toObject = function(includeInstance, msg) { + var f, obj = { + currentNonce: msg.getCurrentNonce_asB64(), + currentSignedAmount: msg.getCurrentSignedAmount_asB64(), + currentSignature: msg.getCurrentSignature_asB64(), + oldNonceSignedAmount: msg.getOldNonceSignedAmount_asB64(), + oldNonceSignature: msg.getOldNonceSignature_asB64(), + plannedAmount: jspb.Message.getFieldWithDefault(msg, 6, 0), + usedAmount: jspb.Message.getFieldWithDefault(msg, 7, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.ChannelStateReply} + */ +proto.escrow.ChannelStateReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.ChannelStateReply; + return proto.escrow.ChannelStateReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.ChannelStateReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.ChannelStateReply} + */ +proto.escrow.ChannelStateReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentNonce(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentSignedAmount(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setCurrentSignature(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOldNonceSignedAmount(value); + break; + case 5: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setOldNonceSignature(value); + break; + case 6: + var value = /** @type {number} */ (reader.readUint64()); + msg.setPlannedAmount(value); + break; + case 7: + var value = /** @type {number} */ (reader.readUint64()); + msg.setUsedAmount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.ChannelStateReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.ChannelStateReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.ChannelStateReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCurrentNonce_asU8(); + if (f.length > 0) { + writer.writeBytes( + 1, + f + ); + } + f = message.getCurrentSignedAmount_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getCurrentSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getOldNonceSignedAmount_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getOldNonceSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 5, + f + ); + } + f = message.getPlannedAmount(); + if (f !== 0) { + writer.writeUint64( + 6, + f + ); + } + f = message.getUsedAmount(); + if (f !== 0) { + writer.writeUint64( + 7, + f + ); + } +}; + + +/** + * optional bytes current_nonce = 1; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * optional bytes current_nonce = 1; + * This is a type-conversion wrapper around `getCurrentNonce()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentNonce())); +}; + + +/** + * optional bytes current_nonce = 1; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentNonce()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentNonce_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentNonce())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentNonce = function(value) { + return jspb.Message.setProto3BytesField(this, 1, value); +}; + + +/** + * optional bytes current_signed_amount = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes current_signed_amount = 2; + * This is a type-conversion wrapper around `getCurrentSignedAmount()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentSignedAmount())); +}; + + +/** + * optional bytes current_signed_amount = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentSignedAmount()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignedAmount_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentSignedAmount())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentSignedAmount = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional bytes current_signature = 3; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes current_signature = 3; + * This is a type-conversion wrapper around `getCurrentSignature()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getCurrentSignature())); +}; + + +/** + * optional bytes current_signature = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getCurrentSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getCurrentSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getCurrentSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setCurrentSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * This is a type-conversion wrapper around `getOldNonceSignedAmount()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOldNonceSignedAmount())); +}; + + +/** + * optional bytes old_nonce_signed_amount = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOldNonceSignedAmount()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignedAmount_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOldNonceSignedAmount())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setOldNonceSignedAmount = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * @return {!(string|Uint8Array)} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * This is a type-conversion wrapper around `getOldNonceSignature()` + * @return {string} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getOldNonceSignature())); +}; + + +/** + * optional bytes old_nonce_signature = 5; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getOldNonceSignature()` + * @return {!Uint8Array} + */ +proto.escrow.ChannelStateReply.prototype.getOldNonceSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getOldNonceSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setOldNonceSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 5, value); +}; + + +/** + * optional uint64 planned_amount = 6; + * @return {number} + */ +proto.escrow.ChannelStateReply.prototype.getPlannedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setPlannedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional uint64 used_amount = 7; + * @return {number} + */ +proto.escrow.ChannelStateReply.prototype.getUsedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.ChannelStateReply} returns this + */ +proto.escrow.ChannelStateReply.prototype.setUsedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.FreeCallStateRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.FreeCallStateRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.FreeCallStateRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateRequest.toObject = function(includeInstance, msg) { + var f, obj = { + userId: jspb.Message.getFieldWithDefault(msg, 1, ""), + tokenForFreeCall: msg.getTokenForFreeCall_asB64(), + tokenExpiryDateBlock: jspb.Message.getFieldWithDefault(msg, 3, 0), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 5, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.FreeCallStateRequest} + */ +proto.escrow.FreeCallStateRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.FreeCallStateRequest; + return proto.escrow.FreeCallStateRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.FreeCallStateRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.FreeCallStateRequest} + */ +proto.escrow.FreeCallStateRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setUserId(value); + break; + case 2: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setTokenForFreeCall(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setTokenExpiryDateBlock(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.FreeCallStateRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.FreeCallStateRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getTokenForFreeCall_asU8(); + if (f.length > 0) { + writer.writeBytes( + 2, + f + ); + } + f = message.getTokenExpiryDateBlock(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } +}; + + +/** + * optional string user_id = 1; + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getUserId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setUserId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional bytes token_for_free_call = 2; + * @return {!(string|Uint8Array)} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * optional bytes token_for_free_call = 2; + * This is a type-conversion wrapper around `getTokenForFreeCall()` + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getTokenForFreeCall())); +}; + + +/** + * optional bytes token_for_free_call = 2; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getTokenForFreeCall()` + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenForFreeCall_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getTokenForFreeCall())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setTokenForFreeCall = function(value) { + return jspb.Message.setProto3BytesField(this, 2, value); +}; + + +/** + * optional uint64 token_expiry_date_block = 3; + * @return {number} + */ +proto.escrow.FreeCallStateRequest.prototype.getTokenExpiryDateBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setTokenExpiryDateBlock = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes signature = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes signature = 4; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint64 current_block = 5; + * @return {number} + */ +proto.escrow.FreeCallStateRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateRequest} returns this + */ +proto.escrow.FreeCallStateRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.FreeCallStateReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.FreeCallStateReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.FreeCallStateReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateReply.toObject = function(includeInstance, msg) { + var f, obj = { + userId: jspb.Message.getFieldWithDefault(msg, 1, ""), + freeCallsAvailable: jspb.Message.getFieldWithDefault(msg, 2, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.FreeCallStateReply} + */ +proto.escrow.FreeCallStateReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.FreeCallStateReply; + return proto.escrow.FreeCallStateReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.FreeCallStateReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.FreeCallStateReply} + */ +proto.escrow.FreeCallStateReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setUserId(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setFreeCallsAvailable(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.FreeCallStateReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.FreeCallStateReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.FreeCallStateReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.FreeCallStateReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUserId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getFreeCallsAvailable(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } +}; + + +/** + * optional string user_id = 1; + * @return {string} + */ +proto.escrow.FreeCallStateReply.prototype.getUserId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.FreeCallStateReply} returns this + */ +proto.escrow.FreeCallStateReply.prototype.setUserId = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional uint64 free_calls_available = 2; + * @return {number} + */ +proto.escrow.FreeCallStateReply.prototype.getFreeCallsAvailable = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.FreeCallStateReply} returns this + */ +proto.escrow.FreeCallStateReply.prototype.setFreeCallsAvailable = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +goog.object.extend(exports, proto.escrow); diff --git a/snet-sdk-js/packages/web/src/proto/state_service_pb_service.js b/snet-sdk-js/packages/web/src/proto/state_service_pb_service.js new file mode 100644 index 000000000..3f7c8a0a3 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/state_service_pb_service.js @@ -0,0 +1,116 @@ +// package: escrow +// file: state_service.proto + +var state_service_pb = require("./state_service_pb"); +var grpc = require("@improbable-eng/grpc-web").grpc; + +var PaymentChannelStateService = (function () { + function PaymentChannelStateService() {} + PaymentChannelStateService.serviceName = "escrow.PaymentChannelStateService"; + return PaymentChannelStateService; +}()); + +PaymentChannelStateService.GetChannelState = { + methodName: "GetChannelState", + service: PaymentChannelStateService, + requestStream: false, + responseStream: false, + requestType: state_service_pb.ChannelStateRequest, + responseType: state_service_pb.ChannelStateReply +}; + +exports.PaymentChannelStateService = PaymentChannelStateService; + +function PaymentChannelStateServiceClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +PaymentChannelStateServiceClient.prototype.getChannelState = function getChannelState(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(PaymentChannelStateService.GetChannelState, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.PaymentChannelStateServiceClient = PaymentChannelStateServiceClient; + +var FreeCallStateService = (function () { + function FreeCallStateService() {} + FreeCallStateService.serviceName = "escrow.FreeCallStateService"; + return FreeCallStateService; +}()); + +FreeCallStateService.GetFreeCallsAvailable = { + methodName: "GetFreeCallsAvailable", + service: FreeCallStateService, + requestStream: false, + responseStream: false, + requestType: state_service_pb.FreeCallStateRequest, + responseType: state_service_pb.FreeCallStateReply +}; + +exports.FreeCallStateService = FreeCallStateService; + +function FreeCallStateServiceClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +FreeCallStateServiceClient.prototype.getFreeCallsAvailable = function getFreeCallsAvailable(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(FreeCallStateService.GetFreeCallsAvailable, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.FreeCallStateServiceClient = FreeCallStateServiceClient; + diff --git a/snet-sdk-js/packages/web/src/proto/token_service.proto b/snet-sdk-js/packages/web/src/proto/token_service.proto new file mode 100644 index 000000000..33a66563e --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/token_service.proto @@ -0,0 +1,63 @@ +syntax = "proto3"; + +package escrow; + +option java_package = "io.singularitynet.daemon.escrow"; +//It is expected that the user would call the GetChannelState to Determine the Current state of the Channel +//Based on the usage forecast, the user/client will have to sign for an amount L + U , where L is the last amount Signed +//and U is the amount based on expected usage. +//Please be aware that the Signing up an amount upfront ( Pre Paid) does come with a risk and hence the +//user must exercise caution on the amount signed specially with new service providers. +//If there is no need of making concurrent calls then you may consider pay per mode. +//Using a Token, the Client can now make concurrent calls, which was not supported previously with the pay per mode. +//However the pay per mode is a lot secure than the pre-paid mode. +service TokenService { + // GetToken method checks the Signature sent and returns a Token + // 1) The Signature is valid and has to be signed in the below format + //"__MPE_claim_message"+MpeContractAddress+ChannelID+ChannelNonce+SignedAmount + //Signature is to let the Service Provider make a claim + // 2) Signed amount >= Last amount Signed. + // if Signed amount == Last Signed amount , then check if planned_amount < used_amount + // if Signed amount > Last Signed amount , then update the planned amount = Signed Amount + // GetToken method in a way behaves as a renew Token too!. + rpc GetToken(TokenRequest) returns (TokenReply) {} + + + +} + +// TokenRequest is a request for getting a valid token. +message TokenRequest { + // channel_id contains id of the channel which state is requested. + uint64 channel_id = 1; + // current_nonce is a latest nonce of the payment channel. + uint64 current_nonce = 2; + //signed_amount is the amount signed by client with current_nonce + uint64 signed_amount = 3; + // Signature is a client signature of the message which contains 2 parts + //Part 1 : MPE Signature "__MPE_claim_message"+MpeContractAddress+ChannelID+ChannelNonce+SignedAmount + //Part 2 : Current Block Number + bytes signature = 4; + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 5; + + bytes claim_signature = 6; + +} + +// TokenReply message contains a latest channel state. current_nonce and +message TokenReply { + // current_nonce is a latest nonce of the payment channel. + uint64 channel_id = 1; + + //it could be absent if none message was signed with current_nonce + string token = 2; + + //If the client / user chooses to sign upfront , the planned amount in cogs will be indicative of this. + uint64 planned_amount = 3; + + //If the client / user chooses to sign upfront , the used amount in cogs will be indicative of how much of the + //planned amount has actually been used. + uint64 used_amount = 4; + +} diff --git a/snet-sdk-js/packages/web/src/proto/token_service_pb.js b/snet-sdk-js/packages/web/src/proto/token_service_pb.js new file mode 100644 index 000000000..82d8e0575 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/token_service_pb.js @@ -0,0 +1,607 @@ +// source: token_service.proto +/** + * @fileoverview + * @enhanceable + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +goog.exportSymbol('proto.escrow.TokenReply', null, global); +goog.exportSymbol('proto.escrow.TokenRequest', null, global); +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.TokenRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.TokenRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.TokenRequest.displayName = 'proto.escrow.TokenRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.escrow.TokenReply = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.escrow.TokenReply, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.escrow.TokenReply.displayName = 'proto.escrow.TokenReply'; +} + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.TokenRequest.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.TokenRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.TokenRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenRequest.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: jspb.Message.getFieldWithDefault(msg, 1, 0), + currentNonce: jspb.Message.getFieldWithDefault(msg, 2, 0), + signedAmount: jspb.Message.getFieldWithDefault(msg, 3, 0), + signature: msg.getSignature_asB64(), + currentBlock: jspb.Message.getFieldWithDefault(msg, 5, 0), + claimSignature: msg.getClaimSignature_asB64() + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.TokenRequest} + */ +proto.escrow.TokenRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.TokenRequest; + return proto.escrow.TokenRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.TokenRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.TokenRequest} + */ +proto.escrow.TokenRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentNonce(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setSignedAmount(value); + break; + case 4: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 5: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + case 6: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setClaimSignature(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.TokenRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.TokenRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId(); + if (f !== 0) { + writer.writeUint64( + 1, + f + ); + } + f = message.getCurrentNonce(); + if (f !== 0) { + writer.writeUint64( + 2, + f + ); + } + f = message.getSignedAmount(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 4, + f + ); + } + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 5, + f + ); + } + f = message.getClaimSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 6, + f + ); + } +}; + + +/** + * optional uint64 channel_id = 1; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getChannelId = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setChannelId = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional uint64 current_nonce = 2; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getCurrentNonce = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setCurrentNonce = function(value) { + return jspb.Message.setProto3IntField(this, 2, value); +}; + + +/** + * optional uint64 signed_amount = 3; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getSignedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setSignedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional bytes signature = 4; + * @return {!(string|Uint8Array)} + */ +proto.escrow.TokenRequest.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * optional bytes signature = 4; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +proto.escrow.TokenRequest.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 4; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 4, value); +}; + + +/** + * optional uint64 current_block = 5; + * @return {number} + */ +proto.escrow.TokenRequest.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setCurrentBlock = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional bytes claim_signature = 6; + * @return {!(string|Uint8Array)} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** + * optional bytes claim_signature = 6; + * This is a type-conversion wrapper around `getClaimSignature()` + * @return {string} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getClaimSignature())); +}; + + +/** + * optional bytes claim_signature = 6; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getClaimSignature()` + * @return {!Uint8Array} + */ +proto.escrow.TokenRequest.prototype.getClaimSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getClaimSignature())); +}; + + +/** + * @param {!(string|Uint8Array)} value + * @return {!proto.escrow.TokenRequest} returns this + */ +proto.escrow.TokenRequest.prototype.setClaimSignature = function(value) { + return jspb.Message.setProto3BytesField(this, 6, value); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.escrow.TokenReply.prototype.toObject = function(opt_includeInstance) { + return proto.escrow.TokenReply.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.escrow.TokenReply} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenReply.toObject = function(includeInstance, msg) { + var f, obj = { + channelId: jspb.Message.getFieldWithDefault(msg, 1, 0), + token: jspb.Message.getFieldWithDefault(msg, 2, ""), + plannedAmount: jspb.Message.getFieldWithDefault(msg, 3, 0), + usedAmount: jspb.Message.getFieldWithDefault(msg, 4, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.escrow.TokenReply} + */ +proto.escrow.TokenReply.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.escrow.TokenReply; + return proto.escrow.TokenReply.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.escrow.TokenReply} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.escrow.TokenReply} + */ +proto.escrow.TokenReply.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setChannelId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setToken(value); + break; + case 3: + var value = /** @type {number} */ (reader.readUint64()); + msg.setPlannedAmount(value); + break; + case 4: + var value = /** @type {number} */ (reader.readUint64()); + msg.setUsedAmount(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.escrow.TokenReply.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.escrow.TokenReply.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.escrow.TokenReply} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.escrow.TokenReply.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getChannelId(); + if (f !== 0) { + writer.writeUint64( + 1, + f + ); + } + f = message.getToken(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getPlannedAmount(); + if (f !== 0) { + writer.writeUint64( + 3, + f + ); + } + f = message.getUsedAmount(); + if (f !== 0) { + writer.writeUint64( + 4, + f + ); + } +}; + + +/** + * optional uint64 channel_id = 1; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getChannelId = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setChannelId = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional string token = 2; + * @return {string} + */ +proto.escrow.TokenReply.prototype.getToken = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setToken = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional uint64 planned_amount = 3; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getPlannedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setPlannedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 3, value); +}; + + +/** + * optional uint64 used_amount = 4; + * @return {number} + */ +proto.escrow.TokenReply.prototype.getUsedAmount = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.escrow.TokenReply} returns this + */ +proto.escrow.TokenReply.prototype.setUsedAmount = function(value) { + return jspb.Message.setProto3IntField(this, 4, value); +}; + + +goog.object.extend(exports, proto.escrow); diff --git a/snet-sdk-js/packages/web/src/proto/token_service_pb_service.js b/snet-sdk-js/packages/web/src/proto/token_service_pb_service.js new file mode 100644 index 000000000..5fae2c430 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/token_service_pb_service.js @@ -0,0 +1,61 @@ +// package: escrow +// file: token_service.proto + +var token_service_pb = require("./token_service_pb"); +var grpc = require("@improbable-eng/grpc-web").grpc; + +var TokenService = (function () { + function TokenService() {} + TokenService.serviceName = "escrow.TokenService"; + return TokenService; +}()); + +TokenService.GetToken = { + methodName: "GetToken", + service: TokenService, + requestStream: false, + responseStream: false, + requestType: token_service_pb.TokenRequest, + responseType: token_service_pb.TokenReply +}; + +exports.TokenService = TokenService; + +function TokenServiceClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +TokenServiceClient.prototype.getToken = function getToken(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(TokenService.GetToken, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.TokenServiceClient = TokenServiceClient; + diff --git a/snet-sdk-js/packages/web/src/proto/training.proto b/snet-sdk-js/packages/web/src/proto/training.proto new file mode 100644 index 000000000..f9199151d --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/training.proto @@ -0,0 +1,112 @@ +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +package training; +//Please note that the AI developers need to provide a server implementation of the gprc server of this proto. +message ModelDetails { + //This Id will be generated when you invoke the create_model method and hence doesnt need to be filled when you + //invoke the create model + string model_id = 1; + //define the training method name + string grpc_method_name = 2; + //define the grpc service name , under which the method is defined + string grpc_service_name = 3; + string description = 4; + + string status = 6; + string updated_date = 7; + //List of all the addresses that will have access to this model + repeated string address_list = 8; + // this is optional + string training_data_link = 9; + string model_name = 10; + + + string organization_id = 11; + string service_id = 12 ; + string group_id = 13; + + //set this to true if you want your model to be used by other AI consumers + bool is_publicly_accessible = 14; + +} + +message AuthorizationDetails { + uint64 current_block = 1; + //Signer can fill in any message here + string message = 2; + //signature of the following message: + //("user specified message", user_address, current_block_number) + bytes signature = 3; + string signer_address = 4; + +} + +enum Status { + CREATED = 0; + IN_PROGRESS = 1; + ERRORED = 2; + COMPLETED = 3; + DELETED = 4; +} + +message CreateModelRequest { + AuthorizationDetails authorization = 1; + ModelDetails model_details = 2; +} + +//the signer address will get to know all the models associated with this address. +message AccessibleModelsRequest { + string grpc_method_name = 1; + string grpc_service_name = 2; + AuthorizationDetails authorization = 3; +} + +message AccessibleModelsResponse { + repeated ModelDetails list_of_models = 1; +} + +message ModelDetailsRequest { + ModelDetails model_details = 1 ; + AuthorizationDetails authorization = 2; +} + +//helps determine which service end point to call for model training +//format is of type "packageName/serviceName/MethodName", Example :"/example_service.Calculator/estimate_add" +//Daemon will invoke the model training end point , when the below method option is specified +message TrainingMethodOption { + string trainingMethodIndicator = 1; +} + +extend google.protobuf.MethodOptions { + TrainingMethodOption my_method_option = 9999197; +} + +message UpdateModelRequest { + ModelDetails update_model_details = 1 ; + AuthorizationDetails authorization = 2; +} + + +message ModelDetailsResponse { + Status status = 1; + ModelDetails model_details = 2; + +} + +service Model { + + // The AI developer needs to Implement this service and Daemon will call these + // There will be no cost borne by the consumer in calling these methods, + // Pricing will apply when you actually call the training methods defined. + // AI consumer will call all these methods + rpc create_model(CreateModelRequest) returns (ModelDetailsResponse) {} + rpc delete_model(UpdateModelRequest) returns (ModelDetailsResponse) {} + rpc get_model_status(ModelDetailsRequest) returns (ModelDetailsResponse) {} + + + // Daemon will implement , however the AI developer should skip implementing these and just provide dummy code. + rpc update_model_access(UpdateModelRequest) returns (ModelDetailsResponse) {} + rpc get_all_models(AccessibleModelsRequest) returns (AccessibleModelsResponse) {} + + +} diff --git a/snet-sdk-js/packages/web/src/proto/training_pb.js b/snet-sdk-js/packages/web/src/proto/training_pb.js new file mode 100644 index 000000000..53473c403 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/training_pb.js @@ -0,0 +1,2116 @@ +/** + * @fileoverview + * @enhanceable + * @suppress {messageConventions} JS Compiler reports an error if a variable or + * field starts with 'MSG_' and isn't a translatable message. + * @public + */ +// GENERATED CODE -- DO NOT EDIT! + +var jspb = require('google-protobuf'); +var goog = jspb; +var global = Function('return this')(); + +var google_protobuf_descriptor_pb = require('google-protobuf/google/protobuf/descriptor_pb.js'); +goog.exportSymbol('training.AccessibleModelsRequest', null, global); +goog.exportSymbol('training.AccessibleModelsResponse', null, global); +goog.exportSymbol('training.AuthorizationDetails', null, global); +goog.exportSymbol('training.CreateModelRequest', null, global); +goog.exportSymbol('training.ModelDetails', null, global); +goog.exportSymbol('training.ModelDetailsRequest', null, global); +goog.exportSymbol('training.ModelDetailsResponse', null, global); +goog.exportSymbol('training.Status', null, global); +goog.exportSymbol('training.TrainingMethodOption', null, global); +goog.exportSymbol('training.UpdateModelRequest', null, global); +goog.exportSymbol('training.myMethodOption', null, global); + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.ModelDetails = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, training.ModelDetails.repeatedFields_, null); +}; +goog.inherits(training.ModelDetails, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.ModelDetails.displayName = 'training.ModelDetails'; +} +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +training.ModelDetails.repeatedFields_ = [8]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.ModelDetails.prototype.toObject = function(opt_includeInstance) { + return training.ModelDetails.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.ModelDetails} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetails.toObject = function(includeInstance, msg) { + var f, obj = { + modelId: jspb.Message.getFieldWithDefault(msg, 1, ""), + grpcMethodName: jspb.Message.getFieldWithDefault(msg, 2, ""), + grpcServiceName: jspb.Message.getFieldWithDefault(msg, 3, ""), + description: jspb.Message.getFieldWithDefault(msg, 4, ""), + status: jspb.Message.getFieldWithDefault(msg, 6, ""), + updatedDate: jspb.Message.getFieldWithDefault(msg, 7, ""), + addressListList: jspb.Message.getRepeatedField(msg, 8), + trainingDataLink: jspb.Message.getFieldWithDefault(msg, 9, ""), + modelName: jspb.Message.getFieldWithDefault(msg, 10, ""), + organizationId: jspb.Message.getFieldWithDefault(msg, 11, ""), + serviceId: jspb.Message.getFieldWithDefault(msg, 12, ""), + groupId: jspb.Message.getFieldWithDefault(msg, 13, ""), + isPubliclyAccessible: jspb.Message.getFieldWithDefault(msg, 14, false) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.ModelDetails} + */ +training.ModelDetails.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.ModelDetails; + return training.ModelDetails.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.ModelDetails} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.ModelDetails} + */ +training.ModelDetails.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setModelId(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setGrpcMethodName(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setGrpcServiceName(value); + break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setDescription(value); + break; + case 6: + var value = /** @type {string} */ (reader.readString()); + msg.setStatus(value); + break; + case 7: + var value = /** @type {string} */ (reader.readString()); + msg.setUpdatedDate(value); + break; + case 8: + var value = /** @type {string} */ (reader.readString()); + msg.addAddressList(value); + break; + case 9: + var value = /** @type {string} */ (reader.readString()); + msg.setTrainingDataLink(value); + break; + case 10: + var value = /** @type {string} */ (reader.readString()); + msg.setModelName(value); + break; + case 11: + var value = /** @type {string} */ (reader.readString()); + msg.setOrganizationId(value); + break; + case 12: + var value = /** @type {string} */ (reader.readString()); + msg.setServiceId(value); + break; + case 13: + var value = /** @type {string} */ (reader.readString()); + msg.setGroupId(value); + break; + case 14: + var value = /** @type {boolean} */ (reader.readBool()); + msg.setIsPubliclyAccessible(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.ModelDetails.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.ModelDetails.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.ModelDetails} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetails.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getModelId(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getGrpcMethodName(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getGrpcServiceName(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } + f = message.getDescription(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } + f = message.getStatus(); + if (f.length > 0) { + writer.writeString( + 6, + f + ); + } + f = message.getUpdatedDate(); + if (f.length > 0) { + writer.writeString( + 7, + f + ); + } + f = message.getAddressListList(); + if (f.length > 0) { + writer.writeRepeatedString( + 8, + f + ); + } + f = message.getTrainingDataLink(); + if (f.length > 0) { + writer.writeString( + 9, + f + ); + } + f = message.getModelName(); + if (f.length > 0) { + writer.writeString( + 10, + f + ); + } + f = message.getOrganizationId(); + if (f.length > 0) { + writer.writeString( + 11, + f + ); + } + f = message.getServiceId(); + if (f.length > 0) { + writer.writeString( + 12, + f + ); + } + f = message.getGroupId(); + if (f.length > 0) { + writer.writeString( + 13, + f + ); + } + f = message.getIsPubliclyAccessible(); + if (f) { + writer.writeBool( + 14, + f + ); + } +}; + + +/** + * optional string model_id = 1; + * @return {string} + */ +training.ModelDetails.prototype.getModelId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setModelId = function(value) { + jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string grpc_method_name = 2; + * @return {string} + */ +training.ModelDetails.prototype.getGrpcMethodName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setGrpcMethodName = function(value) { + jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string grpc_service_name = 3; + * @return {string} + */ +training.ModelDetails.prototype.getGrpcServiceName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setGrpcServiceName = function(value) { + jspb.Message.setProto3StringField(this, 3, value); +}; + + +/** + * optional string description = 4; + * @return {string} + */ +training.ModelDetails.prototype.getDescription = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setDescription = function(value) { + jspb.Message.setProto3StringField(this, 4, value); +}; + + +/** + * optional string status = 6; + * @return {string} + */ +training.ModelDetails.prototype.getStatus = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setStatus = function(value) { + jspb.Message.setProto3StringField(this, 6, value); +}; + + +/** + * optional string updated_date = 7; + * @return {string} + */ +training.ModelDetails.prototype.getUpdatedDate = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setUpdatedDate = function(value) { + jspb.Message.setProto3StringField(this, 7, value); +}; + + +/** + * repeated string address_list = 8; + * @return {!Array} + */ +training.ModelDetails.prototype.getAddressListList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 8)); +}; + + +/** @param {!Array} value */ +training.ModelDetails.prototype.setAddressListList = function(value) { + jspb.Message.setField(this, 8, value || []); +}; + + +/** + * @param {!string} value + * @param {number=} opt_index + */ +training.ModelDetails.prototype.addAddressList = function(value, opt_index) { + jspb.Message.addToRepeatedField(this, 8, value, opt_index); +}; + + +training.ModelDetails.prototype.clearAddressListList = function() { + this.setAddressListList([]); +}; + + +/** + * optional string training_data_link = 9; + * @return {string} + */ +training.ModelDetails.prototype.getTrainingDataLink = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setTrainingDataLink = function(value) { + jspb.Message.setProto3StringField(this, 9, value); +}; + + +/** + * optional string model_name = 10; + * @return {string} + */ +training.ModelDetails.prototype.getModelName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setModelName = function(value) { + jspb.Message.setProto3StringField(this, 10, value); +}; + + +/** + * optional string organization_id = 11; + * @return {string} + */ +training.ModelDetails.prototype.getOrganizationId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setOrganizationId = function(value) { + jspb.Message.setProto3StringField(this, 11, value); +}; + + +/** + * optional string service_id = 12; + * @return {string} + */ +training.ModelDetails.prototype.getServiceId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setServiceId = function(value) { + jspb.Message.setProto3StringField(this, 12, value); +}; + + +/** + * optional string group_id = 13; + * @return {string} + */ +training.ModelDetails.prototype.getGroupId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 13, "")); +}; + + +/** @param {string} value */ +training.ModelDetails.prototype.setGroupId = function(value) { + jspb.Message.setProto3StringField(this, 13, value); +}; + + +/** + * optional bool is_publicly_accessible = 14; + * Note that Boolean fields may be set to 0/1 when serialized from a Java server. + * You should avoid comparisons like {@code val === true/false} in those cases. + * @return {boolean} + */ +training.ModelDetails.prototype.getIsPubliclyAccessible = function() { + return /** @type {boolean} */ (jspb.Message.getFieldWithDefault(this, 14, false)); +}; + + +/** @param {boolean} value */ +training.ModelDetails.prototype.setIsPubliclyAccessible = function(value) { + jspb.Message.setProto3BooleanField(this, 14, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.AuthorizationDetails = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.AuthorizationDetails, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.AuthorizationDetails.displayName = 'training.AuthorizationDetails'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.AuthorizationDetails.prototype.toObject = function(opt_includeInstance) { + return training.AuthorizationDetails.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.AuthorizationDetails} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AuthorizationDetails.toObject = function(includeInstance, msg) { + var f, obj = { + currentBlock: jspb.Message.getFieldWithDefault(msg, 1, 0), + message: jspb.Message.getFieldWithDefault(msg, 2, ""), + signature: msg.getSignature_asB64(), + signerAddress: jspb.Message.getFieldWithDefault(msg, 4, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.AuthorizationDetails} + */ +training.AuthorizationDetails.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.AuthorizationDetails; + return training.AuthorizationDetails.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.AuthorizationDetails} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.AuthorizationDetails} + */ +training.AuthorizationDetails.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readUint64()); + msg.setCurrentBlock(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setMessage(value); + break; + case 3: + var value = /** @type {!Uint8Array} */ (reader.readBytes()); + msg.setSignature(value); + break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setSignerAddress(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.AuthorizationDetails.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.AuthorizationDetails.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.AuthorizationDetails} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AuthorizationDetails.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getCurrentBlock(); + if (f !== 0) { + writer.writeUint64( + 1, + f + ); + } + f = message.getMessage(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getSignature_asU8(); + if (f.length > 0) { + writer.writeBytes( + 3, + f + ); + } + f = message.getSignerAddress(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } +}; + + +/** + * optional uint64 current_block = 1; + * @return {number} + */ +training.AuthorizationDetails.prototype.getCurrentBlock = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** @param {number} value */ +training.AuthorizationDetails.prototype.setCurrentBlock = function(value) { + jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional string message = 2; + * @return {string} + */ +training.AuthorizationDetails.prototype.getMessage = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** @param {string} value */ +training.AuthorizationDetails.prototype.setMessage = function(value) { + jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional bytes signature = 3; + * @return {!(string|Uint8Array)} + */ +training.AuthorizationDetails.prototype.getSignature = function() { + return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * optional bytes signature = 3; + * This is a type-conversion wrapper around `getSignature()` + * @return {string} + */ +training.AuthorizationDetails.prototype.getSignature_asB64 = function() { + return /** @type {string} */ (jspb.Message.bytesAsB64( + this.getSignature())); +}; + + +/** + * optional bytes signature = 3; + * Note that Uint8Array is not supported on all browsers. + * @see http://caniuse.com/Uint8Array + * This is a type-conversion wrapper around `getSignature()` + * @return {!Uint8Array} + */ +training.AuthorizationDetails.prototype.getSignature_asU8 = function() { + return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8( + this.getSignature())); +}; + + +/** @param {!(string|Uint8Array)} value */ +training.AuthorizationDetails.prototype.setSignature = function(value) { + jspb.Message.setProto3BytesField(this, 3, value); +}; + + +/** + * optional string signer_address = 4; + * @return {string} + */ +training.AuthorizationDetails.prototype.getSignerAddress = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** @param {string} value */ +training.AuthorizationDetails.prototype.setSignerAddress = function(value) { + jspb.Message.setProto3StringField(this, 4, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.CreateModelRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.CreateModelRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.CreateModelRequest.displayName = 'training.CreateModelRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.CreateModelRequest.prototype.toObject = function(opt_includeInstance) { + return training.CreateModelRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.CreateModelRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.CreateModelRequest.toObject = function(includeInstance, msg) { + var f, obj = { + authorization: (f = msg.getAuthorization()) && training.AuthorizationDetails.toObject(includeInstance, f), + modelDetails: (f = msg.getModelDetails()) && training.ModelDetails.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.CreateModelRequest} + */ +training.CreateModelRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.CreateModelRequest; + return training.CreateModelRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.CreateModelRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.CreateModelRequest} + */ +training.CreateModelRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new training.AuthorizationDetails; + reader.readMessage(value,training.AuthorizationDetails.deserializeBinaryFromReader); + msg.setAuthorization(value); + break; + case 2: + var value = new training.ModelDetails; + reader.readMessage(value,training.ModelDetails.deserializeBinaryFromReader); + msg.setModelDetails(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.CreateModelRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.CreateModelRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.CreateModelRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.CreateModelRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getAuthorization(); + if (f != null) { + writer.writeMessage( + 1, + f, + training.AuthorizationDetails.serializeBinaryToWriter + ); + } + f = message.getModelDetails(); + if (f != null) { + writer.writeMessage( + 2, + f, + training.ModelDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * optional AuthorizationDetails authorization = 1; + * @return {?training.AuthorizationDetails} + */ +training.CreateModelRequest.prototype.getAuthorization = function() { + return /** @type{?training.AuthorizationDetails} */ ( + jspb.Message.getWrapperField(this, training.AuthorizationDetails, 1)); +}; + + +/** @param {?training.AuthorizationDetails|undefined} value */ +training.CreateModelRequest.prototype.setAuthorization = function(value) { + jspb.Message.setWrapperField(this, 1, value); +}; + + +training.CreateModelRequest.prototype.clearAuthorization = function() { + this.setAuthorization(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.CreateModelRequest.prototype.hasAuthorization = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional ModelDetails model_details = 2; + * @return {?training.ModelDetails} + */ +training.CreateModelRequest.prototype.getModelDetails = function() { + return /** @type{?training.ModelDetails} */ ( + jspb.Message.getWrapperField(this, training.ModelDetails, 2)); +}; + + +/** @param {?training.ModelDetails|undefined} value */ +training.CreateModelRequest.prototype.setModelDetails = function(value) { + jspb.Message.setWrapperField(this, 2, value); +}; + + +training.CreateModelRequest.prototype.clearModelDetails = function() { + this.setModelDetails(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.CreateModelRequest.prototype.hasModelDetails = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.AccessibleModelsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.AccessibleModelsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.AccessibleModelsRequest.displayName = 'training.AccessibleModelsRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.AccessibleModelsRequest.prototype.toObject = function(opt_includeInstance) { + return training.AccessibleModelsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.AccessibleModelsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AccessibleModelsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + grpcMethodName: jspb.Message.getFieldWithDefault(msg, 1, ""), + grpcServiceName: jspb.Message.getFieldWithDefault(msg, 2, ""), + authorization: (f = msg.getAuthorization()) && training.AuthorizationDetails.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.AccessibleModelsRequest} + */ +training.AccessibleModelsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.AccessibleModelsRequest; + return training.AccessibleModelsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.AccessibleModelsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.AccessibleModelsRequest} + */ +training.AccessibleModelsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setGrpcMethodName(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setGrpcServiceName(value); + break; + case 3: + var value = new training.AuthorizationDetails; + reader.readMessage(value,training.AuthorizationDetails.deserializeBinaryFromReader); + msg.setAuthorization(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.AccessibleModelsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.AccessibleModelsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.AccessibleModelsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AccessibleModelsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getGrpcMethodName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getGrpcServiceName(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getAuthorization(); + if (f != null) { + writer.writeMessage( + 3, + f, + training.AuthorizationDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * optional string grpc_method_name = 1; + * @return {string} + */ +training.AccessibleModelsRequest.prototype.getGrpcMethodName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** @param {string} value */ +training.AccessibleModelsRequest.prototype.setGrpcMethodName = function(value) { + jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string grpc_service_name = 2; + * @return {string} + */ +training.AccessibleModelsRequest.prototype.getGrpcServiceName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** @param {string} value */ +training.AccessibleModelsRequest.prototype.setGrpcServiceName = function(value) { + jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional AuthorizationDetails authorization = 3; + * @return {?training.AuthorizationDetails} + */ +training.AccessibleModelsRequest.prototype.getAuthorization = function() { + return /** @type{?training.AuthorizationDetails} */ ( + jspb.Message.getWrapperField(this, training.AuthorizationDetails, 3)); +}; + + +/** @param {?training.AuthorizationDetails|undefined} value */ +training.AccessibleModelsRequest.prototype.setAuthorization = function(value) { + jspb.Message.setWrapperField(this, 3, value); +}; + + +training.AccessibleModelsRequest.prototype.clearAuthorization = function() { + this.setAuthorization(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.AccessibleModelsRequest.prototype.hasAuthorization = function() { + return jspb.Message.getField(this, 3) != null; +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.AccessibleModelsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, training.AccessibleModelsResponse.repeatedFields_, null); +}; +goog.inherits(training.AccessibleModelsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.AccessibleModelsResponse.displayName = 'training.AccessibleModelsResponse'; +} +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +training.AccessibleModelsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.AccessibleModelsResponse.prototype.toObject = function(opt_includeInstance) { + return training.AccessibleModelsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.AccessibleModelsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AccessibleModelsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + listOfModelsList: jspb.Message.toObjectList(msg.getListOfModelsList(), + training.ModelDetails.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.AccessibleModelsResponse} + */ +training.AccessibleModelsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.AccessibleModelsResponse; + return training.AccessibleModelsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.AccessibleModelsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.AccessibleModelsResponse} + */ +training.AccessibleModelsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new training.ModelDetails; + reader.readMessage(value,training.ModelDetails.deserializeBinaryFromReader); + msg.addListOfModels(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.AccessibleModelsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.AccessibleModelsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.AccessibleModelsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.AccessibleModelsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getListOfModelsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + training.ModelDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated ModelDetails list_of_models = 1; + * @return {!Array} + */ +training.AccessibleModelsResponse.prototype.getListOfModelsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, training.ModelDetails, 1)); +}; + + +/** @param {!Array} value */ +training.AccessibleModelsResponse.prototype.setListOfModelsList = function(value) { + jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!training.ModelDetails=} opt_value + * @param {number=} opt_index + * @return {!training.ModelDetails} + */ +training.AccessibleModelsResponse.prototype.addListOfModels = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, training.ModelDetails, opt_index); +}; + + +training.AccessibleModelsResponse.prototype.clearListOfModelsList = function() { + this.setListOfModelsList([]); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.ModelDetailsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.ModelDetailsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.ModelDetailsRequest.displayName = 'training.ModelDetailsRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.ModelDetailsRequest.prototype.toObject = function(opt_includeInstance) { + return training.ModelDetailsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.ModelDetailsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetailsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + modelDetails: (f = msg.getModelDetails()) && training.ModelDetails.toObject(includeInstance, f), + authorization: (f = msg.getAuthorization()) && training.AuthorizationDetails.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.ModelDetailsRequest} + */ +training.ModelDetailsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.ModelDetailsRequest; + return training.ModelDetailsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.ModelDetailsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.ModelDetailsRequest} + */ +training.ModelDetailsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new training.ModelDetails; + reader.readMessage(value,training.ModelDetails.deserializeBinaryFromReader); + msg.setModelDetails(value); + break; + case 2: + var value = new training.AuthorizationDetails; + reader.readMessage(value,training.AuthorizationDetails.deserializeBinaryFromReader); + msg.setAuthorization(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.ModelDetailsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.ModelDetailsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.ModelDetailsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetailsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getModelDetails(); + if (f != null) { + writer.writeMessage( + 1, + f, + training.ModelDetails.serializeBinaryToWriter + ); + } + f = message.getAuthorization(); + if (f != null) { + writer.writeMessage( + 2, + f, + training.AuthorizationDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * optional ModelDetails model_details = 1; + * @return {?training.ModelDetails} + */ +training.ModelDetailsRequest.prototype.getModelDetails = function() { + return /** @type{?training.ModelDetails} */ ( + jspb.Message.getWrapperField(this, training.ModelDetails, 1)); +}; + + +/** @param {?training.ModelDetails|undefined} value */ +training.ModelDetailsRequest.prototype.setModelDetails = function(value) { + jspb.Message.setWrapperField(this, 1, value); +}; + + +training.ModelDetailsRequest.prototype.clearModelDetails = function() { + this.setModelDetails(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.ModelDetailsRequest.prototype.hasModelDetails = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional AuthorizationDetails authorization = 2; + * @return {?training.AuthorizationDetails} + */ +training.ModelDetailsRequest.prototype.getAuthorization = function() { + return /** @type{?training.AuthorizationDetails} */ ( + jspb.Message.getWrapperField(this, training.AuthorizationDetails, 2)); +}; + + +/** @param {?training.AuthorizationDetails|undefined} value */ +training.ModelDetailsRequest.prototype.setAuthorization = function(value) { + jspb.Message.setWrapperField(this, 2, value); +}; + + +training.ModelDetailsRequest.prototype.clearAuthorization = function() { + this.setAuthorization(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.ModelDetailsRequest.prototype.hasAuthorization = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.TrainingMethodOption = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.TrainingMethodOption, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.TrainingMethodOption.displayName = 'training.TrainingMethodOption'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.TrainingMethodOption.prototype.toObject = function(opt_includeInstance) { + return training.TrainingMethodOption.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.TrainingMethodOption} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.TrainingMethodOption.toObject = function(includeInstance, msg) { + var f, obj = { + trainingmethodindicator: jspb.Message.getFieldWithDefault(msg, 1, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.TrainingMethodOption} + */ +training.TrainingMethodOption.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.TrainingMethodOption; + return training.TrainingMethodOption.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.TrainingMethodOption} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.TrainingMethodOption} + */ +training.TrainingMethodOption.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setTrainingmethodindicator(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.TrainingMethodOption.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.TrainingMethodOption.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.TrainingMethodOption} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.TrainingMethodOption.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTrainingmethodindicator(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } +}; + + +/** + * optional string trainingMethodIndicator = 1; + * @return {string} + */ +training.TrainingMethodOption.prototype.getTrainingmethodindicator = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** @param {string} value */ +training.TrainingMethodOption.prototype.setTrainingmethodindicator = function(value) { + jspb.Message.setProto3StringField(this, 1, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.UpdateModelRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.UpdateModelRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.UpdateModelRequest.displayName = 'training.UpdateModelRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.UpdateModelRequest.prototype.toObject = function(opt_includeInstance) { + return training.UpdateModelRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.UpdateModelRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.UpdateModelRequest.toObject = function(includeInstance, msg) { + var f, obj = { + updateModelDetails: (f = msg.getUpdateModelDetails()) && training.ModelDetails.toObject(includeInstance, f), + authorization: (f = msg.getAuthorization()) && training.AuthorizationDetails.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.UpdateModelRequest} + */ +training.UpdateModelRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.UpdateModelRequest; + return training.UpdateModelRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.UpdateModelRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.UpdateModelRequest} + */ +training.UpdateModelRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new training.ModelDetails; + reader.readMessage(value,training.ModelDetails.deserializeBinaryFromReader); + msg.setUpdateModelDetails(value); + break; + case 2: + var value = new training.AuthorizationDetails; + reader.readMessage(value,training.AuthorizationDetails.deserializeBinaryFromReader); + msg.setAuthorization(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.UpdateModelRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.UpdateModelRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.UpdateModelRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.UpdateModelRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getUpdateModelDetails(); + if (f != null) { + writer.writeMessage( + 1, + f, + training.ModelDetails.serializeBinaryToWriter + ); + } + f = message.getAuthorization(); + if (f != null) { + writer.writeMessage( + 2, + f, + training.AuthorizationDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * optional ModelDetails update_model_details = 1; + * @return {?training.ModelDetails} + */ +training.UpdateModelRequest.prototype.getUpdateModelDetails = function() { + return /** @type{?training.ModelDetails} */ ( + jspb.Message.getWrapperField(this, training.ModelDetails, 1)); +}; + + +/** @param {?training.ModelDetails|undefined} value */ +training.UpdateModelRequest.prototype.setUpdateModelDetails = function(value) { + jspb.Message.setWrapperField(this, 1, value); +}; + + +training.UpdateModelRequest.prototype.clearUpdateModelDetails = function() { + this.setUpdateModelDetails(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.UpdateModelRequest.prototype.hasUpdateModelDetails = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional AuthorizationDetails authorization = 2; + * @return {?training.AuthorizationDetails} + */ +training.UpdateModelRequest.prototype.getAuthorization = function() { + return /** @type{?training.AuthorizationDetails} */ ( + jspb.Message.getWrapperField(this, training.AuthorizationDetails, 2)); +}; + + +/** @param {?training.AuthorizationDetails|undefined} value */ +training.UpdateModelRequest.prototype.setAuthorization = function(value) { + jspb.Message.setWrapperField(this, 2, value); +}; + + +training.UpdateModelRequest.prototype.clearAuthorization = function() { + this.setAuthorization(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.UpdateModelRequest.prototype.hasAuthorization = function() { + return jspb.Message.getField(this, 2) != null; +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +training.ModelDetailsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(training.ModelDetailsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + training.ModelDetailsResponse.displayName = 'training.ModelDetailsResponse'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +training.ModelDetailsResponse.prototype.toObject = function(opt_includeInstance) { + return training.ModelDetailsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!training.ModelDetailsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetailsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + status: jspb.Message.getFieldWithDefault(msg, 1, 0), + modelDetails: (f = msg.getModelDetails()) && training.ModelDetails.toObject(includeInstance, f) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!training.ModelDetailsResponse} + */ +training.ModelDetailsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new training.ModelDetailsResponse; + return training.ModelDetailsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!training.ModelDetailsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!training.ModelDetailsResponse} + */ +training.ModelDetailsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {!training.Status} */ (reader.readEnum()); + msg.setStatus(value); + break; + case 2: + var value = new training.ModelDetails; + reader.readMessage(value,training.ModelDetails.deserializeBinaryFromReader); + msg.setModelDetails(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +training.ModelDetailsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + training.ModelDetailsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!training.ModelDetailsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +training.ModelDetailsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getStatus(); + if (f !== 0.0) { + writer.writeEnum( + 1, + f + ); + } + f = message.getModelDetails(); + if (f != null) { + writer.writeMessage( + 2, + f, + training.ModelDetails.serializeBinaryToWriter + ); + } +}; + + +/** + * optional Status status = 1; + * @return {!training.Status} + */ +training.ModelDetailsResponse.prototype.getStatus = function() { + return /** @type {!training.Status} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** @param {!training.Status} value */ +training.ModelDetailsResponse.prototype.setStatus = function(value) { + jspb.Message.setProto3EnumField(this, 1, value); +}; + + +/** + * optional ModelDetails model_details = 2; + * @return {?training.ModelDetails} + */ +training.ModelDetailsResponse.prototype.getModelDetails = function() { + return /** @type{?training.ModelDetails} */ ( + jspb.Message.getWrapperField(this, training.ModelDetails, 2)); +}; + + +/** @param {?training.ModelDetails|undefined} value */ +training.ModelDetailsResponse.prototype.setModelDetails = function(value) { + jspb.Message.setWrapperField(this, 2, value); +}; + + +training.ModelDetailsResponse.prototype.clearModelDetails = function() { + this.setModelDetails(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +training.ModelDetailsResponse.prototype.hasModelDetails = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * @enum {number} + */ +training.Status = { + CREATED: 0, + IN_PROGRESS: 1, + ERRORED: 2, + COMPLETED: 3, + DELETED: 4 +}; + + +/** + * A tuple of {field number, class constructor} for the extension + * field named `myMethodOption`. + * @type {!jspb.ExtensionFieldInfo} + */ +training.myMethodOption = new jspb.ExtensionFieldInfo( + 9999197, + {myMethodOption: 0}, + training.TrainingMethodOption, + /** @type {?function((boolean|undefined),!jspb.Message=): !Object} */ ( + training.TrainingMethodOption.toObject), + 0); + +google_protobuf_descriptor_pb.MethodOptions.extensionsBinary[9999197] = new jspb.ExtensionFieldBinaryInfo( + training.myMethodOption, + jspb.BinaryReader.prototype.readMessage, + jspb.BinaryWriter.prototype.writeMessage, + training.TrainingMethodOption.serializeBinaryToWriter, + training.TrainingMethodOption.deserializeBinaryFromReader, + false); +// This registers the extension field with the extended class, so that +// toObject() will function correctly. +google_protobuf_descriptor_pb.MethodOptions.extensions[9999197] = training.myMethodOption; + +goog.object.extend(exports, training); diff --git a/snet-sdk-js/packages/web/src/proto/training_pb_service.js b/snet-sdk-js/packages/web/src/proto/training_pb_service.js new file mode 100644 index 000000000..61ae6c886 --- /dev/null +++ b/snet-sdk-js/packages/web/src/proto/training_pb_service.js @@ -0,0 +1,221 @@ +// package: training +// file: singnet/snet-daemon/training/training.proto + +var singnet_snet_daemon_training_training_pb = require("./training_pb"); +var grpc = require("@improbable-eng/grpc-web").grpc; + +var Model = (function () { + function Model() {} + Model.serviceName = "training.Model"; + return Model; +}()); + +Model.create_model = { + methodName: "create_model", + service: Model, + requestStream: false, + responseStream: false, + requestType: singnet_snet_daemon_training_training_pb.CreateModelRequest, + responseType: singnet_snet_daemon_training_training_pb.ModelDetailsResponse +}; + +Model.delete_model = { + methodName: "delete_model", + service: Model, + requestStream: false, + responseStream: false, + requestType: singnet_snet_daemon_training_training_pb.UpdateModelRequest, + responseType: singnet_snet_daemon_training_training_pb.ModelDetailsResponse +}; + +Model.get_model_status = { + methodName: "get_model_status", + service: Model, + requestStream: false, + responseStream: false, + requestType: singnet_snet_daemon_training_training_pb.ModelDetailsRequest, + responseType: singnet_snet_daemon_training_training_pb.ModelDetailsResponse +}; + +Model.update_model_access = { + methodName: "update_model_access", + service: Model, + requestStream: false, + responseStream: false, + requestType: singnet_snet_daemon_training_training_pb.UpdateModelRequest, + responseType: singnet_snet_daemon_training_training_pb.ModelDetailsResponse +}; + +Model.get_all_models = { + methodName: "get_all_models", + service: Model, + requestStream: false, + responseStream: false, + requestType: singnet_snet_daemon_training_training_pb.AccessibleModelsRequest, + responseType: singnet_snet_daemon_training_training_pb.AccessibleModelsResponse +}; + +exports.Model = Model; + +function ModelClient(serviceHost, options) { + this.serviceHost = serviceHost; + this.options = options || {}; +} + +ModelClient.prototype.create_model = function create_model(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Model.create_model, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +ModelClient.prototype.delete_model = function delete_model(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Model.delete_model, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +ModelClient.prototype.get_model_status = function get_model_status(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Model.get_model_status, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +ModelClient.prototype.update_model_access = function update_model_access(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Model.update_model_access, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +ModelClient.prototype.get_all_models = function get_all_models(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Model.get_all_models, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + +exports.ModelClient = ModelClient; + diff --git a/snet-sdk-js/packages/web/src/sdk-core.js b/snet-sdk-js/packages/web/src/sdk-core.js new file mode 100644 index 000000000..39b395a8e --- /dev/null +++ b/snet-sdk-js/packages/web/src/sdk-core.js @@ -0,0 +1,5 @@ +import SnetSdk from './core'; + +export default SnetSdk; + +export * from './core'; diff --git a/snet-sdk-js/packages/web/src/utils/BrowserConsole.js b/snet-sdk-js/packages/web/src/utils/BrowserConsole.js new file mode 100644 index 000000000..e3b92dcdc --- /dev/null +++ b/snet-sdk-js/packages/web/src/utils/BrowserConsole.js @@ -0,0 +1,48 @@ +const Transport = require('winston-transport'); + +export default class BrowserConsole extends Transport { + constructor(opts) { + super(opts); + + this.methods = { + debug: "debug", + error: "error", + info: "info", + warn: "warn" + }; + + if (opts && opts.level && Level.hasOwnProperty(opts.level)) { + this.level = opts.level; + } + } + + log(logEntry, next) { + setImmediate(() => { + this.emit("logged", logEntry); + }); + + const { message, level } = logEntry; + const mappedMethod = this.methods[level]; + + if (Object.getOwnPropertySymbols(logEntry).length === 2) { + console[mappedMethod](message); + } else { + let args = logEntry[Object.getOwnPropertySymbols(logEntry)[1]]; + args = args.length >= 1 ? args[0] : args; + if (args) { + console[mappedMethod](message, args); + } else { + console[mappedMethod](message); + } + } + + next(); + } +} + +const Level = { + error: 0, + warn: 1, + info: 2, + debug: 4 +}; \ No newline at end of file diff --git a/snet-sdk-js/packages/web/src/utils/logger.js b/snet-sdk-js/packages/web/src/utils/logger.js new file mode 100644 index 000000000..17ebb0f8a --- /dev/null +++ b/snet-sdk-js/packages/web/src/utils/logger.js @@ -0,0 +1,6 @@ +import { logger } from '../sdk-core'; +import BrowserConsole from './BrowserConsole'; + +logger.add(new BrowserConsole({ + level: 'debug', +})); From 3c94e7a854cb88108e73c8d042875a28769018a3 Mon Sep 17 00:00:00 2001 From: Marina Fedyantceva Date: Tue, 12 Mar 2024 18:00:48 +0300 Subject: [PATCH 03/27] fixed lint errors --- .eslintignore | 3 + .eslintrc.json | 7 +- scripts/generate-stubs.js | 10 +- scripts/sitemap-generator.js | 4 +- scripts/zip-components.js | 2 +- src/App.js | 6 +- src/Redux/Store.js | 4 +- src/Redux/actionCreators/ErrorActions.js | 12 +- src/Redux/actionCreators/LoaderActions.js | 8 +- src/Redux/actionCreators/PaymentActions.js | 18 +- src/Redux/actionCreators/ServiceActions.js | 80 ++- .../actionCreators/ServiceDetailsActions.js | 42 +- src/Redux/actionCreators/StylesActions.js | 2 +- src/Redux/actionCreators/UiContentActions.js | 2 +- src/Redux/actionCreators/UserActions.js | 318 +++++---- src/Redux/reducers/PaymentReducer.js | 12 +- src/Redux/reducers/ServiceDetailsReducer.js | 16 +- src/Redux/reducers/UserReducer.js | 8 +- src/assets/Theme.js | 4 +- src/assets/externalScripts/gdpr.js | 4 +- .../thirdPartyServices/common/FileUploader.js | 2 +- .../common/MethodNamesDropDown.js | 2 +- .../common/OutlinedDropdown.js | 2 +- .../common/OutlinedLabel.js | 74 +- .../common/OutlinedTextArea.js | 6 +- .../gene_annotation_service/form/index.js | 72 +- .../mozi/gene_annotation_service/index.js | 2 +- .../proto/annotation_pb_service.js | 8 +- .../gene_annotation_service/result/index.js | 64 +- .../mozi/gene_annotation_service/service.js | 6 +- .../gene_annotation_service/tables/index.js | 24 +- .../gene_annotation_service/tabs/index.js | 68 +- .../visualizer/index.js | 665 ++++++++---------- .../nunet-org/binary-classification/index.js | 2 +- .../fake-news-score-service/index.js | 2 +- .../fake_news_score_service/index.js | 5 +- .../nunet-org/uclnlp-service/index.js | 4 +- .../covid-detection/cough_test_pb_service.js | 2 +- .../rejuve/covid-detection/index.js | 2 +- .../image_recon_pb_service.js | 29 +- .../snet/cntk_image_recon/index.js | 4 +- .../language_understanding_pb_service.js | 29 +- .../snet/cntk_lstm_forecast/index.js | 13 +- .../time_series_forecast_pb_service.js | 17 +- .../next_day_trend_pb_service.js | 17 +- .../CoreferenceResolutionService_pb.js | 8 +- ...CoreferenceResolutionService_pb_service.js | 17 +- .../coreference_resolution_service/index.js | 2 +- .../colorization_pb_service.js | 2 +- .../snet/deoldify-colorizer/index.js | 2 +- .../EmotionService_pb.js | 4 +- .../EmotionService_pb_service.js | 17 +- .../EmotionVisualizer.js | 4 +- .../snet/example_service/index.js | 8 +- .../example_service/pricing_pb_service.js | 1 - .../snet/example_service/training_pb.js | 32 +- .../snet/face_align/face_alignment_pb.js | 8 +- .../face_align/face_alignment_pb_service.js | 17 +- .../snet/face_align/index.js | 6 +- .../face_detect/face_detect_pb_service.js | 19 +- .../snet/face_detect/index.js | 4 +- .../snet/face_identity/face_recognition_pb.js | 8 +- .../face_recognition_pb_service.js | 35 +- .../snet/face_identity/index.js | 10 +- .../snet/face_landmarks/face_landmarks_pb.js | 12 +- .../face_landmarks_pb_service.js | 29 +- .../snet/face_landmarks/index.js | 12 +- .../fbprophet_forecast_pb_service.js | 15 +- .../snet/fbprophet-forecast/index.js | 265 ++++--- .../edgedetect_pb_service.js | 17 +- .../video_action_recon_pb_service.js | 23 +- .../image-viewer-helpers/ImageGridViewer.js | 2 +- .../image-viewer-helpers/MasonryLayout.js | 4 +- .../image_retrival_pb_service.js | 17 +- .../LanguageDetection_pb_service.js | 17 +- .../snet/language_detection/index.js | 4 +- .../match_service/MatchingAPI_pb_service.js | 105 +-- .../snet/match_service/index.js | 2 +- .../MinecraftizingService_pb_service.js | 21 +- .../snet/moses_service/CrossValOpts.js | 10 +- .../snet/moses_service/DatasetUpload.js | 2 +- .../snet/moses_service/MosesOpts.js | 50 +- .../snet/moses_service/MosesServiceForm.js | 28 +- .../snet/moses_service/MosesServiceResult.js | 2 +- .../snet/moses_service/TargetFeature.js | 24 +- .../snet/moses_service/index.js | 2 +- .../snet/moses_service/moses_service_pb.js | 8 +- .../moses_service/moses_service_pb_service.js | 17 +- .../snet/moses_service/utils.js | 10 +- .../NamedEntityDisambiguation_pb_service.js | 23 +- .../snet/named_entity_disambiguation/index.js | 2 +- .../snet/named_entity_recognition/index.js | 8 +- ...named_entity_recognition_rpc_pb_service.js | 31 +- .../snet/network_analytics_bipartite/index.js | 8 +- .../network_analytics_bipartite_pb.js | 16 +- .../network_analytics_bipartite_pb_service.js | 29 +- .../network_analytics_robustness/index.js | 8 +- .../network_analytics_robustness_pb.js | 16 +- ...network_analytics_robustness_pb_service.js | 41 +- .../snet/news_summary/summary_pb_service.js | 17 +- .../snet/opencog_miner/index.js | 4 +- .../snet/opencog_miner/opencog_pb.js | 2 +- .../snet/opencog_miner/opencog_pb_service.js | 27 +- .../opencog_vqa/vqa_opencog_pb_service.js | 19 +- .../romance_translator_pb.js | 1 - .../romance_translator_pb_service.js | 17 +- .../snet/places365_scene_recognition/index.js | 21 +- .../scene_recognition_pb_service.js | 17 +- .../snet/pneumonia_diagnosis/index.js | 4 +- .../pneumonia_diagnosis_pb_service.js | 15 +- .../snet/question_answering_long_seq/index.js | 2 +- .../qa_pb_service.js | 15 +- .../question_answering_short_seq/index.js | 2 +- .../qa_bert_pb_service.js | 15 +- .../snet/real_time_voice_cloning/index.js | 4 +- .../voice_cloning_pb_service.js | 17 +- .../snet/s2vt_video_captioning/index.js | 4 +- .../video_cap_pb_service.js | 17 +- .../snet/semantic_segmentation/index.js | 2 +- .../semantic_segmentation/segmentation_pb.js | 8 +- .../segmentation_pb_service.js | 29 +- .../semantic_segmentation_aerial/index.js | 14 +- .../snet/semantic_similarity_binary/index.js | 2 +- .../ss_bert_pb_service.js | 15 +- .../snet/sentiment_analysis/index.js | 646 ++++++++--------- .../sentiment_analysis_rpc_pb_service.js | 17 +- .../snet/sound_spleeter/index.js | 4 +- .../sound_spleeter_pb_service.js | 17 +- .../snet/speech_recognition/asr_pb_service.js | 15 +- .../snet/speech_recognition/index.js | 4 +- .../snet/speech_synthesis/index.js | 2 +- .../snet/speech_synthesis/tts_pb_service.js | 15 +- .../snet/style_transfer/index.js | 18 +- .../style_transfer_pb_service.js | 17 +- .../snet/super_resolution/index.js | 18 +- .../super_resolution_pb_service.js | 23 +- .../snet/text_generation/index.js | 16 +- .../snet/text_generation/ntg_pb_service.js | 15 +- .../snet/text_generation/styles.js | 2 +- .../time_series_anomaly_discovery/index.js | 47 +- .../timeSeriesAnomalyDetection_pb_service.js | 24 +- .../snet/topic_analysis/index.js | 8 +- .../topic_analysis_pb_service.js | 17 +- .../snet/translation/index.js | 2 +- .../snet/translation/translate_pb_service.js | 17 +- .../snet/yolov3_object_detection/index.js | 2 +- .../object_detection_pb_service.js | 17 +- .../alpha_zero_pb_service.js | 17 +- .../snet/zeta36_chess_alpha_zero/index.js | 2 +- .../standardComponents/HoverIcon.js | 4 +- .../standardComponents/SNETImageUpload.js | 30 +- .../Filter/StyledExpansionPanel.js | 4 +- .../Filter/ToolBar/ServiceSortOptions.js | 10 +- .../MainSection/Filter/ToolBar/index.js | 12 +- .../MainSection/Filter/ToolBar/styles.js | 2 +- .../AiMarketplace/MainSection/Filter/index.js | 8 +- .../MainSection/Filter/styles.js | 2 +- .../CardGroup/GridViewItem/index.js | 2 +- .../CardGroup/GridViewItem/styles.js | 2 +- .../CardGroup/ServiceListItem/index.js | 2 +- .../CardGroup/ServiceListItem/styles.js | 2 +- .../ServiceCollection/CardGroup/index.js | 6 +- .../ServiceCollection/CardGroup/styles.js | 2 +- .../StyledPagination/index.js | 4 +- .../StyledPagination/styles.js | 2 +- .../MainSection/ServiceCollection/styles.js | 2 +- .../AiMarketplace/MainSection/index.js | 16 +- .../AiMarketplace/MainSection/styles.js | 2 +- .../ServiceListingHeader/index.js | 6 +- .../ServiceListingHeader/styles.js | 2 +- src/components/AiMarketplace/index.js | 2 +- .../Header/MobileHeader/index.js | 8 +- .../Header/MobileHeader/styles.js | 2 +- src/components/AiRequestForm/Header/NavBar.js | 2 +- src/components/AiRequestForm/Header/index.js | 2 +- src/components/AiRequestForm/Header/styles.js | 2 +- src/components/AiRequestForm/styles.js | 2 +- .../Category/FeatureMedia/styles.js | 2 +- .../GetStarted/Category/VerticalTabs/index.js | 2 +- .../Category/VerticalTabs/styles.js | 2 +- src/components/GetStarted/Category/styles.js | 2 +- .../GetStarted/Features/Feature/styles.js | 2 +- src/components/GetStarted/Features/index.js | 2 +- src/components/GetStarted/Features/styles.js | 2 +- .../GetStartedDescription/styles.js | 2 +- src/components/GetStarted/styles.js | 6 +- src/components/HOC/WithInAppHeader/index.js | 4 +- src/components/HOC/WithInAppHeader/styles.js | 2 +- src/components/HOC/WithRegistrationHeader.js | 2 +- src/components/Hooks/useLocalStorage.js | 2 +- src/components/Hooks/useOutsideClick.js | 2 +- src/components/Login/ForgotPassword/index.js | 14 +- src/components/Login/ForgotPassword/styles.js | 2 +- .../Login/ForgotPasswordSubmit/index.js | 23 +- .../Login/ForgotPasswordSubmit/styles.js | 2 +- src/components/Login/Signup/index.js | 24 +- src/components/Login/Signup/styles.js | 2 +- src/components/Login/index.js | 14 +- src/components/Login/styles.js | 2 +- .../Onboarding/Authentication/index.js | 12 +- .../Onboarding/Authentication/styles.js | 2 +- src/components/Onboarding/TermsOfUse/index.js | 11 +- .../Onboarding/TermsOfUse/styles.js | 2 +- src/components/Onboarding/index.js | 4 +- src/components/Onboarding/styles.js | 2 +- src/components/PromoComponent/index.js | 4 +- src/components/PromoComponent/styles.js | 2 +- .../AboutService/PromoBox/index.js | 2 +- .../AboutService/PromoBox/styles.js | 2 +- .../ServiceDemo/CompletedActions/styles.js | 2 +- .../ServiceDemo/ImageUpload/styles.js | 2 +- .../Purchase/ActiveSession/styles.js | 2 +- .../Purchase/ChannelSelectionBox/styles.js | 2 +- .../GeneralAccountWallet/LinkProvider.js | 2 +- .../GeneralAccountWallet/NextAction.js | 12 +- .../PaymentPopup/Details/index.js | 8 +- .../PaymentPopup/Details/styles.js | 2 +- .../PaymentPopup/PrivateKey/styles.js | 2 +- .../PaymentPopup/Purchase/styles.js | 2 +- .../PaymentPopup/Summary/index.js | 4 +- .../PaymentPopup/Summary/styles.js | 2 +- .../PaymentPopup/VerifyKey/index.js | 10 +- .../PaymentPopup/VerifyKey/styles.js | 2 +- .../PaymentPopup/index.js | 24 +- .../PaymentPopup/styles.js | 2 +- .../GeneralAccountWallet/index.js | 13 +- .../GeneralAccountWallet/styles.js | 2 +- .../ExpiredSession/MetamaskFlow/index.js | 25 +- .../ExpiredSession/MetamaskFlow/style.js | 2 +- .../Purchase/ExpiredSession/index.js | 19 +- .../Purchase/ExpiredSession/styles.js | 2 +- .../Purchase/PaymentInfoCard/styles.js | 2 +- .../Purchase/PurchaseDialog/Deposit/styles.js | 2 +- .../Purchase/PurchaseDialog/DialogTitle.js | 2 +- .../Purchase/PurchaseDialog/index.js | 2 +- .../Purchase/PurchaseDialog/styles.js | 2 +- .../ServiceDemo/Purchase/styles.js | 2 +- .../ServiceDemo/ThirdPartyAIService.js | 4 +- .../ThirdPartyServiceErrorBoundary.js | 2 +- .../ServiceDemo/UserFeedback/index.js | 14 +- .../ServiceDemo/UserFeedback/styles.js | 2 +- .../AboutService/ServiceDemo/index.js | 22 +- .../AboutService/ServiceDemo/styles.js | 2 +- .../AboutService/ServiceOverview.js | 2 +- .../ServiceDetails/AboutService/Tags.js | 2 +- .../ServiceDetails/AboutService/index.js | 4 +- .../ServiceDetails/AboutService/styles.js | 2 +- .../ServiceDetails/ConnectMetamask/styles.js | 49 +- .../CreatorDetails/Contacts/index.js | 2 +- .../CreatorDetails/Contacts/styles.js | 2 +- .../ServiceDetails/CreatorDetails/styles.js | 2 +- .../ExistingModel/ModelDetails/index.js | 2 +- .../ExistingModel/ModelDetails/styles.js | 4 +- .../ServiceDetails/ExistingModel/index.js | 10 +- .../ServiceDetails/ExistingModel/styles.js | 40 +- .../InstallAndRunService/Nodejs/index.js | 9 +- .../InstallAndRunService/Python/index.js | 81 ++- .../InstallAndRunService/index.js | 9 +- .../InstallAndRunService/styles.js | 4 +- .../ServiceDetails/MediaGallery/index.js | 14 +- .../ServiceDetails/MediaGallery/styles.js | 2 +- .../ServiceDetails/PaymentCanceled.js | 9 +- .../ServiceDetails/PaymentCancelled.js | 9 +- .../ServiceDetails/PricingDetails/styles.js | 2 +- .../ServiceDetails/ProjectDetails/styles.js | 4 +- .../ServiceDetails/StyledTabs/index.js | 4 +- .../ServiceDetails/StyledTabs/styles.js | 2 +- .../ServiceDetails/TitleCard/index.js | 2 +- .../ServiceDetails/TitleCard/styles.js | 2 +- .../CreateModel/Data/Upload/UploadFromLink.js | 2 +- .../CreateModel/Data/Upload/styles.js | 12 +- .../TrainingModels/CreateModel/Data/styles.js | 44 +- .../CreateModel/Finish/styles.js | 16 +- .../ModelInfo/AddMoreEthAddress.js | 4 +- .../CreateModel/ModelInfo/index.js | 30 +- .../CreateModel/ModelInfo/styles.js | 2 +- .../CreateModel/Payment/PaymentMode.js | 8 +- .../CreateModel/Payment/index.js | 18 +- .../CreateModel/Payment/styles.js | 6 +- .../TrainingModels/CreateModel/styles.js | 62 +- .../ServiceDetails/TrainingModels/index.js | 5 +- .../ServiceDetails/TrainingModels/styles.js | 26 +- src/components/ServiceDetails/index.js | 10 +- src/components/ServiceDetails/styles.js | 2 +- .../UserProfileAccount/Deposit/styles.js | 2 +- .../MetamaskDetails/index.js | 14 +- .../MetamaskDetails/styles.js | 2 +- .../ProviderBalance/ChannelList/index.js | 2 +- .../ProviderBalance/ChannelList/styles.js | 2 +- .../ProviderBalance/styles.js | 2 +- .../ProvidersLinkedCount/styles.js | 2 +- .../UserProfile/UserProfileAccount/index.js | 10 +- .../UserProfile/UserProfileAccount/styles.js | 2 +- .../UserProfile/UserProfileHeader/styles.js | 2 +- .../UserProfile/UserProfileModels/index.js | 16 +- .../UserProfile/UserProfileModels/styles.js | 16 +- .../ConfirmDelete/index.js | 6 +- .../ConfirmDelete/styles.js | 2 +- .../UserProfile/UserProfileSettings/index.js | 10 +- .../UserProfile/UserProfileSettings/styles.js | 2 +- .../Payments/PaymentData/styles.js | 2 +- .../Payments/styles.js | 2 +- .../UserProfileTransactionHistory/index.js | 15 +- .../UserProfileTransactionHistory/styles.js | 2 +- src/components/UserProfile/index.js | 4 +- src/components/UserProfile/styles.js | 2 +- .../UserProfilePopUp/UserMenu/index.js | 4 +- .../UserProfilePopUp/UserMenu/styles.js | 2 +- .../UserProfilePopUpHeader/styles.js | 2 +- src/components/UserProfilePopUp/index.js | 2 +- src/components/UserProfilePopUp/styles.js | 2 +- src/components/common/AlertBox/styles.js | 2 +- src/components/common/AlertText/styles.js | 2 +- src/components/common/AppLoader/index.js | 2 +- src/components/common/AppLoader/styles.js | 2 +- .../common/CodeSnippet/Function/styles.js | 2 +- .../common/CodeSnippet/Key/styles.js | 2 +- .../common/CodeSnippet/ValueNumber/styles.js | 2 +- .../common/CodeSnippet/ValueString/styles.js | 2 +- src/components/common/CodeSnippet/styles.js | 2 +- src/components/common/ErrorBox/styles.js | 2 +- .../Footer/PrimaryFooter/FooterLinks/index.js | 4 +- .../PrimaryFooter/FooterLinks/styles.js | 2 +- .../common/Footer/PrimaryFooter/index.js | 2 +- .../common/Footer/PrimaryFooter/styles.js | 2 +- .../common/Footer/SecondaryFooter/index.js | 2 +- .../common/Footer/SecondaryFooter/styles.js | 2 +- src/components/common/Footer/styles.js | 2 +- src/components/common/Header/HeaderActions.js | 2 +- .../common/Header/MobileHeader/index.js | 17 +- .../common/Header/MobileHeader/styles.js | 2 +- src/components/common/Header/NavBar.js | 4 +- src/components/common/Header/SignOut.js | 9 +- src/components/common/Header/index.js | 2 +- src/components/common/Header/styles.js | 2 +- src/components/common/InlineLoader/styles.js | 2 +- .../common/LoginOnboardingHeader/index.js | 9 +- .../common/LoginOnboardingHeader/styles.js | 2 +- .../common/NetworkChangeOverlay/index.js | 11 +- .../common/NetworkChangeOverlay/styles.js | 2 +- .../common/NoDemoComponent/styles.js | 2 +- .../common/NotificationBar/styles.js | 2 +- .../common/OfflineIndicator/styles.js | 2 +- src/components/common/PrivateRoute.js | 2 +- .../ProgressBar/ProgressSection/styles.js | 2 +- src/components/common/ProgressBar/styles.js | 2 +- src/components/common/RatingsCount/styles.js | 2 +- src/components/common/SeoMetadata.js | 6 +- src/components/common/StyledButton/styles.js | 2 +- src/components/common/StyledDropdown/index.js | 2 +- .../common/StyledDropdown/styles.js | 2 +- .../common/StyledLinearProgress/styles.js | 2 +- src/components/common/StyledMenu/index.js | 4 +- src/components/common/StyledMenu/styles.js | 2 +- src/components/common/StyledTable/index.js | 6 +- src/components/common/StyledTable/styles.js | 2 +- .../common/StyledTextField/styles.js | 2 +- .../common/UpdateNotificationBar/styles.js | 2 +- .../common/UserProfileCard/styles.js | 2 +- src/config/APIEndpoints.js | 2 +- src/config/Networks.js | 2 +- src/config/aws_config.js | 2 +- src/index.js | 8 +- src/sandbox/SandboxApp.js | 2 +- src/stories/SNETImageUpload.stories.js | 2 +- src/stories/index.stories.js | 2 +- src/utility/ErrorHandling.js | 2 +- src/utility/MediaHelper.js | 27 +- src/utility/PricingStrategy.js | 10 +- src/utility/Validation.js | 2 +- src/utility/constants/LoaderContent.js | 2 +- src/utility/constants/Pagination.js | 4 +- src/utility/constants/UserPopupMenu.js | 2 +- src/utility/sdk.js | 55 +- src/utility/snetSdk.js | 16 +- 375 files changed, 2700 insertions(+), 2782 deletions(-) diff --git a/.eslintignore b/.eslintignore index 11ef1cc96..58d37e3b5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -83,3 +83,6 @@ src/aws_config.js #external scripts src/assets/externalScripts + +# test new version sdk +/snet-sdk-js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 1bbb2946c..c3ea91f70 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,16 +1,17 @@ { "extends": ["react-app", "eslint:recommended"], - "parser": "babel-eslint", + "parser": "@babel/eslint-parser", "plugins": ["react", "prettier", "react-hooks"], "rules": { "prettier/prettier": [ - "error", + "warn", { "singleQuote": false, "semi": true, "max-len": { "code": 120, "ignoreUrls": true }, "tab-width": 2 } ], "lines-between-class-members": ["error", "always"], "jsx-a11y/anchor-is-valid": "warn", "prefer-arrow-callback": ["error", { "allowNamedFunctions": false, "allowUnboundThis": false }], - "no-empty-function": ["error"], + "no-empty-function": ["warn"], + "no-prototype-builtins": ["warn"], "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "react/self-closing-comp": ["error", { "component": true, "html": true }], diff --git a/scripts/generate-stubs.js b/scripts/generate-stubs.js index d3276325b..87923cb12 100644 --- a/scripts/generate-stubs.js +++ b/scripts/generate-stubs.js @@ -66,7 +66,7 @@ const promptForDetails = async () => { type: "text", name: "packageName", message: "Package Name", - validate: value => + validate: (value) => value && Boolean(value.trim()) ? true : "Please enter the Package Name. It wil be in the .proto file", }); if (!response.packageName) { @@ -78,7 +78,7 @@ const promptForDetails = async () => { type: "text", name: "orgId", message: "Organization Id", - validate: value => (value && Boolean(value.trim()) ? true : "Please enter the organization Id"), + validate: (value) => (value && Boolean(value.trim()) ? true : "Please enter the organization Id"), initial: process.env.REACT_APP_SANDBOX_ORG_ID, }); if (!response.orgId) { @@ -91,7 +91,7 @@ const promptForDetails = async () => { type: "text", name: "serviceId", message: "Service Id", - validate: value => (value && Boolean(value.trim()) ? true : "Please enter the service Id"), + validate: (value) => (value && Boolean(value.trim()) ? true : "Please enter the service Id"), initial: process.env.REACT_APP_SANDBOX_SERVICE_ID, }); if (!response.serviceId) { @@ -112,7 +112,7 @@ const getProtoFilePathFromArgs = () => { return protoFilePath; }; -const downloadProtoCBinary = async protoBinaryFileURL => { +const downloadProtoCBinary = async (protoBinaryFileURL) => { let data; try { const response = await axios.get(protoBinaryFileURL, { responseType: "arraybuffer" }); @@ -130,7 +130,7 @@ const downloadProtoCBinary = async protoBinaryFileURL => { } }; -const executeProtoCBinary = async protoFilePath => { +const executeProtoCBinary = async (protoFilePath) => { if (shouldIncludeNamespacePrefix) { outputDir = `${outputDir}/${orgId.replace(/-/g, "_")}_${serviceId.replace(/-/g, "_")}`; } diff --git a/scripts/sitemap-generator.js b/scripts/sitemap-generator.js index fafdbc6a3..1d0037475 100644 --- a/scripts/sitemap-generator.js +++ b/scripts/sitemap-generator.js @@ -55,12 +55,12 @@ async function generateSitemap() { console.log("fetching service"); await fetchServices(); console.log("fetched all services"); - const idMap = services.map(service => ({ + const idMap = services.map((service) => ({ orgId: service.org_id, serviceId: service.service_id, })); - const activeUserTabsMap = ["account", "settings", "transactions"].map(tab => ({ "activeTab?": tab })); + const activeUserTabsMap = ["account", "settings", "transactions"].map((tab) => ({ "activeTab?": tab })); const paramsConfig = { "/servicedetails/org/:orgId/service/:serviceId": idMap, diff --git a/scripts/zip-components.js b/scripts/zip-components.js index f82d0bef4..e6f583286 100644 --- a/scripts/zip-components.js +++ b/scripts/zip-components.js @@ -35,7 +35,7 @@ output.on("close", () => { console.log(`Your component has been archived successfully. \n${outputFilePath}`); }); -archive.on("error", err => { +archive.on("error", (err) => { throw err; }); diff --git a/src/App.js b/src/App.js index e485efda7..781e3c0c2 100644 --- a/src/App.js +++ b/src/App.js @@ -38,7 +38,7 @@ Amplify.configure(aws_config); ReactGA.initialize(process.env.REACT_APP_GA_TRACKING_ID); const history = createBrowserHistory(); -history.listen(location => { +history.listen((location) => { ReactGA.set({ page: location.pathname }); ReactGA.pageview(location.pathname); }); @@ -155,14 +155,14 @@ class App extends Component { } } -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ isLoggedIn: state.userReducer.login.isLoggedIn, isTermsAccepted: state.userReducer.isTermsAccepted, isInitialized: state.userReducer.isInitialized, hamburgerMenu: state.stylesReducer.hamburgerMenu, }); -const mapDispatchToProps = dispatch => ({ +const mapDispatchToProps = (dispatch) => ({ fetchUserDetails: () => dispatch(userActions.fetchUserDetails), }); diff --git a/src/Redux/Store.js b/src/Redux/Store.js index 0791dc0f2..968ccf8af 100644 --- a/src/Redux/Store.js +++ b/src/Redux/Store.js @@ -1,8 +1,8 @@ import { applyMiddleware, compose, createStore } from "redux"; -import thunk from "redux-thunk"; +import { thunk } from "redux-thunk"; import rootReducer from "./reducers"; -import sandboxState from '../sandbox/sandbox_state'; +import sandboxState from "../sandbox/sandbox_state"; let composeEnhancers = compose; const middlewares = [thunk]; diff --git a/src/Redux/actionCreators/ErrorActions.js b/src/Redux/actionCreators/ErrorActions.js index dc2185241..fffb95f3c 100644 --- a/src/Redux/actionCreators/ErrorActions.js +++ b/src/Redux/actionCreators/ErrorActions.js @@ -5,26 +5,26 @@ export const RESET_FORGOT_PASSWORD_SUBMIT_ERROR = "RESET_FORGOT_PASSWORD_SUBMIT_ export const UPDATE_PROFILE_SETTINGS_ERROR = "UPDATE_PROFILE_SETTINGS_ERROR"; export const RESET_PROFILE_SETTINGS_ERROR = "RESET_PROFILE_SETTINGS_ERROR"; -export const updateForgotPasswordError = error => dispatch => { +export const updateForgotPasswordError = (error) => (dispatch) => { dispatch({ type: UPDATE_FORGOT_PASSWORD_ERROR, payload: { forgotPassword: error } }); }; -export const resetForgotPasswordError = dispatch => { +export const resetForgotPasswordError = (dispatch) => { dispatch({ type: RESET_FORGOT_PASSWORD_ERROR, payload: { forgotPassword: undefined } }); }; -export const updateForgotPasswordSubmitError = error => dispatch => { +export const updateForgotPasswordSubmitError = (error) => (dispatch) => { dispatch({ type: UPDATE_FORGOT_PASSWORD_SUBMIT_ERROR, payload: { forgotPasswordSubmit: error } }); }; -export const resetForgotPasswordSubmitError = dispatch => { +export const resetForgotPasswordSubmitError = (dispatch) => { dispatch({ type: RESET_FORGOT_PASSWORD_SUBMIT_ERROR, payload: { forgotPasswordSubmit: undefined } }); }; -export const updateProfileSettingsError = error => dispatch => { +export const updateProfileSettingsError = (error) => (dispatch) => { dispatch({ type: UPDATE_PROFILE_SETTINGS_ERROR, payload: { profileSettings: error } }); }; -export const resetProfileSettingsError = dispatch => { +export const resetProfileSettingsError = (dispatch) => { dispatch({ type: RESET_PROFILE_SETTINGS_ERROR, payload: { profileSettings: undefined } }); }; diff --git a/src/Redux/actionCreators/LoaderActions.js b/src/Redux/actionCreators/LoaderActions.js index d20aa1b5b..9aaf9c530 100644 --- a/src/Redux/actionCreators/LoaderActions.js +++ b/src/Redux/actionCreators/LoaderActions.js @@ -3,18 +3,18 @@ export const STOP_APP_LOADER = "STOP_APP_LOADER"; export const START_AISERVICE_LIST_LOADER = "START_AISERVICE_LIST_LOADER"; export const STOP_AISERVICE_LIST_LOADER = "STOP_AISERVICE_LIST_LOADER"; -export const startAppLoader = loaderContent => dispatch => { +export const startAppLoader = (loaderContent) => (dispatch) => { return dispatch({ type: START_APP_LOADER, payload: { app: { loading: true, ...loaderContent } } }); }; -export const stopAppLoader = dispatch => { +export const stopAppLoader = (dispatch) => { return dispatch({ type: STOP_APP_LOADER, payload: { app: { loading: false, loaderHeader: "", loaderText: "" } } }); }; -export const startAIServiceListLoader = dispatch => { +export const startAIServiceListLoader = (dispatch) => { return dispatch({ type: START_AISERVICE_LIST_LOADER }); }; -export const stopAIServiceListLoader = dispatch => { +export const stopAIServiceListLoader = (dispatch) => { return dispatch({ type: STOP_AISERVICE_LIST_LOADER }); }; diff --git a/src/Redux/actionCreators/PaymentActions.js b/src/Redux/actionCreators/PaymentActions.js index d1ddcbd84..53bd5fb22 100644 --- a/src/Redux/actionCreators/PaymentActions.js +++ b/src/Redux/actionCreators/PaymentActions.js @@ -18,7 +18,7 @@ const initiatePaymentAPI = (token, paymentObj) => { return API.post(apiName, apiPath, apiOptions); }; -export const initiatePayment = paymentObj => async dispatch => { +export const initiatePayment = (paymentObj) => async (dispatch) => { try { dispatch(loaderActions.startAppLoader(LoaderContent.INITIATE_PAYPAL)); const { token } = await dispatch(userActions.fetchAuthenticatedUser()); @@ -40,7 +40,7 @@ const executePaymentAPI = (token, paymentExecObj) => { return API.post(apiName, apiPath, apiOptions); }; -export const executePayment = paymentExecObj => async dispatch => { +export const executePayment = (paymentExecObj) => async (dispatch) => { const { token } = await dispatch(userActions.fetchAuthenticatedUser()); return await executePaymentAPI(token, paymentExecObj); }; @@ -52,17 +52,17 @@ const orderDetailsAPI = (token, orderId) => { return API.get(apiName, apiPath, apiOptions); }; -export const fetchOrderDetails = orderId => async dispatch => { +export const fetchOrderDetails = (orderId) => async (dispatch) => { const { token } = await dispatch(userActions.fetchAuthenticatedUser()); return await orderDetailsAPI(token, orderId); }; -export const updatePaypalInProgress = (orderId, orderType, paymentId, paypalPaymentId, PayerID) => dispatch => { +export const updatePaypalInProgress = (orderId, orderType, paymentId, paypalPaymentId, PayerID) => (dispatch) => { dispatch({ type: UPDATE_PAYPAL_IN_PROGRESS, payload: { orderId, orderType, paymentId, paypalPaymentId, PayerID } }); dispatch(userActions.updateWallet({ type: walletTypes.GENERAL })); }; -export const updatePaypalCompleted = dispatch => { +export const updatePaypalCompleted = (dispatch) => { dispatch({ type: UPDATE_PAYPAL_COMPLETED }); }; @@ -73,23 +73,23 @@ const cancelOrderAPI = (token, orderId) => () => { return API.get(apiName, path, apiOptions); }; -export const cancelOrder = orderId => async dispatch => { +export const cancelOrder = (orderId) => async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); await dispatch(cancelOrderAPI(token, orderId)); }; -const fetchUSDConversionRateSuccess = data => async dispatch => { +const fetchUSDConversionRateSuccess = (data) => async (dispatch) => { await dispatch({ type: UPDATE_USD_AGI_RATE, payload: data.amount_in_agi }); return dispatch({ type: UPDATE_USD_COGS_RATE, payload: data.amount_in_cogs }); }; -const USDConversionRateAPI = async amount => { +const USDConversionRateAPI = async (amount) => { const url = new URL(`${APIEndpoints.ORCHESTRATOR.endpoint}${APIPaths.USD_RATE}?amount=${amount}`); const response = await fetch(url); return response.json(); }; -export const fetchUSDConversionRate = async dispatch => { +export const fetchUSDConversionRate = async (dispatch) => { const { data } = await USDConversionRateAPI(1); return dispatch(fetchUSDConversionRateSuccess(data)); }; diff --git a/src/Redux/actionCreators/ServiceActions.js b/src/Redux/actionCreators/ServiceActions.js index e89d008e3..4a646e7de 100644 --- a/src/Redux/actionCreators/ServiceActions.js +++ b/src/Redux/actionCreators/ServiceActions.js @@ -16,15 +16,15 @@ export const UPDATE_ACTIVE_FILTER_ITEM = "UPDATE_ACTIVE_FILTER_ITEM"; export const RESET_FILTER_ITEM = "RESET_FILTER_ITEM"; export const UPDATE_FEEDBACK = "UPDATE_FEEDBACK"; -export const updateActiveFilterItem = activeFilterItem => dispatch => { +export const updateActiveFilterItem = (activeFilterItem) => (dispatch) => { dispatch({ type: UPDATE_ACTIVE_FILTER_ITEM, payload: { ...activeFilterItem } }); }; -export const resetFilterItem = dispatch => { +export const resetFilterItem = (dispatch) => { dispatch({ type: RESET_FILTER_ITEM }); }; -export const fetchServiceSuccess = res => dispatch => { +export const fetchServiceSuccess = (res) => (dispatch) => { dispatch({ type: UPDATE_PAGINATION_DETAILS, payload: { @@ -39,51 +39,57 @@ export const fetchServiceSuccess = res => dispatch => { dispatch(loaderActions.stopAIServiceListLoader); }; -export const fetchService = (pagination, filters = []) => dispatch => { - dispatch(loaderActions.startAIServiceListLoader); - const url = new URL(`${APIEndpoints.CONTRACT.endpoint}/service`); - return fetch(url, { - method: "POST", - body: JSON.stringify({ ...pagination, filters }), - }) - .then(res => res.json()) - .then(res => dispatch(fetchServiceSuccess(res))) - .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); -}; +export const fetchService = + (pagination, filters = []) => + (dispatch) => { + dispatch(loaderActions.startAIServiceListLoader); + const url = new URL(`${APIEndpoints.CONTRACT.endpoint}/service`); + return fetch(url, { + method: "POST", + body: JSON.stringify({ ...pagination, filters }), + }) + .then((res) => res.json()) + .then((res) => dispatch(fetchServiceSuccess(res))) + .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); + }; -export const updatePagination = pagination => dispatch => { +export const updatePagination = (pagination) => (dispatch) => { dispatch({ type: UPDATE_PAGINATION_DETAILS, payload: pagination, }); }; -export const fetchFilterData = attribute => dispatch => { +export const fetchFilterData = (attribute) => (dispatch) => { const url = `${APIEndpoints.CONTRACT.endpoint}${APIPaths.FILTER_DATA}${attribute}`; return fetch(url) - .then(res => res.json()) - .then(res => { + .then((res) => res.json()) + .then((res) => { dispatch({ type: UPDATE_FILTER_DATA, payload: { [attribute]: res.data.values } }); }); }; -export const handleFilterChange = ({ pagination, filterObj, currentActiveFilterData }) => dispatch => { - dispatch(loaderActions.startAIServiceListLoader); - Promise.all([ - dispatch(updatePagination(pagination)), - dispatch(fetchService(pagination, filterObj)), - dispatch(updateActiveFilterItem(currentActiveFilterData)), - ]) - .then(() => dispatch(loaderActions.stopAIServiceListLoader)) - .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); -}; +export const handleFilterChange = + ({ pagination, filterObj, currentActiveFilterData }) => + (dispatch) => { + dispatch(loaderActions.startAIServiceListLoader); + Promise.all([ + dispatch(updatePagination(pagination)), + dispatch(fetchService(pagination, filterObj)), + dispatch(updateActiveFilterItem(currentActiveFilterData)), + ]) + .then(() => dispatch(loaderActions.stopAIServiceListLoader)) + .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); + }; -export const resetFilter = ({ pagination }) => dispatch => { - dispatch(loaderActions.startAIServiceListLoader); - Promise.all([dispatch(updatePagination(pagination)), dispatch(fetchService(pagination)), dispatch(resetFilterItem)]) - .then(() => dispatch(loaderActions.stopAIServiceListLoader)) - .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); -}; +export const resetFilter = + ({ pagination }) => + (dispatch) => { + dispatch(loaderActions.startAIServiceListLoader); + Promise.all([dispatch(updatePagination(pagination)), dispatch(fetchService(pagination)), dispatch(resetFilterItem)]) + .then(() => dispatch(loaderActions.stopAIServiceListLoader)) + .catch(() => dispatch(loaderActions.stopAIServiceListLoader)); + }; const fetchFeedbackAPI = (email, orgId, serviceId, token) => { const apiName = APIEndpoints.USER.name; @@ -106,7 +112,7 @@ const fetchAuthTokenAPI = (serviceId, groupId, publicKey, orgId, userId, token) return API.get(apiName, apiPath, apiOptions); }; -export const downloadAuthToken = (serviceId, groupId, publicKey, orgId) => async dispatch => { +export const downloadAuthToken = (serviceId, groupId, publicKey, orgId) => async (dispatch) => { try { dispatch(loaderActions.startAppLoader(LoaderContent.GENERATE_AUTH_TOKEN)); const { token, email } = await dispatch(userActions.fetchAuthenticatedUser()); @@ -129,7 +135,7 @@ export const downloadAuthToken = (serviceId, groupId, publicKey, orgId) => async }; //Username review -export const fetchFeedback = (orgId, serviceId) => async dispatch => { +export const fetchFeedback = (orgId, serviceId) => async (dispatch) => { const { email, token } = await dispatch(userActions.fetchAuthenticatedUser()); return fetchFeedbackAPI(email, orgId, serviceId, token); }; @@ -141,7 +147,7 @@ const submitFeedbackAPI = (feedbackObj, token) => { return API.post(apiName, path, apiOptions); }; -export const submitFeedback = (orgId, serviceId, feedback) => async dispatch => { +export const submitFeedback = (orgId, serviceId, feedback) => async (dispatch) => { const { token } = await dispatch(userActions.fetchAuthenticatedUser()); const feedbackObj = { feedback: { diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 918dcbbde..dfbce8e86 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -12,15 +12,15 @@ export const RESET_SERVICE_DETAILS = "RESET_SERVICE_DETAILS"; export const UPDATE_FREE_CALLS_INFO = "UPDATE_FREE_CALLS_INFO"; export const UPDATE_TRAINING_DETAILS = "UPDATE_TRAINING_DETAILS"; -const resetServiceDetails = dispatch => { +const resetServiceDetails = (dispatch) => { dispatch({ type: RESET_SERVICE_DETAILS }); }; -const fetchServiceDetailsFailure = err => dispatch => { +const fetchServiceDetailsFailure = (err) => (dispatch) => { dispatch(loaderActions.stopAppLoader); }; -const fetchServiceDetailsSuccess = serviceDetails => dispatch => { +const fetchServiceDetailsSuccess = (serviceDetails) => (dispatch) => { // const enhancedServiceDetails = { // ...serviceDetails, // data: { ...serviceDetails.data, media: serviceDetails.data.media.map(el => ({ ...el, url: cacheS3Url(el.url) })) }, @@ -35,7 +35,7 @@ const fetchServiceDetailsAPI = async (orgId, serviceId) => { return response.json(); }; -export const fetchServiceDetails = (orgId, serviceId) => async dispatch => { +export const fetchServiceDetails = (orgId, serviceId) => async (dispatch) => { try { dispatch(loaderActions.startAppLoader(LoaderContent.FETCH_SERVICE_DETAILS)); dispatch(resetServiceDetails); @@ -47,33 +47,31 @@ export const fetchServiceDetails = (orgId, serviceId) => async dispatch => { } }; -const fetchMeteringDataSuccess = usageData => dispatch => { +const fetchMeteringDataSuccess = (usageData) => (dispatch) => { dispatch({ type: UPDATE_FREE_CALLS_INFO, payload: usageData.total_calls_made, }); }; -const fetchTrainingModelSuccess = serviceTrainingData => dispatch => { +const fetchTrainingModelSuccess = (serviceTrainingData) => (dispatch) => { dispatch({ type: UPDATE_TRAINING_DETAILS, payload: serviceTrainingData }); }; -const fetchServiceTrainingDataAPI = async(orgId, serviceId)=>{ - try{ - const dataForUrl = await fetchServiceDetailsAPI(orgId, serviceId); - const url = `${dataForUrl.data.groups[0].endpoints[0].endpoint}/servicemethoddetails`; - const response = await fetch(url); - return response.json(); - } - catch(error){ +const fetchServiceTrainingDataAPI = async (orgId, serviceId) => { + try { + const dataForUrl = await fetchServiceDetailsAPI(orgId, serviceId); + const url = `${dataForUrl.data.groups[0].endpoints[0].endpoint}/servicemethoddetails`; + const response = await fetch(url); + return response.json(); + } catch (error) { return {}; } }; -export const fetchTrainingModel = (orgId, serviceId) => async dispatch =>{ +export const fetchTrainingModel = (orgId, serviceId) => async (dispatch) => { const serviceTrainingData = await fetchServiceTrainingDataAPI(orgId, serviceId); dispatch(fetchTrainingModelSuccess(serviceTrainingData)); - }; const meteringAPI = (token, orgId, serviceId, groupId, userId) => { @@ -84,8 +82,10 @@ const meteringAPI = (token, orgId, serviceId, groupId, userId) => { return API.get(apiName, apiPath, apiOptions); }; -export const fetchMeteringData = ({ orgId, serviceId, groupId }) => async dispatch => { - const { email, token } = await dispatch(fetchAuthenticatedUser()); - const usageData = await meteringAPI(token, orgId, serviceId, groupId, email); - return dispatch(fetchMeteringDataSuccess(usageData)); -}; +export const fetchMeteringData = + ({ orgId, serviceId, groupId }) => + async (dispatch) => { + const { email, token } = await dispatch(fetchAuthenticatedUser()); + const usageData = await meteringAPI(token, orgId, serviceId, groupId, email); + return dispatch(fetchMeteringDataSuccess(usageData)); + }; diff --git a/src/Redux/actionCreators/StylesActions.js b/src/Redux/actionCreators/StylesActions.js index c521c8d1a..cd0893d67 100644 --- a/src/Redux/actionCreators/StylesActions.js +++ b/src/Redux/actionCreators/StylesActions.js @@ -1,5 +1,5 @@ export const SET_HAMBURGER_MENU_STATE = "SET_HAMBURGER_MENU_STATE"; -export const updateHamburgerState = hamburgerState => dispatch => { +export const updateHamburgerState = (hamburgerState) => (dispatch) => { dispatch({ type: SET_HAMBURGER_MENU_STATE, payload: { hamburgerMenu: hamburgerState } }); }; diff --git a/src/Redux/actionCreators/UiContentActions.js b/src/Redux/actionCreators/UiContentActions.js index a9c6c118e..e66a431b4 100644 --- a/src/Redux/actionCreators/UiContentActions.js +++ b/src/Redux/actionCreators/UiContentActions.js @@ -3,7 +3,7 @@ import { APIEndpoints, APIPaths } from "../../config/APIEndpoints"; export const UPDATE_CAROUSEL = "UPDATE_CAROUSEL"; -export const fetchCarousel = () => async dispatch => { +export const fetchCarousel = () => async (dispatch) => { const response = await fetchCarouselAPI(); dispatch({ type: UPDATE_CAROUSEL, payload: response.data }); }; diff --git a/src/Redux/actionCreators/UserActions.js b/src/Redux/actionCreators/UserActions.js index 9f1b66b27..bd71bfb83 100644 --- a/src/Redux/actionCreators/UserActions.js +++ b/src/Redux/actionCreators/UserActions.js @@ -38,7 +38,7 @@ export const walletTypes = { DEFAULT: "default", }; -const setJWTExp = exp => ({ type: SET_JWT_EXP, payload: exp }); +const setJWTExp = (exp) => ({ type: SET_JWT_EXP, payload: exp }); export const fetchAuthenticatedUser = () => async (dispatch, getState) => { let bypassCache = false; @@ -59,36 +59,36 @@ export const fetchAuthenticatedUser = () => async (dispatch, getState) => { }; }; -export const appInitializationSuccess = dispatch => { +export const appInitializationSuccess = (dispatch) => { dispatch({ type: APP_INITIALIZATION_SUCCESS, payload: { isInitialized: true } }); dispatch(loaderActions.stopAppLoader); }; -export const updateNickname = nickname => dispatch => { +export const updateNickname = (nickname) => (dispatch) => { dispatch({ type: UPDATE_NICKNAME, payload: { nickname } }); }; -export const updateEmail = email => dispatch => { +export const updateEmail = (email) => (dispatch) => { dispatch({ type: UPDATE_EMAIL, payload: { email } }); }; -export const updateEmailVerified = value => dispatch => { +export const updateEmailVerified = (value) => (dispatch) => { dispatch({ type: UPDATE_EMAIL_VERIFIED, payload: { isEmailVerified: value } }); }; -const updateEmailAlertsSubscription = emailAlerts => dispatch => { +const updateEmailAlertsSubscription = (emailAlerts) => (dispatch) => { dispatch({ type: UPDATE_EMAIL_ALERTS_SUBSCRIPTION, payload: emailAlerts }); }; -const updateIsTermsAccepted = isTermsAccepted => dispatch => { +const updateIsTermsAccepted = (isTermsAccepted) => (dispatch) => { dispatch({ type: UPDATE_IS_TERMS_ACCEPTED, payload: isTermsAccepted }); }; -const fetchUserProfile = token => dispatch => { +const fetchUserProfile = (token) => (dispatch) => { const apiName = APIEndpoints.USER.name; const path = APIPaths.GET_USER_PROFILE; const apiOptions = initializeAPIOptions(token); - return API.get(apiName, path, apiOptions).then(res => { + return API.get(apiName, path, apiOptions).then((res) => { if (res.data.data.length === 0) { dispatch(registerInMarketplace(token)); return; @@ -98,14 +98,14 @@ const fetchUserProfile = token => dispatch => { }); }; -const fetchUserTransactionsAPI = token => { +const fetchUserTransactionsAPI = (token) => { const apiName = APIEndpoints.ORCHESTRATOR.name; const path = APIPaths.ORDERS_LIST; const apiOptions = initializeAPIOptions(token); return API.get(apiName, path, apiOptions); }; -export const fetchUserTransactions = async dispatch => { +export const fetchUserTransactions = async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); dispatch(loaderActions.startAppLoader(LoaderContent.TRANSACTION_HISTORY)); const response = await fetchUserTransactionsAPI(token); @@ -113,8 +113,8 @@ export const fetchUserTransactions = async dispatch => { dispatch(loaderActions.stopAppLoader); }; -const fetchUserTransactionsSuccess = response => dispatch => { - const transactionHistory = response.data.orders.map(value => { +const fetchUserTransactionsSuccess = (response) => (dispatch) => { + const transactionHistory = response.data.orders.map((value) => { const timestamp = moment(value.created_at); return { date: timestamp.format("DD MMM YYYY"), @@ -132,11 +132,11 @@ const fetchUserTransactionsSuccess = response => dispatch => { dispatch(updateTransactionHistory(transactionHistory)); }; -export const updateTransactionHistory = transactionHistory => dispatch => { +export const updateTransactionHistory = (transactionHistory) => (dispatch) => { dispatch({ type: UPDATE_TRANSACTION_HISTORY, payload: transactionHistory }); }; -const noAuthenticatedUser = dispatch => { +const noAuthenticatedUser = (dispatch) => { dispatch({ type: SET_USER_DETAILS, payload: { @@ -146,7 +146,7 @@ const noAuthenticatedUser = dispatch => { }); }; -const fetchUserDetailsSuccess = (isEmailVerified, email, nickname) => dispatch => { +const fetchUserDetailsSuccess = (isEmailVerified, email, nickname) => (dispatch) => { dispatch({ type: SET_USER_DETAILS, payload: { @@ -160,7 +160,7 @@ const fetchUserDetailsSuccess = (isEmailVerified, email, nickname) => dispatch = dispatch(loaderActions.stopAppLoader); }; -const fetchUserDetailsError = err => dispatch => { +const fetchUserDetailsError = (err) => (dispatch) => { if (err === "No current user") { dispatch(noAuthenticatedUser); dispatch(loaderActions.stopAppLoader); @@ -168,7 +168,7 @@ const fetchUserDetailsError = err => dispatch => { dispatch(appInitializationSuccess); }; -export const fetchUserDetails = async dispatch => { +export const fetchUserDetails = async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.APP_INIT)); try { const { nickname, token, email, email_verified } = await dispatch(fetchAuthenticatedUser()); @@ -192,17 +192,17 @@ export const updateUserProfileInit = (token, updatedUserData) => { return API.post(apiName, path, apiOptions); }; -const updateUserProfileSuccess = token => dispatch => { +const updateUserProfileSuccess = (token) => (dispatch) => { dispatch(fetchUserProfile(token)); dispatch(loaderActions.stopAppLoader); }; -const updateUserProfileFailure = err => dispatch => { +const updateUserProfileFailure = (err) => (dispatch) => { dispatch(errorActions.updateProfileSettingsError(String(err))); dispatch(loaderActions.stopAppLoader); }; -export const updateUserProfile = updatedUserData => async dispatch => { +export const updateUserProfile = (updatedUserData) => async (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.UPDATE_PROFILE)); try { const { token } = await dispatch(fetchAuthenticatedUser()); @@ -216,80 +216,84 @@ export const updateUserProfile = updatedUserData => async dispatch => { } }; -export const resetLoginError = dispatch => { +export const resetLoginError = (dispatch) => { dispatch({ type: RESET_LOGIN_ERROR }); }; -export const updateLoginError = error => dispatch => { +export const updateLoginError = (error) => (dispatch) => { dispatch({ type: UPDATE_LOGIN_ERROR, payload: error }); }; -export const loginSuccess = ({ res, history, route }) => async dispatch => { - const userDetails = { - type: userActions.LOGIN_SUCCESS, - payload: { - login: { isLoggedIn: true }, - email: res.attributes.email, - nickname: res.attributes.nickname, - isEmailVerified: res.attributes.email_verified, - }, +export const loginSuccess = + ({ res, history, route }) => + async (dispatch) => { + const userDetails = { + type: userActions.LOGIN_SUCCESS, + payload: { + login: { isLoggedIn: true }, + email: res.attributes.email, + nickname: res.attributes.nickname, + isEmailVerified: res.attributes.email_verified, + }, + }; + dispatch(userDetails); + history.push(route); + await dispatch(fetchUserProfile(res.signInUserSession.idToken.jwtToken)); + dispatch(loaderActions.stopAppLoader); }; - dispatch(userDetails); - history.push(route); - await dispatch(fetchUserProfile(res.signInUserSession.idToken.jwtToken)); - dispatch(loaderActions.stopAppLoader); -}; -export const login = ({ email, password, history, route }) => dispatch => { - dispatch(loaderActions.startAppLoader(LoaderContent.LOGIN)); - let userDetails = {}; - return Auth.signIn(email, password) - .then(res => { - dispatch(loginSuccess({ res, history, route })); - }) - .catch(err => { - if (err.code === "PasswordResetRequiredException") { - dispatch(updateEmail(email)); - history.push(`/${Routes.RESET_PASSWORD}`); - dispatch(loaderActions.stopAppLoader); - return; - } else if (err.code === "UserNotConfirmedException") { - dispatch(updateEmail(email)); +export const login = + ({ email, password, history, route }) => + (dispatch) => { + dispatch(loaderActions.startAppLoader(LoaderContent.LOGIN)); + let userDetails = {}; + return Auth.signIn(email, password) + .then((res) => { + dispatch(loginSuccess({ res, history, route })); + }) + .catch((err) => { + if (err.code === "PasswordResetRequiredException") { + dispatch(updateEmail(email)); + history.push(`/${Routes.RESET_PASSWORD}`); + dispatch(loaderActions.stopAppLoader); + return; + } else if (err.code === "UserNotConfirmedException") { + dispatch(updateEmail(email)); + userDetails = { + type: userActions.LOGIN_SUCCESS, + payload: { login: { isLoggedIn: true } }, + }; + dispatch(userDetails); + history.push(`/${Routes.ONBOARDING}`); + dispatch(loaderActions.stopAppLoader); + return; + } + const error = parseError(err); userDetails = { - type: userActions.LOGIN_SUCCESS, - payload: { login: { isLoggedIn: true } }, + type: userActions.LOGIN_ERROR, + payload: { login: { error } }, }; dispatch(userDetails); - history.push(`/${Routes.ONBOARDING}`); dispatch(loaderActions.stopAppLoader); - return; - } - const error = parseError(err); - userDetails = { - type: userActions.LOGIN_ERROR, - payload: { login: { error } }, - }; - dispatch(userDetails); - dispatch(loaderActions.stopAppLoader); - throw err; - }); -}; + throw err; + }); + }; -const registrationAPI = token => { +const registrationAPI = (token) => { const apiName = APIEndpoints.USER.name; const apiPath = APIPaths.SIGNUP; const apiOptions = initializeAPIOptions(token); return API.get(apiName, apiPath, apiOptions); }; -const registerInMarketplace = token => async dispatch => { +const registerInMarketplace = (token) => async (dispatch) => { const response = await registrationAPI(token); if (response.data === "success") { dispatch(fetchUserProfile(token)); } }; -export const signOut = dispatch => { +export const signOut = (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.SIGN_OUT)); let userDetails = { type: SIGN_OUT, @@ -315,22 +319,24 @@ export const signOut = dispatch => { }); }; -const userDeleted = ({ history, route }) => dispatch => { - dispatch({ - type: SET_USER_DETAILS, - payload: { - login: { - isLoggedIn: false, +const userDeleted = + ({ history, route }) => + (dispatch) => { + dispatch({ + type: SET_USER_DETAILS, + payload: { + login: { + isLoggedIn: false, + }, + isEmailVerified: false, + walletAddress: undefined, + email: "", }, - isEmailVerified: false, - walletAddress: undefined, - email: "", - }, - }); - history.push(route); -}; + }); + history.push(route); + }; -const deleteUserFromMarketPlace = token => { +const deleteUserFromMarketPlace = (token) => { const apiName = APIEndpoints.USER.name; const path = APIPaths.DELETE_USER; const apiOptions = { @@ -339,95 +345,107 @@ const deleteUserFromMarketPlace = token => { return API.get(apiName, path, apiOptions); }; -const deleteUserFromCognito = (user, { history, route }) => dispatch => { - new Promise((resolve, reject) => { - user.deleteUser(error => { - if (error) { - reject(error); - dispatch(loaderActions.stopAppLoader); - } - resolve(); +const deleteUserFromCognito = + (user, { history, route }) => + (dispatch) => { + new Promise((resolve, reject) => { + user.deleteUser((error) => { + if (error) { + reject(error); + dispatch(loaderActions.stopAppLoader); + } + resolve(); + }); + }).then(() => { + dispatch(userDeleted({ history, route })); + dispatch(loaderActions.stopAppLoader); }); - }).then(() => { - dispatch(userDeleted({ history, route })); - dispatch(loaderActions.stopAppLoader); - }); -}; + }; -export const deleteUserAccount = ({ history, route }) => async dispatch => { - dispatch(loaderActions.startAppLoader(LoaderContent.DELETE_USER)); - const currentUser = await Auth.currentAuthenticatedUser({ bypassCache: true }); - await deleteUserFromMarketPlace(currentUser.signInUserSession.idToken.jwtToken); - dispatch(deleteUserFromCognito(currentUser, { history, route })); -}; +export const deleteUserAccount = + ({ history, route }) => + async (dispatch) => { + dispatch(loaderActions.startAppLoader(LoaderContent.DELETE_USER)); + const currentUser = await Auth.currentAuthenticatedUser({ bypassCache: true }); + await deleteUserFromMarketPlace(currentUser.signInUserSession.idToken.jwtToken); + dispatch(deleteUserFromCognito(currentUser, { history, route })); + }; -const forgotPasswordInit = dispatch => { +const forgotPasswordInit = (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.FORGOT_PASSWORD)); dispatch(errorActions.resetForgotPasswordError); }; -const forgotPasswordSuccessfull = ({ email, history, route }) => dispatch => { - dispatch(updateEmail(email)); - history.push(route); - dispatch(loaderActions.stopAppLoader); -}; +const forgotPasswordSuccessfull = + ({ email, history, route }) => + (dispatch) => { + dispatch(updateEmail(email)); + history.push(route); + dispatch(loaderActions.stopAppLoader); + }; -const forgotPasswordFailure = error => dispatch => { +const forgotPasswordFailure = (error) => (dispatch) => { dispatch(errorActions.updateForgotPasswordError(error)); dispatch(loaderActions.stopAppLoader); }; -export const forgotPassword = ({ email, history, route }) => dispatch => { - dispatch(forgotPasswordInit); - Auth.forgotPassword(email) - .then(() => { - dispatch(forgotPasswordSuccessfull({ email, history, route })); - }) - .catch(err => { - dispatch(forgotPasswordFailure(err.message)); - }); -}; +export const forgotPassword = + ({ email, history, route }) => + (dispatch) => { + dispatch(forgotPasswordInit); + Auth.forgotPassword(email) + .then(() => { + dispatch(forgotPasswordSuccessfull({ email, history, route })); + }) + .catch((err) => { + dispatch(forgotPasswordFailure(err.message)); + }); + }; -const forgotPasswordSubmitInit = dispatch => { +const forgotPasswordSubmitInit = (dispatch) => { dispatch(loaderActions.startAppLoader(LoaderContent.FORGOT_PASSWORD_SUBMIT)); dispatch(errorActions.resetForgotPasswordSubmitError); }; -const forgotPasswordSubmitSuccessfull = ({ email, history, route }) => dispatch => { - dispatch(updateEmail(email)); - dispatch(loaderActions.stopAppLoader); - history.push(route); -}; +const forgotPasswordSubmitSuccessfull = + ({ email, history, route }) => + (dispatch) => { + dispatch(updateEmail(email)); + dispatch(loaderActions.stopAppLoader); + history.push(route); + }; -const forgotPasswordSubmitFailure = error => dispatch => { +const forgotPasswordSubmitFailure = (error) => (dispatch) => { dispatch(errorActions.updateForgotPasswordSubmitError(error)); dispatch(loaderActions.stopAppLoader); }; -export const forgotPasswordSubmit = ({ email, code, password, history, route }) => dispatch => { - dispatch(forgotPasswordSubmitInit); - Auth.forgotPasswordSubmit(email, code, password) - .then(() => { - dispatch(forgotPasswordSubmitSuccessfull({ email, history, route })); - }) - .catch(err => { - dispatch(forgotPasswordSubmitFailure(err.message)); - }); -}; +export const forgotPasswordSubmit = + ({ email, code, password, history, route }) => + (dispatch) => { + dispatch(forgotPasswordSubmitInit); + Auth.forgotPasswordSubmit(email, code, password) + .then(() => { + dispatch(forgotPasswordSubmitSuccessfull({ email, history, route })); + }) + .catch((err) => { + dispatch(forgotPasswordSubmitFailure(err.message)); + }); + }; -export const updateWallet = walletDetails => dispatch => { +export const updateWallet = (walletDetails) => (dispatch) => { dispatch({ type: UPDATE_WALLET, payload: { ...walletDetails } }); }; -export const updateWalletList = walletList => dispatch => { +export const updateWalletList = (walletList) => (dispatch) => { dispatch({ type: UPDATE_WALLET_LIST, payload: walletList }); }; -export const updateFirstTimeFetchWallet = value => dispatch => { +export const updateFirstTimeFetchWallet = (value) => (dispatch) => { dispatch({ type: UPDATE_FIRST_TIME_FETCH_WALLET, payload: value }); }; -const fetchWalletSuccess = response => dispatch => { +const fetchWalletSuccess = (response) => (dispatch) => { if (!isEmpty(response.data.wallets)) { dispatch(updateWalletList(response.data.wallets)); } @@ -444,32 +462,32 @@ const fetchWalletAPI = (token, orgId, groupId) => { return API.get(apiName, apiPath, apiOptions); }; -export const fetchWallet = (orgId, groupId) => async dispatch => { +export const fetchWallet = (orgId, groupId) => async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); const response = await fetchWalletAPI(token, orgId, groupId); return dispatch(fetchWalletSuccess(response)); }; -const fetchAvailableUserWalletsAPI = token => { +const fetchAvailableUserWalletsAPI = (token) => { const apiName = APIEndpoints.ORCHESTRATOR.name; const apiPath = APIPaths.WALLETS; const apiOptions = initializeAPIOptions(token); return API.get(apiName, apiPath, apiOptions); }; -export const fetchAvailableUserWallets = () => async dispatch => { +export const fetchAvailableUserWallets = () => async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); const response = await fetchAvailableUserWalletsAPI(token); return response.data.wallets; }; -export const fetchWalletLinkedProviders = async address => { +export const fetchWalletLinkedProviders = async (address) => { const url = `${APIEndpoints.CONTRACT.endpoint}${APIPaths.LINKED_PROVIDERS}?wallet_address=${address}`; - const response = await fetch(url).then(res => res.json()); + const response = await fetch(url).then((res) => res.json()); return response.organizations || []; }; -export const startWalletDetailsPolling = (orgId, groupId) => dispatch => { +export const startWalletDetailsPolling = (orgId, groupId) => (dispatch) => { if (!walletPollingInterval) { walletPollingInterval = setInterval(() => dispatch(fetchWallet(orgId, groupId)), 15000); return dispatch(fetchWallet(orgId, groupId)); @@ -491,12 +509,12 @@ const updateDefaultWalletAPI = (token, address) => { return API.post(apiName, apiPath, apiOptions); }; -const updateDefaultWallet = address => async dispatch => { +const updateDefaultWallet = (address) => async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); return await updateDefaultWalletAPI(token, address); }; -const registerWalletSuccess = address => dispatch => { +const registerWalletSuccess = (address) => (dispatch) => { return dispatch(updateDefaultWallet(address)); }; @@ -508,7 +526,7 @@ const registerWalletAPI = (token, address, type) => { return API.post(apiName, apiPath, apiOptions); }; -export const registerWallet = (address, type) => async dispatch => { +export const registerWallet = (address, type) => async (dispatch) => { const { token } = await dispatch(fetchAuthenticatedUser()); await registerWalletAPI(token, address, type); return dispatch(registerWalletSuccess(address)); diff --git a/src/Redux/reducers/PaymentReducer.js b/src/Redux/reducers/PaymentReducer.js index ff84fa00c..3e1f56dba 100644 --- a/src/Redux/reducers/PaymentReducer.js +++ b/src/Redux/reducers/PaymentReducer.js @@ -27,29 +27,29 @@ const paymentReducer = (state = InitialPaymentDetails, action) => { } }; -export const anyPendingTxn = state => { +export const anyPendingTxn = (state) => { if (process.env.REACT_APP_SANDBOX) { return false; } const { walletList } = state.userReducer; const istransactionsPending = walletList.some( - wallet => wallet.transactions && wallet.transactions.some(txn => txn.status === "PENDING") + (wallet) => wallet.transactions && wallet.transactions.some((txn) => txn.status === "PENDING") ); return istransactionsPending; }; -export const anyFailedTxn = state => { +export const anyFailedTxn = (state) => { if (process.env.REACT_APP_SANDBOX) { return false; } const { walletList } = state.userReducer; const istransactionsFailed = walletList.some( - wallet => wallet.transactions && wallet.transactions.some(txn => txn.status === "FAILED") + (wallet) => wallet.transactions && wallet.transactions.some((txn) => txn.status === "FAILED") ); return istransactionsFailed; }; -export const USDToAgi = state => usd => { +export const USDToAgi = (state) => (usd) => { const { usd_agi_rate, agi_divisibility } = state.paymentReducer; if (!usd_agi_rate) { return undefined; @@ -57,7 +57,7 @@ export const USDToAgi = state => usd => { return (usd * usd_agi_rate).toFixed(agi_divisibility); }; -export const USDToCogs = state => usd => { +export const USDToCogs = (state) => (usd) => { const { usd_cogs_rate } = state.paymentReducer; if (!usd_cogs_rate) { return undefined; diff --git a/src/Redux/reducers/ServiceDetailsReducer.js b/src/Redux/reducers/ServiceDetailsReducer.js index e291dae46..6438555db 100644 --- a/src/Redux/reducers/ServiceDetailsReducer.js +++ b/src/Redux/reducers/ServiceDetailsReducer.js @@ -30,7 +30,7 @@ const serviceDetailsReducer = (state = InitialServiceDetails, action) => { } }; -export const freeCalls = state => { +export const freeCalls = (state) => { const selectedGroup = groupInfo(state); if (!selectedGroup) { return {}; @@ -44,7 +44,7 @@ export const freeCalls = state => { }; }; -export const currentServiceDetails = state => { +export const currentServiceDetails = (state) => { return state.serviceDetailsReducer.details; }; @@ -57,16 +57,16 @@ export const serviceDetails = (state, orgId, serviceId) => { return currentServiceDetails(state); }; -const groups = state => { +const groups = (state) => { return state.serviceDetailsReducer.details.groups; }; -const enhanceGroup = group => ({ ...group, endpoints: map(group.endpoints, ({ endpoint }) => endpoint) }); +const enhanceGroup = (group) => ({ ...group, endpoints: map(group.endpoints, ({ endpoint }) => endpoint) }); -export const groupInfo = state => { +export const groupInfo = (state) => { const serviceGroups = groups(state); const availableGroup = find(serviceGroups, ({ endpoints }) => - some(endpoints, endpoint => endpoint.is_available === 1) + some(endpoints, (endpoint) => endpoint.is_available === 1) ); if (availableGroup) { return enhanceGroup(availableGroup); @@ -77,11 +77,11 @@ export const groupInfo = state => { } }; -export const pricing = state => { +export const pricing = (state) => { const group = groupInfo(state); if (!group) return {}; - return find(group.pricing, price => price.default === true); + return find(group.pricing, (price) => price.default === true); }; export default serviceDetailsReducer; diff --git a/src/Redux/reducers/UserReducer.js b/src/Redux/reducers/UserReducer.js index a28a8cf96..7bb4e2a28 100644 --- a/src/Redux/reducers/UserReducer.js +++ b/src/Redux/reducers/UserReducer.js @@ -126,14 +126,14 @@ const userReducer = (state = InitialUserDetails, action) => { } }; -export const channelInfo = state => { +export const channelInfo = (state) => { const { walletList } = state.userReducer; if (isEmpty(walletList)) { return {}; } const walletWithChannel = walletList.find( - wallet => wallet.type === walletTypes.GENERAL && !isEmpty(wallet.channels[0]) + (wallet) => wallet.type === walletTypes.GENERAL && !isEmpty(wallet.channels[0]) ); if (walletWithChannel) { const selectedChannel = walletWithChannel.channels[0]; @@ -145,9 +145,9 @@ export const channelInfo = state => { return {}; }; -export const anyGeneralWallet = state => { +export const anyGeneralWallet = (state) => { const { walletList } = state.userReducer; - return walletList.some(wallet => wallet.type === walletTypes.GENERAL); + return walletList.some((wallet) => wallet.type === walletTypes.GENERAL); }; export default userReducer; diff --git a/src/assets/Theme.js b/src/assets/Theme.js index 19f9974b6..247e9a820 100644 --- a/src/assets/Theme.js +++ b/src/assets/Theme.js @@ -1,4 +1,4 @@ -import { createMuiTheme } from "@material-ui/core/styles"; +import { createTheme } from "@material-ui/core/styles"; const customBlue = "#4086ff"; const customHoverBlue = "#005ACB"; @@ -79,7 +79,7 @@ const infoBg = "#DEEAFF"; const warningBg = "#FDF3E5"; const warningBorder = "#F18D5A"; -const theme = createMuiTheme({ +const theme = createTheme({ palette: { text: { primary: customBlue, diff --git a/src/assets/externalScripts/gdpr.js b/src/assets/externalScripts/gdpr.js index fc59cd8bb..ffeea9a74 100644 --- a/src/assets/externalScripts/gdpr.js +++ b/src/assets/externalScripts/gdpr.js @@ -29,7 +29,7 @@ export default function(){ var msgIsString = typeof event.data === "string"; var json; if(msgIsString) { - json = event.data.indexOf("__cmpCall") != -1 ? JSON.parse(event.data) : {}; + json = event.data.indexOf("__cmpCall") !==-1 ? JSON.parse(event.data) : {}; } else { json = event.data; } @@ -54,7 +54,7 @@ export default function(){ else if (b[0] === 'ping') { b[2]({"gdprAppliesGlobally": gdprAppliesGlobally, "cmpLoaded": false}, true); - } else if (c == '__cmp') + } else if (c==='__cmp') return false; else { if (typeof __cmp.a === 'undefined') { diff --git a/src/assets/thirdPartyServices/common/FileUploader.js b/src/assets/thirdPartyServices/common/FileUploader.js index bb277ccb7..6acf5bba2 100644 --- a/src/assets/thirdPartyServices/common/FileUploader.js +++ b/src/assets/thirdPartyServices/common/FileUploader.js @@ -21,7 +21,7 @@ export default class FileUploader extends React.Component { const props = { name: "uploadedFiles", multiple: this.props.multiple ? true : false, - onDrop: files => { + onDrop: (files) => { this.props.handleFileUpload(files); return false; }, diff --git a/src/assets/thirdPartyServices/common/MethodNamesDropDown.js b/src/assets/thirdPartyServices/common/MethodNamesDropDown.js index 0b83f6cb8..f784fa9a1 100644 --- a/src/assets/thirdPartyServices/common/MethodNamesDropDown.js +++ b/src/assets/thirdPartyServices/common/MethodNamesDropDown.js @@ -9,7 +9,7 @@ const MethodNamesDropDown = ({ list, value, onChange, ...restProps }) => { onChange={onChange} {...restProps} > - {list.map(item => ( + {list.map((item) => ( ))} diff --git a/src/assets/thirdPartyServices/common/OutlinedDropdown.js b/src/assets/thirdPartyServices/common/OutlinedDropdown.js index 3ea368466..406187ae6 100644 --- a/src/assets/thirdPartyServices/common/OutlinedDropdown.js +++ b/src/assets/thirdPartyServices/common/OutlinedDropdown.js @@ -48,7 +48,7 @@ class OutlinedDropDown extends React.Component { variant="outlined" > {list ? ( - list.map(option => { + list.map((option) => { return ( {option.label} diff --git a/src/assets/thirdPartyServices/common/OutlinedLabel.js b/src/assets/thirdPartyServices/common/OutlinedLabel.js index 0018c273e..74cb5289a 100644 --- a/src/assets/thirdPartyServices/common/OutlinedLabel.js +++ b/src/assets/thirdPartyServices/common/OutlinedLabel.js @@ -12,7 +12,8 @@ class OutlinedLabel extends React.Component { const { infoTitle, value, htmlValue, variant, htmlTooltip } = this.props; return ( -