@chainvue/verus-sdk 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/VerusSDK.d.ts +4 -1
- package/dist/VerusSDK.d.ts.map +1 -1
- package/dist/VerusSDK.js +6 -2
- package/dist/VerusSDK.js.map +1 -1
- package/dist/assemble/assembler.d.ts +66 -0
- package/dist/assemble/assembler.d.ts.map +1 -0
- package/dist/assemble/assembler.js +155 -0
- package/dist/assemble/assembler.js.map +1 -0
- package/dist/assemble/fundedIdentityUpdate.d.ts +35 -0
- package/dist/assemble/fundedIdentityUpdate.d.ts.map +1 -0
- package/dist/assemble/fundedIdentityUpdate.js +114 -0
- package/dist/assemble/fundedIdentityUpdate.js.map +1 -0
- package/dist/bundle.js +29248 -29255
- package/dist/currency/index.d.ts.map +1 -1
- package/dist/currency/index.js +32 -66
- package/dist/currency/index.js.map +1 -1
- package/dist/fork/boundary.d.ts +19 -0
- package/dist/fork/boundary.d.ts.map +1 -0
- package/dist/fork/boundary.js +49 -0
- package/dist/fork/boundary.js.map +1 -0
- package/dist/fork-shims.d.ts +7 -0
- package/dist/identity/index.d.ts +18 -17
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/identity/index.js +315 -307
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/public.d.ts +21 -0
- package/dist/identity/public.d.ts.map +1 -0
- package/dist/identity/public.js +38 -0
- package/dist/identity/public.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +9 -0
- package/dist/keys/index.js.map +1 -1
- package/dist/message/index.js +7 -7
- package/dist/message/index.js.map +1 -1
- package/dist/signing/index.d.ts +13 -4
- package/dist/signing/index.d.ts.map +1 -1
- package/dist/signing/index.js +27 -14
- package/dist/signing/index.js.map +1 -1
- package/dist/transfer/index.d.ts.map +1 -1
- package/dist/transfer/index.js +40 -56
- package/dist/transfer/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -48
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +6 -11
- package/dist/utils/index.js.map +1 -1
- package/dist/utxo/index.d.ts.map +1 -1
- package/dist/utxo/index.js +12 -4
- package/dist/utxo/index.js.map +1 -1
- package/package.json +1 -1
package/dist/identity/index.js
CHANGED
|
@@ -38,25 +38,30 @@ exports.buildAndSignRegistration = buildAndSignRegistration;
|
|
|
38
38
|
exports.buildAndSignIdentityUpdate = buildAndSignIdentityUpdate;
|
|
39
39
|
const crypto_1 = require("crypto");
|
|
40
40
|
const bn_js_1 = __importDefault(require("bn.js"));
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
Object.defineProperty(exports, "nameAndParentAddrToIAddr", { enumerable: true, get: function () { return
|
|
45
|
-
const
|
|
41
|
+
const boundary_js_1 = require("../fork/boundary.js");
|
|
42
|
+
const boundary_js_2 = require("../fork/boundary.js");
|
|
43
|
+
const boundary_js_3 = require("../fork/boundary.js");
|
|
44
|
+
Object.defineProperty(exports, "nameAndParentAddrToIAddr", { enumerable: true, get: function () { return boundary_js_3.nameAndParentAddrToIAddr; } });
|
|
45
|
+
const boundary_js_4 = require("../fork/boundary.js");
|
|
46
46
|
const index_js_1 = require("../constants/index.js");
|
|
47
47
|
const index_js_2 = require("../utils/index.js");
|
|
48
48
|
const index_js_3 = require("../signing/index.js");
|
|
49
|
-
const
|
|
49
|
+
const assembler_js_1 = require("../assemble/assembler.js");
|
|
50
|
+
const fundedIdentityUpdate_js_1 = require("../assemble/fundedIdentityUpdate.js");
|
|
50
51
|
const brands_js_1 = require("../core/brands.js");
|
|
51
52
|
const errors_js_1 = require("../errors.js");
|
|
52
|
-
const
|
|
53
|
-
const { createUnfundedIdentityUpdate
|
|
53
|
+
const index_js_4 = require("../keys/index.js");
|
|
54
|
+
const { createUnfundedIdentityUpdate } = boundary_js_4.smarttxs;
|
|
54
55
|
const HASH160_BYTE_LENGTH = 20;
|
|
55
56
|
const HASH256_BYTE_LENGTH = 32;
|
|
56
57
|
/** Null identity hash (20 zero bytes) */
|
|
57
58
|
const NULL_ID_HASH = Buffer.alloc(HASH160_BYTE_LENGTH, 0);
|
|
58
59
|
/** Eval code for CAdvancedNameReservation (sub-ID) */
|
|
59
60
|
const EVAL_IDENTITY_ADVANCEDRESERVATION = 10;
|
|
61
|
+
/** Consensus maximum identity unlock delay (blocks) — CIdentity::MAX_UNLOCK_DELAY, ~22 years. */
|
|
62
|
+
const MAX_UNLOCK_DELAY = 11563200;
|
|
63
|
+
/** ~1 year of blocks (1 block/min); a lock delay above this needs an explicit opt-in. */
|
|
64
|
+
const LOCK_DELAY_SANITY_BLOCKS = 525600;
|
|
60
65
|
// ─── Script / Hash Builders ────────────────────────────────────────
|
|
61
66
|
/**
|
|
62
67
|
* Assert a base58check address has the expected version byte.
|
|
@@ -71,7 +76,7 @@ const EVAL_IDENTITY_ADVANCEDRESERVATION = 10;
|
|
|
71
76
|
function assertAddressVersion(address, expectedVersion, label) {
|
|
72
77
|
let version;
|
|
73
78
|
try {
|
|
74
|
-
version = (0,
|
|
79
|
+
version = (0, boundary_js_3.fromBase58Check)(address).version;
|
|
75
80
|
}
|
|
76
81
|
catch {
|
|
77
82
|
throw new errors_js_1.TransactionBuildError(`${label} is not a valid base58check address: ${JSON.stringify(address)}`);
|
|
@@ -89,7 +94,7 @@ function assertAddressVersion(address, expectedVersion, label) {
|
|
|
89
94
|
* primary authority (currency define; update/lock/unlock check inline).
|
|
90
95
|
*/
|
|
91
96
|
function assertWifIsPrimary(wif, identity, network) {
|
|
92
|
-
const signerAddress =
|
|
97
|
+
const signerAddress = boundary_js_4.ECPair.fromWIF(wif, network).getAddress();
|
|
93
98
|
const currentPrimaries = (identity.primary_addresses ?? []).map((k) => k.toAddress());
|
|
94
99
|
if (!currentPrimaries.includes(signerAddress)) {
|
|
95
100
|
throw new errors_js_1.TransactionBuildError(`the provided WIF (${signerAddress}) is not among the identity's primary addresses ` +
|
|
@@ -146,9 +151,9 @@ function calculateCommitmentHash(serializedReservation) {
|
|
|
146
151
|
* Serialize a CCommitmentHash
|
|
147
152
|
*/
|
|
148
153
|
function serializeCommitmentHash(hash) {
|
|
149
|
-
const tokenOutput = new
|
|
154
|
+
const tokenOutput = new boundary_js_1.TokenOutput({
|
|
150
155
|
version: new bn_js_1.default(0),
|
|
151
|
-
values: new
|
|
156
|
+
values: new boundary_js_1.CurrencyValueMap(),
|
|
152
157
|
});
|
|
153
158
|
const tokenBuf = tokenOutput.toBuffer();
|
|
154
159
|
return Buffer.concat([tokenBuf, hash]);
|
|
@@ -157,43 +162,43 @@ function serializeCommitmentHash(hash) {
|
|
|
157
162
|
* Build the commitment output script
|
|
158
163
|
*/
|
|
159
164
|
function buildCommitmentScript(commitmentHashBuf, controlAddress) {
|
|
160
|
-
const controlDest = new
|
|
161
|
-
const master = new
|
|
165
|
+
const controlDest = new boundary_js_1.TxDestination(boundary_js_1.KeyID.fromAddress(controlAddress));
|
|
166
|
+
const master = new boundary_js_1.OptCCParams({
|
|
162
167
|
version: new bn_js_1.default(3),
|
|
163
|
-
eval_code: new bn_js_1.default(
|
|
168
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE),
|
|
164
169
|
m: new bn_js_1.default(1),
|
|
165
170
|
n: new bn_js_1.default(1),
|
|
166
171
|
destinations: [controlDest],
|
|
167
172
|
vdata: [],
|
|
168
173
|
});
|
|
169
|
-
const params = new
|
|
174
|
+
const params = new boundary_js_1.OptCCParams({
|
|
170
175
|
version: new bn_js_1.default(3),
|
|
171
|
-
eval_code: new bn_js_1.default(
|
|
176
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_IDENTITY_COMMITMENT),
|
|
172
177
|
m: new bn_js_1.default(1),
|
|
173
178
|
n: new bn_js_1.default(1),
|
|
174
179
|
destinations: [controlDest],
|
|
175
180
|
vdata: [commitmentHashBuf],
|
|
176
181
|
});
|
|
177
|
-
const script = new
|
|
182
|
+
const script = new boundary_js_1.SmartTransactionScript(master, params);
|
|
178
183
|
return script.toBuffer();
|
|
179
184
|
}
|
|
180
185
|
/**
|
|
181
186
|
* Build the name reservation output script
|
|
182
187
|
*/
|
|
183
188
|
function buildReservationScript(newIdentityIAddress, serializedReservation, isAdvanced = false) {
|
|
184
|
-
const identityDest = new
|
|
189
|
+
const identityDest = new boundary_js_1.TxDestination(boundary_js_1.IdentityID.fromAddress(newIdentityIAddress));
|
|
185
190
|
const evalCode = isAdvanced
|
|
186
191
|
? EVAL_IDENTITY_ADVANCEDRESERVATION
|
|
187
|
-
:
|
|
188
|
-
const master = new
|
|
192
|
+
: boundary_js_2.EVALS.EVAL_IDENTITY_RESERVATION;
|
|
193
|
+
const master = new boundary_js_1.OptCCParams({
|
|
189
194
|
version: new bn_js_1.default(3),
|
|
190
|
-
eval_code: new bn_js_1.default(
|
|
195
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE),
|
|
191
196
|
m: new bn_js_1.default(1),
|
|
192
197
|
n: new bn_js_1.default(1),
|
|
193
198
|
destinations: [identityDest],
|
|
194
199
|
vdata: [],
|
|
195
200
|
});
|
|
196
|
-
const params = new
|
|
201
|
+
const params = new boundary_js_1.OptCCParams({
|
|
197
202
|
version: new bn_js_1.default(3),
|
|
198
203
|
eval_code: new bn_js_1.default(evalCode),
|
|
199
204
|
m: new bn_js_1.default(1),
|
|
@@ -201,14 +206,14 @@ function buildReservationScript(newIdentityIAddress, serializedReservation, isAd
|
|
|
201
206
|
destinations: [identityDest],
|
|
202
207
|
vdata: [serializedReservation],
|
|
203
208
|
});
|
|
204
|
-
const script = new
|
|
209
|
+
const script = new boundary_js_1.SmartTransactionScript(master, params);
|
|
205
210
|
return script.toBuffer();
|
|
206
211
|
}
|
|
207
212
|
/**
|
|
208
213
|
* Build an identity definition output script from an Identity object
|
|
209
214
|
*/
|
|
210
215
|
function buildIdentityScript(identity) {
|
|
211
|
-
const identityScript =
|
|
216
|
+
const identityScript = boundary_js_1.IdentityScript.fromIdentity(identity);
|
|
212
217
|
return identityScript.toBuffer();
|
|
213
218
|
}
|
|
214
219
|
/**
|
|
@@ -223,7 +228,7 @@ function deriveIdentityAddress(name, parentIAddress) {
|
|
|
223
228
|
if (parentIAddress) {
|
|
224
229
|
assertAddressVersion(parentIAddress, index_js_1.I_ADDR_VERSION, 'parent');
|
|
225
230
|
}
|
|
226
|
-
return (0,
|
|
231
|
+
return (0, boundary_js_3.nameAndParentAddrToIAddr)(name, parentIAddress || undefined);
|
|
227
232
|
}
|
|
228
233
|
/**
|
|
229
234
|
* Determine if a parent is the VRSC root system
|
|
@@ -251,6 +256,15 @@ salt = generateSalt()) {
|
|
|
251
256
|
// registration and the commitment fee is wasted). Require an i-address.
|
|
252
257
|
if (referralIAddress) {
|
|
253
258
|
assertAddressVersion(referralIAddress, index_js_1.I_ADDR_VERSION, 'referral');
|
|
259
|
+
// A sub-ID commitment (non-VRSC parent) would bake the referral into the
|
|
260
|
+
// advanced reservation hash, but _buildSubIdRegistration emits NO referral
|
|
261
|
+
// payout — the registration would then mismatch its own committed hash and
|
|
262
|
+
// the daemon rejects it, wasting the commitment fee. Fail closed here rather
|
|
263
|
+
// than committing a referral this SDK cannot honor.
|
|
264
|
+
if (!isVRSCParent(parentIAddress, network)) {
|
|
265
|
+
throw new errors_js_1.TransactionBuildError('referrals are not supported for sub-ID (non-VRSC-parent) registrations in this SDK; ' +
|
|
266
|
+
'omit the referral for a sub-ID name commitment.');
|
|
267
|
+
}
|
|
254
268
|
}
|
|
255
269
|
const referralHash = referralIAddress
|
|
256
270
|
? (0, index_js_2.iAddressToHash)(referralIAddress)
|
|
@@ -309,26 +323,26 @@ function identityPaymentScript(iAddress) {
|
|
|
309
323
|
* raw string here is exactly the hole that let a wrong-kind address through.
|
|
310
324
|
*/
|
|
311
325
|
function buildReferralPaymentScript(iAddress) {
|
|
312
|
-
const identityDest = new
|
|
326
|
+
const identityDest = new boundary_js_1.TxDestination(boundary_js_1.IdentityID.fromAddress(iAddress));
|
|
313
327
|
// Master: EVAL_NONE outputs have empty master (no index destinations)
|
|
314
328
|
// This matches the daemon's MakeMofNCCScript for EVAL_NONE condition objects
|
|
315
|
-
const master = new
|
|
329
|
+
const master = new boundary_js_1.OptCCParams({
|
|
316
330
|
version: new bn_js_1.default(3),
|
|
317
|
-
eval_code: new bn_js_1.default(
|
|
331
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE),
|
|
318
332
|
m: new bn_js_1.default(0),
|
|
319
333
|
n: new bn_js_1.default(0),
|
|
320
334
|
destinations: [],
|
|
321
335
|
vdata: [],
|
|
322
336
|
});
|
|
323
|
-
const params = new
|
|
337
|
+
const params = new boundary_js_1.OptCCParams({
|
|
324
338
|
version: new bn_js_1.default(3),
|
|
325
|
-
eval_code: new bn_js_1.default(
|
|
339
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE),
|
|
326
340
|
m: new bn_js_1.default(1),
|
|
327
341
|
n: new bn_js_1.default(1),
|
|
328
342
|
destinations: [identityDest],
|
|
329
343
|
vdata: [],
|
|
330
344
|
});
|
|
331
|
-
const script = new
|
|
345
|
+
const script = new boundary_js_1.SmartTransactionScript(master, params);
|
|
332
346
|
return script.toBuffer();
|
|
333
347
|
}
|
|
334
348
|
/**
|
|
@@ -351,17 +365,17 @@ function createIdentityObject(params) {
|
|
|
351
365
|
// R-address primary or an i-address authority is now a compile error at the
|
|
352
366
|
// call site, where the raw string is parsed.
|
|
353
367
|
validateMinSigs(params.minSigs ?? 1, params.primaryAddresses.length);
|
|
354
|
-
const primaryKeys = params.primaryAddresses.map(addr =>
|
|
355
|
-
const identity = new
|
|
356
|
-
version:
|
|
368
|
+
const primaryKeys = params.primaryAddresses.map(addr => boundary_js_1.KeyID.fromAddress(addr));
|
|
369
|
+
const identity = new boundary_js_1.Identity({
|
|
370
|
+
version: boundary_js_1.Identity.VERSION_CURRENT,
|
|
357
371
|
flags: new bn_js_1.default(0),
|
|
358
372
|
min_sigs: new bn_js_1.default(params.minSigs || 1),
|
|
359
373
|
primary_addresses: primaryKeys,
|
|
360
|
-
parent:
|
|
361
|
-
system_id:
|
|
374
|
+
parent: boundary_js_1.IdentityID.fromAddress(params.parentIAddress),
|
|
375
|
+
system_id: boundary_js_1.IdentityID.fromAddress(params.systemId),
|
|
362
376
|
name: params.name,
|
|
363
|
-
revocation_authority:
|
|
364
|
-
recovery_authority:
|
|
377
|
+
revocation_authority: boundary_js_1.IdentityID.fromAddress(params.revocationAuthority),
|
|
378
|
+
recovery_authority: boundary_js_1.IdentityID.fromAddress(params.recoveryAuthority),
|
|
365
379
|
private_addresses: [],
|
|
366
380
|
unlock_after: new bn_js_1.default(0),
|
|
367
381
|
});
|
|
@@ -370,25 +384,61 @@ function createIdentityObject(params) {
|
|
|
370
384
|
/**
|
|
371
385
|
* Build the parent-currency registration-fee output for a sub-ID.
|
|
372
386
|
*
|
|
373
|
-
* The
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
387
|
+
* The fee-output STRUCTURE depends on the parent currency's proofprotocol,
|
|
388
|
+
* verified against accepted on-chain registrations on VRSCTEST:
|
|
389
|
+
* - proofprotocol 2 (centralized / token, e.g. `ownora-nft`): a plain reserve
|
|
390
|
+
* output (EVAL_RESERVE_OUTPUT) holding `feeAmount` of the parent AT the
|
|
391
|
+
* parent's i-address, ZERO native.
|
|
392
|
+
* - otherwise (e.g. proofprotocol 1 PBaaS/fractional, `fum`): a CReserveTransfer
|
|
393
|
+
* (EVAL_RESERVE_TRANSFER) of `feeAmount` to the parent, carrying
|
|
394
|
+
* RESERVE_TRANSFER_FEE (0.0002) native.
|
|
380
395
|
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
396
|
+
* An offline SDK cannot read the parent's proofprotocol, so the caller passes it
|
|
397
|
+
* (`parentProofProtocol`). In both cases the native the transaction actually
|
|
398
|
+
* burns is the parent's idimportfees (`nativeImportFee`); the fee output's own
|
|
399
|
+
* native value is funded and cancels out in the conservation accounting.
|
|
385
400
|
*
|
|
386
|
-
* `
|
|
387
|
-
* are unused: the fee is denominated in and paid to the parent currency.
|
|
401
|
+
* `_controlAddress` is retained for signature compatibility and is unused.
|
|
388
402
|
*/
|
|
389
|
-
function buildRegistrationFeeOutput(parentCurrencyId, feeAmount, systemId, _controlAddress) {
|
|
390
|
-
|
|
391
|
-
|
|
403
|
+
function buildRegistrationFeeOutput(parentCurrencyId, feeAmount, systemId, _controlAddress, parentProofProtocol = 2) {
|
|
404
|
+
if (parentProofProtocol === 2) {
|
|
405
|
+
return buildTokenChangeOutput((0, brands_js_1.parseIAddress)(parentCurrencyId, 'parentCurrencyId'), new Map([[parentCurrencyId, feeAmount]]));
|
|
406
|
+
}
|
|
407
|
+
return buildRegistrationFeeReserveTransfer(parentCurrencyId, feeAmount, systemId);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* CReserveTransfer fee output for a sub-ID under a non-centralized (proofprotocol
|
|
411
|
+
* != 2) parent — the daemon's form for PBaaS/fractional currencies. Carries
|
|
412
|
+
* RESERVE_TRANSFER_FEE native.
|
|
413
|
+
*/
|
|
414
|
+
function buildRegistrationFeeReserveTransfer(parentCurrencyId, feeAmount, systemId) {
|
|
415
|
+
const destination = new boundary_js_1.TxDestination(boundary_js_1.KeyID.fromAddress(index_js_1.RESERVE_TRANSFER_EVAL_PKH));
|
|
416
|
+
const values = new boundary_js_1.CurrencyValueMap({
|
|
417
|
+
value_map: new Map([[parentCurrencyId, new bn_js_1.default(feeAmount.toString(10))]]),
|
|
418
|
+
multivalue: false,
|
|
419
|
+
});
|
|
420
|
+
const parentHash = (0, boundary_js_3.fromBase58Check)(parentCurrencyId).hash;
|
|
421
|
+
const transferDest = new boundary_js_1.TransferDestination({ type: boundary_js_1.DEST_ID, destination_bytes: Buffer.from(parentHash) });
|
|
422
|
+
const flags = boundary_js_1.RESERVE_TRANSFER_VALID.or(boundary_js_1.RESERVE_TRANSFER_BURN_CHANGE_PRICE);
|
|
423
|
+
const resTransfer = new boundary_js_1.ReserveTransfer({
|
|
424
|
+
values,
|
|
425
|
+
version: new bn_js_1.default(1),
|
|
426
|
+
flags,
|
|
427
|
+
fee_currency_id: systemId,
|
|
428
|
+
fee_amount: new bn_js_1.default(index_js_1.RESERVE_TRANSFER_FEE.toString(10)),
|
|
429
|
+
transfer_destination: transferDest,
|
|
430
|
+
dest_currency_id: parentCurrencyId,
|
|
431
|
+
});
|
|
432
|
+
const master = new boundary_js_1.OptCCParams({
|
|
433
|
+
version: new bn_js_1.default(3), eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE), m: new bn_js_1.default(1), n: new bn_js_1.default(1),
|
|
434
|
+
destinations: [destination], vdata: [],
|
|
435
|
+
});
|
|
436
|
+
const params = new boundary_js_1.OptCCParams({
|
|
437
|
+
version: new bn_js_1.default(3), eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_RESERVE_TRANSFER), m: new bn_js_1.default(1), n: new bn_js_1.default(1),
|
|
438
|
+
destinations: [destination], vdata: [resTransfer.toBuffer()],
|
|
439
|
+
});
|
|
440
|
+
const script = new boundary_js_1.SmartTransactionScript(master, params);
|
|
441
|
+
return { script: script.toBuffer(), nativeValue: index_js_1.RESERVE_TRANSFER_FEE };
|
|
392
442
|
}
|
|
393
443
|
/**
|
|
394
444
|
* Build a token change output (EVAL_RESERVE_OUTPUT)
|
|
@@ -400,10 +450,10 @@ function buildTokenChangeOutput(changeAddress, currencyChanges) {
|
|
|
400
450
|
// R-addresses, and reject a P2SH address (not a valid reserve destination).
|
|
401
451
|
let destination;
|
|
402
452
|
if ((0, brands_js_1.isIAddress)(changeAddress)) {
|
|
403
|
-
destination = new
|
|
453
|
+
destination = new boundary_js_1.TxDestination(boundary_js_1.IdentityID.fromAddress(changeAddress));
|
|
404
454
|
}
|
|
405
455
|
else if ((0, brands_js_1.isRAddress)(changeAddress)) {
|
|
406
|
-
destination = new
|
|
456
|
+
destination = new boundary_js_1.TxDestination(boundary_js_1.KeyID.fromAddress(changeAddress));
|
|
407
457
|
}
|
|
408
458
|
else {
|
|
409
459
|
throw new errors_js_1.TransactionBuildError(`token change address must be an R-address or identity i-address, got: ${changeAddress}`);
|
|
@@ -414,28 +464,28 @@ function buildTokenChangeOutput(changeAddress, currencyChanges) {
|
|
|
414
464
|
valueMap.set(currency, new bn_js_1.default(amount.toString(10)));
|
|
415
465
|
}
|
|
416
466
|
}
|
|
417
|
-
const values = new
|
|
467
|
+
const values = new boundary_js_1.CurrencyValueMap({
|
|
418
468
|
value_map: valueMap,
|
|
419
469
|
multivalue: false,
|
|
420
470
|
});
|
|
421
|
-
const tokenOutput = new
|
|
422
|
-
const master = new
|
|
471
|
+
const tokenOutput = new boundary_js_1.TokenOutput({ version: new bn_js_1.default(1), values });
|
|
472
|
+
const master = new boundary_js_1.OptCCParams({
|
|
423
473
|
version: new bn_js_1.default(3),
|
|
424
|
-
eval_code: new bn_js_1.default(
|
|
474
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_NONE),
|
|
425
475
|
m: new bn_js_1.default(1),
|
|
426
476
|
n: new bn_js_1.default(1),
|
|
427
477
|
destinations: [destination],
|
|
428
478
|
vdata: [],
|
|
429
479
|
});
|
|
430
|
-
const params = new
|
|
480
|
+
const params = new boundary_js_1.OptCCParams({
|
|
431
481
|
version: new bn_js_1.default(3),
|
|
432
|
-
eval_code: new bn_js_1.default(
|
|
482
|
+
eval_code: new bn_js_1.default(boundary_js_2.EVALS.EVAL_RESERVE_OUTPUT),
|
|
433
483
|
m: new bn_js_1.default(1),
|
|
434
484
|
n: new bn_js_1.default(1),
|
|
435
485
|
destinations: [destination],
|
|
436
486
|
vdata: [tokenOutput.toBuffer()],
|
|
437
487
|
});
|
|
438
|
-
const script = new
|
|
488
|
+
const script = new boundary_js_1.SmartTransactionScript(master, params);
|
|
439
489
|
return { script: script.toBuffer(), nativeValue: 0n };
|
|
440
490
|
}
|
|
441
491
|
// ─── Validation ──────────────────────────────────────────────────
|
|
@@ -444,7 +494,7 @@ function validateIdentityWif(wif) {
|
|
|
444
494
|
if (!wif || typeof wif !== 'string') {
|
|
445
495
|
throw new errors_js_1.InvalidWifError('WIF is required');
|
|
446
496
|
}
|
|
447
|
-
const check = (0,
|
|
497
|
+
const check = (0, index_js_4.validateWif)(wif);
|
|
448
498
|
if (!check.valid) {
|
|
449
499
|
throw new errors_js_1.InvalidWifError(check.error);
|
|
450
500
|
}
|
|
@@ -468,54 +518,30 @@ function buildAndSignCommitment(params, network) {
|
|
|
468
518
|
throw new errors_js_1.TransactionBuildError('At least one UTXO is required');
|
|
469
519
|
}
|
|
470
520
|
const verusNetwork = (0, index_js_3.getNetwork)(network === 'testnet');
|
|
471
|
-
const networkConfig = index_js_1.NETWORK_CONFIG[network];
|
|
472
521
|
// The commitment output is controlled by, and must be spent by, the key that
|
|
473
522
|
// completes the registration in step 2 — which signs with this same WIF. Using
|
|
474
523
|
// changeAddress as the control address broke that when changeAddress differed
|
|
475
524
|
// from the WIF's address (unsignable commitment), or was an i-address
|
|
476
525
|
// (KeyID.fromAddress laundered it to an uncontrollable key — permanently
|
|
477
526
|
// unspendable, wasting the commitment fee). Derive it from the WIF instead.
|
|
478
|
-
const controlAddress =
|
|
527
|
+
const controlAddress = boundary_js_4.ECPair.fromWIF(params.wif, verusNetwork).getAddress();
|
|
479
528
|
const commitment = prepareNameCommitment(params.name, controlAddress, params.referral, params.parent, network, params.salt);
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
// as a reserve-output change (bundled with the native change) — otherwise
|
|
493
|
-
// that token value is silently forfeited to the miner.
|
|
494
|
-
const hasTokenChange = selection.currencyChanges.size > 0;
|
|
495
|
-
if (hasTokenChange || selection.nativeChange > 0n) {
|
|
496
|
-
if (hasTokenChange) {
|
|
497
|
-
const tokenChangeScript = buildTokenChangeOutput((0, brands_js_1.parseAddress)(params.changeAddress, 'changeAddress'), selection.currencyChanges);
|
|
498
|
-
txb.addOutput(tokenChangeScript.script, (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
499
|
-
}
|
|
500
|
-
else if (params.changeAddress.startsWith('i')) {
|
|
501
|
-
// utxo-lib's addOutput only resolves base58 R-addresses; an i-address
|
|
502
|
-
// changeAddress needs the explicit P2ID script (matching sendCurrency), or
|
|
503
|
-
// it throws an untyped "no matching Script".
|
|
504
|
-
txb.addOutput(identityPaymentScript((0, brands_js_1.parseIAddress)(params.changeAddress, 'changeAddress')), (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
505
|
-
}
|
|
506
|
-
else {
|
|
507
|
-
txb.addOutput(params.changeAddress, (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
// No token is paid out here (the commitment output carries none), so every
|
|
511
|
-
// token in the selected inputs must return as change.
|
|
512
|
-
(0, index_js_4.assertTokenConservation)(selection.selected, new Map(), selection.currencyChanges, networkConfig.chainId, 'name commitment');
|
|
513
|
-
const unsignedTx = txb.buildIncomplete();
|
|
514
|
-
const { signedTx, txid } = (0, index_js_3.signTransactionSmart)(unsignedTx.toHex(), params.wif, selection.selected, verusNetwork);
|
|
529
|
+
// The commitment output carries no token and no native value; the assembler
|
|
530
|
+
// selects funding, emits change, and enforces conservation by construction.
|
|
531
|
+
const assembled = (0, assembler_js_1.assembleAndSign)({
|
|
532
|
+
network,
|
|
533
|
+
wif: params.wif,
|
|
534
|
+
expiryHeight: params.expiryHeight,
|
|
535
|
+
funding: params.utxos,
|
|
536
|
+
outputs: [{ script: commitment.commitmentScript, nativeSat: 0n }],
|
|
537
|
+
changeAddress: params.changeAddress,
|
|
538
|
+
fee: { policy: 'estimate' },
|
|
539
|
+
label: 'name commitment',
|
|
540
|
+
});
|
|
515
541
|
return {
|
|
516
|
-
signedTx,
|
|
517
|
-
txid,
|
|
518
|
-
fee:
|
|
542
|
+
signedTx: assembled.signedTx,
|
|
543
|
+
txid: assembled.txid,
|
|
544
|
+
fee: assembled.fee,
|
|
519
545
|
identityAddress: commitment.identityAddress,
|
|
520
546
|
commitmentData: {
|
|
521
547
|
name: params.name,
|
|
@@ -541,6 +567,17 @@ function buildAndSignRegistration(params, network) {
|
|
|
541
567
|
const verusNetwork = (0, index_js_3.getNetwork)(network === 'testnet');
|
|
542
568
|
const networkConfig = index_js_1.NETWORK_CONFIG[network];
|
|
543
569
|
const systemId = networkConfig.chainId;
|
|
570
|
+
// Step 2 spends the name-commitment output, which is controlled by the key that
|
|
571
|
+
// created it in step 1. The fork signs the commitment input with whatever WIF it
|
|
572
|
+
// is handed, so a mismatched WIF produces a tx the daemon rejects at broadcast
|
|
573
|
+
// (and the commitment fee is wasted). Verify the WIF's hash is the commitment's
|
|
574
|
+
// control hash up front. (The 20-byte control hash is embedded in the CC script.)
|
|
575
|
+
const signerAddr = boundary_js_4.ECPair.fromWIF(params.wif, verusNetwork).getAddress();
|
|
576
|
+
const signerHash = (0, index_js_2.addressToScriptPubKey)(signerAddr).subarray(3, 23).toString('hex');
|
|
577
|
+
if (!params.commitmentUtxo.script.toLowerCase().includes(signerHash)) {
|
|
578
|
+
throw new errors_js_1.TransactionBuildError(`the provided WIF (${signerAddr}) does not control the name-commitment output; step 2 must be ` +
|
|
579
|
+
`signed by the same key that created the commitment in step 1.`);
|
|
580
|
+
}
|
|
544
581
|
const commitData = params.commitmentData;
|
|
545
582
|
const parentIAddress = commitData.parent || systemId;
|
|
546
583
|
const isSubId = !isVRSCParent(commitData.parent || undefined, network);
|
|
@@ -558,11 +595,11 @@ function buildAndSignRegistration(params, network) {
|
|
|
558
595
|
const identityScript = buildIdentityScript(identity);
|
|
559
596
|
const reservationScript = buildReservationScript((0, brands_js_1.parseIAddress)(identityAddress, 'identityAddress'), Buffer.from(commitData.namereservationHex, 'hex'), isSubId);
|
|
560
597
|
if (isSubId) {
|
|
561
|
-
return _buildSubIdRegistration(params,
|
|
598
|
+
return _buildSubIdRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress, systemId, network);
|
|
562
599
|
}
|
|
563
|
-
return _buildVrscRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress,
|
|
600
|
+
return _buildVrscRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress, network);
|
|
564
601
|
}
|
|
565
|
-
function _buildVrscRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress,
|
|
602
|
+
function _buildVrscRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress, network) {
|
|
566
603
|
const commitData = params.commitmentData;
|
|
567
604
|
const hasReferral = !!commitData.referral;
|
|
568
605
|
const fees = calculateRegistrationFees(hasReferral, params.registrationFee, params.referralLevels);
|
|
@@ -596,155 +633,119 @@ function _buildVrscRegistration(params, identityScript, reservationScript, ident
|
|
|
596
633
|
// exactly (totalFee - issuerFee) every referred registration.
|
|
597
634
|
const totalFee = params.registrationFee ?? index_js_1.DEFAULT_REGISTRATION_FEE;
|
|
598
635
|
const totalReferralPayments = referralOutputs.reduce((sum, o) => sum + o.value, 0n);
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
// it throws an untyped "no matching Script".
|
|
630
|
-
txb.addOutput(identityPaymentScript((0, brands_js_1.parseIAddress)(params.changeAddress, 'changeAddress')), (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
txb.addOutput(params.changeAddress, (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
// Referral payouts are native; no token is paid out, so all token value in the
|
|
637
|
-
// selected inputs must return as change.
|
|
638
|
-
(0, index_js_4.assertTokenConservation)(selection.selected, new Map(), selection.currencyChanges, systemId, 'identity registration');
|
|
639
|
-
const unsignedTx = txb.buildIncomplete();
|
|
640
|
-
const allUtxos = [commitUtxo, ...selection.selected];
|
|
641
|
-
// The registration fee is burned as an IMPLICIT miner fee (identity and
|
|
642
|
-
// reservation outputs carry 0; referral payouts come out of the total) —
|
|
643
|
-
// this matches on-chain registrations, and the daemon exempts recognized
|
|
644
|
-
// identity definitions from its absurd-fee check (IS_HIGH_FEE). utxo-lib's
|
|
645
|
-
// client-side fee-rate cap (default 2500 sat/vbyte) must be told the
|
|
646
|
-
// intended absolute fee or build() throws "Transaction has absurd fees".
|
|
647
|
-
const expectedImplicitFee = commitUtxo.satoshis + requiredNative - totalReferralPayments + selection.fee;
|
|
648
|
-
// Independent value-conservation check on the assembled transaction: recompute
|
|
649
|
-
// the native fee straight from inputs and outputs and require it to equal the
|
|
650
|
-
// fee this path intends to pay. buildAndSign has the same guard; registration
|
|
651
|
-
// moves the largest amounts (the registration fee is burned as implicit fee),
|
|
652
|
-
// so a selection/accounting slip must fail loudly here instead of being paid
|
|
653
|
-
// to miners.
|
|
654
|
-
const assembledNativeFee = allUtxos.reduce((sum, u) => sum + u.satoshis, 0n) -
|
|
655
|
-
unsignedTx.outs.reduce((sum, o) => sum + BigInt(o.value), 0n);
|
|
656
|
-
if (assembledNativeFee !== expectedImplicitFee) {
|
|
657
|
-
throw new errors_js_1.TransactionBuildError(`identity registration value conservation failed: assembled native fee ${assembledNativeFee} sat ` +
|
|
658
|
-
`!= intended ${expectedImplicitFee} sat`);
|
|
659
|
-
}
|
|
660
|
-
const { signedTx, txid } = (0, index_js_3.signTransactionSmart)(unsignedTx.toHex(), params.wif, allUtxos, network, expectedImplicitFee);
|
|
636
|
+
// With a referral the registrant pays the discounted issuer fee, otherwise the
|
|
637
|
+
// full registration fee. Either way the referral payouts come OUT OF that
|
|
638
|
+
// outlay; the remainder is burned as the implicit miner fee.
|
|
639
|
+
const registrationOutlay = hasReferral ? fees.issuerFee : totalFee;
|
|
640
|
+
// The identity and reservation outputs carry 0 native; the referral payouts are
|
|
641
|
+
// the only value-bearing outputs. The rest of the outlay is a DECLARED burn, so
|
|
642
|
+
// the assembler funds it, sizes the fee cap for it, and enforces conservation.
|
|
643
|
+
const assembled = (0, assembler_js_1.assembleAndSign)({
|
|
644
|
+
network,
|
|
645
|
+
wif: params.wif,
|
|
646
|
+
expiryHeight: params.expiryHeight,
|
|
647
|
+
funding: params.utxos,
|
|
648
|
+
// The name-commitment output is spent as input 0 (it carries no currency).
|
|
649
|
+
leadingInputs: [params.commitmentUtxo],
|
|
650
|
+
outputs: [
|
|
651
|
+
{ script: identityScript, nativeSat: 0n },
|
|
652
|
+
...referralOutputs.map((o) => ({ script: o.script, nativeSat: o.value })),
|
|
653
|
+
{ script: reservationScript, nativeSat: 0n },
|
|
654
|
+
],
|
|
655
|
+
changeAddress: params.changeAddress,
|
|
656
|
+
// Legacy fee estimate: identity + referrals + reservation, plus the one extra
|
|
657
|
+
// change slot the original path counted on top of selectUtxos' own reserve.
|
|
658
|
+
feeOutputCount: 2 + referralOutputs.length + 1,
|
|
659
|
+
fee: {
|
|
660
|
+
policy: 'declared',
|
|
661
|
+
burnSat: registrationOutlay - totalReferralPayments,
|
|
662
|
+
reason: 'identity registration fee (burned as implicit miner fee)',
|
|
663
|
+
},
|
|
664
|
+
label: 'identity registration',
|
|
665
|
+
});
|
|
661
666
|
return {
|
|
662
|
-
signedTx,
|
|
663
|
-
txid,
|
|
664
|
-
fee:
|
|
667
|
+
signedTx: assembled.signedTx,
|
|
668
|
+
txid: assembled.txid,
|
|
669
|
+
fee: assembled.fee,
|
|
665
670
|
identityAddress,
|
|
666
671
|
// The registrant's registration outlay: the discounted issuer fee when
|
|
667
672
|
// referred, the full fee otherwise. (Of this, referralPayments go to the
|
|
668
673
|
// referrers and the remainder is the implicit miner fee.)
|
|
669
|
-
registrationFee:
|
|
674
|
+
registrationFee: registrationOutlay,
|
|
670
675
|
referralPayments: referralOutputs.length,
|
|
671
676
|
referralAmountEach: fees.referralAmount,
|
|
672
|
-
inputsUsed:
|
|
673
|
-
nativeChange:
|
|
677
|
+
inputsUsed: assembled.inputsUsed,
|
|
678
|
+
nativeChange: assembled.nativeChange,
|
|
674
679
|
};
|
|
675
680
|
}
|
|
676
|
-
function _buildSubIdRegistration(params,
|
|
681
|
+
function _buildSubIdRegistration(params, identityScript, reservationScript, identityAddress, parentIAddress, systemId, network) {
|
|
677
682
|
const registrationFeeAmount = params.registrationFeeAmount;
|
|
678
683
|
if (!registrationFeeAmount || registrationFeeAmount <= 0n) {
|
|
679
684
|
throw new errors_js_1.TransactionBuildError('registrationFeeAmount is required for sub-ID registration. ' +
|
|
680
685
|
'Specify the fee in parent currency satoshis.');
|
|
681
686
|
}
|
|
682
|
-
|
|
687
|
+
// The fee-output structure differs by the parent's proofprotocol (2 =
|
|
688
|
+
// reserve output, else reserve transfer), which the offline SDK cannot read —
|
|
689
|
+
// the caller must supply it (from `getcurrency <parent>.proofprotocol`).
|
|
690
|
+
if (params.parentProofProtocol === undefined) {
|
|
691
|
+
throw new errors_js_1.TransactionBuildError("parentProofProtocol is required for sub-ID registration: pass the parent currency's proofprotocol " +
|
|
692
|
+
'(from `getcurrency <parent>`) — 2 for a centralized/token currency, 1 for a PBaaS/fractional one.');
|
|
693
|
+
}
|
|
694
|
+
const feeOutput = buildRegistrationFeeOutput(parentIAddress, registrationFeeAmount, systemId, params.changeAddress, params.parentProofProtocol);
|
|
683
695
|
const requiredCurrencies = new Map([
|
|
684
696
|
[parentIAddress, registrationFeeAmount],
|
|
685
697
|
]);
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
const unsignedTx = txb.buildIncomplete();
|
|
730
|
-
const allUtxos = [commitUtxo, ...selection.selected];
|
|
731
|
-
// Native value conservation. The parent-currency registration fee is a token
|
|
732
|
-
// reserve-transfer (selection returns any excess as token change), so the
|
|
733
|
-
// NATIVE fee this path pays is the commitment input's value (folded to fee,
|
|
734
|
-
// like VRSC registration) + the native import fee + the miner fee. Verified
|
|
735
|
-
// live on VRSCTEST: a sub-ID under `ownora` (1.0 token fee) paid 0.02 native.
|
|
736
|
-
(0, index_js_3.assertNativeConservation)(allUtxos, unsignedTx.outs, commitUtxo.satoshis + nativeImportFee + selection.fee, 'sub-ID registration');
|
|
737
|
-
const { signedTx, txid } = (0, index_js_3.signTransactionSmart)(unsignedTx.toHex(), params.wif, allUtxos, network);
|
|
698
|
+
// The parent currency's idimportfees must leave the transaction as native fee;
|
|
699
|
+
// this SDK is offline and cannot read it, so the caller must pass it explicitly.
|
|
700
|
+
// Silently defaulting to 0 underfunds a currency that charges an import fee, and
|
|
701
|
+
// the daemon rejects the registration.
|
|
702
|
+
if (params.nativeImportFee === undefined) {
|
|
703
|
+
throw new errors_js_1.TransactionBuildError("nativeImportFee is required for sub-ID registration: pass the parent currency's idimportfees " +
|
|
704
|
+
'(from `getcurrency <parent>`), or 0n if it charges none.');
|
|
705
|
+
}
|
|
706
|
+
const nativeImportFee = params.nativeImportFee;
|
|
707
|
+
// The identity + reservation outputs carry 0 native but embed the full
|
|
708
|
+
// serialized identity / advanced name reservation (a large contentMultimap
|
|
709
|
+
// makes them multi-KB), so the fee is sized from their real bytes — matching
|
|
710
|
+
// the VRSC/update/define paths — instead of the fixed smart-output estimate.
|
|
711
|
+
// The fee output carries the parent-currency registration fee (a reserve output
|
|
712
|
+
// for pp2, a reserve transfer for pp1); the parent's idimportfees leaves as a
|
|
713
|
+
// DECLARED native burn. Verified live on VRSCTEST: a sub-ID under `ownora`
|
|
714
|
+
// (1.0 token fee) paid 0.02 native.
|
|
715
|
+
const assembled = (0, assembler_js_1.assembleAndSign)({
|
|
716
|
+
network,
|
|
717
|
+
wif: params.wif,
|
|
718
|
+
expiryHeight: params.expiryHeight,
|
|
719
|
+
funding: params.utxos,
|
|
720
|
+
// The name-commitment output is spent as input 0 (it carries no currency).
|
|
721
|
+
leadingInputs: [params.commitmentUtxo],
|
|
722
|
+
outputs: [
|
|
723
|
+
{ script: identityScript, nativeSat: 0n },
|
|
724
|
+
{ script: feeOutput.script, nativeSat: feeOutput.nativeValue, carries: requiredCurrencies },
|
|
725
|
+
{ script: reservationScript, nativeSat: 0n },
|
|
726
|
+
],
|
|
727
|
+
changeAddress: params.changeAddress,
|
|
728
|
+
// Legacy fee estimate: identity + fee + reservation, plus the one extra change
|
|
729
|
+
// slot the original path counted on top of selectUtxos' own reserve.
|
|
730
|
+
feeOutputCount: 4,
|
|
731
|
+
extraOutputBytes: identityScript.length + reservationScript.length + feeOutput.script.length,
|
|
732
|
+
fee: {
|
|
733
|
+
policy: 'declared',
|
|
734
|
+
burnSat: nativeImportFee,
|
|
735
|
+
reason: "parent currency's idimportfees (leaves as native import fee)",
|
|
736
|
+
},
|
|
737
|
+
label: 'sub-ID registration',
|
|
738
|
+
});
|
|
738
739
|
return {
|
|
739
|
-
signedTx,
|
|
740
|
-
txid,
|
|
741
|
-
fee:
|
|
740
|
+
signedTx: assembled.signedTx,
|
|
741
|
+
txid: assembled.txid,
|
|
742
|
+
fee: assembled.fee,
|
|
742
743
|
identityAddress,
|
|
743
744
|
registrationFee: registrationFeeAmount,
|
|
744
745
|
referralPayments: 0,
|
|
745
746
|
referralAmountEach: 0n,
|
|
746
|
-
inputsUsed:
|
|
747
|
-
nativeChange:
|
|
747
|
+
inputsUsed: assembled.inputsUsed,
|
|
748
|
+
nativeChange: assembled.nativeChange,
|
|
748
749
|
};
|
|
749
750
|
}
|
|
750
751
|
/**
|
|
@@ -759,9 +760,7 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
759
760
|
throw new errors_js_1.TransactionBuildError('At least one funding UTXO is required');
|
|
760
761
|
}
|
|
761
762
|
const verusNetwork = (0, index_js_3.getNetwork)(network === 'testnet');
|
|
762
|
-
const
|
|
763
|
-
const systemId = networkConfig.chainId;
|
|
764
|
-
const identity = new verus_typescript_primitives_1.Identity();
|
|
763
|
+
const identity = new boundary_js_1.Identity();
|
|
765
764
|
identity.fromBuffer(Buffer.from(params.identityHex, 'hex'));
|
|
766
765
|
// update/lock/unlock are authorized by the identity's primary key(s). The fork
|
|
767
766
|
// signs CC inputs with whatever key it is given, so a WIF that does not control
|
|
@@ -783,12 +782,23 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
783
782
|
requiresPrimary = authority === self;
|
|
784
783
|
}
|
|
785
784
|
if (requiresPrimary) {
|
|
786
|
-
const signerAddress =
|
|
785
|
+
const signerAddress = boundary_js_4.ECPair.fromWIF(params.wif, verusNetwork).getAddress();
|
|
787
786
|
const currentPrimaries = (identity.primary_addresses ?? []).map((k) => k.toAddress());
|
|
788
787
|
if (!currentPrimaries.includes(signerAddress)) {
|
|
789
788
|
throw new errors_js_1.TransactionBuildError(`the provided WIF (${signerAddress}) is not among the identity's primary addresses ` +
|
|
790
789
|
`[${currentPrimaries.join(', ')}]; it cannot authorize a ${operation}.`);
|
|
791
790
|
}
|
|
791
|
+
// Spending the identity input under primary authority needs min_sigs
|
|
792
|
+
// signatures, but this SDK signs with a single WIF and the bundled fork
|
|
793
|
+
// cannot attach a second signature to a CryptoCondition input. Emitting a
|
|
794
|
+
// 1-of-N-signed tx for a min_sigs>1 identity yields a hex the daemon rejects
|
|
795
|
+
// at broadcast; fail closed instead.
|
|
796
|
+
const currentMinSigs = identity.min_sigs?.toNumber?.() ?? 1;
|
|
797
|
+
if (currentMinSigs > 1) {
|
|
798
|
+
throw new errors_js_1.TransactionBuildError(`this identity requires ${currentMinSigs} signatures (min_sigs > 1); the SDK signs with a single ` +
|
|
799
|
+
`WIF and the bundled fork cannot multi-sign a CryptoCondition input, so a valid ${operation} ` +
|
|
800
|
+
`transaction cannot be produced. Use the daemon for multisig identities.`);
|
|
801
|
+
}
|
|
792
802
|
}
|
|
793
803
|
switch (operation) {
|
|
794
804
|
case 'update': {
|
|
@@ -807,13 +817,21 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
807
817
|
identity.setRecovery(params.recoveryAuthority);
|
|
808
818
|
}
|
|
809
819
|
if (params.contentMap) {
|
|
820
|
+
// The daemon REPLACES the whole contentmap when the field is provided
|
|
821
|
+
// (and the contentMultimap branch below already replaces via fromJson).
|
|
822
|
+
// Merging into the parsed on-chain map instead re-attested stale keys and
|
|
823
|
+
// gave no way to delete one. Clear first so the provided map is authoritative.
|
|
824
|
+
identity.content_map.clear();
|
|
810
825
|
for (const [key, value] of Object.entries(params.contentMap)) {
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
|
|
814
|
-
//
|
|
815
|
-
|
|
816
|
-
|
|
826
|
+
// Keys are vdxf i-addresses; the primitives run fromBase58Check(key),
|
|
827
|
+
// which discards the version byte — guard it like the multimap branch.
|
|
828
|
+
assertAddressVersion(key, index_js_1.I_ADDR_VERSION, `contentMap key "${key}"`);
|
|
829
|
+
// Values serialize as a fixed uint256 (32 bytes). Buffer.from(_, 'hex')
|
|
830
|
+
// silently drops non-hex and truncates, so a wrong-length value would be
|
|
831
|
+
// committed on-chain as wrong bytes (or build a payload the daemon can't
|
|
832
|
+
// deserialize). Require exactly 64 hex chars.
|
|
833
|
+
if (!/^[0-9a-fA-F]{64}$/.test(value)) {
|
|
834
|
+
throw new errors_js_1.TransactionBuildError(`contentMap["${key}"] must be a 32-byte (64-hex-char) value (got ${JSON.stringify(value)})`);
|
|
817
835
|
}
|
|
818
836
|
identity.content_map.set(key, Buffer.from(value, 'hex'));
|
|
819
837
|
}
|
|
@@ -835,7 +853,7 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
835
853
|
}
|
|
836
854
|
jsonObj[key] = items;
|
|
837
855
|
}
|
|
838
|
-
identity.content_multimap =
|
|
856
|
+
identity.content_multimap = boundary_js_1.ContentMultiMap.fromJson(jsonObj);
|
|
839
857
|
}
|
|
840
858
|
break;
|
|
841
859
|
}
|
|
@@ -851,6 +869,13 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
851
869
|
if (params.primaryAddresses) {
|
|
852
870
|
identity.setPrimaryAddresses(params.primaryAddresses);
|
|
853
871
|
}
|
|
872
|
+
// Recovery commonly replaces the primary set; allow lowering min_sigs so a
|
|
873
|
+
// 2-of-2 identity can be recovered to a single fresh key (otherwise the
|
|
874
|
+
// stale min_sigs would exceed the new primary count — see the guard below).
|
|
875
|
+
if (params.minSigs !== undefined) {
|
|
876
|
+
validateMinSigs(params.minSigs, identity.primary_addresses?.length ?? 0);
|
|
877
|
+
identity.min_sigs = new bn_js_1.default(params.minSigs);
|
|
878
|
+
}
|
|
854
879
|
if (params.revocationAuthority) {
|
|
855
880
|
identity.setRevocation(params.revocationAuthority);
|
|
856
881
|
}
|
|
@@ -860,11 +885,20 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
860
885
|
break;
|
|
861
886
|
}
|
|
862
887
|
case 'lock': {
|
|
863
|
-
const
|
|
864
|
-
if (!
|
|
865
|
-
throw new errors_js_1.TransactionBuildError('
|
|
888
|
+
const delay = lockUnlockParams?.unlockDelayBlocks;
|
|
889
|
+
if (delay === undefined || !Number.isInteger(delay) || delay <= 0) {
|
|
890
|
+
throw new errors_js_1.TransactionBuildError('unlockDelayBlocks (a positive integer RELATIVE delay in blocks, not a block height) is required for lock');
|
|
891
|
+
}
|
|
892
|
+
if (delay > MAX_UNLOCK_DELAY) {
|
|
893
|
+
throw new errors_js_1.TransactionBuildError(`unlockDelayBlocks ${delay} exceeds the consensus maximum ${MAX_UNLOCK_DELAY} (~22 years)`);
|
|
866
894
|
}
|
|
867
|
-
|
|
895
|
+
// A block height (millions) passed as a delay locks the identity for years.
|
|
896
|
+
// Require an explicit opt-in above ~1 year to catch that mistake.
|
|
897
|
+
if (delay > LOCK_DELAY_SANITY_BLOCKS && !lockUnlockParams?.sanityOverride) {
|
|
898
|
+
throw new errors_js_1.TransactionBuildError(`unlockDelayBlocks ${delay} is over ~1 year (${LOCK_DELAY_SANITY_BLOCKS} blocks) — this is a RELATIVE ` +
|
|
899
|
+
`delay, not a block height; pass sanityOverride: true if that long a lock is intended.`);
|
|
900
|
+
}
|
|
901
|
+
identity.lock(new bn_js_1.default(delay));
|
|
868
902
|
break;
|
|
869
903
|
}
|
|
870
904
|
case 'unlock': {
|
|
@@ -881,69 +915,43 @@ function buildAndSignIdentityUpdate(params, network, operation = 'update', lockU
|
|
|
881
915
|
break;
|
|
882
916
|
}
|
|
883
917
|
}
|
|
918
|
+
// A valid identity always has min_sigs <= number of primary addresses. Shrinking
|
|
919
|
+
// the primary set (update/recover) without lowering minSigs would leave a stale
|
|
920
|
+
// min_sigs the daemon rejects — or, worse, a permanently unspendable identity.
|
|
921
|
+
const finalPrimaries = identity.primary_addresses?.length ?? 0;
|
|
922
|
+
const finalMinSigs = identity.min_sigs?.toNumber?.() ?? 1;
|
|
923
|
+
if (finalMinSigs > finalPrimaries) {
|
|
924
|
+
throw new errors_js_1.TransactionBuildError(`resulting identity has min_sigs ${finalMinSigs} but only ${finalPrimaries} primary address(es); ` +
|
|
925
|
+
`pass minSigs to lower it when reducing the primary set.`);
|
|
926
|
+
}
|
|
884
927
|
const identityBuf = identity.toBuffer();
|
|
885
928
|
const unfundedHex = createUnfundedIdentityUpdate(identityBuf.toString('hex'), verusNetwork, (0, index_js_3.resolveExpiryHeight)(params.expiryHeight));
|
|
886
|
-
const
|
|
887
|
-
//
|
|
888
|
-
//
|
|
889
|
-
//
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
for (const out of unfundedTx.outs) {
|
|
904
|
-
txb.addOutput(out.script, out.value);
|
|
905
|
-
}
|
|
906
|
-
if (selection.nativeChange > 0n) {
|
|
907
|
-
// utxo-lib's addOutput only resolves base58 R-addresses; an i-address
|
|
908
|
-
// changeAddress needs the explicit P2ID script (matching sendCurrency), or
|
|
909
|
-
// it throws an untyped "no matching Script".
|
|
910
|
-
if (params.changeAddress.startsWith('i')) {
|
|
911
|
-
txb.addOutput(identityPaymentScript((0, brands_js_1.parseIAddress)(params.changeAddress, 'changeAddress')), (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
912
|
-
}
|
|
913
|
-
else {
|
|
914
|
-
txb.addOutput(params.changeAddress, (0, index_js_2.toSafeNumber)(selection.nativeChange));
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
const fundedTx = txb.buildIncomplete();
|
|
918
|
-
const fundedHex = fundedTx.toHex();
|
|
919
|
-
const prevOutScripts = selection.selected.map(u => Buffer.from(u.script, 'hex'));
|
|
920
|
-
const idUtxo = params.identityUtxo;
|
|
921
|
-
// The identity input is spent and its definition output is recreated with
|
|
922
|
-
// value 0, so any native value riding on identityUtxo would be silently
|
|
923
|
-
// burned to miner fee. Identity outputs normally carry 0; fail closed if not.
|
|
924
|
-
if (idUtxo.satoshis !== 0n) {
|
|
925
|
-
throw new errors_js_1.TransactionBuildError(`identityUtxo carries ${idUtxo.satoshis} native satoshis, which would be burned to miner fee ` +
|
|
926
|
-
`(the recreated identity output is value 0). Spend that value separately before updating.`);
|
|
927
|
-
}
|
|
928
|
-
const completedHex = completeFundedIdentityUpdate(fundedHex, verusNetwork, prevOutScripts, {
|
|
929
|
-
hash: Buffer.from(idUtxo.txid, 'hex').reverse(),
|
|
930
|
-
index: idUtxo.outputIndex,
|
|
931
|
-
sequence: 0xffffffff,
|
|
932
|
-
script: Buffer.from(idUtxo.script, 'hex'),
|
|
929
|
+
const unfundedTx = boundary_js_4.Transaction.fromHex(unfundedHex, verusNetwork);
|
|
930
|
+
// Respend the identity UTXO and recreate its (value-0) definition output; the
|
|
931
|
+
// shared assembler funds it, emits native change, grafts on the identity input
|
|
932
|
+
// (re-signed last by the fork), and enforces native + token conservation.
|
|
933
|
+
const assembled = (0, fundedIdentityUpdate_js_1.assembleFundedIdentityUpdate)({
|
|
934
|
+
network,
|
|
935
|
+
wif: params.wif,
|
|
936
|
+
expiryHeight: params.expiryHeight,
|
|
937
|
+
funding: params.utxos,
|
|
938
|
+
identityUtxo: params.identityUtxo,
|
|
939
|
+
outputs: unfundedTx.outs.map((o) => ({ script: o.script, nativeSat: BigInt(o.value) })),
|
|
940
|
+
changeAddress: params.changeAddress,
|
|
941
|
+
// The identity output embeds the full serialized identity (a large
|
|
942
|
+
// contentMultimap can make it multi-KB); size the fee from the unfunded tx's
|
|
943
|
+
// real bytes so a big update isn't fee-estimated below the relay minimum.
|
|
944
|
+
extraOutputBytes: unfundedHex.length / 2,
|
|
945
|
+
label: `identity ${operation}`,
|
|
933
946
|
});
|
|
934
|
-
const allUtxos = [...selection.selected, idUtxo];
|
|
935
|
-
// The identity input and its recreated output are both value 0, so the
|
|
936
|
-
// assembled native fee must equal selection.fee. Fail loudly on any slip.
|
|
937
|
-
(0, index_js_3.assertNativeConservation)(allUtxos, utxo_lib_1.Transaction.fromHex(completedHex, verusNetwork).outs, selection.fee, `identity ${operation}`);
|
|
938
|
-
const { signedTx, txid } = (0, index_js_3.signTransactionSmart)(completedHex, params.wif, allUtxos, verusNetwork);
|
|
939
947
|
return {
|
|
940
|
-
signedTx,
|
|
941
|
-
txid,
|
|
942
|
-
fee:
|
|
948
|
+
signedTx: assembled.signedTx,
|
|
949
|
+
txid: assembled.txid,
|
|
950
|
+
fee: assembled.fee,
|
|
943
951
|
identityAddress: identity.getIdentityAddress(),
|
|
944
952
|
operation,
|
|
945
|
-
inputsUsed:
|
|
946
|
-
nativeChange:
|
|
953
|
+
inputsUsed: assembled.inputsUsed,
|
|
954
|
+
nativeChange: assembled.nativeChange,
|
|
947
955
|
};
|
|
948
956
|
}
|
|
949
957
|
//# sourceMappingURL=index.js.map
|