diff --git a/README.md b/README.md
index 4bef680473..db8212f900 100644
--- a/README.md
+++ b/README.md
@@ -4,117 +4,148 @@
[](https://goreportcard.com/report/github.com/hyperledger/firefly)
[](https://hyperledger.github.io/firefly//)
-Hyperledger FireFly is a multiparty system for enterprise data flows, powered by blockchain. It solves all of the layers of complexity that sit between the low level blockchain and high level business processes and user interfaces. FireFly enables developers to build blockchain apps for enterprise radically faster by allowing them to focus on business logic instead of infrastructure.
+
-FireFly focusses on:
+Hyperledger FireFly is an API and data orchestration layer on top of core blockchain technologies.
-- Providing a great developer API and experience, with a CLI and UI as first class project components
-- Pluggability for implementations of multi-party system infrastructure (blockchains, off-chain data exchange, identity, compute etc.)
-- Making proven multi-party system patterns easy for new projects to adopt
-- Providing developer friendly access to custom transactions+events in the underlying blockchain platforms
-- Giving visibility and control on the private data exchange that occurs between businesses in a multi-party system
-- Simplifying the journey of building multi-party business processes, by empowering non-blockchain developers to build great APIs+UX
+It implements a [multi-party system](#multi-party-systems) for building enterprise decentralized applications.
-You will see enterprise focussed code in FireFly solving hard "plumbing" problems like on-chain/off-chain event sequencing and aggregation, and enough smart contract code to make the patterns possible. You will then find patterns of integration with the individual communities that are already building the deep blockchain & multi-party compute tech, like Hyperledger Fabric, Hyperledger Besu, Quorum, Corda, IPFS, Hyperledger Avalon, OpenZeppelin, NodeRED etc.
+- Transaction submission and event streaming
+ - Radically simplified API access to your on-chain smart contracts
+- Multi-protocol blockchain integration
+ - [Hyperledger Fabric](https://www.hyperledger.org/use/fabric)
+ - Enterprise Ethereum - [Hyperledger Besu](https://www.hyperledger.org/use/besu) & [Quorum](https://github.com/ConsenSys/quorum)
+ - [Corda](https://www.corda.net/) *(work in progress)*
+- Developer friendly event-driven REST & WebSocket APIs
+ - For building multi-party business applications that solve real enterprise use cases
+- Digital assets
+ - Tokens and NFTs ready for use, with indexed transaction history, and easy extension/customization
+- On-chain/off-chain orchestration
+ - Enterprise data flows backed by blockchain, with secure off-chain transfer of private docs+data
+ - Pluggable private data exchange / messaging (inc. [HTTPS + Mutual TLS](https://github.com/hyperledger/firefly-dataexchange-https))
+- Identity, data format, and interface distribution
+ - Broadcast data schema, proven identity, and on-chain logic integration APIs across the network
+ - Pluggable data distribution network with batch optimization (inc. [IPFS](https://ipfs.io/))
+ - *Pluggable DIDs for identity and multi-protocol on-chain interface definition are work in progress*
+- Microservice architecture, optimized for docker deployment
+ - Fully pluggable architecture, embracing multiple runtime technologies (Go, Node.js, Java etc.)
+- Built by developers for developers
+ - Ready to go in minutes, with a CLI, built-in UI explorer, OpenAPI spec, and samples
+- Data operations at the boundary of your data center
+ - Fast database cache + audit of all data flowing out of your enterprise, to the network
-> Watch this space for patterns on integrating Tokens into the model (fungible token value exchange, and NFTs), which is a big current focus of evolution in the gen2 FireFly architecture (building on the work done in gen1, also in this repo). The tokens working group is being lead by [Jim Zhang](https://github.com/jimthematrix)
+## Quick Start Guide
-
+Follow the [get started](https://hyperledger.github.io/firefly/gettingstarted/gettingstarted.html) guide in the doc, and your
+local developer environment will be up in minutes.
+
+You'll have your own private multi-party system, comprising a blockchain (Ethereum/Fabric) with API/Event connectors, a Private Data Exchange, an IPFS data sharing network, and ERC-1155 Token/NFT implementations.
+
+All with the Hyperledger FireFly Explorer UI of course, and a [samples to get you building fast](https://github.com/hyperledger/firefly-samples).
+
+
+
+## API Reference
+
+All the Hyperledger FireFly APIs are self-documenting via Swagger, and you can just open them up on `/api` on your running FireFly.
+
+Or you can check out the [latest API here](https://hyperledger.github.io/firefly/swagger/swagger.html).
## Documentation
-https://hyperledger.github.io/firefly//
+https://hyperledger.github.io/firefly
-## FireFly repos
+## Multi-party Systems
-FireFly has a plugin based architecture design, with a microservice runtime footprint.
-As such there are a number of repos, and the list will grow as the community evolves.
+Hyperledger Firefly is an implementation of a multi-party system.
-But not to worry, one of those repos is a CLI designed to get you running with all the components you need in minutes!
+
-- CLI / Developer experience - https://github.com/hyperledger/firefly-cli
-- UI Explorer - https://github.com/hyperledger/firefly-ui
-- Sample applications - https://github.com/hyperledger/firefly-samples
-- Core (this repo) - https://github.com/hyperledger/firefly
-- HTTP Data Exchange - https://github.com/hyperledger/firefly-dataexchange-https
-- Ethereum (Hyperledger Besu / Quorum) connector: https://github.com/hyperledger/firefly-ethconnect
-- Corda connector: https://github.com/hyperledger/firefly-cordaconnect - contributed from Kaleido generation 1 - porting to generation 2
-- Hyperledger Fabric connector - in design phase, including collaboration with https://github.com/hyperledger/fabric-smart-client
+Multi-party systems have the potential to unlock the next wave of digitization in core transaction processing systems. They combine the best features of the existing secure data exchange models for API/WebService/Messaging integration of business data/processes today, with the new technologies of the blockchain revolution.
-> Note only the projects that are primarily built to support FireFly are listed here, not all
-> of the ecosystem of projects that integrate underneath the plugins. See [below](#firefly-code-hierarchy) for
-> more information on the landscape of plugins and components.
+Working within existing regulatory environments, and existing IT and data security governance frameworks, multi-party systems provide a secure gateway for organizations to participate securely in blockchain backed business ecosystems.
-## Getting Started
+They are the middleware tier for decentralized applications, which are fundamentally different to centralized/SaaS applications because they are hosted independently by each IT organization, and can be customized by each organization to their own IT landscape. These applications communicate through a mix of blockchain, and private data exchange, to execute multi-party transactions at scale - powered by revolutionary new programming constructs like digital assets.
-Use the FireFly CLI for fast bootstrap: https://github.com/hyperledger/firefly-cli
+The next wave of business applications that build in a decentralized way on multi-party systems, can orchestrate data and business process flows across organizational boundaries. The integrity of the end-to-end transactions can be established throughout its lifecycle, rather than requiring complex handoff and compensation logic each time a new party performs its step. Yet the autonomy of each business and IT team is maintained in a way that could not be by pooling data and business logic in a 3rd party centralized business application.
-## Navigating this repo
+Blockchain and other advanced cryptography technologies like zero-knowledge proofs (ZKPs), and trusted execution environments (TEEs), are the core technologies that enable this new model of cross-organizational data flow.
-There are **two core codebases** currently active in this repo:
+In an enterprise context these raw technologies are necessary, but not sufficient. Organizations need a comprehensive toolset at the boundary of their *existing core systems of record* to govern the flow of data out of their own secure IT infrastructure.
+- To store the private data staged in canonical formats ready for exchange with other parties
+- For retrieval of the state of transaction and data flows in-flight in the system
+- To provide an audit record and reporting system for what has been shared so far
+- Providing event-driven integration APIs fit for purpose to integrate to the core systems of record
-### Generation 2: FireFly
+This all needs to be fast, secure and reliable.
-Directories:
+[Learn more in the Hyperledger FireFly Documentation](https://hyperledger.github.io/firefly/)
-- [internal](./internal): The core Golang implementation code
-- [pkg](./pkg): Interfaces intended for external project use
-- [cmd](./cmd): The command line entry point
-- [smart_contracts](./smart_contracts): smart contract code for Firefly's onchain logic, with support for Ethereum and Hyperledger Fabric in their respective sub-directories
+## Event-driven programming model
-[Full code layout here](#firefly-code-hierarchy)
+The core programming model of FireFly is event-driven:
+- FireFly delivers data and actions from your application instance, reliably to on-chain logic and privately to other parties in the network
+- FireFly receives data and actions from on-chain, and other parties in the network, correlates them, and once complete and verified delivers them to your application for processing
-This latest generation is re-engineered from the ground up to improve developer experience, runtime performance, and extensibility.
-
-This means a simplified REST/WebSocket programming model for app development, and a wider range of infrastructure options for deployment.
-
-It also means a focus on an architecture and code structure for a vibrant open source community.
-
-A few highlights:
-
-- Golang codebase
- - Strong coding standards, including unit test coverage, translation support, logging and more
- - Fast starting, low memory footprint, multi-threaded runtime
-- OpenAPI 3.0 API specification (Swagger)
- - Generated from the API router code, to avoid divergence with the implementation
-- Active/active HA architecture for the core runtime
- - Deferring to the core database for state high availability
- - Exploiting leader election where required
-- Fully pluggable architecture
- - Everything from Database through to Blockchain, and Compute
- - Golang plugin infrastructure to decouple the core code from the implementation
- - Remote Agent model to decouple code languages, and HA designs
-- Updated API resource model
- - `Asset`, `Data`, `Message`, `Event`, `Topic`, `Transaction`
-- Added flexibility, with simplified the developer experience:
- - Versioning of data definitions
- - Introducing a first class `Context` construct link related events into a single sequence
- - Allow many pieces of data to be attached to a single message, and be automatically re-assembled on arrival
- - Clearer separation of concerns between the FireFly DB and the Application DB
- - Better search, filter and query support
-
-### Generation 1: Kaleido Asset Trail (KAT)
+For this reason FireFly has a pluggable database that keeps track of all those interactions.
-Directories:
+This database is *not intended to replace* your application database (apart from in early PoC scenarios). Instead it complements it.
+
+You process the events from the network as they happen, **including ones you submit** because they have to be ordered with other events in the network ([learn more](https://hyperledger.github.io/firefly/keyconcepts/multiparty_process_flow.html)).
+
+Then you update the indexed business objects in your own database, as a result of the ordered state changes that come from the network. At any point you can go back and retrieve the set of events that caused that update to your "latest" state, whether that's on-chain transaction events, digital asset transfers (Tokens/NFTs), private data transfers, or a combination.
+
+
+
+## Learn more about Hyperledger FireFly Architecture
+
+- [YouTube Channel](https://www.youtube.com/playlist?list=PL0MZ85B_96CFVEdBNsHRoX_f15AJacZJD)
+ - Check out the architecture series
+- [Architecture reference documentation](https://hyperledger.github.io/firefly/architecture/node_component_architecture.html)
+ - Still evolving, and open for feedback - let us know what you think [on Rocket Chat](https://chat.hyperledger.org/channel/firefly)
+- [Tagged git issues](https://github.com/hyperledger/firefly/issues?q=is%3Aissue+is%3Aopen+label%3Aarchitecture)
+ - Watch out for a new formalized Feature Improvement Request (FIR) process coming soon
+
+## Hyperledger FireFly project status
+
+A number projects are actively building on Hyperledger FireFly today, and the current feature set and API is sufficient to build many decentralized applications. Some of the microservice components have matured through a number of years (including production adoption), others are new, and some areas are still evolving quickly and subject to flux in the APIs and feature set.
+
+Overall, the community is working hard towards a V1.0 release.
-- [kat](./kat): The core TypeScript runtime
-- [solidity_kat](./solidity_kat): Ethereum/Solidity smart contract code
-- [cordapp_kat](./cordapp_kat): The Corda smart contract (CorDapp)
+A good reference for the scope of the V1.0 release is included in issue #117. You might be interested in getting involved.
-This was the original implementation of the multi-party systems API by Kaleido, and is already deployed in a number production projects.
+## Git repositories
-The codebase distilled years of learning, into a set of patterns for performing blockchain orchestrated data exchange.
+There are multiple Git repos making up the Hyperledger FireFly project, and this
+list is likely to grow as additional pluggable extensions come online in the community:
-It depends on the following Kaleido services:
+- Command Line Interface (CLI) - https://github.com/hyperledger/firefly-cli
+- Core (this repo) - https://github.com/hyperledger/firefly
+- Sample applications - https://github.com/hyperledger/firefly-samples
+- HTTPS Data Exchange - https://github.com/hyperledger/firefly-dataexchange-https
+- Hyperledger Fabric connector - https://github.com/hyperledger/firefly-fabconnect
+- Ethereum (Hyperledger Besu / Quorum) connector - https://github.com/hyperledger/firefly-ethconnect
+- Corda connector: https://github.com/hyperledger/firefly-cordaconnect - contributed from Kaleido generation 1 - porting to generation 2
+- FireFly Explorer UI - https://github.com/hyperledger/firefly-ui
+
+## Contributing
+
+Interested in contributing to the community?
+
+Check out our [Contributor Guide](https://hyperledger.github.io/firefly/contributors/contributors.html), and **welcome!**.
+
+## Navigating this core repo
+
+Directories:
+
+- [internal](./internal): The core Golang implementation code
+- [pkg](./pkg): Interfaces intended for external project use
+- [cmd](./cmd): The command line entry point
+- [smart_contracts](./smart_contracts): smart contract code for Firefly's onchain logic, with support for Ethereum and Hyperledger Fabric in their respective sub-directories
-- Blockchain nodes
- - Ethereum with the Kaleido [Kaleido REST API Gateway](https://docs.kaleido.io/kaleido-services/ethconnect/)
- - Corda with the Kaleido built-in API for streaming KAT transactions
-- [Kaleido Event Streams](https://docs.kaleido.io/kaleido-services/event-streams/)
-- [Kaleido App2App Messaging](https://docs.kaleido.io/kaleido-services/app2app/)
-- [Kaleido Document Exchange](https://docs.kaleido.io/kaleido-services/document-store/)
+[Full code layout here](#firefly-code-hierarchy)
-## FireFly code hierarchy
+## FireFly Core code hierarchy
```
┌──────────┐ ┌───────────────┐
@@ -343,39 +374,3 @@ Plugins: Each plugin comprises a Go shim, plus a remote agent microservice runti
└───────────────┘ * Plugins integrate by returning their config structure for unmarshaling (JSON tags)
```
-
-## API Query Syntax
-
-REST collections provide filter, `skip`, `limit` and `sort` support.
-
-- The field in the message is used as the query parameter
-- When multiple query parameters are supplied these are combined with AND
-- When the same query parameter is supplied multiple times, these are combined with OR
-
-### Example
-
-`GET` `/api/v1/messages?confirmed=>0&type=broadcast&topic=t1&topic=t2&context=@someprefix&sort=sequence&descending&skip=100&limit=50`
-
-This states:
-
-- Filter on `confirmed` greater than 0
-- Filter on `type` exactly equal to `broadcast`
-- Filter on `topic` exactly equal to `t1` _or_ `t2`
-- Filter on `context` containing the case-sensitive string `someprefix`
-- Sort on `sequence` in `descending` order
-- Paginate with `limit` of `50` and `skip` of `100` (e.g. get page 3, with 50/page)
-
-Table of filter operations, which must be the first character of the query string (after the `=` in the above URL path example)
-
-| Operator | Description |
-| -------- | --------------------------------- |
-| (none) | Equal |
-| `!` | Not equal |
-| `<` | Less than |
-| `<=` | Less than or equal |
-| `>` | Greater than |
-| `>=` | Greater than or equal |
-| `@` | Containing - case sensitive |
-| `!@` | Not containing - case sensitive |
-| `^` | Containing - case insensitive |
-| `!^` | Not containing - case insensitive |
diff --git a/architecture/intro_to_firefly_teaser.svg b/architecture/intro_to_firefly_teaser.svg
deleted file mode 100644
index 88c3b21f8a..0000000000
--- a/architecture/intro_to_firefly_teaser.svg
+++ /dev/null
@@ -1,2277 +0,0 @@
-
-
diff --git a/images/event_driven_programming_model.png b/images/event_driven_programming_model.png
new file mode 100644
index 0000000000..3fea1fd08a
Binary files /dev/null and b/images/event_driven_programming_model.png differ
diff --git a/images/firefly_explorer.png b/images/firefly_explorer.png
new file mode 100644
index 0000000000..d5da5acfc8
Binary files /dev/null and b/images/firefly_explorer.png differ
diff --git a/images/hyperledger_firefly_logo.png b/images/hyperledger_firefly_logo.png
new file mode 100644
index 0000000000..f1f0ddfe28
Binary files /dev/null and b/images/hyperledger_firefly_logo.png differ
diff --git a/images/multi_party_systems.png b/images/multi_party_systems.png
new file mode 100644
index 0000000000..17a008735f
Binary files /dev/null and b/images/multi_party_systems.png differ
diff --git a/kat/.gitignore b/kat/.gitignore
deleted file mode 100644
index af125da1cd..0000000000
--- a/kat/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.nyc_output
-coverage
-build
-test-resources/sandbox
-node_modules
\ No newline at end of file
diff --git a/kat/.vscode/settings.json b/kat/.vscode/settings.json
deleted file mode 100644
index f773748f87..0000000000
--- a/kat/.vscode/settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "mochaExplorer.files": "src/test/**/*.ts",
- "mochaExplorer.require": "ts-node/register"
-}
\ No newline at end of file
diff --git a/kat/README.md b/kat/README.md
deleted file mode 100644
index 940eef7999..0000000000
--- a/kat/README.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Kaleido Asset Trail
-
-
-
-## Setup
-
-Kaleido asset trail can be run as a Kaleido member service or as a standalone application.
-For the latter, deploy an ERC20 token and use its address in the constructor of the [asset Trail smart contract](solidity_new/contracts/AssetTrail.sol),
-
-For each participating member, deploy the following runtimes:
-* IPFS
-* App2App Messaging (with 2 destinations representing KAT and the client)
-* Document Exchange (with 1 destination)
-
-You must also define an Event Stream with subscriptions to all relevant
-events for your use case (subscribe to all events if unsure).
-
-Asset trail has built-in storage and can optionally be configured to use MongoDB.
-
-Create a new folder for your config.
-Populate `config.json` with the URLs for the deployed contract API, the event stream, the IPFS/App2App/Document
-Exchange runtimes, a valid set of credentials, and the locally running MongoDB.
-
-```json
-{
- "port": 5000,
- "assetTrailInstanceID": "asset-trail-org-a",
- "protocol": "ethereum",
- "apiGateway": {
- "apiEndpoint": "https://xx12345678-yy12345678-connect.aws-0.kaleido.io/instances/0xc4ed58b23059e5b2f867b6040b46dcea04396c8b"
- },
- "eventStreams": {
- "wsEndpoint": "wss://xx12345678-yy12345678-connect.aws-0.kaleido.io/ws",
- "topic": "A"
- },
- "ipfs": {
- "apiEndpoint": "https://xx12345678-yy12345678-ipfs.aws-0.kaleido.io"
- },
- "app2app": {
- "socketIOEndpoint": "wss://xx12345678-yy12345678-app2app.aws-0.kaleido.io/api/v1",
- "destinations": {
- "kat": "kld://app2app/z/dev2/m/zzdza6cuf9/e/zzdk0au9rl/s/zzspcgumw6/d/kat",
- "client": "kld://app2app/z/dev2/m/zzdza6cuf9/e/zzdk0au9rl/s/zzspcgumw6/d/client"
- }
- },
- "docExchange": {
- "apiEndpoint": "https://xx12345678-yy12345678-documentstore.aws-0.kaleido.io/api/v1",
- "socketIOEndpoint": "wss://xx12345678-yy12345678-documentstore.aws-0.kaleido.io/api/v1",
- "destination": "kld://documentstore/z/dev2/m/zzdza6cuf9/e/zzdk0au9rl/s/zzu2yrgdqw/d/a"
- },
- "appCredentials": {
- "user": "xx12345678",
- "password": "yyyyyyyyyyy"
- },
- "mongodb": {
- "connectionUrl": "mongodb://localhost:27017",
- "databaseName": "assettrail0"
- }
-}
-```
-
-You can create separate config folders for each org you wish to simulate.
-
-Run the server with the following (substitute the path to your own data directory as needed):
-```
-cd core
-DATA_DIRECTORY=data/single-region/OrgA nodemon
-```
-
-If using Visual Studio Code, there is also a provided [.vscode/launch.json](launch.json) file which can be
-edited to add launch configurations to the UI.
diff --git a/kat/asset_trail_overview.png b/kat/asset_trail_overview.png
deleted file mode 100644
index d99b989beb..0000000000
Binary files a/kat/asset_trail_overview.png and /dev/null differ
diff --git a/kat/nodemon.json b/kat/nodemon.json
deleted file mode 100644
index 759d521c9d..0000000000
--- a/kat/nodemon.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "watch": ["src"],
- "ext": ".ts,.js",
- "ignore": [],
- "exec": "ts-node ./src/app.ts"
-}
\ No newline at end of file
diff --git a/kat/package.json b/kat/package.json
deleted file mode 100644
index ade95df96b..0000000000
--- a/kat/package.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
- "name": "kaleido-asset-trail",
- "version": "1.0.3",
- "description": "Kaleido Asset Trail",
- "main": "build/index.js",
- "scripts": {
- "start:dev": "nodemon",
- "build": "rimraf ./build && tsc",
- "start": "npm run build && node build/app.js",
- "test_wc": "env DATA_DIRECTORY=$PWD/test-resources/sandbox/ethereum mocha --bail --timeout 25000 \"src/test/ethereum-suite.ts\" && env DATA_DIRECTORY=$PWD/test-resources/sandbox/corda mocha --bail --timeout 25000 \"src/test/corda-suite.ts\"",
- "test": "nyc npm run test_wc"
- },
- "keywords": [],
- "author": "",
- "license": "Apache-2.0",
- "dependencies": {
- "ajv": "^6.12.5",
- "axios": "^0.21.1",
- "body-parser": "^1.19.0",
- "bs58": "^4.0.1",
- "busboy": "^0.3.1",
- "chokidar": "^3.4.3",
- "express": "^4.17.1",
- "form-data": "^3.0.0",
- "ldapjs": "^2.2.3",
- "mock-require": "^3.0.3",
- "mongodb": "^3.6.3",
- "nanoid": "^3.1.21",
- "nedb": "git+https://github.com:hyperledger/nedb.git",
- "nedb-promises": "^4.1.0",
- "proxyquire": "^2.1.3",
- "socket.io": "^2.3.0",
- "socket.io-client": "^2.3.1",
- "uuid": "^8.3.1",
- "ws": "^7.4.5"
- },
- "devDependencies": {
- "@types/axios": "^0.14.0",
- "@types/bs58": "^4.0.1",
- "@types/busboy": "^0.2.3",
- "@types/express": "^4.17.8",
- "@types/ldapjs": "^1.0.9",
- "@types/mocha": "^8.0.3",
- "@types/mock-require": "^2.0.0",
- "@types/mongodb": "^3.6.3",
- "@types/node": "^14.11.5",
- "@types/proxyquire": "^1.3.28",
- "@types/rimraf": "^3.0.0",
- "@types/sinon": "^9.0.10",
- "@types/socket.io": "^2.1.11",
- "@types/socket.io-client": "^1.4.34",
- "@types/supertest": "^2.0.10",
- "@types/uuid": "^8.3.0",
- "@types/ws": "^7.2.7",
- "mocha": "^8.4.0",
- "nock": "^13.0.4",
- "nodemon": "^2.0.4",
- "nyc": "^15.1.0",
- "rimraf": "^3.0.2",
- "sinon": "^10.0.0",
- "supertest": "^6.0.0",
- "ts-node": "^9.0.0",
- "ts-sinon": "^2.0.1",
- "typescript": "^4.2.4"
- },
- "nyc": {
- "extension": [
- ".ts",
- ".tsx"
- ],
- "exclude": [
- "coverage",
- "test",
- "dist",
- "**/*.d.ts",
- "src/test"
- ],
- "reporter": [
- "html",
- "text-summary"
- ],
- "all": true,
- "check-coverage": true,
- "statements": 64,
- "branches": 42,
- "functions": 64,
- "lines": 66
- },
- "mocha": {
- "require": [
- "ts-node/register",
- "source-map-support/register"
- ]
- }
-}
diff --git a/kat/src/app.ts b/kat/src/app.ts
deleted file mode 100644
index 8cd1aa8e2c..0000000000
--- a/kat/src/app.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { start } from './index';
-import * as utils from './lib/utils';
-
-const log = utils.getLogger('app.ts');
-
-export const promise = start().catch(err => {
- log.error(`Failed to start asset trail. ${err}`);
-});
\ No newline at end of file
diff --git a/kat/src/clients/api-gateway.ts b/kat/src/clients/api-gateway.ts
deleted file mode 100644
index cb705d4e40..0000000000
--- a/kat/src/clients/api-gateway.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { config } from '../lib/config';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponse } from '../lib/interfaces';
-import * as ethereumGateway from './gateway-providers/ethereum';
-import * as cordaGateway from './gateway-providers/corda';
-
-// Member APIs
-
-export const upsertMember = async (address: string, name: string, app2appDestination: string,
- docExchangeDestination: string, sync: boolean): Promise => {
- return ethereumGateway.upsertMember(address, name, app2appDestination, docExchangeDestination, sync);
-};
-
-
-// Asset definition APIs
-
-export const createAssetDefinition = async (author: string, assetDefinitionHash: string, sync: boolean):
- Promise => {
- return ethereumGateway.createAssetDefinition(author, assetDefinitionHash, sync);
-};
-
-
-// Payment definition APIs
-
-export const createDescribedPaymentDefinition = async (paymentDefinitionID: string, name: string, author: string,
- descriptionSchemaHash: string, sync: boolean): Promise => {
- return ethereumGateway.createDescribedPaymentDefinition(paymentDefinitionID, name, author, descriptionSchemaHash, sync);
-};
-
-export const createPaymentDefinition = async (paymentDefinitionID: string, name: string, author: string, sync: boolean):
- Promise => {
- return ethereumGateway.createPaymentDefinition(paymentDefinitionID, name, author, sync);
-};
-
-
-// Asset instance APIs
-
-export const createDescribedAssetInstance = async (assetInstanceID: string, assetDefinitionID: string, author: string,
- descriptionHash: string, contentHash: string, participants: string[] | undefined, sync = false): Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.createDescribedAssetInstance(assetInstanceID, assetDefinitionID, descriptionHash, contentHash, participants);
- case 'ethereum':
- return ethereumGateway.createDescribedAssetInstance(assetInstanceID, assetDefinitionID, author, descriptionHash, contentHash, sync);
- }
-};
-
-export const createAssetInstance = async (assetInstanceID: string, assetDefinitionID: string, author: string,
- contentHash: string, participants: string[] | undefined, sync = false): Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.createAssetInstance(assetInstanceID, assetDefinitionID, contentHash, participants);
- case 'ethereum':
- return ethereumGateway.createAssetInstance(assetInstanceID, assetDefinitionID, author, contentHash, sync);
- }
-};
-
-export const createAssetInstanceBatch = async (batchHash: string, author: string, participants: string[] | undefined, sync = false): Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.createAssetInstanceBatch(batchHash, participants);
- case 'ethereum':
- return ethereumGateway.createAssetInstanceBatch(batchHash, author, sync);
- }
-}
-
-export const setAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string,
- participants: string[] | undefined, sync: boolean): Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.setAssetInstanceProperty(assetDefinitionID, assetInstanceID, key, value, participants);
- case 'ethereum':
- return ethereumGateway.setAssetInstanceProperty(assetDefinitionID, assetInstanceID, author, key, value, sync);
- }
-};
-
-
-// Payment instance APIs
-
-export const createDescribedPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string,
- author: string, member: string, amount: number, descriptionHash: string, participants: string[] | undefined, sync: boolean):
- Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.createDescribedPaymentInstance(paymentInstanceID, paymentDefinitionID, member, amount, descriptionHash, participants);
- case 'ethereum':
- return ethereumGateway.createDescribedPaymentInstance(paymentInstanceID, paymentDefinitionID, author, member, amount, descriptionHash, sync);
- }
-};
-
-export const createPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string,
- author: string, member: string, amount: number, participants: string[] | undefined, sync: boolean):
- Promise => {
- switch (config.protocol) {
- case 'corda':
- return cordaGateway.createPaymentInstance(paymentInstanceID, paymentDefinitionID, member, amount, participants);
- case 'ethereum':
- return ethereumGateway.createPaymentInstance(paymentInstanceID, paymentDefinitionID, author, member, amount, sync);
- }
-};
diff --git a/kat/src/clients/app2app.ts b/kat/src/clients/app2app.ts
deleted file mode 100644
index 0057135dc5..0000000000
--- a/kat/src/clients/app2app.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import io from 'socket.io-client';
-import { config } from '../lib/config';
-import * as utils from '../lib/utils';
-import { AssetTradeMessage, IApp2AppMessage, IApp2AppMessageListener } from '../lib/interfaces';
-
-const log = utils.getLogger('clients/app2app.ts');
-
-let socket: SocketIOClient.Socket
-let listeners: IApp2AppMessageListener[] = [];
-
-export const init = async () => {
- establishSocketIOConnection();
-};
-
-function subscribeWithRetry() {
- log.trace(`App2App subscription: ${config.app2app.destinations.kat}`)
- socket.emit('subscribe', [config.app2app.destinations.kat], (err: any, data: any) => {
- if (err) {
- log.error(`App2App subscription failure (retrying): ${err}`);
- setTimeout(subscribeWithRetry, utils.constants.SUBSCRIBE_RETRY_INTERVAL);
- return;
- }
- log.trace(`App2App subscription succeeded: ${JSON.stringify(data)}`);
- });
-}
-
-const establishSocketIOConnection = () => {
- let error = false;
- const { APP2APP_BATCH_SIZE, APP2APP_BATCH_TIMEOUT, APP2APP_READ_AHEAD } = utils.constants;
- socket = io.connect(`${config.app2app.socketIOEndpoint}?auto_commit=false&read_ahead=${APP2APP_READ_AHEAD}&batch_size=${APP2APP_BATCH_SIZE}&batch_timeout=${APP2APP_BATCH_TIMEOUT}`, {
- transportOptions: {
- polling: {
- extraHeaders: {
- Authorization: 'Basic ' + Buffer.from(`${config.appCredentials.user}` +
- `:${config.appCredentials.password}`).toString('base64')
- }
- }
- }
- }).on('connect', () => {
- if (error) {
- error = false;
- log.info('App2App messaging Socket IO connection restored');
- }
- subscribeWithRetry();
- }).on('connect_error', (err: Error) => {
- error = true;
- log.error(`App2App messaging Socket IO connection error. ${err.toString()}`);
- }).on('error', (err: Error) => {
- error = true;
- log.error(`App2app messaging Socket IO error. ${err.toString()}`);
- }).on('exception', (err: Error, extra?: any) => {
- // Exceptions are such things as delivery failures. They do not put the connection in error state
- log.error(`App2app messaging exception. ${err.toString()}`, extra);
- }).on('data', (app2appMessage: IApp2AppMessage) => {
- log.trace(`App2App message ${JSON.stringify(app2appMessage)}`);
- try {
- const content: AssetTradeMessage = JSON.parse(app2appMessage.content);
- log.trace(`App2App message type=${content.type}`)
- for (const listener of listeners) {
- listener(app2appMessage.headers, content);
- }
- } catch (err) {
- log.error(`App2App message error ${err}`);
- } finally {
- socket.emit('commit');
- }
- }) as SocketIOClient.Socket;
-};
-
-export const addListener = (listener: IApp2AppMessageListener) => {
- listeners.push(listener);
-};
-
-export const removeListener = (listener: IApp2AppMessageListener) => {
- listeners = listeners.filter(entry => entry != listener);
-};
-
-export const dispatchMessage = (to: string, content: any) => {
- log.trace(`App2App dispatch type=${content.type}`)
- socket.emit('produce', {
- headers: {
- from: config.app2app.destinations.kat,
- to
- },
- content: JSON.stringify(content)
- }, 'kat', (err: any) => {
- if(err) {
- log.error(`Failed to dispatch App2App message.`, err);
- }
- });
-};
-
-export const reset = () => {
- if (socket) {
- log.info('App2App Socket IO connection reset');
- socket.close();
- establishSocketIOConnection();
- }
-};
\ No newline at end of file
diff --git a/kat/src/clients/database.ts b/kat/src/clients/database.ts
deleted file mode 100644
index 939d372731..0000000000
--- a/kat/src/clients/database.ts
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { config } from '../lib/config';
-import { ClientEventType, IClientEventListener, IDatabaseProvider, IDBAssetDefinition, IDBAssetInstance, IDBBatch, IDBBlockchainData, IDBMember, IDBPaymentDefinition, IDBPaymentInstance, IStoredSubscriptions } from '../lib/interfaces';
-import * as utils from '../lib/utils';
-import MongoDBProvider from './db-providers/mongodb';
-import NEDBProvider from './db-providers/nedb';
-const log = utils.getLogger('handlers/asset-trade.ts');
-
-let databaseProvider: IDatabaseProvider;
-
-export const init = async () => {
- if (config.mongodb !== undefined) {
- databaseProvider = new MongoDBProvider();
- } else {
- databaseProvider = new NEDBProvider();
- }
- await databaseProvider.init();
-};
-
-let listeners: IClientEventListener[] = [];
-
-// COLLECTION AGNOSTIC QUERIES
-
-export const createCollection = (collectionName: string, indexes: { fields: string[], unique?: boolean }[]) => {
- return databaseProvider.createCollection(collectionName, indexes);
-};
-
-// MEMBER QUERIES
-
-export const retrieveMemberByAddress = (address: string): Promise => {
- return databaseProvider.findOne('members', { address });
-};
-
-export const retrieveMembers = (query: object, skip: number, limit: number): Promise => {
- return databaseProvider.find('members', query, { name: 1 }, skip, limit);
-};
-
-export const upsertMember = async (member: IDBMember) => {
- await databaseProvider.updateOne('members', { address: member.address }, { $set: member }, true);
- emitEvent('member-registered', member);
-};
-
-// ASSET DEFINITION QUERIES
-
-export const retrieveAssetDefinitions = (query: object, skip: number, limit: number): Promise => {
- return databaseProvider.find('asset-definitions', query, { name: 1 }, skip, limit)
-};
-
-export const countAssetDefinitions = (query: object): Promise => {
- return databaseProvider.count('asset-definitions', query);
-};
-
-export const retrieveAssetDefinitionByID = (assetDefinitionID: string): Promise => {
- return databaseProvider.findOne('asset-definitions', { assetDefinitionID });
-};
-
-export const retrieveAssetDefinitionByName = (name: string): Promise => {
- return databaseProvider.findOne('asset-definitions', { name });
-};
-
-export const upsertAssetDefinition = async (assetDefinition: IDBAssetDefinition) => {
- await databaseProvider.updateOne('asset-definitions', { assetDefinitionID: assetDefinition.assetDefinitionID }, { $set: assetDefinition }, true);
- if (assetDefinition.submitted !== undefined) {
- emitEvent('asset-definition-submitted', assetDefinition);
- } else if (assetDefinition.transactionHash !== undefined) {
- emitEvent('asset-definition-created', assetDefinition);
- }
-};
-
-export const markAssetDefinitionAsConflict = async (assetDefinitionID: string, timestamp: number) => {
- await databaseProvider.updateOne('asset-definitions', { assetDefinitionID }, { $set: { timestamp, conflict: true } }, false);
- emitEvent('asset-definition-name-conflict', { assetDefinitionID })
-};
-
-// PAYMENT DEFINITION QUERIES
-
-export const retrievePaymentDefinitions = (query: object, skip: number, limit: number): Promise => {
- return databaseProvider.find('payment-definitions', query, { name: 1 }, skip, limit);
-};
-
-export const countPaymentDefinitions = (query: object): Promise => {
- return databaseProvider.count('payment-definitions', query);
-};
-
-export const retrievePaymentDefinitionByID = (paymentDefinitionID: string): Promise => {
- return databaseProvider.findOne('payment-definitions', { paymentDefinitionID });
-};
-
-export const retrievePaymentDefinitionByName = (name: string): Promise => {
- return databaseProvider.findOne('payment-definitions', { name });
-};
-
-export const upsertPaymentDefinition = async (paymentDefinition: IDBPaymentDefinition) => {
- await databaseProvider.updateOne('payment-definitions', { paymentDefinitionID: paymentDefinition.paymentDefinitionID }, { $set: paymentDefinition }, true)
- if (paymentDefinition.submitted !== undefined) {
- emitEvent('payment-definition-submitted', paymentDefinition);
- } else if (paymentDefinition.transactionHash !== undefined) {
- emitEvent('payment-definition-created', paymentDefinition);
- }
-};
-
-export const markPaymentDefinitionAsConflict = async (paymentDefinitionID: string, timestamp: number) => {
- await databaseProvider.updateOne('payment-definitions', { paymentDefinitionID }, { $set: { conflict: true, timestamp } }, false);
- emitEvent('payment-definition-name-conflict', { paymentDefinitionID })
-};
-
-// ASSET INSTANCE QUERIES
-
-export const retrieveAssetInstances = (assetDefinitionID: string, query: object, sort: object, skip: number, limit: number): Promise => {
- return databaseProvider.find(`asset-instance-${assetDefinitionID}`, query, sort, skip, limit);
-};
-
-export const countAssetInstances = (assetDefinitionID: string, query: object): Promise => {
- return databaseProvider.count(`asset-instance-${assetDefinitionID}`, query);
-};
-
-export const retrieveAssetInstanceByID = (assetDefinitionID: string, assetInstanceID: string): Promise => {
- return databaseProvider.findOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID });
-};
-
-export const retrieveAssetInstanceByDefinitionIDAndContentHash = (assetDefinitionID: string, contentHash: string): Promise => {
- return databaseProvider.findOne(`asset-instance-${assetDefinitionID}`, { contentHash });
-};
-
-export const upsertAssetInstance = async (assetInstance: IDBAssetInstance) => {
- await databaseProvider.updateOne(`asset-instance-${assetInstance.assetDefinitionID}`, { assetInstanceID: assetInstance.assetInstanceID }, { $set: assetInstance }, true);
- if (assetInstance.submitted !== undefined) {
- emitEvent('asset-instance-submitted', assetInstance);
- } else if (assetInstance.transactionHash !== undefined) {
- emitEvent('asset-instance-created', assetInstance);
- }
-};
-
-export const setAssetInstanceReceipt = async (assetDefinitionID: string, assetInstanceID: string, receipt: string) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { receipt } }, true);
-};
-
-export const setAssetInstancePrivateContent = async (assetDefinitionID: string, assetInstanceID: string, content: object | undefined, filename: string | undefined) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { content, filename } }, true);
- log.info(`Emitting event for private-asset-instance-content-stored`);
- emitEvent('private-asset-instance-content-stored', { assetDefinitionID, assetInstanceID, content, filename });
-};
-
-export const markAssetInstanceAsConflict = async (assetDefinitionID: string, assetInstanceID: string, timestamp: number) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID }, { $set: { conflict: true, timestamp } }, false);
- emitEvent('asset-instance-content-conflict', { assetDefinitionID, assetInstanceID });
-};
-
-export const setSubmittedAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string, submitted: number, batchID?: string) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
- {
- $set: {
- [`properties.${author}.${key}.value`]: value,
- [`properties.${author}.${key}.submitted`]: submitted,
- [`properties.${author}.${key}.batchID`]: batchID,
- }
- }, false);
- emitEvent('asset-instance-property-submitted', { assetDefinitionID, assetInstanceID, key, value, submitted, batchID });
-};
-
-export const setAssetInstancePropertyReceipt = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, receipt: string) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
- {
- $set: {
- [`properties.${author}.${key}.receipt`]: receipt
- }
- }, false);
-};
-
-export const setConfirmedAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string, timestamp: number, { blockNumber, transactionHash }: IDBBlockchainData) => {
- await databaseProvider.updateOne(`asset-instance-${assetDefinitionID}`, { assetInstanceID },
- {
- $set: {
- [`properties.${author}.${key}.value`]: value,
- [`properties.${author}.${key}.history.${timestamp}`]: { value, timestamp, blockNumber, transactionHash }
- }
- }, false);
- emitEvent('asset-instance-property-set', { assetDefinitionID, assetInstanceID, author, key, value, timestamp, blockNumber, transactionHash });
-};
-
-// PAYMENT INSTANCE QUERIES
-
-export const retrievePaymentInstances = (query: object, sort: object, skip: number, limit: number): Promise => {
- return databaseProvider.find('payment-instances', query, sort, skip, limit);
-};
-
-export const countPaymentInstances = (query: object): Promise => {
- return databaseProvider.count('payment-instances', query);
-};
-
-export const retrievePaymentInstanceByID = (paymentInstanceID: string): Promise => {
- return databaseProvider.findOne('payment-instances', { paymentInstanceID });
-};
-
-export const upsertPaymentInstance = async (paymentInstance: IDBPaymentInstance) => {
- await databaseProvider.updateOne('payment-instances', { paymentInstanceID: paymentInstance.paymentInstanceID }, { $set: paymentInstance }, true);
- if (paymentInstance.submitted !== undefined) {
- emitEvent('payment-instance-submitted', paymentInstance);
- } else {
- emitEvent('payment-instance-created', paymentInstance);
- }
-};
-
-
-// BATCH QUERIES
-
-export const retrieveBatches = (query: object, skip: number, limit: number, sort: {[f: string]: number} = {}): Promise => {
- return databaseProvider.find('batches', query, sort, skip, limit);
-};
-
-export const retrieveBatchByID = (batchID: string): Promise => {
- return databaseProvider.findOne('batches', { batchID });
-};
-
-export const retrieveBatchByHash = (batchHash: string): Promise => {
- return databaseProvider.findOne('batches', { batchHash });
-};
-
-export const upsertBatch = async (batch: IDBBatch) => {
- await databaseProvider.updateOne('batches', { batchID: batch.batchID }, { $set: batch }, true);
-};
-
-// SUBSCRIPTION MANAGEMENT
-
-export const retrieveSubscriptions = (): Promise => {
- return databaseProvider.findOne('state', { key: 'subscriptions' });
-};
-
-export const upsertSubscriptions = (subscriptions: IStoredSubscriptions): Promise => {
- return databaseProvider.updateOne('state', { key: 'subscriptions' }, { $set: subscriptions }, true);
-};
-
-// EVENT HANDLING
-
-export const addListener = (listener: IClientEventListener) => {
- listeners.push(listener);
-};
-
-export const removeListener = (listener: IClientEventListener) => {
- listeners = listeners.filter(entry => entry != listener);
-};
-
-const emitEvent = (eventType: ClientEventType, content: object) => {
- for (const listener of listeners) {
- listener(eventType, content);
- }
-};
-
-export const shutDown = () => {
- databaseProvider.shutDown();
-};
diff --git a/kat/src/clients/db-providers/mongodb.ts b/kat/src/clients/db-providers/mongodb.ts
deleted file mode 100644
index 8b06f26fee..0000000000
--- a/kat/src/clients/db-providers/mongodb.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { Db, MongoClient } from 'mongodb';
-import { config } from '../../lib/config';
-import { databaseCollectionName, IDatabaseProvider, indexes } from '../../lib/interfaces';
-import { databaseCollectionIndexes } from '../../lib/utils';
-
-let db: Db;
-let mongoClient: MongoClient;
-
-export default class MongoDBProvider implements IDatabaseProvider {
-
- async init() {
- try {
- mongoClient = await MongoClient.connect(config.mongodb.connectionUrl,
- { useNewUrlParser: true, useUnifiedTopology: true, ignoreUndefined: true });
- db = mongoClient.db(config.mongodb.databaseName);
- for (const [collectionName, indexes] of Object.entries(databaseCollectionIndexes)) {
- this.createCollection(collectionName, indexes);
- }
- } catch (err) {
- throw new Error(`Failed to connect to Mongodb. ${err}`);
- }
- }
-
- async createCollection(collectionName: string, indexes: indexes) {
- try {
- for (const index of indexes) {
- const fields: { [f: string]: number } = {};
- for (const field of index.fields) {
- fields[field] = 1; // all ascending currently
- }
- db.collection(collectionName).createIndex(fields, { unique: !!index.unique });
- }
- } catch (err) {
- throw new Error(`Failed to create collection. ${err}`);
- }
- }
-
- count(collectionName: databaseCollectionName, query: object): Promise {
- return db.collection(collectionName).find(query).count();
- }
-
- find(collectionName: databaseCollectionName, query: object, sort: object, skip: number, limit: number): Promise {
- return db.collection(collectionName).find(query, { projection: { _id: 0 } }).sort(sort).skip(skip).limit(limit).toArray();
- }
-
- findOne(collectionName: databaseCollectionName, query: object): Promise {
- return db.collection(collectionName).findOne(query, { projection: { _id: 0 } });
- }
-
- async updateOne(collectionName: databaseCollectionName, query: object, value: object, upsert: boolean) {
- await db.collection(collectionName).updateOne(query, value, { upsert });
- }
-
- shutDown() {
- mongoClient.close();
- }
-
-}
diff --git a/kat/src/clients/db-providers/nedb.ts b/kat/src/clients/db-providers/nedb.ts
deleted file mode 100644
index a539e7a089..0000000000
--- a/kat/src/clients/db-providers/nedb.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import Datastore from 'nedb-promises';
-import path from 'path';
-import { databaseCollectionName, IDatabaseProvider, indexes } from '../../lib/interfaces';
-import { constants, databaseCollectionIndexes } from '../../lib/utils';
-
-const projection = { _id: 0 };
-let collections: { [name: string]: Datastore } = {};
-
-export default class NEDBProvider implements IDatabaseProvider {
-
- async init() {
- try {
- for (const [collectionName, indexes] of Object.entries(databaseCollectionIndexes)) {
- this.createCollection(collectionName, indexes);
- }
- } catch (err) {
- throw new Error(`Failed to initialize NEDB. ${err}`);
- }
- }
-
- async createCollection(collectionName: string, indexes: indexes) {
- const collection = Datastore.create({
- filename: path.join(constants.DATA_DIRECTORY, `${collectionName}.json`),
- autoload: true
- });
- for (const index of indexes) {
- // No compound indexes here
- for (let fieldName of index.fields) {
- collection.ensureIndex({ fieldName, unique: !!index.unique });
- }
- }
- collections[collectionName] = collection;
- }
-
- count(collectionName: databaseCollectionName, query: object): Promise {
- return collections[collectionName].count(query);
- }
-
- find(collectionName: databaseCollectionName, query: object, sort: object, skip: number, limit: number): Promise {
- return collections[collectionName].find(query, projection).skip(skip).limit(limit).sort(sort);
- }
-
- findOne(collectionName: databaseCollectionName, query: object): Promise {
- return collections[collectionName].findOne(query, projection);
- }
-
- async updateOne(collectionName: databaseCollectionName, query: object, value: object, upsert: boolean) {
- await collections[collectionName].update(query, value, { upsert });
- }
-
- shutDown() { }
-
-}
diff --git a/kat/src/clients/doc-exchange.ts b/kat/src/clients/doc-exchange.ts
deleted file mode 100644
index ff9a791019..0000000000
--- a/kat/src/clients/doc-exchange.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { IDocExchangeTransferData, IDocExchangeListener, IDocExchangeDocumentDetails } from '../lib/interfaces';
-import { config } from '../lib/config';
-import { Stream, Readable } from 'stream';
-import io from 'socket.io-client';
-import FormData from 'form-data';
-import axios from 'axios';
-import * as utils from '../lib/utils';
-
-const log = utils.getLogger('clients/doc-exchange.ts');
-
-let socket: SocketIOClient.Socket
-let listeners: IDocExchangeListener[] = [];
-
-export const init = async () => {
- try {
- const response = await axios.get(`${config.docExchange.apiEndpoint}/documents`, {
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- if (!Array.isArray(response.data.entries)) {
- throw 'Invalid response';
- } else {
- establishSocketIOConnection();
- }
- } catch (err) {
- throw new Error(`Document exchange REST connection failed. ${err}`);
- }
-};
-
-const establishSocketIOConnection = () => {
- let error = false;
- socket = io.connect(config.docExchange.socketIOEndpoint, {
- transportOptions: {
- polling: {
- extraHeaders: {
- Authorization: 'Basic ' + Buffer.from(`${config.appCredentials.user}` +
- `:${config.appCredentials.password}`).toString('base64')
- }
- }
- }
- }).on('connect', () => {
- if (error) {
- error = false;
- log.info('Document exchange Socket IO connection restored');
- }
- }).on('connect_error', (err: Error) => {
- error = true;
- log.error(`Document exchange Socket IO connection error. ${err.toString()}`);
- }).on('error', (err: Error) => {
- error = true;
- log.error(`Document exchange Socket IO error. ${err.toString()}`);
- }).on('document_received', (transferData: IDocExchangeTransferData) => {
- log.trace(`Doc exchange transfer event ${JSON.stringify(transferData)}`);
- for (const listener of listeners) {
- listener(transferData);
- }
- }) as SocketIOClient.Socket;
-};
-
-export const addListener = (listener: IDocExchangeListener) => {
- listeners.push(listener);
-};
-
-export const removeListener = (listener: IDocExchangeListener) => {
- listeners = listeners.filter(entry => entry != listener);
-};
-
-export const downloadStream = async (documentPath: string): Promise => {
- const response = await axios.get(`${config.docExchange.apiEndpoint}/documents/${documentPath}`, {
- responseType: 'arraybuffer',
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return response.data;
-};
-
-export const downloadJSON = async (documentPath: string): Promise => {
- const response = await axios.get(`${config.docExchange.apiEndpoint}/documents/${documentPath}`, {
- responseType: 'json',
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return response.data;
-};
-
-export const findDocumentByHash = async (documentHash: string): Promise => {
- const result = await axios({
- url: `${config.docExchange.apiEndpoint}/search?query=${documentHash}&by_hash=true`,
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- if (result.data.documents.length > 0) {
- return result.data.documents[0].full_path;
- }
- return null;
-}
-
-export const uploadString = async (value: string, path: string): Promise => {
- const readable = new Readable();
- readable.push(value);
- readable.push(null);
- return uploadStream(readable, path);
-};
-
-export const uploadStream = async (stream: Stream, path: string): Promise => {
- const formData = new FormData();
- formData.append('document', stream);
- const result = await axios({
- method: 'put',
- url: `${config.docExchange.apiEndpoint}/documents/${path}`,
- data: formData,
- headers: formData.getHeaders(),
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return result.data.hash;
-};
-
-export const transfer = async (from: string, to: string, document: string) => {
- await axios({
- method: 'post',
- url: `${config.docExchange.apiEndpoint}/transfers`,
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- },
- data: { from, to, document }
- });
-}
-
-export const getDocumentDetails = async (filePath: string): Promise => {
- const result = await axios({
- url: `${config.docExchange.apiEndpoint}/documents/${filePath}?details_only=true`,
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return result.data;
-};
-
-export const reset = () => {
- if (socket) {
- log.info('Document exchange Socket IO connection reset');
- socket.close();
- establishSocketIOConnection();
- }
-};
diff --git a/kat/src/clients/event-streams.ts b/kat/src/clients/event-streams.ts
deleted file mode 100644
index 6ff008cb1c..0000000000
--- a/kat/src/clients/event-streams.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import WebSocket from 'ws';
-import { config } from '../lib/config';
-import * as utils from '../lib/utils';
-import { IDBBlockchainData, IEventAssetDefinitionCreated, IEventAssetInstanceBatchCreated, IEventAssetInstanceCreated, IEventAssetInstancePropertySet, IEventPaymentDefinitionCreated, IEventPaymentInstanceCreated, IEventStreamMessage, IEventStreamRawMessageCorda } from '../lib/interfaces';
-import * as membersHandler from '../handlers/members';
-import * as assetDefinitionsHandler from '../handlers/asset-definitions';
-import * as paymentDefinitionsHandler from '../handlers/payment-definitions';
-import * as assetInstancesHandler from '../handlers/asset-instances';
-import * as paymentInstanceHandler from '../handlers/payment-instances';
-import { IEventMemberRegistered } from '../lib/interfaces';
-
-const log = utils.getLogger('clients/event-streams.ts');
-
-let ws: WebSocket;
-let heartBeatTimeout: NodeJS.Timeout;
-let disconnectionDetected = false;
-let disconnectionTimeout: NodeJS.Timeout;
-
-export const init = () => {
- ws = new WebSocket(config.eventStreams.wsEndpoint, {
- headers: {
- Authorization: 'Basic ' + Buffer.from(`${config.eventStreams.auth?.user ?? config.appCredentials.user}` +
- `:${config.eventStreams.auth?.password ?? config.appCredentials.password}`).toString('base64')
-
- }
- });
- addEventHandlers();
-};
-
-export const shutDown = () => {
- if (disconnectionTimeout) {
- clearTimeout(disconnectionTimeout);
- }
- if (ws) {
- clearTimeout(heartBeatTimeout);
- ws.close();
- }
-};
-
-const addEventHandlers = () => {
- ws.on('open', () => {
- if (disconnectionDetected) {
- disconnectionDetected = false;
- log.info('Event stream websocket restored');
- }
- ws.send(JSON.stringify({
- type: 'listen',
- topic: config.eventStreams.topic
- }));
- heartBeat();
- }).on('close', () => {
- disconnectionDetected = true;
- log.error(`Event stream websocket disconnected, attempting to reconnect in ${utils.constants.EVENT_STREAM_WEBSOCKET_RECONNECTION_DELAY_SECONDS} second(s)`);
- disconnectionTimeout = setTimeout(() => {
- init();
- }, utils.constants.EVENT_STREAM_WEBSOCKET_RECONNECTION_DELAY_SECONDS * 1000);
- }).on('message', async (message: string) => {
- await handleMessage(message);
- ws.send(JSON.stringify({
- type: 'ack',
- topic: config.eventStreams.topic
- }));
- }).on('pong', () => {
- heartBeat();
- }).on('error', err => {
- log.error(`Event stream websocket error. ${err}`);
- });
-};
-
-const heartBeat = () => {
- ws.ping();
- clearTimeout(heartBeatTimeout);
- heartBeatTimeout = setTimeout(() => {
- log.error('Event stream ping timeout');
- ws.terminate();
- }, utils.constants.EVENT_STREAM_PING_TIMEOUT_SECONDS * 1000);
-}
-
-const processRawMessage = (message: string): Array => {
- switch (config.protocol) {
- case 'ethereum':
- return JSON.parse(message);
- case 'corda':
- const cordaMessages: Array = JSON.parse(message);
- return cordaMessages.map(msg => (
- {
- data: {
- ...msg.data.data,
- timestamp: Date.parse(msg.recordedTime)
- },
- transactionHash: msg.stateRef.txhash,
- subId: msg.subId,
- signature: msg.signature
- }
- )
- );
- }
-}
-
-const getBlockchainData = (message: IEventStreamMessage): IDBBlockchainData => {
- switch (config.protocol) {
- case 'ethereum':
- return {
- blockNumber: Number(message.blockNumber),
- transactionHash: message.transactionHash
- }
- case 'corda': {
- return {
- transactionHash: message.transactionHash
- }
- }
- }
-}
-
-const eventSignatures = () => {
- switch (config.protocol) {
- case 'ethereum': return utils.contractEventSignatures
- case 'corda': return utils.contractEventSignaturesCorda
- }
-}
-
-const handleMessage = async (message: string) => {
- const messageArray: Array = processRawMessage(message);
- log.info(`Event batch (${messageArray.length})`)
- const signatures = eventSignatures();
- for (const message of messageArray) {
- log.trace(`Event ${JSON.stringify(message)}`);
- log.info(`Event signature: ${message.signature}`);
- const blockchainData: IDBBlockchainData = getBlockchainData(message);
- try {
- switch (message.signature) {
- case signatures.MEMBER_REGISTERED:
- await membersHandler.handleMemberRegisteredEvent(message.data as IEventMemberRegistered, blockchainData); break;
- case signatures.ASSET_DEFINITION_CREATED:
- await assetDefinitionsHandler.handleAssetDefinitionCreatedEvent(message.data as IEventAssetDefinitionCreated, blockchainData); break;
- case signatures.DESCRIBED_PAYMENT_DEFINITION_CREATED:
- case signatures.PAYMENT_DEFINITION_CREATED:
- await paymentDefinitionsHandler.handlePaymentDefinitionCreatedEvent(message.data as IEventPaymentDefinitionCreated, blockchainData); break;
- case signatures.ASSET_INSTANCE_CREATED:
- case signatures.DESCRIBED_ASSET_INSTANCE_CREATED:
- await assetInstancesHandler.handleAssetInstanceCreatedEvent(message.data as IEventAssetInstanceCreated, blockchainData); break;
- case signatures.ASSET_INSTANCE_BATCH_CREATED:
- await assetInstancesHandler.handleAssetInstanceBatchCreatedEvent(message.data as IEventAssetInstanceBatchCreated, blockchainData); break;
- case signatures.DESCRIBED_PAYMENT_INSTANCE_CREATED:
- case signatures.PAYMENT_INSTANCE_CREATED:
- await paymentInstanceHandler.handlePaymentInstanceCreatedEvent(message.data as IEventPaymentInstanceCreated, blockchainData); break;
- case signatures.ASSET_PROPERTY_SET:
- await assetInstancesHandler.handleSetAssetInstancePropertyEvent(message.data as IEventAssetInstancePropertySet, blockchainData); break;
- }
- } catch (err) {
- log.error(`Failed to handle event: ${message.signature} for message: ${JSON.stringify(message)} with error`, err.stack);
- }
- }
-};
diff --git a/kat/src/clients/gateway-providers/corda.ts b/kat/src/clients/gateway-providers/corda.ts
deleted file mode 100644
index 65ac70ae45..0000000000
--- a/kat/src/clients/gateway-providers/corda.ts
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import axios from 'axios';
-import { config } from '../../lib/config';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponse } from '../../lib/interfaces';
-import { getLogger } from '../../lib/utils';
-const log = getLogger('gateway-providers/corda.ts');
-
-// Asset instance APIs
-
-export const createDescribedAssetInstance = async (assetInstanceID: string, assetDefinitionID: string,
- descriptionHash: string, contentHash: string, participants: string[] | undefined): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createDescribedAssetInstance`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetInstanceID: assetInstanceID,
- assetDefinitionID: assetDefinitionID,
- descriptionHash,
- contentHash,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to create described asset instance ${assetInstanceID}`, err);
- }
-};
-
-export const createAssetInstance = async (assetInstanceID: string, assetDefinitionID: string,
- contentHash: string, participants: string[] | undefined): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createAssetInstance`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetInstanceID: assetInstanceID,
- assetDefinitionID: assetDefinitionID,
- contentHash,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to create asset instance ${assetInstanceID}`, err);
- }
-};
-
-export const createAssetInstanceBatch = async (batchHash: string, participants: string[] | undefined): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createAssetInstanceBatch`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- batchHash,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to create asset instance batch ${batchHash}`, err);
- }
-};
-
-export const setAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, key: string, value: string,
- participants: string[] | undefined | undefined): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/setAssetInstanceProperty`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetDefinitionID: assetDefinitionID,
- assetInstanceID: assetInstanceID,
- key,
- value,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to set asset instance property ${key} (instance=${assetInstanceID})`, err);
- }
-};
-
-export const createDescribedPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string, member: string, amount: number, descriptionHash: string, participants: string[] | undefined):
- Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createDescribedPaymentInstance`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentInstanceID: paymentInstanceID,
- paymentDefinitionID: paymentDefinitionID,
- member,
- amount,
- descriptionHash,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to create described asset payment instance ${paymentInstanceID}`, err);
- }
-};
-
-export const createPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string,
- member: string, amount: number, participants: string[] | undefined): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createPaymentInstance`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentInstanceID: paymentInstanceID,
- paymentDefinitionID: paymentDefinitionID,
- member,
- amount,
- participants
- }
- });
- return { ...response.data, type: 'sync' };
- } catch (err) {
- return handleError(`Failed to create asset payment instance ${paymentInstanceID}`, err);
- }
-};
-
-function handleError(msg: string, err: any): Promise {
- const errMsg = err.response?.data?.error ?? err.response.data.message ?? err.toString();
- log.error(`${msg}. ${errMsg}`);
- throw new Error(msg);
-}
\ No newline at end of file
diff --git a/kat/src/clients/gateway-providers/ethereum.ts b/kat/src/clients/gateway-providers/ethereum.ts
deleted file mode 100644
index 28493d8680..0000000000
--- a/kat/src/clients/gateway-providers/ethereum.ts
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import axios from 'axios';
-import { config } from '../../lib/config';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponseEthereum } from '../../lib/interfaces';
-import * as utils from '../../lib/utils';
-const log = utils.getLogger('gateway-providers/ethereum.ts');
-
-// Member APIs
-
-export const upsertMember = async (address: string, name: string, app2appDestination: string,
- docExchangeDestination: string, sync: boolean): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/registerMember?kld-from=${address}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- name,
- assetTrailInstanceID: config.assetTrailInstanceID,
- app2appDestination,
- docExchangeDestination
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to register new member ${name}`, err);
- }
-};
-
-
-// Asset definition APIs
-
-export const createAssetDefinition = async (author: string, assetDefinitionHash: string, sync: boolean):
- Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createAssetDefinition?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetDefinitionHash
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create asset definition ${assetDefinitionHash}`, err);
- }
-};
-
-
-// Payment definition APIs
-
-export const createDescribedPaymentDefinition = async (paymentDefinitionID: string, name: string, author: string,
- descriptionSchemaHash: string, sync: boolean): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createDescribedPaymentDefinition?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentDefinitionID: utils.uuidToHex(paymentDefinitionID),
- name,
- descriptionSchemaHash
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create described payment definition ${paymentDefinitionID}`, err);
- }
-};
-
-export const createPaymentDefinition = async (paymentDefinitionID: string, name: string, author: string, sync: boolean):
- Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createPaymentDefinition?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentDefinitionID: utils.uuidToHex(paymentDefinitionID),
- name
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create payment definition ${paymentDefinitionID}`, err);
- }
-};
-
-
-// Asset instance APIs
-
-export const createDescribedAssetInstance = async (assetInstanceID: string, assetDefinitionID: string, author: string,
- descriptionHash: string, contentHash: string, sync = false): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createDescribedAssetInstance?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetInstanceID: utils.uuidToHex(assetInstanceID),
- assetDefinitionID: utils.uuidToHex(assetDefinitionID),
- descriptionHash,
- contentHash
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create described asset instance ${assetInstanceID}`, err);
- }
-};
-
-export const createAssetInstance = async (assetInstanceID: string, assetDefinitionID: string, author: string,
- contentHash: string, sync = false): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createAssetInstance?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetInstanceID: utils.uuidToHex(assetInstanceID),
- assetDefinitionID: utils.uuidToHex(assetDefinitionID),
- contentHash
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create asset instance ${assetInstanceID}`, err);
- }
-};
-
-export const createAssetInstanceBatch = async (batchHash: string, author: string, sync = false): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createAssetInstanceBatch?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- batchHash,
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create asset instance batch ${batchHash}`, err);
- }
-};
-
-export const setAssetInstanceProperty = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string,
- sync: boolean): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/setAssetInstanceProperty?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- assetDefinitionID: utils.uuidToHex(assetDefinitionID),
- assetInstanceID: utils.uuidToHex(assetInstanceID),
- key,
- value
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to set asset instance property ${key} (instance=${assetInstanceID})`, err);
- }
-};
-
-
-// Payment instance APIs
-
-export const createDescribedPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string,
- author: string, member: string, amount: number, descriptionHash: string, sync: boolean):
- Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createDescribedPaymentInstance?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentInstanceID: utils.uuidToHex(paymentInstanceID),
- paymentDefinitionID: utils.uuidToHex(paymentDefinitionID),
- member,
- amount,
- descriptionHash
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create described payment instance ${paymentInstanceID}`, err);
- }
-};
-
-export const createPaymentInstance = async (paymentInstanceID: string, paymentDefinitionID: string, author: string,
- member: string, amount: number, sync: boolean): Promise => {
- try {
- const response = await axios({
- method: 'post',
- url: `${config.apiGateway.apiEndpoint}/createPaymentInstance?kld-from=${author}&kld-sync=${sync}`,
- auth: {
- username: config.apiGateway.auth?.user ?? config.appCredentials.user,
- password: config.apiGateway.auth?.password ?? config.appCredentials.password
- },
- data: {
- paymentInstanceID: utils.uuidToHex(paymentInstanceID),
- paymentDefinitionID: utils.uuidToHex(paymentDefinitionID),
- member,
- amount
- }
- });
- return { ...response.data, type: sync ? 'sync' : 'async' };
- } catch (err) {
- return handleError(`Failed to create payment instance ${paymentInstanceID}`, err);
- }
-};
-
-function handleError(msg: string, err: any): Promise {
- const errMsg = err.response?.data?.error ?? err.response.data.message ?? err.toString();
- log.error(`${msg}. ${errMsg}`);
- throw new Error(msg);
-}
\ No newline at end of file
diff --git a/kat/src/clients/ipfs.ts b/kat/src/clients/ipfs.ts
deleted file mode 100644
index f504db29c0..0000000000
--- a/kat/src/clients/ipfs.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import FormData from 'form-data';
-import { Stream, Readable } from 'stream';
-import { constants } from '../lib/utils';
-import { config } from '../lib/config';
-import * as utils from '../lib/utils';
-
-export const init = async () => {
- try {
- const response = await utils.axiosWithRetry({
- url: `${config.ipfs.apiEndpoint}/api/v0/version`,
- method: 'post',
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- if (response.data.Version === undefined) {
- throw 'Invalid response';
- }
- } catch (err) {
- throw new Error(`IPFS Connection failed. ${err}`);
- }
-};
-
-export const downloadJSON = async (hash: string): Promise => {
- const response = await utils.axiosWithRetry({
- url: `${config.ipfs.gatewayEndpoint || config.ipfs.apiEndpoint}/ipfs/${hash}`,
- method: 'get',
- responseType: 'json',
- timeout: constants.IPFS_TIMEOUT_MS,
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return response.data;
-};
-
-export const uploadString = (value: string): Promise => {
- const readable = new Readable();
- readable.push(value);
- readable.push(null);
- return uploadStream(readable);
-};
-
-export const uploadStream = async (stream: Stream): Promise => {
- const formData = new FormData();
- formData.append('document', stream);
- const response = await utils.axiosWithRetry({
- url: `${config.ipfs.apiEndpoint}/api/v0/add`,
- method: 'post',
- data: formData,
- headers: formData.getHeaders(),
- auth: {
- username: config.appCredentials.user,
- password: config.appCredentials.password
- }
- });
- return response.data.Hash;
-};
diff --git a/kat/src/handlers/asset-definitions.ts b/kat/src/handlers/asset-definitions.ts
deleted file mode 100644
index c409639ab5..0000000000
--- a/kat/src/handlers/asset-definitions.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import Ajv from 'ajv';
-import * as utils from '../lib/utils';
-import * as ipfs from '../clients/ipfs';
-import * as apiGateway from '../clients/api-gateway';
-import * as database from '../clients/database';
-import RequestError from '../lib/request-handlers';
-import indexSchema from '../schemas/indexes.json'
-import assetDefinitionSchema from '../schemas/asset-definition.json'
-const log = utils.getLogger('handler/asset-definitions.ts');
-
-import {
- IDBBlockchainData,
- IDBAssetDefinition,
- IEventAssetDefinitionCreated,
- IAssetDefinitionRequest,
- indexes
-} from '../lib/interfaces';
-import { config } from '../lib/config';
-
-const ajv = new Ajv();
-
-export const handleGetAssetDefinitionsRequest = (query: object, skip: number, limit: number) => {
- return database.retrieveAssetDefinitions(query, skip, limit);
-};
-
-export const handleCountAssetDefinitionsRequest = async (query: object) => {
- return { count: await database.countAssetDefinitions(query) };
-};
-
-export const handleGetAssetDefinitionRequest = async (assetDefinitionID: string) => {
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Asset definition not found', 404);
- }
- return assetDefinition;
-};
-
-export const handleCreateAssetDefinitionRequest = async (assetDefinitionID: string, name: string, isContentPrivate: boolean, isContentUnique: boolean,
- author: string, descriptionSchema: Object | undefined, contentSchema: Object | undefined, indexes: { fields: string[], unique?: boolean }[] | undefined, sync: boolean) => {
- if (descriptionSchema !== undefined && !ajv.validateSchema(descriptionSchema)) {
- throw new RequestError('Invalid description schema', 400);
- }
- if (contentSchema !== undefined && !ajv.validateSchema(contentSchema)) {
- throw new RequestError('Invalid content schema', 400);
- }
- if (indexes !== undefined && !ajv.validate(indexSchema, indexes)) {
- throw new RequestError('Indexes do not conform to index schema', 400);
- }
- if (await database.retrieveAssetDefinitionByName(name) !== null) {
- throw new RequestError('Asset definition name conflict', 409);
- }
- const timestamp = utils.getTimestamp();
- const assetDefinition: IAssetDefinitionRequest = {
- assetDefinitionID,
- name,
- isContentPrivate,
- isContentUnique,
- descriptionSchema,
- contentSchema,
- indexes
- }
-
- let assetDefinitionHash: string;
- let receipt: string | undefined;
-
- switch (config.protocol) {
- case 'ethereum':
- assetDefinitionHash = utils.ipfsHashToSha256(await ipfs.uploadString(JSON.stringify(assetDefinition)));
- const apiGatewayResponse = await apiGateway.createAssetDefinition(author, assetDefinitionHash, sync);
- if (apiGatewayResponse.type === 'async') {
- receipt = apiGatewayResponse.id;
- }
- break;
- case 'corda':
- assetDefinitionHash = utils.getSha256(JSON.stringify(assetDefinition));
- await createCollection(assetDefinitionID, indexes);
- break;
- }
- await database.upsertAssetDefinition({
- assetDefinitionID,
- author,
- name,
- isContentPrivate,
- isContentUnique,
- descriptionSchema,
- assetDefinitionHash,
- contentSchema,
- receipt,
- indexes,
- submitted: timestamp
- });
- log.info(`New asset definition ${assetDefinitionID} from API request published to blockchain and added to local database`);
- return assetDefinitionID;
-};
-
-export const handleAssetDefinitionCreatedEvent = async (event: IEventAssetDefinitionCreated, { blockNumber, transactionHash }: IDBBlockchainData) => {
- let assetDefinition = await ipfs.downloadJSON(utils.sha256ToIPFSHash(event.assetDefinitionHash));
- if (!ajv.validate(assetDefinitionSchema, assetDefinition)) {
- throw new RequestError(`Invalid asset definition content ${JSON.stringify(ajv.errors)}`, 400);
- }
- const dbAssetDefinitionByID = await database.retrieveAssetDefinitionByID(assetDefinition.assetDefinitionID);
- if (dbAssetDefinitionByID !== null) {
- if (dbAssetDefinitionByID.transactionHash !== undefined) {
- throw new Error(`Asset definition ID conflict ${assetDefinition.assetDefinitionID}`);
- }
- } else {
- const dbAssetDefinitionByName = await database.retrieveAssetDefinitionByName(assetDefinition.name);
- if (dbAssetDefinitionByName !== null) {
- if (dbAssetDefinitionByName.transactionHash !== undefined) {
- throw new Error(`Asset definition name conflict ${dbAssetDefinitionByName.name}`);
- } else {
- await database.markAssetDefinitionAsConflict(dbAssetDefinitionByName.assetDefinitionID, Number(event.timestamp));
- }
- }
- }
-
- await database.upsertAssetDefinition({
- ...assetDefinition,
- author: event.author,
- assetDefinitionHash: event.assetDefinitionHash,
- timestamp: Number(event.timestamp),
- blockNumber,
- transactionHash
- });
- await createCollection(assetDefinition.assetDefinitionID, assetDefinition.indexes);
-
- log.info(`New asset definition ${assetDefinition.assetDefinitionID} from blockchain event added to local database`);
-};
-
-const createCollection = async (assetDefinitionID: string, assetDefinitionIndexes: indexes | undefined) => {
- const collectionName = `asset-instance-${assetDefinitionID}`;
- let indexes: indexes = [{ fields: ['assetInstanceID'], unique: true }, { fields: ['author'], unique: false }];
- if (assetDefinitionIndexes !== undefined) {
- indexes = indexes.concat(assetDefinitionIndexes)
- }
- await database.createCollection(collectionName, indexes);
-};
\ No newline at end of file
diff --git a/kat/src/handlers/asset-instances-pinning.ts b/kat/src/handlers/asset-instances-pinning.ts
deleted file mode 100644
index 873e406acb..0000000000
--- a/kat/src/handlers/asset-instances-pinning.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import * as apiGateway from '../clients/api-gateway';
-import * as ipfs from '../clients/ipfs';
-import { BatchManager } from '../lib/batch-manager';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponse, IAssetInstance, IAssetInstancePropertySet, IBatchRecord, IDBBatch, IPinnedBatch, BatchRecordType } from '../lib/interfaces';
-import * as utils from '../lib/utils';
-
-const log = utils.getLogger('lib/asset-instance-pinning.ts');
-
-export class AssetInstancesPinning {
-
- private batchManager = new BatchManager('asset-instances', this.processBatch.bind(this));
-
- public async init() {
- await this.batchManager.init();
- }
-
- public async pin(instance: IAssetInstance): Promise {
- const pinnedInstance: IBatchRecord = { recordType: BatchRecordType.assetInstance, ...instance };
- if (instance.isContentPrivate) delete pinnedInstance.content;
- const batchID = await this.batchManager.getProcessor(instance.author).add(pinnedInstance);
- log.trace(`Pinning initiated for asset ${instance.assetInstanceID}/${instance.assetInstanceID} in batch ${batchID}`);
- return batchID;
- }
-
- public async pinProperty(property: IAssetInstancePropertySet): Promise {
- const pinnedProperty: IBatchRecord = { recordType: BatchRecordType.assetProperty, ...property };
- const batchID = await this.batchManager.getProcessor(property.author).add(pinnedProperty);
- log.trace(`Pinning initiated for property ${property.assetInstanceID}/${property.assetInstanceID}/${property.key} in batch ${batchID}`);
- return batchID;
- }
-
- private async processBatch(batch: IDBBatch) {
- // Extract the hashable portion, and write it to IPFS, and store the hash
- const pinnedBatch: IPinnedBatch = {
- type: batch.type,
- created: batch.created,
- author: batch.author,
- completed: batch.completed,
- batchID: batch.batchID,
- records: batch.records,
- };
- batch.batchHash = utils.ipfsHashToSha256(await ipfs.uploadString(JSON.stringify(pinnedBatch)));;
-
- let apiGatewayResponse: IAPIGatewayAsyncResponse | IAPIGatewaySyncResponse;
- apiGatewayResponse = await apiGateway.createAssetInstanceBatch(batch.batchHash, batch.author, batch.participants);
- batch.receipt = apiGatewayResponse.type === 'async' ? apiGatewayResponse.id : undefined;
-
- // The batch processor who called us does the store back to the local MongoDB, as part of completing the batch
- }
-
-}
-
-/**
- * Singleton instance
- */
-export const assetInstancesPinning = new AssetInstancesPinning();
diff --git a/kat/src/handlers/asset-instances.ts b/kat/src/handlers/asset-instances.ts
deleted file mode 100644
index 5250599793..0000000000
--- a/kat/src/handlers/asset-instances.ts
+++ /dev/null
@@ -1,542 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import Ajv from 'ajv';
-import { v4 as uuidV4 } from 'uuid';
-import * as apiGateway from '../clients/api-gateway';
-import * as app2app from '../clients/app2app';
-import * as database from '../clients/database';
-import * as docExchange from '../clients/doc-exchange';
-import * as ipfs from '../clients/ipfs';
-import { config } from '../lib/config';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponse, IAssetInstance, IAssetInstancePropertySet, IAssetTradePrivateAssetInstancePush, IBatchRecord, IDBAssetInstance, IDBBlockchainData, IEventAssetInstanceBatchCreated, IEventAssetInstanceCreated, IEventAssetInstancePropertySet, IPendingAssetInstancePrivateContentDelivery, BatchRecordType } from '../lib/interfaces';
-import RequestError from '../lib/request-handlers';
-import * as utils from '../lib/utils';
-import { assetInstancesPinning } from './asset-instances-pinning';
-import * as assetTrade from './asset-trade';
-
-
-const log = utils.getLogger('handlers/asset-instances.ts');
-
-const ajv = new Ajv();
-
-export let pendingAssetInstancePrivateContentDeliveries: { [assetInstanceID: string]: IPendingAssetInstancePrivateContentDelivery } = {};
-
-export const handleGetAssetInstancesRequest = (assetDefinitionID: string, query: object, sort: object, skip: number, limit: number) => {
- return database.retrieveAssetInstances(assetDefinitionID, query, sort, skip, limit);
-};
-
-export const handleCountAssetInstancesRequest = async (assetDefinitionID: string, query: object) => {
- return { count: await database.countAssetInstances(assetDefinitionID, query) };
-};
-
-export const handleGetAssetInstanceRequest = async (assetDefinitionID: string, assetInstanceID: string, content: boolean) => {
- const assetInstance = await database.retrieveAssetInstanceByID(assetDefinitionID, assetInstanceID);
- if (assetInstance === null) {
- throw new RequestError('Asset instance not found', 404);
- }
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Asset definition not found', 500);
- }
- if (content) {
- if (assetDefinition.contentSchema) {
- return assetInstance.content;
- } else {
- try {
- return await docExchange.downloadStream(utils.getUnstructuredFilePathInDocExchange(assetInstance.assetInstanceID));
- } catch (err) {
- if (err.response?.status === 404) {
- throw new RequestError('Asset instance content not present in off-chain storage', 404);
- } else {
- throw new RequestError(`Failed to obtain asset content from off-chain storage. ${err}`, 500);
- }
- }
- }
- }
- return assetInstance;
-};
-
-export const handleCreateStructuredAssetInstanceRequest = async (author: string, assetDefinitionID: string, description: Object | undefined, content: Object, isContentPrivate: boolean | undefined, participants: string[] | undefined, sync: boolean) => {
- let descriptionHash: string | undefined;
- let contentHash: string;
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Unknown asset definition', 400);
- }
- if (assetDefinition.conflict === true) {
- throw new RequestError('Cannot instantiate assets of conflicted definition', 400);
- }
- // For ethereum, we need to make assert definition transaction is mined
- if (config.protocol === 'ethereum' && assetDefinition.transactionHash === undefined) {
- throw new RequestError('Asset definition transaction must be mined', 400);
- }
- if (!assetDefinition.contentSchema) {
- throw new RequestError('Unstructured asset instances must be created using multipart/form-data', 400);
- }
- if (assetDefinition.descriptionSchema) {
- if (!description) {
- throw new RequestError('Missing asset description', 400);
- }
- if (!ajv.validate(assetDefinition.descriptionSchema, description)) {
- throw new RequestError('Description does not conform to asset definition schema', 400);
- }
- descriptionHash = `0x${utils.getSha256(JSON.stringify(description))}`;
- }
- if (!ajv.validate(assetDefinition.contentSchema, content)) {
- throw new RequestError('Content does not conform to asset definition schema', 400);
- }
- if(isContentPrivate === undefined) {
- isContentPrivate = assetDefinition.isContentPrivate;
- }
- contentHash = `0x${utils.getSha256(JSON.stringify(content))}`;
- if (assetDefinition.isContentUnique && (await database.retrieveAssetInstanceByDefinitionIDAndContentHash(assetDefinition.assetDefinitionID, contentHash)) !== null) {
- throw new RequestError(`Asset instance content conflict`);
- }
- if (config.protocol === 'corda') {
- // validate participants are registered members
- if (participants !== undefined) {
- for (const participant of participants) {
- if (await database.retrieveMemberByAddress(participant) === null) {
- throw new RequestError('One or more participants are not registered', 400);
- }
- }
- } else {
- throw new RequestError('Missing asset participants', 400);
- }
- }
- const assetInstanceID = uuidV4();
- const timestamp = utils.getTimestamp();
- const assetInstance: IAssetInstance = {
- assetInstanceID,
- author,
- assetDefinitionID,
- descriptionHash,
- description,
- contentHash,
- content,
- isContentPrivate
- };
-
- let dbAssetInstance: IDBAssetInstance = assetInstance;
- dbAssetInstance.submitted = timestamp;
- if (config.protocol === 'corda') {
- dbAssetInstance.participants = participants;
- }
- // If there are public IPFS shared parts of this instance, we can batch it together with all other
- // assets we are publishing for performance. Reducing both the data we write to the blockchain, and
- // most importantly the number of IPFS transactions.
- // Curently we do batch only for ethereum
- if ((assetDefinition.descriptionSchema || !isContentPrivate) && config.protocol === 'ethereum') {
- dbAssetInstance.batchID = await assetInstancesPinning.pin(assetInstance);
- await database.upsertAssetInstance(dbAssetInstance);
- log.info(`Structured asset instance batch ${dbAssetInstance.batchID} saved in local database and pinned to blockchain`);
- } else {
- await database.upsertAssetInstance(dbAssetInstance);
- // One-for-one blockchain transactions to instances
- let apiGatewayResponse: IAPIGatewayAsyncResponse | IAPIGatewaySyncResponse;
- if (descriptionHash) {
- apiGatewayResponse = await apiGateway.createDescribedAssetInstance(assetInstanceID, assetDefinitionID, author, descriptionHash, contentHash, participants, sync);
- } else {
- apiGatewayResponse = await apiGateway.createAssetInstance(assetInstanceID, assetDefinitionID, author, contentHash, participants, sync);
- }
- log.info(`Structured asset instance ${assetInstanceID} saved in local database and pinning transaction submitted to the blockchain`);
- // dbAssetInstance.receipt = apiGatewayResponse.type === 'async' ? apiGatewayResponse.id : undefined;
- if(apiGatewayResponse.type === 'async') {
- await database.setAssetInstanceReceipt(assetDefinitionID, assetInstanceID, apiGatewayResponse.id);
- log.trace(`Structured asset instance ${assetInstanceID} published in the blockchain (gateway receipt=${apiGatewayResponse.id})`);
- }
- }
- return assetInstanceID;
-};
-
-export const handleCreateUnstructuredAssetInstanceRequest = async (author: string, assetDefinitionID: string, description: Object | undefined, content: NodeJS.ReadableStream, filename: string, isContentPrivate: boolean | undefined, participants: string[] | undefined, sync: boolean) => {
- let descriptionHash: string | undefined;
- let contentHash: string;
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Unknown asset definition', 400);
- }
- if (assetDefinition.contentSchema) {
- throw new RequestError('Structured asset instances must be created using JSON', 400);
- }
- if (assetDefinition.descriptionSchema) {
- if (!ajv.validate(assetDefinition.descriptionSchema, description)) {
- throw new RequestError('Description does not conform to asset definition schema', 400);
- }
- descriptionHash = utils.ipfsHashToSha256(await ipfs.uploadString(JSON.stringify(description)));
- }
- if(isContentPrivate === undefined) {
- isContentPrivate = assetDefinition.isContentPrivate;
- }
- const assetInstanceID = uuidV4();
- if (assetDefinition.isContentPrivate) {
- contentHash = `0x${await docExchange.uploadStream(content, utils.getUnstructuredFilePathInDocExchange(assetInstanceID))}`;
- } else {
- contentHash = utils.ipfsHashToSha256(await ipfs.uploadString(JSON.stringify(content)));
- }
- if (assetDefinition.isContentUnique && (await database.retrieveAssetInstanceByDefinitionIDAndContentHash(assetDefinitionID, contentHash)) !== null) {
- throw new RequestError('Asset instance content conflict', 409);
- }
- if (config.protocol === 'corda') {
- // validate participants are registered
- if (participants) {
- for (const participant of participants) {
- if (await database.retrieveMemberByAddress(participant) === null) {
- throw new RequestError(`One or more participants are not registered`, 400);
- }
- }
- } else {
- throw new RequestError(`Missing asset participants`, 400);
- }
- }
- let apiGatewayResponse: IAPIGatewayAsyncResponse | IAPIGatewaySyncResponse;
- const timestamp = utils.getTimestamp();
- await database.upsertAssetInstance({
- assetInstanceID,
- author,
- assetDefinitionID,
- descriptionHash,
- description,
- contentHash,
- filename,
- isContentPrivate,
- participants,
- submitted: timestamp
- });
- if (descriptionHash) {
- apiGatewayResponse = await apiGateway.createDescribedAssetInstance(assetInstanceID, assetDefinitionID, author, descriptionHash, contentHash, participants, sync);
- } else {
- apiGatewayResponse = await apiGateway.createAssetInstance(assetInstanceID, assetDefinitionID, author, contentHash, participants, sync);
- }
- log.info(`Unstructured asset instance ${assetInstanceID} saved in local database and pinning transaction submitted to the blockchain`);
- if(apiGatewayResponse.type === 'async') {
- await database.setAssetInstanceReceipt(assetDefinitionID, assetInstanceID, apiGatewayResponse.id);
- log.trace(`Unstructured asset instance ${assetInstanceID} published in the blockchain (gateway receipt=${apiGatewayResponse.id})`);
- }
- return assetInstanceID;
-}
-
-export const handleSetAssetInstancePropertyRequest = async (assetDefinitionID: string, assetInstanceID: string, author: string, key: string, value: string, sync: boolean) => {
- const assetInstance = await database.retrieveAssetInstanceByID(assetDefinitionID, assetInstanceID);
- if (assetInstance === null) {
- throw new RequestError('Unknown asset instance', 400);
- }
- if (assetInstance.transactionHash === undefined) {
- throw new RequestError('Asset instance transaction must be mined', 400);
- }
- if (assetInstance.properties) {
- const authorMetadata = assetInstance.properties[author];
- if (authorMetadata) {
- const valueData = authorMetadata[key];
- if (valueData?.value === value && valueData.history !== undefined) {
- const keys = Object.keys(valueData.history);
- const lastConfirmedValue = valueData.history[keys[keys.length - 1]];
- if (lastConfirmedValue.value === value) {
- throw new RequestError('Property already set', 409);
- }
- }
- }
- }
- const submitted = utils.getTimestamp();
- if (config.protocol === 'ethereum') {
- const property: IAssetInstancePropertySet = {
- assetDefinitionID,
- assetInstanceID,
- author,
- key,
- value,
- };
- const batchID = await assetInstancesPinning.pinProperty(property);
- await database.setSubmittedAssetInstanceProperty(assetDefinitionID, assetInstanceID, author, key, value, submitted, batchID);
- log.info(`Asset instance property ${key} (instance=${assetInstanceID}) set via batch`);
- } else {
- await database.setSubmittedAssetInstanceProperty(assetDefinitionID, assetInstanceID, author, key, value, submitted);
- log.info(`Asset instance property ${key} (instance=${assetInstanceID}) set in local database`);
- const apiGatewayResponse = await apiGateway.setAssetInstanceProperty(assetDefinitionID, assetInstanceID, author, key, value, assetInstance.participants, sync);
- if(apiGatewayResponse.type === 'async') {
- await database.setAssetInstancePropertyReceipt(assetDefinitionID, assetInstanceID, author, key, apiGatewayResponse.id);
- }
- log.info(`Asset instance property ${key} (instance=${assetInstanceID}) pinning transaction submitted to blockchain`);
- }
-
-};
-
-export const handleAssetInstanceBatchCreatedEvent = async (event: IEventAssetInstanceBatchCreated, { blockNumber, transactionHash }: IDBBlockchainData) => {
-
- let batch = await database.retrieveBatchByHash(event.batchHash);
- if (!batch) {
- batch = await ipfs.downloadJSON(utils.sha256ToIPFSHash(event.batchHash));
- }
- if (!batch) {
- throw new Error('Unknown batch hash: ' + event.batchHash);
- }
-
- // Process each record within the batch, as if it is an individual event
- const records: IBatchRecord[] = batch.records || [];
- for (let record of records) {
- if (!record.recordType || record.recordType === BatchRecordType.assetInstance) {
- const recordEvent: IEventAssetInstanceCreated = {
- assetDefinitionID: '',
- assetInstanceID: '',
- author: record.author,
- contentHash: record.contentHash!,
- descriptionHash: record.descriptionHash!,
- timestamp: event.timestamp,
- isContentPrivate: record.isContentPrivate
- };
- try {
- await handleAssetInstanceCreatedEvent(recordEvent, { blockNumber, transactionHash }, record);
- } catch (err) {
- // We failed to process this record, but continue to attempt the other records in the batch
- log.error(`Record ${record.assetDefinitionID}/${record.assetInstanceID} in batch ${batch.batchID} with hash ${event.batchHash} failed`, err.stack);
- }
- } else if (record.recordType === BatchRecordType.assetProperty) {
- try {
- const propertyEvent: IEventAssetInstancePropertySet = {
- assetDefinitionID: record.assetDefinitionID,
- assetInstanceID: record.assetInstanceID,
- author: record.author,
- key: record.key,
- value: record.value,
- timestamp: event.timestamp,
- };
- await handleSetAssetInstancePropertyEvent(propertyEvent, { blockNumber, transactionHash }, true);
- } catch (err) {
- // We failed to process this record, but continue to attempt the other records in the batch
- log.error(`Property ${record.assetDefinitionID}/${record.assetInstanceID}/${record.key} in batch ${batch.batchID} with hash ${event.batchHash} failed`, err.stack);
- }
- } else {
- log.error(`Batch record type '${record.recordType}' not known`, record);
- }
- }
-
- // Write the batch itself to our local database
- await database.upsertBatch({
- ...batch,
- timestamp: Number(event.timestamp),
- blockNumber,
- transactionHash
- });
- log.info(`Asset instance batch ${event.batchHash} from blockchain event (blockNumber=${blockNumber} hash=${transactionHash}) saved in local database`);
-
-}
-
-export const handleAssetInstanceCreatedEvent = async (event: IEventAssetInstanceCreated, { blockNumber, transactionHash }: IDBBlockchainData, batchInstance?: IBatchRecord) => {
- let eventAssetInstanceID: string;
- let eventAssetDefinitionID: string;
- if (batchInstance === undefined) {
- switch (config.protocol) {
- case 'corda':
- eventAssetInstanceID = event.assetInstanceID;
- eventAssetDefinitionID = event.assetDefinitionID;
- break;
- case 'ethereum':
- eventAssetInstanceID = utils.hexToUuid(event.assetInstanceID);
- eventAssetDefinitionID = utils.hexToUuid(event.assetDefinitionID);
- break;
- }
- } else {
- eventAssetInstanceID = batchInstance.assetInstanceID;
- eventAssetDefinitionID = batchInstance.assetDefinitionID;
- log.info(`batch instance ${eventAssetDefinitionID}:${eventAssetInstanceID}`);
- }
- const dbAssetInstance = await database.retrieveAssetInstanceByID(eventAssetDefinitionID, eventAssetInstanceID);
- if (dbAssetInstance !== null && dbAssetInstance.transactionHash !== undefined) {
- throw new Error(`Duplicate asset instance ID`);
- }
- const assetDefinition = await database.retrieveAssetDefinitionByID(eventAssetDefinitionID);
- if (assetDefinition === null) {
- throw new Error('Unkown asset definition');
- }
- // For ethereum, we need to make asset definition transaction is mined
- if (config.protocol === 'ethereum' && assetDefinition.transactionHash === undefined) {
- throw new Error('Asset definition transaction must be mined');
- }
- if (assetDefinition.isContentUnique) {
- const assetInstanceByContentID = await database.retrieveAssetInstanceByDefinitionIDAndContentHash(eventAssetDefinitionID, event.contentHash);
- if (assetInstanceByContentID !== null && eventAssetInstanceID !== assetInstanceByContentID.assetInstanceID) {
- if (assetInstanceByContentID.transactionHash !== undefined) {
- throw new Error(`Asset instance content conflict ${event.contentHash}`);
- } else {
- await database.markAssetInstanceAsConflict(eventAssetDefinitionID, assetInstanceByContentID.assetInstanceID, Number(event.timestamp));
- }
- }
- }
- let description: Object | undefined = batchInstance?.description;
- if (assetDefinition.descriptionSchema && !description) {
- if (event.descriptionHash) {
- if (event.descriptionHash === dbAssetInstance?.descriptionHash) {
- description = dbAssetInstance.description;
- } else {
- description = await ipfs.downloadJSON(utils.sha256ToIPFSHash(event.descriptionHash));
- if (!ajv.validate(assetDefinition.descriptionSchema, description)) {
- throw new Error('Description does not conform to schema');
- }
- }
- } else {
- throw new Error('Missing asset instance description');
- }
- }
- let content: Object | undefined = batchInstance?.content;
- if (assetDefinition.contentSchema && !content) {
- if (event.contentHash === dbAssetInstance?.contentHash) {
- content = dbAssetInstance.content;
- } else if (!assetDefinition.isContentPrivate) {
- content = await ipfs.downloadJSON(utils.sha256ToIPFSHash(event.contentHash));
- if (!ajv.validate(assetDefinition.contentSchema, content)) {
- throw new Error('Content does not conform to schema');
- }
- }
- }
- log.trace(`Updating asset instance ${eventAssetInstanceID} with blockchain pinned info blockNumber=${blockNumber} hash=${transactionHash}`);
- let assetInstanceDB: IDBAssetInstance = {
- assetInstanceID: eventAssetInstanceID,
- author: event.author,
- assetDefinitionID: assetDefinition.assetDefinitionID,
- descriptionHash: event.descriptionHash,
- description,
- contentHash: event.contentHash,
- timestamp: Number(event.timestamp),
- content,
- blockNumber,
- transactionHash,
- isContentPrivate: event.isContentPrivate ?? assetDefinition.isContentPrivate
- };
- if (config.protocol === 'corda') {
- assetInstanceDB.participants = event.participants;
- }
- await database.upsertAssetInstance(assetInstanceDB);
- if (assetInstanceDB.isContentPrivate) {
- const privateData = pendingAssetInstancePrivateContentDeliveries[eventAssetInstanceID];
- if (privateData !== undefined) {
- const author = await database.retrieveMemberByAddress(event.author);
- if (author === null) {
- throw new Error('Pending private data author unknown');
- }
- if (author.app2appDestination !== privateData.fromDestination) {
- throw new Error('Pending private data destination mismatch');
- }
- if (privateData.content !== undefined) {
- const privateDataHash = `0x${utils.getSha256(JSON.stringify(privateData.content))}`;
- if (privateDataHash !== event.contentHash) {
- throw new Error('Pending private data content hash mismatch');
- }
- }
- await database.setAssetInstancePrivateContent(eventAssetDefinitionID, eventAssetInstanceID, privateData.content, privateData.filename);
- delete pendingAssetInstancePrivateContentDeliveries[eventAssetInstanceID];
- }
- }
- log.info(`Asset instance ${eventAssetDefinitionID}/${eventAssetInstanceID} from blockchain event (blockNumber=${blockNumber} hash=${transactionHash}) saved in local database`);
-};
-
-export const handleSetAssetInstancePropertyEvent = async (event: IEventAssetInstancePropertySet, blockchainData: IDBBlockchainData, isBatch?: boolean) => {
- let eventAssetInstanceID: string;
- let eventAssetDefinitionID: string;
- if (config.protocol === 'corda' || isBatch) {
- eventAssetInstanceID = event.assetInstanceID;
- eventAssetDefinitionID = event.assetDefinitionID;
- } else {
- eventAssetInstanceID = utils.hexToUuid(event.assetInstanceID);
- eventAssetDefinitionID = utils.hexToUuid(event.assetDefinitionID);
- }
- const dbAssetInstance = await database.retrieveAssetInstanceByID(eventAssetDefinitionID, eventAssetInstanceID);
- if (dbAssetInstance === null) {
- throw new Error('Uknown asset instance');
- }
- if (dbAssetInstance.transactionHash === undefined) {
- throw new Error('Unconfirmed asset instance');
- }
- if (!event.key) {
- throw new Error('Invalid property key');
- }
- await database.setConfirmedAssetInstanceProperty(eventAssetDefinitionID, eventAssetInstanceID, event.author, event.key, event.value, Number(event.timestamp), blockchainData);
- log.info(`Asset instance property ${event.key} (instance=${eventAssetDefinitionID}) from blockchain event (blockNumber=${blockchainData.blockNumber} hash=${blockchainData.transactionHash}) saved in local database`);
-};
-
-export const handleAssetInstanceTradeRequest = async (assetDefinitionID: string, requesterAddress: string, assetInstanceID: string, metadata: object | undefined) => {
- const assetInstance = await database.retrieveAssetInstanceByID(assetDefinitionID, assetInstanceID);
- if (assetInstance === null) {
- throw new RequestError('Uknown asset instance', 404);
- }
- const author = await database.retrieveMemberByAddress(assetInstance.author);
- if (author === null) {
- throw new RequestError('Asset author must be registered', 400);
- }
- if (author.assetTrailInstanceID === config.assetTrailInstanceID) {
- throw new RequestError('Asset instance authored', 400);
- }
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Unknown asset definition', 500);
- }
- if (assetDefinition.contentSchema !== undefined) {
- if (assetInstance.content !== undefined) {
- throw new RequestError('Asset content already available', 400);
- }
- } else {
- try {
- const documentDetails = await docExchange.getDocumentDetails(utils.getUnstructuredFilePathInDocExchange(assetInstanceID));
- if (documentDetails.hash === assetInstance.contentHash) {
- throw new RequestError('Asset content already available', 400);
- }
- } catch (err) {
- if (err.response?.status !== 404) {
- throw new RequestError(err, 500);
- }
- }
- }
- const requester = await database.retrieveMemberByAddress(requesterAddress);
- if (requester === null) {
- throw new RequestError('Requester must be registered', 400);
- }
- await assetTrade.coordinateAssetTrade(assetInstance, assetDefinition, requester.address, metadata, author.app2appDestination);
- log.info(`Asset instance trade request from requester ${requesterAddress} (instance=${assetInstanceID}) successfully completed`);
-};
-
-export const handlePushPrivateAssetInstanceRequest = async (assetDefinitionID: string, assetInstanceID: string, memberAddress: string) => {
- const member = await database.retrieveMemberByAddress(memberAddress);
- if (member === null) {
- throw new RequestError('Unknown member', 400);
- }
- const assetInstance = await database.retrieveAssetInstanceByID(assetDefinitionID, assetInstanceID);
- if (assetInstance === null) {
- throw new RequestError('Unknown asset instance', 400);
- }
- const author = await database.retrieveMemberByAddress(assetInstance.author);
- if (author === null) {
- throw new RequestError('Unknown asset author', 500);
- }
- if (author.assetTrailInstanceID !== config.assetTrailInstanceID) {
- throw new RequestError('Must be asset instance author', 403);
- }
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetInstance.assetDefinitionID);
- if (assetDefinition === null) {
- throw new RequestError('Unknown asset definition', 500);
- }
- let privateAssetTradePrivateInstancePush: IAssetTradePrivateAssetInstancePush = {
- type: 'private-asset-instance-push',
- assetInstanceID,
- assetDefinitionID
- };
- if (assetDefinition.contentSchema !== undefined) {
- privateAssetTradePrivateInstancePush.content = assetInstance.content;
- } else {
- await docExchange.transfer(author.docExchangeDestination, member.docExchangeDestination,
- utils.getUnstructuredFilePathInDocExchange(assetInstanceID));
- privateAssetTradePrivateInstancePush.filename = assetInstance.filename;
- log.info(`Private asset instance push request for member ${memberAddress} (instance=${assetInstanceID}) successfully completed`);
- }
- app2app.dispatchMessage(member.app2appDestination, privateAssetTradePrivateInstancePush);
-};
diff --git a/kat/src/handlers/asset-trade.ts b/kat/src/handlers/asset-trade.ts
deleted file mode 100644
index f8a266899f..0000000000
--- a/kat/src/handlers/asset-trade.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { v4 as uuidV4 } from 'uuid';
-import Ajv from 'ajv';
-import { config } from '../lib/config';
-import { AssetTradeMessage, IApp2AppMessageHeader, IApp2AppMessageListener, IAssetTradePrivateAssetInstanceAuthorizationRequest, IAssetTradePrivateAssetInstancePush, IAssetTradePrivateAssetInstanceRequest, IAssetTradePrivateAssetInstanceResponse, IDBAssetDefinition, IDBAssetInstance, IDBMember, IDocExchangeListener, IDocExchangeTransferData } from "../lib/interfaces";
-import * as utils from '../lib/utils';
-import * as database from '../clients/database';
-import * as app2app from '../clients/app2app';
-import * as docExchange from '../clients/doc-exchange';
-import { pendingAssetInstancePrivateContentDeliveries } from './asset-instances';
-const log = utils.getLogger('handlers/asset-trade.ts');
-
-const ajv = new Ajv();
-
-export const assetTradeHandler = (headers: IApp2AppMessageHeader, content: AssetTradeMessage) => {
- if (content.type === 'private-asset-instance-request') {
- processPrivateAssetInstanceRequest(headers, content);
- } else if (content.type === 'private-asset-instance-push') {
- processPrivateAssetInstancePush(headers, content);
- }
-};
-
-const processPrivateAssetInstanceRequest = async (headers: IApp2AppMessageHeader, request: IAssetTradePrivateAssetInstanceRequest) => {
- let tradeResponse: IAssetTradePrivateAssetInstanceResponse = {
- type: "private-asset-instance-response",
- tradeID: request.tradeID,
- assetInstanceID: request.assetInstanceID
- };
- const requester = await database.retrieveMemberByAddress(request.requester.address);
- try {
- if (requester === null) {
- throw new Error(`Unknown requester ${request.requester.address}`);
- }
- if (requester.assetTrailInstanceID !== request.requester.assetTrailInstanceID) {
- throw new Error(`Requester asset trail instance mismatch. Expected ${requester.assetTrailInstanceID}, ` +
- `actual ${request.requester.assetTrailInstanceID}`);
- }
- if (requester.app2appDestination !== headers.from) {
- throw new Error(`Requester App2App destination mismatch. Expected ${requester.app2appDestination}, ` +
- `actual ${headers.from}`);
- }
- const assetInstance = await database.retrieveAssetInstanceByID(request.assetDefinitionID, request.assetInstanceID);
- if (assetInstance === null) {
- throw new Error(`Unknown asset instance ${request.assetInstanceID}`);
- }
- const author = await database.retrieveMemberByAddress(assetInstance.author);
- if (author === null) {
- throw new Error(`Unknown asset instance author`);
- }
- if (author.assetTrailInstanceID !== config.assetTrailInstanceID) {
- throw new Error(`Asset instance ${assetInstance.assetInstanceID} not authored`);
- }
- const assetDefinition = await database.retrieveAssetDefinitionByID(assetInstance.assetDefinitionID);
- if (assetDefinition === null) {
- throw new Error(`Unknown asset definition ${assetInstance.assetDefinitionID}`);
- }
- if (!assetDefinition.isContentPrivate) {
- throw new Error(`Asset instance ${assetInstance.assetInstanceID} not private`);
- }
- const authorized = await handlePrivateAssetInstanceAuthorization(assetInstance, requester, request.metadata);
- if (authorized !== true) {
- throw new Error('Access denied');
- }
- if (assetDefinition.contentSchema) {
- tradeResponse.content = assetInstance.content;
- } else {
- await docExchange.transfer(config.docExchange.destination, requester.docExchangeDestination,
- utils.getUnstructuredFilePathInDocExchange(request.assetInstanceID));
- tradeResponse.filename = assetInstance.filename;
- log.info(`Private asset instance trade request (instance=${assetInstance.assetDefinitionID}, requester=${request.requester.address}, tradeId=${request.tradeID}) successfully completed`);
- }
- } catch (err) {
- tradeResponse.rejection = err.message;
- } finally {
- app2app.dispatchMessage(headers.from, tradeResponse);
- }
-};
-
-const handlePrivateAssetInstanceAuthorization = (assetInstance: IDBAssetInstance, requester: IDBMember, metadata: object | undefined): Promise => {
- return new Promise((resolve, reject) => {
- const authorizationID = uuidV4();
- const authorizationRequest: IAssetTradePrivateAssetInstanceAuthorizationRequest = {
- type: 'private-asset-instance-authorization-request',
- authorizationID,
- assetInstance,
- requester,
- metadata
- };
- const timeout = setTimeout(() => {
- app2app.removeListener(app2appListener);
- reject(new Error('Asset instance authorization response timed out'));
- }, utils.constants.TRADE_AUTHORIZATION_TIMEOUT_SECONDS * 1000);
- const app2appListener: IApp2AppMessageListener = (headers: IApp2AppMessageHeader, content: AssetTradeMessage) => {
- if (headers.from === config.app2app.destinations.client && content.type === 'private-asset-instance-authorization-response' &&
- content.authorizationID === authorizationID) {
- clearTimeout(timeout);
- app2app.removeListener(app2appListener);
- resolve(content.authorized);
- }
- };
- app2app.addListener(app2appListener);
- app2app.dispatchMessage(config.app2app.destinations.client, authorizationRequest);
- });
-};
-
-export const coordinateAssetTrade = async (assetInstance: IDBAssetInstance, assetDefinition: IDBAssetDefinition,
- requesterAddress: string, metadata: object | undefined, authorDestination: string) => {
- const tradeID = uuidV4();
- const tradeRequest: IAssetTradePrivateAssetInstanceRequest = {
- type: 'private-asset-instance-request',
- tradeID,
- assetInstanceID: assetInstance.assetInstanceID,
- assetDefinitionID: assetInstance.assetDefinitionID,
- requester: {
- assetTrailInstanceID: config.assetTrailInstanceID,
- address: requesterAddress
- },
- metadata
- };
- const docExchangePromise = assetDefinition.contentSchema === undefined ? getDocumentExchangePromise(assetInstance.assetInstanceID) : Promise.resolve();
- const app2appPromise: Promise = new Promise((resolve, reject) => {
- const timeout = setTimeout(() => {
- app2app.removeListener(app2appListener);
- reject(new Error('Asset instance author response timed out'));
- }, utils.constants.ASSET_INSTANCE_TRADE_TIMEOUT_SECONDS * 1000);
- const app2appListener: IApp2AppMessageListener = (_headers: IApp2AppMessageHeader, content: AssetTradeMessage) => {
- if (content.type === 'private-asset-instance-response' && content.tradeID === tradeID) {
- clearTimeout(timeout);
- app2app.removeListener(app2appListener);
- if (content.rejection) {
- reject(new Error(`Asset instance request rejected. ${content.rejection}`));
- } else {
- const contentHash = `0x${utils.getSha256(JSON.stringify(content.content))}`;
- if (contentHash !== assetInstance.contentHash) {
- reject(new Error('Asset instance content hash mismatch'));
- } else if (assetDefinition.contentSchema && !ajv.validate(assetDefinition.contentSchema, content.content)) {
- reject(new Error('Asset instance content does not conform to schema'));
- } else {
- database.setAssetInstancePrivateContent(assetInstance.assetDefinitionID, content.assetInstanceID, content.content, content.filename);
- resolve();
- }
- }
- }
- };
- app2app.addListener(app2appListener);
- app2app.dispatchMessage(authorDestination, tradeRequest);
- });
- await Promise.all([app2appPromise, docExchangePromise]);
-};
-
-const getDocumentExchangePromise = (assetInstanceID: string): Promise => {
- return new Promise((resolve, reject) => {
- const timeout = setTimeout(() => {
- docExchange.removeListener(docExchangeListener);
- reject(new Error('Off-chain asset transfer timeout'));
- }, utils.constants.DOCUMENT_EXCHANGE_TRANSFER_TIMEOUT_SECONDS * 1000);
- const docExchangeListener: IDocExchangeListener = (event: IDocExchangeTransferData) => {
- if (event.document === utils.getUnstructuredFilePathInDocExchange(assetInstanceID)) {
- clearTimeout(timeout);
- docExchange.removeListener(docExchangeListener);
- resolve();
- }
- };
- docExchange.addListener(docExchangeListener);
- });
-};
-
-const processPrivateAssetInstancePush = async (headers: IApp2AppMessageHeader, push: IAssetTradePrivateAssetInstancePush) => {
- log.trace(`Handling private asset instance push event (instance=${push.assetInstanceID}, filename=${push.filename})`);
- const assetInstance = await database.retrieveAssetInstanceByID(push.assetDefinitionID, push.assetInstanceID);
- if (assetInstance !== null) {
- log.trace(`Found existing asset instance, ${JSON.stringify(assetInstance, null, 2)}`);
- const author = await database.retrieveMemberByAddress(assetInstance.author);
- if (author === null) {
- throw new Error(`Unknown author for asset ${assetInstance.assetInstanceID}`);
- }
- if (author.app2appDestination !== headers.from) {
- throw new Error(`Asset instance author destination mismatch ${author.app2appDestination} - ${headers.from}`);
- }
- if (push.content) {
- const contentHash = `0x${utils.getSha256(JSON.stringify(push.content))}`;
- if (assetInstance.contentHash !== contentHash) {
- throw new Error('Private asset content hash mismatch');
- }
- }
- await database.setAssetInstancePrivateContent(push.assetDefinitionID, push.assetInstanceID, push.content, push.filename);
- log.info(`Private asset instance from push event (instance=${push.assetInstanceID}, filename=${push.filename}) saved in local database`);
- } else {
- log.info(`Private asset instance ${push.assetDefinitionID}/${push.assetInstanceID} from push event not found in local database, adding to pending instances`);
- pendingAssetInstancePrivateContentDeliveries[push.assetInstanceID] = { ...push, fromDestination: headers.from };
- }
-}
diff --git a/kat/src/handlers/batches.ts b/kat/src/handlers/batches.ts
deleted file mode 100644
index 74170c5e8e..0000000000
--- a/kat/src/handlers/batches.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import * as database from '../clients/database';
-import RequestError from '../lib/request-handlers';
-
-export const handleGetBatchRequest = async (batchID: string) => {
- const batch = await database.retrieveBatchByID(batchID);
- if (batch === null) {
- throw new RequestError('Asset instance not found', 404);
- }
- return batch;
-};
diff --git a/kat/src/handlers/client-events.ts b/kat/src/handlers/client-events.ts
deleted file mode 100644
index 409db2f647..0000000000
--- a/kat/src/handlers/client-events.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { ClientEventType } from "../lib/interfaces";
-import { config } from '../lib/config';
-import { settings } from '../lib/settings';
-import * as utils from '../lib/utils';
-import * as app2app from '../clients/app2app';
-
-const log = utils.getLogger('handlers/client-events.ts');
-
-export const clientEventHandler = (eventType: ClientEventType, content: object) => {
- if (settings.clientEvents.includes(eventType)) {
- log.info(`Dispatched client event ${eventType}`);
- app2app.dispatchMessage(config.app2app.destinations.client, { type: eventType, content });
- }
-};
diff --git a/kat/src/handlers/members.ts b/kat/src/handlers/members.ts
deleted file mode 100644
index 901b6a0dad..0000000000
--- a/kat/src/handlers/members.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import * as database from '../clients/database';
-import * as apiGateway from '../clients/api-gateway';
-import * as utils from '../lib/utils';
-import { IDBBlockchainData, IDBMember, IEventMemberRegistered } from '../lib/interfaces';
-import RequestError from '../lib/request-handlers';
-import { config } from '../lib/config';
-
-export const handleGetMembersRequest = (query: object, skip: number, limit: number) => {
- return database.retrieveMembers(query, skip, limit);
-};
-
-export const handleGetMemberRequest = async (address: string) => {
- const member = await database.retrieveMemberByAddress(address);
- if (member === null) {
- throw new RequestError('Member not found', 404);
- }
- return member;
-};
-
-export const handleUpsertMemberRequest = async (address: string, name: string, assetTrailInstanceID: string, app2appDestination: string, docExchangeDestination: string, sync: boolean) => {
- const timestamp = utils.getTimestamp();
- let memberDB: IDBMember = {
- address,
- name,
- assetTrailInstanceID,
- app2appDestination,
- docExchangeDestination,
- submitted: timestamp
- };
- if(config.protocol === 'ethereum') {
- const apiGatewayResponse = await apiGateway.upsertMember(address, name, app2appDestination, docExchangeDestination, sync);
- if(apiGatewayResponse.type === 'async') {
- memberDB.receipt = apiGatewayResponse.id
- }
- }
- await database.upsertMember(memberDB);
-};
-
-export const handleMemberRegisteredEvent = async ({ member, name, assetTrailInstanceID, app2appDestination, docExchangeDestination, timestamp }:
- IEventMemberRegistered, { blockNumber, transactionHash}: IDBBlockchainData) => {
- await database.upsertMember({
- address: member,
- name,
- app2appDestination,
- docExchangeDestination,
- assetTrailInstanceID,
- timestamp: Number(timestamp),
- blockNumber,
- transactionHash
- });
-};
diff --git a/kat/src/handlers/payment-definitions.ts b/kat/src/handlers/payment-definitions.ts
deleted file mode 100644
index e1f085f5d2..0000000000
--- a/kat/src/handlers/payment-definitions.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright © 2021 Kaleido, Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import { v4 as uuidV4 } from 'uuid';
-import Ajv from 'ajv';
-import * as utils from '../lib/utils';
-import * as ipfs from '../clients/ipfs';
-import * as apiGateway from '../clients/api-gateway';
-import * as database from '../clients/database';
-import RequestError from '../lib/request-handlers';
-import { IAPIGatewayAsyncResponse, IAPIGatewaySyncResponse, IDBBlockchainData, IEventPaymentDefinitionCreated } from '../lib/interfaces';
-
-const ajv = new Ajv();
-
-export const handleGetPaymentDefinitionsRequest = (query: object, skip: number, limit: number) => {
- return database.retrievePaymentDefinitions(query, skip, limit);
-};
-
-export const handleCountPaymentDefinitionsRequest = async (query: object) => {
- return { count: await database.countPaymentDefinitions(query) };
-};
-
-export const handleGetPaymentDefinitionRequest = async (paymentDefinitionID: string) => {
- const paymentDefinition = await database.retrievePaymentDefinitionByID(paymentDefinitionID);
- if (paymentDefinition === null) {
- throw new RequestError('Payment definition not found', 404);
- }
- return paymentDefinition;
-};
-
-export const handleCreatePaymentDefinitionRequest = async (name: string, author: string, descriptionSchema: Object | undefined, sync: boolean) => {
- if (descriptionSchema !== undefined && !ajv.validateSchema(descriptionSchema)) {
- throw new RequestError('Invalid description schema', 400);
- }
- if (await database.retrievePaymentDefinitionByName(name) !== null) {
- throw new RequestError('Payment definition name conflict', 409);
- }
- let descriptionSchemaHash: string | undefined;
- let apiGatewayResponse: IAPIGatewayAsyncResponse | IAPIGatewaySyncResponse;
- const timestamp = utils.getTimestamp();
-
- const paymentDefinitionID = uuidV4();
- if (descriptionSchema) {
- descriptionSchemaHash = utils.ipfsHashToSha256(await ipfs.uploadString(JSON.stringify(descriptionSchema)));
- apiGatewayResponse = await apiGateway.createDescribedPaymentDefinition(paymentDefinitionID, name, author, descriptionSchemaHash, sync);
- } else {
- apiGatewayResponse = await apiGateway.createPaymentDefinition(paymentDefinitionID, name, author, sync);
- }
- const receipt = apiGatewayResponse.type === 'async' ? apiGatewayResponse.id : undefined;
- await database.upsertPaymentDefinition({
- paymentDefinitionID,
- name,
- author,
- descriptionSchemaHash,
- descriptionSchema,
- submitted: timestamp,
- receipt
- });
- return paymentDefinitionID;
-};
-
-export const handlePaymentDefinitionCreatedEvent = async (event: IEventPaymentDefinitionCreated, { blockNumber, transactionHash }: IDBBlockchainData) => {
- const paymentDefinitionID = utils.hexToUuid(event.paymentDefinitionID);
- const dbPaymentDefinitionByID = await database.retrievePaymentDefinitionByID(paymentDefinitionID);
- if (dbPaymentDefinitionByID !== null) {
- if (dbPaymentDefinitionByID.transactionHash !== undefined) {
- throw new Error(`Payment definition ID conflict ${paymentDefinitionID}`);
- }
- } else {
- const dbpaymentDefinitionByName = await database.retrievePaymentDefinitionByName(event.name);
- if (dbpaymentDefinitionByName !== null) {
- if (dbpaymentDefinitionByName.transactionHash !== undefined) {
- throw new Error(`Payment definition name conflict ${event.name}`);
- } else {
- await database.markPaymentDefinitionAsConflict(dbpaymentDefinitionByName.paymentDefinitionID, Number(event.timestamp));
- }
- }
- }
- let descriptionSchema;
- if (event.descriptionSchemaHash) {
- if (event.descriptionSchemaHash === dbPaymentDefinitionByID?.descriptionSchemaHash) {
- descriptionSchema = dbPaymentDefinitionByID?.descriptionSchema
- } else {
- descriptionSchema = await ipfs.downloadJSON