@exodus/bitcoin-api 2.4.1 → 2.5.0-alpha.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.4.1",
3
+ "version": "2.5.0-alpha.0",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -22,13 +22,13 @@
22
22
  "@exodus/basic-utils": "^2.0.1",
23
23
  "@exodus/bip-schnorr": "0.6.6-fork-1",
24
24
  "@exodus/bip44-constants": "^195.0.0",
25
- "@exodus/bitcoinjs-lib": "6.0.2-beta.5",
26
25
  "@exodus/models": "^8.10.4",
27
26
  "@exodus/secp256k1": "4.0.2-exodus.0",
28
27
  "@exodus/simple-retry": "0.0.6",
29
28
  "@exodus/timer": "^1.0.0",
30
29
  "@noble/secp256k1": "~1.5.3",
31
30
  "bech32": "^1.1.3",
31
+ "bitcoinjs-lib": "^6.1.5",
32
32
  "coininfo": "5.1.0",
33
33
  "delay": "4.0.1",
34
34
  "ecpair": "2.0.1",
@@ -40,5 +40,6 @@
40
40
  "devDependencies": {
41
41
  "@exodus/bitcoin-meta": "^1.0.1",
42
42
  "jest-when": "^3.5.1"
43
- }
43
+ },
44
+ "gitHead": "04641210f5ac2a4b490cf348b2917b28cf463ba2"
44
45
  }
@@ -1,4 +1,5 @@
1
1
  import secp256k1 from '@exodus/secp256k1'
2
+ import { toXOnly } from '../../tx-sign/taproot'
2
3
 
3
4
  /**
4
5
  * Common ecc functions between mobile and desktop. Once mobile accepts @noble/secp256k1, we can unify both
@@ -24,7 +25,7 @@ export const common = {
24
25
  const t = secp256k1.publicKeyTweakAdd(toPubKey(p), tweak)
25
26
  return {
26
27
  parity: t[0] === 0x02 ? 0 : 1,
27
- xOnlyPubkey: t.slice(1, 33),
28
+ xOnlyPubkey: toXOnly(t),
28
29
  }
29
30
  } catch (err) {
30
31
  return null
@@ -1,4 +1,4 @@
1
- import { TinySecp256k1Interface } from '@exodus/bitcoinjs-lib'
1
+ import { TinySecp256k1Interface, initEccLib } from 'bitcoinjs-lib'
2
2
  import { Point, schnorr, sign } from '@noble/secp256k1'
3
3
  import { common, toPubKey } from './common'
4
4
 
@@ -48,3 +48,5 @@ export const desktopEcc: TinySecp256k1Interface = {
48
48
  pointCompress: (p: Uint8Array, compressed?: boolean): Uint8Array =>
49
49
  Point.fromHex(p).toRawBytes(compressed),
50
50
  }
51
+
52
+ initEccLib(desktopEcc)
@@ -1,4 +1,4 @@
1
- import { TinySecp256k1Interface } from '@exodus/bitcoinjs-lib'
1
+ import { TinySecp256k1Interface, initEccLib } from 'bitcoinjs-lib'
2
2
  import secp256k1 from '@exodus/secp256k1'
3
3
  // TODO: temp import until '@noble/secp256k1' can be used
4
4
  import { isPoint } from 'tiny-secp256k1'
@@ -56,3 +56,5 @@ export const mobileEcc: TinySecp256k1Interface = {
56
56
  pointCompress: (p: Uint8Array, compressed?: boolean): Uint8Array =>
57
57
  secp256k1.publicKeyConvert(p, compressed),
58
58
  }
59
+
60
+ initEccLib(mobileEcc)
@@ -1,4 +1,4 @@
1
- import { payments } from '@exodus/bitcoinjs-lib'
1
+ import { payments } from 'bitcoinjs-lib'
2
2
  import assert from 'minimalistic-assert'
3
3
 
4
4
  function isPaymentFactory(payment: any): (script: Buffer, eccLib?: any) => boolean {
@@ -2,7 +2,7 @@ import bs58check from 'bs58check'
2
2
  import * as bech32 from 'bech32'
3
3
  import assert from 'minimalistic-assert'
4
4
  import { identity, pickBy } from 'lodash'
5
- import * as bitcoinjsOriginal from '@exodus/bitcoinjs-lib'
5
+ import * as bitcoinjsOriginal from 'bitcoinjs-lib'
6
6
 
7
7
  export const createBtcLikeAddress = ({
8
8
  versions,
@@ -3,9 +3,10 @@ import wif from 'wif'
3
3
  import * as bech32 from 'bech32'
4
4
  import assert from 'minimalistic-assert'
5
5
  import { identity, pickBy } from 'lodash'
6
- import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
6
+ import * as defaultBitcoinjsLib from 'bitcoinjs-lib'
7
7
  import secp256k1 from 'secp256k1'
8
8
  import { hash160 } from './hash-utils'
9
+ import { toXOnly } from './tx-sign/taproot'
9
10
 
10
11
  export const publicKeyToHashFactory = (p2pkh) => (publicKey) => {
11
12
  const payload = Buffer.concat([Buffer.from([p2pkh]), hash160(publicKey)])
@@ -91,7 +92,7 @@ export const createBtcLikeKeys = ({
91
92
  ? (publicKey: Buffer): string => {
92
93
  const network = coinInfo.toBitcoinJS()
93
94
  return bitcoinjsLib.payments.p2tr(
94
- { internalPubkey: publicKey.slice(1, 33), network },
95
+ { internalPubkey: toXOnly(publicKey), network },
95
96
  { eccLib: ecc }
96
97
  ).address
97
98
  }
@@ -2,7 +2,7 @@ import assert from 'minimalistic-assert'
2
2
  import BIPPath from 'bip32-path'
3
3
  import BN from 'bn.js'
4
4
  import lodash from 'lodash'
5
- import { Transaction as BitcoinTransactionClass } from '@exodus/bitcoinjs-lib'
5
+ import { Transaction as BitcoinTransactionClass } from 'bitcoinjs-lib'
6
6
 
7
7
  export const parseUnsignedTxFactory = ({ Transaction = BitcoinTransactionClass } = {}) => async ({
8
8
  asset,
@@ -16,7 +16,7 @@ import {
16
16
  import { findUnconfirmedSentRbfTxs } from '../tx-utils'
17
17
  import { getOrdinalsUtxos, getUsableUtxos, getUtxos } from '../utxos-utils'
18
18
 
19
- import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
19
+ import * as defaultBitcoinjsLib from 'bitcoinjs-lib'
20
20
 
21
21
  const ASSETS_SUPPORTED_BIP_174 = [
22
22
  'bitcoin',
@@ -1,12 +1,12 @@
1
1
  import assert from 'minimalistic-assert'
2
2
  import lodash from 'lodash'
3
3
  import ECPairFactory from 'ecpair'
4
- import { payments, Psbt, Transaction } from '@exodus/bitcoinjs-lib'
4
+ import { payments, Psbt, Transaction } from 'bitcoinjs-lib'
5
5
  import { getOwnProperty } from '@exodus/basic-utils'
6
6
 
7
7
  import secp256k1 from 'secp256k1'
8
8
 
9
- import { toAsyncSigner, tweakSigner } from './taproot'
9
+ import { toAsyncSigner, toXOnly, tweakSigner } from './taproot'
10
10
 
11
11
  let ECPair
12
12
 
@@ -63,7 +63,8 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
63
63
  const privateKey = getOwnProperty(privateKeysAddressMap, address, 'string')
64
64
  assert(privateKey, `there is no private key for address ${address}`)
65
65
  const key = ECPair.fromWIF(privateKey, networkInfo)
66
- return { key, purpose }
66
+ const publicKey = secp256k1.publicKeyCreate(key.privateKey, true)
67
+ return { key, purpose, publicKey }
67
68
  }
68
69
  const path = addressPathsMap[address]
69
70
  assert(hdkeys, 'hdkeys must be provided')
@@ -72,18 +73,24 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
72
73
  assert(hdkey, `hdkey for purpose for ${purpose} and address ${address} could not be resolved`)
73
74
  const derivedhdkey = hdkey.derive(path)
74
75
  const privateEncoded = keys.encodePrivate(derivedhdkey.privateKey)
75
- return { key: ECPair.fromWIF(privateEncoded, networkInfo), purpose }
76
+ const key = ECPair.fromWIF(privateEncoded, networkInfo)
77
+ const publicKey = derivedhdkey.publicKey
78
+ return { key, publicKey, purpose }
76
79
  })
77
80
 
78
81
  // Fill tx
79
82
  for (const { txId, vout, address, value, script, sequence } of inputs) {
80
- const { purpose } = getKeyAndPurpose(address)
83
+ const { purpose, publicKey } = getKeyAndPurpose(address)
84
+
81
85
  const isSegwitAddress = purpose === 84
82
86
  const isTaprootAddress = purpose === 86
83
87
  const txIn = { hash: txId, index: vout, sequence }
84
88
  if (isSegwitAddress || isTaprootAddress) {
85
89
  // witness outputs only require the value and the script, not the full transaction
86
90
  txIn.witnessUtxo = { value, script: Buffer.from(script, 'hex') }
91
+ if (isTaprootAddress) {
92
+ txIn.tapInternalKey = toXOnly(publicKey)
93
+ }
87
94
  } else {
88
95
  const rawTx = (rawTxs || []).find((t) => t.txId === txId)
89
96
  // non-witness outptus require the full transaction
@@ -109,14 +116,13 @@ export const signTxFactory = ({ assetName, resolvePurpose, keys, coinInfo, netwo
109
116
  // so signing is only done AFTER all inputs have been updated
110
117
  for (let index = 0; index < inputs.length; index++) {
111
118
  const { address } = inputs[index]
112
- const { key, purpose } = getKeyAndPurpose(address)
119
+ const { key, purpose, publicKey } = getKeyAndPurpose(address)
113
120
 
114
121
  if (purpose === 49) {
115
122
  // If spending from a P2SH address, we assume the address is P2SH wrapping
116
123
  // P2WPKH. Exodus doesn't use P2SH addresses so we should only ever be
117
124
  // signing a P2SH input if we are importing a private key
118
125
  // BIP143: As a default policy, only compressed public keys are accepted in P2WPKH and P2WSH
119
- const publicKey = secp256k1.publicKeyCreate(key.privateKey, true)
120
126
  const p2wpkh = payments.p2wpkh({ pubkey: publicKey })
121
127
  const p2sh = payments.p2sh({ redeem: p2wpkh })
122
128
  psbt.updateInput(index, {
@@ -1,4 +1,4 @@
1
- import { crypto } from '@exodus/bitcoinjs-lib'
1
+ import { crypto } from 'bitcoinjs-lib'
2
2
  import assert from 'minimalistic-assert'
3
3
  import { getSchnorrEntropy } from './default-entropy'
4
4
 
@@ -16,7 +16,7 @@ export function tweakSigner({ signer, ECPair, ecc, tweakHash, network }) {
16
16
 
17
17
  const tweakedPrivateKey = ecc.privateAdd(
18
18
  privateKey,
19
- tapTweakHash(signer.publicKey.slice(1, 33), tweakHash)
19
+ tapTweakHash(toXOnly(signer.publicKey), tweakHash)
20
20
  )
21
21
  if (!tweakedPrivateKey) {
22
22
  throw new Error('Invalid tweaked private key!')
@@ -48,3 +48,7 @@ export function toAsyncSigner({ keyPair, ecc }) {
48
48
  }
49
49
  return keyPair
50
50
  }
51
+
52
+ export const toXOnly = (publicKey) => {
53
+ return publicKey.slice(1, 33)
54
+ }