@btc-vision/transaction 1.5.4 → 1.6.1
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/browser/_version.d.ts +1 -1
- package/browser/epoch/ChallengeSolution.d.ts +45 -0
- package/browser/epoch/interfaces/IChallengeSolution.d.ts +50 -0
- package/browser/epoch/validator/EpochValidator.d.ts +19 -0
- package/browser/generators/Features.d.ts +6 -1
- package/browser/generators/Generator.d.ts +1 -0
- package/browser/generators/builders/CalldataGenerator.d.ts +2 -1
- package/browser/generators/builders/DeploymentGenerator.d.ts +3 -1
- package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/keypair/Address.d.ts +2 -0
- package/browser/opnet.d.ts +4 -3
- package/browser/transaction/TransactionFactory.d.ts +4 -15
- package/browser/transaction/browser/Web3Provider.d.ts +3 -3
- package/browser/transaction/builders/ChallengeSolutionTransaction.d.ts +1 -18
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +6 -4
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +4 -6
- package/browser/transaction/mineable/TimelockGenerator.d.ts +9 -0
- package/browser/utils/StringToBuffer.d.ts +1 -0
- package/browser/utxo/OPNetLimitedProvider.d.ts +0 -4
- package/browser/verification/TapscriptVerificator.d.ts +4 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/epoch/ChallengeSolution.d.ts +45 -0
- package/build/epoch/ChallengeSolution.js +105 -0
- package/build/epoch/interfaces/IChallengeSolution.d.ts +50 -0
- package/build/epoch/interfaces/IChallengeSolution.js +1 -0
- package/build/epoch/validator/EpochValidator.d.ts +19 -0
- package/build/epoch/validator/EpochValidator.js +99 -0
- package/build/generators/Features.d.ts +6 -1
- package/build/generators/Features.js +1 -0
- package/build/generators/Generator.d.ts +1 -0
- package/build/generators/Generator.js +17 -1
- package/build/generators/builders/CalldataGenerator.d.ts +2 -1
- package/build/generators/builders/CalldataGenerator.js +4 -2
- package/build/generators/builders/DeploymentGenerator.d.ts +3 -1
- package/build/generators/builders/DeploymentGenerator.js +6 -4
- package/build/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
- package/build/generators/builders/LegacyCalldataGenerator.js +2 -2
- package/build/keypair/Address.d.ts +2 -0
- package/build/keypair/Address.js +12 -0
- package/build/keypair/EcKeyPair.js +6 -3
- package/build/opnet.d.ts +4 -3
- package/build/opnet.js +4 -3
- package/build/transaction/TransactionFactory.d.ts +4 -15
- package/build/transaction/TransactionFactory.js +4 -32
- package/build/transaction/browser/Web3Provider.d.ts +3 -3
- package/build/transaction/builders/ChallengeSolutionTransaction.d.ts +0 -18
- package/build/transaction/builders/ChallengeSolutionTransaction.js +1 -51
- package/build/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/build/transaction/builders/DeploymentTransaction.d.ts +6 -4
- package/build/transaction/builders/DeploymentTransaction.js +20 -8
- package/build/transaction/builders/InteractionTransaction.js +8 -1
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +5 -4
- package/build/transaction/builders/SharedInteractionTransaction.js +7 -7
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -0
- package/build/transaction/builders/TransactionBuilder.js +31 -3
- package/build/transaction/interfaces/ITransactionParameters.d.ts +4 -6
- package/build/transaction/mineable/TimelockGenerator.d.ts +9 -0
- package/build/transaction/mineable/TimelockGenerator.js +24 -0
- package/build/utils/StringToBuffer.d.ts +1 -0
- package/build/utils/StringToBuffer.js +3 -0
- package/build/utxo/OPNetLimitedProvider.d.ts +0 -4
- package/build/utxo/OPNetLimitedProvider.js +0 -7
- package/build/verification/TapscriptVerificator.d.ts +4 -1
- package/build/verification/TapscriptVerificator.js +2 -2
- package/package.json +15 -15
- package/src/_version.ts +1 -1
- package/src/epoch/ChallengeSolution.ts +196 -0
- package/src/epoch/interfaces/IChallengeSolution.ts +56 -0
- package/src/epoch/validator/EpochValidator.ts +180 -0
- package/src/generators/Features.ts +6 -0
- package/src/generators/Generator.ts +24 -2
- package/src/generators/builders/CalldataGenerator.ts +7 -3
- package/src/generators/builders/DeploymentGenerator.ts +18 -6
- package/src/generators/builders/LegacyCalldataGenerator.ts +3 -3
- package/src/keypair/Address.ts +34 -0
- package/src/keypair/EcKeyPair.ts +9 -4
- package/src/opnet.ts +6 -3
- package/src/transaction/TransactionFactory.ts +7 -62
- package/src/transaction/browser/Web3Provider.ts +3 -3
- package/src/transaction/builders/ChallengeSolutionTransaction.ts +3 -4
- package/src/transaction/builders/CustomScriptTransaction.ts +2 -1
- package/src/transaction/builders/DeploymentTransaction.ts +29 -11
- package/src/transaction/builders/InteractionTransaction.ts +10 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +12 -28
- package/src/transaction/builders/TransactionBuilder.ts +40 -2
- package/src/transaction/interfaces/ITransactionParameters.ts +5 -8
- package/src/transaction/mineable/TimelockGenerator.ts +42 -0
- package/src/utils/StringToBuffer.ts +3 -0
- package/src/utxo/OPNetLimitedProvider.ts +0 -17
- package/src/verification/TapscriptVerificator.ts +8 -4
- package/browser/generators/builders/MineableReward.d.ts +0 -7
- package/browser/transaction/mineable/ChallengeGenerator.d.ts +0 -9
- package/build/generators/builders/MineableReward.d.ts +0 -7
- package/build/generators/builders/MineableReward.js +0 -48
- package/build/transaction/mineable/ChallengeGenerator.d.ts +0 -9
- package/build/transaction/mineable/ChallengeGenerator.js +0 -28
- package/src/generators/builders/MineableReward.ts +0 -66
- package/src/transaction/mineable/ChallengeGenerator.ts +0 -39
|
@@ -13,7 +13,7 @@ export class InteractionTransaction extends SharedInteractionTransaction {
|
|
|
13
13
|
if (this.contractSecret.length !== 32) {
|
|
14
14
|
throw new Error('Invalid contract secret length. Expected 32 bytes.');
|
|
15
15
|
}
|
|
16
|
-
this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.
|
|
16
|
+
this.compiledTargetScript = this.calldataGenerator.compile(this.calldata, this.contractSecret, this.challenge, this.priorityFee, this.generateFeatures(parameters));
|
|
17
17
|
this.scriptTree = this.getScriptTree();
|
|
18
18
|
this.internalInit();
|
|
19
19
|
}
|
|
@@ -25,6 +25,13 @@ export class InteractionTransaction extends SharedInteractionTransaction {
|
|
|
25
25
|
data: parameters.loadedStorage,
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
+
const submission = parameters.challenge.getSubmission();
|
|
29
|
+
if (submission) {
|
|
30
|
+
features.push({
|
|
31
|
+
opcode: Features.EPOCH_SUBMISSION,
|
|
32
|
+
data: submission,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
28
35
|
return features;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -4,7 +4,8 @@ import { TransactionBuilder } from './TransactionBuilder.js';
|
|
|
4
4
|
import { TransactionType } from '../enums/TransactionType.js';
|
|
5
5
|
import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.js';
|
|
6
6
|
import { SharedInteractionParameters } from '../interfaces/ITransactionParameters.js';
|
|
7
|
-
import {
|
|
7
|
+
import { ITimeLockOutput } from '../mineable/TimelockGenerator.js';
|
|
8
|
+
import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
|
|
8
9
|
export declare abstract class SharedInteractionTransaction<T extends TransactionType> extends TransactionBuilder<T> {
|
|
9
10
|
static readonly MAXIMUM_CALLDATA_SIZE: number;
|
|
10
11
|
readonly randomBytes: Buffer;
|
|
@@ -12,8 +13,8 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
|
|
|
12
13
|
protected leftOverFundsScriptRedeem: P2TRPayment | null;
|
|
13
14
|
protected abstract readonly compiledTargetScript: Buffer;
|
|
14
15
|
protected abstract readonly scriptTree: Taptree;
|
|
15
|
-
protected readonly
|
|
16
|
-
protected readonly
|
|
16
|
+
protected readonly challenge: ChallengeSolution;
|
|
17
|
+
protected readonly epochChallenge: ITimeLockOutput;
|
|
17
18
|
protected calldataGenerator: CalldataGenerator;
|
|
18
19
|
protected readonly calldata: Buffer;
|
|
19
20
|
protected abstract readonly contractSecret: Buffer;
|
|
@@ -22,7 +23,7 @@ export declare abstract class SharedInteractionTransaction<T extends Transaction
|
|
|
22
23
|
protected constructor(parameters: SharedInteractionParameters);
|
|
23
24
|
getContractSecret(): Buffer;
|
|
24
25
|
getRndBytes(): Buffer;
|
|
25
|
-
getPreimage():
|
|
26
|
+
getPreimage(): ChallengeSolution;
|
|
26
27
|
protected scriptSignerXOnlyPubKey(): Buffer;
|
|
27
28
|
protected generateKeyPairFromSeed(): ECPairInterface;
|
|
28
29
|
protected buildTransaction(): Promise<void>;
|
|
@@ -4,7 +4,7 @@ import { CalldataGenerator } from '../../generators/builders/CalldataGenerator.j
|
|
|
4
4
|
import { Compressor } from '../../bytecode/Compressor.js';
|
|
5
5
|
import { EcKeyPair } from '../../keypair/EcKeyPair.js';
|
|
6
6
|
import { BitcoinUtils } from '../../utils/BitcoinUtils.js';
|
|
7
|
-
import {
|
|
7
|
+
import { TimeLockGenerator } from '../mineable/TimelockGenerator.js';
|
|
8
8
|
export class SharedInteractionTransaction extends TransactionBuilder {
|
|
9
9
|
constructor(parameters) {
|
|
10
10
|
super(parameters);
|
|
@@ -28,12 +28,12 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
28
28
|
if (!parameters.calldata) {
|
|
29
29
|
throw new Error('Calldata is required');
|
|
30
30
|
}
|
|
31
|
-
if (!parameters.
|
|
32
|
-
throw new Error('
|
|
31
|
+
if (!parameters.challenge) {
|
|
32
|
+
throw new Error('Challenge solution is required');
|
|
33
33
|
}
|
|
34
|
-
this.
|
|
34
|
+
this.challenge = parameters.challenge;
|
|
35
35
|
this.disableAutoRefund = parameters.disableAutoRefund || false;
|
|
36
|
-
this.
|
|
36
|
+
this.epochChallenge = TimeLockGenerator.generateTimeLockAddress(this.challenge.publicKey.originalPublicKeyBuffer(), this.network);
|
|
37
37
|
this.calldata = Compressor.compress(parameters.calldata);
|
|
38
38
|
this.randomBytes = parameters.randomBytes || BitcoinUtils.rndBytes();
|
|
39
39
|
this.scriptSigner = this.generateKeyPairFromSeed();
|
|
@@ -46,7 +46,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
46
46
|
return this.randomBytes;
|
|
47
47
|
}
|
|
48
48
|
getPreimage() {
|
|
49
|
-
return this.
|
|
49
|
+
return this.challenge;
|
|
50
50
|
}
|
|
51
51
|
scriptSignerXOnlyPubKey() {
|
|
52
52
|
return toXOnly(Buffer.from(this.scriptSigner.publicKey));
|
|
@@ -196,7 +196,7 @@ export class SharedInteractionTransaction extends TransactionBuilder {
|
|
|
196
196
|
amountSpent - MINIMUM_AMOUNT_CA > MINIMUM_AMOUNT_REWARD) {
|
|
197
197
|
this.addOutput({
|
|
198
198
|
value: Number(amountSpent - amountToCA),
|
|
199
|
-
address: this.
|
|
199
|
+
address: this.epochChallenge.address,
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
const amount = this.addOptionalOutputsAndGetAmount();
|
|
@@ -33,9 +33,11 @@ export declare abstract class TransactionBuilder<T extends TransactionType> exte
|
|
|
33
33
|
protected from: string;
|
|
34
34
|
protected _maximumFeeRate: number;
|
|
35
35
|
protected isPubKeyDestination: boolean;
|
|
36
|
+
protected note?: Buffer;
|
|
36
37
|
protected constructor(parameters: ITransactionParameters);
|
|
37
38
|
static getFrom(from: string | undefined, keypair: ECPairInterface | Signer, network: Network): string;
|
|
38
39
|
static witnessStackToScriptWitness(witness: Buffer[]): Buffer;
|
|
40
|
+
addOPReturn(buffer: Buffer): void;
|
|
39
41
|
getFundingTransactionParameters(): Promise<IFundingTransactionParameters>;
|
|
40
42
|
setDestinationAddress(address: string): void;
|
|
41
43
|
setMaximumFeeRate(feeRate: number): void;
|
|
@@ -28,6 +28,14 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
28
28
|
this.utxos = parameters.utxos;
|
|
29
29
|
this.optionalInputs = parameters.optionalInputs || [];
|
|
30
30
|
this.to = parameters.to || undefined;
|
|
31
|
+
if (parameters.note) {
|
|
32
|
+
if (typeof parameters.note === 'string') {
|
|
33
|
+
this.note = Buffer.from(parameters.note, 'utf8');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.note = parameters.note;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
this.isPubKeyDestination = this.to
|
|
32
40
|
? AddressVerificator.isValidPublicKey(this.to, this.network)
|
|
33
41
|
: false;
|
|
@@ -67,6 +75,13 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
67
75
|
writeVector(witness);
|
|
68
76
|
return buffer;
|
|
69
77
|
}
|
|
78
|
+
addOPReturn(buffer) {
|
|
79
|
+
const compileScript = script.compile([opcodes.OP_RETURN, buffer]);
|
|
80
|
+
this.addOutput({
|
|
81
|
+
value: 0,
|
|
82
|
+
script: compileScript,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
70
85
|
async getFundingTransactionParameters() {
|
|
71
86
|
if (!this.estimatedFees) {
|
|
72
87
|
this.estimatedFees = await this.estimateTransactionFees();
|
|
@@ -141,9 +156,19 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
141
156
|
this.inputs.push(input);
|
|
142
157
|
}
|
|
143
158
|
addOutput(output) {
|
|
144
|
-
if (output.value === 0)
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
if (output.value === 0) {
|
|
160
|
+
const script = output;
|
|
161
|
+
if (!script.script || script.script.length === 0) {
|
|
162
|
+
throw new Error('Output value is 0 and no script provided');
|
|
163
|
+
}
|
|
164
|
+
if (script.script.length < 2) {
|
|
165
|
+
throw new Error('Output script is too short');
|
|
166
|
+
}
|
|
167
|
+
if (script.script[0] !== opcodes.OP_RETURN) {
|
|
168
|
+
throw new Error('Output script must start with OP_RETURN when value is 0');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else if (output.value < TransactionBuilder.MINIMUM_DUST) {
|
|
147
172
|
throw new Error(`Output value is less than the minimum dust ${output.value} < ${TransactionBuilder.MINIMUM_DUST}`);
|
|
148
173
|
}
|
|
149
174
|
this.outputs.push(output);
|
|
@@ -203,6 +228,9 @@ export class TransactionBuilder extends TweakedTransaction {
|
|
|
203
228
|
return total;
|
|
204
229
|
}
|
|
205
230
|
async addRefundOutput(amountSpent) {
|
|
231
|
+
if (this.note) {
|
|
232
|
+
this.addOPReturn(this.note);
|
|
233
|
+
}
|
|
206
234
|
const sendBackAmount = this.totalInputAmount - amountSpent;
|
|
207
235
|
if (sendBackAmount >= TransactionBuilder.MINIMUM_DUST) {
|
|
208
236
|
if (AddressVerificator.isValidP2TRAddress(this.from, this.network)) {
|
|
@@ -2,6 +2,7 @@ import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
|
2
2
|
import { ITweakedTransactionData } from '../shared/TweakedTransaction.js';
|
|
3
3
|
import { ChainId } from '../../network/ChainId.js';
|
|
4
4
|
import { PsbtOutputExtended } from '@btc-vision/bitcoin';
|
|
5
|
+
import { ChallengeSolution } from '../../epoch/ChallengeSolution.js';
|
|
5
6
|
export interface LoadedStorage {
|
|
6
7
|
[key: string]: string[];
|
|
7
8
|
}
|
|
@@ -15,6 +16,7 @@ export interface ITransactionParameters extends ITweakedTransactionData {
|
|
|
15
16
|
optionalOutputs?: PsbtOutputExtended[];
|
|
16
17
|
chainId?: ChainId;
|
|
17
18
|
noSignatures?: boolean;
|
|
19
|
+
readonly note?: string | Buffer;
|
|
18
20
|
readonly feeRate: number;
|
|
19
21
|
readonly priorityFee: bigint;
|
|
20
22
|
readonly gasSatFee: bigint;
|
|
@@ -23,14 +25,10 @@ export interface IFundingTransactionParameters extends ITransactionParameters {
|
|
|
23
25
|
amount: bigint;
|
|
24
26
|
splitInputsInto?: number;
|
|
25
27
|
}
|
|
26
|
-
export interface IChallengeSolutionTransactionParameters extends ITransactionParameters {
|
|
27
|
-
amount: bigint;
|
|
28
|
-
readonly challengeSolution: Buffer;
|
|
29
|
-
}
|
|
30
28
|
export interface SharedInteractionParameters extends ITransactionParameters {
|
|
31
29
|
calldata?: Buffer;
|
|
32
30
|
disableAutoRefund?: boolean;
|
|
33
|
-
readonly
|
|
31
|
+
readonly challenge: ChallengeSolution;
|
|
34
32
|
readonly randomBytes?: Buffer;
|
|
35
33
|
readonly loadedStorage?: LoadedStorage;
|
|
36
34
|
}
|
|
@@ -43,5 +41,5 @@ export interface IDeploymentParameters extends Omit<ITransactionParameters, 'to'
|
|
|
43
41
|
readonly bytecode: Buffer;
|
|
44
42
|
readonly calldata?: Buffer;
|
|
45
43
|
readonly randomBytes?: Buffer;
|
|
46
|
-
readonly
|
|
44
|
+
readonly challenge: ChallengeSolution;
|
|
47
45
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Network } from '@btc-vision/bitcoin';
|
|
2
|
+
export interface ITimeLockOutput {
|
|
3
|
+
address: string;
|
|
4
|
+
witnessScript: Buffer;
|
|
5
|
+
}
|
|
6
|
+
export declare class TimeLockGenerator {
|
|
7
|
+
private static readonly CSV_BLOCKS;
|
|
8
|
+
static generateTimeLockAddress(publicKey: Buffer, network?: Network, csvBlocks?: number): ITimeLockOutput;
|
|
9
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import bitcoin, { networks, opcodes, script } from '@btc-vision/bitcoin';
|
|
2
|
+
export class TimeLockGenerator {
|
|
3
|
+
static generateTimeLockAddress(publicKey, network = networks.bitcoin, csvBlocks = TimeLockGenerator.CSV_BLOCKS) {
|
|
4
|
+
const witnessScript = script.compile([
|
|
5
|
+
script.number.encode(csvBlocks),
|
|
6
|
+
opcodes.OP_CHECKSEQUENCEVERIFY,
|
|
7
|
+
opcodes.OP_DROP,
|
|
8
|
+
publicKey,
|
|
9
|
+
opcodes.OP_CHECKSIG,
|
|
10
|
+
]);
|
|
11
|
+
const p2wsh = bitcoin.payments.p2wsh({
|
|
12
|
+
redeem: { output: witnessScript },
|
|
13
|
+
network,
|
|
14
|
+
});
|
|
15
|
+
if (!p2wsh.address) {
|
|
16
|
+
throw new Error('Failed to generate P2WSH address');
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
address: p2wsh.address,
|
|
20
|
+
witnessScript: witnessScript,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
TimeLockGenerator.CSV_BLOCKS = 75;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stringToBuffer(str: string): Buffer;
|
|
@@ -7,9 +7,6 @@ export interface WalletUTXOs {
|
|
|
7
7
|
readonly pending: RawUTXOResponse[];
|
|
8
8
|
readonly spentTransactions: RawUTXOResponse[];
|
|
9
9
|
}
|
|
10
|
-
export interface PreimageData {
|
|
11
|
-
readonly preimage: Buffer;
|
|
12
|
-
}
|
|
13
10
|
export declare class OPNetLimitedProvider {
|
|
14
11
|
private readonly opnetAPIUrl;
|
|
15
12
|
private readonly utxoPath;
|
|
@@ -18,7 +15,6 @@ export declare class OPNetLimitedProvider {
|
|
|
18
15
|
fetchUTXO(settings: FetchUTXOParams): Promise<UTXO[]>;
|
|
19
16
|
fetchUTXOMultiAddr(settings: FetchUTXOParamsMultiAddress): Promise<UTXO[]>;
|
|
20
17
|
broadcastTransaction(transaction: string, psbt: boolean): Promise<BroadcastResponse | undefined>;
|
|
21
|
-
getPreimage(): Promise<PreimageData | undefined>;
|
|
22
18
|
splitUTXOs(wallet: Wallet, network: Network, splitInputsInto: number, amountPerUTXO: bigint): Promise<BroadcastResponse | {
|
|
23
19
|
error: string;
|
|
24
20
|
}>;
|
|
@@ -103,13 +103,6 @@ export class OPNetLimitedProvider {
|
|
|
103
103
|
}
|
|
104
104
|
return result;
|
|
105
105
|
}
|
|
106
|
-
async getPreimage() {
|
|
107
|
-
const result = await this.rpcMethod('btc_preimage', []);
|
|
108
|
-
if (!result) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
return result;
|
|
112
|
-
}
|
|
113
106
|
async splitUTXOs(wallet, network, splitInputsInto, amountPerUTXO) {
|
|
114
107
|
const utxoSetting = {
|
|
115
108
|
addresses: [wallet.p2wpkh, wallet.p2tr],
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Network, Taptree } from '@btc-vision/bitcoin';
|
|
2
|
+
import { ChallengeSolution } from '../epoch/ChallengeSolution.js';
|
|
3
|
+
import { Feature, Features } from '../generators/Features.js';
|
|
2
4
|
export interface ContractAddressVerificationParams {
|
|
3
5
|
readonly deployerPubKey: Buffer;
|
|
4
6
|
readonly contractSaltPubKey: Buffer;
|
|
5
7
|
readonly originalSalt: Buffer;
|
|
6
8
|
readonly bytecode: Buffer;
|
|
7
|
-
readonly
|
|
9
|
+
readonly challenge: ChallengeSolution;
|
|
8
10
|
readonly priorityFee: bigint;
|
|
11
|
+
readonly features: Feature<Features>[];
|
|
9
12
|
readonly calldata?: Buffer;
|
|
10
13
|
readonly network?: Network;
|
|
11
14
|
}
|
|
@@ -5,7 +5,7 @@ export class TapscriptVerificator {
|
|
|
5
5
|
static getContractAddress(params) {
|
|
6
6
|
const network = params.network || networks.bitcoin;
|
|
7
7
|
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
8
|
-
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.
|
|
8
|
+
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
|
|
9
9
|
const scriptTree = [
|
|
10
10
|
{
|
|
11
11
|
output: compiledTargetScript,
|
|
@@ -21,7 +21,7 @@ export class TapscriptVerificator {
|
|
|
21
21
|
static verifyControlBlock(params, controlBlock) {
|
|
22
22
|
const network = params.network || networks.bitcoin;
|
|
23
23
|
const scriptBuilder = new DeploymentGenerator(params.deployerPubKey, toXOnly(params.contractSaltPubKey), network);
|
|
24
|
-
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.
|
|
24
|
+
const compiledTargetScript = scriptBuilder.compile(params.bytecode, params.originalSalt, params.challenge, params.priorityFee, params.calldata, params.features);
|
|
25
25
|
const scriptTree = [
|
|
26
26
|
{
|
|
27
27
|
output: compiledTargetScript,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btc-vision/transaction",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.1",
|
|
5
5
|
"author": "BlobMaster41",
|
|
6
6
|
"description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
|
|
7
7
|
"engines": {
|
|
@@ -64,39 +64,39 @@
|
|
|
64
64
|
"docs": "typedoc --out docs --exclude 'src/tests/*.ts' --tsconfig tsconfig.json --readme README.md --name OPNet --plugin typedoc-material-theme --themeColor '#cb9820' --exclude src/tests/test.ts --exclude src/index.ts src"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@babel/core": "^7.
|
|
67
|
+
"@babel/core": "^7.28.0",
|
|
68
68
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
69
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
70
|
-
"@babel/preset-env": "^7.
|
|
69
|
+
"@babel/plugin-transform-runtime": "^7.28.0",
|
|
70
|
+
"@babel/preset-env": "^7.28.0",
|
|
71
71
|
"@babel/preset-flow": "^7.27.1",
|
|
72
72
|
"@babel/preset-react": "^7.27.1",
|
|
73
73
|
"@babel/preset-typescript": "^7.27.1",
|
|
74
|
-
"@types/node": "^
|
|
74
|
+
"@types/node": "^24.1.0",
|
|
75
75
|
"@types/sha.js": "^2.4.4",
|
|
76
|
-
"eslint": "^9.
|
|
76
|
+
"eslint": "^9.32.0",
|
|
77
77
|
"gulp": "^5.0.1",
|
|
78
78
|
"gulp-cached": "^1.1.1",
|
|
79
79
|
"gulp-typescript": "^6.0.0-alpha.1",
|
|
80
80
|
"https-browserify": "^1.0.0",
|
|
81
81
|
"os-browserify": "^0.3.0",
|
|
82
|
-
"prettier": "^3.
|
|
82
|
+
"prettier": "^3.6.2",
|
|
83
83
|
"stream-browserify": "^3.0.0",
|
|
84
84
|
"stream-http": "^3.2.0",
|
|
85
|
-
"typedoc": "^0.28.
|
|
86
|
-
"typescript-eslint": "^8.
|
|
85
|
+
"typedoc": "^0.28.7",
|
|
86
|
+
"typescript-eslint": "^8.38.0",
|
|
87
87
|
"webpack-cli": "^6.0.1"
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"@babel/plugin-proposal-object-rest-spread": "^7.
|
|
90
|
+
"@babel/plugin-proposal-object-rest-spread": "^7.21.4-esm",
|
|
91
91
|
"@bitcoinerlab/secp256k1": "^1.2.0",
|
|
92
92
|
"@btc-vision/bitcoin": "^6.4.6",
|
|
93
93
|
"@btc-vision/bitcoin-rpc": "^1.0.2",
|
|
94
94
|
"@btc-vision/logger": "^1.0.6",
|
|
95
|
-
"@eslint/js": "^9.
|
|
96
|
-
"@noble/secp256k1": "^2.
|
|
95
|
+
"@eslint/js": "^9.32.0",
|
|
96
|
+
"@noble/secp256k1": "^2.3.0",
|
|
97
97
|
"assert": "^2.1.0",
|
|
98
98
|
"babel-loader": "^10.0.0",
|
|
99
|
-
"babel-plugin-transform-import-meta": "^2.3.
|
|
99
|
+
"babel-plugin-transform-import-meta": "^2.3.3",
|
|
100
100
|
"babel-preset-react": "^6.24.1",
|
|
101
101
|
"babelify": "^10.0.0",
|
|
102
102
|
"bech32": "^2.0.0",
|
|
@@ -109,10 +109,10 @@
|
|
|
109
109
|
"gulp-eslint-new": "^2.4.0",
|
|
110
110
|
"gulp-logger-new": "^1.0.1",
|
|
111
111
|
"process": "^0.11.10",
|
|
112
|
-
"sha.js": "^2.4.
|
|
112
|
+
"sha.js": "^2.4.12",
|
|
113
113
|
"ts-loader": "^9.5.2",
|
|
114
114
|
"ts-node": "^10.9.2",
|
|
115
115
|
"typescript": "^5.8.3",
|
|
116
|
-
"webpack": "^5.
|
|
116
|
+
"webpack": "^5.100.2"
|
|
117
117
|
}
|
|
118
118
|
}
|
package/src/_version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.
|
|
1
|
+
export const version = '1.6.1';
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { stringToBuffer } from '../utils/StringToBuffer.js';
|
|
2
|
+
import {
|
|
3
|
+
IChallengeSolution,
|
|
4
|
+
IChallengeSubmission,
|
|
5
|
+
IChallengeVerification,
|
|
6
|
+
RawChallenge,
|
|
7
|
+
RawChallengeSubmission,
|
|
8
|
+
RawChallengeVerification,
|
|
9
|
+
} from './interfaces/IChallengeSolution.js';
|
|
10
|
+
import { Address } from '../keypair/Address.js';
|
|
11
|
+
import { EpochValidator } from './validator/EpochValidator.js';
|
|
12
|
+
import { BinaryWriter } from '../buffer/BinaryWriter.js';
|
|
13
|
+
import { MessageSigner } from '../keypair/MessageSigner.js';
|
|
14
|
+
|
|
15
|
+
export class ChallengeVerification implements IChallengeVerification {
|
|
16
|
+
public readonly epochHash: Buffer;
|
|
17
|
+
public readonly epochRoot: Buffer;
|
|
18
|
+
public readonly targetHash: Buffer;
|
|
19
|
+
public readonly targetChecksum: Buffer;
|
|
20
|
+
public readonly startBlock: bigint;
|
|
21
|
+
public readonly endBlock: bigint;
|
|
22
|
+
public readonly proofs: readonly Buffer[];
|
|
23
|
+
|
|
24
|
+
constructor(data: RawChallengeVerification) {
|
|
25
|
+
this.epochHash = stringToBuffer(data.epochHash);
|
|
26
|
+
this.epochRoot = stringToBuffer(data.epochRoot);
|
|
27
|
+
this.targetHash = stringToBuffer(data.targetHash);
|
|
28
|
+
this.targetChecksum = stringToBuffer(data.targetChecksum);
|
|
29
|
+
this.startBlock = BigInt(data.startBlock);
|
|
30
|
+
this.endBlock = BigInt(data.endBlock);
|
|
31
|
+
this.proofs = Object.freeze(data.proofs.map((proof) => stringToBuffer(proof)));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ChallengeSubmission implements IChallengeSubmission {
|
|
36
|
+
public readonly publicKey: Address;
|
|
37
|
+
public readonly solution: Buffer;
|
|
38
|
+
public readonly graffiti: Buffer | undefined;
|
|
39
|
+
public readonly signature: Buffer;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
data: RawChallengeSubmission,
|
|
43
|
+
public readonly epochNumber: bigint,
|
|
44
|
+
) {
|
|
45
|
+
this.publicKey = Address.fromString(data.publicKey);
|
|
46
|
+
this.solution = stringToBuffer(data.solution);
|
|
47
|
+
this.graffiti = data.graffiti ? stringToBuffer(data.graffiti) : undefined;
|
|
48
|
+
this.signature = stringToBuffer(data.signature);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public verifySignature(): boolean {
|
|
52
|
+
const signatureDataWriter = new BinaryWriter();
|
|
53
|
+
signatureDataWriter.writeAddress(this.publicKey);
|
|
54
|
+
signatureDataWriter.writeU64(this.epochNumber);
|
|
55
|
+
signatureDataWriter.writeBytes(this.solution);
|
|
56
|
+
|
|
57
|
+
if (this.graffiti) {
|
|
58
|
+
signatureDataWriter.writeBytes(this.graffiti);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const buffer = signatureDataWriter.getBuffer();
|
|
62
|
+
return MessageSigner.verifySignature(this.publicKey, buffer, this.signature);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class ChallengeSolution implements IChallengeSolution {
|
|
67
|
+
public readonly epochNumber: bigint;
|
|
68
|
+
public readonly publicKey: Address;
|
|
69
|
+
public readonly solution: Buffer;
|
|
70
|
+
public readonly salt: Buffer;
|
|
71
|
+
public readonly graffiti: Buffer;
|
|
72
|
+
public readonly difficulty: number;
|
|
73
|
+
public readonly verification: ChallengeVerification;
|
|
74
|
+
|
|
75
|
+
private readonly submission?: ChallengeSubmission;
|
|
76
|
+
|
|
77
|
+
constructor(data: RawChallenge) {
|
|
78
|
+
this.epochNumber = BigInt(data.epochNumber);
|
|
79
|
+
this.publicKey = Address.fromString(data.publicKey);
|
|
80
|
+
this.solution = stringToBuffer(data.solution);
|
|
81
|
+
this.salt = stringToBuffer(data.salt);
|
|
82
|
+
this.graffiti = stringToBuffer(data.graffiti);
|
|
83
|
+
this.difficulty = data.difficulty;
|
|
84
|
+
this.verification = new ChallengeVerification(data.verification);
|
|
85
|
+
this.submission = data.submission
|
|
86
|
+
? new ChallengeSubmission(data.submission, this.epochNumber + 2n)
|
|
87
|
+
: data.submission;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Static method to validate from raw data directly
|
|
92
|
+
*/
|
|
93
|
+
public static async validateRaw(data: RawChallenge): Promise<boolean> {
|
|
94
|
+
return EpochValidator.validateEpochWinner(data);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public verifySubmissionSignature(): boolean {
|
|
98
|
+
if (!this.submission) {
|
|
99
|
+
throw new Error('No submission provided in request.');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return this.submission.verifySignature();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getSubmission(): ChallengeSubmission | undefined {
|
|
106
|
+
if (!this.submission) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!this.verifySubmissionSignature()) {
|
|
111
|
+
throw new Error('Invalid submission signature.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return this.submission;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Verify this challenge
|
|
119
|
+
* @returns {Promise<boolean>} True if the challenge is valid
|
|
120
|
+
*/
|
|
121
|
+
public async verify(): Promise<boolean> {
|
|
122
|
+
return EpochValidator.validateChallengeSolution(this);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get the preimage challenge
|
|
127
|
+
* @returns {Buffer} The solution/challenge as a buffer
|
|
128
|
+
*/
|
|
129
|
+
public toBuffer(): Buffer {
|
|
130
|
+
return this.solution;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the solution as a hex string
|
|
135
|
+
* @returns {string} The solution as a hex string with 0x prefix
|
|
136
|
+
*/
|
|
137
|
+
public toHex(): string {
|
|
138
|
+
return '0x' + this.solution.toString('hex');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Convert to raw format for serialization
|
|
143
|
+
*/
|
|
144
|
+
public toRaw(): RawChallenge {
|
|
145
|
+
return {
|
|
146
|
+
epochNumber: this.epochNumber.toString(),
|
|
147
|
+
publicKey: this.publicKey.toHex(),
|
|
148
|
+
solution: this.toHex(),
|
|
149
|
+
salt: '0x' + this.salt.toString('hex'),
|
|
150
|
+
graffiti: '0x' + this.graffiti.toString('hex'),
|
|
151
|
+
difficulty: this.difficulty,
|
|
152
|
+
verification: {
|
|
153
|
+
epochHash: '0x' + this.verification.epochHash.toString('hex'),
|
|
154
|
+
epochRoot: '0x' + this.verification.epochRoot.toString('hex'),
|
|
155
|
+
targetHash: '0x' + this.verification.targetHash.toString('hex'),
|
|
156
|
+
targetChecksum: '0x' + this.verification.targetChecksum.toString('hex'),
|
|
157
|
+
startBlock: this.verification.startBlock.toString(),
|
|
158
|
+
endBlock: this.verification.endBlock.toString(),
|
|
159
|
+
proofs: this.verification.proofs.map((p) => '0x' + p.toString('hex')),
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Calculate the expected solution hash for this challenge
|
|
166
|
+
* @returns {Promise<Buffer>} The calculated solution hash
|
|
167
|
+
*/
|
|
168
|
+
public async calculateSolution(): Promise<Buffer> {
|
|
169
|
+
return EpochValidator.calculateSolution(
|
|
170
|
+
this.verification.targetChecksum,
|
|
171
|
+
this.publicKey.toBuffer(),
|
|
172
|
+
this.salt,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check if the challenge meets a specific difficulty requirement
|
|
178
|
+
* @param {number} minDifficulty The minimum difficulty required
|
|
179
|
+
* @returns {Promise<{valid: boolean; difficulty: number}>} Validation result
|
|
180
|
+
*/
|
|
181
|
+
public checkDifficulty(minDifficulty: number): { valid: boolean; difficulty: number } {
|
|
182
|
+
return EpochValidator.checkDifficulty(
|
|
183
|
+
this.solution,
|
|
184
|
+
this.verification.targetHash,
|
|
185
|
+
minDifficulty,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get the mining target block for this epoch
|
|
191
|
+
* @returns {bigint | null} The target block number or null if epoch 0
|
|
192
|
+
*/
|
|
193
|
+
public getMiningTargetBlock(): bigint | null {
|
|
194
|
+
return EpochValidator.getMiningTargetBlock(this.epochNumber);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Address } from '../../keypair/Address.js';
|
|
2
|
+
|
|
3
|
+
export interface IChallengeVerification {
|
|
4
|
+
readonly epochHash: Buffer;
|
|
5
|
+
readonly epochRoot: Buffer;
|
|
6
|
+
readonly targetHash: Buffer;
|
|
7
|
+
readonly targetChecksum: Buffer;
|
|
8
|
+
readonly startBlock: bigint;
|
|
9
|
+
readonly endBlock: bigint;
|
|
10
|
+
readonly proofs: readonly Buffer[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IChallengeSolution {
|
|
14
|
+
readonly epochNumber: bigint;
|
|
15
|
+
readonly publicKey: Address;
|
|
16
|
+
readonly solution: Buffer;
|
|
17
|
+
readonly salt: Buffer;
|
|
18
|
+
readonly graffiti: Buffer;
|
|
19
|
+
readonly difficulty: number;
|
|
20
|
+
readonly verification: IChallengeVerification;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RawChallengeVerification {
|
|
24
|
+
readonly epochHash: string;
|
|
25
|
+
readonly epochRoot: string;
|
|
26
|
+
readonly targetHash: string;
|
|
27
|
+
readonly targetChecksum: string;
|
|
28
|
+
readonly startBlock: string;
|
|
29
|
+
readonly endBlock: string;
|
|
30
|
+
readonly proofs: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RawChallengeSubmission {
|
|
34
|
+
readonly publicKey: string;
|
|
35
|
+
readonly solution: string;
|
|
36
|
+
readonly graffiti?: string;
|
|
37
|
+
readonly signature: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface IChallengeSubmission {
|
|
41
|
+
readonly publicKey: Address;
|
|
42
|
+
readonly solution: Buffer;
|
|
43
|
+
readonly graffiti?: Buffer;
|
|
44
|
+
readonly signature: Buffer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RawChallenge {
|
|
48
|
+
readonly epochNumber: string;
|
|
49
|
+
readonly publicKey: string;
|
|
50
|
+
readonly solution: string;
|
|
51
|
+
readonly salt: string;
|
|
52
|
+
readonly graffiti: string;
|
|
53
|
+
readonly difficulty: number;
|
|
54
|
+
readonly verification: RawChallengeVerification;
|
|
55
|
+
readonly submission?: RawChallengeSubmission;
|
|
56
|
+
}
|