Skip to content

Commit

Permalink
Merge pull request #126 from bitcoinjs/feat/asmjs
Browse files Browse the repository at this point in the history
Add ASM.JS package creation to CI
  • Loading branch information
junderw authored Jul 22, 2023
2 parents fb0287d + 8fbc572 commit 94c8e80
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 73 deletions.
42 changes: 37 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,26 @@ jobs:
- uses: actions/cache@v2
id: binaryen-cache
with:
path: binaryen-version_100
key: binaryen-version_100
path: binaryen-version_114
key: binaryen-version_114

- name: Install wasm-opt (binaryen)
if: steps.binaryen-cache.outputs.cache-hit != 'true'
run: |
wget https://github.com/WebAssembly/binaryen/releases/download/version_100/binaryen-version_100-x86_64-linux.tar.gz
tar zxvf binaryen-version_100-x86_64-linux.tar.gz binaryen-version_100/bin/wasm-opt
wget https://github.com/WebAssembly/binaryen/releases/download/version_114/binaryen-version_114-x86_64-linux.tar.gz
tar zxvf binaryen-version_114-x86_64-linux.tar.gz binaryen-version_114/bin/wasm-opt binaryen-version_114/bin/wasm2js
- name: Build wasm
run: export PATH=$PATH:./binaryen-version_100/bin/ && make build-wasm
run: export PATH=$PATH:./binaryen-version_114/bin/ && make build-wasm

- name: Build JS
run: make build-js

- name: Build ASM.JS
run: |
export PATH=$PATH:./binaryen-version_114/bin/ && make build-asmjs
sed -i 's/DELETE ME TO RUN//g' tests/index.js
- name: Run Node.js tests and coverage
run: |
make test-node-raw-ci
Expand Down Expand Up @@ -131,6 +136,24 @@ jobs:
name: package
path: tiny-secp256k1-*

- uses: actions/cache@v2
id: binaryen-cache
with:
path: binaryen-version_114
key: binaryen-version_114

- name: Create ASM.js package
run: |
export PATH=$PATH:./binaryen-version_114/bin/ && make build-asmjs
cd tiny-secp256k1-asmjs
npm pack
- name: Upload ASM.js package
uses: actions/upload-artifact@v2
with:
name: package-asmjs
path: tiny-secp256k1-asmjs/bitcoin-js-tiny-secp256k1-asmjs-*

benchmark:
name: Benchmark
needs: [test]
Expand Down Expand Up @@ -159,6 +182,15 @@ jobs:
name: wasm
path: lib

- uses: actions/cache@v2
id: binaryen-cache
with:
path: binaryen-version_114
key: binaryen-version_114

- name: Build ASM.JS
run: export PATH=$PATH:./binaryen-version_114/bin/ && make build-asmjs

- name: Install benchmark dependencies
run: cd benches && npm ci

Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/gh-pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ jobs:
- uses: actions/cache@v2
id: binaryen-cache
with:
path: binaryen-version_100
key: binaryen-version_100
path: binaryen-version_114
key: binaryen-version_114

- name: Install wasm-opt (binaryen)
if: steps.binaryen-cache.outputs.cache-hit != 'true'
run: |
wget https://github.com/WebAssembly/binaryen/releases/download/version_100/binaryen-version_100-x86_64-linux.tar.gz
tar zxvf binaryen-version_100-x86_64-linux.tar.gz binaryen-version_100/bin/wasm-opt
wget https://github.com/WebAssembly/binaryen/releases/download/version_114/binaryen-version_114-x86_64-linux.tar.gz
tar zxvf binaryen-version_114-x86_64-linux.tar.gz binaryen-version_114/bin/wasm-opt binaryen-version_114/bin/wasm2js
- name: Build wasm
run: export PATH=$PATH:./binaryen-version_100/bin/ && make build-wasm
run: export PATH=$PATH:./binaryen-version_114/bin/ && make build-wasm

- name: Build JS
run: make build-js
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
/target
/tests/browser
/types
/tiny-secp256k1-asmjs
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ RUN curl -sSf https://apt.llvm.org/llvm.sh | bash -s -- 12 && \
ln -s $(which clang-12) /usr/bin/clang

# Install wasm-opt from binaryen
RUN git clone --depth 1 --branch version_100 https://github.com/WebAssembly/binaryen.git /binaryen && \
RUN git clone --depth 1 --branch version_114 https://github.com/WebAssembly/binaryen.git /binaryen && \
cd /binaryen && \
cmake . && \
make -j$(nproc) && \
Expand Down
59 changes: 57 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,59 @@ build-wasm:
mkdir -p lib && cp -f target/wasm32-unknown-unknown/release/secp256k1_wasm.wasm lib/secp256k1.wasm
wasm-opt -O4 --strip-debug --strip-producers --output lib/secp256k1.wasm lib/secp256k1.wasm

.PHONY: build-asmjs
build-asmjs: asmjs-build asmjs-fixes

.PHONY: build-asmjs-dev
build-asmjs-dev: asmjs-build-dev asmjs-fixes

.PHONY: asmjs-build
asmjs-build:
mkdir -p tiny-secp256k1-asmjs/lib
cp -R lib/ tiny-secp256k1-asmjs/
wasm2js -O4 --disable-gc -n tiny-secp256k1-asmjs/lib/secp256k1.wasm --output tiny-secp256k1-asmjs/lib/secp256k1.asm.js

.PHONY: asmjs-build-dev
asmjs-build-dev:
mkdir -p tiny-secp256k1-asmjs/lib
cp -R lib/ tiny-secp256k1-asmjs/
wasm2js tiny-secp256k1-asmjs/lib/secp256k1.wasm --output tiny-secp256k1-asmjs/lib/secp256k1.asm.js

.PHONY: asmjs-fixes
asmjs-fixes:
### Remove the copied wasm file and replace all imports
rm tiny-secp256k1-asmjs/lib/secp256k1.wasm
for FILE in $$(grep -lR secp256k1.wasm ./tiny-secp256k1-asmjs/lib); do \
sed -i 's/\.wasm/.asm.js/g' "$$FILE"; \
done

### Copy over package.json, README, LICENSE
cp package.json tiny-secp256k1-asmjs/
cp LICENSE tiny-secp256k1-asmjs/
cp README.md tiny-secp256k1-asmjs/
sed -i 's/"tiny-secp256k1"/"@bitcoin-js\/tiny-secp256k1-asmjs"/g' tiny-secp256k1-asmjs/package.json
grep -v wasm_loader tiny-secp256k1-asmjs/package.json > tiny-secp256k1-asmjs/package.json.new
mv tiny-secp256k1-asmjs/package.json.new tiny-secp256k1-asmjs/package.json

### 4 places where we want to replace the name tiny-secp256k1 in the README
sed -i 's/\(# \|install \|add \|v\/\|package\/\)tiny-secp256k1/\1@bitcoin-js\/tiny-secp256k1-asmjs/g' tiny-secp256k1-asmjs/README.md

### Copy the asm JS to cjs folder (needs modification)
cp tiny-secp256k1-asmjs/lib/secp256k1.asm.js tiny-secp256k1-asmjs/lib/cjs/secp256k1.asm.js
sed -i -e 's/import \* as \(.*\) from .\.\/\(.*\)\.js.;/const \1 = require('"'"'.\/\2.cjs'"'"');/g' tiny-secp256k1-asmjs/lib/cjs/secp256k1.asm.js
sed -i -e 's/export var /module.exports./g' tiny-secp256k1-asmjs/lib/cjs/secp256k1.asm.js
mv tiny-secp256k1-asmjs/lib/cjs/secp256k1.asm.js tiny-secp256k1-asmjs/lib/cjs/secp256k1.asm.cjs

### Modify imports for cjs only
for FILE in $$(grep -lR secp256k1.asm.js ./tiny-secp256k1-asmjs/lib/cjs); do \
sed -i 's/\.asm.js/.asm.cjs/g' "$$FILE"; \
done

### The NodeJS loader is not needed with ASM JS
mv tiny-secp256k1-asmjs/lib/cjs/wasm_loader.browser.cjs tiny-secp256k1-asmjs/lib/cjs/wasm_loader.cjs
mv tiny-secp256k1-asmjs/lib/wasm_loader.browser.js tiny-secp256k1-asmjs/lib/wasm_loader.js
rm tiny-secp256k1-asmjs/lib/wasm_loader.browser.d.ts

.PHONY: build-wasm-debug
build-wasm-debug:
RUSTFLAGS="-C link-args=-zstack-size=655360" cargo build --target wasm32-unknown-unknown
Expand All @@ -48,6 +101,8 @@ clean-built:
examples/react-app/dist/*.txt \
examples/react-app/dist/*.wasm \
lib \
tiny-secp256k1-asmjs \
*.tgz \
tests/browser

eslint_files = benches/*.{js,json} examples/**/*.{js,json} src_ts/*.ts tests/*.js *.json *.cjs
Expand Down Expand Up @@ -79,7 +134,7 @@ test-browser-build-raw:
npx webpack build -c tests/browser.webpack.js

.PHONY: test-browser-build
test-browser-build: build-js build-wasm-debug test-browser-build-raw
test-browser-build: build-js build-wasm-debug build-asmjs test-browser-build-raw

test_browser_raw = node tests/browser-run.js | npx tap-summary

Expand All @@ -105,7 +160,7 @@ test-node-raw-ci:
$(test_node_raw) --no-ansi --no-progress

.PHONY: test-node
test-node: build-js build-wasm-debug test-node-raw
test-node: build-js build-wasm-debug build-asmjs test-node-raw

.PHONY: test-node-coverage-raw
test-node-coverage-raw:
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ Previous version of `tiny-secp256k1` implement [C++ addon](https://nodejs.org/ap

Current version use Rust crate (which use C library) compiled to [WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly). With Wasm same code executed in any environment. Wasm is faster than `elliptic` but slower than node bindings ([results in PR](https://github.com/bitcoinjs/tiny-secp256k1/pull/53#issuecomment-801844450) or you can run own benchmark in `benches` directory).

Tools like webpack, environments like React Native, and a large part of the JavaScript/TypeScript ecosystem has support for WASM based libraries. However, it usually involves special config settings which might be difficult to figure out. We have examples in the examples folder that uses webpack to create a demo website.

However, there are also **alternative implementations** of the interface of this library.

## Alternatives

1. [`@bitcoinjs-lib/tiny-secp256k1-asmjs`](https://www.npmjs.com/package/@bitcoin-js/tiny-secp256k1-asmjs) - This library uses wasm2js to convert this library into pure JS. It is about 10x ~ 20x slower than WASM and 3x ~ 10x slower than our old v1 JS implementation.
2. [`@bitcoinerlab/secp256k1`](https://www.npmjs.com/package/@bitcoinerlab/secp256k1) - This library uses noble/secp256k1, and therefore it uses JS native `BigInt`. If you can support `BigInt` it is much faster than ASM.JS, however, this is not maintained by this library's maintainers, so there's no guarantee that they will keep up with any interface changes in the future. Please check before using. It is about 1.5x ~ 5x slower than WASM.

## Building

For building locally you need C/C++ toolchain, Rust version >=1.50.0 and `wasm-opt` from [binaryen](https://github.com/WebAssembly/binaryen).
Expand Down
7 changes: 6 additions & 1 deletion benches/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import tiny_secp256k1_prev_js from "tiny-secp256k1/js.js";
import tiny_secp256k1_prev_native from "tiny-secp256k1/native.js";
import * as tiny_secp256k1 from "../lib/index.js";
import * as tiny_secp256k1_asmjs from "../tiny-secp256k1-asmjs/lib/index.js";
import * as cryptocoinjs_secp256k1 from "./cryptocoinjs_secp256k1.js";
import noble_secp256k1 from "./noble_secp256k1.js";
import noble_secp256k1 from "@bitcoinerlab/secp256k1";
import {
fecdsa,
fpoints,
Expand All @@ -27,6 +28,10 @@ const modules = [
name: "tiny-secp256k1 (WASM)",
secp256k1: tiny_secp256k1,
},
{
name: "tiny-secp256k1 (ASM.JS)",
secp256k1: tiny_secp256k1_asmjs,
},
{
name: "[email protected] (C++ addon, NAN/V8)",
secp256k1: tiny_secp256k1_prev_native,
Expand Down
16 changes: 0 additions & 16 deletions benches/noble_secp256k1.js

This file was deleted.

62 changes: 51 additions & 11 deletions benches/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion benches/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "node --experimental-json-modules index.js"
},
"dependencies": {
"noble-secp256k1": "=1.1.2",
"@bitcoinerlab/secp256k1": "=1.0.5",
"secp256k1": "=4.0.2",
"tiny-secp256k1": "=1.1.6"
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
}
},
"browser": {
"./lib/rand.js": "./lib/rand.browser.js",
"./lib/wasm_loader.js": "./lib/wasm_loader.browser.js"
"./lib/wasm_loader.js": "./lib/wasm_loader.browser.js",
"./lib/rand.js": "./lib/rand.browser.js"
},
"types": "./lib/index.d.ts",
"files": [
Expand Down
Loading

0 comments on commit 94c8e80

Please sign in to comment.