@bsv/sdk 1.0.38 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/compat/Utxo.js +50 -0
- package/dist/cjs/src/compat/Utxo.js.map +1 -0
- package/dist/cjs/src/compat/index.js +3 -1
- package/dist/cjs/src/compat/index.js.map +1 -1
- package/dist/cjs/src/primitives/AESGCM.js +1 -1
- package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js +0 -1
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/transaction/Broadcaster.js +15 -0
- package/dist/cjs/src/transaction/Broadcaster.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +73 -10
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/broadcasters/ARC.js +26 -5
- package/dist/cjs/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/cjs/src/transaction/index.js +4 -1
- package/dist/cjs/src/transaction/index.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/compat/Utxo.js +44 -0
- package/dist/esm/src/compat/Utxo.js.map +1 -0
- package/dist/esm/src/compat/index.js +1 -0
- package/dist/esm/src/compat/index.js.map +1 -1
- package/dist/esm/src/primitives/AESGCM.js +1 -1
- package/dist/esm/src/primitives/AESGCM.js.map +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +0 -1
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/transaction/Broadcaster.js +12 -1
- package/dist/esm/src/transaction/Broadcaster.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +73 -10
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/broadcasters/ARC.js +27 -4
- package/dist/esm/src/transaction/broadcasters/ARC.js.map +1 -1
- package/dist/esm/src/transaction/index.js +1 -0
- package/dist/esm/src/transaction/index.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/compat/Utxo.d.ts +42 -0
- package/dist/types/src/compat/Utxo.d.ts.map +1 -0
- package/dist/types/src/compat/index.d.ts +1 -0
- package/dist/types/src/compat/index.d.ts.map +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
- package/dist/types/src/transaction/Broadcaster.d.ts +8 -0
- package/dist/types/src/transaction/Broadcaster.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +14 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/transaction/TransactionInput.d.ts +0 -4
- package/dist/types/src/transaction/TransactionInput.d.ts.map +1 -1
- package/dist/types/src/transaction/broadcasters/ARC.d.ts +9 -3
- package/dist/types/src/transaction/broadcasters/ARC.d.ts.map +1 -1
- package/dist/types/src/transaction/index.d.ts +2 -1
- package/dist/types/src/transaction/index.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/compat.md +52 -10
- package/docs/transaction.md +82 -3
- package/package.json +1 -1
- package/src/compat/Utxo.ts +55 -0
- package/src/compat/index.ts +1 -0
- package/src/primitives/AESGCM.ts +4 -4
- package/src/script/templates/P2PKH.ts +0 -1
- package/src/transaction/Broadcaster.ts +14 -0
- package/src/transaction/Transaction.ts +74 -10
- package/src/transaction/TransactionInput.ts +0 -4
- package/src/transaction/__tests/Transaction.test.ts +8 -8
- package/src/transaction/broadcasters/ARC.ts +35 -7
- package/src/transaction/broadcasters/__tests/ARC.test.ts +2 -2
- package/src/transaction/index.ts +2 -1
package/docs/transaction.md
CHANGED
|
@@ -47,7 +47,6 @@ export default interface TransactionInput {
|
|
|
47
47
|
sourceTransaction?: Transaction;
|
|
48
48
|
sourceTXID?: string;
|
|
49
49
|
sourceOutputIndex: number;
|
|
50
|
-
sourceSatoshis?: number;
|
|
51
50
|
unlockingScript?: UnlockingScript;
|
|
52
51
|
unlockingScriptTemplate?: {
|
|
53
52
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>;
|
|
@@ -348,8 +347,10 @@ Configuration options for the ARC broadcaster.
|
|
|
348
347
|
```ts
|
|
349
348
|
export interface ArcConfig {
|
|
350
349
|
apiKey?: string;
|
|
351
|
-
deploymentId?: string;
|
|
352
350
|
httpClient?: HttpClient;
|
|
351
|
+
deploymentId?: string;
|
|
352
|
+
callbackUrl?: string;
|
|
353
|
+
callbackToken?: string;
|
|
353
354
|
headers?: Record<string, string>;
|
|
354
355
|
}
|
|
355
356
|
```
|
|
@@ -366,6 +367,22 @@ Authentication token for the ARC API
|
|
|
366
367
|
apiKey?: string
|
|
367
368
|
```
|
|
368
369
|
|
|
370
|
+
#### Property callbackToken
|
|
371
|
+
|
|
372
|
+
default access token for notification callback endpoint. It will be used as a Authorization header for the http callback
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
callbackToken?: string
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Property callbackUrl
|
|
379
|
+
|
|
380
|
+
notification callback endpoint for proofs and double spend notification
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
callbackUrl?: string
|
|
384
|
+
```
|
|
385
|
+
|
|
369
386
|
#### Property deploymentId
|
|
370
387
|
|
|
371
388
|
Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set
|
|
@@ -376,7 +393,7 @@ deploymentId?: string
|
|
|
376
393
|
|
|
377
394
|
#### Property headers
|
|
378
395
|
|
|
379
|
-
|
|
396
|
+
additional headers to be attached to all tx submissions.
|
|
380
397
|
|
|
381
398
|
```ts
|
|
382
399
|
headers?: Record<string, string>
|
|
@@ -699,6 +716,8 @@ export default class ARC implements Broadcaster {
|
|
|
699
716
|
readonly URL: string;
|
|
700
717
|
readonly apiKey: string | undefined;
|
|
701
718
|
readonly deploymentId: string;
|
|
719
|
+
readonly callbackUrl: string | undefined;
|
|
720
|
+
readonly callbackToken: string | undefined;
|
|
702
721
|
readonly headers: Record<string, string> | undefined;
|
|
703
722
|
constructor(URL: string, config?: ArcConfig);
|
|
704
723
|
constructor(URL: string, apiKey?: string);
|
|
@@ -827,6 +846,7 @@ export default class Transaction {
|
|
|
827
846
|
metadata: Record<string, any>;
|
|
828
847
|
merklePath?: MerklePath;
|
|
829
848
|
static fromBEEF(beef: number[]): Transaction
|
|
849
|
+
static fromEF(ef: number[]): Transaction
|
|
830
850
|
static parseScriptOffsets(bin: number[]): {
|
|
831
851
|
inputs: Array<{
|
|
832
852
|
vin: number;
|
|
@@ -841,6 +861,7 @@ export default class Transaction {
|
|
|
841
861
|
}
|
|
842
862
|
static fromBinary(bin: number[]): Transaction
|
|
843
863
|
static fromHex(hex: string): Transaction
|
|
864
|
+
static fromHexEF(hex: string): Transaction
|
|
844
865
|
static fromHexBEEF(hex: string): Transaction
|
|
845
866
|
constructor(version: number = 1, inputs: TransactionInput[] = [], outputs: TransactionOutput[] = [], lockTime: number = 0, metadata: Record<string, any> = {}, merklePath?: MerklePath)
|
|
846
867
|
addInput(input: TransactionInput): void
|
|
@@ -969,6 +990,23 @@ Argument Details
|
|
|
969
990
|
+ **bin**
|
|
970
991
|
+ The binary array representation of the transaction.
|
|
971
992
|
|
|
993
|
+
#### Method fromEF
|
|
994
|
+
|
|
995
|
+
Creates a new transaction, linked to its inputs and their associated merkle paths, from a EF (BRC-30) structure.
|
|
996
|
+
|
|
997
|
+
```ts
|
|
998
|
+
static fromEF(ef: number[]): Transaction
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
Returns
|
|
1002
|
+
|
|
1003
|
+
An extended transaction, linked to its associated inputs by locking script and satoshis amounts only.
|
|
1004
|
+
|
|
1005
|
+
Argument Details
|
|
1006
|
+
|
|
1007
|
+
+ **ef**
|
|
1008
|
+
+ A binary representation of a transaction in EF format.
|
|
1009
|
+
|
|
972
1010
|
#### Method fromHex
|
|
973
1011
|
|
|
974
1012
|
Creates a Transaction instance from a hexadecimal string.
|
|
@@ -1003,6 +1041,23 @@ Argument Details
|
|
|
1003
1041
|
+ **hex**
|
|
1004
1042
|
+ The hexadecimal string representation of the transaction BEEF.
|
|
1005
1043
|
|
|
1044
|
+
#### Method fromHexEF
|
|
1045
|
+
|
|
1046
|
+
Creates a Transaction instance from a hexadecimal string encoded EF.
|
|
1047
|
+
|
|
1048
|
+
```ts
|
|
1049
|
+
static fromHexEF(hex: string): Transaction
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
Returns
|
|
1053
|
+
|
|
1054
|
+
- A new Transaction instance.
|
|
1055
|
+
|
|
1056
|
+
Argument Details
|
|
1057
|
+
|
|
1058
|
+
+ **hex**
|
|
1059
|
+
+ The hexadecimal string representation of the transaction EF.
|
|
1060
|
+
|
|
1006
1061
|
#### Method getFee
|
|
1007
1062
|
|
|
1008
1063
|
Utility method that returns the current fee based on inputs and outputs
|
|
@@ -1236,11 +1291,35 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
|
|
|
1236
1291
|
| [defaultBroadcaster](#function-defaultbroadcaster) |
|
|
1237
1292
|
| [defaultChainTracker](#function-defaultchaintracker) |
|
|
1238
1293
|
| [defaultHttpClient](#function-defaulthttpclient) |
|
|
1294
|
+
| [isBroadcastFailure](#function-isbroadcastfailure) |
|
|
1295
|
+
| [isBroadcastResponse](#function-isbroadcastresponse) |
|
|
1296
|
+
|
|
1297
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
1298
|
+
|
|
1299
|
+
---
|
|
1300
|
+
|
|
1301
|
+
### Function: isBroadcastResponse
|
|
1302
|
+
|
|
1303
|
+
Convenience type guard for response from `Broadcaster.broadcast`
|
|
1304
|
+
|
|
1305
|
+
```ts
|
|
1306
|
+
export function isBroadcastResponse(r: BroadcastResponse | BroadcastFailure): r is BroadcastResponse
|
|
1307
|
+
```
|
|
1239
1308
|
|
|
1240
1309
|
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
1241
1310
|
|
|
1242
1311
|
---
|
|
1312
|
+
### Function: isBroadcastFailure
|
|
1313
|
+
|
|
1314
|
+
Convenience type guard for response from `Broadcaster.broadcast`
|
|
1243
1315
|
|
|
1316
|
+
```ts
|
|
1317
|
+
export function isBroadcastFailure(r: BroadcastResponse | BroadcastFailure): r is BroadcastFailure
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
1321
|
+
|
|
1322
|
+
---
|
|
1244
1323
|
### Function: defaultHttpClient
|
|
1245
1324
|
|
|
1246
1325
|
Returns a default HttpClient implementation based on the environment that it is run on.
|
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Transaction from '../transaction/Transaction.js'
|
|
2
|
+
import TransactionInput from '../transaction/TransactionInput.js'
|
|
3
|
+
import LockingScript from '../script/LockingScript.js'
|
|
4
|
+
import UnlockingScript from '../script/UnlockingScript.js'
|
|
5
|
+
|
|
6
|
+
type jsonUtxo = {
|
|
7
|
+
txid: string
|
|
8
|
+
vout: number
|
|
9
|
+
satoshis: number
|
|
10
|
+
script: string
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @method fromUtxo
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* This function creates a transaction input from a utxo json object
|
|
17
|
+
* The idea being old code that uses utxos rather than sourceTranactions can convert using this.
|
|
18
|
+
*
|
|
19
|
+
* @deprecated
|
|
20
|
+
* This approach is made available for compatibility only. It is deprecated in favor of using sourceTransactions
|
|
21
|
+
* directly. It's recommended that wallets general keep transactions which store unspent outputs in their entirety,
|
|
22
|
+
* along with corresonding Merkle paths. The reason you would keep the whole transaction is such that you can prove
|
|
23
|
+
* the txid, and therefore its inclusion within a specific block.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const i = fromUtxo({
|
|
27
|
+
* txid: '434555433eaca96dff6e71a4d02febd0dd3832e5ca4e5734623ca914522e17d5',
|
|
28
|
+
* vout: 0,
|
|
29
|
+
* script: '51',
|
|
30
|
+
* satoshis: 1234
|
|
31
|
+
* }, new P2PKH().unlock(p))
|
|
32
|
+
*
|
|
33
|
+
* tx.addInput(i)
|
|
34
|
+
*
|
|
35
|
+
* @param utxo: jsonUtxo
|
|
36
|
+
* @param unlockingScriptTemplate: { sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>, estimateLength: (tx: Transaction, inputIndex: number) => Promise<number> }
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
export default function fromUtxo(utxo: jsonUtxo, unlockingScriptTemplate: {
|
|
40
|
+
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
41
|
+
estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
|
|
42
|
+
}): TransactionInput {
|
|
43
|
+
const sourceTransaction = new Transaction(0, [], [], 0)
|
|
44
|
+
sourceTransaction.outputs = Array(utxo.vout + 1).fill(null)
|
|
45
|
+
sourceTransaction.outputs[utxo.vout] = {
|
|
46
|
+
satoshis: utxo.satoshis,
|
|
47
|
+
lockingScript: LockingScript.fromHex(utxo.script)
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
sourceTransaction,
|
|
51
|
+
sourceOutputIndex: utxo.vout,
|
|
52
|
+
unlockingScriptTemplate,
|
|
53
|
+
sequence: 0xFFFFFFFF
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/compat/index.ts
CHANGED
package/src/primitives/AESGCM.ts
CHANGED
|
@@ -180,15 +180,15 @@ export function AES (input: number[], key: number[]): number[] {
|
|
|
180
180
|
|
|
181
181
|
// Since the BigNumber representation of keys ignores big endian zeroes,
|
|
182
182
|
// extend incoming key arrays with zeros to the smallest standard key size.
|
|
183
|
-
|
|
183
|
+
const ekey = Array.from(key)
|
|
184
184
|
if (ekey.length <= 16) {
|
|
185
|
-
while (ekey.length < 16) ekey.unshift(0)
|
|
185
|
+
while (ekey.length < 16) ekey.unshift(0)
|
|
186
186
|
roundLimit = 11
|
|
187
187
|
} else if (ekey.length <= 24) {
|
|
188
|
-
while (ekey.length < 24) ekey.unshift(0)
|
|
188
|
+
while (ekey.length < 24) ekey.unshift(0)
|
|
189
189
|
roundLimit = 13
|
|
190
190
|
} else if (key.length <= 32) {
|
|
191
|
-
while (ekey.length < 32) ekey.unshift(0)
|
|
191
|
+
while (ekey.length < 32) ekey.unshift(0)
|
|
192
192
|
roundLimit = 15
|
|
193
193
|
} else {
|
|
194
194
|
throw new Error('Illegal key length: ' + String(key.length))
|
|
@@ -91,7 +91,6 @@ export default class P2PKH implements ScriptTemplate {
|
|
|
91
91
|
)
|
|
92
92
|
}
|
|
93
93
|
sourceSatoshis ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis
|
|
94
|
-
sourceSatoshis ||= input.sourceSatoshis
|
|
95
94
|
if (!sourceSatoshis) {
|
|
96
95
|
throw new Error(
|
|
97
96
|
'The sourceSatoshis or input sourceTransaction is required for transaction signing.'
|
|
@@ -40,3 +40,17 @@ export interface Broadcaster {
|
|
|
40
40
|
broadcast: (transaction: Transaction) =>
|
|
41
41
|
Promise<BroadcastResponse | BroadcastFailure>
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Convenience type guard for response from `Broadcaster.broadcast`
|
|
46
|
+
*/
|
|
47
|
+
export function isBroadcastResponse (r: BroadcastResponse | BroadcastFailure): r is BroadcastResponse {
|
|
48
|
+
return r.status === 'success'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Convenience type guard for response from `Broadcaster.broadcast`
|
|
53
|
+
*/
|
|
54
|
+
export function isBroadcastFailure (r: BroadcastResponse | BroadcastFailure): r is BroadcastFailure {
|
|
55
|
+
return r.status === 'error'
|
|
56
|
+
}
|
|
@@ -122,6 +122,59 @@ export default class Transaction {
|
|
|
122
122
|
return transactions[lastTXID].tx
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates a new transaction, linked to its inputs and their associated merkle paths, from a EF (BRC-30) structure.
|
|
128
|
+
* @param ef A binary representation of a transaction in EF format.
|
|
129
|
+
* @returns An extended transaction, linked to its associated inputs by locking script and satoshis amounts only.
|
|
130
|
+
*/
|
|
131
|
+
static fromEF (ef: number[]): Transaction {
|
|
132
|
+
const br = new Reader(ef)
|
|
133
|
+
const version = br.readUInt32LE()
|
|
134
|
+
if (toHex(br.read(6)) !== '0000000000ef') throw new Error('Invalid EF marker')
|
|
135
|
+
const inputsLength = br.readVarIntNum()
|
|
136
|
+
const inputs: TransactionInput[] = []
|
|
137
|
+
for (let i = 0; i < inputsLength; i++) {
|
|
138
|
+
const sourceTXID = toHex(br.readReverse(32))
|
|
139
|
+
const sourceOutputIndex = br.readUInt32LE()
|
|
140
|
+
const scriptLength = br.readVarIntNum()
|
|
141
|
+
const scriptBin = br.read(scriptLength)
|
|
142
|
+
const unlockingScript = UnlockingScript.fromBinary(scriptBin)
|
|
143
|
+
const sequence = br.readUInt32LE()
|
|
144
|
+
const satoshis = br.readUInt64LEBn().toNumber()
|
|
145
|
+
const lockingScriptLength = br.readVarIntNum()
|
|
146
|
+
const lockingScriptBin = br.read(lockingScriptLength)
|
|
147
|
+
const lockingScript = LockingScript.fromBinary(lockingScriptBin)
|
|
148
|
+
const sourceTransaction = new Transaction(null, [], [], null)
|
|
149
|
+
sourceTransaction.outputs = Array(sourceOutputIndex + 1).fill(null)
|
|
150
|
+
sourceTransaction.outputs[sourceOutputIndex] = {
|
|
151
|
+
satoshis,
|
|
152
|
+
lockingScript
|
|
153
|
+
}
|
|
154
|
+
inputs.push({
|
|
155
|
+
sourceTransaction,
|
|
156
|
+
sourceTXID,
|
|
157
|
+
sourceOutputIndex,
|
|
158
|
+
unlockingScript,
|
|
159
|
+
sequence
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
const outputsLength = br.readVarIntNum()
|
|
163
|
+
const outputs: TransactionOutput[] = []
|
|
164
|
+
for (let i = 0; i < outputsLength; i++) {
|
|
165
|
+
const satoshis = br.readUInt64LEBn().toNumber()
|
|
166
|
+
const scriptLength = br.readVarIntNum()
|
|
167
|
+
const scriptBin = br.read(scriptLength)
|
|
168
|
+
const lockingScript = LockingScript.fromBinary(scriptBin)
|
|
169
|
+
outputs.push({
|
|
170
|
+
satoshis,
|
|
171
|
+
lockingScript
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
const lockTime = br.readUInt32LE()
|
|
175
|
+
return new Transaction(version, inputs, outputs, lockTime)
|
|
176
|
+
}
|
|
177
|
+
|
|
125
178
|
/**
|
|
126
179
|
* Since the validation of blockchain data is atomically transaction data validation,
|
|
127
180
|
* any application seeking to validate data in output scripts must store the entire transaction as well.
|
|
@@ -220,6 +273,17 @@ export default class Transaction {
|
|
|
220
273
|
return Transaction.fromBinary(toArray(hex, 'hex'))
|
|
221
274
|
}
|
|
222
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Creates a Transaction instance from a hexadecimal string encoded EF.
|
|
278
|
+
*
|
|
279
|
+
* @static
|
|
280
|
+
* @param {string} hex - The hexadecimal string representation of the transaction EF.
|
|
281
|
+
* @returns {Transaction} - A new Transaction instance.
|
|
282
|
+
*/
|
|
283
|
+
static fromHexEF (hex: string): Transaction {
|
|
284
|
+
return Transaction.fromEF(toArray(hex, 'hex'))
|
|
285
|
+
}
|
|
286
|
+
|
|
223
287
|
/**
|
|
224
288
|
* Creates a Transaction instance from a hexadecimal string encoded BEEF.
|
|
225
289
|
*
|
|
@@ -316,12 +380,10 @@ export default class Transaction {
|
|
|
316
380
|
// change = inputs - fee - non-change outputs
|
|
317
381
|
let change = 0
|
|
318
382
|
for (const input of this.inputs) {
|
|
319
|
-
if (typeof input.sourceTransaction !== 'object'
|
|
320
|
-
throw new Error('Source transactions
|
|
383
|
+
if (typeof input.sourceTransaction !== 'object') {
|
|
384
|
+
throw new Error('Source transactions are required for all inputs during fee computation')
|
|
321
385
|
}
|
|
322
|
-
change += input.sourceTransaction
|
|
323
|
-
? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
324
|
-
: input.sourceSatoshis
|
|
386
|
+
change += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
325
387
|
}
|
|
326
388
|
change -= fee
|
|
327
389
|
let changeCount = 0
|
|
@@ -367,12 +429,10 @@ export default class Transaction {
|
|
|
367
429
|
getFee (): number {
|
|
368
430
|
let totalIn = 0
|
|
369
431
|
for (const input of this.inputs) {
|
|
370
|
-
if (typeof input.sourceTransaction !== 'object'
|
|
432
|
+
if (typeof input.sourceTransaction !== 'object') {
|
|
371
433
|
throw new Error('Source transactions or sourceSatoshis are required for all inputs to calculate fee')
|
|
372
434
|
}
|
|
373
|
-
totalIn += input.sourceTransaction
|
|
374
|
-
? input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
375
|
-
: input.sourceSatoshis || 0
|
|
435
|
+
totalIn += input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis
|
|
376
436
|
}
|
|
377
437
|
let totalOut = 0
|
|
378
438
|
for (const output of this.outputs) {
|
|
@@ -465,7 +525,11 @@ export default class Transaction {
|
|
|
465
525
|
if (typeof i.sourceTransaction === 'undefined') {
|
|
466
526
|
throw new Error('All inputs must have source transactions when serializing to EF format')
|
|
467
527
|
}
|
|
468
|
-
|
|
528
|
+
if (typeof i.sourceTXID === 'undefined') {
|
|
529
|
+
writer.write(i.sourceTransaction.hash() as number[])
|
|
530
|
+
} else {
|
|
531
|
+
writer.write(toArray(i.sourceTXID, 'hex').reverse() as number[])
|
|
532
|
+
}
|
|
469
533
|
writer.writeUInt32LE(i.sourceOutputIndex)
|
|
470
534
|
const scriptBin = i.unlockingScript.toBinary()
|
|
471
535
|
writer.writeVarIntNum(scriptBin.length)
|
|
@@ -16,9 +16,6 @@ import Transaction from './Transaction.js'
|
|
|
16
16
|
* @property {number} sourceOutputIndex - The index of the output in the source transaction
|
|
17
17
|
* that this input is spending. It is zero-based, indicating the position of the
|
|
18
18
|
* output in the array of outputs of the source transaction.
|
|
19
|
-
* @property {number} [sourceSatoshis] - The amount of satoshis of the source transaction
|
|
20
|
-
* output that this input is spending, used for fee calculation and signing when
|
|
21
|
-
* source transaction is not present
|
|
22
19
|
* @property {UnlockingScript} [unlockingScript] - Optional. The script that 'unlocks' the
|
|
23
20
|
* source output for spending. This script typically contains signatures and
|
|
24
21
|
* public keys that evidence the ownership of the output.
|
|
@@ -57,7 +54,6 @@ export default interface TransactionInput {
|
|
|
57
54
|
sourceTransaction?: Transaction
|
|
58
55
|
sourceTXID?: string
|
|
59
56
|
sourceOutputIndex: number
|
|
60
|
-
sourceSatoshis?: number
|
|
61
57
|
unlockingScript?: UnlockingScript
|
|
62
58
|
unlockingScriptTemplate?: {
|
|
63
59
|
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
@@ -9,6 +9,7 @@ import { hash256, hash160 } from '../../../dist/cjs/src/primitives/Hash'
|
|
|
9
9
|
import PrivateKey from '../../../dist/cjs/src/primitives/PrivateKey'
|
|
10
10
|
import Curve from '../../../dist/cjs/src/primitives/Curve'
|
|
11
11
|
import P2PKH from '../../../dist/cjs/src/script/templates/P2PKH'
|
|
12
|
+
import fromUtxo from '../../../dist/cjs/src/compat/Utxo'
|
|
12
13
|
|
|
13
14
|
import sighashVectors from '../../primitives/__tests/sighash.vectors'
|
|
14
15
|
import invalidTransactions from './tx.invalid.vectors'
|
|
@@ -396,14 +397,13 @@ describe('Transaction', () => {
|
|
|
396
397
|
const priv = PrivateKey.fromRandom()
|
|
397
398
|
const tx = new Transaction()
|
|
398
399
|
utxos.forEach(utxo => {
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
})
|
|
400
|
+
const u = {
|
|
401
|
+
txid: utxo.tx_hash,
|
|
402
|
+
vout: utxo.tx_pos,
|
|
403
|
+
script: new P2PKH().lock(priv.toPublicKey().toHash()).toHex(),
|
|
404
|
+
satoshis: utxo.value
|
|
405
|
+
}
|
|
406
|
+
tx.addInput(fromUtxo(u, new P2PKH().unlock(priv)))
|
|
407
407
|
})
|
|
408
408
|
tx.addOutput({
|
|
409
409
|
lockingScript: new P2PKH().lock(priv.toAddress()),
|
|
@@ -9,11 +9,15 @@ import {toHex} from "../../primitives/utils.js";
|
|
|
9
9
|
export interface ArcConfig {
|
|
10
10
|
/** Authentication token for the ARC API */
|
|
11
11
|
apiKey?: string
|
|
12
|
-
/** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
|
|
13
|
-
deploymentId?: string
|
|
14
12
|
/** The HTTP client used to make requests to the ARC API. */
|
|
15
13
|
httpClient?: HttpClient
|
|
16
|
-
/**
|
|
14
|
+
/** Deployment id used annotating api calls in XDeployment-ID header - this value will be randomly generated if not set */
|
|
15
|
+
deploymentId?: string
|
|
16
|
+
/** notification callback endpoint for proofs and double spend notification */
|
|
17
|
+
callbackUrl?: string
|
|
18
|
+
/** default access token for notification callback endpoint. It will be used as a Authorization header for the http callback */
|
|
19
|
+
callbackToken?: string
|
|
20
|
+
/** additional headers to be attached to all tx submissions. */
|
|
17
21
|
headers?: Record<string, string>
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -29,6 +33,8 @@ export default class ARC implements Broadcaster {
|
|
|
29
33
|
readonly URL: string
|
|
30
34
|
readonly apiKey: string | undefined
|
|
31
35
|
readonly deploymentId: string
|
|
36
|
+
readonly callbackUrl: string | undefined
|
|
37
|
+
readonly callbackToken: string | undefined
|
|
32
38
|
readonly headers: Record<string, string> | undefined
|
|
33
39
|
private readonly httpClient: HttpClient;
|
|
34
40
|
|
|
@@ -52,11 +58,16 @@ export default class ARC implements Broadcaster {
|
|
|
52
58
|
if (typeof config === 'string') {
|
|
53
59
|
this.apiKey = config
|
|
54
60
|
this.httpClient = defaultHttpClient()
|
|
61
|
+
this.deploymentId = defaultDeploymentId()
|
|
62
|
+
this.callbackToken = undefined
|
|
63
|
+
this.callbackUrl = undefined
|
|
55
64
|
} else {
|
|
56
|
-
const {apiKey, deploymentId,
|
|
65
|
+
const {apiKey, deploymentId, httpClient, callbackToken, callbackUrl, headers } = config ?? {} as ArcConfig
|
|
66
|
+
this.apiKey = apiKey
|
|
57
67
|
this.httpClient = httpClient ?? defaultHttpClient()
|
|
58
68
|
this.deploymentId = deploymentId ?? defaultDeploymentId()
|
|
59
|
-
this.
|
|
69
|
+
this.callbackToken = callbackToken
|
|
70
|
+
this.callbackUrl = callbackUrl
|
|
60
71
|
this.headers = headers
|
|
61
72
|
}
|
|
62
73
|
}
|
|
@@ -95,11 +106,20 @@ export default class ARC implements Broadcaster {
|
|
|
95
106
|
message: `${txStatus} ${extraInfo}`
|
|
96
107
|
}
|
|
97
108
|
} else {
|
|
98
|
-
|
|
109
|
+
const r: BroadcastFailure = {
|
|
99
110
|
status: 'error',
|
|
100
111
|
code: response.status.toString() ?? 'ERR_UNKNOWN',
|
|
101
|
-
description:
|
|
112
|
+
description: 'Unknown error'
|
|
113
|
+
}
|
|
114
|
+
if (typeof response.data === 'string') {
|
|
115
|
+
try {
|
|
116
|
+
const data = JSON.parse(response.data)
|
|
117
|
+
if (typeof data.detail === 'string') {
|
|
118
|
+
r.description = data.detail
|
|
119
|
+
}
|
|
120
|
+
} catch {}
|
|
102
121
|
}
|
|
122
|
+
return r
|
|
103
123
|
}
|
|
104
124
|
} catch (error) {
|
|
105
125
|
return {
|
|
@@ -122,6 +142,14 @@ export default class ARC implements Broadcaster {
|
|
|
122
142
|
headers['Authorization'] = `Bearer ${this.apiKey}`
|
|
123
143
|
}
|
|
124
144
|
|
|
145
|
+
if (this.callbackUrl) {
|
|
146
|
+
headers['X-CallbackUrl'] = this.callbackUrl
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (this.callbackToken) {
|
|
150
|
+
headers['X-CallbackToken'] = this.callbackToken
|
|
151
|
+
}
|
|
152
|
+
|
|
125
153
|
if (!!this.headers) {
|
|
126
154
|
for (const key in this.headers) {
|
|
127
155
|
headers[key] = this.headers[key]
|
|
@@ -165,9 +165,9 @@ describe('ARC Broadcaster', () => {
|
|
|
165
165
|
it('should handle non-200 responses', async () => {
|
|
166
166
|
const mockFetch = mockedFetch({
|
|
167
167
|
status: '400',
|
|
168
|
-
data: {
|
|
168
|
+
data: JSON.stringify({
|
|
169
169
|
detail: 'Bad request'
|
|
170
|
-
}
|
|
170
|
+
})
|
|
171
171
|
})
|
|
172
172
|
|
|
173
173
|
const broadcaster = new ARC(URL, {httpClient: new FetchHttpClient(mockFetch)})
|
package/src/transaction/index.ts
CHANGED
|
@@ -2,5 +2,6 @@ export { default as Transaction } from './Transaction.js'
|
|
|
2
2
|
export { default as MerklePath } from './MerklePath.js'
|
|
3
3
|
export type { default as TransactionInput } from './TransactionInput.js'
|
|
4
4
|
export type { default as TransactionOutput } from './TransactionOutput.js'
|
|
5
|
-
export type
|
|
5
|
+
export type { Broadcaster, BroadcastFailure, BroadcastResponse } from './Broadcaster.js'
|
|
6
|
+
export { isBroadcastResponse, isBroadcastFailure } from './Broadcaster.js'
|
|
6
7
|
export type { default as ChainTracker } from './ChainTracker.js'
|