edge-currency-plugins 3.16.0 → 3.17.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 (26) hide show
  1. package/android/src/main/assets/edge-currency-plugins/edge-currency-plugins.js +51 -49
  2. package/lib/common/plugin/types.js +11 -0
  3. package/lib/common/utxobased/engine/ServerStates.js +17 -1
  4. package/lib/common/utxobased/engine/UtxoEngineProcessor.js +53 -18
  5. package/lib/common/utxobased/info/all.js +6 -0
  6. package/lib/common/utxobased/info/bitcoin.js +1 -1
  7. package/lib/common/utxobased/info/ecashbitcoin.js +90 -0
  8. package/lib/common/utxobased/info/ecashbitcoinbetanet.js +91 -0
  9. package/lib/common/utxobased/keymanager/keymanager.js +6 -1
  10. package/lib/common/utxobased/network/Blockbook.js +3 -1
  11. package/lib/common/utxobased/network/BlockbookElectrum.js +243 -77
  12. package/lib/common/utxobased/network/Socket.js +26 -20
  13. package/lib/common/utxobased/network/electrumApi.js +49 -1
  14. package/lib/src/common/plugin/types.d.ts +10 -0
  15. package/lib/src/common/utxobased/engine/ServerStates.d.ts +1 -0
  16. package/lib/src/common/utxobased/engine/UtxoEngineProcessor.d.ts +2 -0
  17. package/lib/src/common/utxobased/info/all.d.ts +2 -0
  18. package/lib/src/common/utxobased/info/ecashbitcoin.d.ts +3 -0
  19. package/lib/src/common/utxobased/info/ecashbitcoinbetanet.d.ts +3 -0
  20. package/lib/src/common/utxobased/network/Blockbook.d.ts +3 -1
  21. package/lib/src/common/utxobased/network/BlockbookElectrum.d.ts +2 -0
  22. package/lib/src/common/utxobased/network/electrumApi.d.ts +21 -0
  23. package/lib/test/common/utxobased/engine/UtxoEngineProcessor.spec.d.ts +1 -0
  24. package/lib/test/common/utxobased/keymanager/coins/ecashbitcointransactiontest.spec.d.ts +1 -0
  25. package/lib/test/common/utxobased/network/BlockbookElectrum.spec.d.ts +1 -0
  26. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _altcoinjs = require('altcoin-js');
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _altcoinjs = require('altcoin-js');
2
+
2
3
 
3
4
 
4
5
 
@@ -22,6 +23,17 @@ var _Blockbook = require('./Blockbook');
22
23
 
23
24
 
24
25
 
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
25
37
  var _electrumApi = require('./electrumApi');
26
38
 
27
39
 
@@ -49,8 +61,9 @@ var _electrumApi = require('./electrumApi');
49
61
 
50
62
 
51
63
 
52
- const asResponseMessage = (raw) => {
53
- const message = _cleaners.asJSON.call(void 0,
64
+
65
+ const asElectrumMessage = _cleaners.asJSON.call(void 0,
66
+ _cleaners.asEither.call(void 0,
54
67
  _cleaners.asObject.call(void 0, {
55
68
  id: _cleaners.asString,
56
69
  result: _cleaners.asUnknown,
@@ -60,8 +73,30 @@ const asResponseMessage = (raw) => {
60
73
  _cleaners.asObject.call(void 0, { connected: _cleaners.asString })
61
74
  )
62
75
  )
76
+ }),
77
+ // A subscription's notifications carry its method in place of an id:
78
+ _cleaners.asObject.call(void 0, {
79
+ method: _cleaners.asString,
80
+ params: _cleaners.asArray.call(void 0, _cleaners.asUnknown)
63
81
  })
64
- )(raw)
82
+ )
83
+ )
84
+
85
+ const { method: headersSubscribeMethod } = _electrumApi.headersSubscribeMessage.call(void 0, )
86
+
87
+ const asResponseMessage = (raw) => {
88
+ const message = asElectrumMessage(raw)
89
+
90
+ // The socket files each subscription under its method name:
91
+ if ('method' in message) {
92
+ return { id: message.method, data: message.params[0] }
93
+ }
94
+
95
+ // Electrum answers a subscription with the current header, not the
96
+ // acknowledgement the socket waits for. fetchInfo reports that height:
97
+ if (message.id === headersSubscribeMethod && message.error == null) {
98
+ return { id: message.id, data: { subscribed: true } }
99
+ }
65
100
 
66
101
  return {
67
102
  id: message.id,
@@ -70,11 +105,20 @@ const asResponseMessage = (raw) => {
70
105
  }
71
106
  }
72
107
 
73
- const asResponse = (raw) => {
108
+ const makeAsResponse = (
109
+ isWatchingBlocks
110
+ ) => (raw) => {
74
111
  const payloadString = _cleaners.asString.call(void 0, raw).trimEnd()
75
112
  const messageStrings = payloadString.split('\n')
76
113
  const messages = messageStrings.map(asResponseMessage)
77
- return messages
114
+
115
+ // fetchInfo's blockchain.headers.subscribe subscribes the connection to new
116
+ // blocks, so the server can announce one before watchBlocks files the
117
+ // subscription. The socket drops connections over messages it cannot route,
118
+ // so announcements until then are skipped. watchBlocks reports each block
119
+ // after it subscribes:
120
+ if (isWatchingBlocks()) return messages
121
+ return messages.filter(message => message.id !== headersSubscribeMethod)
78
122
  }
79
123
 
80
124
  function makeBlockbookElectrum(
@@ -98,14 +142,149 @@ const asResponse = (raw) => {
98
142
  address,
99
143
  coin: pluginInfo.coinInfo.name
100
144
  })
101
- const scriptHashBuffer = _altcoinjs.crypto.sha256(Buffer.from(scriptPubkey, 'hex'))
102
- const scriptHash = scriptHashBuffer.reverse().toString('hex')
103
- return scriptHash
145
+ return toElectrumScriptHash(Buffer.from(scriptPubkey, 'hex'))
104
146
  }
105
147
 
148
+ // Makes the Electrum requests for a Blockbook task, and returns the
149
+ // Blockbook response built from their answers:
150
+ function* translateTask(
151
+ task
152
+ ) {
153
+ // Translate getAccountUtxo to blockchain.scripthash.listunspent:
154
+ if (task.method === 'getAccountUtxo') {
155
+ const params = task.params
156
+ const address =
157
+ asAddress != null ? asAddress(params.descriptor) : params.descriptor
158
+ const scriptHash = addressToScriptHash(address)
159
+
160
+ // Safe because yielding WsTask<ListUnspentResponse> should get
161
+ // ListUnspentResponse next.
162
+ const electrumUtxos = (yield {
163
+ ..._electrumApi.listUnspentMessage.call(void 0, scriptHash)
164
+ })
165
+
166
+ const blockbookUtxos = electrumUtxos.map(
167
+ utxo => ({
168
+ txid: utxo.tx_hash,
169
+ vout: utxo.tx_pos,
170
+ value: utxo.value.toString(),
171
+ height: utxo.height
172
+ })
173
+ )
174
+ return blockbookUtxos
175
+ }
176
+
177
+ // Get Address Transactions:
178
+ if (task.method === 'getAccountInfo') {
179
+ const params = task.params
180
+ const address =
181
+ asAddress != null ? asAddress(params.descriptor) : params.descriptor
182
+ const scriptHash = addressToScriptHash(address)
183
+
184
+ // Safe because yielding WsTask<AddressTransactionsResponse> should get
185
+ // AddressTransactionsResponse next.
186
+ const electrumUtxos = (yield {
187
+ ..._electrumApi.addressTransactionsMessage.call(void 0, scriptHash)
188
+ })
189
+
190
+ const blockbookUtxos = {
191
+ address, // unused
192
+ balance: '0', // unused
193
+ totalReceived: '0', // unused
194
+ totalSent: '0', // unused
195
+ txs: electrumUtxos.length,
196
+ unconfirmedBalance: '0', // unused
197
+ unconfirmedTxs: electrumUtxos.reduce(
198
+ // Electrum puts mempool transactions at height 0 or -1:
199
+ (sum, tx) => sum + (tx.height <= 0 ? 1 : 0),
200
+ 0
201
+ ),
202
+ txids: [], // unused
203
+ transactions: [], // TODO: this requires an extra query per txid
204
+ page: 0, // unused
205
+ totalPages: 1,
206
+ itemsOnPage: 0 // unused
207
+ }
208
+ return blockbookUtxos
209
+ }
210
+
211
+ // Translate getTransaction to blockchain.transaction.get:
212
+ if (task.method === 'getTransaction') {
213
+ const params = task.params
214
+ return yield* transactionTask(params.txid)
215
+ }
216
+
217
+ throw new Error(`${task.method} not supported for Electrum connections`)
218
+ }
219
+
220
+ function* transactionTask(
221
+ txid
222
+ ) {
223
+ // Safe because yielding WsTask<RawTransactionResponse> should get
224
+ // RawTransactionResponse next.
225
+ const hex = (yield {
226
+ ..._electrumApi.rawTransactionMessage.call(void 0, txid)
227
+ })
228
+ const transaction = _altcoinjs.Transaction.fromHex(hex)
229
+
230
+ // Electrum only reports a transaction's height in the history of a
231
+ // script it pays. Servers refuse the history of busy scripts, so each
232
+ // paid script is tried in turn. When none answers the task fails, so the
233
+ // engine retries it instead of taking the transaction as unconfirmed:
234
+ let blockHeight = 0
235
+ let historyError
236
+ for (const { script } of transaction.outs) {
237
+ if (script[0] === _altcoinjs.opcodes.OP_RETURN) continue
238
+ try {
239
+ // Safe because yielding WsTask<AddressTransactionsResponse> should get
240
+ // AddressTransactionsResponse next.
241
+ const history = (yield {
242
+ ..._electrumApi.addressTransactionsMessage.call(void 0, toElectrumScriptHash(script))
243
+ })
244
+ blockHeight = _nullishCoalesce(_optionalChain([history, 'access', _ => _.find, 'call', _2 => _2(item => item.tx_hash === txid), 'optionalAccess', _3 => _3.height]), () => ( 0))
245
+ historyError = undefined
246
+ break
247
+ } catch (error) {
248
+ log.warn(`failed to fetch the height of transaction ${txid}:`, error)
249
+ historyError = error
250
+ }
251
+ }
252
+ if (historyError != null) throw historyError
253
+
254
+ // The engine reads a fetched transaction's hex, height, outpoints, and
255
+ // outputs. It keeps the date, fee, and input amounts it already stored,
256
+ // which Electrum could only supply with a request per previous transaction:
257
+ const isCoinbase = transaction.isCoinbase()
258
+ return {
259
+ txid,
260
+ hex,
261
+ // Like Blockbook, Electrum puts mempool transactions at height 0 or -1:
262
+ blockHeight,
263
+ confirmations: 0,
264
+ blockTime: 0,
265
+ fees: '0',
266
+ vin: transaction.ins.map((input, n) => ({
267
+ addresses: [],
268
+ n,
269
+ sequence: input.sequence,
270
+ txid: Buffer.from(input.hash).reverse().toString('hex'),
271
+ value: '0',
272
+ vout: input.index,
273
+ coinbase: isCoinbase ? input.script.toString('hex') : undefined
274
+ })),
275
+ vout: transaction.outs.map((output, n) => ({
276
+ n,
277
+ value: output.value.toString(),
278
+ addresses: [], // unused, the engine reads the script in `hex`
279
+ hex: output.script.toString('hex')
280
+ }))
281
+ }
282
+ }
283
+
284
+ let isWatchingBlocks = false
106
285
  const instance = _Blockbook.makeBlockbook.call(void 0, {
107
286
  asAddress,
108
- asResponse,
287
+ asResponse: makeAsResponse(() => isWatchingBlocks),
109
288
  connectionUri,
110
289
  engineEmitter,
111
290
  initOptions,
@@ -114,85 +293,47 @@ const asResponse = (raw) => {
114
293
  uri
115
294
  ) {
116
295
  const generator = taskGeneratorFn(uri)
117
- let nextValue
296
+ let result = await generator.next()
118
297
 
119
298
  while (true) {
120
- const result = await generator.next(nextValue)
121
- const task = result.value
122
-
123
299
  // If the generator result is not a WsTask we will return it:
124
- if (_optionalChain([result, 'optionalAccess', _ => _.done]) === true) return result.value
300
+ if (result.done === true) return result.value
301
+ const task = result.value
125
302
  if (typeof task === 'boolean') return task
126
303
 
127
- // Translate getAccountUtxo to blockchain.scripthash.listunspent:
128
- if (task.method === 'getAccountUtxo') {
129
- const params = task.params
130
- const address =
131
- asAddress != null ? asAddress(params.descriptor) : params.descriptor
132
- const scriptHash = addressToScriptHash(address)
133
-
134
- // Safe because yielding WsTask<ListUnspentResponse> should get
135
- // ListUnspentResponse next.
136
- const electrumUtxos = (yield {
137
- ..._electrumApi.listUnspentMessage.call(void 0, scriptHash)
138
- })
139
-
140
- const blockbookUtxos = electrumUtxos.map(
141
- utxo => ({
142
- txid: utxo.tx_hash,
143
- vout: utxo.tx_pos,
144
- value: utxo.value.toString(),
145
- height: utxo.height
146
- })
147
- )
148
- nextValue = blockbookUtxos
149
- continue
150
- }
151
-
152
- // Get Address Transactions:
153
- if (task.method === 'getAccountInfo') {
154
- const params = task.params
155
- const address =
156
- asAddress != null ? asAddress(params.descriptor) : params.descriptor
157
- const scriptHash = addressToScriptHash(address)
158
-
159
- // Safe because yielding WsTask<AddressTransactionsResponse> should get
160
- // AddressTransactionsResponse next.
161
- const electrumUtxos = (yield {
162
- ..._electrumApi.addressTransactionsMessage.call(void 0, scriptHash)
163
- })
164
-
165
- const blockbookUtxos = {
166
- address, // unused
167
- balance: '0', // unused
168
- totalReceived: '0', // unused
169
- totalSent: '0', // unused
170
- txs: electrumUtxos.length,
171
- unconfirmedBalance: '0', // unused
172
- unconfirmedTxs: electrumUtxos.reduce(
173
- (sum, tx) => (sum + tx.height >= 0 ? 1 : 0),
174
- 0
175
- ),
176
- txids: [], // unused
177
- transactions: [], // TODO: this requires an extra query per txid
178
- page: 0, // unused
179
- totalPages: 1,
180
- itemsOnPage: 0 // unused
181
- }
182
- nextValue = blockbookUtxos
304
+ let response
305
+ try {
306
+ response = yield* translateTask(task)
307
+ } catch (error) {
308
+ // Hand the failure to the task that made the request, as the socket
309
+ // does for the requests it sends unchanged:
310
+ result = await generator.throw(error)
183
311
  continue
184
312
  }
313
+ result = await generator.next(response)
185
314
  }
186
315
  },
187
316
  ping: async () => {
188
317
  return await instance.promisifyWsMessage(_electrumApi.pingMessage.call(void 0, ))
189
318
  },
319
+ subscribeNewBlockMessage: () => ({
320
+ ..._electrumApi.headersSubscribeMessage.call(void 0, ),
321
+ // Notifications hold the header itself, with no response envelope:
322
+ cleaner: (raw) => {
323
+ const header = _electrumApi.asHeadersSubscribeResponse.call(void 0, raw)
324
+ return { height: header.height, hash: headerToBlockHash(header.hex) }
325
+ }
326
+ }),
190
327
  socketEmitter,
191
328
  walletId
192
329
  })
193
330
 
194
- instance.broadcastTx = async () => {
195
- throw new Error('broadcastTx not supported for Electrum connections')
331
+ instance.broadcastTx = async transaction => {
332
+ const txid = await instance.promisifyWsMessage(
333
+ _electrumApi.broadcastTransactionMessage.call(void 0, transaction.signedTx)
334
+ )
335
+ // Blockbook reports the broadcast txid as `result`:
336
+ return { result: txid }
196
337
  }
197
338
  instance.fetchAddress = async () => {
198
339
  throw new Error('fetchAddress not supported for Electrum connections')
@@ -201,18 +342,43 @@ const asResponse = (raw) => {
201
342
  throw new Error('fetchAddressUtxos not supported for Electrum connections')
202
343
  }
203
344
  instance.fetchInfo = async () => {
204
- throw new Error('fetchInfo not supported for Electrum connections')
345
+ const header = await instance.promisifyWsMessage(_electrumApi.headersSubscribeMessage.call(void 0, ))
346
+ return {
347
+ name: pluginInfo.currencyInfo.displayName, // unused
348
+ shortcut: pluginInfo.currencyInfo.currencyCode, // unused
349
+ decimals: 8, // unused
350
+ version: '', // unused
351
+ bestHeight: header.height,
352
+ bestHash: headerToBlockHash(header.hex),
353
+ block0Hash: '', // unused
354
+ testnet: false, // unused
355
+ backend: undefined // unused
356
+ }
205
357
  }
206
358
  instance.fetchTransaction = async () => {
207
359
  throw new Error('fetchTransaction not supported for Electrum connections')
208
360
  }
209
361
 
210
- instance.watchAddresses = async () => {
211
- return // Fail gracefully
362
+ const { watchBlocks } = instance
363
+ instance.watchBlocks = deferredBlockSub => {
364
+ isWatchingBlocks = true
365
+ watchBlocks(deferredBlockSub)
212
366
  }
213
- instance.watchBlocks = async () => {
367
+ instance.watchAddresses = async () => {
214
368
  return // Fail gracefully
215
369
  }
216
370
 
217
371
  return instance
218
372
  } exports.makeBlockbookElectrum = makeBlockbookElectrum;
373
+
374
+ /** True for the URIs of Electrum servers, as opposed to Blockbook servers. */
375
+ const isElectrumUri = (uri) =>
376
+ /^electrumwss?:/.test(uri); exports.isElectrumUri = isElectrumUri
377
+
378
+ // Electrum indexes scripts by their SHA-256, in reverse byte order:
379
+ const toElectrumScriptHash = (scriptPubkey) =>
380
+ _altcoinjs.crypto.sha256(scriptPubkey).reverse().toString('hex')
381
+
382
+ // A block's hash is its header's double SHA-256, in reverse byte order:
383
+ const headerToBlockHash = (headerHex) =>
384
+ _altcoinjs.crypto.hash256(Buffer.from(headerHex, 'hex')).reverse().toString('hex')
@@ -267,6 +267,23 @@ const asResponseDefault = (raw) => [
267
267
  }
268
268
  }
269
269
 
270
+ // A task resumes with each response or failure, and then makes its next
271
+ // request or reports with a boolean that it is done:
272
+ const submitNextTask = (
273
+ generator,
274
+ resumed
275
+ ) => {
276
+ resumed
277
+ .then(result => {
278
+ const task = result.value
279
+ if (typeof task === 'boolean') return
280
+ instance.submitTask(task, generator)
281
+ })
282
+ .catch(e => {
283
+ log.error(e.message)
284
+ })
285
+ }
286
+
270
287
  const onTimer = () => {
271
288
  log(`socket timer with server ${uri} expired, check if healthCheck needed`)
272
289
  const now = Date.now() - TIMER_SLACK
@@ -283,11 +300,10 @@ const asResponseDefault = (raw) => [
283
300
 
284
301
  for (const [id, request] of Object.entries(pendingRequests)) {
285
302
  if (request.startTime + timeout < now) {
286
- request.generator
287
- .throw(new Error(`Timeout for request ${id}`))
288
- .catch(e => {
289
- log.error(e.message)
290
- })
303
+ submitNextTask(
304
+ request.generator,
305
+ request.generator.throw(new Error(`Timeout for request ${id}`))
306
+ )
291
307
  _utils.removeItem.call(void 0, pendingRequests, id)
292
308
  }
293
309
  }
@@ -358,23 +374,13 @@ const asResponseDefault = (raw) => [
358
374
  } else {
359
375
  nextValue = responseMessage.data
360
376
  }
361
- request.generator
362
- .next(nextValue)
363
- .then(result => {
364
- const task = result.value
365
- if (typeof task === 'boolean') {
366
- return
367
- }
368
- instance.submitTask(task, request.generator)
369
- })
370
- .catch(e => {
371
- log.error(e.message)
372
- })
377
+ submitNextTask(
378
+ request.generator,
379
+ request.generator.next(nextValue)
380
+ )
373
381
  } catch (error) {
374
382
  console.log({ uri, error, response: responseMessage, request })
375
- request.generator.throw(error).catch(e => {
376
- log.error(e.message)
377
- })
383
+ submitNextTask(request.generator, request.generator.throw(error))
378
384
  }
379
385
  continue
380
386
  }
@@ -144,13 +144,32 @@ var _cleaners = require('cleaners');
144
144
  const pingMessage = () => {
145
145
  return {
146
146
  method: 'server.ping',
147
- params: undefined,
147
+ // Electrum servers such as Fulcrum reject the `{}` the socket sends for
148
+ // missing params as named params, so an empty array is required:
149
+ params: [],
148
150
  cleaner: exports.asElectrumResponse.call(void 0, exports.asPingResponse)
149
151
  }
150
152
  }; exports.pingMessage = pingMessage
151
153
 
152
154
  const asPingResponse = _cleaners.asMaybe.call(void 0, _cleaners.asUndefined); exports.asPingResponse = asPingResponse
153
155
 
156
+ /**
157
+ * Subscribe to Headers (Get Block Height)
158
+ */
159
+ const headersSubscribeMessage = () => ({
160
+ method: 'blockchain.headers.subscribe',
161
+ params: [],
162
+ cleaner: exports.asElectrumResponse.call(void 0, exports.asHeadersSubscribeResponse)
163
+ }); exports.headersSubscribeMessage = headersSubscribeMessage
164
+
165
+
166
+
167
+ // The chain tip's header, which the server also sends for each new tip:
168
+ const asHeadersSubscribeResponse = _cleaners.asObject.call(void 0, {
169
+ height: _cleaners.asNumber,
170
+ hex: _cleaners.asString
171
+ }); exports.asHeadersSubscribeResponse = asHeadersSubscribeResponse
172
+
154
173
  /**
155
174
  * List Unspent (Get UTXOs)
156
175
  */
@@ -201,3 +220,32 @@ var _cleaners = require('cleaners');
201
220
  }); exports.transactionMessage = transactionMessage
202
221
 
203
222
  const asTransactionResponse = exports.asElectrumTransaction; exports.asTransactionResponse = asTransactionResponse
223
+
224
+ /**
225
+ * Get Raw Transaction
226
+ */
227
+ const rawTransactionMessage = (
228
+ txHash
229
+ ) => ({
230
+ method: 'blockchain.transaction.get',
231
+ params: [txHash, false],
232
+ cleaner: exports.asElectrumResponse.call(void 0, exports.asRawTransactionResponse)
233
+ }); exports.rawTransactionMessage = rawTransactionMessage
234
+
235
+ const asRawTransactionResponse = _cleaners.asString; exports.asRawTransactionResponse = asRawTransactionResponse
236
+
237
+ /**
238
+ * Broadcast Transaction
239
+ */
240
+ const broadcastTransactionMessage = (
241
+ rawTx
242
+ ) => ({
243
+ method: 'blockchain.transaction.broadcast',
244
+ params: [rawTx],
245
+ cleaner: exports.asElectrumResponse.call(void 0, exports.asBroadcastTransactionResponse)
246
+ }); exports.broadcastTransactionMessage = broadcastTransactionMessage
247
+
248
+
249
+
250
+ // The server answers with the transaction's txid:
251
+ const asBroadcastTransactionResponse = _cleaners.asString; exports.asBroadcastTransactionResponse = asBroadcastTransactionResponse
@@ -161,6 +161,16 @@ export interface CoinInfo {
161
161
  * build-in replay-protection mechanisms.
162
162
  */
163
163
  sighash?: number;
164
+ /**
165
+ * Optional nLockTime value set on every transaction. This is used by forks
166
+ * whose nodes treat a magic nLockTime as final while the parent chain reads
167
+ * it as an unreachable block height, making the fork's transactions
168
+ * impossible to replay on the parent chain.
169
+ *
170
+ * Transactions for these currencies always have a non-final input sequence,
171
+ * since nodes ignore nLockTime when every input sequence is final.
172
+ */
173
+ lockTime?: number;
164
174
  /**
165
175
  * A function to be passed to AltcoinJS `signInput` method. This is used to
166
176
  * get the input hash for the signature algorithm before signing the input.
@@ -52,6 +52,7 @@ export interface ServerStates {
52
52
  stop: () => void;
53
53
  serverCanGetTx: (uri: string, txid: string) => boolean;
54
54
  serverCanGetAddress: (uri: string, address: string) => boolean;
55
+ serverCanGetAddressHistory: (uri: string, address: string) => boolean;
55
56
  serverIsAwareOfAddress: (uri: string, address: string) => boolean;
56
57
  getServerState: (uri: string) => ServerState | undefined;
57
58
  refillServers: () => void;
@@ -42,6 +42,8 @@ interface CommonParams {
42
42
  emitter: EngineEmitter;
43
43
  taskCache: TaskCache;
44
44
  pendingTimeouts: Set<NodeJS.Timeout>;
45
+ electrumHistoryAddresses: Set<string>;
46
+ reopenAddresses: (addresses: Iterable<string>) => number;
45
47
  updateProgressRatio: (scriptPubkey: string, process: AddressProcess) => Promise<void>;
46
48
  updateSeenTxCheckpoint: () => void;
47
49
  io: EdgeIo;
@@ -12,6 +12,8 @@ export { info as digibyte } from './digibyte';
12
12
  export { info as dogecoin } from './dogecoin';
13
13
  export { info as eboost } from './eboost';
14
14
  export { info as ecash } from './ecash';
15
+ export { info as ecashbitcoin } from './ecashbitcoin';
16
+ export { info as ecashbitcoinbetanet } from './ecashbitcoinbetanet';
15
17
  export { info as feathercoin } from './feathercoin';
16
18
  export { info as groestlcoin } from './groestlcoin';
17
19
  export { info as litecoin } from './litecoin';
@@ -0,0 +1,3 @@
1
+ import { CoinInfo, PluginInfo } from '../../plugin/types';
2
+ export declare const coinInfo: CoinInfo;
3
+ export declare const info: PluginInfo;
@@ -0,0 +1,3 @@
1
+ import { CoinInfo, PluginInfo } from '../../plugin/types';
2
+ export declare const coinInfo: CoinInfo;
3
+ export declare const info: PluginInfo;
@@ -2,7 +2,7 @@ import { Cleaner } from 'cleaners';
2
2
  import { EdgeLog, EdgeTransaction } from 'edge-core-js/types';
3
3
  import { EngineEmitter } from '../../plugin/EngineEmitter';
4
4
  import { UtxoInitOptions } from '../engine/types';
5
- import { AddressResponse, AddresssMessageParams, AddressUtxosResponse, BlockbookTask, BlockbookTransaction, BroadcastTxResponse, InfoResponse, SubscribeAddressResponse, TransactionResponse } from './blockbookApi';
5
+ import { AddressResponse, AddresssMessageParams, AddressUtxosResponse, BlockbookTask, BlockbookTransaction, BroadcastTxResponse, InfoResponse, SubscribeAddressResponse, SubscribeNewBlockResponse, TransactionResponse } from './blockbookApi';
6
6
  import Deferred from './Deferred';
7
7
  import { TaskGeneratorFn, WsResponse, WsTaskGenerator } from './Socket';
8
8
  import { SocketEmitter } from './SocketEmitter';
@@ -40,6 +40,8 @@ interface BlockbookConfig {
40
40
  log: EdgeLog;
41
41
  taskGeneratorFn: TaskGeneratorFn;
42
42
  ping?: () => Promise<void>;
43
+ /** The new-block subscription, for servers that are not Blockbook. */
44
+ subscribeNewBlockMessage?: () => BlockbookTask<SubscribeNewBlockResponse>;
43
45
  socketEmitter: SocketEmitter;
44
46
  walletId: string;
45
47
  /** Socket timing overrides, for tests. */
@@ -25,3 +25,5 @@ export interface ElectrumResponseMessage {
25
25
  } | undefined;
26
26
  }
27
27
  export declare function makeBlockbookElectrum(config: BlockbookElectrumConfig): Blockbook;
28
+ /** True for the URIs of Electrum servers, as opposed to Blockbook servers. */
29
+ export declare const isElectrumUri: (uri: string) => boolean;
@@ -67,6 +67,15 @@ export declare const asElectrumResponse: <T>(asT: Cleaner<T>) => Cleaner<T>;
67
67
  export declare const pingMessage: () => ElectrumTask<PingResponse>;
68
68
  export declare type PingResponse = ReturnType<typeof asPingResponse>;
69
69
  export declare const asPingResponse: Cleaner<undefined>;
70
+ /**
71
+ * Subscribe to Headers (Get Block Height)
72
+ */
73
+ export declare const headersSubscribeMessage: () => ElectrumTask<HeadersSubscribeResponse>;
74
+ export declare type HeadersSubscribeResponse = ReturnType<typeof asHeadersSubscribeResponse>;
75
+ export declare const asHeadersSubscribeResponse: import("cleaners").ObjectCleaner<{
76
+ height: number;
77
+ hex: string;
78
+ }>;
70
79
  /**
71
80
  * List Unspent (Get UTXOs)
72
81
  */
@@ -88,3 +97,15 @@ export declare const asAddressTransactionsResponse: Cleaner<{
88
97
  export declare const transactionMessage: (txHash: string, asAddress?: Cleaner<string>) => ElectrumTask<TransactionResponse>;
89
98
  export declare type TransactionResponse = ElectrumTransaction;
90
99
  export declare const asTransactionResponse: (asAddress?: Cleaner<string>) => Cleaner<ElectrumTransaction>;
100
+ /**
101
+ * Get Raw Transaction
102
+ */
103
+ export declare const rawTransactionMessage: (txHash: string) => ElectrumTask<RawTransactionResponse>;
104
+ export declare type RawTransactionResponse = ReturnType<typeof asRawTransactionResponse>;
105
+ export declare const asRawTransactionResponse: Cleaner<string>;
106
+ /**
107
+ * Broadcast Transaction
108
+ */
109
+ export declare const broadcastTransactionMessage: (rawTx: string) => ElectrumTask<BroadcastTransactionResponse>;
110
+ export declare type BroadcastTransactionResponse = ReturnType<typeof asBroadcastTransactionResponse>;
111
+ export declare const asBroadcastTransactionResponse: Cleaner<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-currency-plugins",
3
- "version": "3.16.0",
3
+ "version": "3.17.0",
4
4
  "description": "Edge currency plugins to fetch and process wallet accounts, balances and transactions.",
5
5
  "homepage": "https://edge.app/",
6
6
  "repository": "https://github.com/EdgeApp/edge-currency-plugins",