ChainSafe / blst-ts

Typescript wrapper for https://github.com/supranational/blst native bindings, a highly performant BLS12-381 signature library
Other
18 stars 13 forks source link

Add browser support #63

Closed bryanchriswhite closed 6 months ago

bryanchriswhite commented 2 years ago

Summary

This PR re-organizes the work from fetchai/blst-ts#1 in an attempt to make it easier to review and to revert unnecessary changes. This work has also been released on npm as @fetchai/blst-ts.

While all applicable tests are passing in a browser environment, I'm reluctant to say that this "closes" #18 because it is missing bindings to the constants:

The rest of the bindings were to functions, which is well documented, but I was neither able to to find documentation on nor come up with a way to bind to C/C++ constants in the time I've spent on this so far. I would be extremely receptive to any help in this area! :smile:

The blst submodule is pointed at the current HEAD of supranational/blst#121 but should be updated to the main branch when possible.

Dependencies

CLAassistant commented 2 years ago

CLA assistant check
All committers have signed the CLA.

dot-asm commented 2 years ago

I hope you don't mind if I ask some questions and comment. The goal is to gather some understanding of the platform and corresponding challenges. Maybe there is something that would be appropriate to do slightly differently on the blst side, more specifically in blst.hpp...

dot-asm commented 2 years ago

Is it correct understanding that the glue code was first auto-generated and then manually fixed up? If so, maybe it would be appropriate to exercise approach similar to blst/bindings/go/generate.py. I.e. instead of applying same fix-ups to P1 and P2, one would put together P1 and then transliterate it to P2? So the each time P1 is modified, P2 is modified accordingly with zero effort. It's even possible to keep it in one single file. If there is interest, I can make more concrete suggestion...

dot-asm commented 2 years ago

exercise approach similar to blst/bindings/go/generate.py

One can even get more creative and interleave C++ and Javascript in the same file, method by method, for better overview...

dot-asm commented 2 years ago

exercise approach similar to blst/bindings/go/generate.py

One can even get more creative and interleave C++ and Javascript in the same file, method by method, for better overview...

Just to give an idea:

#!/usr/bin/env python3

js = """
P1_Affine.prototype['dup'] = P1_Affine.prototype.dup = /** @suppress {undefinedVars, duplicate} @this{Object} */function() {
  var self = this.ptr;
  return wrapPointer(_emscripten_bind_P1_Affine_dup_0(self), P1_Affine);
};;
"""
cpp = """
blst::P1_Affine* EMSCRIPTEN_KEEPALIVE emscripten_bind_P1_Affine_dup_0(blst::P1_Affine* self) {
  return new blst::P1_Affine(self->dup());
}
"""
js += """
P1_Affine.prototype['to_jacobian'] = P1_Affine.prototype.to_jacobian = /** @suppress {undefinedVars, duplicate} @this{Object} */function() {
  var self = this.ptr;
  return wrapPointer(_emscripten_bind_P1_Affine_to_jacobian_0(self), P1);
};;
"""
cpp += """
blst::P1* EMSCRIPTEN_KEEPALIVE emscripten_bind_P1_Affine_to_jacobian_0(blst::P1_Affine* self) {
  return new blst::P1(self->to_jacobian());
}
"""

print(cpp) # to cpp file, then replace P1 with P2 and emit again
print("8<------" * 10)
print(js)  # to js file, then replace P1 with P2 and emit again
dot-asm commented 2 years ago

My suggestion would be to provide an empty .idl file that simply lists the classes:

[Prefix="blst::"] interface P1_Affine { };
[Prefix="blst::"] interface P1 { };
[Prefix="blst::"] interface P2_Affine { };
[Prefix="blst::"] interface P2 { };
[Prefix="blst::"] interface SecretKey { };
[Prefix="blst::"] interface PT { };
[Prefix="blst::"] interface Pairing { };

This will be used to re-generate empty bindings and all those Javascript helpers [in case it's deemed necessary]. Then one would use a script that interleaves C++/Javascript snippets to append manually written methods to previously generated files. And override the constructors.

As for P1->P2 transliteration. A better way to do it, better than bindings/go/generate.py that is, is to do something along following:

def xchg_1vs2(matchobj):
    if matchobj.group(2) == '1':
        return matchobj.group(1) + '2'
    else:
        return matchobj.group(1) + '1'

...

print(cpp, file=cpp_fd)
print(js, file=js_fd)
print(re.sub(r'((?<!f)[pgPG])([12])', xchg_1vs2, cpp), file=cpp_fd)
print(re.sub(r'((?<!f)[pgPG])([12])', xchg_1vs2, js), file=js_fd)

[Just in case, I'm using this method to generate C# bindings.]

bryanchriswhite commented 2 years ago

My suggestion would be to provide an empty .idl file that simply lists the classes:

...

This will be used to re-generate empty bindings and all those Javascript helpers [in case it's deemed necessary]. Then one would use a script that interleaves C++/Javascript snippets to append manually written methods to previously generated files. And override the constructors.

I love this idea :heart_eyes:! It's as if you've done this before :wink:

dapplion commented 2 years ago

@bryanchriswhite so happy to see this PR here in a such a great form, I sincerely appreciate your perseverance :heart:

Also thank you @dot-asm for jumping in already with valuable suggestions. Your help was instrumental in delivering blst-ts in the first place.

Will do an in-depth review latter this week

dapplion commented 2 years ago

I'm gonna think more what's the best way to provide both bindings and wasm in the same library. @bryanchriswhite do you think wasm has utility in NodeJS or may only be used in web?

CC @wemeetagain please take a look, specially how this integrates into chainsafe/bls and esm modules

bryanchriswhite commented 2 years ago

do you think wasm has utility in NodeJS or may only be used in web?

@dapplion I believe that node supports the same WebAssembly API as the browser these days and emscripten's preamble is intended to support both. So I'm pretty confident that one could use the wasm binding in node as well.

dot-asm commented 2 years ago

This will be used to re-generate empty bindings and all those Javascript helpers [in case it's deemed necessary]. Then one would use a script that interleaves C++/Javascript snippets to append manually written methods to previously generated files. And override the constructors.

This last part, overriding the constructors, is not possible. Hence https://github.com/dot-asm/blst/tree/emscripten/bindings/emscipten, as a starting point...

dapplion commented 2 years ago

@bryanchriswhite ping, reached out via email from your Github profile

dot-asm commented 2 years ago

There was typo in directory name, fixed. And runnable.js is equivalent of node.js/runnable.js now...

dot-asm commented 2 years ago

https://github.com/dot-asm/blst/tree/emscripten/bindings/emscripten is essentially ready to roll. "Essentially" is an invitation to check it out and provide feedback. Once it's done, it can go to the main blst repo. build.py creates blst.wasm and blst.js in current working directory. And it passes through additional command line flags to emcc, e.g. /some/where/blst/bindings/emscripten/build.py -O2 --closure 1. blst.js interface aims to be fully compatible with the node.js module.

matthewkeil commented 6 months ago

Closing this PR. Browser support is provided via https://github.com/supranational/blst/tree/master/bindings/emscripten and also via https://github.com/ChainSafe/bls . This repos now hosts napi async bindings for multi-threaded verification and is node specific. There is some discussion about building multi-threaded wasm or emscripten bindings but that is still a future "wish list" thing. If this work becomes relevant again we can open a fresh PR and refer back to this one for the comment history. Thank you for your work on this @bryanchriswhite. If you have any questions or concerns please feel free to reach out to us on our discord and we will be happy to chat 😄