mainnet-js 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -1
- package/dist/{mainnet-1.0.11.js → mainnet-1.0.13.js} +3 -3
- package/dist/module/transaction/Wif.d.ts +2 -1
- package/dist/module/transaction/Wif.d.ts.map +1 -1
- package/dist/module/transaction/Wif.js +2 -2
- package/dist/module/transaction/Wif.js.map +1 -1
- package/dist/module/wallet/Bcmr.d.ts +1 -1
- package/dist/module/wallet/Bcmr.d.ts.map +1 -1
- package/dist/module/wallet/Bcmr.js +66 -35
- package/dist/module/wallet/Bcmr.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +5 -0
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/transaction/Wif.ts +3 -2
- package/src/wallet/Bcmr.test.ts +93 -2
- package/src/wallet/Bcmr.ts +82 -52
- package/src/wallet/Cashtokens.test.ts +14 -3
- package/src/wallet/Wif.ts +8 -0
package/src/wallet/Bcmr.ts
CHANGED
|
@@ -50,6 +50,10 @@ export class BCMR {
|
|
|
50
50
|
uri: string,
|
|
51
51
|
contentHash?: string
|
|
52
52
|
): Promise<Registry> {
|
|
53
|
+
if (uri.indexOf("https://") < 0) {
|
|
54
|
+
uri = `https://${uri}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
// content hashes HTTPS Publication Outputs per spec
|
|
54
58
|
if (contentHash) {
|
|
55
59
|
// request as text and verify hash
|
|
@@ -110,7 +114,7 @@ export class BCMR {
|
|
|
110
114
|
|
|
111
115
|
/**
|
|
112
116
|
* buildAuthChain Build an authchain - Zeroth-Descendant Transaction Chain, refer to https://github.com/bitjson/chip-bcmr#zeroth-descendant-transaction-chains
|
|
113
|
-
* The authchain in this implementation is specific to resolve to a valid metadata
|
|
117
|
+
* The authchain in this implementation is specific to resolve to a valid metadata registry
|
|
114
118
|
*
|
|
115
119
|
* @param {string} options.transactionHash (required) transaction hash from which to build the auth chain
|
|
116
120
|
* @param {Network?} options.network (default=mainnet) network to query the data from
|
|
@@ -144,12 +148,52 @@ export class BCMR {
|
|
|
144
148
|
const provider = getGlobalProvider(
|
|
145
149
|
options.network
|
|
146
150
|
) as ElectrumNetworkProvider;
|
|
147
|
-
|
|
151
|
+
|
|
152
|
+
if (options.rawTx === undefined) {
|
|
148
153
|
options.rawTx = await provider.getRawTransactionObject(
|
|
149
154
|
options.transactionHash
|
|
150
155
|
);
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
// figure out the autchain by moving towards authhead
|
|
159
|
+
const getAuthChainChild = async () => {
|
|
160
|
+
const history =
|
|
161
|
+
options.historyCache ||
|
|
162
|
+
(await provider.getHistory(
|
|
163
|
+
options.rawTx!.vout[0].scriptPubKey.addresses[0]
|
|
164
|
+
));
|
|
165
|
+
const thisTx = history.find(
|
|
166
|
+
(val) => val.tx_hash === options.transactionHash
|
|
167
|
+
);
|
|
168
|
+
let filteredHistory = history.filter((val) =>
|
|
169
|
+
val.height > 0
|
|
170
|
+
? val.height >= thisTx!.height || val.height <= 0
|
|
171
|
+
: val.height <= 0 && val.tx_hash !== thisTx!.tx_hash
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
for (const historyTx of filteredHistory) {
|
|
175
|
+
const historyRawTx = await provider.getRawTransactionObject(
|
|
176
|
+
historyTx.tx_hash
|
|
177
|
+
);
|
|
178
|
+
const authChainVin = historyRawTx.vin.find(
|
|
179
|
+
(val) => val.txid === options.transactionHash && val.vout === 0
|
|
180
|
+
);
|
|
181
|
+
// if we've found continuation of authchain, we shall recurse into it
|
|
182
|
+
if (authChainVin) {
|
|
183
|
+
// reuse queried address history if the next element in chain is the same address
|
|
184
|
+
const historyCache =
|
|
185
|
+
options.rawTx!.vout[0].scriptPubKey.addresses[0] ===
|
|
186
|
+
historyRawTx.vout[0].scriptPubKey.addresses[0]
|
|
187
|
+
? filteredHistory
|
|
188
|
+
: undefined;
|
|
189
|
+
|
|
190
|
+
// combine the authchain element with the rest obtained
|
|
191
|
+
return { rawTx: historyRawTx, historyCache };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
};
|
|
196
|
+
|
|
153
197
|
// helper function to enforce the constraints on the 0th output, decode the BCMR's OP_RETURN data
|
|
154
198
|
// returns resolved AuthChainElement
|
|
155
199
|
const makeAuthChainElement = (
|
|
@@ -236,15 +280,31 @@ export class BCMR {
|
|
|
236
280
|
// content hash is in OP_SHA256 byte order per spec
|
|
237
281
|
result.contentHash = binToHex(chunks[1].slice().reverse());
|
|
238
282
|
result.uri = binToUtf8(chunks[2]);
|
|
283
|
+
if (result.uri.indexOf("https://") < 0) {
|
|
284
|
+
result.uri = `https://${result.uri}`;
|
|
285
|
+
}
|
|
239
286
|
}
|
|
240
287
|
return result;
|
|
241
288
|
};
|
|
242
289
|
|
|
243
290
|
// make authchain element and combine with the rest obtained
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
options.rawTx.hash
|
|
247
|
-
)
|
|
291
|
+
let element: AuthChainElement;
|
|
292
|
+
try {
|
|
293
|
+
element = makeAuthChainElement(options.rawTx, options.rawTx.hash);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
// follow authchain to head and look for BCMR outputs
|
|
296
|
+
const child = await getAuthChainChild();
|
|
297
|
+
if (child) {
|
|
298
|
+
return await BCMR.buildAuthChain({
|
|
299
|
+
...options,
|
|
300
|
+
transactionHash: child.rawTx.hash,
|
|
301
|
+
rawTx: child.rawTx,
|
|
302
|
+
historyCache: child.historyCache,
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
248
308
|
|
|
249
309
|
let chainBase: AuthChain = [];
|
|
250
310
|
if (options.resolveBase) {
|
|
@@ -294,49 +354,19 @@ export class BCMR {
|
|
|
294
354
|
// if we follow to head, we need to locate the next transaction spending our 0th output
|
|
295
355
|
// and repeat the building process recursively
|
|
296
356
|
if (options.followToHead) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
options.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
for (const historyTx of filteredHistory) {
|
|
313
|
-
const historyRawTx = await provider.getRawTransactionObject(
|
|
314
|
-
historyTx.tx_hash
|
|
315
|
-
);
|
|
316
|
-
const authChainVin = historyRawTx.vin.find(
|
|
317
|
-
(val) => val.txid === options.transactionHash && val.vout === 0
|
|
318
|
-
);
|
|
319
|
-
// if we've found continuation of authchain, we shall recurse into it
|
|
320
|
-
if (authChainVin) {
|
|
321
|
-
// reuse queried address history if the next element in chain is the same address
|
|
322
|
-
const historyCache =
|
|
323
|
-
options.rawTx.vout[0].scriptPubKey.addresses[0] ===
|
|
324
|
-
historyRawTx.vout[0].scriptPubKey.addresses[0]
|
|
325
|
-
? filteredHistory
|
|
326
|
-
: undefined;
|
|
327
|
-
// query next chain element
|
|
328
|
-
const chainHead = await BCMR.buildAuthChain({
|
|
329
|
-
transactionHash: historyRawTx.hash,
|
|
330
|
-
network: options.network,
|
|
331
|
-
rawTx: historyRawTx,
|
|
332
|
-
historyCache: historyCache,
|
|
333
|
-
followToHead: options.followToHead,
|
|
334
|
-
resolveBase: false,
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// combine the authchain element with the rest obtained
|
|
338
|
-
return [...chainBase, element, ...chainHead];
|
|
339
|
-
}
|
|
357
|
+
const child = await getAuthChainChild();
|
|
358
|
+
if (child) {
|
|
359
|
+
const chainHead = await BCMR.buildAuthChain({
|
|
360
|
+
transactionHash: child.rawTx.hash,
|
|
361
|
+
network: options.network,
|
|
362
|
+
rawTx: child.rawTx,
|
|
363
|
+
historyCache: child.historyCache,
|
|
364
|
+
followToHead: options.followToHead,
|
|
365
|
+
resolveBase: false,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// combine the authchain element with the rest obtained
|
|
369
|
+
return [...chainBase, element, ...chainHead];
|
|
340
370
|
}
|
|
341
371
|
}
|
|
342
372
|
|
|
@@ -383,10 +413,10 @@ export class BCMR {
|
|
|
383
413
|
for (const registry of this.metadataRegistries.slice().reverse()) {
|
|
384
414
|
// registry identity is an authbase string pointer
|
|
385
415
|
if (typeof registry.registryIdentity === "string") {
|
|
386
|
-
// enforce spec, ensure
|
|
416
|
+
// enforce spec, ensure identities have this authbase
|
|
387
417
|
if (registry.identities?.[registry.registryIdentity]) {
|
|
388
418
|
// find the latest identity in history and add it to the list
|
|
389
|
-
const latestIdentityInHistory = registry.identities![tokenId][0];
|
|
419
|
+
const latestIdentityInHistory = registry.identities![tokenId]?.[0];
|
|
390
420
|
if (latestIdentityInHistory) {
|
|
391
421
|
return latestIdentityInHistory;
|
|
392
422
|
}
|
|
@@ -398,7 +428,7 @@ export class BCMR {
|
|
|
398
428
|
}
|
|
399
429
|
|
|
400
430
|
// find the latest identity in history and add it to the list
|
|
401
|
-
const latestIdentityInHistory = registry.identities![tokenId][0];
|
|
431
|
+
const latestIdentityInHistory = registry.identities![tokenId]?.[0];
|
|
402
432
|
if (latestIdentityInHistory) {
|
|
403
433
|
return latestIdentityInHistory;
|
|
404
434
|
}
|
|
@@ -27,6 +27,19 @@ describe(`Test cashtokens`, () => {
|
|
|
27
27
|
expect(utxos[0].token?.tokenId).toBeDefined();
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test("Test token genesis and max amount to send", async () => {
|
|
31
|
+
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
32
|
+
const bob = await RegTestWallet.newRandom();
|
|
33
|
+
await alice.send([[bob.cashaddr!, 0.101, "bch"]]);
|
|
34
|
+
const genesisResponse = await bob.tokenGenesis({
|
|
35
|
+
amount: 100,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const maxAmountToSend = await bob.getMaxAmountToSend();
|
|
39
|
+
await bob.send([[alice.cashaddr!, maxAmountToSend.sat!, "sat"]]);
|
|
40
|
+
expect(await bob.getBalance("sat")).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
30
43
|
test("Test tokens will not be burned when sending bch value", async () => {
|
|
31
44
|
const alice = await RegTestWallet.fromId(process.env.ALICE_ID!);
|
|
32
45
|
const bob = await RegTestWallet.newRandom();
|
|
@@ -53,8 +66,6 @@ describe(`Test cashtokens`, () => {
|
|
|
53
66
|
]);
|
|
54
67
|
expect(await bob.getTokenBalance(tokenId)).toBe(25);
|
|
55
68
|
expect(await bob.getBalance("sat")).toBe(5000);
|
|
56
|
-
console.log(await bob.getAddressUtxos());
|
|
57
|
-
console.log(await bob.getMaxAmountToSend());
|
|
58
69
|
|
|
59
70
|
await bob.send(
|
|
60
71
|
new SendRequest({
|
|
@@ -604,7 +615,7 @@ describe(`Test cashtokens`, () => {
|
|
|
604
615
|
expect(tokenUtxos2.length).toBe(1);
|
|
605
616
|
});
|
|
606
617
|
|
|
607
|
-
test("Test enforcing
|
|
618
|
+
test("Test enforcing token addresses", async () => {
|
|
608
619
|
const bob = await RegTestWallet.newRandom();
|
|
609
620
|
|
|
610
621
|
const previousValue = Config.EnforceCashTokenReceiptAddresses;
|
package/src/wallet/Wif.ts
CHANGED
|
@@ -1126,6 +1126,14 @@ export class Wallet extends BaseWallet {
|
|
|
1126
1126
|
utxos = await this.getAddressUtxos(this.cashaddr);
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
|
+
// filter out token utxos if there are no token requests
|
|
1130
|
+
if (
|
|
1131
|
+
checkTokenQuantities &&
|
|
1132
|
+
!sendRequests.some((val) => val instanceof TokenSendRequest)
|
|
1133
|
+
) {
|
|
1134
|
+
utxos = utxos.filter((val) => !val.token);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1129
1137
|
const addTokenChangeOutputs = (
|
|
1130
1138
|
inputs: UtxoI[],
|
|
1131
1139
|
outputs: SendRequestType[]
|