-
Notifications
You must be signed in to change notification settings - Fork 1
/
devtools.html
417 lines (362 loc) Β· 15.7 KB
/
devtools.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Dev tools</title>
<style type="text/css">
label { display: block; }
fieldset { border: 0; }
label.radio { display: inline-block; }
textarea { display: inline-block; background-color: #f3f2f2; }
input { background-color: #f3f2f2; }
/*
{
name: 'tundra3',
colors: [
'#87c3ca',
'#7b7377',
'#b2475d',
'#7d3e3e',
'#eb7f64',
'#d9c67a',
'#f3f2f2'
]
},
*/
</style>
<script src="https://cdn.jsdelivr.net/npm/json-bigint-parser-browser@1.0.4/json-bigint-browser.min.js"></script>
</head>
<body style='background-color: #f3efe6'>
<h2>dev tools, WIP, use at your own risk</h2>
<p>everything happens browser-side, enjoy</p>
<form>
<div class='section'>
<h2>Secret (private) key to public key</h2>
<div style='display: inline-block; vertical-align: top;'>
<label for="privkey-hex">Private key in hex (in hex):</label>
<input name="privkey-hex" id="privkey-hex" size="100" value="1A9FBB379403527C5AF09823D208224F3EF047C0D33575E58A7265E31844EFB2"/>
<br>
<input name="privkey-pubkey-propagate" id="privkey-pubkey-propagate" type="checkbox" checked="checked"/>
<label for="privkey-pubkey-propagate" style='display: inline-block'>Propagate to public-key field</label>
</div>
<div style='display: inline-block; border: 1px solid black; width: 50%; word-wrap: break-word;'>
<pre id="privkey-to-pubkey-output" style="width:100%; word-wrap: break-word;"> </pre>
</div>
<div style="clear:both"></div>
</div>
<div class='section'>
<h2>Public key to address</h2>
<div style='display: inline-block; vertical-align: top;'>
<label for="pubkey-hex">Public key in hex (in hex):</label>
<input name="pubkey-hex" id="pubkey-hex" size="100" value="E6833171B35BCBF1EBF8236F0BEE5E639C3721BC11096CEF13BACF5070EB48F4"/>
</div>
<div style='display: inline-block; border: 1px solid black; width: 50%; word-wrap: break-word;'>
<pre id="pubkey-to-address-output" style="width:100%; word-wrap: break-word;"> </pre>
</div>
<div style="clear:both"></div>
</div>
<div class='section'>
<h2>hex-address to address</h2>
<div style='display: inline-block; vertical-align: top;'>
<label for="address-hex">Address (in hex):</label>
<input name="address-hex" id="address-hex" size="100" value="98F8D50272C1FBF0A8033C307C6543CF26A72774BE217202"/>
</div>
<div style='display: inline-block; border: 1px solid black; width: 50%;'>
<pre id="address-hex-to-address-output"> </pre>
</div>
<div style="clear:both"></div>
</div>
<div class='section'>
<h2>transaction assembly</h2>
<div style='display: inline-block; vertical-align: top;'>
<fieldset>
<input type="radio" name="transaction-descriptor-network" id="transaction-descriptor-mainnet" value="mainnet">
<label class="radio" for="transaction-descriptor-mainnet">Mainnet</label>
<input type="radio" name="transaction-descriptor-network" id="transaction-descriptor-testnet" value="testnet" checked>
<label class="radio" for="transaction-descriptor-testnet">Testnet</label>
</fieldset>
<label for="transaction-descriptor">Transaction descriptor:</label>
<textarea name="transaction-descriptor" id="transaction-descriptor" rows="20" cols="100">
{
"type": "secret_lock_transaction_v1",
"signature": "5871A742A7139C84225700D3DA73F3E8D4D9BA43DDA07124B02A26777B1065DE7CEF7AA7DAAB466D8DDE1B689F90FE7D22791F977731742D111F3DF671882D0E",
"signerPublicKey": "093D4D1258785AD48BB4FD762D48569BC01F49669074FA72084550DF94B4F200",
"fee": "20900n",
"deadline": "25414456928n",
"mosaic": { "mosaicId": "0xE74B99BA41F4AFEEn", "amount": "7887000000n"},
"duration": "10419n",
"recipientAddress": "TALICECI35BNIJQA5CNUKI2DY3SXNEHPZJSOVAA",
"secret": "B867DB875479BCC0287352CDAA4A1755689B8338777D0915E9ACD9F6EDBC96CB",
"hashAlgorithm": "hash_256"
}</textarea>
</div>
<div style='display: inline-block; border: 1px solid black; width: 50%;'>
Serialized transaction (in hex):
<pre id="transaction-serialized-output"> </pre>
Calculated transaction hash:
<pre id="transaction-serialized-hash"> </pre>
Generated transaction signature:
<pre id="transaction-signature"> </pre>
</div>
<div style="clear:both"></div>
<label for="transaction-privkey-hex">Private key in hex (in hex):</label>
<input name="transaction-privkey-hex" id="transaction-privkey-hex" size="100" value="1A9FBB379403527C5AF09823D208224F3EF047C0D33575E58A7265E31844EFB2"/>
</div>
<div class='section'>
<h2>transaction disector</h2>
<div style='display: inline-block; vertical-align: top;'>
<label for="transaction-hex">Transaction data (in hex):</label>
<textarea name="transaction-hex" id="transaction-hex" rows="30" cols="100">
38010000
00000000
31c6338364173ff0aef735a028ce88e9d823987c17a96a203355ed52225bef40b686c28c475a5af0aa214a9d4425ed6873b36c2430b49dd3b552b663a013ed0b
e6833171b35bcbf1ebf8236f0bee5e639c3721bc11096cef13bacf5070eb48f4
00000000
01 98 4141
60e3160000000000
b8ce9f8206000000
0fde76073bb6d2779cf64d4ad4d230d00e61aa5906589f67924256875650ad8a
90000000
00000000
46000000
00000000
e6833171b35bcbf1ebf8236f0bee5e639c3721bc11096cef13bacf5070eb48f4
00000000
01 98 4d41
97a967203b33d451
0000000000000000
b04bd105
02 00
0000
41000000
00000000
e6833171b35bcbf1ebf8236f0bee5e639c3721bc11096cef13bacf5070eb48f4
00000000
01 98 4d42
97a967203b33d451
0100000000000000
01 00000000000000
</textarea>
</div>
<div style='display: inline-block; border: 1px solid black; width: 50%;'>
Dissected transaction:
<pre id="transaction-dissected-output"> </pre>
Calculated transaction hash:
<pre id="transaction-dissected-hash"> </pre>
Signature verification:
<div id="transaction-dissected-verify"> </div>
</div>
<div style="clear:both"></div>
<div style='display: inline-block; border: 1px solid black; width: 50%;'>
Try following mainnet transaction (with valid signature)
<pre>B000000000000000CB5F66BE485E944261247B8F0A43B1DCAE15EB3D7E63C4DE
C0AFC93FEB9753EA75ABA60E94844BDEF17F8282F96DC70A9D5267EEA60A370F
2F8C6043F9B4FD04BB96F55104638A1455B1D9BF0273753D86ADBA95182DFF3B
EB56470E6683102D0000000001685441C0440000000000008CC76C470F000000
686CAB4F4761FE018B2ED187905C97B8D1272A0E2C87BC580000010000000000
F82302A23F91ED6B8065140600000000</pre>
</div>
</div>
</form>
<script type="module">
import symbolSdk from './devtools-bundle/bundle.web.js';
const factory = symbolSdk.symbol.TransactionFactory;
const domPrivkeyHex = document.getElementById('privkey-hex');
const domPrivkeyPropagate = document.getElementById('privkey-pubkey-propagate');
const domPubkeyHex = document.getElementById('pubkey-hex');
const domAddressHex = document.getElementById('address-hex');
const domTransactionDescriptorNetwork = document.getElementById('transaction-descriptor-network');
const domTransactionDescriptor = document.getElementById('transaction-descriptor');
const domTransactionHex = document.getElementById('transaction-hex');
const domTransactionPrivkeyHex = document.getElementById('transaction-privkey-hex');
const domTransactionDissectedOutput = document.getElementById('transaction-dissected-output');
const domTransactionDissectedHash = document.getElementById('transaction-dissected-hash');
const domTransactionDissectedVerify = document.getElementById('transaction-dissected-verify');
const domSetText = (domElem, textContent) => {
domElem.replaceChild(document.createTextNode(textContent), domElem.childNodes[0]);
}
const getOrError = (input, converter, domOutput, action) => {
try {
action(converter(input));
} catch (error) {
domSetText(domOutput, `Error: ${error.message}`);
}
}
domPrivkeyHex.addEventListener('input', () => {
const data = domPrivkeyHex.value.replaceAll(/\s/g, '');
const domOutput = document.getElementById('privkey-to-pubkey-output');
getOrError(data, elem => new symbolSdk.PrivateKey(elem), domOutput, privateKey => {
const keyPair = new symbolSdk.facade.SymbolFacade.KeyPair(privateKey);
domSetText(domOutput, keyPair.publicKey);
if (domPrivkeyPropagate.checked) {
domPubkeyHex.value = keyPair.publicKey;
}
});
});
domPrivkeyHex.dispatchEvent(new Event('input', {bublles:true}));
domPubkeyHex.addEventListener('input', () => {
const data = domPubkeyHex.value.replaceAll(/\s/g, '');
const domOutput = document.getElementById('pubkey-to-address-output');
getOrError(data, elem => new symbolSdk.PublicKey(elem), domOutput, publicKey => {
const mainnet = new symbolSdk.facade.SymbolFacade('mainnet');
const testnet = new symbolSdk.facade.SymbolFacade('testnet');
const mainnetAddress = mainnet.network.publicKeyToAddress(publicKey);
const testnetAddress = testnet.network.publicKeyToAddress(publicKey);
const addresses = `mainnet: ${mainnetAddress}\ntestnet: ${testnetAddress}`;
domSetText(domOutput, addresses);
});
}, false);
domPubkeyHex.dispatchEvent(new Event('input', {bublles:true}));
domAddressHex.addEventListener('input', () => {
const addressInHex = domAddressHex.value.replaceAll(/\s/g, '');
const domOutput = document.getElementById('address-hex-to-address-output');
getOrError(addressInHex, symbolSdk.utils.hexToUint8, domOutput, addressAsBytes => {
const address = new symbolSdk.facade.SymbolFacade.Address(addressAsBytes);
const addresses = `${address}`;
domSetText(domOutput, address);
});
}, false);
domAddressHex.dispatchEvent(new Event('input', {bublles:true}));
const updateTransactionOutput = () => {
const networkName = document.querySelector('input[name="transaction-descriptor-network"]:checked').value;
const facade = new symbolSdk.facade.SymbolFacade(networkName);
const domTransactionSerializedOutput = document.getElementById('transaction-serialized-output');
const domTransactionSerializedHash = document.getElementById('transaction-serialized-hash');
const domTransactionSignature = document.getElementById('transaction-signature');
try {
// parse as json and try to treat strings ending with "n" as bigints
const descriptorData = JSONbig.parse(domTransactionDescriptor.value, (key, value) => {
if (('string' === typeof value) && ((value.match(/^0[x][a-f\d]+n$/i) || value.match(/^\d+n$/i)))) {
return BigInt(value.slice(0, -1));
}
return value;
});
let signature = null;
if ('signature' in descriptorData) {
signature = new symbolSdk.Signature(descriptorData['signature']);
console.log('(domTransactionDescriptor) temporarily removing signature', signature);
delete descriptorData['signature'];
}
// build transaction
const transaction = facade.transactionFactory.create(descriptorData);
if (signature) {
console.log('(domTransactionDescriptor) reattaching sig', signature);
symbolSdk.symbol.SymbolTransactionFactory.attachSignature(transaction, signature);
}
const transactionHex = symbolSdk.utils.uint8ToHex(transaction.serialize());
const transactionHexFixed = transactionHex.replaceAll(/(.{64})/g, "$1\n");
domSetText(domTransactionSerializedOutput, transactionHexFixed);
const transactionHash = facade.hashTransaction(transaction);
domSetText(domTransactionSerializedHash, transactionHash);
const data = domTransactionPrivkeyHex.value.replaceAll(/\s/g, '');
const privateKey = new symbolSdk.PrivateKey(data);
const keyPair = new symbolSdk.facade.SymbolFacade.KeyPair(privateKey);
const generatedSignature = facade.signTransaction(keyPair, transaction);
const generatedSignatureHex = symbolSdk.utils.uint8ToHex(generatedSignature.bytes);
domSetText(domTransactionSignature, generatedSignatureHex);
} catch (error) {
domSetText(domTransactionSerializedOutput, `exception occured:\n${error}`);
}
};
domTransactionDescriptor.addEventListener('input', updateTransactionOutput);
domTransactionPrivkeyHex.addEventListener('input', updateTransactionOutput);
document.getElementsByName('transaction-descriptor-network').forEach(x => x.addEventListener('click', updateTransactionOutput));
domTransactionDescriptor.dispatchEvent(new Event('input', {bublles:true}));
const reformat = (buf, a_start, a_level) => {
let formatted='';
const start = a_start || 0;
const level = a_level || 0;
// mode 1 = parse obj, mode 2 = parse array
const mode = 1;
if (level > 10)
return;
let i=start;
while (i < buf.length) {
if (buf[i]==')') {
break;
}
if (buf[i]==']' && level>1) {
break;
}
if (buf[i]=='(') {
formatted += ' '.repeat(2*level);
formatted += '(\n'
const res = reformat(buf, i+1, level+1);
formatted += res.formatted;
i += res.skip;
if (buf[i] !== ')') {
console.log('(reformat) ERROR, expected closing brace');
i = buf.length;
break;
}
formatted += ' '.repeat(2*level);
formatted += ')\n'
i += 1;
break;
}
const identifier = buf.slice(i).match(/([^:]+): /);
formatted += ' '.repeat(2*level);
formatted += identifier[0];
i += identifier[0].length;
if (buf[i]=='[') {
formatted += '[\n';
while (i < buf.length) {
const res = reformat(buf, i+1, level+1);
formatted += res.formatted;
i += res.skip;
if (buf[i] !== ',' && buf[i] !== ']') {
console.log('(reformat) ERROR, expected coma, got', buf[i]);
i = buf.length;
break;
}
if (buf[i] === ']') {
formatted += ' '.repeat(2*level);
formatted += ']\n'
i += 1;
break;
}
console.log('(reformat) got comma, will try to parse NEXT element');
}
if (buf[i] !== ',' || buf[i+1] !== ' ') {
console.log('(reformat) ERROR, expected coma followed by space, got', buf[i]);
i = buf.length;
break;
}
i += 2;
continue;
} else {
const value = buf.slice(i).match(/([^,]+,) /)
formatted += value[1] + '\n';
i += value[0].length;
}
}
return { formatted, skip: i - start + 1 };
};
domTransactionHex.addEventListener('input', () => {
const data = domTransactionHex.value.replaceAll(/\s/g, '');
try {
const transaction_hex = symbolSdk.utils.hexToUint8(data);
const transaction = factory.deserialize(transaction_hex);
const res = reformat(transaction.toString());
domSetText(domTransactionDissectedOutput, res.formatted);
const testnet = new symbolSdk.facade.SymbolFacade('testnet');
if (testnet.network.identifier == transaction.network.value) {
const transactionHash = testnet.hashTransaction(transaction);
domSetText(domTransactionDissectedHash, transactionHash);
const verifyResult = testnet.verifyTransaction(transaction, transaction.signature);
domSetText(domTransactionDissectedVerify, verifyResult ? 'VALID π (testnet)' : 'invalid signature π (testnet)');
} else {
const mainnet = new symbolSdk.facade.SymbolFacade('mainnet');
const transactionHash = mainnet.hashTransaction(transaction);
domSetText(domTransactionDissectedHash, transactionHash);
const verifyResult = mainnet.verifyTransaction(transaction, transaction.signature);
domSetText(domTransactionDissectedVerify, verifyResult ? 'VALID π (mainnet)' : 'invalid signature π (mainnet)');
}
} catch (error) {
domSetText(domTransactionDissectedOutput, `exception occured:\n${error}`);
}
}, false);
domTransactionHex.dispatchEvent(new Event('input', {bubbles:true}));
</script>
</body>
</html>