@bsv/sdk 2.0.8 → 2.0.9
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/primitives/index.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +45 -0
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/primitives/index.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +45 -0
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/primitives/index.d.ts +1 -1
- package/dist/types/src/primitives/index.d.ts.map +1 -1
- package/dist/types/src/transaction/Transaction.d.ts +9 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/transaction.md +24 -1
- package/package.json +1 -1
- package/src/primitives/index.ts +1 -1
- package/src/transaction/Transaction.ts +46 -0
- package/src/transaction/__tests/Transaction.test.ts +170 -0
|
@@ -1673,10 +1673,11 @@ export default class Transaction {
|
|
|
1673
1673
|
toAtomicBEEF(allowPartial?: boolean): number[]
|
|
1674
1674
|
toAtomicBEEFUint8Array(allowPartial?: boolean): Uint8Array
|
|
1675
1675
|
async completeWithWallet(wallet: WalletInterface, actionDescription?: DescriptionString5to50Bytes, originator?: string, options?: CreateActionOptions): Promise<void>
|
|
1676
|
+
preimage(inputIndex?: number, signatureScope?: number, subscript?: LockingScript): number[]
|
|
1676
1677
|
}
|
|
1677
1678
|
```
|
|
1678
1679
|
|
|
1679
|
-
See also: [BroadcastFailure](./transaction.md#interface-broadcastfailure), [BroadcastResponse](./transaction.md#interface-broadcastresponse), [Broadcaster](./transaction.md#interface-broadcaster), [ChainTracker](./transaction.md#interface-chaintracker), [CreateActionOptions](./wallet.md#interface-createactionoptions), [DescriptionString5to50Bytes](./wallet.md#type-descriptionstring5to50bytes), [FeeModel](./transaction.md#interface-feemodel), [LivePolicy](./transaction.md#class-livepolicy), [MerklePath](./transaction.md#class-merklepath), [Reader](./primitives.md#class-reader), [ReaderUint8Array](./primitives.md#class-readeruint8array), [TransactionInput](./transaction.md#interface-transactioninput), [TransactionOutput](./transaction.md#interface-transactionoutput), [WalletInterface](./wallet.md#interface-walletinterface), [Writer](./primitives.md#class-writer), [WriterUint8Array](./primitives.md#class-writeruint8array), [defaultBroadcaster](./transaction.md#function-defaultbroadcaster), [defaultChainTracker](./transaction.md#function-defaultchaintracker), [sign](./compat.md#variable-sign), [toHex](./primitives.md#variable-tohex), [toUint8Array](./primitives.md#variable-touint8array), [verify](./compat.md#variable-verify)
|
|
1680
|
+
See also: [BroadcastFailure](./transaction.md#interface-broadcastfailure), [BroadcastResponse](./transaction.md#interface-broadcastresponse), [Broadcaster](./transaction.md#interface-broadcaster), [ChainTracker](./transaction.md#interface-chaintracker), [CreateActionOptions](./wallet.md#interface-createactionoptions), [DescriptionString5to50Bytes](./wallet.md#type-descriptionstring5to50bytes), [FeeModel](./transaction.md#interface-feemodel), [LivePolicy](./transaction.md#class-livepolicy), [LockingScript](./script.md#class-lockingscript), [MerklePath](./transaction.md#class-merklepath), [Reader](./primitives.md#class-reader), [ReaderUint8Array](./primitives.md#class-readeruint8array), [TransactionInput](./transaction.md#interface-transactioninput), [TransactionOutput](./transaction.md#interface-transactionoutput), [WalletInterface](./wallet.md#interface-walletinterface), [Writer](./primitives.md#class-writer), [WriterUint8Array](./primitives.md#class-writeruint8array), [defaultBroadcaster](./transaction.md#function-defaultbroadcaster), [defaultChainTracker](./transaction.md#function-defaultchaintracker), [sign](./compat.md#variable-sign), [toHex](./primitives.md#variable-tohex), [toUint8Array](./primitives.md#variable-touint8array), [verify](./compat.md#variable-verify)
|
|
1680
1681
|
|
|
1681
1682
|
#### Method addInput
|
|
1682
1683
|
|
|
@@ -2033,6 +2034,28 @@ Argument Details
|
|
|
2033
2034
|
+ **bin**
|
|
2034
2035
|
+ binary transaction data
|
|
2035
2036
|
|
|
2037
|
+
#### Method preimage
|
|
2038
|
+
|
|
2039
|
+
Returns the formatted preimage of a transaction for the requested input index, signature scope (default SIGHASH_FORKID | SIGHASH_ALL), and optional subscript.
|
|
2040
|
+
|
|
2041
|
+
```ts
|
|
2042
|
+
preimage(inputIndex?: number, signatureScope?: number, subscript?: LockingScript): number[]
|
|
2043
|
+
```
|
|
2044
|
+
See also: [LockingScript](./script.md#class-lockingscript)
|
|
2045
|
+
|
|
2046
|
+
Returns
|
|
2047
|
+
|
|
2048
|
+
The formatted preimage
|
|
2049
|
+
|
|
2050
|
+
Argument Details
|
|
2051
|
+
|
|
2052
|
+
+ **inputIndex**
|
|
2053
|
+
+ The index of the input to generate the preimage for
|
|
2054
|
+
+ **signatureScope**
|
|
2055
|
+
+ The signature scope to use for the preimage
|
|
2056
|
+
+ **subscript**
|
|
2057
|
+
+ The subscript to use for the preimage (optional)
|
|
2058
|
+
|
|
2036
2059
|
#### Method sign
|
|
2037
2060
|
|
|
2038
2061
|
Signs a transaction, hydrating all its unlocking scripts based on the provided script templates where they are available.
|
package/package.json
CHANGED
package/src/primitives/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * as ECDSA from './ECDSA.js'
|
|
|
9
9
|
export * as Utils from './utils.js'
|
|
10
10
|
export * as Hash from './Hash.js'
|
|
11
11
|
export { default as Random } from './Random.js'
|
|
12
|
-
export { default as TransactionSignature } from './TransactionSignature.js'
|
|
12
|
+
export { default as TransactionSignature, type SignatureHashCache } from './TransactionSignature.js'
|
|
13
13
|
export { default as Polynomial, PointInFiniteField } from './Polynomial.js'
|
|
14
14
|
export { default as Schnorr } from './Schnorr.js'
|
|
15
15
|
export { default as Secp256r1 } from './Secp256r1.js'
|
|
@@ -16,6 +16,7 @@ import { defaultChainTracker } from './chaintrackers/DefaultChainTracker.js'
|
|
|
16
16
|
import { Beef, BEEF_V1 } from './Beef.js'
|
|
17
17
|
import P2PKH from '../script/templates/P2PKH.js'
|
|
18
18
|
import type { WalletInterface, DescriptionString5to50Bytes, CreateActionOptions } from '../wallet/Wallet.interfaces.js'
|
|
19
|
+
import TransactionSignature from '../primitives/TransactionSignature.js'
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Represents a complete Bitcoin transaction. This class encapsulates all the details
|
|
@@ -1292,4 +1293,49 @@ export default class Transaction {
|
|
|
1292
1293
|
...newTransaction.metadata
|
|
1293
1294
|
}
|
|
1294
1295
|
}
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* Returns the formatted preimage of a transaction for the requested input index, signature scope (default SIGHASH_FORKID | SIGHASH_ALL), and optional subscript.
|
|
1299
|
+
* @param inputIndex - The index of the input to generate the preimage for
|
|
1300
|
+
* @param signatureScope - The signature scope to use for the preimage
|
|
1301
|
+
* @param subscript - The subscript to use for the preimage (optional)
|
|
1302
|
+
* @returns The formatted preimage
|
|
1303
|
+
*/
|
|
1304
|
+
preimage (inputIndex?: number, signatureScope?: number, subscript?: LockingScript): number[] {
|
|
1305
|
+
inputIndex ??= 0
|
|
1306
|
+
signatureScope ??= TransactionSignature.SIGHASH_FORKID | TransactionSignature.SIGHASH_ALL
|
|
1307
|
+
if (inputIndex < 0 || inputIndex >= this.inputs.length) {
|
|
1308
|
+
throw new Error('Invalid input index')
|
|
1309
|
+
}
|
|
1310
|
+
const flags = signatureScope & 0xf0
|
|
1311
|
+
if (flags !== 224 && flags !== 192 && flags !== 64) {
|
|
1312
|
+
throw new Error('FORKID must be set')
|
|
1313
|
+
}
|
|
1314
|
+
const coverage = signatureScope & 0x0f
|
|
1315
|
+
if (coverage < 1 || coverage > 3) {
|
|
1316
|
+
throw new Error('Invalid signature coverage, must be all, none or single')
|
|
1317
|
+
}
|
|
1318
|
+
const input = this.inputs[inputIndex]
|
|
1319
|
+
if (input.sourceTransaction == null) {
|
|
1320
|
+
throw new Error('Source transaction is required')
|
|
1321
|
+
}
|
|
1322
|
+
const output = input.sourceTransaction.outputs[input.sourceOutputIndex]
|
|
1323
|
+
if (output == null) {
|
|
1324
|
+
throw new Error(`Source transaction's output at index ${input.sourceOutputIndex} is required`)
|
|
1325
|
+
}
|
|
1326
|
+
const otherInputs = this.inputs.filter((_, index) => index !== inputIndex)
|
|
1327
|
+
return TransactionSignature.format({
|
|
1328
|
+
sourceTXID: input.sourceTXID ?? input.sourceTransaction.id('hex'),
|
|
1329
|
+
sourceOutputIndex: input.sourceOutputIndex,
|
|
1330
|
+
sourceSatoshis: output.satoshis,
|
|
1331
|
+
transactionVersion: this.version,
|
|
1332
|
+
otherInputs,
|
|
1333
|
+
inputIndex,
|
|
1334
|
+
outputs: this.outputs,
|
|
1335
|
+
inputSequence: input.sequence ?? 0xffffffff,
|
|
1336
|
+
subscript: subscript ?? output.lockingScript,
|
|
1337
|
+
lockTime: this.lockTime,
|
|
1338
|
+
scope: signatureScope
|
|
1339
|
+
})
|
|
1340
|
+
}
|
|
1295
1341
|
}
|
|
@@ -2054,4 +2054,174 @@ describe('Transaction', () => {
|
|
|
2054
2054
|
await expect(tx.verify('scripts only', new SatoshisPerKilobyte(1), 35)).resolves.toBe(true)
|
|
2055
2055
|
})
|
|
2056
2056
|
})
|
|
2057
|
+
|
|
2058
|
+
describe('#preimage', () => {
|
|
2059
|
+
let sourceTx: Transaction
|
|
2060
|
+
let spendTx: Transaction
|
|
2061
|
+
let privateKey: PrivateKey
|
|
2062
|
+
let publicKey: any
|
|
2063
|
+
let publicKeyHash: number[]
|
|
2064
|
+
let p2pkh: P2PKH
|
|
2065
|
+
|
|
2066
|
+
beforeEach(() => {
|
|
2067
|
+
privateKey = new PrivateKey(1)
|
|
2068
|
+
publicKey = new Curve().g.mul(privateKey)
|
|
2069
|
+
publicKeyHash = hash160(publicKey.encode(true))
|
|
2070
|
+
p2pkh = new P2PKH()
|
|
2071
|
+
sourceTx = new Transaction(
|
|
2072
|
+
1,
|
|
2073
|
+
[],
|
|
2074
|
+
[
|
|
2075
|
+
{
|
|
2076
|
+
lockingScript: p2pkh.lock(publicKeyHash),
|
|
2077
|
+
satoshis: 4000
|
|
2078
|
+
}
|
|
2079
|
+
],
|
|
2080
|
+
0
|
|
2081
|
+
)
|
|
2082
|
+
spendTx = new Transaction(
|
|
2083
|
+
1,
|
|
2084
|
+
[
|
|
2085
|
+
{
|
|
2086
|
+
sourceTransaction: sourceTx,
|
|
2087
|
+
sourceOutputIndex: 0,
|
|
2088
|
+
unlockingScript: new UnlockingScript(),
|
|
2089
|
+
sequence: 0xffffffff
|
|
2090
|
+
}
|
|
2091
|
+
],
|
|
2092
|
+
[
|
|
2093
|
+
{
|
|
2094
|
+
satoshis: 1000,
|
|
2095
|
+
lockingScript: p2pkh.lock(publicKeyHash)
|
|
2096
|
+
},
|
|
2097
|
+
{
|
|
2098
|
+
lockingScript: p2pkh.lock(publicKeyHash),
|
|
2099
|
+
change: true
|
|
2100
|
+
}
|
|
2101
|
+
],
|
|
2102
|
+
0
|
|
2103
|
+
)
|
|
2104
|
+
})
|
|
2105
|
+
|
|
2106
|
+
it('should generate preimage with default parameters', () => {
|
|
2107
|
+
const preimage = spendTx.preimage()
|
|
2108
|
+
expect(Array.isArray(preimage)).toBe(true)
|
|
2109
|
+
expect(preimage.length).toBeGreaterThan(0)
|
|
2110
|
+
expect(typeof preimage[0]).toBe('number')
|
|
2111
|
+
})
|
|
2112
|
+
|
|
2113
|
+
it('should generate preimage with custom inputIndex', () => {
|
|
2114
|
+
// Add another input to test inputIndex > 0
|
|
2115
|
+
const sourceTx2 = new Transaction(
|
|
2116
|
+
1,
|
|
2117
|
+
[],
|
|
2118
|
+
[
|
|
2119
|
+
{
|
|
2120
|
+
lockingScript: p2pkh.lock(publicKeyHash),
|
|
2121
|
+
satoshis: 2000
|
|
2122
|
+
}
|
|
2123
|
+
],
|
|
2124
|
+
0
|
|
2125
|
+
)
|
|
2126
|
+
spendTx.addInput({
|
|
2127
|
+
sourceTransaction: sourceTx2,
|
|
2128
|
+
sourceOutputIndex: 0,
|
|
2129
|
+
unlockingScript: new UnlockingScript(),
|
|
2130
|
+
sequence: 0xffffffff
|
|
2131
|
+
})
|
|
2132
|
+
|
|
2133
|
+
const preimage0 = spendTx.preimage(0)
|
|
2134
|
+
const preimage1 = spendTx.preimage(1)
|
|
2135
|
+
expect(Array.isArray(preimage0)).toBe(true)
|
|
2136
|
+
expect(Array.isArray(preimage1)).toBe(true)
|
|
2137
|
+
expect(preimage0).not.toEqual(preimage1)
|
|
2138
|
+
})
|
|
2139
|
+
|
|
2140
|
+
it('should generate preimage with custom signatureScope', () => {
|
|
2141
|
+
const defaultPreimage = spendTx.preimage()
|
|
2142
|
+
const customPreimage = spendTx.preimage(0, TransactionSignature.SIGHASH_FORKID | TransactionSignature.SIGHASH_NONE)
|
|
2143
|
+
expect(Array.isArray(customPreimage)).toBe(true)
|
|
2144
|
+
expect(customPreimage).not.toEqual(defaultPreimage)
|
|
2145
|
+
})
|
|
2146
|
+
|
|
2147
|
+
it('should generate preimage with custom subscript', () => {
|
|
2148
|
+
const customScript = LockingScript.fromASM(
|
|
2149
|
+
'OP_CHECKSIG'
|
|
2150
|
+
)
|
|
2151
|
+
const preimage = spendTx.preimage(0, undefined, customScript)
|
|
2152
|
+
expect(Array.isArray(preimage)).toBe(true)
|
|
2153
|
+
})
|
|
2154
|
+
|
|
2155
|
+
it('should throw error for invalid input index (negative)', () => {
|
|
2156
|
+
expect(() => {
|
|
2157
|
+
spendTx.preimage(-1)
|
|
2158
|
+
}).toThrow('Invalid input index')
|
|
2159
|
+
})
|
|
2160
|
+
|
|
2161
|
+
it('should throw error for invalid input index (out of bounds)', () => {
|
|
2162
|
+
expect(() => {
|
|
2163
|
+
spendTx.preimage(1)
|
|
2164
|
+
}).toThrow('Invalid input index')
|
|
2165
|
+
})
|
|
2166
|
+
|
|
2167
|
+
it('should throw error when sourceTransaction is missing', () => {
|
|
2168
|
+
const txWithoutSource = new Transaction(
|
|
2169
|
+
1,
|
|
2170
|
+
[
|
|
2171
|
+
{
|
|
2172
|
+
sourceTXID: '00'.repeat(32),
|
|
2173
|
+
sourceOutputIndex: 0,
|
|
2174
|
+
unlockingScript: new UnlockingScript(),
|
|
2175
|
+
sequence: 0xffffffff
|
|
2176
|
+
}
|
|
2177
|
+
],
|
|
2178
|
+
[
|
|
2179
|
+
{
|
|
2180
|
+
satoshis: 1000,
|
|
2181
|
+
lockingScript: p2pkh.lock(publicKeyHash)
|
|
2182
|
+
}
|
|
2183
|
+
],
|
|
2184
|
+
0
|
|
2185
|
+
)
|
|
2186
|
+
expect(() => {
|
|
2187
|
+
txWithoutSource.preimage()
|
|
2188
|
+
}).toThrow('Source transaction is required')
|
|
2189
|
+
})
|
|
2190
|
+
|
|
2191
|
+
it('should throw error when source transaction output is missing', () => {
|
|
2192
|
+
const tx = new Transaction(
|
|
2193
|
+
1,
|
|
2194
|
+
[
|
|
2195
|
+
{
|
|
2196
|
+
sourceTransaction: new Transaction(1, [], [], 0), // No outputs
|
|
2197
|
+
sourceOutputIndex: 0,
|
|
2198
|
+
unlockingScript: new UnlockingScript(),
|
|
2199
|
+
sequence: 0xffffffff
|
|
2200
|
+
}
|
|
2201
|
+
],
|
|
2202
|
+
[
|
|
2203
|
+
{
|
|
2204
|
+
satoshis: 1000,
|
|
2205
|
+
lockingScript: p2pkh.lock(publicKeyHash)
|
|
2206
|
+
}
|
|
2207
|
+
],
|
|
2208
|
+
0
|
|
2209
|
+
)
|
|
2210
|
+
expect(() => {
|
|
2211
|
+
tx.preimage()
|
|
2212
|
+
}).toThrow('Source transaction\'s output at index 0 is required')
|
|
2213
|
+
})
|
|
2214
|
+
|
|
2215
|
+
it('should throw error when FORKID is not set in signatureScope', () => {
|
|
2216
|
+
expect(() => {
|
|
2217
|
+
spendTx.preimage(0, TransactionSignature.SIGHASH_ALL)
|
|
2218
|
+
}).toThrow('FORKID must be set')
|
|
2219
|
+
})
|
|
2220
|
+
|
|
2221
|
+
it('should throw error for invalid signature coverage', () => {
|
|
2222
|
+
expect(() => {
|
|
2223
|
+
spendTx.preimage(0, TransactionSignature.SIGHASH_FORKID | 0x04) // Invalid coverage
|
|
2224
|
+
}).toThrow('Invalid signature coverage, must be all, none or single')
|
|
2225
|
+
})
|
|
2226
|
+
})
|
|
2057
2227
|
})
|