@exodus/bitcoin-api 2.12.1 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -4
- package/src/bitcoinjs-lib/ecc/index.js +8 -2
- package/src/index.js +1 -0
- package/src/multisig-address.js +81 -0
- package/src/tx-send/index.js +6 -12
- package/src/tx-sign/create-sign-with-wallet.js +5 -3
- package/src/tx-sign/default-create-tx.js +1 -1
- package/src/tx-sign/default-prepare-for-signing.js +13 -5
- package/src/tx-sign/index.js +1 -0
- package/src/tx-sign/taproot.js +4 -4
- package/src/bitcoinjs-lib/ecc/common.js +0 -81
- package/src/bitcoinjs-lib/ecc/desktop.js +0 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Exodus bitcoin-api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -23,14 +23,13 @@
|
|
|
23
23
|
"@exodus/bip322-js": "^1.1.0-exodus.4",
|
|
24
24
|
"@exodus/bip44-constants": "^195.0.0",
|
|
25
25
|
"@exodus/bitcoin-lib": "2.3.0",
|
|
26
|
+
"@exodus/bitcoinerlab-secp256k1": "^1.0.5-exodus.1",
|
|
26
27
|
"@exodus/bitcoinjs-lib": "^6.1.5-exodus.1",
|
|
27
28
|
"@exodus/currency": "^2.3.2",
|
|
28
29
|
"@exodus/fetch": "^1.3.0",
|
|
29
30
|
"@exodus/models": "^11.0.0",
|
|
30
|
-
"@exodus/secp256k1": "4.0.2-exodus.0",
|
|
31
31
|
"@exodus/simple-retry": "0.0.6",
|
|
32
32
|
"@exodus/timer": "^1.0.0",
|
|
33
|
-
"@noble/secp256k1": "~1.7.1",
|
|
34
33
|
"bech32": "^1.1.3",
|
|
35
34
|
"bip32-path": "^0.4.2",
|
|
36
35
|
"bn.js": "4.12.0",
|
|
@@ -58,5 +57,5 @@
|
|
|
58
57
|
"jest-when": "^3.5.1",
|
|
59
58
|
"safe-buffer": "^5.2.1"
|
|
60
59
|
},
|
|
61
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "e5132a9674de7cc87a4392e18007c77dc7532894"
|
|
62
61
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { initEccLib } from '@exodus/bitcoinjs-lib'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import bitcoinerlab from '@exodus/bitcoinerlab-secp256k1'
|
|
4
|
+
|
|
5
|
+
export const ecc = bitcoinerlab
|
|
6
|
+
|
|
7
|
+
initEccLib(ecc)
|
|
8
|
+
|
|
9
|
+
export const eccFactory = () => ecc
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from './unconfirmed-ancestor-data'
|
|
|
16
16
|
export * from './parse-unsigned-tx'
|
|
17
17
|
export * from './insight-api-client/util'
|
|
18
18
|
export * from './move-funds'
|
|
19
|
+
export { createEncodeMultisigContract } from './multisig-address'
|
|
19
20
|
export { toAsyncSigner } from './tx-sign/taproot'
|
|
20
21
|
export { toXOnly } from './bitcoinjs-lib/ecc-utils'
|
|
21
22
|
export * from './ordinals-utils'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
|
|
2
|
+
import { toXOnly } from './bitcoinjs-lib/ecc-utils'
|
|
3
|
+
import { eccFactory } from './bitcoinjs-lib/ecc'
|
|
4
|
+
|
|
5
|
+
// Key to use when key path spending is disabled https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
|
|
6
|
+
const DUMMY_TAPROOT_PUBKEY = Buffer.from(
|
|
7
|
+
'50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
|
|
8
|
+
'hex'
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// Leaf version for BIP342 is 0xc0 or 192 https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#specification
|
|
12
|
+
const LEAF_VERSION_TAPSCRIPT = 192
|
|
13
|
+
|
|
14
|
+
// Limit multisig keys to 20 for now
|
|
15
|
+
const MAX_PUBKEYS = 20
|
|
16
|
+
|
|
17
|
+
export const createEncodeMultisigContract =
|
|
18
|
+
({
|
|
19
|
+
bitcoinjsLib = defaultBitcoinjsLib,
|
|
20
|
+
network = bitcoinjsLib.Network.bitcoin,
|
|
21
|
+
ecc = eccFactory(),
|
|
22
|
+
}) =>
|
|
23
|
+
(publicKeys, { threshold = publicKeys.length, version = 0 } = Object.create(null)) => {
|
|
24
|
+
if (
|
|
25
|
+
!Array.isArray(publicKeys) ||
|
|
26
|
+
publicKeys.some((k) => !Buffer.isBuffer(k) || !ecc.isPointCompressed(k))
|
|
27
|
+
) {
|
|
28
|
+
throw new Error('publicKeys must be an Array of Buffers representing compressed public keys')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (publicKeys.length <= 0 || publicKeys.length > MAX_PUBKEYS) {
|
|
32
|
+
throw new Error(`asset.encodeMultisigContract supports from 1 to ${MAX_PUBKEYS} pubKeys`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (new Set(publicKeys.map((k) => k.toString('hex'))).size !== publicKeys.length) {
|
|
36
|
+
throw new Error('publicKeys must not contain any duplicates')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!Number.isSafeInteger(version)) {
|
|
40
|
+
throw new TypeError('asset.encodeMultisigContract requires meta.version to be an integer')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Only support version 0 for now
|
|
44
|
+
if (version !== 0) {
|
|
45
|
+
throw new Error(`asset.encodeMultisigContract does not support version ${version}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!Number.isSafeInteger(threshold) || threshold <= 0 || threshold > MAX_PUBKEYS) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`asset.encodeMultisigContract requires meta.threshold to be an integer between 1 and ${MAX_PUBKEYS}`
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (threshold > publicKeys.length) {
|
|
55
|
+
throw new Error('threshold must be <= publicKeys.length')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Sort according to BIP67 https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
|
|
59
|
+
publicKeys.sort((a, b) => Buffer.compare(a, b))
|
|
60
|
+
|
|
61
|
+
// Create multisig redeem script https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#cite_note-5
|
|
62
|
+
const OPS = bitcoinjsLib.script.OPS
|
|
63
|
+
const chunks = []
|
|
64
|
+
const keysIter = publicKeys[Symbol.iterator]()
|
|
65
|
+
|
|
66
|
+
chunks.push(toXOnly(keysIter.next().value), OPS.OP_CHECKSIG)
|
|
67
|
+
for (const key of keysIter) {
|
|
68
|
+
chunks.push(toXOnly(key), OPS.OP_CHECKSIGADD)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
chunks.push(Buffer.from([threshold]), OPS.OP_NUMEQUAL)
|
|
72
|
+
|
|
73
|
+
const output = bitcoinjsLib.script.compile(chunks)
|
|
74
|
+
|
|
75
|
+
return bitcoinjsLib.payments.p2tr({
|
|
76
|
+
internalPubkey: DUMMY_TAPROOT_PUBKEY,
|
|
77
|
+
scriptTree: { output },
|
|
78
|
+
redeem: { output, redeemVersion: LEAF_VERSION_TAPSCRIPT },
|
|
79
|
+
network,
|
|
80
|
+
})
|
|
81
|
+
}
|
package/src/tx-send/index.js
CHANGED
|
@@ -222,11 +222,9 @@ export const getPrepareSendTransaction =
|
|
|
222
222
|
allowUnconfirmedRbfEnabledUtxos,
|
|
223
223
|
utxosDescendingOrder,
|
|
224
224
|
rbfEnabled: providedRbfEnabled,
|
|
225
|
+
assetClientInterface,
|
|
225
226
|
}) =>
|
|
226
|
-
async (
|
|
227
|
-
{ asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
|
|
228
|
-
{ assetClientInterface }
|
|
229
|
-
) => {
|
|
227
|
+
async ({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options }) => {
|
|
230
228
|
const {
|
|
231
229
|
multipleAddressesEnabled,
|
|
232
230
|
feePerKB,
|
|
@@ -472,11 +470,9 @@ export const createAndBroadcastTXFactory =
|
|
|
472
470
|
allowUnconfirmedRbfEnabledUtxos,
|
|
473
471
|
ordinalsEnabled = false,
|
|
474
472
|
utxosDescendingOrder,
|
|
473
|
+
assetClientInterface,
|
|
475
474
|
}) =>
|
|
476
|
-
async (
|
|
477
|
-
{ asset: maybeToken, walletAccount, address, amount: tokenAmount, options },
|
|
478
|
-
{ assetClientInterface }
|
|
479
|
-
) => {
|
|
475
|
+
async ({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options }) => {
|
|
480
476
|
// Prepare transaction
|
|
481
477
|
const { bumpTxId, nft, isExchange, isBip70, isRbfAllowed = true, feeOpts } = options
|
|
482
478
|
|
|
@@ -501,10 +497,8 @@ export const createAndBroadcastTXFactory =
|
|
|
501
497
|
allowUnconfirmedRbfEnabledUtxos,
|
|
502
498
|
utxosDescendingOrder,
|
|
503
499
|
rbfEnabled,
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
{ assetClientInterface }
|
|
507
|
-
)
|
|
500
|
+
assetClientInterface,
|
|
501
|
+
})({ asset: maybeToken, walletAccount, address, amount: tokenAmount, options })
|
|
508
502
|
const {
|
|
509
503
|
amount,
|
|
510
504
|
change,
|
|
@@ -37,7 +37,9 @@ export function createSignWithWallet({
|
|
|
37
37
|
const { key, purpose, publicKey } = getKeyAndPurpose(address)
|
|
38
38
|
|
|
39
39
|
const isP2SH = purpose === 49
|
|
40
|
-
const
|
|
40
|
+
const hasTapLeafScript =
|
|
41
|
+
psbt.data.inputs[index].tapLeafScript && psbt.data.inputs[index].tapLeafScript.length > 0
|
|
42
|
+
const isTaprootKeySpend = purpose === 86 && !hasTapLeafScript
|
|
41
43
|
|
|
42
44
|
if (isP2SH) {
|
|
43
45
|
// If spending from a P2SH address, we assume the address is P2SH wrapping
|
|
@@ -56,7 +58,7 @@ export function createSignWithWallet({
|
|
|
56
58
|
} else {
|
|
57
59
|
throw new Error('Expected P2SH script to be a nested segwit input')
|
|
58
60
|
}
|
|
59
|
-
} else if (
|
|
61
|
+
} else if (isTaprootKeySpend && !Buffer.isBuffer(psbt.data.inputs[index].tapInternalKey)) {
|
|
60
62
|
// tapInternalKey is metadata for signing and not part of the hash to sign.
|
|
61
63
|
// so modifying it here is fine.
|
|
62
64
|
psbt.updateInput(index, {
|
|
@@ -67,7 +69,7 @@ export function createSignWithWallet({
|
|
|
67
69
|
// desktop / BE / mobile with bip-schnorr signing
|
|
68
70
|
await psbt.signInputAsync(
|
|
69
71
|
index,
|
|
70
|
-
toAsyncSigner({ keyPair: key,
|
|
72
|
+
toAsyncSigner({ keyPair: key, isTaprootKeySpend, network }),
|
|
71
73
|
allowedSigHashTypes
|
|
72
74
|
)
|
|
73
75
|
}
|
|
@@ -35,7 +35,7 @@ export const signTxFactory = ({ assetName, resolvePurpose, coinInfo, network })
|
|
|
35
35
|
|
|
36
36
|
await signWithWallet(psbt, inputsToSign)
|
|
37
37
|
|
|
38
|
-
const skipFinalize = !!unsignedTx.txData.psbtBuffer
|
|
38
|
+
const skipFinalize = !!unsignedTx.txData.psbtBuffer || unsignedTx.txMeta.returnPsbt
|
|
39
39
|
return extractTransaction({ psbt, skipFinalize })
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -17,17 +17,21 @@ export function createPrepareForSigning({ assetName, resolvePurpose, coinInfo })
|
|
|
17
17
|
assert(coinInfo, 'coinInfo is required')
|
|
18
18
|
|
|
19
19
|
return ({ unsignedTx }) => {
|
|
20
|
+
const networkInfo = coinInfo.toBitcoinJS()
|
|
21
|
+
|
|
20
22
|
const isPsbtBufferPassed =
|
|
21
23
|
unsignedTx.txData.psbtBuffer &&
|
|
22
24
|
unsignedTx.txMeta.addressPathsMap &&
|
|
23
25
|
unsignedTx.txMeta.inputsToSign
|
|
24
26
|
if (isPsbtBufferPassed) {
|
|
25
27
|
// PSBT created externally (Web3, etc..)
|
|
26
|
-
return createPsbtFromBuffer({
|
|
28
|
+
return createPsbtFromBuffer({
|
|
29
|
+
psbtBuffer: unsignedTx.txData.psbtBuffer,
|
|
30
|
+
networkInfo,
|
|
31
|
+
})
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
// Create PSBT based on internal Exodus data structure
|
|
30
|
-
const networkInfo = coinInfo.toBitcoinJS()
|
|
31
35
|
const psbt = createPsbtFromTxData({
|
|
32
36
|
...unsignedTx.txData,
|
|
33
37
|
...unsignedTx.txMeta,
|
|
@@ -41,8 +45,8 @@ export function createPrepareForSigning({ assetName, resolvePurpose, coinInfo })
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
// Creates a PSBT instance from the passed transaction buffer provided by 3rd parties (e.g. dApps).
|
|
44
|
-
function createPsbtFromBuffer({ psbtBuffer, ecc }) {
|
|
45
|
-
return Psbt.fromBuffer(psbtBuffer, { eccLib: ecc })
|
|
48
|
+
function createPsbtFromBuffer({ psbtBuffer, ecc, networkInfo }) {
|
|
49
|
+
return Psbt.fromBuffer(psbtBuffer, { eccLib: ecc, network: networkInfo })
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
// Creates a PSBT instance from the passed inputs, outputs etc. The wallet itself provides this data.
|
|
@@ -54,7 +58,7 @@ function createPsbtFromTxData({ inputs, outputs, rawTxs, networkInfo, resolvePur
|
|
|
54
58
|
const psbt = new Psbt({ maximumFeeRate, network: networkInfo })
|
|
55
59
|
|
|
56
60
|
// Fill tx
|
|
57
|
-
for (const { txId, vout, address, value, script, sequence } of inputs) {
|
|
61
|
+
for (const { txId, vout, address, value, script, sequence, tapLeafScript } of inputs) {
|
|
58
62
|
// TODO: don't use the purpose as intermediate variable
|
|
59
63
|
// see internals of `resolvePurposes`, just use `isP2TR, isP2SH etc directly
|
|
60
64
|
const purpose = resolvePurpose(address)
|
|
@@ -64,6 +68,10 @@ function createPsbtFromTxData({ inputs, outputs, rawTxs, networkInfo, resolvePur
|
|
|
64
68
|
|
|
65
69
|
const txIn = { hash: txId, index: vout, sequence }
|
|
66
70
|
if (isSegwitAddress || isTaprootAddress) {
|
|
71
|
+
if (isTaprootAddress && tapLeafScript) {
|
|
72
|
+
txIn.tapLeafScript = tapLeafScript
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
// witness outputs only require the value and the script, not the full transaction
|
|
68
76
|
txIn.witnessUtxo = { value, script: Buffer.from(script, 'hex') }
|
|
69
77
|
} else {
|
package/src/tx-sign/index.js
CHANGED
package/src/tx-sign/taproot.js
CHANGED
|
@@ -40,22 +40,22 @@ function tapTweakHash(pubKey, h) {
|
|
|
40
40
|
/**
|
|
41
41
|
* Take a sync signer and make it async.
|
|
42
42
|
*/
|
|
43
|
-
export function toAsyncSigner({ keyPair,
|
|
43
|
+
export function toAsyncSigner({ keyPair, isTaprootKeySpend, network }) {
|
|
44
44
|
assert(keyPair, 'keyPair is required')
|
|
45
45
|
|
|
46
|
-
if (
|
|
46
|
+
if (isTaprootKeySpend) {
|
|
47
47
|
keyPair = tweakSigner({ signer: keyPair, ECPair, network })
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
51
51
|
keyPair.sign = async (h) => {
|
|
52
|
-
const sig =
|
|
52
|
+
const sig = ecc.sign(h, keyPair.privateKey)
|
|
53
53
|
return Buffer.from(sig)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
57
57
|
keyPair.signSchnorr = async (h) => {
|
|
58
|
-
const sig =
|
|
58
|
+
const sig = ecc.signSchnorr(h, keyPair.privateKey, getSchnorrEntropy())
|
|
59
59
|
return Buffer.from(sig)
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import secp256k1 from '@exodus/secp256k1'
|
|
2
|
-
import { toXOnly } from '../ecc-utils'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Common ecc functions between mobile and desktop. Once mobile accepts @noble/secp256k1, we can unify both
|
|
6
|
-
*/
|
|
7
|
-
export const common = {
|
|
8
|
-
/**
|
|
9
|
-
*
|
|
10
|
-
* @param msg32 {Uint8Array} the message
|
|
11
|
-
* @param seckey {Uint8Array} the private key
|
|
12
|
-
* @param data {Uint8Array} the data to sign
|
|
13
|
-
*/
|
|
14
|
-
sign: (msg32, seckey, data) => secp256k1.ecdsaSign(msg32, seckey, { data }).signature,
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
* @param msg32 {Uint8Array} the message to verify
|
|
19
|
-
* @param publicKey {Uint8Array} the public key
|
|
20
|
-
* @param signature {Uint8Array} the signature
|
|
21
|
-
* @returns {boolean}
|
|
22
|
-
*/
|
|
23
|
-
verify: (msg32, publicKey, signature) => secp256k1.ecdsaVerify(signature, msg32, publicKey),
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
* @param seckey {Uint8Array}
|
|
28
|
-
* @param tweak {Uint8Array}
|
|
29
|
-
*/
|
|
30
|
-
privateAdd: (seckey, tweak) =>
|
|
31
|
-
// cloning input. secp256k1 modifies it and it cannot be reused/cached
|
|
32
|
-
secp256k1.privateKeyTweakAdd(Buffer.from(seckey), tweak),
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @param seckey {Uint8Array} the public key
|
|
36
|
-
*/
|
|
37
|
-
privateNegate: (seckey) =>
|
|
38
|
-
// cloning input. secp256k1 modifies it and it cannot be reused/cached
|
|
39
|
-
secp256k1.privateKeyNegate(Buffer.from(seckey)),
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
* @param publicKey {Uint8Array}
|
|
44
|
-
* @param tweak {Uint8Array}
|
|
45
|
-
* @returns {{parity: (number), xOnlyPubkey}|null}
|
|
46
|
-
*/
|
|
47
|
-
xOnlyPointAddTweak: (publicKey, tweak) => {
|
|
48
|
-
try {
|
|
49
|
-
const t = secp256k1.publicKeyTweakAdd(toPubKey(publicKey), tweak)
|
|
50
|
-
return {
|
|
51
|
-
parity: t[0] === 0x02 ? 0 : 1,
|
|
52
|
-
xOnlyPubkey: toXOnly(t),
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
return null
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param seckey {Uint8Array}
|
|
61
|
-
* @returns {boolean}
|
|
62
|
-
*/
|
|
63
|
-
isPrivate: (seckey) => secp256k1.privateKeyVerify(seckey),
|
|
64
|
-
/**
|
|
65
|
-
* @param seckey {Uint8Array}
|
|
66
|
-
* @param compressed {boolean}
|
|
67
|
-
*/
|
|
68
|
-
pointFromScalar: (seckey, compressed) => secp256k1.publicKeyCreate(seckey, compressed),
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
* @param xOnly {Uint8Array}
|
|
74
|
-
* @returns {Uint8Array}
|
|
75
|
-
*/
|
|
76
|
-
export const toPubKey = (xOnly) => {
|
|
77
|
-
const p = new Uint8Array(33)
|
|
78
|
-
p.set([0x02])
|
|
79
|
-
p.set(xOnly, 1)
|
|
80
|
-
return p
|
|
81
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { initEccLib } from '@exodus/bitcoinjs-lib'
|
|
2
|
-
import { Point, schnorr, sign } from '@noble/secp256k1'
|
|
3
|
-
import { common, toPubKey } from './common'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Wrapper around `secp256k1` in order to follow the bitcoinjs-lib `TinySecp256k1Interface`
|
|
7
|
-
* Schnorr signatures are offered by @noble/secp256k1
|
|
8
|
-
*/
|
|
9
|
-
export const desktopEcc = {
|
|
10
|
-
...common,
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @param message {Uint8Array}
|
|
14
|
-
* @param privateKey {Uint8Array}
|
|
15
|
-
* @param extraEntropy {Uint8Array}
|
|
16
|
-
* @returns {Promise<Uint8Array>}
|
|
17
|
-
*/
|
|
18
|
-
signAsync: async (message, privateKey, extraEntropy) =>
|
|
19
|
-
sign(message, privateKey, {
|
|
20
|
-
extraEntropy,
|
|
21
|
-
canonical: true,
|
|
22
|
-
der: false,
|
|
23
|
-
}),
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @param message {Uint8Array}
|
|
27
|
-
* @param privateKey {Uint8Array}
|
|
28
|
-
* @param extraEntropy {Uint8Array}
|
|
29
|
-
* @returns {Promise<Uint8Array>}
|
|
30
|
-
*/
|
|
31
|
-
signSchnorrAsync: async (message, privateKey, extraEntropy) =>
|
|
32
|
-
schnorr.sign(message, privateKey, extraEntropy),
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @param message {Uint8Array}
|
|
36
|
-
* @param publicKey {Uint8Array}
|
|
37
|
-
* @param signature {Uint8Array}
|
|
38
|
-
* @returns {Promise<boolean>}
|
|
39
|
-
*/
|
|
40
|
-
verifySchnorrAsync: async (message, publicKey, signature) =>
|
|
41
|
-
schnorr.verify(signature, message, publicKey),
|
|
42
|
-
|
|
43
|
-
// The underlying library does not expose sync functions for Schnorr sign and verify.
|
|
44
|
-
// These function are explicitly defined here as `null` for documentation purposes.
|
|
45
|
-
// Update, latest version of https://github.com/paulmillr/noble-secp256k1 does support SYNC
|
|
46
|
-
signSchnorr: null,
|
|
47
|
-
verifySchnorr: null,
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @param publicKey {Uint8Array}
|
|
51
|
-
* @returns {boolean}
|
|
52
|
-
*/
|
|
53
|
-
isPoint: (publicKey) => {
|
|
54
|
-
try {
|
|
55
|
-
Point.fromHex(publicKey).assertValidity()
|
|
56
|
-
return true
|
|
57
|
-
} catch {
|
|
58
|
-
return false
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param publicKey {Uint8Array}
|
|
64
|
-
* @returns {boolean}
|
|
65
|
-
*/
|
|
66
|
-
isXOnlyPoint: (publicKey) => {
|
|
67
|
-
try {
|
|
68
|
-
Point.fromHex(toPubKey(publicKey)).assertValidity()
|
|
69
|
-
return true
|
|
70
|
-
} catch {
|
|
71
|
-
return false
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param publicKey {Uint8Array}
|
|
77
|
-
* @param compressed {boolean}
|
|
78
|
-
* @returns {Uint8Array}
|
|
79
|
-
*/
|
|
80
|
-
pointCompress: (publicKey, compressed) => Point.fromHex(publicKey).toRawBytes(compressed),
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
initEccLib(desktopEcc)
|