@teleportdao/bitcoin 1.7.7 → 1.7.10
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/dist/helper/teleswap-helper.d.ts.map +1 -1
- package/dist/helper/teleswap-helper.js +4 -3
- package/dist/helper/teleswap-helper.js.map +1 -1
- package/package.json +3 -3
- package/src/bitcoin-interface-ordinal.ts +181 -181
- package/src/bitcoin-interface-teleswap.ts +252 -252
- package/src/bitcoin-interface-utils.ts +59 -59
- package/src/bitcoin-interface.ts +247 -247
- package/src/bitcoin-utils.ts +591 -591
- package/src/bitcoin-wallet-base.ts +310 -310
- package/src/helper/brc20-helper.ts +181 -181
- package/src/helper/ordinal-helper.ts +118 -118
- package/src/helper/teleswap-helper.ts +11 -3
- package/src/index.ts +15 -15
- package/src/ordinal-wallet.ts +738 -738
- package/src/sign/index.ts +1 -1
- package/src/sign/sign-transaction.ts +108 -108
- package/src/teleswap-wallet.ts +152 -152
- package/src/transaction-builder/bitcoin-transaction-builder.ts +44 -44
- package/src/transaction-builder/index.ts +3 -3
- package/src/transaction-builder/ordinal-transaction-builder.ts +147 -147
- package/src/transaction-builder/transaction-builder.ts +706 -706
- package/src/type.ts +43 -43
- package/src/utils/networks.ts +33 -33
- package/src/utils/tools.ts +89 -89
- package/tsconfig.json +9 -9
- package/webpack.config.js +16 -16
- package/dist/bitcoin-base.d.ts +0 -93
- package/dist/bitcoin-base.d.ts.map +0 -1
- package/dist/bitcoin-base.js +0 -236
- package/dist/bitcoin-base.js.map +0 -1
- package/dist/helper/burn-request-helper.d.ts +0 -7
- package/dist/helper/burn-request-helper.d.ts.map +0 -1
- package/dist/helper/burn-request-helper.js +0 -26
- package/dist/helper/burn-request-helper.js.map +0 -1
- package/dist/helper/teleport-request-helper.d.ts +0 -47
- package/dist/helper/teleport-request-helper.d.ts.map +0 -1
- package/dist/helper/teleport-request-helper.js +0 -146
- package/dist/helper/teleport-request-helper.js.map +0 -1
- package/dist/teleport-dao-payments.d.ts +0 -76
- package/dist/teleport-dao-payments.d.ts.map +0 -1
- package/dist/teleport-dao-payments.js +0 -217
- package/dist/teleport-dao-payments.js.map +0 -1
package/src/bitcoin-utils.ts
CHANGED
|
@@ -1,591 +1,591 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
-
import * as bip39 from "bip39"
|
|
3
|
-
import BIP32Factory from "bip32"
|
|
4
|
-
import * as ecc from "@bitcoinerlab/secp256k1"
|
|
5
|
-
import * as bitcoin from "bitcoinjs-lib"
|
|
6
|
-
import * as bitcoinEcPair from "bitcoinjs-ecpair"
|
|
7
|
-
import networks from "./utils/networks"
|
|
8
|
-
import { Transaction } from "./type"
|
|
9
|
-
|
|
10
|
-
const bip32 = BIP32Factory(ecc)
|
|
11
|
-
const { ECPair } = bitcoinEcPair
|
|
12
|
-
|
|
13
|
-
bitcoin.initEccLib(ecc)
|
|
14
|
-
|
|
15
|
-
const varUnit = require("varuint-bitcoin")
|
|
16
|
-
const fastRoot = require("merkle-lib/fastRoot")
|
|
17
|
-
const merkle = require("merkle-lib")
|
|
18
|
-
const merkleProof = require("merkle-lib/proof")
|
|
19
|
-
|
|
20
|
-
// const bip32Prefix = {
|
|
21
|
-
// xprv: "0x0488ade4", // Mainnet - P2PKH or P2SH - m/44'/0'
|
|
22
|
-
// yprv: "0x049d7878", // Mainnet - P2WPKH in P2SH - m/49'/0'
|
|
23
|
-
// zprv: "0x04b2430c", // Mainnet - P2WPKH - m/84'/0'
|
|
24
|
-
// Yprv: "0x0295b005", // Mainnet - Multi-signature P2WSH in P2SH
|
|
25
|
-
// Zprv: "0x02aa7a99", // Mainnet - Multi-signature P2WSH
|
|
26
|
-
// tprv: "0x04358394", // Testnet - P2PKH or P2SH - m/44'/1'
|
|
27
|
-
// uprv: "0x044a4e28", // Testnet - P2WPKH in P2SH - m/49'/1'
|
|
28
|
-
// vprv: "0x045f18bc", // Testnet - P2WPKH - m/84'/1'
|
|
29
|
-
// Uprv: "0x024285b5", // Testnet - Multi-signature P2WSH in P2SH
|
|
30
|
-
// Vprv: "0x02575048", // Testnet - Multi-signature P2WSH
|
|
31
|
-
|
|
32
|
-
// xpub: "0x0488b21e", // Mainnet - P2PKH or P2SH - m/44'/0'
|
|
33
|
-
// ypub: "0x049d7cb2", // Mainnet - P2WPKH in P2SH - m/49'/0'
|
|
34
|
-
// zpub: "0x04b24746", // Mainnet - P2WPKH - m/84'/0'
|
|
35
|
-
// Ypub: "0x0295b43f", // Mainnet - Multi-signature P2WSH in P2SH
|
|
36
|
-
// Zpub: "0x02aa7ed3", // Mainnet - Multi-signature P2WSH
|
|
37
|
-
// tpub: "0x043587cf", // Testnet - P2PKH or P2SH - m/44'/1'
|
|
38
|
-
// upub: "0x044a5262", // Testnet - P2WPKH in P2SH - m/49'/1'
|
|
39
|
-
// vpub: "0x045f1cf6", // Testnet - P2WPKH - m/84'/1'
|
|
40
|
-
// Upub: "0x024289ef", // Testnet - Multi-signature P2WSH in P2SH
|
|
41
|
-
// Vpub: "0x02575483", // Testnet - Multi-signature P2WSH
|
|
42
|
-
// }
|
|
43
|
-
export { networks }
|
|
44
|
-
|
|
45
|
-
export function generateMnemonic() {
|
|
46
|
-
const mnemonic = bip39.generateMnemonic(256)
|
|
47
|
-
return mnemonic
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export async function getPrivateKeyHexFromMnemonic(mnemonic: string, index = 0, isChange = false) {
|
|
51
|
-
const node = bip32.fromSeed(await bip39.mnemonicToSeed(mnemonic))
|
|
52
|
-
return node
|
|
53
|
-
.derive(isChange ? 1 : 0)
|
|
54
|
-
.derive(index)
|
|
55
|
-
.privateKey!.toString("hex")
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function reverseBytes(hexInput: string) {
|
|
59
|
-
return Buffer.from(hexInput, "hex").reverse().toString("hex")
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function getPublicKeyHexByXpubAndIndex(
|
|
63
|
-
xpub: string,
|
|
64
|
-
index = 0,
|
|
65
|
-
isChange = false,
|
|
66
|
-
network = networks.bitcoin,
|
|
67
|
-
) {
|
|
68
|
-
const node = bip32.fromBase58(xpub, network)
|
|
69
|
-
return node
|
|
70
|
-
.derive(isChange ? 1 : 0)
|
|
71
|
-
.derive(index)
|
|
72
|
-
.publicKey.toString("hex")
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function getPubKeyFromPrivateKeyWIF(
|
|
76
|
-
privateKeyWIF: string,
|
|
77
|
-
network = bitcoin.networks.bitcoin,
|
|
78
|
-
) {
|
|
79
|
-
let key = ECPair.fromWIF(privateKeyWIF, network)
|
|
80
|
-
return key.publicKey
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function getPubKeyFromPrivateKeyHex(
|
|
84
|
-
privateKeyHex: string,
|
|
85
|
-
network = bitcoin.networks.bitcoin,
|
|
86
|
-
) {
|
|
87
|
-
let key = ECPair.fromPrivateKey(Buffer.from(privateKeyHex, "hex"), { network })
|
|
88
|
-
return key.publicKey
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function getPrivateKeyHexFromWIF(privateKeyWIF: string, network = bitcoin.networks.bitcoin) {
|
|
92
|
-
let key = ECPair.fromWIF(privateKeyWIF, network)
|
|
93
|
-
return key.privateKey!.toString("hex")
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function parseRawTransaction(rawTransaction: string) {
|
|
97
|
-
const size = {
|
|
98
|
-
version: 4,
|
|
99
|
-
flag: 2,
|
|
100
|
-
tx: 32,
|
|
101
|
-
index: 4,
|
|
102
|
-
sequence: 4,
|
|
103
|
-
amount: 8,
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let offset = 0
|
|
107
|
-
let version = rawTransaction.slice(offset, size.version * 2)
|
|
108
|
-
offset += size.version * 2
|
|
109
|
-
let flag = rawTransaction.slice(offset, offset + size.flag * 2)
|
|
110
|
-
offset = flag === "0001" ? offset + size.flag * 2 : offset // * 0x0001 is flag in segwit transactions
|
|
111
|
-
|
|
112
|
-
let inputsStartIndex = offset
|
|
113
|
-
|
|
114
|
-
let numberOfInputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
115
|
-
let noiSize = varUnit.encodingLength(numberOfInputs)
|
|
116
|
-
offset += noiSize * 2
|
|
117
|
-
|
|
118
|
-
for (let i = 0; i < numberOfInputs; i += 1) {
|
|
119
|
-
offset += size.tx * 2
|
|
120
|
-
offset += size.index * 2
|
|
121
|
-
let sigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
122
|
-
let sigLengthSize = varUnit.encodingLength(sigLength)
|
|
123
|
-
offset += sigLengthSize * 2
|
|
124
|
-
offset += sigLength * 2
|
|
125
|
-
offset += size.sequence * 2
|
|
126
|
-
}
|
|
127
|
-
let inputLastIndex = offset
|
|
128
|
-
let outputStartIndex = offset
|
|
129
|
-
|
|
130
|
-
let numberOfOutputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
131
|
-
let nooSize = varUnit.encodingLength(numberOfOutputs)
|
|
132
|
-
offset += nooSize * 2
|
|
133
|
-
|
|
134
|
-
for (let i = 0; i < numberOfOutputs; i += 1) {
|
|
135
|
-
offset += size.amount * 2
|
|
136
|
-
let unlockSigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
137
|
-
let unlockSigLengthSize = varUnit.encodingLength(unlockSigLength)
|
|
138
|
-
offset += unlockSigLengthSize * 2
|
|
139
|
-
offset += unlockSigLength * 2
|
|
140
|
-
}
|
|
141
|
-
let outputLastIndex = offset
|
|
142
|
-
|
|
143
|
-
version = `0x${version}`
|
|
144
|
-
flag = `0x${flag}`
|
|
145
|
-
const vin = `0x${rawTransaction.slice(inputsStartIndex, inputLastIndex)}`
|
|
146
|
-
const vout = `0x${rawTransaction.slice(outputStartIndex, outputLastIndex)}`
|
|
147
|
-
let witness = `0x${rawTransaction.slice(outputLastIndex, rawTransaction.length - 8)}`
|
|
148
|
-
let locktime = `0x${rawTransaction.slice(rawTransaction.length - 8, rawTransaction.length)}`
|
|
149
|
-
return { version, flag, vin, vout, witness, locktime }
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function calculateMerkleProof(
|
|
153
|
-
blockTransactions: string[],
|
|
154
|
-
txId: string,
|
|
155
|
-
blockMerkleRoot?: string,
|
|
156
|
-
) {
|
|
157
|
-
let transactionIndex = blockTransactions.findIndex((tx) => tx === txId)
|
|
158
|
-
if (transactionIndex < 0) throw new Error("txId is not in this tree")
|
|
159
|
-
let data = blockTransactions.map((a) => Buffer.from(a, "hex").reverse())
|
|
160
|
-
|
|
161
|
-
if (
|
|
162
|
-
blockMerkleRoot &&
|
|
163
|
-
blockMerkleRoot !== fastRoot(data, bitcoin.crypto.hash256).toString("hex")
|
|
164
|
-
) {
|
|
165
|
-
throw new Error("calculated merkleRoot and block merkleRoot not matched")
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
let tree = merkle(data, bitcoin.crypto.hash256)
|
|
169
|
-
let proof = merkleProof(tree, data[transactionIndex])
|
|
170
|
-
|
|
171
|
-
let intermediateNodesArray = proof
|
|
172
|
-
.map((_id?: Buffer) => _id && _id.toString("hex"))
|
|
173
|
-
.filter((_id?: Buffer) => _id != null)
|
|
174
|
-
let intermediateNodes: string = intermediateNodesArray.reduce(
|
|
175
|
-
(a: string, value: string, index: number) =>
|
|
176
|
-
index !== transactionIndex % 2 && index < intermediateNodesArray.length - 1 ? a + value : a,
|
|
177
|
-
"0x",
|
|
178
|
-
)
|
|
179
|
-
return {
|
|
180
|
-
intermediateNodes,
|
|
181
|
-
transactionIndex,
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export function parseBlockHeader(headerHex: string) {
|
|
186
|
-
const size: {
|
|
187
|
-
[key: string]: number
|
|
188
|
-
} = {
|
|
189
|
-
version: 4,
|
|
190
|
-
previousBlockHash: 32,
|
|
191
|
-
merkleRoot: 32,
|
|
192
|
-
timestamp: 4,
|
|
193
|
-
difficulty: 4,
|
|
194
|
-
nonce: 4,
|
|
195
|
-
}
|
|
196
|
-
let offset = 0
|
|
197
|
-
let result: {
|
|
198
|
-
[key: string]: string
|
|
199
|
-
} = {}
|
|
200
|
-
for (let key in size) {
|
|
201
|
-
result[key] = headerHex.slice(offset, offset + size[key] * 2)
|
|
202
|
-
offset += size[key] * 2
|
|
203
|
-
}
|
|
204
|
-
return result as {
|
|
205
|
-
version: string
|
|
206
|
-
previousBlockHash: string
|
|
207
|
-
merkleRoot: string
|
|
208
|
-
timestamp: string
|
|
209
|
-
difficulty: string
|
|
210
|
-
nonce: string
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function getAddressType(address: string, network = bitcoin.networks.bitcoin) {
|
|
215
|
-
if (address.startsWith(`${network.bech32}1p`)) {
|
|
216
|
-
// todo validate p2tr address
|
|
217
|
-
return "p2tr"
|
|
218
|
-
}
|
|
219
|
-
if (address.startsWith(network.bech32)) {
|
|
220
|
-
bitcoin.address.fromBech32(address)
|
|
221
|
-
return "p2wpkh"
|
|
222
|
-
}
|
|
223
|
-
let base58Data = bitcoin.address.fromBase58Check(address)
|
|
224
|
-
if (base58Data.version === Number(network.scriptHash)) {
|
|
225
|
-
return "p2sh"
|
|
226
|
-
}
|
|
227
|
-
if (base58Data.version === Number(network.pubKeyHash)) {
|
|
228
|
-
return "p2pkh"
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
throw new Error("invalid address")
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function createAddressObjectByAddress(address: string, network = bitcoin.networks.bitcoin) {
|
|
235
|
-
let addressType = getAddressType(address, network)
|
|
236
|
-
let addressObject
|
|
237
|
-
switch (addressType) {
|
|
238
|
-
case "p2pkh":
|
|
239
|
-
addressObject = bitcoin.payments.p2pkh({
|
|
240
|
-
address,
|
|
241
|
-
network,
|
|
242
|
-
})
|
|
243
|
-
break
|
|
244
|
-
case "p2wpkh":
|
|
245
|
-
addressObject = bitcoin.payments.p2wpkh({
|
|
246
|
-
address,
|
|
247
|
-
network,
|
|
248
|
-
})
|
|
249
|
-
break
|
|
250
|
-
case "p2sh":
|
|
251
|
-
addressObject = bitcoin.payments.p2sh({
|
|
252
|
-
address,
|
|
253
|
-
network,
|
|
254
|
-
})
|
|
255
|
-
break
|
|
256
|
-
case "p2tr":
|
|
257
|
-
addressObject = bitcoin.payments.p2tr({
|
|
258
|
-
address,
|
|
259
|
-
network,
|
|
260
|
-
})
|
|
261
|
-
break
|
|
262
|
-
default:
|
|
263
|
-
throw new Error("address type is incorrect")
|
|
264
|
-
}
|
|
265
|
-
return { addressObject, addressType }
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// used in block parser functions ( newt 2 functions )
|
|
269
|
-
export function convertBitcoinScriptToAddress(script: Buffer, network = bitcoin.networks.bitcoin) {
|
|
270
|
-
try {
|
|
271
|
-
return bitcoin.address.fromOutputScript(script, network)
|
|
272
|
-
} catch (error) {
|
|
273
|
-
return undefined
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function parseRawBlock(
|
|
278
|
-
rawBlockHex: string,
|
|
279
|
-
blockNumber?: number,
|
|
280
|
-
network = bitcoin.networks.bitcoin,
|
|
281
|
-
) {
|
|
282
|
-
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
283
|
-
let blockHash = block.getHash().toString("hex")
|
|
284
|
-
let merkleRoot = block.merkleRoot!.toString("hex")
|
|
285
|
-
let prvBlockHash = block.prevHash!.toString("hex")
|
|
286
|
-
return {
|
|
287
|
-
blockNumber,
|
|
288
|
-
merkleRoot,
|
|
289
|
-
prvBlockHash,
|
|
290
|
-
transactions: block.transactions?.map((tx) => ({
|
|
291
|
-
txId: tx.getId(),
|
|
292
|
-
version: tx.version,
|
|
293
|
-
locktime: tx.locktime,
|
|
294
|
-
blockNumber,
|
|
295
|
-
blockHash,
|
|
296
|
-
vout: tx.outs.map((vo, index) => ({
|
|
297
|
-
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
298
|
-
script: vo.script.toString("hex"),
|
|
299
|
-
value: vo.value,
|
|
300
|
-
index,
|
|
301
|
-
})),
|
|
302
|
-
vin: tx.ins.map((vi) => ({
|
|
303
|
-
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
304
|
-
index: vi.index,
|
|
305
|
-
})),
|
|
306
|
-
})),
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function extractTransactionsAndBlockInfoFromRawBlock(
|
|
311
|
-
rawBlockHex: string,
|
|
312
|
-
blockNumber: number,
|
|
313
|
-
addresses: string[] = [],
|
|
314
|
-
inputTxIds: {
|
|
315
|
-
txId: string
|
|
316
|
-
index: number
|
|
317
|
-
address: string
|
|
318
|
-
script?: string
|
|
319
|
-
value?: number
|
|
320
|
-
}[] = [],
|
|
321
|
-
network = bitcoin.networks.bitcoin,
|
|
322
|
-
) {
|
|
323
|
-
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
324
|
-
let blockHash = block.getHash().reverse().toString("hex")
|
|
325
|
-
let merkleRoot = block.merkleRoot!.toString("hex")
|
|
326
|
-
let prvBlockHash = block.prevHash!.toString("hex")
|
|
327
|
-
|
|
328
|
-
let blockInfo = {
|
|
329
|
-
blockNumber,
|
|
330
|
-
blockHash,
|
|
331
|
-
merkleRoot,
|
|
332
|
-
prvBlockHash,
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
let addressScript = addresses.map((address) =>
|
|
336
|
-
createAddressObjectByAddress(address, network).addressObject.output!.toString("hex"),
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
let blockTxIds = block.transactions!.map((tx) => tx.getId())
|
|
340
|
-
|
|
341
|
-
let withdrawTxs: Transaction[] = []
|
|
342
|
-
let depositTxs: Transaction[] = []
|
|
343
|
-
block.transactions?.forEach((tx) => {
|
|
344
|
-
let txId = tx.getId()
|
|
345
|
-
|
|
346
|
-
let inputTxAddress: string | undefined
|
|
347
|
-
let isWithdraw = tx.ins.find((vi) => {
|
|
348
|
-
let viTxId = Buffer.from(vi.hash).reverse().toString("hex")
|
|
349
|
-
let viInput = inputTxIds.find((vin) => vin.txId === viTxId && vin.index === vi.index)
|
|
350
|
-
inputTxAddress = viInput?.address
|
|
351
|
-
return !!inputTxAddress
|
|
352
|
-
})
|
|
353
|
-
if (isWithdraw && inputTxAddress) {
|
|
354
|
-
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
355
|
-
|
|
356
|
-
withdrawTxs.push({
|
|
357
|
-
txId: tx.getId(),
|
|
358
|
-
version: tx.version,
|
|
359
|
-
locktime: tx.locktime,
|
|
360
|
-
blockNumber,
|
|
361
|
-
blockHash,
|
|
362
|
-
merkleProof: txMerkleProof,
|
|
363
|
-
vout: tx.outs.map((vo) => ({
|
|
364
|
-
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
365
|
-
script: vo.script.toString("hex"),
|
|
366
|
-
value: vo.value,
|
|
367
|
-
})),
|
|
368
|
-
vin: tx.ins.map((vi) => {
|
|
369
|
-
let viTxId = Buffer.from(vi.hash).reverse().toString("hex")
|
|
370
|
-
let viInput = inputTxIds.find((vin) => vin.txId === viTxId && vin.index === vi.index)
|
|
371
|
-
return {
|
|
372
|
-
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
373
|
-
index: vi.index,
|
|
374
|
-
address: viInput?.address,
|
|
375
|
-
script: viInput?.script,
|
|
376
|
-
value: viInput?.value,
|
|
377
|
-
}
|
|
378
|
-
}),
|
|
379
|
-
address: inputTxAddress,
|
|
380
|
-
addressScript: createAddressObjectByAddress(
|
|
381
|
-
inputTxAddress,
|
|
382
|
-
network,
|
|
383
|
-
).addressObject.output!.toString("hex"),
|
|
384
|
-
})
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
let addressIndex: number = -1
|
|
388
|
-
let isDeposit = tx.outs.find((blockTxVo) => {
|
|
389
|
-
let sIndex = addressScript.findIndex(
|
|
390
|
-
(addScript) => addScript === blockTxVo.script.toString("hex"),
|
|
391
|
-
)
|
|
392
|
-
if (sIndex >= 0) {
|
|
393
|
-
addressIndex = sIndex
|
|
394
|
-
return true
|
|
395
|
-
}
|
|
396
|
-
return false
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
if (isDeposit && addressIndex >= 0) {
|
|
400
|
-
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
401
|
-
depositTxs.push({
|
|
402
|
-
txId: tx.getId(),
|
|
403
|
-
version: tx.version,
|
|
404
|
-
locktime: tx.locktime,
|
|
405
|
-
blockNumber,
|
|
406
|
-
blockHash,
|
|
407
|
-
merkleProof: txMerkleProof,
|
|
408
|
-
vout: tx.outs.map((vo) => ({
|
|
409
|
-
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
410
|
-
script: vo.script.toString("hex"),
|
|
411
|
-
value: vo.value,
|
|
412
|
-
})),
|
|
413
|
-
vin: tx.ins.map((vi) => ({
|
|
414
|
-
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
415
|
-
index: vi.index,
|
|
416
|
-
})),
|
|
417
|
-
addressScript: addressScript[addressIndex],
|
|
418
|
-
address: addresses[addressIndex],
|
|
419
|
-
})
|
|
420
|
-
}
|
|
421
|
-
})
|
|
422
|
-
return {
|
|
423
|
-
blockInfo,
|
|
424
|
-
withdrawTxs,
|
|
425
|
-
depositTxs,
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
export function validateAddress(address: string, network = bitcoin.networks.bitcoin) {
|
|
430
|
-
try {
|
|
431
|
-
let isValid = false
|
|
432
|
-
let isAddressSegwit = address.startsWith(network.bech32)
|
|
433
|
-
if (isAddressSegwit) {
|
|
434
|
-
bitcoin.address.fromBech32(address)
|
|
435
|
-
isValid = true
|
|
436
|
-
} else {
|
|
437
|
-
let base58Data = bitcoin.address.fromBase58Check(address)
|
|
438
|
-
isValid =
|
|
439
|
-
base58Data.version === Number(network.scriptHash) ||
|
|
440
|
-
base58Data.version === Number(network.pubKeyHash)
|
|
441
|
-
}
|
|
442
|
-
return isValid
|
|
443
|
-
} catch (error) {
|
|
444
|
-
return false
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function toXOnlyPublicKey(pubKey: Buffer) {
|
|
449
|
-
return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33)
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
export function createAddressObjectByScript(
|
|
453
|
-
{ addressType, script }: { addressType: string; script: Buffer },
|
|
454
|
-
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
455
|
-
) {
|
|
456
|
-
let addressObject
|
|
457
|
-
switch (addressType) {
|
|
458
|
-
case "p2pkh":
|
|
459
|
-
addressObject = bitcoin.payments.p2pkh({
|
|
460
|
-
output: script,
|
|
461
|
-
network,
|
|
462
|
-
})
|
|
463
|
-
break
|
|
464
|
-
case "p2wpkh":
|
|
465
|
-
addressObject = bitcoin.payments.p2wpkh({
|
|
466
|
-
output: script,
|
|
467
|
-
network,
|
|
468
|
-
})
|
|
469
|
-
break
|
|
470
|
-
case "p2sh":
|
|
471
|
-
addressObject = bitcoin.payments.p2sh({
|
|
472
|
-
output: script,
|
|
473
|
-
network,
|
|
474
|
-
})
|
|
475
|
-
break
|
|
476
|
-
case "p2wsh":
|
|
477
|
-
addressObject = bitcoin.payments.p2wsh({
|
|
478
|
-
output: script,
|
|
479
|
-
network,
|
|
480
|
-
})
|
|
481
|
-
break
|
|
482
|
-
case "p2tr":
|
|
483
|
-
addressObject = bitcoin.payments.p2tr({
|
|
484
|
-
output: script,
|
|
485
|
-
network,
|
|
486
|
-
})
|
|
487
|
-
break
|
|
488
|
-
default:
|
|
489
|
-
throw new Error("address type is incorrect")
|
|
490
|
-
}
|
|
491
|
-
return addressObject
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
export function createAddressObjectByHash(
|
|
495
|
-
{ addressType, hash }: { addressType: string; hash: Buffer },
|
|
496
|
-
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
497
|
-
) {
|
|
498
|
-
let addressObject
|
|
499
|
-
switch (addressType) {
|
|
500
|
-
case "p2pkh":
|
|
501
|
-
addressObject = bitcoin.payments.p2pkh({
|
|
502
|
-
hash,
|
|
503
|
-
network,
|
|
504
|
-
})
|
|
505
|
-
break
|
|
506
|
-
case "p2wpkh":
|
|
507
|
-
addressObject = bitcoin.payments.p2wpkh({
|
|
508
|
-
hash,
|
|
509
|
-
network,
|
|
510
|
-
})
|
|
511
|
-
break
|
|
512
|
-
case "p2sh":
|
|
513
|
-
addressObject = bitcoin.payments.p2sh({
|
|
514
|
-
hash,
|
|
515
|
-
network,
|
|
516
|
-
})
|
|
517
|
-
break
|
|
518
|
-
case "p2wsh":
|
|
519
|
-
addressObject = bitcoin.payments.p2wsh({
|
|
520
|
-
hash,
|
|
521
|
-
network,
|
|
522
|
-
})
|
|
523
|
-
break
|
|
524
|
-
case "p2tr":
|
|
525
|
-
// here hash is the public key of tweaked private key (internal public key)
|
|
526
|
-
addressObject = bitcoin.payments.p2tr({
|
|
527
|
-
pubkey: hash,
|
|
528
|
-
network,
|
|
529
|
-
})
|
|
530
|
-
break
|
|
531
|
-
default:
|
|
532
|
-
throw new Error("address type is incorrect")
|
|
533
|
-
}
|
|
534
|
-
return addressObject
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
export function createAddressObjectByPublicKey(
|
|
538
|
-
{ addressType, publicKey }: { addressType: string; publicKey: Buffer },
|
|
539
|
-
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
540
|
-
): bitcoin.payments.Payment {
|
|
541
|
-
let addressObject
|
|
542
|
-
switch (addressType) {
|
|
543
|
-
case "p2pkh":
|
|
544
|
-
addressObject = bitcoin.payments.p2pkh({
|
|
545
|
-
pubkey: publicKey,
|
|
546
|
-
network,
|
|
547
|
-
})
|
|
548
|
-
break
|
|
549
|
-
case "p2wpkh":
|
|
550
|
-
addressObject = bitcoin.payments.p2wpkh({
|
|
551
|
-
pubkey: publicKey,
|
|
552
|
-
network,
|
|
553
|
-
})
|
|
554
|
-
break
|
|
555
|
-
case "p2sh-p2wpkh":
|
|
556
|
-
addressObject = bitcoin.payments.p2sh({
|
|
557
|
-
redeem: bitcoin.payments.p2wpkh({
|
|
558
|
-
pubkey: publicKey,
|
|
559
|
-
network,
|
|
560
|
-
}),
|
|
561
|
-
})
|
|
562
|
-
break
|
|
563
|
-
case "p2tr":
|
|
564
|
-
// this public key is public key of main private key
|
|
565
|
-
addressObject = bitcoin.payments.p2tr({
|
|
566
|
-
internalPubkey: toXOnlyPublicKey(publicKey),
|
|
567
|
-
network,
|
|
568
|
-
})
|
|
569
|
-
break
|
|
570
|
-
default:
|
|
571
|
-
throw new Error("address type is incorrect")
|
|
572
|
-
}
|
|
573
|
-
return addressObject
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export function publicKeyConvertor(publicKeyHex: string, compressed = true) {
|
|
577
|
-
let pubkey = ECPair.fromPublicKey(Buffer.from(publicKeyHex, "hex"), {
|
|
578
|
-
compressed,
|
|
579
|
-
})
|
|
580
|
-
return pubkey.publicKey.toString("hex")
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
export const addressTypeHelper: {
|
|
584
|
-
addressTypesNumber: {
|
|
585
|
-
[key: string]: number
|
|
586
|
-
}
|
|
587
|
-
addressTypes: string[]
|
|
588
|
-
} = {
|
|
589
|
-
addressTypesNumber: { p2pk: 0, p2pkh: 1, p2sh: 2, p2wpkh: 3, p2wsh: 4, p2tr: 5 },
|
|
590
|
-
addressTypes: ["p2pk", "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"],
|
|
591
|
-
}
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
import * as bip39 from "bip39"
|
|
3
|
+
import BIP32Factory from "bip32"
|
|
4
|
+
import * as ecc from "@bitcoinerlab/secp256k1"
|
|
5
|
+
import * as bitcoin from "bitcoinjs-lib"
|
|
6
|
+
import * as bitcoinEcPair from "bitcoinjs-ecpair"
|
|
7
|
+
import networks from "./utils/networks"
|
|
8
|
+
import { Transaction } from "./type"
|
|
9
|
+
|
|
10
|
+
const bip32 = BIP32Factory(ecc)
|
|
11
|
+
const { ECPair } = bitcoinEcPair
|
|
12
|
+
|
|
13
|
+
bitcoin.initEccLib(ecc)
|
|
14
|
+
|
|
15
|
+
const varUnit = require("varuint-bitcoin")
|
|
16
|
+
const fastRoot = require("merkle-lib/fastRoot")
|
|
17
|
+
const merkle = require("merkle-lib")
|
|
18
|
+
const merkleProof = require("merkle-lib/proof")
|
|
19
|
+
|
|
20
|
+
// const bip32Prefix = {
|
|
21
|
+
// xprv: "0x0488ade4", // Mainnet - P2PKH or P2SH - m/44'/0'
|
|
22
|
+
// yprv: "0x049d7878", // Mainnet - P2WPKH in P2SH - m/49'/0'
|
|
23
|
+
// zprv: "0x04b2430c", // Mainnet - P2WPKH - m/84'/0'
|
|
24
|
+
// Yprv: "0x0295b005", // Mainnet - Multi-signature P2WSH in P2SH
|
|
25
|
+
// Zprv: "0x02aa7a99", // Mainnet - Multi-signature P2WSH
|
|
26
|
+
// tprv: "0x04358394", // Testnet - P2PKH or P2SH - m/44'/1'
|
|
27
|
+
// uprv: "0x044a4e28", // Testnet - P2WPKH in P2SH - m/49'/1'
|
|
28
|
+
// vprv: "0x045f18bc", // Testnet - P2WPKH - m/84'/1'
|
|
29
|
+
// Uprv: "0x024285b5", // Testnet - Multi-signature P2WSH in P2SH
|
|
30
|
+
// Vprv: "0x02575048", // Testnet - Multi-signature P2WSH
|
|
31
|
+
|
|
32
|
+
// xpub: "0x0488b21e", // Mainnet - P2PKH or P2SH - m/44'/0'
|
|
33
|
+
// ypub: "0x049d7cb2", // Mainnet - P2WPKH in P2SH - m/49'/0'
|
|
34
|
+
// zpub: "0x04b24746", // Mainnet - P2WPKH - m/84'/0'
|
|
35
|
+
// Ypub: "0x0295b43f", // Mainnet - Multi-signature P2WSH in P2SH
|
|
36
|
+
// Zpub: "0x02aa7ed3", // Mainnet - Multi-signature P2WSH
|
|
37
|
+
// tpub: "0x043587cf", // Testnet - P2PKH or P2SH - m/44'/1'
|
|
38
|
+
// upub: "0x044a5262", // Testnet - P2WPKH in P2SH - m/49'/1'
|
|
39
|
+
// vpub: "0x045f1cf6", // Testnet - P2WPKH - m/84'/1'
|
|
40
|
+
// Upub: "0x024289ef", // Testnet - Multi-signature P2WSH in P2SH
|
|
41
|
+
// Vpub: "0x02575483", // Testnet - Multi-signature P2WSH
|
|
42
|
+
// }
|
|
43
|
+
export { networks }
|
|
44
|
+
|
|
45
|
+
export function generateMnemonic() {
|
|
46
|
+
const mnemonic = bip39.generateMnemonic(256)
|
|
47
|
+
return mnemonic
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getPrivateKeyHexFromMnemonic(mnemonic: string, index = 0, isChange = false) {
|
|
51
|
+
const node = bip32.fromSeed(await bip39.mnemonicToSeed(mnemonic))
|
|
52
|
+
return node
|
|
53
|
+
.derive(isChange ? 1 : 0)
|
|
54
|
+
.derive(index)
|
|
55
|
+
.privateKey!.toString("hex")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function reverseBytes(hexInput: string) {
|
|
59
|
+
return Buffer.from(hexInput, "hex").reverse().toString("hex")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getPublicKeyHexByXpubAndIndex(
|
|
63
|
+
xpub: string,
|
|
64
|
+
index = 0,
|
|
65
|
+
isChange = false,
|
|
66
|
+
network = networks.bitcoin,
|
|
67
|
+
) {
|
|
68
|
+
const node = bip32.fromBase58(xpub, network)
|
|
69
|
+
return node
|
|
70
|
+
.derive(isChange ? 1 : 0)
|
|
71
|
+
.derive(index)
|
|
72
|
+
.publicKey.toString("hex")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function getPubKeyFromPrivateKeyWIF(
|
|
76
|
+
privateKeyWIF: string,
|
|
77
|
+
network = bitcoin.networks.bitcoin,
|
|
78
|
+
) {
|
|
79
|
+
let key = ECPair.fromWIF(privateKeyWIF, network)
|
|
80
|
+
return key.publicKey
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getPubKeyFromPrivateKeyHex(
|
|
84
|
+
privateKeyHex: string,
|
|
85
|
+
network = bitcoin.networks.bitcoin,
|
|
86
|
+
) {
|
|
87
|
+
let key = ECPair.fromPrivateKey(Buffer.from(privateKeyHex, "hex"), { network })
|
|
88
|
+
return key.publicKey
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getPrivateKeyHexFromWIF(privateKeyWIF: string, network = bitcoin.networks.bitcoin) {
|
|
92
|
+
let key = ECPair.fromWIF(privateKeyWIF, network)
|
|
93
|
+
return key.privateKey!.toString("hex")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function parseRawTransaction(rawTransaction: string) {
|
|
97
|
+
const size = {
|
|
98
|
+
version: 4,
|
|
99
|
+
flag: 2,
|
|
100
|
+
tx: 32,
|
|
101
|
+
index: 4,
|
|
102
|
+
sequence: 4,
|
|
103
|
+
amount: 8,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let offset = 0
|
|
107
|
+
let version = rawTransaction.slice(offset, size.version * 2)
|
|
108
|
+
offset += size.version * 2
|
|
109
|
+
let flag = rawTransaction.slice(offset, offset + size.flag * 2)
|
|
110
|
+
offset = flag === "0001" ? offset + size.flag * 2 : offset // * 0x0001 is flag in segwit transactions
|
|
111
|
+
|
|
112
|
+
let inputsStartIndex = offset
|
|
113
|
+
|
|
114
|
+
let numberOfInputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
115
|
+
let noiSize = varUnit.encodingLength(numberOfInputs)
|
|
116
|
+
offset += noiSize * 2
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < numberOfInputs; i += 1) {
|
|
119
|
+
offset += size.tx * 2
|
|
120
|
+
offset += size.index * 2
|
|
121
|
+
let sigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
122
|
+
let sigLengthSize = varUnit.encodingLength(sigLength)
|
|
123
|
+
offset += sigLengthSize * 2
|
|
124
|
+
offset += sigLength * 2
|
|
125
|
+
offset += size.sequence * 2
|
|
126
|
+
}
|
|
127
|
+
let inputLastIndex = offset
|
|
128
|
+
let outputStartIndex = offset
|
|
129
|
+
|
|
130
|
+
let numberOfOutputs = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
131
|
+
let nooSize = varUnit.encodingLength(numberOfOutputs)
|
|
132
|
+
offset += nooSize * 2
|
|
133
|
+
|
|
134
|
+
for (let i = 0; i < numberOfOutputs; i += 1) {
|
|
135
|
+
offset += size.amount * 2
|
|
136
|
+
let unlockSigLength = varUnit.decode(Buffer.from(rawTransaction.slice(offset), "hex"))
|
|
137
|
+
let unlockSigLengthSize = varUnit.encodingLength(unlockSigLength)
|
|
138
|
+
offset += unlockSigLengthSize * 2
|
|
139
|
+
offset += unlockSigLength * 2
|
|
140
|
+
}
|
|
141
|
+
let outputLastIndex = offset
|
|
142
|
+
|
|
143
|
+
version = `0x${version}`
|
|
144
|
+
flag = `0x${flag}`
|
|
145
|
+
const vin = `0x${rawTransaction.slice(inputsStartIndex, inputLastIndex)}`
|
|
146
|
+
const vout = `0x${rawTransaction.slice(outputStartIndex, outputLastIndex)}`
|
|
147
|
+
let witness = `0x${rawTransaction.slice(outputLastIndex, rawTransaction.length - 8)}`
|
|
148
|
+
let locktime = `0x${rawTransaction.slice(rawTransaction.length - 8, rawTransaction.length)}`
|
|
149
|
+
return { version, flag, vin, vout, witness, locktime }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function calculateMerkleProof(
|
|
153
|
+
blockTransactions: string[],
|
|
154
|
+
txId: string,
|
|
155
|
+
blockMerkleRoot?: string,
|
|
156
|
+
) {
|
|
157
|
+
let transactionIndex = blockTransactions.findIndex((tx) => tx === txId)
|
|
158
|
+
if (transactionIndex < 0) throw new Error("txId is not in this tree")
|
|
159
|
+
let data = blockTransactions.map((a) => Buffer.from(a, "hex").reverse())
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
blockMerkleRoot &&
|
|
163
|
+
blockMerkleRoot !== fastRoot(data, bitcoin.crypto.hash256).toString("hex")
|
|
164
|
+
) {
|
|
165
|
+
throw new Error("calculated merkleRoot and block merkleRoot not matched")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let tree = merkle(data, bitcoin.crypto.hash256)
|
|
169
|
+
let proof = merkleProof(tree, data[transactionIndex])
|
|
170
|
+
|
|
171
|
+
let intermediateNodesArray = proof
|
|
172
|
+
.map((_id?: Buffer) => _id && _id.toString("hex"))
|
|
173
|
+
.filter((_id?: Buffer) => _id != null)
|
|
174
|
+
let intermediateNodes: string = intermediateNodesArray.reduce(
|
|
175
|
+
(a: string, value: string, index: number) =>
|
|
176
|
+
index !== transactionIndex % 2 && index < intermediateNodesArray.length - 1 ? a + value : a,
|
|
177
|
+
"0x",
|
|
178
|
+
)
|
|
179
|
+
return {
|
|
180
|
+
intermediateNodes,
|
|
181
|
+
transactionIndex,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function parseBlockHeader(headerHex: string) {
|
|
186
|
+
const size: {
|
|
187
|
+
[key: string]: number
|
|
188
|
+
} = {
|
|
189
|
+
version: 4,
|
|
190
|
+
previousBlockHash: 32,
|
|
191
|
+
merkleRoot: 32,
|
|
192
|
+
timestamp: 4,
|
|
193
|
+
difficulty: 4,
|
|
194
|
+
nonce: 4,
|
|
195
|
+
}
|
|
196
|
+
let offset = 0
|
|
197
|
+
let result: {
|
|
198
|
+
[key: string]: string
|
|
199
|
+
} = {}
|
|
200
|
+
for (let key in size) {
|
|
201
|
+
result[key] = headerHex.slice(offset, offset + size[key] * 2)
|
|
202
|
+
offset += size[key] * 2
|
|
203
|
+
}
|
|
204
|
+
return result as {
|
|
205
|
+
version: string
|
|
206
|
+
previousBlockHash: string
|
|
207
|
+
merkleRoot: string
|
|
208
|
+
timestamp: string
|
|
209
|
+
difficulty: string
|
|
210
|
+
nonce: string
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getAddressType(address: string, network = bitcoin.networks.bitcoin) {
|
|
215
|
+
if (address.startsWith(`${network.bech32}1p`)) {
|
|
216
|
+
// todo validate p2tr address
|
|
217
|
+
return "p2tr"
|
|
218
|
+
}
|
|
219
|
+
if (address.startsWith(network.bech32)) {
|
|
220
|
+
bitcoin.address.fromBech32(address)
|
|
221
|
+
return "p2wpkh"
|
|
222
|
+
}
|
|
223
|
+
let base58Data = bitcoin.address.fromBase58Check(address)
|
|
224
|
+
if (base58Data.version === Number(network.scriptHash)) {
|
|
225
|
+
return "p2sh"
|
|
226
|
+
}
|
|
227
|
+
if (base58Data.version === Number(network.pubKeyHash)) {
|
|
228
|
+
return "p2pkh"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw new Error("invalid address")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function createAddressObjectByAddress(address: string, network = bitcoin.networks.bitcoin) {
|
|
235
|
+
let addressType = getAddressType(address, network)
|
|
236
|
+
let addressObject
|
|
237
|
+
switch (addressType) {
|
|
238
|
+
case "p2pkh":
|
|
239
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
240
|
+
address,
|
|
241
|
+
network,
|
|
242
|
+
})
|
|
243
|
+
break
|
|
244
|
+
case "p2wpkh":
|
|
245
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
246
|
+
address,
|
|
247
|
+
network,
|
|
248
|
+
})
|
|
249
|
+
break
|
|
250
|
+
case "p2sh":
|
|
251
|
+
addressObject = bitcoin.payments.p2sh({
|
|
252
|
+
address,
|
|
253
|
+
network,
|
|
254
|
+
})
|
|
255
|
+
break
|
|
256
|
+
case "p2tr":
|
|
257
|
+
addressObject = bitcoin.payments.p2tr({
|
|
258
|
+
address,
|
|
259
|
+
network,
|
|
260
|
+
})
|
|
261
|
+
break
|
|
262
|
+
default:
|
|
263
|
+
throw new Error("address type is incorrect")
|
|
264
|
+
}
|
|
265
|
+
return { addressObject, addressType }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// used in block parser functions ( newt 2 functions )
|
|
269
|
+
export function convertBitcoinScriptToAddress(script: Buffer, network = bitcoin.networks.bitcoin) {
|
|
270
|
+
try {
|
|
271
|
+
return bitcoin.address.fromOutputScript(script, network)
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return undefined
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function parseRawBlock(
|
|
278
|
+
rawBlockHex: string,
|
|
279
|
+
blockNumber?: number,
|
|
280
|
+
network = bitcoin.networks.bitcoin,
|
|
281
|
+
) {
|
|
282
|
+
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
283
|
+
let blockHash = block.getHash().toString("hex")
|
|
284
|
+
let merkleRoot = block.merkleRoot!.toString("hex")
|
|
285
|
+
let prvBlockHash = block.prevHash!.toString("hex")
|
|
286
|
+
return {
|
|
287
|
+
blockNumber,
|
|
288
|
+
merkleRoot,
|
|
289
|
+
prvBlockHash,
|
|
290
|
+
transactions: block.transactions?.map((tx) => ({
|
|
291
|
+
txId: tx.getId(),
|
|
292
|
+
version: tx.version,
|
|
293
|
+
locktime: tx.locktime,
|
|
294
|
+
blockNumber,
|
|
295
|
+
blockHash,
|
|
296
|
+
vout: tx.outs.map((vo, index) => ({
|
|
297
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
298
|
+
script: vo.script.toString("hex"),
|
|
299
|
+
value: vo.value,
|
|
300
|
+
index,
|
|
301
|
+
})),
|
|
302
|
+
vin: tx.ins.map((vi) => ({
|
|
303
|
+
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
304
|
+
index: vi.index,
|
|
305
|
+
})),
|
|
306
|
+
})),
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function extractTransactionsAndBlockInfoFromRawBlock(
|
|
311
|
+
rawBlockHex: string,
|
|
312
|
+
blockNumber: number,
|
|
313
|
+
addresses: string[] = [],
|
|
314
|
+
inputTxIds: {
|
|
315
|
+
txId: string
|
|
316
|
+
index: number
|
|
317
|
+
address: string
|
|
318
|
+
script?: string
|
|
319
|
+
value?: number
|
|
320
|
+
}[] = [],
|
|
321
|
+
network = bitcoin.networks.bitcoin,
|
|
322
|
+
) {
|
|
323
|
+
let block = bitcoin.Block.fromBuffer(Buffer.from(rawBlockHex, "hex"))
|
|
324
|
+
let blockHash = block.getHash().reverse().toString("hex")
|
|
325
|
+
let merkleRoot = block.merkleRoot!.toString("hex")
|
|
326
|
+
let prvBlockHash = block.prevHash!.toString("hex")
|
|
327
|
+
|
|
328
|
+
let blockInfo = {
|
|
329
|
+
blockNumber,
|
|
330
|
+
blockHash,
|
|
331
|
+
merkleRoot,
|
|
332
|
+
prvBlockHash,
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let addressScript = addresses.map((address) =>
|
|
336
|
+
createAddressObjectByAddress(address, network).addressObject.output!.toString("hex"),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
let blockTxIds = block.transactions!.map((tx) => tx.getId())
|
|
340
|
+
|
|
341
|
+
let withdrawTxs: Transaction[] = []
|
|
342
|
+
let depositTxs: Transaction[] = []
|
|
343
|
+
block.transactions?.forEach((tx) => {
|
|
344
|
+
let txId = tx.getId()
|
|
345
|
+
|
|
346
|
+
let inputTxAddress: string | undefined
|
|
347
|
+
let isWithdraw = tx.ins.find((vi) => {
|
|
348
|
+
let viTxId = Buffer.from(vi.hash).reverse().toString("hex")
|
|
349
|
+
let viInput = inputTxIds.find((vin) => vin.txId === viTxId && vin.index === vi.index)
|
|
350
|
+
inputTxAddress = viInput?.address
|
|
351
|
+
return !!inputTxAddress
|
|
352
|
+
})
|
|
353
|
+
if (isWithdraw && inputTxAddress) {
|
|
354
|
+
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
355
|
+
|
|
356
|
+
withdrawTxs.push({
|
|
357
|
+
txId: tx.getId(),
|
|
358
|
+
version: tx.version,
|
|
359
|
+
locktime: tx.locktime,
|
|
360
|
+
blockNumber,
|
|
361
|
+
blockHash,
|
|
362
|
+
merkleProof: txMerkleProof,
|
|
363
|
+
vout: tx.outs.map((vo) => ({
|
|
364
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
365
|
+
script: vo.script.toString("hex"),
|
|
366
|
+
value: vo.value,
|
|
367
|
+
})),
|
|
368
|
+
vin: tx.ins.map((vi) => {
|
|
369
|
+
let viTxId = Buffer.from(vi.hash).reverse().toString("hex")
|
|
370
|
+
let viInput = inputTxIds.find((vin) => vin.txId === viTxId && vin.index === vi.index)
|
|
371
|
+
return {
|
|
372
|
+
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
373
|
+
index: vi.index,
|
|
374
|
+
address: viInput?.address,
|
|
375
|
+
script: viInput?.script,
|
|
376
|
+
value: viInput?.value,
|
|
377
|
+
}
|
|
378
|
+
}),
|
|
379
|
+
address: inputTxAddress,
|
|
380
|
+
addressScript: createAddressObjectByAddress(
|
|
381
|
+
inputTxAddress,
|
|
382
|
+
network,
|
|
383
|
+
).addressObject.output!.toString("hex"),
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
let addressIndex: number = -1
|
|
388
|
+
let isDeposit = tx.outs.find((blockTxVo) => {
|
|
389
|
+
let sIndex = addressScript.findIndex(
|
|
390
|
+
(addScript) => addScript === blockTxVo.script.toString("hex"),
|
|
391
|
+
)
|
|
392
|
+
if (sIndex >= 0) {
|
|
393
|
+
addressIndex = sIndex
|
|
394
|
+
return true
|
|
395
|
+
}
|
|
396
|
+
return false
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
if (isDeposit && addressIndex >= 0) {
|
|
400
|
+
let txMerkleProof = calculateMerkleProof(blockTxIds, txId, merkleRoot)
|
|
401
|
+
depositTxs.push({
|
|
402
|
+
txId: tx.getId(),
|
|
403
|
+
version: tx.version,
|
|
404
|
+
locktime: tx.locktime,
|
|
405
|
+
blockNumber,
|
|
406
|
+
blockHash,
|
|
407
|
+
merkleProof: txMerkleProof,
|
|
408
|
+
vout: tx.outs.map((vo) => ({
|
|
409
|
+
address: convertBitcoinScriptToAddress(vo.script, network),
|
|
410
|
+
script: vo.script.toString("hex"),
|
|
411
|
+
value: vo.value,
|
|
412
|
+
})),
|
|
413
|
+
vin: tx.ins.map((vi) => ({
|
|
414
|
+
txId: Buffer.from(vi.hash).reverse().toString("hex"),
|
|
415
|
+
index: vi.index,
|
|
416
|
+
})),
|
|
417
|
+
addressScript: addressScript[addressIndex],
|
|
418
|
+
address: addresses[addressIndex],
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
return {
|
|
423
|
+
blockInfo,
|
|
424
|
+
withdrawTxs,
|
|
425
|
+
depositTxs,
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function validateAddress(address: string, network = bitcoin.networks.bitcoin) {
|
|
430
|
+
try {
|
|
431
|
+
let isValid = false
|
|
432
|
+
let isAddressSegwit = address.startsWith(network.bech32)
|
|
433
|
+
if (isAddressSegwit) {
|
|
434
|
+
bitcoin.address.fromBech32(address)
|
|
435
|
+
isValid = true
|
|
436
|
+
} else {
|
|
437
|
+
let base58Data = bitcoin.address.fromBase58Check(address)
|
|
438
|
+
isValid =
|
|
439
|
+
base58Data.version === Number(network.scriptHash) ||
|
|
440
|
+
base58Data.version === Number(network.pubKeyHash)
|
|
441
|
+
}
|
|
442
|
+
return isValid
|
|
443
|
+
} catch (error) {
|
|
444
|
+
return false
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function toXOnlyPublicKey(pubKey: Buffer) {
|
|
449
|
+
return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export function createAddressObjectByScript(
|
|
453
|
+
{ addressType, script }: { addressType: string; script: Buffer },
|
|
454
|
+
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
455
|
+
) {
|
|
456
|
+
let addressObject
|
|
457
|
+
switch (addressType) {
|
|
458
|
+
case "p2pkh":
|
|
459
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
460
|
+
output: script,
|
|
461
|
+
network,
|
|
462
|
+
})
|
|
463
|
+
break
|
|
464
|
+
case "p2wpkh":
|
|
465
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
466
|
+
output: script,
|
|
467
|
+
network,
|
|
468
|
+
})
|
|
469
|
+
break
|
|
470
|
+
case "p2sh":
|
|
471
|
+
addressObject = bitcoin.payments.p2sh({
|
|
472
|
+
output: script,
|
|
473
|
+
network,
|
|
474
|
+
})
|
|
475
|
+
break
|
|
476
|
+
case "p2wsh":
|
|
477
|
+
addressObject = bitcoin.payments.p2wsh({
|
|
478
|
+
output: script,
|
|
479
|
+
network,
|
|
480
|
+
})
|
|
481
|
+
break
|
|
482
|
+
case "p2tr":
|
|
483
|
+
addressObject = bitcoin.payments.p2tr({
|
|
484
|
+
output: script,
|
|
485
|
+
network,
|
|
486
|
+
})
|
|
487
|
+
break
|
|
488
|
+
default:
|
|
489
|
+
throw new Error("address type is incorrect")
|
|
490
|
+
}
|
|
491
|
+
return addressObject
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export function createAddressObjectByHash(
|
|
495
|
+
{ addressType, hash }: { addressType: string; hash: Buffer },
|
|
496
|
+
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
497
|
+
) {
|
|
498
|
+
let addressObject
|
|
499
|
+
switch (addressType) {
|
|
500
|
+
case "p2pkh":
|
|
501
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
502
|
+
hash,
|
|
503
|
+
network,
|
|
504
|
+
})
|
|
505
|
+
break
|
|
506
|
+
case "p2wpkh":
|
|
507
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
508
|
+
hash,
|
|
509
|
+
network,
|
|
510
|
+
})
|
|
511
|
+
break
|
|
512
|
+
case "p2sh":
|
|
513
|
+
addressObject = bitcoin.payments.p2sh({
|
|
514
|
+
hash,
|
|
515
|
+
network,
|
|
516
|
+
})
|
|
517
|
+
break
|
|
518
|
+
case "p2wsh":
|
|
519
|
+
addressObject = bitcoin.payments.p2wsh({
|
|
520
|
+
hash,
|
|
521
|
+
network,
|
|
522
|
+
})
|
|
523
|
+
break
|
|
524
|
+
case "p2tr":
|
|
525
|
+
// here hash is the public key of tweaked private key (internal public key)
|
|
526
|
+
addressObject = bitcoin.payments.p2tr({
|
|
527
|
+
pubkey: hash,
|
|
528
|
+
network,
|
|
529
|
+
})
|
|
530
|
+
break
|
|
531
|
+
default:
|
|
532
|
+
throw new Error("address type is incorrect")
|
|
533
|
+
}
|
|
534
|
+
return addressObject
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export function createAddressObjectByPublicKey(
|
|
538
|
+
{ addressType, publicKey }: { addressType: string; publicKey: Buffer },
|
|
539
|
+
network: bitcoin.Network = bitcoin.networks.bitcoin,
|
|
540
|
+
): bitcoin.payments.Payment {
|
|
541
|
+
let addressObject
|
|
542
|
+
switch (addressType) {
|
|
543
|
+
case "p2pkh":
|
|
544
|
+
addressObject = bitcoin.payments.p2pkh({
|
|
545
|
+
pubkey: publicKey,
|
|
546
|
+
network,
|
|
547
|
+
})
|
|
548
|
+
break
|
|
549
|
+
case "p2wpkh":
|
|
550
|
+
addressObject = bitcoin.payments.p2wpkh({
|
|
551
|
+
pubkey: publicKey,
|
|
552
|
+
network,
|
|
553
|
+
})
|
|
554
|
+
break
|
|
555
|
+
case "p2sh-p2wpkh":
|
|
556
|
+
addressObject = bitcoin.payments.p2sh({
|
|
557
|
+
redeem: bitcoin.payments.p2wpkh({
|
|
558
|
+
pubkey: publicKey,
|
|
559
|
+
network,
|
|
560
|
+
}),
|
|
561
|
+
})
|
|
562
|
+
break
|
|
563
|
+
case "p2tr":
|
|
564
|
+
// this public key is public key of main private key
|
|
565
|
+
addressObject = bitcoin.payments.p2tr({
|
|
566
|
+
internalPubkey: toXOnlyPublicKey(publicKey),
|
|
567
|
+
network,
|
|
568
|
+
})
|
|
569
|
+
break
|
|
570
|
+
default:
|
|
571
|
+
throw new Error("address type is incorrect")
|
|
572
|
+
}
|
|
573
|
+
return addressObject
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export function publicKeyConvertor(publicKeyHex: string, compressed = true) {
|
|
577
|
+
let pubkey = ECPair.fromPublicKey(Buffer.from(publicKeyHex, "hex"), {
|
|
578
|
+
compressed,
|
|
579
|
+
})
|
|
580
|
+
return pubkey.publicKey.toString("hex")
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export const addressTypeHelper: {
|
|
584
|
+
addressTypesNumber: {
|
|
585
|
+
[key: string]: number
|
|
586
|
+
}
|
|
587
|
+
addressTypes: string[]
|
|
588
|
+
} = {
|
|
589
|
+
addressTypesNumber: { p2pk: 0, p2pkh: 1, p2sh: 2, p2wpkh: 3, p2wsh: 4, p2tr: 5 },
|
|
590
|
+
addressTypes: ["p2pk", "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"],
|
|
591
|
+
}
|