bells-secp256k1 v0.1.1
tiny-secp256k1
This library is under development, and, like the secp256k1 C library (through secp256k1-sys Rust crate) it depends on, this is a research effort to determine an optimal API for end-users of the bitcoinjs ecosystem.
Installation
npm
npm install tiny-secp256k1yarn
yarn add tiny-secp256k1WebAssembly and Node.js version
Previous version of tiny-secp256k1 implement C++ addon through NAN (Native Abstractions for Node.js) and elliptic as fallback when addon can not be built or in browser-like environement.
Current version use Rust crate (which use C library) compiled to WebAssembly. With Wasm same code executed in any environment. Wasm is faster than elliptic but slower than node bindings (results in PR 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
@bitcoinjs-lib/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.@bitcoinerlab/secp256k1- This library uses noble/secp256k1, and therefore it uses JS nativeBigInt. If you can supportBigIntit 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.
rustup is a recommended way to install Rust. You also will need wasm32-unknown-unknown target.
rustup toolchain install stable --target wasm32-unknown-unknown --component clippy --component rustfmtAfter installing development dependencies with npm you can build Wasm:
make build-wasmor run tests:
make testAlternative way is to use Docker:
% docker build -t tiny-secp256k1 .
% docker run -it --rm -v `pwd`:/tiny-secp256k1 -w /tiny-secp256k1 tiny-secp256k1
# make buildExamples
tiny-secp256k1 includes two examples. First is simple script for Node.js which generate random data and print arguments and methods results. Second is React app.
React app is builded in GitHub Actions on each commit to master branch and uploaded to gh-pages branch, which is always available online: https://bitcoinjs.github.io/tiny-secp256k1/
Documentation
isPoint (A)
isPoint :: Buffer -> BoolReturns false if
Ais not encoded with a sequence tag of0x02,0x03or0x04A.xis not in[1...p - 1]A.yis not in[1...p - 1]
isPointCompressed (A)
isPointCompressed :: Buffer -> BoolReturns false if the pubkey is not compressed.
isXOnlyPoint (A)
isXOnlyPoint :: Buffer -> BoolReturns false if the pubkey is not an xOnlyPubkey.
isPrivate (d)
isPrivate :: Buffer -> BoolReturns false if
dis not 256-bit, ordis not in[1..order - 1]
pointAdd (A, B, compressed)
pointAdd :: Buffer -> Buffer [-> Bool] -> Maybe BufferReturns null if result is at infinity.
Throws:
Expected Pointif!isPoint(A)Expected Pointif!isPoint(B)
pointAddScalar (A, tweak, compressed)
pointAddScalar :: Buffer -> Buffer [-> Bool] -> Maybe BufferReturns null if result is at infinity.
Throws:
Expected Pointif!isPoint(A)Expected Tweakiftweakis not in[0...order - 1]
pointCompress (A, compressed)
pointCompress :: Buffer -> Bool -> BufferThrows:
Expected Pointif!isPoint(A)
pointFromScalar (d, compressed)
pointFromScalar :: Buffer [-> Bool] -> Maybe BufferReturns null if result is at infinity.
Throws:
Expected Privateif!isPrivate(d)
xOnlyPointFromScalar (d)
xOnlyPointFromScalar :: Buffer -> BufferReturns the xOnlyPubkey for a given private key
Throws:
Expected Privateif!isPrivate(d)
xOnlyPointFromPoint (p)
xOnlyPointFromPoint :: Buffer -> BufferReturns the xOnlyPubkey for a given DER public key
Throws:
Expected Pointif!isPoint(p)
pointMultiply (A, tweak, compressed)
pointMultiply :: Buffer -> Buffer [-> Bool] -> Maybe BufferReturns null if result is at infinity.
Throws:
Expected Pointif!isPoint(A)Expected Tweakiftweakis not in[0...order - 1]
privateAdd (d, tweak)
privateAdd :: Buffer -> Buffer -> Maybe BufferReturns null if result is equal to 0.
Throws:
Expected Privateif!isPrivate(d)Expected Tweakiftweakis not in[0...order - 1]
privateSub (d, tweak)
privateSub :: Buffer -> Buffer -> Maybe BufferReturns null if result is equal to 0.
Throws:
Expected Privateif!isPrivate(d)Expected Tweakiftweakis not in[0...order - 1]
privateNegate (d)
privateNegate :: Buffer -> BufferReturns the negation of d on the order n (n - d)
Throws:
Expected Privateif!isPrivate(d)
xOnlyPointAddTweak (p, tweak)
xOnlyPointAddTweak :: Buffer -> Buffer -> { parity: 1 | 0; xOnlyPubkey: Buffer; }Returns the tweaked xOnlyPubkey along with the parity bit (number type of 1|0)
Throws:
Expected Pointif!isXOnlyPoint(p)Expected Tweakif!isXOnlyPoint(tweak)
xOnlyPointAddTweakCheck (p1, p2, tweak, tweakParity)
xOnlyPointAddTweakCheck :: Buffer -> Buffer -> Buffer [-> 1 | 0] -> BoolChecks the tweaked pubkey (p2) against the original pubkey (p1) and tweak. This is slightly slower if you include tweakParity, tweakParity will make it faster for aggregation later on.
Throws:
Expected Pointif!isXOnlyPoint(p1)Expected Pointif!isXOnlyPoint(p2)Expected Tweakif!isXOnlyPoint(tweak)Expected ParityiftweakParity is not 1 or 0
sign (h, d, e)
sign :: Buffer -> Buffer [-> Buffer] -> BufferReturns normalized signatures, each of (r, s) values are guaranteed to less than order / 2.
Uses RFC6979.
Adds e as Added Entropy to the deterministic k generation.
Throws:
Expected Privateif!isPrivate(d)Expected Scalarifhis not 256-bitExpected Extra Data (32 bytes)ifeis not 256-bit
signRecoverable (h, d, e)
signRecoverable :: Buffer -> Buffer [-> Buffer] -> { recoveryId: 0 | 1 | 2 | 3; signature: Buffer; }Returns normalized signatures and recovery Id, each of (r, s) values are guaranteed to less than order / 2.
Uses RFC6979.
Adds e as Added Entropy to the deterministic k generation.
Throws:
Expected Privateif!isPrivate(d)Expected Scalarifhis not 256-bitExpected Extra Data (32 bytes)ifeis not 256-bit
signSchnorr (h, d, e)
signSchnorr :: Buffer -> Buffer [-> Buffer] -> BufferReturns normalized schnorr signature.
Uses BIP340 nonce generation.
Adds e as Added Entropy.
Throws:
Expected Privateif!isPrivate(d)Expected Scalarifhis not 256-bitExpected Extra Data (32 bytes)ifeis not 256-bit
verify (h, Q, signature, strict = false)
verify :: Buffer -> Buffer -> Buffer [-> Bool] -> BoolReturns false if any of (r, s) values are equal to 0, or if the signature is rejected.
If strict is true, valid signatures with any of (r, s) values greater than order / 2 are rejected.
Throws:
Expected Pointif!isPoint(Q)Expected Signatureifsignaturehas any (r, s) values not in range[0...order - 1]Expected Scalarifhis not 256-bit
recover (h, signature, recoveryId, compressed = false)
verify :: Buffer -> Buffer -> Number [-> Bool] -> Maybe BufferReturns the ECDSA public key from a signature if it can be recovered, null otherwise.
Throws:
Expected Signatureifsignaturehas any (r, s) values not in range(0...order - 1]Bad Recovery Idifrecid & 2 !== 0andsignaturehas any r value not in range(0...P - N - 1]Expected Hashifhis not 256-bit
verifySchnorr (h, Q, signature)
verifySchnorr :: Buffer -> Buffer -> Buffer -> BoolReturns false if any of (r, s) values are equal to 0, or if the signature is rejected.
Throws:
Expected Pointif!isPoint(Q)Expected Signatureifsignaturehas any (r, s) values not in range[0...order - 1]Expected Scalarifhis not 256-bit
Credit
This library uses the native library secp256k1 by the bitcoin-core developers through Rust crate secp256k1-sys, including derivatives of its tests and test vectors.