Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FABN-1722: export BlockDecoder for typescript definition #477

Closed
wants to merge 1 commit into from

Conversation

jscode017
Copy link

Signed-off-by: jsjs026 [email protected]

I refer to https://hyperledger.github.io/fabric-sdk-node/release-2.2/BlockDecoder.html
I am not sure I did it right, there are some questions/discussions.

  1. For parameters end with suffix proto, I used type object here
  2. Do I need to do additional things besides modifying the index.d.ts file?

@jscode017 jscode017 requested a review from a team as a code owner July 27, 2021 05:30
@@ -245,6 +245,15 @@ export class Proposal extends ServiceAction {
public compareProposalResponseResults(proposalResponses: any[]): boolean;
}

export class BlockDecoder {
constructor();
public decode(blockBuf: Buffer): fabproto6.common.Block;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jscode017 These methods are static according to the documentation, please update the type definitions accordingly. https://hyperledger.github.io/fabric-sdk-node/release-2.2/BlockDecoder.html

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, cheers!

@petermetz
Copy link
Member

petermetz commented Jul 27, 2021

Do I need to do additional things besides modifying the index.d.ts file?

@jscode017 I'd also ask you to test if it actually works as expected, here's how you can do that manually as a one-off:

  1. You locate the fabric-common package directory inside the node_modules folder,
  2. then inside there you locate the same index.d.ts file your PR here changes.
  3. You update that index.d.ts manually to have the changes from this PR as well.
  4. You go to the the place in your code where you want to import the BlockDecoder and import it via the module syntax e.g. import { BlockDecoder } from "fabric-common";
  5. Then you see if your code still compiles without issues. If yes, then that's a very successful smoke test, if not then you can just figure out what's wrong based on the compiler errors, fix it in the index.d.ts and finally update your PR here to match that.

@jscode017
Copy link
Author

@petermetz
It works when I change the index.d.ts in node-modules!
Or at least the decode() method works.(Since I do not have test codes for other methods).

@petermetz
Copy link
Member

@jscode017 Good enough for me. Of course the Fabric maintainers will provide the actual review but I wanted to help out with the preliminary as much as possible.

Copy link
Member

@bestbeforetoday bestbeforetoday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a tricky one. If you dig down into the protobuf definitions you will find that there are several points where properties are just Uint8Array. Often this byte data is actually a serialized protobuf structure. What the block decoder is doing is traversing down through the protobuf structure unpacking everything (including the nested serialized protobufs) into plain JavaScript objects, so the return values don't map exactly to any of the protobuf TypeScript definitions.

So, short of hand-crafting TypeScript definitions for the complete unpacked object structures, what can we do? Well the protobuf TypeScript definitions include both implementation classes (e.g. common.Block) and accompanying interfaces (e.g. common.IBlock). The class definitions include lots of protobuf-specific methods that definitely don't exist on the unpacked JavaScript objects returned from the block decoder, so they are the wrong thing to use. The interface definitions only define the protobuf properties so they are pretty close to what you get from the block decoder, with the caveat that they define some properties (like block.data.data) as raw bytes.

Even the protobuf interface definitions are a lot better than no definitions so, unless you are really motivated to hand-craft type definitions for the entire object structure, I would suggest just using those instead of the protobuf implementation class definitions.

export class BlockDecoder {
constructor();
public static decode(blockBuf: Buffer): fabproto6.common.Block;
public static decodeBlock(blockProto: object): fabproto6.common.Block;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will take a fabproto6.common.IBlock as its argument and version of that completely unpacked to a plain JavaScript object... or (a little confusingly) just return an fabproto6.common.IBlock too

constructor();
public static decode(blockBuf: Buffer): fabproto6.common.Block;
public static decodeBlock(blockProto: object): fabproto6.common.Block;
public static decodeFilteredBlock(filteredBlockProto: object): fabproto6.protos.FilteredBlock;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argument should be protos.IFilteredBlock

public static decode(blockBuf: Buffer): fabproto6.common.Block;
public static decodeBlock(blockProto: object): fabproto6.common.Block;
public static decodeFilteredBlock(filteredBlockProto: object): fabproto6.protos.FilteredBlock;
public static decodeBlockWithPrivateData(BlockAndPrivateData: object): fabproto6.protos.BlockAndPrivateData;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argument should be protos.IBlockAndPrivateData

@petermetz
Copy link
Member

@bestbeforetoday If you are interested I could recommend the thesayyn/protoc-gen-ts for generating the Typescript sources from the .proto files. I used it in a recent PR of mine to add gRPC support in the Cactus API server and was very happy with the way it solved my issues.

PR: hyperledger-cacti/cacti#1190

An example of a proto message in TS (generated): https://github.com/hyperledger/cactus/blob/58a3a1c8c836f9b4a0fa09346cb9e42145cdb437/packages/cactus-cmd-api-server/src/main/typescript/generated/proto/protoc-gen-ts/models/health_check_response_pb.ts

Comment on lines +248 to +249
export class BlockDecoder {
constructor();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BlockDecoder only exposes static methods so it's not really intended to be used as class and instances created with new BlockDecoder(). It might be worth defining it in TypeScript just as a namespace instead of a class with a constructor, and the static methods declared as functions within that namespace. Something like:

declare namespace BlockDecoder {
    export function decode(blockBuf: Buffer): fabproto6.common.IBlock;
}

@bestbeforetoday
Copy link
Member

@petermetz Thanks for the pointer to thesayyn/protoc-gen-ts. I am currently using improbable-eng/ts-protoc-gen to extend protoc generated output to include TypeScript definitions in the new fabric-gateway client API, which is similar and seems to work fine.

The fabric-protos package, which is published as part of this Node SDK, also includes TypeScript definitions for the generated protobuf implementations. These are generated using protobuf.js and are a little different in structure to the implementations generated by protoc, but functionally equivalent. It would be a big change to move away from protobuf.js.

There are a few other options for gRPC/protobuf and TypeScript generation listed at badsyntax/grpc-js-typescript.

The problem addressed by this PR is not the lack of TypeScript definitions for the protobufs, it's that the objects returned by BlockDecoder methods are not the generated protobufs, and it is the definitions for the returned objects that are missing. BlockDecoder takes actual Fabric protobuf objects as input and deserializes all the serialized protobufs nested within those protobuf structures to make working with them easier. The structure of those output objects is similar to the generated protobuf interface definitions already in fabric-protos, but not the same as many of the fields defined in the protobufs as bytes are replaced by actual objects. Ideally we would have correct TypeScript definitions for what is really returned by the BlockDecoder but, since they were not written, we currently have any type used instead.

@bestbeforetoday
Copy link
Member

This has been inactive for a long time and is not yet in a state to merge so I'll close it. Feel free to reopen if progress in made on the implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants