@teleportdao/bitcoin 1.0.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.
Files changed (72) hide show
  1. package/dist/bitcoin-base.d.ts +56 -0
  2. package/dist/bitcoin-base.d.ts.map +1 -0
  3. package/dist/bitcoin-base.js +138 -0
  4. package/dist/bitcoin-base.js.map +1 -0
  5. package/dist/bitcoin-interface-utils.d.ts +18 -0
  6. package/dist/bitcoin-interface-utils.d.ts.map +1 -0
  7. package/dist/bitcoin-interface-utils.js +31 -0
  8. package/dist/bitcoin-interface-utils.js.map +1 -0
  9. package/dist/bitcoin-interface.d.ts +154 -0
  10. package/dist/bitcoin-interface.d.ts.map +1 -0
  11. package/dist/bitcoin-interface.js +248 -0
  12. package/dist/bitcoin-interface.js.map +1 -0
  13. package/dist/bitcoin-utils.d.ts +70 -0
  14. package/dist/bitcoin-utils.d.ts.map +1 -0
  15. package/dist/bitcoin-utils.js +388 -0
  16. package/dist/bitcoin-utils.js.map +1 -0
  17. package/dist/helper/burn-request-helper.d.ts +7 -0
  18. package/dist/helper/burn-request-helper.d.ts.map +1 -0
  19. package/dist/helper/burn-request-helper.js +26 -0
  20. package/dist/helper/burn-request-helper.js.map +1 -0
  21. package/dist/helper/teleport-request-helper.d.ts +45 -0
  22. package/dist/helper/teleport-request-helper.d.ts.map +1 -0
  23. package/dist/helper/teleport-request-helper.js +141 -0
  24. package/dist/helper/teleport-request-helper.js.map +1 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +15 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/sign/sign-transaction.d.ts +8 -0
  30. package/dist/sign/sign-transaction.d.ts.map +1 -0
  31. package/dist/sign/sign-transaction.js +41 -0
  32. package/dist/sign/sign-transaction.js.map +1 -0
  33. package/dist/teleport-dao-payments.d.ts +92 -0
  34. package/dist/teleport-dao-payments.d.ts.map +1 -0
  35. package/dist/teleport-dao-payments.js +203 -0
  36. package/dist/teleport-dao-payments.js.map +1 -0
  37. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +12 -0
  38. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -0
  39. package/dist/transaction-builder/bitcoin-transaction-builder.js +50 -0
  40. package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -0
  41. package/dist/transaction-builder/transaction-builder-common.d.ts +80 -0
  42. package/dist/transaction-builder/transaction-builder-common.d.ts.map +1 -0
  43. package/dist/transaction-builder/transaction-builder-common.js +170 -0
  44. package/dist/transaction-builder/transaction-builder-common.js.map +1 -0
  45. package/dist/transaction-builder/transaction-builder.d.ts +19 -0
  46. package/dist/transaction-builder/transaction-builder.d.ts.map +1 -0
  47. package/dist/transaction-builder/transaction-builder.js +130 -0
  48. package/dist/transaction-builder/transaction-builder.js.map +1 -0
  49. package/dist/utils/networks.d.ts +36 -0
  50. package/dist/utils/networks.d.ts.map +1 -0
  51. package/dist/utils/networks.js +30 -0
  52. package/dist/utils/networks.js.map +1 -0
  53. package/dist/utils/tools.d.ts +13 -0
  54. package/dist/utils/tools.d.ts.map +1 -0
  55. package/dist/utils/tools.js +65 -0
  56. package/dist/utils/tools.js.map +1 -0
  57. package/package.json +34 -0
  58. package/src/bitcoin-base.js +174 -0
  59. package/src/bitcoin-interface-utils.js +42 -0
  60. package/src/bitcoin-interface.js +267 -0
  61. package/src/bitcoin-utils.js +443 -0
  62. package/src/helper/burn-request-helper.js +27 -0
  63. package/src/helper/teleport-request-helper.js +162 -0
  64. package/src/index.js +15 -0
  65. package/src/sign/sign-transaction.js +36 -0
  66. package/src/teleport-dao-payments.js +276 -0
  67. package/src/transaction-builder/bitcoin-transaction-builder.js +37 -0
  68. package/src/transaction-builder/transaction-builder-common.js +228 -0
  69. package/src/transaction-builder/transaction-builder.js +135 -0
  70. package/src/utils/networks.js +31 -0
  71. package/src/utils/tools.js +72 -0
  72. package/tsconfig.json +9 -0
@@ -0,0 +1,174 @@
1
+ const bip39 = require("bip39")
2
+ const bip32 = require("bip32")
3
+ const hdWalletNetworksPath = require("@teleportdao/configs").hdWalletPath
4
+
5
+ const networks = require("./utils/networks")
6
+
7
+ const TransactionBuilder = require("./transaction-builder/bitcoin-transaction-builder")
8
+ const BitcoinSign = require("./sign/sign-transaction")
9
+
10
+ class BitcoinBase {
11
+ constructor(
12
+ networkName,
13
+ connectionInfo = {
14
+ api: {
15
+ enabled: true,
16
+ provider: "BlockStream",
17
+ },
18
+ },
19
+ ) {
20
+ this.network = networks[networkName]
21
+ this.hdWalletPath = hdWalletNetworksPath[networkName.replace("_testnet", "")]
22
+
23
+ this.transactionBuilder = new TransactionBuilder(connectionInfo, networkName, this.network)
24
+ this.btcInterface = this.transactionBuilder.btcInterface
25
+
26
+ this.signer = new BitcoinSign(this.network)
27
+
28
+ this.currentAccount = null
29
+ this.currentAccountType = null
30
+
31
+ this.privateKey = null
32
+ this.publicKey = null
33
+ // todo multisig
34
+ this.publicKeys = []
35
+ }
36
+
37
+ setMultiSigAccount(accountType = "p2sh") {
38
+ /* eslint-disable no-unreachable */
39
+ // todo : not completed
40
+ switch (accountType) {
41
+ // case 'p2sh':
42
+ // this.currentAccount = ''
43
+ // break
44
+ // case 'p2wsh':
45
+ // this.currentAccount = ''
46
+ // break
47
+ // case 'p2sh-p2wsh':
48
+ // this.currentAccount = ''
49
+ // break
50
+ default:
51
+ throw new Error("accountType is incorrect")
52
+ }
53
+ this.currentAccountType = accountType
54
+ }
55
+
56
+ setAccountPrivateKey(privateKeyHex) {
57
+ this.privateKey = Buffer.from(privateKeyHex, "hex")
58
+ }
59
+
60
+ setAccountPublicKey(publicKeyHex) {
61
+ this.publicKey = Buffer.from(publicKeyHex, "hex")
62
+ }
63
+
64
+ setAccountPrivateKeyByMnemonic({
65
+ mnemonic,
66
+ mnemonicPassword = "",
67
+ index = 0,
68
+ walletNumber = 0,
69
+ addressType = "p2sh-p2wpkh",
70
+ }) {
71
+ if (!bip39.validateMnemonic(mnemonic)) throw new Error("invalid mnemonic")
72
+ const seed = bip39.mnemonicToSeedSync(mnemonic, mnemonicPassword)
73
+ const node = bip32.fromSeed(seed)
74
+
75
+ if (!this.hdWalletPath[addressType]) throw new Error("incorrect path or addressType")
76
+
77
+ const path = `${this.hdWalletPath[addressType]}/${walletNumber}`
78
+ const account = node.derivePath(path)
79
+ const userKeyPair = account.derive(index)
80
+ this.setAccountPrivateKey(userKeyPair.privateKey.toString("hex"))
81
+ this.setAccountPublicKey(userKeyPair.publicKey.toString("hex"))
82
+ }
83
+
84
+ setAccount(accountType = "p2pkh") {
85
+ let addressObj = this.transactionBuilder.createAddressObject({
86
+ addressType: accountType,
87
+ publicKey: this.publicKey,
88
+ })
89
+ this.currentAccount = addressObj.address
90
+ this.currentAccountType = accountType
91
+ this.addressObj = addressObj
92
+ return addressObj.address
93
+ }
94
+
95
+ /**
96
+ *
97
+ * @param {Object} signerInfo
98
+ * @param {String} signerInfo.address
99
+ * @param {String} signerInfo.addressType
100
+ * @param {String?} signerInfo.publicKey
101
+ * *@param {Array?} signerInfo.publicKeys // todo not used yet- used in multi sig
102
+ * @param {String?} signerInfo.derivationPath
103
+ * @param {String?} signerInfo.privateKeyId
104
+ * @returns
105
+ */
106
+ async getExtendedUtxo({ address, addressType, publicKey, derivationPath }) {
107
+ return this.transactionBuilder.getExtendedUtxo({
108
+ address,
109
+ addressType,
110
+ publicKey,
111
+ derivationPath,
112
+ })
113
+ }
114
+
115
+ static checkBalanceIsSufficient({
116
+ targets,
117
+ extendedUtxo,
118
+ changeAddress,
119
+ feeRate,
120
+ fullAmount = false,
121
+ }) {
122
+ try {
123
+ TransactionBuilder.helperHandleInputsAndOutputs({
124
+ targets,
125
+ extendedUtxo,
126
+ changeAddress,
127
+ feeRate,
128
+ fullAmount,
129
+ })
130
+ return true
131
+ } catch (err) {
132
+ return false
133
+ }
134
+ }
135
+
136
+ async send({ receiverAddress, amount, fullAmount = false, speed = "normal" }) {
137
+ let extendedUtxo = await this.getExtendedUtxo({
138
+ address: this.currentAccount,
139
+ addressType: this.currentAccountType,
140
+ publicKey: this.publicKey.toString("hex"),
141
+ })
142
+ let feeRate = await this.transactionBuilder._getFeeRate(speed)
143
+ let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
144
+ extendedUtxo,
145
+ targets: [
146
+ {
147
+ address: receiverAddress,
148
+ value: amount,
149
+ },
150
+ ],
151
+ changeAddress: this.currentAccount,
152
+ feeRate,
153
+ fullAmount,
154
+ })
155
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
156
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
157
+ let txId = await this.transactionBuilder.sendTx(signedTx)
158
+ return txId
159
+ }
160
+
161
+ async sendSignedPsbt(signedPsbt) {
162
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
163
+ let txId = await this.transactionBuilder.sendTx(signedTx)
164
+ return txId
165
+ }
166
+
167
+ async sendMultiSignedPsbt(signedPsbts = []) {
168
+ let signedTx = this.signer.finalizePsbts(signedPsbts)
169
+ let txId = await this.transactionBuilder.sendTx(signedTx)
170
+ return txId
171
+ }
172
+ }
173
+
174
+ module.exports = BitcoinBase
@@ -0,0 +1,42 @@
1
+ const networks = require("./utils/networks")
2
+ const {
3
+ createAddressObjectByHash,
4
+ createAddressObjectByAddress,
5
+ createAddressObjectByPublicKey,
6
+ } = require("./bitcoin-utils")
7
+
8
+ class BitcoinInterfaceUtils {
9
+ constructor(networkName) {
10
+ this.testnet = networkName.includes("_testnet")
11
+ this.network = networks[networkName]
12
+ }
13
+
14
+ convertHashToAddress(hashHex, addressType) {
15
+ let addressObj = createAddressObjectByHash(
16
+ { addressType, hash: Buffer.from(hashHex, "hex") },
17
+ this.network,
18
+ )
19
+ return addressObj.address
20
+ }
21
+
22
+ convertAddressToScript(address) {
23
+ let { addressObject, addressType } = createAddressObjectByAddress(address, this.network)
24
+ return {
25
+ script: addressObject.output,
26
+ hash: addressObject.hash,
27
+ addressType,
28
+ }
29
+ }
30
+
31
+ convertAddressToObject(address) {
32
+ let addObj = createAddressObjectByAddress(address, this.network)
33
+ return addObj
34
+ }
35
+
36
+ createAddressObjectByPublicKey(address, addressType) {
37
+ let addObj = createAddressObjectByPublicKey({ address, addressType }, this.network)
38
+ return addObj
39
+ }
40
+ }
41
+
42
+ module.exports = BitcoinInterfaceUtils
@@ -0,0 +1,267 @@
1
+ const { getRpcProvider, getApiProvider } = require("@teleportdao/providers").bitcoin
2
+ const { runWithRetries, sleep } = require("./utils/tools")
3
+ const {
4
+ parseRawTransaction,
5
+ calculateMerkleProof,
6
+ parseBlockHeader,
7
+ extractTransactionsAndBlockInfoFromRawBlock,
8
+ } = require("./bitcoin-utils")
9
+ const { checkAndParseProtocolRequest } = require("./helper/teleport-request-helper")
10
+ const { getBurnTransactionInfo } = require("./helper/burn-request-helper")
11
+ const BitcoinInterfaceUtils = require("./bitcoin-interface-utils")
12
+
13
+ class BitcoinInterface extends BitcoinInterfaceUtils {
14
+ constructor(connectionInfo, networkName, config = { minTeleporterFeeAmount: 0 }) {
15
+ super(networkName)
16
+ if (connectionInfo.rpc?.enabled) {
17
+ this.rpcProvider = getRpcProvider(connectionInfo.rpc)
18
+ } else if (connectionInfo.api.provider !== "BlockStream") {
19
+ throw new Error("if rpc is disabled, we just support BlockStream as api provider")
20
+ }
21
+
22
+ if (connectionInfo.api.enabled) {
23
+ this.apiProviderName = connectionInfo.api.provider
24
+ this.apiProvider = getApiProvider(connectionInfo.api, networkName)
25
+ }
26
+
27
+ this.minTeleporterFeeAmount = config.minTeleporterFeeAmount
28
+ this.provider = this.rpcProvider || this.apiProvider
29
+ }
30
+
31
+ // rpc + api
32
+
33
+ // general
34
+ async getLatestBlockNumber() {
35
+ let latestHeight = await this.provider.getLatestBlockNumber()
36
+ return latestHeight
37
+ }
38
+
39
+ async getBlockHash(blockNumber) {
40
+ let headerHash = await runWithRetries(() => this.provider.getBlockHash(blockNumber))
41
+ return headerHash
42
+ }
43
+
44
+ async getBlockHeaderHex(blockNumber) {
45
+ let headerHex = await runWithRetries(() => this.provider.getBlockHeaderHex(blockNumber))
46
+ return headerHex
47
+ }
48
+
49
+ async getTransaction(txId) {
50
+ return this.provider.getTransaction(txId)
51
+ }
52
+
53
+ // speed : low normal fast
54
+ async getFeeRate(speed = "normal") {
55
+ if (!(speed === "normal" || speed === "slow" || speed === "fast")) {
56
+ throw new Error("incorrect speed")
57
+ }
58
+ let fee = await this.provider.getFeeRate(speed)
59
+ return fee
60
+ }
61
+
62
+ // ----------- specific
63
+
64
+ // relayer
65
+ async getHexBlockHeaders(startBlockNumber, endBlockNumber) {
66
+ const blockHeaders = []
67
+ let difficulty = null
68
+ let hexBlockHeaders = ""
69
+
70
+ let fromBlockNumber = startBlockNumber
71
+ for (let blockNumber = startBlockNumber; blockNumber <= endBlockNumber; blockNumber += 1) {
72
+ let blockHeader = await this.getBlockHeaderHex(blockNumber)
73
+ console.log("block", blockNumber)
74
+ let parsedBlockHeader = parseBlockHeader(blockHeader)
75
+ if (difficulty && parsedBlockHeader.difficulty !== difficulty) {
76
+ blockHeaders.push({
77
+ hexBlockHeaders,
78
+ fromBlockNumber,
79
+ toBlockNumber: blockNumber - 1,
80
+ difficulty,
81
+ })
82
+ hexBlockHeaders = blockHeader
83
+ fromBlockNumber = blockNumber
84
+ } else {
85
+ hexBlockHeaders += blockHeader
86
+ }
87
+ difficulty = parsedBlockHeader.difficulty
88
+ }
89
+ if (hexBlockHeaders) {
90
+ blockHeaders.push({
91
+ hexBlockHeaders,
92
+ fromBlockNumber,
93
+ toBlockNumber: endBlockNumber,
94
+ difficulty,
95
+ })
96
+ }
97
+
98
+ return blockHeaders
99
+ }
100
+
101
+ async getRequestProof(transaction) {
102
+ let transactionHex =
103
+ transaction.hex || (await this.provider.getRawTransaction(transaction.txId))
104
+
105
+ let txInfo
106
+ if (!(transaction.blockHash && transaction.blockNumber)) {
107
+ txInfo = await this.provider.getTransaction(transaction.txId)
108
+ }
109
+ let blockHash = transaction.blockHash || txInfo.blockHash
110
+ let blockNumber = transaction.blockNumber || txInfo.blockNumber
111
+ let parsedTx = parseRawTransaction(transactionHex)
112
+ let merkleProof =
113
+ transaction.merkleProof || (await this.getMerkleProof(transaction.txId, blockHash))
114
+
115
+ return {
116
+ parsedTx,
117
+ merkleProof,
118
+ blockNumber,
119
+ blockHash,
120
+ }
121
+ }
122
+
123
+ async getMerkleProof(txId, blockHash) {
124
+ let txIds = await this.provider.getBlockTransactionIds(blockHash)
125
+ // let a = await this.provider.getMerkleProof(txId)
126
+ let proof = calculateMerkleProof(txIds, txId)
127
+ // console.log(a.intermediateNodes === proof.intermediateNodes)
128
+ return proof
129
+ }
130
+
131
+ // ------------------ utxo providers --------------------------------
132
+
133
+ // teleporter + locker
134
+ async getAddressesUtxo(addresses) {
135
+ if (!this.apiProvider) {
136
+ throw new Error("this function need an api provider")
137
+ }
138
+ const allPromises = []
139
+ for (let address of addresses) {
140
+ let promise = await this.apiProvider.getUtxos(address)
141
+ allPromises.push(promise)
142
+ }
143
+ let result = await Promise.all(allPromises)
144
+ return result.flat(1)
145
+ }
146
+
147
+ async getBalance(address) {
148
+ if (!this.apiProvider) {
149
+ throw new Error("this function need an api provider")
150
+ }
151
+ let utxos = (await this.apiProvider.getUtxos(address)) ?? []
152
+ return utxos.reduce((a, tx) => a + Number(tx.value), 0)
153
+ }
154
+
155
+ // ------------------ utxo provider + rpc or blockstream----------------
156
+ // teleporter
157
+ async getBlockTransactions(addresses, blockNumber) {
158
+ let utxos = await this.getAddressesUtxo(addresses)
159
+ let rawBlockHex = await this.rpcProvider.getBlockByBlockNumber(blockNumber, 0)
160
+ let { withdrawTxs, depositTxs } = extractTransactionsAndBlockInfoFromRawBlock(
161
+ rawBlockHex,
162
+ blockNumber,
163
+ addresses,
164
+ utxos,
165
+ this.network,
166
+ )
167
+ // todo : check
168
+ return depositTxs.concat(withdrawTxs)
169
+ }
170
+
171
+ // teleporter
172
+ async getMultipleBlocksTransactions(addresses, startBlockNumber, endBlockNumber) {
173
+ if (endBlockNumber - startBlockNumber > 20) {
174
+ throw new Error("cant get more than 20 block per function call")
175
+ }
176
+ let blockTxs = []
177
+ for (let blockNumber = +startBlockNumber + 1; blockNumber <= endBlockNumber; blockNumber += 1) {
178
+ console.log("get block transactions. blockNumber: ", blockNumber)
179
+ const response = this.getBlockTransactions(addresses, blockNumber)
180
+ blockTxs.push(response)
181
+ await sleep(200)
182
+ }
183
+ blockTxs = await Promise.all(blockTxs)
184
+ return blockTxs.flat(1)
185
+ }
186
+
187
+ // teleporter
188
+ async getTransactionHistory(addresses, startBlockNumber, endBlockNumber) {
189
+ if (this.rpcProvider) {
190
+ let endBlock = endBlockNumber || (await this.getLatestBlockNumber())
191
+ let startBlock = Math.max(+startBlockNumber, +endBlock - 20)
192
+ return this.getMultipleBlocksTransactions(addresses, startBlock, endBlock)
193
+ }
194
+ if (this.apiProviderName !== "BlockStream") {
195
+ throw new Error("just support BlockStream as api provider for this function")
196
+ }
197
+ let txs = await this.apiProvider.getTransactionHistoryForMultipleAddresses(
198
+ addresses,
199
+ startBlockNumber,
200
+ )
201
+ return txs.flat(1)
202
+ }
203
+
204
+ // ------------------just blockstream----------------------
205
+ async getMempoolTransactionHistory(addresses) {
206
+ if (this.apiProviderName !== "BlockStream") {
207
+ throw new Error("teleporter just support BlockStream as api provider")
208
+ }
209
+ let txs = await this.apiProvider.getMempoolTransactionHistoryForMultipleAddresses(addresses)
210
+ return txs.flat(1)
211
+ }
212
+
213
+ //
214
+
215
+ async getTeleporterRequests(addresses, startblockNumber, endBlockNumber, mempool = false) {
216
+ // transaction in StartBlock is not returned --> (startblockNumber,endBlockNumber]
217
+ let transactions = mempool
218
+ ? await this.getMempoolTransactionHistory(addresses)
219
+ : await this.getTransactionHistory(addresses, startblockNumber, endBlockNumber)
220
+
221
+ let requests = []
222
+ let invalidRequests = []
223
+ for (let transaction of transactions) {
224
+ console.log(`received tx to check for teleport: ${transaction.txId}`)
225
+
226
+ let address = transaction.address
227
+ let request = checkAndParseProtocolRequest(transaction.vout, address, {
228
+ minTeleporterFeeAmount: this.minTeleporterFeeAmount,
229
+ })
230
+ let lockerLockingScript =
231
+ transaction.addressScript || this.convertAddressToScript(address).script.toString("hex")
232
+ if (request.status) {
233
+ requests.push({ transaction, request, lockerAddress: address, lockerLockingScript })
234
+ } else if (request.code !== "NO_OP_RETURN") {
235
+ invalidRequests.push({ transaction, request, lockerAddress: address, lockerLockingScript })
236
+ }
237
+ }
238
+ return { requests, invalidRequests }
239
+ }
240
+
241
+ async getLockersBurnTransactions(addresses, startBlockNumber, endBlockNumber, mempool = false) {
242
+ let transactions = mempool
243
+ ? await this.getMempoolTransactionHistory(addresses)
244
+ : await this.getTransactionHistory(addresses, startBlockNumber, endBlockNumber)
245
+
246
+ let validTxs = []
247
+ for (let transaction of transactions) {
248
+ let address = transaction.address
249
+ console.log(`received tx to check burn: ${transaction.txId}`)
250
+ // check if its a transaction to spend btc
251
+ let burnInfo = getBurnTransactionInfo(address, transaction.vin, transaction.vout)
252
+ if (burnInfo) {
253
+ validTxs.push({
254
+ transaction,
255
+ burnInfo,
256
+ lockerAddress: address,
257
+ lockerLockingScript:
258
+ transaction.addressScript ||
259
+ this.convertAddressToScript(address).script.toString("hex"),
260
+ })
261
+ }
262
+ }
263
+ return validTxs
264
+ }
265
+ }
266
+
267
+ module.exports = BitcoinInterface