cashscript 0.8.0-next.2 → 0.8.0-next.3
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/SignatureTemplate.js +1 -1
- package/dist/Transaction.d.ts +2 -2
- package/dist/Transaction.js +103 -54
- package/dist/interfaces.d.ts +6 -0
- package/dist/interfaces.js +1 -0
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +18 -9
- package/package.json +4 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { decodePrivateKeyWif, secp256k1, SigningSerializationFlag } from '@bitauth/libauth';
|
|
2
2
|
import { HashType, SignatureAlgorithm } from './interfaces.js';
|
|
3
3
|
export default class SignatureTemplate {
|
|
4
|
-
constructor(signer, hashtype = HashType.SIGHASH_ALL, signatureAlgorithm = SignatureAlgorithm.SCHNORR) {
|
|
4
|
+
constructor(signer, hashtype = HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS, signatureAlgorithm = SignatureAlgorithm.SCHNORR) {
|
|
5
5
|
this.hashtype = hashtype;
|
|
6
6
|
this.signatureAlgorithm = signatureAlgorithm;
|
|
7
7
|
if (isKeypair(signer)) {
|
package/dist/Transaction.d.ts
CHANGED
|
@@ -20,8 +20,8 @@ export declare class Transaction {
|
|
|
20
20
|
constructor(address: string, provider: NetworkProvider, redeemScript: Script, abiFunction: AbiFunction, args: (Uint8Array | SignatureTemplate)[], selector?: number | undefined);
|
|
21
21
|
from(input: Utxo): this;
|
|
22
22
|
from(inputs: Utxo[]): this;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
fromP2PKH(input: Utxo, template: SignatureTemplate): this;
|
|
24
|
+
fromP2PKH(inputs: Utxo[], template: SignatureTemplate): this;
|
|
25
25
|
to(to: string, amount: bigint, token?: TokenDetails): this;
|
|
26
26
|
to(outputs: Recipient[]): this;
|
|
27
27
|
withOpReturn(chunks: string[]): this;
|
package/dist/Transaction.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { hexToBin, binToHex, encodeTransaction,
|
|
1
|
+
import { hexToBin, binToHex, encodeTransaction, decodeTransaction, } from '@bitauth/libauth';
|
|
2
2
|
import delay from 'delay';
|
|
3
|
-
import {
|
|
3
|
+
import { hash256, placeholder, scriptToBytecode, } from '@cashscript/utils';
|
|
4
4
|
import { isSignableUtxo, } from './interfaces.js';
|
|
5
|
-
import { meep, createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, buildError, createSighashPreimage, validateRecipient, utxoComparator, cashScriptOutputToLibauthOutput, calculateDust, getOutputSize, } from './utils.js';
|
|
5
|
+
import { meep, createInputScript, getInputSize, createOpReturnOutput, getTxSizeWithoutInputs, getPreimageSize, buildError, createSighashPreimage, validateRecipient, utxoComparator, cashScriptOutputToLibauthOutput, calculateDust, getOutputSize, addressToLockScript, publicKeyToP2PKHLockingBytecode, utxoTokenComparator, } from './utils.js';
|
|
6
6
|
import SignatureTemplate from './SignatureTemplate.js';
|
|
7
7
|
const bip68 = await import('bip68');
|
|
8
8
|
export class Transaction {
|
|
@@ -27,7 +27,7 @@ export class Transaction {
|
|
|
27
27
|
this.inputs = this.inputs.concat(inputOrInputs);
|
|
28
28
|
return this;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
fromP2PKH(inputOrInputs, template) {
|
|
31
31
|
if (!Array.isArray(inputOrInputs)) {
|
|
32
32
|
inputOrInputs = [inputOrInputs];
|
|
33
33
|
}
|
|
@@ -82,12 +82,22 @@ export class Transaction {
|
|
|
82
82
|
this.locktime = this.locktime ?? await this.provider.getBlockHeight();
|
|
83
83
|
await this.setInputsAndOutputs();
|
|
84
84
|
const bytecode = scriptToBytecode(this.redeemScript);
|
|
85
|
+
const lockingBytecode = addressToLockScript(this.address);
|
|
85
86
|
const inputs = this.inputs.map((utxo) => ({
|
|
86
87
|
outpointIndex: utxo.vout,
|
|
87
88
|
outpointTransactionHash: hexToBin(utxo.txid),
|
|
88
89
|
sequenceNumber: this.sequence,
|
|
89
90
|
unlockingBytecode: new Uint8Array(),
|
|
90
91
|
}));
|
|
92
|
+
// Generate source outputs from inputs (for signing with SIGHASH_UTXOS)
|
|
93
|
+
const sourceOutputs = this.inputs.map((input) => {
|
|
94
|
+
const sourceOutput = {
|
|
95
|
+
amount: input.satoshis,
|
|
96
|
+
to: isSignableUtxo(input) ? publicKeyToP2PKHLockingBytecode(input.template.getPublicKey()) : lockingBytecode,
|
|
97
|
+
token: input.token,
|
|
98
|
+
};
|
|
99
|
+
return cashScriptOutputToLibauthOutput(sourceOutput);
|
|
100
|
+
});
|
|
91
101
|
const outputs = this.outputs.map(cashScriptOutputToLibauthOutput);
|
|
92
102
|
const transaction = {
|
|
93
103
|
inputs,
|
|
@@ -100,11 +110,9 @@ export class Transaction {
|
|
|
100
110
|
// UTXO's with signature templates are signed using P2PKH
|
|
101
111
|
if (isSignableUtxo(utxo)) {
|
|
102
112
|
const pubkey = utxo.template.getPublicKey();
|
|
103
|
-
const
|
|
104
|
-
const addressContents = { payload: pubkeyHash, type: LockingBytecodeType.p2pkh };
|
|
105
|
-
const prevOutScript = addressContentsToLockingBytecode(addressContents);
|
|
113
|
+
const prevOutScript = publicKeyToP2PKHLockingBytecode(pubkey);
|
|
106
114
|
const hashtype = utxo.template.getHashType();
|
|
107
|
-
const preimage = createSighashPreimage(transaction,
|
|
115
|
+
const preimage = createSighashPreimage(transaction, sourceOutputs, i, prevOutScript, hashtype);
|
|
108
116
|
const sighash = hash256(preimage);
|
|
109
117
|
const signature = utxo.template.generateSignature(sighash);
|
|
110
118
|
const inputScript = scriptToBytecode([signature, pubkey]);
|
|
@@ -118,12 +126,12 @@ export class Transaction {
|
|
|
118
126
|
// First signature is used for sighash preimage (maybe not the best way)
|
|
119
127
|
if (covenantHashType < 0)
|
|
120
128
|
covenantHashType = arg.getHashType();
|
|
121
|
-
const preimage = createSighashPreimage(transaction,
|
|
129
|
+
const preimage = createSighashPreimage(transaction, sourceOutputs, i, bytecode, arg.getHashType());
|
|
122
130
|
const sighash = hash256(preimage);
|
|
123
131
|
return arg.generateSignature(sighash);
|
|
124
132
|
});
|
|
125
133
|
const preimage = this.abiFunction.covenant
|
|
126
|
-
? createSighashPreimage(transaction,
|
|
134
|
+
? createSighashPreimage(transaction, sourceOutputs, i, bytecode, covenantHashType)
|
|
127
135
|
: undefined;
|
|
128
136
|
const inputScript = createInputScript(this.redeemScript, completeArgs, this.selector, preimage);
|
|
129
137
|
inputScripts.push(inputScript);
|
|
@@ -169,67 +177,48 @@ export class Transaction {
|
|
|
169
177
|
if (this.outputs.length === 0) {
|
|
170
178
|
throw Error('Attempted to build a transaction without outputs');
|
|
171
179
|
}
|
|
172
|
-
|
|
173
|
-
const
|
|
180
|
+
const allUtxos = await this.provider.getUtxos(this.address);
|
|
181
|
+
const manualTokenInputs = this.inputs.filter((input) => input.token);
|
|
182
|
+
// This will throw if the amount is not enough
|
|
183
|
+
if (manualTokenInputs.length > 0) {
|
|
184
|
+
selectAllTokenUtxos(manualTokenInputs, this.outputs);
|
|
185
|
+
}
|
|
186
|
+
const automaticTokenInputs = selectAllTokenUtxos(allUtxos, this.outputs);
|
|
187
|
+
const tokenInputs = manualTokenInputs.length > 0 ? manualTokenInputs : automaticTokenInputs;
|
|
188
|
+
if (this.tokenChange) {
|
|
189
|
+
const tokenChangeOutputs = createTokenChangeOutputs(tokenInputs, this.outputs, this.address);
|
|
190
|
+
this.outputs.push(...tokenChangeOutputs);
|
|
191
|
+
}
|
|
174
192
|
// Construct list with all nfts in inputs
|
|
175
193
|
const listNftsInputs = [];
|
|
176
194
|
// If inputs are manually selected, add their tokens to balance
|
|
177
|
-
|
|
195
|
+
this.inputs.forEach((input) => {
|
|
178
196
|
if (!input.token)
|
|
179
|
-
|
|
180
|
-
const tokenCategory = input.token.category;
|
|
181
|
-
if (!netBalanceTokens[tokenCategory]) {
|
|
182
|
-
netBalanceTokens[tokenCategory] = input.token.amount;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
netBalanceTokens[tokenCategory] += input.token.amount;
|
|
186
|
-
}
|
|
197
|
+
return;
|
|
187
198
|
if (input.token.nft) {
|
|
188
199
|
listNftsInputs.push({ ...input.token.nft, category: input.token.category });
|
|
189
200
|
}
|
|
190
|
-
}
|
|
201
|
+
});
|
|
191
202
|
// Construct list with all nfts in outputs
|
|
192
203
|
let listNftsOutputs = [];
|
|
193
204
|
// Subtract all token outputs from the token balances
|
|
194
|
-
|
|
205
|
+
this.outputs.forEach((output) => {
|
|
195
206
|
if (!output.token)
|
|
196
|
-
|
|
197
|
-
const tokenCategory = output.token.category;
|
|
198
|
-
if (!netBalanceTokens[tokenCategory]) {
|
|
199
|
-
netBalanceTokens[tokenCategory] = -output.token.amount;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
netBalanceTokens[tokenCategory] -= output.token.amount;
|
|
203
|
-
}
|
|
207
|
+
return;
|
|
204
208
|
if (output.token.nft) {
|
|
205
209
|
listNftsOutputs.push({ ...output.token.nft, category: output.token.category });
|
|
206
210
|
}
|
|
207
|
-
}
|
|
211
|
+
});
|
|
208
212
|
// If inputs are manually provided, check token balances
|
|
209
213
|
if (this.inputs.length > 0) {
|
|
210
|
-
for (const [category, balance] of Object.entries(netBalanceTokens)) {
|
|
211
|
-
// Add token change outputs if applicable
|
|
212
|
-
if (this.tokenChange && balance > 0) {
|
|
213
|
-
const tokenDetails = {
|
|
214
|
-
category,
|
|
215
|
-
amount: balance,
|
|
216
|
-
};
|
|
217
|
-
const tokenChangeOutput = { to: this.address, amount: BigInt(1000), token: tokenDetails };
|
|
218
|
-
this.outputs.push(tokenChangeOutput);
|
|
219
|
-
}
|
|
220
|
-
// Throw error when token balance is insufficient
|
|
221
|
-
if (balance < 0) {
|
|
222
|
-
throw new Error(`Insufficient token balance for token with category ${category}.`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
214
|
// Compare nfts in- and outputs, check if inputs have nfts corresponding to outputs
|
|
226
215
|
// Keep list of nfts in inputs without matching output
|
|
227
216
|
// First check immutable nfts, then mutable & minting nfts together
|
|
228
|
-
//
|
|
217
|
+
// This is so an immutible input gets matched first and is removed from the list of unused nfts
|
|
229
218
|
let unusedNfts = listNftsInputs;
|
|
230
219
|
for (const nftInput of listNftsInputs) {
|
|
231
220
|
if (nftInput.capability === 'none') {
|
|
232
|
-
for (let i = 0; i < listNftsOutputs.length; i
|
|
221
|
+
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
233
222
|
// Deep equality check token objects
|
|
234
223
|
if (JSON.stringify(listNftsOutputs[i]) === JSON.stringify(nftInput)) {
|
|
235
224
|
listNftsOutputs.splice(i, 1);
|
|
@@ -241,6 +230,7 @@ export class Transaction {
|
|
|
241
230
|
}
|
|
242
231
|
for (const nftInput of listNftsInputs) {
|
|
243
232
|
if (nftInput.capability === 'minting') {
|
|
233
|
+
// eslint-disable-next-line max-len
|
|
244
234
|
const newListNftsOutputs = listNftsOutputs.filter((nftOutput) => nftOutput.category !== nftInput.category);
|
|
245
235
|
if (newListNftsOutputs !== listNftsOutputs) {
|
|
246
236
|
unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
|
|
@@ -248,7 +238,7 @@ export class Transaction {
|
|
|
248
238
|
}
|
|
249
239
|
}
|
|
250
240
|
if (nftInput.capability === 'mutable') {
|
|
251
|
-
for (let i = 0; i < listNftsOutputs.length; i
|
|
241
|
+
for (let i = 0; i < listNftsOutputs.length; i += 1) {
|
|
252
242
|
if (listNftsOutputs[i].category === nftInput.category) {
|
|
253
243
|
listNftsOutputs.splice(i, 1);
|
|
254
244
|
unusedNfts = unusedNfts.filter((nft) => nft !== nftInput);
|
|
@@ -257,8 +247,14 @@ export class Transaction {
|
|
|
257
247
|
}
|
|
258
248
|
}
|
|
259
249
|
}
|
|
250
|
+
for (const nftOutput of listNftsOutputs) {
|
|
251
|
+
const genesisUtxo = getTokenGenesisUtxo(this.inputs, nftOutput.category);
|
|
252
|
+
if (genesisUtxo) {
|
|
253
|
+
listNftsOutputs = listNftsOutputs.filter((nft) => nft !== nftOutput);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
260
256
|
if (listNftsOutputs.length !== 0) {
|
|
261
|
-
throw new Error(
|
|
257
|
+
throw new Error(`NFT output with token category ${listNftsOutputs[0].category} does not have corresponding input`);
|
|
262
258
|
}
|
|
263
259
|
if (this.tokenChange) {
|
|
264
260
|
for (const unusedNft of unusedNfts) {
|
|
@@ -300,17 +296,24 @@ export class Transaction {
|
|
|
300
296
|
}
|
|
301
297
|
else {
|
|
302
298
|
// If inputs are not defined yet, we retrieve the contract's UTXOs and perform selection
|
|
303
|
-
const
|
|
299
|
+
const bchUtxos = allUtxos.filter((utxo) => !utxo.token);
|
|
304
300
|
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
305
301
|
// even if they report UTXOs in a different order
|
|
306
|
-
|
|
307
|
-
|
|
302
|
+
bchUtxos.sort(utxoComparator).reverse();
|
|
303
|
+
// Add all automatically added token inputs to the transaction
|
|
304
|
+
for (const utxo of automaticTokenInputs) {
|
|
308
305
|
this.inputs.push(utxo);
|
|
309
306
|
satsAvailable += addPrecision(utxo.satoshis);
|
|
310
307
|
if (!this.hardcodedFee)
|
|
311
308
|
fee += addPrecision(inputSize * this.feePerByte);
|
|
309
|
+
}
|
|
310
|
+
for (const utxo of bchUtxos) {
|
|
312
311
|
if (satsAvailable > amount + fee)
|
|
313
312
|
break;
|
|
313
|
+
this.inputs.push(utxo);
|
|
314
|
+
satsAvailable += addPrecision(utxo.satoshis);
|
|
315
|
+
if (!this.hardcodedFee)
|
|
316
|
+
fee += addPrecision(inputSize * this.feePerByte);
|
|
314
317
|
}
|
|
315
318
|
}
|
|
316
319
|
// Remove "decimal points" from BigInt numbers (rounding up for fee, down for others)
|
|
@@ -334,6 +337,52 @@ export class Transaction {
|
|
|
334
337
|
}
|
|
335
338
|
}
|
|
336
339
|
}
|
|
340
|
+
const getTokenGenesisUtxo = (utxos, tokenCategory) => {
|
|
341
|
+
const creationUtxo = utxos.find((utxo) => utxo.vout === 0 && utxo.txid === tokenCategory);
|
|
342
|
+
return creationUtxo;
|
|
343
|
+
};
|
|
344
|
+
const getTokenCategories = (outputs) => (outputs
|
|
345
|
+
.filter((output) => output.token)
|
|
346
|
+
.map((output) => output.token.category));
|
|
347
|
+
const calculateTotalTokenAmount = (outputs, tokenCategory) => (outputs
|
|
348
|
+
.filter((output) => output.token?.category === tokenCategory)
|
|
349
|
+
.reduce((acc, output) => acc + output.token.amount, 0n));
|
|
350
|
+
const selectTokenUtxos = (utxos, amountNeeded, tokenCategory) => {
|
|
351
|
+
const genesisUtxo = getTokenGenesisUtxo(utxos, tokenCategory);
|
|
352
|
+
if (genesisUtxo) {
|
|
353
|
+
return [genesisUtxo];
|
|
354
|
+
}
|
|
355
|
+
const tokenUtxos = utxos.filter((utxo) => utxo.token?.category === tokenCategory);
|
|
356
|
+
// We sort the UTXOs mainly so there is consistent behaviour between network providers
|
|
357
|
+
// even if they report UTXOs in a different order
|
|
358
|
+
tokenUtxos.sort(utxoTokenComparator).reverse();
|
|
359
|
+
let amountAvailable = 0n;
|
|
360
|
+
const selectedUtxos = [];
|
|
361
|
+
// Add token UTXOs until we have enough to cover the amount needed (no fee calculation because it's a token)
|
|
362
|
+
for (const utxo of tokenUtxos) {
|
|
363
|
+
selectedUtxos.push(utxo);
|
|
364
|
+
amountAvailable += utxo.token.amount;
|
|
365
|
+
if (amountAvailable >= amountNeeded)
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
if (amountAvailable < amountNeeded) {
|
|
369
|
+
throw new Error(`Insufficient funds for token ${tokenCategory}: available (${amountAvailable}) < needed (${amountNeeded}).`);
|
|
370
|
+
}
|
|
371
|
+
return selectedUtxos;
|
|
372
|
+
};
|
|
373
|
+
const selectAllTokenUtxos = (utxos, outputs) => {
|
|
374
|
+
const tokenCategories = getTokenCategories(outputs);
|
|
375
|
+
return tokenCategories.flatMap((tokenCategory) => selectTokenUtxos(utxos, calculateTotalTokenAmount(outputs, tokenCategory), tokenCategory));
|
|
376
|
+
};
|
|
377
|
+
const createTokenChangeOutputs = (utxos, outputs, address) => {
|
|
378
|
+
const tokenCategories = getTokenCategories(utxos);
|
|
379
|
+
return tokenCategories.map((tokenCategory) => {
|
|
380
|
+
const required = calculateTotalTokenAmount(outputs, tokenCategory);
|
|
381
|
+
const available = calculateTotalTokenAmount(utxos, tokenCategory);
|
|
382
|
+
const change = available - required;
|
|
383
|
+
return { to: address, amount: BigInt(1000), token: { category: tokenCategory, amount: change } };
|
|
384
|
+
});
|
|
385
|
+
};
|
|
337
386
|
// Note: the below is a very simple implementation of a "decimal point" system for BigInt numbers
|
|
338
387
|
// It is safe to use for UTXO fee calculations due to its low numbers, but should not be used for other purposes
|
|
339
388
|
// Also note that multiplication and division between two "decimal" bigints is not supported
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -29,6 +29,11 @@ export interface TokenDetails {
|
|
|
29
29
|
commitment: string;
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
export interface NftObject {
|
|
33
|
+
category: string;
|
|
34
|
+
capability: 'none' | 'mutable' | 'minting';
|
|
35
|
+
commitment: string;
|
|
36
|
+
}
|
|
32
37
|
export interface LibauthOutput {
|
|
33
38
|
lockingBytecode: Uint8Array;
|
|
34
39
|
valueSatoshis: bigint;
|
|
@@ -50,6 +55,7 @@ export declare enum HashType {
|
|
|
50
55
|
SIGHASH_ALL = 1,
|
|
51
56
|
SIGHASH_NONE = 2,
|
|
52
57
|
SIGHASH_SINGLE = 3,
|
|
58
|
+
SIGHASH_UTXOS = 32,
|
|
53
59
|
SIGHASH_ANYONECANPAY = 128
|
|
54
60
|
}
|
|
55
61
|
export declare const Network: {
|
package/dist/interfaces.js
CHANGED
|
@@ -11,6 +11,7 @@ export var HashType;
|
|
|
11
11
|
HashType[HashType["SIGHASH_ALL"] = 1] = "SIGHASH_ALL";
|
|
12
12
|
HashType[HashType["SIGHASH_NONE"] = 2] = "SIGHASH_NONE";
|
|
13
13
|
HashType[HashType["SIGHASH_SINGLE"] = 3] = "SIGHASH_SINGLE";
|
|
14
|
+
HashType[HashType["SIGHASH_UTXOS"] = 32] = "SIGHASH_UTXOS";
|
|
14
15
|
HashType[HashType["SIGHASH_ANYONECANPAY"] = 128] = "SIGHASH_ANYONECANPAY";
|
|
15
16
|
})(HashType || (HashType = {}));
|
|
16
17
|
// Weird setup to allow both Enum parameters, as well as literal strings
|
package/dist/utils.d.ts
CHANGED
|
@@ -13,12 +13,14 @@ export declare function getPreimageSize(script: Uint8Array): number;
|
|
|
13
13
|
export declare function getTxSizeWithoutInputs(outputs: Output[]): number;
|
|
14
14
|
export declare function createInputScript(redeemScript: Script, encodedArgs: Uint8Array[], selector?: number, preimage?: Uint8Array): Uint8Array;
|
|
15
15
|
export declare function createOpReturnOutput(opReturnData: string[]): Output;
|
|
16
|
-
export declare function createSighashPreimage(transaction: Transaction,
|
|
16
|
+
export declare function createSighashPreimage(transaction: Transaction, sourceOutputs: LibauthOutput[], inputIndex: number, coveredBytecode: Uint8Array, hashtype: number): Uint8Array;
|
|
17
17
|
export declare function buildError(reason: string, meepStr: string): FailedTransactionError;
|
|
18
18
|
export declare function meep(tx: any, utxos: Utxo[], script: Script): string;
|
|
19
19
|
export declare function scriptToAddress(script: Script, network: string, addressType: 'p2sh20' | 'p2sh32', tokenSupport: boolean): string;
|
|
20
20
|
export declare function scriptToLockingBytecode(script: Script, addressType: 'p2sh20' | 'p2sh32'): Uint8Array;
|
|
21
|
+
export declare function publicKeyToP2PKHLockingBytecode(publicKey: Uint8Array): Uint8Array;
|
|
21
22
|
export declare function utxoComparator(a: Utxo, b: Utxo): number;
|
|
23
|
+
export declare function utxoTokenComparator(a: Utxo, b: Utxo): number;
|
|
22
24
|
/**
|
|
23
25
|
* Helper function to convert an address to a locking script
|
|
24
26
|
*
|
package/dist/utils.js
CHANGED
|
@@ -119,15 +119,7 @@ function toBin(output) {
|
|
|
119
119
|
const encode = data === output ? utf8ToBin : hexToBin;
|
|
120
120
|
return encode(data);
|
|
121
121
|
}
|
|
122
|
-
export function createSighashPreimage(transaction,
|
|
123
|
-
const sourceOutputs = inputs.map((input) => {
|
|
124
|
-
const sourceOutput = {
|
|
125
|
-
amount: input.satoshis,
|
|
126
|
-
to: Uint8Array.of(),
|
|
127
|
-
token: input.token,
|
|
128
|
-
};
|
|
129
|
-
return cashScriptOutputToLibauthOutput(sourceOutput);
|
|
130
|
-
});
|
|
122
|
+
export function createSighashPreimage(transaction, sourceOutputs, inputIndex, coveredBytecode, hashtype) {
|
|
131
123
|
const context = { inputIndex, sourceOutputs, transaction };
|
|
132
124
|
const signingSerializationType = new Uint8Array([hashtype]);
|
|
133
125
|
const sighashPreimage = generateSigningSerializationBCH(context, { coveredBytecode, signingSerializationType });
|
|
@@ -175,6 +167,12 @@ export function scriptToLockingBytecode(script, addressType) {
|
|
|
175
167
|
const lockingBytecode = addressContentsToLockingBytecode(addressContents);
|
|
176
168
|
return lockingBytecode;
|
|
177
169
|
}
|
|
170
|
+
export function publicKeyToP2PKHLockingBytecode(publicKey) {
|
|
171
|
+
const pubkeyHash = hash160(publicKey);
|
|
172
|
+
const addressContents = { payload: pubkeyHash, type: LockingBytecodeType.p2pkh };
|
|
173
|
+
const lockingBytecode = addressContentsToLockingBytecode(addressContents);
|
|
174
|
+
return lockingBytecode;
|
|
175
|
+
}
|
|
178
176
|
export function utxoComparator(a, b) {
|
|
179
177
|
if (a.satoshis > b.satoshis)
|
|
180
178
|
return 1;
|
|
@@ -182,6 +180,17 @@ export function utxoComparator(a, b) {
|
|
|
182
180
|
return -1;
|
|
183
181
|
return 0;
|
|
184
182
|
}
|
|
183
|
+
export function utxoTokenComparator(a, b) {
|
|
184
|
+
if (!a.token || !b.token)
|
|
185
|
+
throw new Error('UTXO does not have token data');
|
|
186
|
+
if (!a.token.category !== !b.token.category)
|
|
187
|
+
throw new Error('UTXO token categories do not match');
|
|
188
|
+
if (a.token.amount > b.token.amount)
|
|
189
|
+
return 1;
|
|
190
|
+
if (a.token.amount < b.token.amount)
|
|
191
|
+
return -1;
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
185
194
|
/**
|
|
186
195
|
* Helper function to convert an address to a locking script
|
|
187
196
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.8.0-next.
|
|
3
|
+
"version": "0.8.0-next.3",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -38,12 +38,13 @@
|
|
|
38
38
|
"compile:test": "tsc -p tsconfig.test.json",
|
|
39
39
|
"lint": "eslint . --ext .ts --ignore-path ../../.eslintignore",
|
|
40
40
|
"prepare": "yarn build",
|
|
41
|
+
"prepublishOnly": "yarn test && yarn lint",
|
|
41
42
|
"pretest": "yarn build:test",
|
|
42
43
|
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@bitauth/libauth": "^2.0.0-alpha.8",
|
|
46
|
-
"@cashscript/utils": "^0.8.0-next.
|
|
47
|
+
"@cashscript/utils": "^0.8.0-next.3",
|
|
47
48
|
"bip68": "^1.0.4",
|
|
48
49
|
"bitcoin-rpc-promise-retry": "^1.3.0",
|
|
49
50
|
"delay": "^5.0.0",
|
|
@@ -57,5 +58,5 @@
|
|
|
57
58
|
"jest": "^29.4.1",
|
|
58
59
|
"typescript": "^4.1.5"
|
|
59
60
|
},
|
|
60
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "398ff2afab6d731f077002e6be021ed2f1996b4a"
|
|
61
62
|
}
|