hyperledger / fabric-gateway

Go, Node and Java client API for Hyperledger Fabric v2.4+
https://hyperledger.github.io/fabric-gateway/
Apache License 2.0
154 stars 92 forks source link

Enable TypeScript/JavaScript implementation to be used in a browser #609

Open bestbeforetoday opened 1 year ago

bestbeforetoday commented 1 year ago

As a front-end developer I want to use the client API directly from the browser So that a back-end Node.js server is not required as an intermediary

The key issues are the use of Node packages in the implementation, and some exposure of Node packages in the public API. This includes:

The core of the implementation either is or can be implemented without using any Node-specific packages. A workable approach seems to be to split this core capability out into a separate package, which can be depended on by the fabric-gateway package. The fabric-gateway package will then only contain Node-specific additions, such as the default signer, hash and checkpointer implementations. @noble/hashes should provide secure random and hash implementations required by the core package (for transactioncontext.ts) without any dependency on Node.js.

The alternatives available with the existing implementation are:

  1. Use a Node.js back-end server do interact with Fabric on behalf of the browser using the fabric-gateway API, and the off-line signing flow to enable the browser to handle signing using the signing credentials held there.
  2. Use the Gateway gRPC service directly using the @hyperledger/fabric-protos package.
bestbeforetoday commented 1 year ago

I created a prototype implementation by:

  1. Move all the Node client code that can be implemented without using Node standard packages into a node/src/core sub-directory. This required:
    1. Implement a simple alternative to a usage of util.inspect in transactionparser.ts.
    2. Use @noble/hashes/sha256 for SHA-256 hash in signingidentity.ts and transactioncontext.ts.
    3. Use @noble/hashes/utils for randomBytes in transactioncontext.ts.
    4. The few files and index.ts remaining in the top-level node/src import (and export) node/src/core as required.
  2. Add an node/src/core/index.ts file to act as a single entry point to all the pure JavaScript code.
  3. Use esbuild to generate a single bundle.js file from all the content in node/dist/core.

The restructuring of the code seems to be successful but the bundling process highlighted that @grpc/grpc-js itself relies on several built-in Node packages. These can be polyfilled but whether or not it will work with these polyfills is uncertain. A fully browser-compatible (no polyfill) implementation would probably need grpc-web to be used instead of @grpc/grpc-js. This appears at first look to be impractical since:

  1. grpc-web currently does not provide all the required gRPC capability. Bi-directional streaming (used for block eventing) is not supported.
  2. The fabric-gateway API accepts a grpc-js Client object as the gRPC connection to use.
bestbeforetoday commented 1 year ago

My current thinking is that refactoring the current Node implementation so that it (or parts of it) can be run in a browser environment is impractical. It probably makes sense to make use of the WebCrypto (SubtleCrypto) API, and the digest() function it provides is asynchronous. This would cause a breaking change to the synchronous getDigest() and (to a lesser extent) Hash functions exposed by the Fabric Gateway client API.

Since the primary use case is to modify the off-line signing flow by both generating and signing request messages at the client to avoid the need for the server to generate messages and return them to the client for signing, a more practical approach might be to lift and refactor a subset of the existing code out into a separate browser package. This would include capability to:

An optional addition would be to directly build similar, cut-down variants of the block event request objects: FilteredBlockEventsRequest, BlockEventsRequest and BlockAndPrivateDataEventsRequest.

The fabric-protos package also has a dependency on @grpc/grpc-js since it includes gRPC service stubs. As described in previous comments, @grpc/grpc-js has dependencies on many standard Node packages. It might be necessary to build and package within this browser package a subset of the fabric-protos, including only the ones needed to build the objects described above, and excluding gRPC services. Since the browser environment would only interact with the Fabric Gateway client API indirectly by passing serialised protocol buffer messages, this should not present a compatibility problem, since wire-level interoperability of serialized protocol buffer messages is robust.