@exodus/bitcoin-api 2.23.0 → 2.24.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/CHANGELOG.md +14 -0
- package/package.json +2 -2
- package/src/multisig-address.js +12 -5
- package/src/tx-sign/default-sign-hardware.js +40 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.24.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.23.0...@exodus/bitcoin-api@2.24.0) (2024-09-13)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **BTC:** add multisig data to hardware wallet signing ([#3633](https://github.com/ExodusMovement/assets/issues/3633)) ([90293e9](https://github.com/ExodusMovement/assets/commit/90293e9b80799556df1c595558b1544c5a081d7b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **BTC:** allow internal pubkey and sort xonly for multisig ([#3634](https://github.com/ExodusMovement/assets/issues/3634)) ([808bf65](https://github.com/ExodusMovement/assets/commit/808bf65f05576e2758bb32ad5654143577db7c38))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
## [2.23.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.22.1...@exodus/bitcoin-api@2.23.0) (2024-09-11)
|
|
7
21
|
|
|
8
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bitcoin-api",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "Exodus bitcoin-api",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"type": "git",
|
|
62
62
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ba34f68e632d17fdd80cc50358792f449e92180a"
|
|
65
65
|
}
|
package/src/multisig-address.js
CHANGED
|
@@ -5,14 +5,14 @@ import { toXOnly } from './bitcoinjs-lib/ecc-utils.js'
|
|
|
5
5
|
|
|
6
6
|
// Key to use when key path spending is disabled https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
|
|
7
7
|
const DUMMY_TAPROOT_PUBKEY = Buffer.from(
|
|
8
|
-
'
|
|
8
|
+
'0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
|
|
9
9
|
'hex'
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
// Leaf version for BIP342 is 0xc0 or 192 https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#specification
|
|
13
13
|
const LEAF_VERSION_TAPSCRIPT = 192
|
|
14
14
|
|
|
15
|
-
// Limit multisig keys to
|
|
15
|
+
// Limit multisig keys to 16 for now
|
|
16
16
|
const MAX_PUBKEYS = 20
|
|
17
17
|
|
|
18
18
|
export const createEncodeMultisigContract =
|
|
@@ -21,7 +21,14 @@ export const createEncodeMultisigContract =
|
|
|
21
21
|
network = bitcoinjsLib.Network.bitcoin,
|
|
22
22
|
ecc = defaultEcc,
|
|
23
23
|
}) =>
|
|
24
|
-
(
|
|
24
|
+
(
|
|
25
|
+
publicKeys,
|
|
26
|
+
{
|
|
27
|
+
threshold = publicKeys.length,
|
|
28
|
+
version = 0,
|
|
29
|
+
internalPubkey = DUMMY_TAPROOT_PUBKEY,
|
|
30
|
+
} = Object.create(null)
|
|
31
|
+
) => {
|
|
25
32
|
if (
|
|
26
33
|
!Array.isArray(publicKeys) ||
|
|
27
34
|
publicKeys.some((k) => !Buffer.isBuffer(k) || !ecc.isPointCompressed(k))
|
|
@@ -57,7 +64,7 @@ export const createEncodeMultisigContract =
|
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
// Sort according to BIP67 https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
|
|
60
|
-
publicKeys.sort((a, b) => Buffer.compare(a, b))
|
|
67
|
+
publicKeys.sort((a, b) => Buffer.compare(toXOnly(a), toXOnly(b)))
|
|
61
68
|
|
|
62
69
|
// Create multisig redeem script https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#cite_note-5
|
|
63
70
|
const OPS = bitcoinjsLib.script.OPS
|
|
@@ -74,7 +81,7 @@ export const createEncodeMultisigContract =
|
|
|
74
81
|
const output = bitcoinjsLib.script.compile(chunks)
|
|
75
82
|
|
|
76
83
|
return bitcoinjsLib.payments.p2tr({
|
|
77
|
-
internalPubkey:
|
|
84
|
+
internalPubkey: toXOnly(internalPubkey),
|
|
78
85
|
scriptTree: { output },
|
|
79
86
|
redeem: { output, redeemVersion: LEAF_VERSION_TAPSCRIPT },
|
|
80
87
|
network,
|
|
@@ -20,7 +20,7 @@ export const signHardwareFactory = ({ assetName, resolvePurpose, keys, coinInfo
|
|
|
20
20
|
resolvePurpose,
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
return async ({ unsignedTx, hardwareDevice, accountIndex }) => {
|
|
23
|
+
return async ({ unsignedTx, hardwareDevice, accountIndex, multisigData }) => {
|
|
24
24
|
assert(unsignedTx, 'unsignedTx is required')
|
|
25
25
|
assert(hardwareDevice, 'hardwareDevice is required')
|
|
26
26
|
assert(Number.isInteger(accountIndex), 'accountIndex must be integer')
|
|
@@ -37,35 +37,65 @@ export const signHardwareFactory = ({ assetName, resolvePurpose, keys, coinInfo
|
|
|
37
37
|
addressPathsMap,
|
|
38
38
|
hardwareDevice,
|
|
39
39
|
accountIndex,
|
|
40
|
+
multisigData,
|
|
40
41
|
})
|
|
41
42
|
|
|
42
|
-
const skipFinalize = !!unsignedTx.txData.psbtBuffer
|
|
43
|
+
const skipFinalize = !!unsignedTx.txData.psbtBuffer || unsignedTx.txMeta.returnPsbt
|
|
43
44
|
return extractTransaction({ psbt, skipFinalize })
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
function createSignWithHardwareWallet({ assetName, resolvePurpose }) {
|
|
48
|
-
return async ({
|
|
49
|
-
|
|
49
|
+
return async ({
|
|
50
|
+
psbt,
|
|
51
|
+
inputsToSign,
|
|
52
|
+
addressPathsMap,
|
|
53
|
+
accountIndex,
|
|
54
|
+
hardwareDevice,
|
|
55
|
+
multisigData,
|
|
56
|
+
}) => {
|
|
57
|
+
const derivationPathsMap = getDerivationPathsMap({
|
|
58
|
+
resolvePurpose,
|
|
59
|
+
addressPathsMap,
|
|
60
|
+
accountIndex,
|
|
61
|
+
})
|
|
50
62
|
const signatures = await hardwareDevice.signTransaction({
|
|
51
63
|
assetName,
|
|
52
64
|
signableTransaction: psbt.toBuffer(),
|
|
53
|
-
derivationPaths,
|
|
65
|
+
derivationPaths: Object.values(derivationPathsMap),
|
|
66
|
+
derivationPathsMap,
|
|
67
|
+
multisigData,
|
|
54
68
|
})
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
if (multisigData) {
|
|
71
|
+
applyMultisigSignatures(psbt, signatures)
|
|
72
|
+
} else {
|
|
73
|
+
applySignatures(psbt, signatures, inputsToSign)
|
|
74
|
+
}
|
|
57
75
|
}
|
|
58
76
|
}
|
|
59
77
|
|
|
60
|
-
function
|
|
61
|
-
const
|
|
78
|
+
function getDerivationPathsMap({ resolvePurpose, accountIndex, addressPathsMap }) {
|
|
79
|
+
const derivationPathsMap = {}
|
|
62
80
|
for (const [address, path] of Object.entries(addressPathsMap)) {
|
|
63
81
|
const purpose = resolvePurpose(address)
|
|
64
82
|
const derivationPath = `m/${purpose}'/0'/${accountIndex}'/${path.slice(2)}` // TODO: coinindex
|
|
65
|
-
|
|
83
|
+
derivationPathsMap[address] = derivationPath
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
return
|
|
86
|
+
return derivationPathsMap
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function applyMultisigSignatures(psbt, signatures) {
|
|
90
|
+
for (const signature of signatures) {
|
|
91
|
+
const input = psbt.data.inputs[signature.inputIndex]
|
|
92
|
+
if (!input.tapScriptSig) input.tapScriptSig = []
|
|
93
|
+
input.tapScriptSig.push({
|
|
94
|
+
pubkey: signature.publicKey,
|
|
95
|
+
signature: signature.signature,
|
|
96
|
+
leafHash: signature.tapleafHash,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
export function applySignatures(psbt, signatures, inputsToSign) {
|