@btc-vision/transaction 1.7.19 → 1.7.23
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/.babelrc +10 -1
- package/LICENSE +190 -21
- package/README.md +1 -1
- package/browser/_version.d.ts +1 -1
- package/browser/bip39.js +204 -0
- package/browser/bitcoin-utils.js +3172 -0
- package/browser/btc-vision-bip32.js +805 -0
- package/browser/btc-vision-bitcoin.js +4179 -0
- package/browser/btc-vision-logger.js +273 -0
- package/browser/btc-vision-post-quantum.js +542 -0
- package/browser/chain/ChainData.d.ts +1 -1
- package/browser/crypto/crypto.d.ts +1 -1
- package/browser/generators/AddressGenerator.d.ts +1 -1
- package/browser/generators/Generator.d.ts +1 -1
- package/browser/generators/MLDSAData.d.ts +1 -1
- package/browser/generators/builders/CalldataGenerator.d.ts +1 -1
- package/browser/generators/builders/CustomGenerator.d.ts +1 -1
- package/browser/generators/builders/DeploymentGenerator.d.ts +1 -1
- package/browser/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/browser/generators/builders/LegacyCalldataGenerator.d.ts +1 -1
- package/browser/generators/builders/P2WDAGenerator.d.ts +1 -1
- package/browser/index.js +10775 -2
- package/browser/keypair/Address.d.ts +5 -3
- package/browser/keypair/AddressVerificator.d.ts +2 -2
- package/browser/keypair/EcKeyPair.d.ts +2 -2
- package/browser/keypair/MessageSigner.d.ts +2 -2
- package/browser/keypair/Wallet.d.ts +2 -2
- package/browser/metadata/ContractBaseMetadata.d.ts +1 -1
- package/browser/mnemonic/Mnemonic.d.ts +2 -2
- package/browser/noble-curves.js +3316 -0
- package/browser/noble-hashes.js +1608 -0
- package/browser/opnet.d.ts +15 -2
- package/browser/p2wda/P2WDADetector.d.ts +2 -2
- package/browser/polyfills.js +4590 -0
- package/browser/scure-base.js +410 -0
- package/browser/signer/AddressRotation.d.ts +12 -0
- package/browser/signer/SignerUtils.d.ts +1 -1
- package/browser/signer/TweakedSigner.d.ts +1 -1
- package/browser/transaction/TransactionFactory.d.ts +15 -1
- package/browser/transaction/browser/BrowserSignerBase.d.ts +1 -1
- package/browser/transaction/browser/Web3Provider.d.ts +1 -1
- package/browser/transaction/browser/extensions/UnisatSigner.d.ts +1 -1
- package/browser/transaction/browser/extensions/XverseSigner.d.ts +1 -1
- package/browser/transaction/builders/CancelTransaction.d.ts +1 -1
- package/browser/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -1
- package/browser/transaction/builders/FundingTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransactionP2WDA.d.ts +2 -2
- package/browser/transaction/builders/MultiSignTransaction.d.ts +1 -1
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/TransactionBuilder.d.ts +1 -1
- package/browser/transaction/enums/TransactionType.d.ts +3 -1
- package/browser/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/browser/transaction/interfaces/ITransactionParameters.d.ts +3 -1
- package/browser/transaction/interfaces/Tap.d.ts +1 -1
- package/browser/transaction/mineable/TimelockGenerator.d.ts +1 -1
- package/browser/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/browser/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/browser/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/browser/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/browser/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/browser/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/browser/transaction/processor/PsbtTransaction.d.ts +1 -1
- package/browser/transaction/shared/P2TR_MS.d.ts +1 -1
- package/browser/transaction/shared/TweakedTransaction.d.ts +15 -4
- package/browser/utxo/OPNetLimitedProvider.d.ts +1 -1
- package/browser/utxo/interfaces/IUTXO.d.ts +2 -0
- package/browser/valibot.js +4948 -0
- package/browser/vendors.js +12913 -0
- package/browser/verification/TapscriptVerificator.d.ts +1 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/generators/builders/HashCommitmentGenerator.d.ts +49 -0
- package/build/generators/builders/HashCommitmentGenerator.js +229 -0
- package/build/keypair/Address.d.ts +3 -1
- package/build/keypair/Address.js +87 -54
- package/build/opnet.d.ts +14 -1
- package/build/opnet.js +11 -1
- package/build/signer/AddressRotation.d.ts +12 -0
- package/build/signer/AddressRotation.js +16 -0
- package/build/transaction/TransactionFactory.d.ts +14 -0
- package/build/transaction/TransactionFactory.js +36 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.d.ts +44 -0
- package/build/transaction/builders/ConsolidatedInteractionTransaction.js +259 -0
- package/build/transaction/builders/TransactionBuilder.js +2 -0
- package/build/transaction/enums/TransactionType.d.ts +3 -1
- package/build/transaction/enums/TransactionType.js +2 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.d.ts +31 -0
- package/build/transaction/interfaces/IConsolidatedTransactionParameters.js +1 -0
- package/build/transaction/interfaces/ITransactionParameters.d.ts +2 -0
- package/build/transaction/offline/OfflineTransactionManager.d.ts +69 -0
- package/build/transaction/offline/OfflineTransactionManager.js +255 -0
- package/build/transaction/offline/TransactionReconstructor.d.ts +28 -0
- package/build/transaction/offline/TransactionReconstructor.js +243 -0
- package/build/transaction/offline/TransactionSerializer.d.ts +50 -0
- package/build/transaction/offline/TransactionSerializer.js +700 -0
- package/build/transaction/offline/TransactionStateCapture.d.ts +52 -0
- package/build/transaction/offline/TransactionStateCapture.js +275 -0
- package/build/transaction/offline/interfaces/ISerializableState.d.ts +62 -0
- package/build/transaction/offline/interfaces/ISerializableState.js +2 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.d.ts +62 -0
- package/build/transaction/offline/interfaces/ITypeSpecificData.js +19 -0
- package/build/transaction/shared/TweakedTransaction.d.ts +12 -1
- package/build/transaction/shared/TweakedTransaction.js +75 -8
- package/build/utxo/interfaces/IUTXO.d.ts +2 -0
- package/documentation/README.md +5 -0
- package/documentation/offline-transaction-signing.md +650 -0
- package/documentation/transaction-building.md +603 -0
- package/package.json +62 -4
- package/src/_version.ts +1 -1
- package/src/generators/builders/HashCommitmentGenerator.ts +495 -0
- package/src/keypair/Address.ts +123 -70
- package/src/opnet.ts +16 -1
- package/src/signer/AddressRotation.ts +72 -0
- package/src/transaction/TransactionFactory.ts +87 -0
- package/src/transaction/builders/CancelTransaction.ts +4 -2
- package/src/transaction/builders/ConsolidatedInteractionTransaction.ts +561 -0
- package/src/transaction/builders/CustomScriptTransaction.ts +4 -2
- package/src/transaction/builders/MultiSignTransaction.ts +4 -2
- package/src/transaction/builders/TransactionBuilder.ts +8 -2
- package/src/transaction/enums/TransactionType.ts +2 -0
- package/src/transaction/interfaces/IConsolidatedTransactionParameters.ts +78 -0
- package/src/transaction/interfaces/ITransactionParameters.ts +8 -0
- package/src/transaction/offline/OfflineTransactionManager.ts +630 -0
- package/src/transaction/offline/TransactionReconstructor.ts +402 -0
- package/src/transaction/offline/TransactionSerializer.ts +920 -0
- package/src/transaction/offline/TransactionStateCapture.ts +469 -0
- package/src/transaction/offline/interfaces/ISerializableState.ts +141 -0
- package/src/transaction/offline/interfaces/ITypeSpecificData.ts +172 -0
- package/src/transaction/shared/TweakedTransaction.ts +156 -9
- package/src/utxo/interfaces/IUTXO.ts +8 -0
- package/test/address-rotation.test.ts +553 -0
- package/test/offline-transaction.test.ts +2065 -0
- package/vite.config.browser.ts +92 -0
- package/webpack.config.js +143 -2
- package/browser/crypto/crypto-browser.d.ts +0 -11
- package/browser/index.js.LICENSE.txt +0 -29
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { BinaryWriter } from '../../buffer/BinaryWriter.js';
|
|
3
|
+
import { BinaryReader } from '../../buffer/BinaryReader.js';
|
|
4
|
+
import {
|
|
5
|
+
ISerializableTransactionState,
|
|
6
|
+
PrecomputedData,
|
|
7
|
+
SERIALIZATION_FORMAT_VERSION,
|
|
8
|
+
SERIALIZATION_MAGIC_BYTE,
|
|
9
|
+
SerializationHeader,
|
|
10
|
+
SerializedBaseParams,
|
|
11
|
+
SerializedOutput,
|
|
12
|
+
SerializedSignerMapping,
|
|
13
|
+
SerializedUTXO,
|
|
14
|
+
} from './interfaces/ISerializableState.js';
|
|
15
|
+
import {
|
|
16
|
+
CancelSpecificData,
|
|
17
|
+
CustomScriptSpecificData,
|
|
18
|
+
DeploymentSpecificData,
|
|
19
|
+
FundingSpecificData,
|
|
20
|
+
InteractionSpecificData,
|
|
21
|
+
MultiSigSpecificData,
|
|
22
|
+
SerializedLoadedStorage,
|
|
23
|
+
SerializedScriptElement,
|
|
24
|
+
TypeSpecificData,
|
|
25
|
+
} from './interfaces/ITypeSpecificData.js';
|
|
26
|
+
import { TransactionType } from '../enums/TransactionType.js';
|
|
27
|
+
import { RawChallenge, RawChallengeVerification, } from '../../epoch/interfaces/IChallengeSolution.js';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Serializes and deserializes transaction state for offline signing.
|
|
31
|
+
* Uses binary format for compact size.
|
|
32
|
+
*/
|
|
33
|
+
export class TransactionSerializer {
|
|
34
|
+
/**
|
|
35
|
+
* Serialize transaction state to binary format
|
|
36
|
+
* @param state - The transaction state to serialize
|
|
37
|
+
* @returns Buffer containing serialized state with checksum
|
|
38
|
+
*/
|
|
39
|
+
public static serialize(state: ISerializableTransactionState): Buffer {
|
|
40
|
+
const writer = new BinaryWriter();
|
|
41
|
+
|
|
42
|
+
// Write header
|
|
43
|
+
this.writeHeader(writer, state.header);
|
|
44
|
+
|
|
45
|
+
// Write base params
|
|
46
|
+
this.writeBaseParams(writer, state.baseParams);
|
|
47
|
+
|
|
48
|
+
// Write UTXOs
|
|
49
|
+
this.writeUTXOArray(writer, state.utxos);
|
|
50
|
+
this.writeUTXOArray(writer, state.optionalInputs);
|
|
51
|
+
|
|
52
|
+
// Write optional outputs
|
|
53
|
+
this.writeOutputArray(writer, state.optionalOutputs);
|
|
54
|
+
|
|
55
|
+
// Write signer mappings
|
|
56
|
+
writer.writeBoolean(state.addressRotationEnabled);
|
|
57
|
+
this.writeSignerMappings(writer, state.signerMappings);
|
|
58
|
+
|
|
59
|
+
// Write type-specific data
|
|
60
|
+
this.writeTypeSpecificData(writer, state.typeSpecificData);
|
|
61
|
+
|
|
62
|
+
// Write precomputed data
|
|
63
|
+
this.writePrecomputedData(writer, state.precomputedData);
|
|
64
|
+
|
|
65
|
+
// Get buffer and calculate checksum
|
|
66
|
+
const dataBuffer = Buffer.from(writer.getBuffer());
|
|
67
|
+
const checksum = this.calculateChecksum(dataBuffer);
|
|
68
|
+
|
|
69
|
+
return Buffer.concat([dataBuffer, checksum]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Deserialize binary format to transaction state
|
|
74
|
+
* @param data - Buffer containing serialized state
|
|
75
|
+
* @returns Deserialized transaction state
|
|
76
|
+
* @throws Error if checksum validation fails or format is invalid
|
|
77
|
+
*/
|
|
78
|
+
public static deserialize(data: Buffer): ISerializableTransactionState {
|
|
79
|
+
// Verify checksum (last 32 bytes)
|
|
80
|
+
if (data.length < 32) {
|
|
81
|
+
throw new Error('Invalid serialized data: too short');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const checksum = data.subarray(-32);
|
|
85
|
+
const payload = data.subarray(0, -32);
|
|
86
|
+
const expectedChecksum = this.calculateChecksum(payload);
|
|
87
|
+
|
|
88
|
+
if (!checksum.equals(expectedChecksum)) {
|
|
89
|
+
throw new Error('Invalid checksum - data may be corrupted');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const reader = new BinaryReader(payload);
|
|
93
|
+
|
|
94
|
+
// Read header
|
|
95
|
+
const header = this.readHeader(reader);
|
|
96
|
+
|
|
97
|
+
// Verify format version
|
|
98
|
+
if (header.formatVersion > SERIALIZATION_FORMAT_VERSION) {
|
|
99
|
+
throw new Error(`Unsupported format version: ${header.formatVersion}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Read base params
|
|
103
|
+
const baseParams = this.readBaseParams(reader);
|
|
104
|
+
|
|
105
|
+
// Read UTXOs
|
|
106
|
+
const utxos = this.readUTXOArray(reader);
|
|
107
|
+
const optionalInputs = this.readUTXOArray(reader);
|
|
108
|
+
|
|
109
|
+
// Read optional outputs
|
|
110
|
+
const optionalOutputs = this.readOutputArray(reader);
|
|
111
|
+
|
|
112
|
+
// Read signer mappings
|
|
113
|
+
const addressRotationEnabled = reader.readBoolean();
|
|
114
|
+
const signerMappings = this.readSignerMappings(reader);
|
|
115
|
+
|
|
116
|
+
// Read type-specific data
|
|
117
|
+
const typeSpecificData = this.readTypeSpecificData(reader, header.transactionType);
|
|
118
|
+
|
|
119
|
+
// Read precomputed data
|
|
120
|
+
const precomputedData = this.readPrecomputedData(reader);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
header,
|
|
124
|
+
baseParams,
|
|
125
|
+
utxos,
|
|
126
|
+
optionalInputs,
|
|
127
|
+
optionalOutputs,
|
|
128
|
+
addressRotationEnabled,
|
|
129
|
+
signerMappings,
|
|
130
|
+
typeSpecificData,
|
|
131
|
+
precomputedData,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Export state as base64 string (for transport)
|
|
137
|
+
* @param state - Transaction state to export
|
|
138
|
+
* @returns Base64-encoded string
|
|
139
|
+
*/
|
|
140
|
+
public static toBase64(state: ISerializableTransactionState): string {
|
|
141
|
+
return this.serialize(state).toString('base64');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Import state from base64 string
|
|
146
|
+
* @param base64 - Base64-encoded state
|
|
147
|
+
* @returns Deserialized transaction state
|
|
148
|
+
*/
|
|
149
|
+
public static fromBase64(base64: string): ISerializableTransactionState {
|
|
150
|
+
return this.deserialize(Buffer.from(base64, 'base64'));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Export state as hex string
|
|
155
|
+
* @param state - Transaction state to export
|
|
156
|
+
* @returns Hex-encoded string
|
|
157
|
+
*/
|
|
158
|
+
public static toHex(state: ISerializableTransactionState): string {
|
|
159
|
+
return this.serialize(state).toString('hex');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Import state from hex string
|
|
164
|
+
* @param hex - Hex-encoded state
|
|
165
|
+
* @returns Deserialized transaction state
|
|
166
|
+
*/
|
|
167
|
+
public static fromHex(hex: string): ISerializableTransactionState {
|
|
168
|
+
return this.deserialize(Buffer.from(hex, 'hex'));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private static writeHeader(writer: BinaryWriter, header: SerializationHeader): void {
|
|
172
|
+
writer.writeU8(SERIALIZATION_MAGIC_BYTE);
|
|
173
|
+
writer.writeU8(header.formatVersion);
|
|
174
|
+
writer.writeU8(header.consensusVersion);
|
|
175
|
+
writer.writeU8(header.transactionType);
|
|
176
|
+
writer.writeU32(header.chainId);
|
|
177
|
+
writer.writeU64(BigInt(header.timestamp));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private static readHeader(reader: BinaryReader): SerializationHeader {
|
|
181
|
+
const magic = reader.readU8();
|
|
182
|
+
if (magic !== SERIALIZATION_MAGIC_BYTE) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Invalid magic byte: expected 0x${SERIALIZATION_MAGIC_BYTE.toString(16)}, got 0x${magic.toString(16)}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
formatVersion: reader.readU8(),
|
|
190
|
+
consensusVersion: reader.readU8(),
|
|
191
|
+
transactionType: reader.readU8() as TransactionType,
|
|
192
|
+
chainId: reader.readU32(),
|
|
193
|
+
timestamp: Number(reader.readU64()),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private static writeBaseParams(writer: BinaryWriter, params: SerializedBaseParams): void {
|
|
198
|
+
writer.writeStringWithLength(params.from);
|
|
199
|
+
writer.writeBoolean(params.to !== undefined);
|
|
200
|
+
if (params.to !== undefined) {
|
|
201
|
+
writer.writeStringWithLength(params.to);
|
|
202
|
+
}
|
|
203
|
+
writer.writeU32(Math.floor(params.feeRate * 1000)); // Store as milli-sat/vB for precision
|
|
204
|
+
writer.writeU64(BigInt(params.priorityFee));
|
|
205
|
+
writer.writeU64(BigInt(params.gasSatFee));
|
|
206
|
+
writer.writeU8(this.networkNameToU8(params.networkName));
|
|
207
|
+
writer.writeU8(params.txVersion);
|
|
208
|
+
writer.writeBoolean(params.note !== undefined);
|
|
209
|
+
if (params.note !== undefined) {
|
|
210
|
+
writer.writeBytesWithLength(Buffer.from(params.note, 'hex'));
|
|
211
|
+
}
|
|
212
|
+
writer.writeBoolean(params.anchor);
|
|
213
|
+
writer.writeBoolean(params.debugFees ?? false);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private static readBaseParams(reader: BinaryReader): SerializedBaseParams {
|
|
217
|
+
const from = reader.readStringWithLength();
|
|
218
|
+
const hasTo = reader.readBoolean();
|
|
219
|
+
const to = hasTo ? reader.readStringWithLength() : undefined;
|
|
220
|
+
const feeRate = reader.readU32() / 1000; // Convert back from milli-sat/vB
|
|
221
|
+
const priorityFee = reader.readU64().toString();
|
|
222
|
+
const gasSatFee = reader.readU64().toString();
|
|
223
|
+
const networkName = this.u8ToNetworkName(reader.readU8());
|
|
224
|
+
const txVersion = reader.readU8();
|
|
225
|
+
const hasNote = reader.readBoolean();
|
|
226
|
+
const note = hasNote
|
|
227
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
228
|
+
: undefined;
|
|
229
|
+
const anchor = reader.readBoolean();
|
|
230
|
+
const debugFees = reader.readBoolean();
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
from,
|
|
234
|
+
to,
|
|
235
|
+
feeRate,
|
|
236
|
+
priorityFee,
|
|
237
|
+
gasSatFee,
|
|
238
|
+
networkName,
|
|
239
|
+
txVersion,
|
|
240
|
+
note,
|
|
241
|
+
anchor,
|
|
242
|
+
debugFees,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private static writeUTXOArray(writer: BinaryWriter, utxos: SerializedUTXO[]): void {
|
|
247
|
+
writer.writeU16(utxos.length);
|
|
248
|
+
for (const utxo of utxos) {
|
|
249
|
+
this.writeUTXO(writer, utxo);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private static writeUTXO(writer: BinaryWriter, utxo: SerializedUTXO): void {
|
|
254
|
+
// Transaction ID (32 bytes)
|
|
255
|
+
writer.writeBytes(Buffer.from(utxo.transactionId, 'hex'));
|
|
256
|
+
writer.writeU32(utxo.outputIndex);
|
|
257
|
+
writer.writeU64(BigInt(utxo.value));
|
|
258
|
+
writer.writeBytesWithLength(Buffer.from(utxo.scriptPubKeyHex, 'hex'));
|
|
259
|
+
|
|
260
|
+
// Optional address
|
|
261
|
+
writer.writeBoolean(utxo.scriptPubKeyAddress !== undefined);
|
|
262
|
+
if (utxo.scriptPubKeyAddress !== undefined) {
|
|
263
|
+
writer.writeStringWithLength(utxo.scriptPubKeyAddress);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Optional scripts
|
|
267
|
+
writer.writeBoolean(utxo.redeemScript !== undefined);
|
|
268
|
+
if (utxo.redeemScript !== undefined) {
|
|
269
|
+
writer.writeBytesWithLength(Buffer.from(utxo.redeemScript, 'hex'));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
writer.writeBoolean(utxo.witnessScript !== undefined);
|
|
273
|
+
if (utxo.witnessScript !== undefined) {
|
|
274
|
+
writer.writeBytesWithLength(Buffer.from(utxo.witnessScript, 'hex'));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
writer.writeBoolean(utxo.nonWitnessUtxo !== undefined);
|
|
278
|
+
if (utxo.nonWitnessUtxo !== undefined) {
|
|
279
|
+
writer.writeBytesWithLength(Buffer.from(utxo.nonWitnessUtxo, 'hex'));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private static readUTXOArray(reader: BinaryReader): SerializedUTXO[] {
|
|
284
|
+
const count = reader.readU16();
|
|
285
|
+
const utxos: SerializedUTXO[] = [];
|
|
286
|
+
for (let i = 0; i < count; i++) {
|
|
287
|
+
utxos.push(this.readUTXO(reader));
|
|
288
|
+
}
|
|
289
|
+
return utxos;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private static readUTXO(reader: BinaryReader): SerializedUTXO {
|
|
293
|
+
const transactionId = Buffer.from(reader.readBytes(32)).toString('hex');
|
|
294
|
+
const outputIndex = reader.readU32();
|
|
295
|
+
const value = reader.readU64().toString();
|
|
296
|
+
const scriptPubKeyHex = Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
297
|
+
|
|
298
|
+
const hasAddress = reader.readBoolean();
|
|
299
|
+
const scriptPubKeyAddress = hasAddress ? reader.readStringWithLength() : undefined;
|
|
300
|
+
|
|
301
|
+
const hasRedeemScript = reader.readBoolean();
|
|
302
|
+
const redeemScript = hasRedeemScript
|
|
303
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
304
|
+
: undefined;
|
|
305
|
+
|
|
306
|
+
const hasWitnessScript = reader.readBoolean();
|
|
307
|
+
const witnessScript = hasWitnessScript
|
|
308
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
309
|
+
: undefined;
|
|
310
|
+
|
|
311
|
+
const hasNonWitnessUtxo = reader.readBoolean();
|
|
312
|
+
const nonWitnessUtxo = hasNonWitnessUtxo
|
|
313
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
314
|
+
: undefined;
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
transactionId,
|
|
318
|
+
outputIndex,
|
|
319
|
+
value,
|
|
320
|
+
scriptPubKeyHex,
|
|
321
|
+
scriptPubKeyAddress,
|
|
322
|
+
redeemScript,
|
|
323
|
+
witnessScript,
|
|
324
|
+
nonWitnessUtxo,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private static writeOutputArray(writer: BinaryWriter, outputs: SerializedOutput[]): void {
|
|
329
|
+
writer.writeU16(outputs.length);
|
|
330
|
+
for (const output of outputs) {
|
|
331
|
+
this.writeOutput(writer, output);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private static writeOutput(writer: BinaryWriter, output: SerializedOutput): void {
|
|
336
|
+
writer.writeU64(BigInt(output.value));
|
|
337
|
+
|
|
338
|
+
writer.writeBoolean(output.address !== undefined);
|
|
339
|
+
if (output.address !== undefined) {
|
|
340
|
+
writer.writeStringWithLength(output.address);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
writer.writeBoolean(output.script !== undefined);
|
|
344
|
+
if (output.script !== undefined) {
|
|
345
|
+
writer.writeBytesWithLength(Buffer.from(output.script, 'hex'));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
writer.writeBoolean(output.tapInternalKey !== undefined);
|
|
349
|
+
if (output.tapInternalKey !== undefined) {
|
|
350
|
+
writer.writeBytesWithLength(Buffer.from(output.tapInternalKey, 'hex'));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private static readOutputArray(reader: BinaryReader): SerializedOutput[] {
|
|
355
|
+
const count = reader.readU16();
|
|
356
|
+
const outputs: SerializedOutput[] = [];
|
|
357
|
+
for (let i = 0; i < count; i++) {
|
|
358
|
+
outputs.push(this.readOutput(reader));
|
|
359
|
+
}
|
|
360
|
+
return outputs;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private static readOutput(reader: BinaryReader): SerializedOutput {
|
|
364
|
+
const value = Number(reader.readU64());
|
|
365
|
+
|
|
366
|
+
const hasAddress = reader.readBoolean();
|
|
367
|
+
const address = hasAddress ? reader.readStringWithLength() : undefined;
|
|
368
|
+
|
|
369
|
+
const hasScript = reader.readBoolean();
|
|
370
|
+
const script = hasScript
|
|
371
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
372
|
+
: undefined;
|
|
373
|
+
|
|
374
|
+
const hasTapInternalKey = reader.readBoolean();
|
|
375
|
+
const tapInternalKey = hasTapInternalKey
|
|
376
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
377
|
+
: undefined;
|
|
378
|
+
|
|
379
|
+
return { value, address, script, tapInternalKey };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private static writeSignerMappings(
|
|
383
|
+
writer: BinaryWriter,
|
|
384
|
+
mappings: SerializedSignerMapping[],
|
|
385
|
+
): void {
|
|
386
|
+
writer.writeU16(mappings.length);
|
|
387
|
+
for (const mapping of mappings) {
|
|
388
|
+
writer.writeStringWithLength(mapping.address);
|
|
389
|
+
writer.writeU16(mapping.inputIndices.length);
|
|
390
|
+
for (const idx of mapping.inputIndices) {
|
|
391
|
+
writer.writeU16(idx);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private static readSignerMappings(reader: BinaryReader): SerializedSignerMapping[] {
|
|
397
|
+
const count = reader.readU16();
|
|
398
|
+
const mappings: SerializedSignerMapping[] = [];
|
|
399
|
+
for (let i = 0; i < count; i++) {
|
|
400
|
+
const address = reader.readStringWithLength();
|
|
401
|
+
const indicesCount = reader.readU16();
|
|
402
|
+
const inputIndices: number[] = [];
|
|
403
|
+
for (let j = 0; j < indicesCount; j++) {
|
|
404
|
+
inputIndices.push(reader.readU16());
|
|
405
|
+
}
|
|
406
|
+
mappings.push({ address, inputIndices });
|
|
407
|
+
}
|
|
408
|
+
return mappings;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private static writeTypeSpecificData(writer: BinaryWriter, data: TypeSpecificData): void {
|
|
412
|
+
switch (data.type) {
|
|
413
|
+
case TransactionType.FUNDING:
|
|
414
|
+
this.writeFundingData(writer, data);
|
|
415
|
+
break;
|
|
416
|
+
case TransactionType.DEPLOYMENT:
|
|
417
|
+
this.writeDeploymentData(writer, data);
|
|
418
|
+
break;
|
|
419
|
+
case TransactionType.INTERACTION:
|
|
420
|
+
this.writeInteractionData(writer, data);
|
|
421
|
+
break;
|
|
422
|
+
case TransactionType.MULTI_SIG:
|
|
423
|
+
this.writeMultiSigData(writer, data);
|
|
424
|
+
break;
|
|
425
|
+
case TransactionType.CUSTOM_CODE:
|
|
426
|
+
this.writeCustomScriptData(writer, data);
|
|
427
|
+
break;
|
|
428
|
+
case TransactionType.CANCEL:
|
|
429
|
+
this.writeCancelData(writer, data);
|
|
430
|
+
break;
|
|
431
|
+
default:
|
|
432
|
+
throw new Error(`Unsupported transaction type: ${(data as TypeSpecificData).type}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private static readTypeSpecificData(
|
|
437
|
+
reader: BinaryReader,
|
|
438
|
+
type: TransactionType,
|
|
439
|
+
): TypeSpecificData {
|
|
440
|
+
switch (type) {
|
|
441
|
+
case TransactionType.FUNDING:
|
|
442
|
+
return this.readFundingData(reader);
|
|
443
|
+
case TransactionType.DEPLOYMENT:
|
|
444
|
+
return this.readDeploymentData(reader);
|
|
445
|
+
case TransactionType.INTERACTION:
|
|
446
|
+
return this.readInteractionData(reader);
|
|
447
|
+
case TransactionType.MULTI_SIG:
|
|
448
|
+
return this.readMultiSigData(reader);
|
|
449
|
+
case TransactionType.CUSTOM_CODE:
|
|
450
|
+
return this.readCustomScriptData(reader);
|
|
451
|
+
case TransactionType.CANCEL:
|
|
452
|
+
return this.readCancelData(reader);
|
|
453
|
+
default:
|
|
454
|
+
throw new Error(`Unsupported transaction type: ${type}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Funding
|
|
459
|
+
private static writeFundingData(writer: BinaryWriter, data: FundingSpecificData): void {
|
|
460
|
+
writer.writeU64(BigInt(data.amount));
|
|
461
|
+
writer.writeU16(data.splitInputsInto);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private static readFundingData(reader: BinaryReader): FundingSpecificData {
|
|
465
|
+
return {
|
|
466
|
+
type: TransactionType.FUNDING,
|
|
467
|
+
amount: reader.readU64().toString(),
|
|
468
|
+
splitInputsInto: reader.readU16(),
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Deployment
|
|
473
|
+
private static writeDeploymentData(writer: BinaryWriter, data: DeploymentSpecificData): void {
|
|
474
|
+
writer.writeBytesWithLength(Buffer.from(data.bytecode, 'hex'));
|
|
475
|
+
writer.writeBoolean(data.calldata !== undefined);
|
|
476
|
+
if (data.calldata !== undefined) {
|
|
477
|
+
writer.writeBytesWithLength(Buffer.from(data.calldata, 'hex'));
|
|
478
|
+
}
|
|
479
|
+
this.writeChallenge(writer, data.challenge);
|
|
480
|
+
writer.writeBoolean(data.revealMLDSAPublicKey ?? false);
|
|
481
|
+
writer.writeBoolean(data.linkMLDSAPublicKeyToAddress ?? false);
|
|
482
|
+
writer.writeBoolean(data.hashedPublicKey !== undefined);
|
|
483
|
+
if (data.hashedPublicKey !== undefined) {
|
|
484
|
+
writer.writeBytesWithLength(Buffer.from(data.hashedPublicKey, 'hex'));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private static readDeploymentData(reader: BinaryReader): DeploymentSpecificData {
|
|
489
|
+
const bytecode = Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
490
|
+
const hasCalldata = reader.readBoolean();
|
|
491
|
+
const calldata = hasCalldata
|
|
492
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
493
|
+
: undefined;
|
|
494
|
+
const challenge = this.readChallenge(reader);
|
|
495
|
+
const revealMLDSAPublicKey = reader.readBoolean();
|
|
496
|
+
const linkMLDSAPublicKeyToAddress = reader.readBoolean();
|
|
497
|
+
const hasHashedPublicKey = reader.readBoolean();
|
|
498
|
+
const hashedPublicKey = hasHashedPublicKey
|
|
499
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
500
|
+
: undefined;
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
type: TransactionType.DEPLOYMENT,
|
|
504
|
+
bytecode,
|
|
505
|
+
calldata,
|
|
506
|
+
challenge,
|
|
507
|
+
revealMLDSAPublicKey,
|
|
508
|
+
linkMLDSAPublicKeyToAddress,
|
|
509
|
+
hashedPublicKey,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Interaction
|
|
514
|
+
private static writeInteractionData(writer: BinaryWriter, data: InteractionSpecificData): void {
|
|
515
|
+
writer.writeBytesWithLength(Buffer.from(data.calldata, 'hex'));
|
|
516
|
+
writer.writeBoolean(data.contract !== undefined);
|
|
517
|
+
if (data.contract !== undefined) {
|
|
518
|
+
writer.writeStringWithLength(data.contract);
|
|
519
|
+
}
|
|
520
|
+
this.writeChallenge(writer, data.challenge);
|
|
521
|
+
writer.writeBoolean(data.loadedStorage !== undefined);
|
|
522
|
+
if (data.loadedStorage !== undefined) {
|
|
523
|
+
this.writeLoadedStorage(writer, data.loadedStorage);
|
|
524
|
+
}
|
|
525
|
+
writer.writeBoolean(data.isCancellation ?? false);
|
|
526
|
+
writer.writeBoolean(data.disableAutoRefund ?? false);
|
|
527
|
+
writer.writeBoolean(data.revealMLDSAPublicKey ?? false);
|
|
528
|
+
writer.writeBoolean(data.linkMLDSAPublicKeyToAddress ?? false);
|
|
529
|
+
writer.writeBoolean(data.hashedPublicKey !== undefined);
|
|
530
|
+
if (data.hashedPublicKey !== undefined) {
|
|
531
|
+
writer.writeBytesWithLength(Buffer.from(data.hashedPublicKey, 'hex'));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private static readInteractionData(reader: BinaryReader): InteractionSpecificData {
|
|
536
|
+
const calldata = Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
537
|
+
const hasContract = reader.readBoolean();
|
|
538
|
+
const contract = hasContract ? reader.readStringWithLength() : undefined;
|
|
539
|
+
const challenge = this.readChallenge(reader);
|
|
540
|
+
const hasLoadedStorage = reader.readBoolean();
|
|
541
|
+
const loadedStorage = hasLoadedStorage ? this.readLoadedStorage(reader) : undefined;
|
|
542
|
+
const isCancellation = reader.readBoolean();
|
|
543
|
+
const disableAutoRefund = reader.readBoolean();
|
|
544
|
+
const revealMLDSAPublicKey = reader.readBoolean();
|
|
545
|
+
const linkMLDSAPublicKeyToAddress = reader.readBoolean();
|
|
546
|
+
const hasHashedPublicKey = reader.readBoolean();
|
|
547
|
+
const hashedPublicKey = hasHashedPublicKey
|
|
548
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
549
|
+
: undefined;
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
type: TransactionType.INTERACTION,
|
|
553
|
+
calldata,
|
|
554
|
+
contract,
|
|
555
|
+
challenge,
|
|
556
|
+
loadedStorage,
|
|
557
|
+
isCancellation,
|
|
558
|
+
disableAutoRefund,
|
|
559
|
+
revealMLDSAPublicKey,
|
|
560
|
+
linkMLDSAPublicKeyToAddress,
|
|
561
|
+
hashedPublicKey,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// MultiSig
|
|
566
|
+
private static writeMultiSigData(writer: BinaryWriter, data: MultiSigSpecificData): void {
|
|
567
|
+
writer.writeU16(data.pubkeys.length);
|
|
568
|
+
for (const pubkey of data.pubkeys) {
|
|
569
|
+
writer.writeBytesWithLength(Buffer.from(pubkey, 'hex'));
|
|
570
|
+
}
|
|
571
|
+
writer.writeU8(data.minimumSignatures);
|
|
572
|
+
writer.writeStringWithLength(data.receiver);
|
|
573
|
+
writer.writeU64(BigInt(data.requestedAmount));
|
|
574
|
+
writer.writeStringWithLength(data.refundVault);
|
|
575
|
+
writer.writeU16(data.originalInputCount);
|
|
576
|
+
writer.writeBoolean(data.existingPsbtBase64 !== undefined);
|
|
577
|
+
if (data.existingPsbtBase64 !== undefined) {
|
|
578
|
+
writer.writeStringWithLength(data.existingPsbtBase64);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private static readMultiSigData(reader: BinaryReader): MultiSigSpecificData {
|
|
583
|
+
const pubkeysCount = reader.readU16();
|
|
584
|
+
const pubkeys: string[] = [];
|
|
585
|
+
for (let i = 0; i < pubkeysCount; i++) {
|
|
586
|
+
pubkeys.push(Buffer.from(reader.readBytesWithLength()).toString('hex'));
|
|
587
|
+
}
|
|
588
|
+
const minimumSignatures = reader.readU8();
|
|
589
|
+
const receiver = reader.readStringWithLength();
|
|
590
|
+
const requestedAmount = reader.readU64().toString();
|
|
591
|
+
const refundVault = reader.readStringWithLength();
|
|
592
|
+
const originalInputCount = reader.readU16();
|
|
593
|
+
const hasExistingPsbt = reader.readBoolean();
|
|
594
|
+
const existingPsbtBase64 = hasExistingPsbt ? reader.readStringWithLength() : undefined;
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
type: TransactionType.MULTI_SIG,
|
|
598
|
+
pubkeys,
|
|
599
|
+
minimumSignatures,
|
|
600
|
+
receiver,
|
|
601
|
+
requestedAmount,
|
|
602
|
+
refundVault,
|
|
603
|
+
originalInputCount,
|
|
604
|
+
existingPsbtBase64,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Custom Script
|
|
609
|
+
private static writeCustomScriptData(
|
|
610
|
+
writer: BinaryWriter,
|
|
611
|
+
data: CustomScriptSpecificData,
|
|
612
|
+
): void {
|
|
613
|
+
writer.writeU16(data.scriptElements.length);
|
|
614
|
+
for (const element of data.scriptElements) {
|
|
615
|
+
this.writeScriptElement(writer, element);
|
|
616
|
+
}
|
|
617
|
+
writer.writeU16(data.witnesses.length);
|
|
618
|
+
for (const witness of data.witnesses) {
|
|
619
|
+
writer.writeBytesWithLength(Buffer.from(witness, 'hex'));
|
|
620
|
+
}
|
|
621
|
+
writer.writeBoolean(data.annex !== undefined);
|
|
622
|
+
if (data.annex !== undefined) {
|
|
623
|
+
writer.writeBytesWithLength(Buffer.from(data.annex, 'hex'));
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private static writeScriptElement(
|
|
628
|
+
writer: BinaryWriter,
|
|
629
|
+
element: SerializedScriptElement,
|
|
630
|
+
): void {
|
|
631
|
+
writer.writeU8(element.elementType === 'buffer' ? 0 : 1);
|
|
632
|
+
if (element.elementType === 'buffer') {
|
|
633
|
+
writer.writeBytesWithLength(Buffer.from(element.value as string, 'hex'));
|
|
634
|
+
} else {
|
|
635
|
+
writer.writeU32(element.value as number);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private static readCustomScriptData(reader: BinaryReader): CustomScriptSpecificData {
|
|
640
|
+
const elementsCount = reader.readU16();
|
|
641
|
+
const scriptElements: SerializedScriptElement[] = [];
|
|
642
|
+
for (let i = 0; i < elementsCount; i++) {
|
|
643
|
+
scriptElements.push(this.readScriptElement(reader));
|
|
644
|
+
}
|
|
645
|
+
const witnessesCount = reader.readU16();
|
|
646
|
+
const witnesses: string[] = [];
|
|
647
|
+
for (let i = 0; i < witnessesCount; i++) {
|
|
648
|
+
witnesses.push(Buffer.from(reader.readBytesWithLength()).toString('hex'));
|
|
649
|
+
}
|
|
650
|
+
const hasAnnex = reader.readBoolean();
|
|
651
|
+
const annex = hasAnnex
|
|
652
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
653
|
+
: undefined;
|
|
654
|
+
|
|
655
|
+
return {
|
|
656
|
+
type: TransactionType.CUSTOM_CODE,
|
|
657
|
+
scriptElements,
|
|
658
|
+
witnesses,
|
|
659
|
+
annex,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
private static readScriptElement(reader: BinaryReader): SerializedScriptElement {
|
|
664
|
+
const typeFlag = reader.readU8();
|
|
665
|
+
if (typeFlag === 0) {
|
|
666
|
+
return {
|
|
667
|
+
elementType: 'buffer',
|
|
668
|
+
value: Buffer.from(reader.readBytesWithLength()).toString('hex'),
|
|
669
|
+
};
|
|
670
|
+
} else {
|
|
671
|
+
return {
|
|
672
|
+
elementType: 'opcode',
|
|
673
|
+
value: reader.readU32(),
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Cancel
|
|
679
|
+
private static writeCancelData(writer: BinaryWriter, data: CancelSpecificData): void {
|
|
680
|
+
writer.writeBytesWithLength(Buffer.from(data.compiledTargetScript, 'hex'));
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
private static readCancelData(reader: BinaryReader): CancelSpecificData {
|
|
684
|
+
return {
|
|
685
|
+
type: TransactionType.CANCEL,
|
|
686
|
+
compiledTargetScript: Buffer.from(reader.readBytesWithLength()).toString('hex'),
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private static writeChallenge(writer: BinaryWriter, challenge: RawChallenge): void {
|
|
691
|
+
writer.writeU64(BigInt(challenge.epochNumber));
|
|
692
|
+
writer.writeStringWithLength(challenge.mldsaPublicKey);
|
|
693
|
+
writer.writeStringWithLength(challenge.legacyPublicKey);
|
|
694
|
+
writer.writeBytesWithLength(Buffer.from(challenge.solution.replace('0x', ''), 'hex'));
|
|
695
|
+
writer.writeBytesWithLength(Buffer.from(challenge.salt.replace('0x', ''), 'hex'));
|
|
696
|
+
writer.writeBytesWithLength(Buffer.from(challenge.graffiti.replace('0x', ''), 'hex'));
|
|
697
|
+
writer.writeU8(challenge.difficulty);
|
|
698
|
+
|
|
699
|
+
// Verification
|
|
700
|
+
this.writeChallengeVerification(writer, challenge.verification);
|
|
701
|
+
|
|
702
|
+
// Optional submission
|
|
703
|
+
writer.writeBoolean(challenge.submission !== undefined);
|
|
704
|
+
if (challenge.submission !== undefined) {
|
|
705
|
+
writer.writeStringWithLength(challenge.submission.mldsaPublicKey);
|
|
706
|
+
writer.writeStringWithLength(challenge.submission.legacyPublicKey);
|
|
707
|
+
writer.writeBytesWithLength(
|
|
708
|
+
Buffer.from(challenge.submission.solution.replace('0x', ''), 'hex'),
|
|
709
|
+
);
|
|
710
|
+
writer.writeBoolean(challenge.submission.graffiti !== undefined);
|
|
711
|
+
if (challenge.submission.graffiti !== undefined) {
|
|
712
|
+
writer.writeBytesWithLength(
|
|
713
|
+
Buffer.from(challenge.submission.graffiti.replace('0x', ''), 'hex'),
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
writer.writeBytesWithLength(
|
|
717
|
+
Buffer.from(challenge.submission.signature.replace('0x', ''), 'hex'),
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
private static writeChallengeVerification(
|
|
723
|
+
writer: BinaryWriter,
|
|
724
|
+
verification: RawChallengeVerification,
|
|
725
|
+
): void {
|
|
726
|
+
writer.writeBytesWithLength(Buffer.from(verification.epochHash.replace('0x', ''), 'hex'));
|
|
727
|
+
writer.writeBytesWithLength(Buffer.from(verification.epochRoot.replace('0x', ''), 'hex'));
|
|
728
|
+
writer.writeBytesWithLength(Buffer.from(verification.targetHash.replace('0x', ''), 'hex'));
|
|
729
|
+
writer.writeBytesWithLength(
|
|
730
|
+
Buffer.from(verification.targetChecksum.replace('0x', ''), 'hex'),
|
|
731
|
+
);
|
|
732
|
+
writer.writeU64(BigInt(verification.startBlock));
|
|
733
|
+
writer.writeU64(BigInt(verification.endBlock));
|
|
734
|
+
writer.writeU16(verification.proofs.length);
|
|
735
|
+
for (const proof of verification.proofs) {
|
|
736
|
+
writer.writeBytesWithLength(Buffer.from(proof.replace('0x', ''), 'hex'));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
private static readChallenge(reader: BinaryReader): RawChallenge {
|
|
741
|
+
const epochNumber = reader.readU64().toString();
|
|
742
|
+
const mldsaPublicKey = reader.readStringWithLength();
|
|
743
|
+
const legacyPublicKey = reader.readStringWithLength();
|
|
744
|
+
const solution = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
745
|
+
const salt = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
746
|
+
const graffiti = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
747
|
+
const difficulty = reader.readU8();
|
|
748
|
+
|
|
749
|
+
const verification = this.readChallengeVerification(reader);
|
|
750
|
+
|
|
751
|
+
const hasSubmission = reader.readBoolean();
|
|
752
|
+
let submission;
|
|
753
|
+
if (hasSubmission) {
|
|
754
|
+
const subMldsaPublicKey = reader.readStringWithLength();
|
|
755
|
+
const subLegacyPublicKey = reader.readStringWithLength();
|
|
756
|
+
const subSolution = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
757
|
+
const hasGraffiti = reader.readBoolean();
|
|
758
|
+
const subGraffiti = hasGraffiti
|
|
759
|
+
? '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
760
|
+
: undefined;
|
|
761
|
+
const subSignature = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
762
|
+
|
|
763
|
+
submission = {
|
|
764
|
+
mldsaPublicKey: subMldsaPublicKey,
|
|
765
|
+
legacyPublicKey: subLegacyPublicKey,
|
|
766
|
+
solution: subSolution,
|
|
767
|
+
graffiti: subGraffiti,
|
|
768
|
+
signature: subSignature,
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return {
|
|
773
|
+
epochNumber,
|
|
774
|
+
mldsaPublicKey,
|
|
775
|
+
legacyPublicKey,
|
|
776
|
+
solution,
|
|
777
|
+
salt,
|
|
778
|
+
graffiti,
|
|
779
|
+
difficulty,
|
|
780
|
+
verification,
|
|
781
|
+
submission,
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
private static readChallengeVerification(reader: BinaryReader): RawChallengeVerification {
|
|
786
|
+
const epochHash = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
787
|
+
const epochRoot = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
788
|
+
const targetHash = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
789
|
+
const targetChecksum = '0x' + Buffer.from(reader.readBytesWithLength()).toString('hex');
|
|
790
|
+
const startBlock = reader.readU64().toString();
|
|
791
|
+
const endBlock = reader.readU64().toString();
|
|
792
|
+
const proofsCount = reader.readU16();
|
|
793
|
+
const proofs: string[] = [];
|
|
794
|
+
for (let i = 0; i < proofsCount; i++) {
|
|
795
|
+
proofs.push('0x' + Buffer.from(reader.readBytesWithLength()).toString('hex'));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return {
|
|
799
|
+
epochHash,
|
|
800
|
+
epochRoot,
|
|
801
|
+
targetHash,
|
|
802
|
+
targetChecksum,
|
|
803
|
+
startBlock,
|
|
804
|
+
endBlock,
|
|
805
|
+
proofs,
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
private static writeLoadedStorage(
|
|
810
|
+
writer: BinaryWriter,
|
|
811
|
+
storage: SerializedLoadedStorage,
|
|
812
|
+
): void {
|
|
813
|
+
const keys = Object.keys(storage);
|
|
814
|
+
writer.writeU16(keys.length);
|
|
815
|
+
for (const key of keys) {
|
|
816
|
+
writer.writeStringWithLength(key);
|
|
817
|
+
writer.writeStringArray(storage[key]);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
private static readLoadedStorage(reader: BinaryReader): SerializedLoadedStorage {
|
|
822
|
+
const count = reader.readU16();
|
|
823
|
+
const storage: SerializedLoadedStorage = {};
|
|
824
|
+
for (let i = 0; i < count; i++) {
|
|
825
|
+
const key = reader.readStringWithLength();
|
|
826
|
+
storage[key] = reader.readStringArray();
|
|
827
|
+
}
|
|
828
|
+
return storage;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
private static writePrecomputedData(writer: BinaryWriter, data: PrecomputedData): void {
|
|
832
|
+
writer.writeBoolean(data.compiledTargetScript !== undefined);
|
|
833
|
+
if (data.compiledTargetScript !== undefined) {
|
|
834
|
+
writer.writeBytesWithLength(Buffer.from(data.compiledTargetScript, 'hex'));
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
writer.writeBoolean(data.randomBytes !== undefined);
|
|
838
|
+
if (data.randomBytes !== undefined) {
|
|
839
|
+
writer.writeBytesWithLength(Buffer.from(data.randomBytes, 'hex'));
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
writer.writeBoolean(data.estimatedFees !== undefined);
|
|
843
|
+
if (data.estimatedFees !== undefined) {
|
|
844
|
+
writer.writeU64(BigInt(data.estimatedFees));
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
writer.writeBoolean(data.contractSeed !== undefined);
|
|
848
|
+
if (data.contractSeed !== undefined) {
|
|
849
|
+
writer.writeStringWithLength(data.contractSeed);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
writer.writeBoolean(data.contractAddress !== undefined);
|
|
853
|
+
if (data.contractAddress !== undefined) {
|
|
854
|
+
writer.writeStringWithLength(data.contractAddress);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
private static readPrecomputedData(reader: BinaryReader): PrecomputedData {
|
|
859
|
+
const hasCompiledTargetScript = reader.readBoolean();
|
|
860
|
+
const compiledTargetScript = hasCompiledTargetScript
|
|
861
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
862
|
+
: undefined;
|
|
863
|
+
|
|
864
|
+
const hasRandomBytes = reader.readBoolean();
|
|
865
|
+
const randomBytes = hasRandomBytes
|
|
866
|
+
? Buffer.from(reader.readBytesWithLength()).toString('hex')
|
|
867
|
+
: undefined;
|
|
868
|
+
|
|
869
|
+
const hasEstimatedFees = reader.readBoolean();
|
|
870
|
+
const estimatedFees = hasEstimatedFees ? reader.readU64().toString() : undefined;
|
|
871
|
+
|
|
872
|
+
const hasContractSeed = reader.readBoolean();
|
|
873
|
+
const contractSeed = hasContractSeed ? reader.readStringWithLength() : undefined;
|
|
874
|
+
|
|
875
|
+
const hasContractAddress = reader.readBoolean();
|
|
876
|
+
const contractAddress = hasContractAddress ? reader.readStringWithLength() : undefined;
|
|
877
|
+
|
|
878
|
+
return {
|
|
879
|
+
compiledTargetScript,
|
|
880
|
+
randomBytes,
|
|
881
|
+
estimatedFees,
|
|
882
|
+
contractSeed,
|
|
883
|
+
contractAddress,
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Calculate double SHA256 checksum (Bitcoin standard)
|
|
889
|
+
*/
|
|
890
|
+
private static calculateChecksum(data: Buffer): Buffer {
|
|
891
|
+
const hash1 = createHash('sha256').update(data).digest();
|
|
892
|
+
return createHash('sha256').update(hash1).digest();
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
private static networkNameToU8(name: 'mainnet' | 'testnet' | 'regtest'): number {
|
|
896
|
+
switch (name) {
|
|
897
|
+
case 'mainnet':
|
|
898
|
+
return 0;
|
|
899
|
+
case 'testnet':
|
|
900
|
+
return 1;
|
|
901
|
+
case 'regtest':
|
|
902
|
+
return 2;
|
|
903
|
+
default:
|
|
904
|
+
throw new Error(`Unknown network: ${name}`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
private static u8ToNetworkName(value: number): 'mainnet' | 'testnet' | 'regtest' {
|
|
909
|
+
switch (value) {
|
|
910
|
+
case 0:
|
|
911
|
+
return 'mainnet';
|
|
912
|
+
case 1:
|
|
913
|
+
return 'testnet';
|
|
914
|
+
case 2:
|
|
915
|
+
return 'regtest';
|
|
916
|
+
default:
|
|
917
|
+
throw new Error(`Unknown network value: ${value}`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|