mirror of
https://github.com/Xahau/xahau.js.git
synced 2026-03-05 04:02:25 +00:00
Compare commits
547 Commits
ripple-bin
...
xahau@4.0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35c294218d | ||
|
|
58150f156e | ||
|
|
d7b3a02ad2 | ||
|
|
ac33a1584d | ||
|
|
8bbc84057c | ||
|
|
0ca36e1314 | ||
|
|
e454c61994 | ||
|
|
b5f15ac075 | ||
|
|
5ec5ad8e1e | ||
|
|
043620b637 | ||
|
|
83a25c6c34 | ||
|
|
7a79042735 | ||
|
|
72ed28b1fe | ||
|
|
aa1ff0e32d | ||
|
|
69e8b786ed | ||
|
|
d6fdad0a30 | ||
|
|
8b21c9adf6 | ||
|
|
aae7e315e8 | ||
|
|
9544e1794e | ||
|
|
92eb809397 | ||
|
|
799cd65386 | ||
|
|
29d145138b | ||
|
|
496f774154 | ||
|
|
0395c14392 | ||
|
|
2a77e2cd91 | ||
|
|
619c9ae13a | ||
|
|
8beb1292b5 | ||
|
|
649bf7d277 | ||
|
|
e0d368791b | ||
|
|
be9b48b071 | ||
|
|
55892c8b89 | ||
|
|
b1b4995047 | ||
|
|
a726a5a4f9 | ||
|
|
8992d3914e | ||
|
|
c2e01b3d10 | ||
|
|
8bfe5b42b8 | ||
|
|
385a56a1f7 | ||
|
|
f3960c3ccc | ||
|
|
a46e86f17e | ||
|
|
8f5d210806 | ||
|
|
17c91cdd3a | ||
|
|
74a41832ce | ||
|
|
275c95752b | ||
|
|
3bc3c2029b | ||
|
|
c9ef96e0a2 | ||
|
|
00f1a6bcdd | ||
|
|
3858a09e1f | ||
|
|
3aaf526107 | ||
|
|
1460cf5026 | ||
|
|
7e733c4446 | ||
|
|
735ac2eb07 | ||
|
|
c79a5db8f2 | ||
|
|
8e2aba3b78 | ||
|
|
39fed49654 | ||
|
|
a0678857a1 | ||
|
|
38e2091fd2 | ||
|
|
a528d6632a | ||
|
|
ab081e7db9 | ||
|
|
493c56c0fa | ||
|
|
c73b2c5a86 | ||
|
|
02e2b0e48e | ||
|
|
d3b03a536d | ||
|
|
036f1f9850 | ||
|
|
92849e57ce | ||
|
|
32f0d7b121 | ||
|
|
b27bbb49b3 | ||
|
|
23adb4924b | ||
|
|
9b3bb9c14b | ||
|
|
d441361999 | ||
|
|
fb94f2a020 | ||
|
|
7b56a49dae | ||
|
|
212686baae | ||
|
|
4d6fef597c | ||
|
|
8b596a6687 | ||
|
|
8afc9ad506 | ||
|
|
3b08d7d379 | ||
|
|
616ad4af60 | ||
|
|
dbdb35abb5 | ||
|
|
445a05e6ef | ||
|
|
ccad092fc2 | ||
|
|
faa23b430e | ||
|
|
923e5d16ac | ||
|
|
602ac481d7 | ||
|
|
b322396a99 | ||
|
|
b9af7bdb6c | ||
|
|
bdb3ad7f3e | ||
|
|
1fdbf85d47 | ||
|
|
3e3911464f | ||
|
|
be732a4a6b | ||
|
|
e505843dc6 | ||
|
|
ff9489ba10 | ||
|
|
365763d6f7 | ||
|
|
9349a6ba1a | ||
|
|
34f35a5912 | ||
|
|
05f16068ff | ||
|
|
ddda7f4552 | ||
|
|
38406212c3 | ||
|
|
62a0d39ac3 | ||
|
|
0e6ab4e4ee | ||
|
|
a09084289b | ||
|
|
a58bf2663b | ||
|
|
59f0b64372 | ||
|
|
1cdef42d6d | ||
|
|
e9b80e79a3 | ||
|
|
63c71a162d | ||
|
|
38b385969b | ||
|
|
eefb52a9cb | ||
|
|
9cdb60e26a | ||
|
|
e2433101cb | ||
|
|
4c7f46c111 | ||
|
|
960182ecc3 | ||
|
|
134924c395 | ||
|
|
e8f89db00c | ||
|
|
88d8a7b73e | ||
|
|
83b9780b5b | ||
|
|
3f1739895a | ||
|
|
fa4eabef0e | ||
|
|
c5fc25efc2 | ||
|
|
294509cf79 | ||
|
|
3c8a990e6a | ||
|
|
217b111ef2 | ||
|
|
5607320ce2 | ||
|
|
c143dc3e99 | ||
|
|
83870acbfb | ||
|
|
83442b18ba | ||
|
|
1a83997e49 | ||
|
|
3d86318195 | ||
|
|
9676b218b7 | ||
|
|
cddb048588 | ||
|
|
3b70a3b919 | ||
|
|
21c2423bac | ||
|
|
a0e53ac9e1 | ||
|
|
041055082a | ||
|
|
07afcea97d | ||
|
|
328987a0a3 | ||
|
|
0050b2f75e | ||
|
|
3b7dd4ea87 | ||
|
|
8caf2e45bb | ||
|
|
cfb64c3f88 | ||
|
|
30bbc3f837 | ||
|
|
9f8502d522 | ||
|
|
f7d0cfb93a | ||
|
|
e4ccc11236 | ||
|
|
e7e5a3a506 | ||
|
|
e1f651d8c7 | ||
|
|
205043fc62 | ||
|
|
b2c96714a8 | ||
|
|
1efb9d1e9c | ||
|
|
2d5899f87f | ||
|
|
186c778b6c | ||
|
|
407fe0e7a0 | ||
|
|
fb5d4e1767 | ||
|
|
0af516cd6b | ||
|
|
999416a5a9 | ||
|
|
33b8c8ba3e | ||
|
|
300e6e782e | ||
|
|
94a9bfab42 | ||
|
|
99dd0eb44c | ||
|
|
98abafbf12 | ||
|
|
9a85aaa109 | ||
|
|
b555f39e40 | ||
|
|
d1ca360537 | ||
|
|
3f181fa29d | ||
|
|
e05ca2b359 | ||
|
|
c584961767 | ||
|
|
ca689cf9f8 | ||
|
|
4b9d506167 | ||
|
|
2f8e9bfb03 | ||
|
|
1d988aa7e1 | ||
|
|
73f48bd771 | ||
|
|
740210d9a2 | ||
|
|
c8f25a6347 | ||
|
|
22dd17d6b7 | ||
|
|
9983fd7919 | ||
|
|
5e0bd328eb | ||
|
|
f6a18a9a95 | ||
|
|
6bb7fb14a1 | ||
|
|
6109f1fe46 | ||
|
|
0bcbea2a95 | ||
|
|
7d87bf524c | ||
|
|
b47bb39682 | ||
|
|
14f40f1f62 | ||
|
|
0ae9d8d184 | ||
|
|
288cfc5b6d | ||
|
|
c58af28c2a | ||
|
|
bff4144fbf | ||
|
|
68beac73d1 | ||
|
|
a11924b8fc | ||
|
|
c47aa58354 | ||
|
|
e0d12141c7 | ||
|
|
7e56c24217 | ||
|
|
6ed04156e4 | ||
|
|
382d07c4b5 | ||
|
|
7a950f3228 | ||
|
|
c431264fa9 | ||
|
|
530cd823aa | ||
|
|
e09d84f2a2 | ||
|
|
29c5a70ebc | ||
|
|
8b78147945 | ||
|
|
65bf5d40ea | ||
|
|
8e929c5a57 | ||
|
|
e941b73c4b | ||
|
|
523b4f2df0 | ||
|
|
976d7b6460 | ||
|
|
19f6288f16 | ||
|
|
032b5b7c5a | ||
|
|
8b03c995b0 | ||
|
|
edf1155cdd | ||
|
|
752de7d8aa | ||
|
|
3c0a31c8e3 | ||
|
|
a60a3f443f | ||
|
|
cc4b3b960e | ||
|
|
f9824b4fe4 | ||
|
|
1e0718856a | ||
|
|
9572527280 | ||
|
|
bc0ac6dfee | ||
|
|
fa9701eb7c | ||
|
|
97d1c566bf | ||
|
|
b35e1a0ae2 | ||
|
|
eaaae864d1 | ||
|
|
23276e9da7 | ||
|
|
aea2408474 | ||
|
|
14098a7a99 | ||
|
|
e51526ae8c | ||
|
|
ec72f9ac8e | ||
|
|
60b600faea | ||
|
|
5b3c7ae782 | ||
|
|
62cea624f8 | ||
|
|
b9ef0de2df | ||
|
|
91e7369f1b | ||
|
|
d7323a5fcf | ||
|
|
cad0a2a163 | ||
|
|
7679df4551 | ||
|
|
556addff64 | ||
|
|
f6f5f6b09a | ||
|
|
c1cf96ae43 | ||
|
|
87da37a0db | ||
|
|
e52b27e097 | ||
|
|
8e5aa9df99 | ||
|
|
b45ad595ae | ||
|
|
ca60b76939 | ||
|
|
b2da19e1c2 | ||
|
|
99f8a1e98e | ||
|
|
800babbd26 | ||
|
|
c08aa44fe6 | ||
|
|
beafaf3eae | ||
|
|
7687955040 | ||
|
|
0a349ab8fb | ||
|
|
c912abeede | ||
|
|
d91c6e4abf | ||
|
|
37fbc03584 | ||
|
|
49447a9beb | ||
|
|
5581474627 | ||
|
|
93ea98ac4a | ||
|
|
2192e11267 | ||
|
|
352abae003 | ||
|
|
aa75806f2f | ||
|
|
3d94a24739 | ||
|
|
bedb1f0f31 | ||
|
|
6856dec466 | ||
|
|
bd46f19604 | ||
|
|
002ec77218 | ||
|
|
c4097935a9 | ||
|
|
da6fa296c6 | ||
|
|
12be15af40 | ||
|
|
a05f68a352 | ||
|
|
e3eabb5bac | ||
|
|
fa28b47111 | ||
|
|
f760b2e56f | ||
|
|
03ba07dda1 | ||
|
|
8972e5e463 | ||
|
|
104afa4b71 | ||
|
|
c6f6c78d48 | ||
|
|
0972fc29e8 | ||
|
|
63ea6e8c9b | ||
|
|
c564161871 | ||
|
|
5ec6729c61 | ||
|
|
d158770c24 | ||
|
|
e760a4898d | ||
|
|
d47a277e25 | ||
|
|
c523a7c80b | ||
|
|
6a31d4d254 | ||
|
|
19792d4fbb | ||
|
|
f07e2c7108 | ||
|
|
82436918b7 | ||
|
|
74de24cf75 | ||
|
|
eccba409c0 | ||
|
|
c02b33be08 | ||
|
|
dbd5852ba6 | ||
|
|
5925ad2f00 | ||
|
|
9b0c1e53e3 | ||
|
|
23256aed5d | ||
|
|
70500dcc15 | ||
|
|
fd0b2275c1 | ||
|
|
97552cc1a5 | ||
|
|
b152ebc4ce | ||
|
|
b6d6fafebc | ||
|
|
dc51e3a704 | ||
|
|
6228f91c00 | ||
|
|
fa98bd8d26 | ||
|
|
6fa3eacd19 | ||
|
|
5b0989df51 | ||
|
|
b410bde412 | ||
|
|
3791c6292c | ||
|
|
5726a6251d | ||
|
|
695c89eda4 | ||
|
|
848e5bbe78 | ||
|
|
44995a7e49 | ||
|
|
456eac0628 | ||
|
|
6fbfab4fa4 | ||
|
|
b3e520b0e6 | ||
|
|
29c26ea227 | ||
|
|
d3ac518d8e | ||
|
|
127cba571d | ||
|
|
24f1fd7295 | ||
|
|
c647be701e | ||
|
|
70a9ab0510 | ||
|
|
82c5b3598a | ||
|
|
317e2054de | ||
|
|
0853662a82 | ||
|
|
9035dd3f11 | ||
|
|
5ee974cfa1 | ||
|
|
ee6c8350a4 | ||
|
|
97b2e668ab | ||
|
|
add72a175d | ||
|
|
e9184b8435 | ||
|
|
a363c80da9 | ||
|
|
9dd5f6def6 | ||
|
|
6b1ac0bd4f | ||
|
|
4cca7c9088 | ||
|
|
ac8c1b3f64 | ||
|
|
5a95261805 | ||
|
|
2e6d2dc805 | ||
|
|
0b929dde85 | ||
|
|
81f2233507 | ||
|
|
9131ab2515 | ||
|
|
2442ef1415 | ||
|
|
15e5eb552e | ||
|
|
373609c89c | ||
|
|
d3d24bc9fd | ||
|
|
36fd4a7d43 | ||
|
|
028b4b69a1 | ||
|
|
6f00f99290 | ||
|
|
81ad246ebe | ||
|
|
80b8e4f38a | ||
|
|
861fc3b3d1 | ||
|
|
6cffb78569 | ||
|
|
b86f736cab | ||
|
|
7a89f8c63e | ||
|
|
4ffc51ad54 | ||
|
|
d2224e9cfb | ||
|
|
70745f4c94 | ||
|
|
c5433c6ac0 | ||
|
|
be2aa32542 | ||
|
|
18e777b093 | ||
|
|
62a39c69ce | ||
|
|
dfd2fc1ba1 | ||
|
|
41563d1529 | ||
|
|
d57233fc49 | ||
|
|
bbbdf06e91 | ||
|
|
0c6ea2afe2 | ||
|
|
97ff2aa104 | ||
|
|
b3d3bc2f99 | ||
|
|
4ec8c99056 | ||
|
|
3c2bfdf766 | ||
|
|
2286d295bb | ||
|
|
0fa5415d29 | ||
|
|
5d34746f12 | ||
|
|
3d0bec7e89 | ||
|
|
fde954640f | ||
|
|
b2dcce75b0 | ||
|
|
e6c7c88209 | ||
|
|
b1af321eb0 | ||
|
|
8fe87c0e86 | ||
|
|
bef2f79fee | ||
|
|
7dd38f6a76 | ||
|
|
cb8cec2caa | ||
|
|
5fe480ece4 | ||
|
|
5a63f18faf | ||
|
|
c74ffddf59 | ||
|
|
bbc9bd9e4a | ||
|
|
c6cebefdbe | ||
|
|
62a5b60b0d | ||
|
|
45963b7035 | ||
|
|
ad32205306 | ||
|
|
17f04b3083 | ||
|
|
8abcfe4640 | ||
|
|
9e74f94c44 | ||
|
|
b2c2b59a32 | ||
|
|
2944a0f768 | ||
|
|
c4af811629 | ||
|
|
0ff51fe34c | ||
|
|
2f150b6219 | ||
|
|
f6b810e7ba | ||
|
|
46515627ab | ||
|
|
51affe19b7 | ||
|
|
4cf6a47879 | ||
|
|
e52ca91d77 | ||
|
|
93c8562e55 | ||
|
|
8921b6ac93 | ||
|
|
5f5f06f1ab | ||
|
|
a4c2bb998f | ||
|
|
c4c8e58fd9 | ||
|
|
b0cc81f40b | ||
|
|
49d40216d5 | ||
|
|
08180cb935 | ||
|
|
f33e748a26 | ||
|
|
d0534f6db4 | ||
|
|
b40a519a0d | ||
|
|
3bc87c00ec | ||
|
|
1ceae703db | ||
|
|
9df848fcc6 | ||
|
|
0e03b92675 | ||
|
|
bd1d367b89 | ||
|
|
91f4950bd3 | ||
|
|
984a58e642 | ||
|
|
b20c05c368 | ||
|
|
5ab4b67c05 | ||
|
|
8a9a9bcc28 | ||
|
|
76b73e16a9 | ||
|
|
1673f4b964 | ||
|
|
d8dbd44f56 | ||
|
|
5fc1c795bc | ||
|
|
02ed92807e | ||
|
|
438242548e | ||
|
|
6187571334 | ||
|
|
295725361c | ||
|
|
93fae19ae1 | ||
|
|
b420af3914 | ||
|
|
7e0b6849b0 | ||
|
|
ae6390ffcb | ||
|
|
e42d82b158 | ||
|
|
d7c3583a8f | ||
|
|
fdfef0e188 | ||
|
|
14ffaae960 | ||
|
|
8eab8b3c04 | ||
|
|
aff6988f09 | ||
|
|
9ff9fc7767 | ||
|
|
89240eae62 | ||
|
|
7732f22858 | ||
|
|
4fddd8f41d | ||
|
|
f5b2d71cd7 | ||
|
|
6c3f5a0e3a | ||
|
|
42ea7d83bc | ||
|
|
9689f31602 | ||
|
|
c994ac7b65 | ||
|
|
a3fb213d3d | ||
|
|
10469a2710 | ||
|
|
7c75e5b489 | ||
|
|
73546f203a | ||
|
|
317e223ce7 | ||
|
|
0486037ee4 | ||
|
|
9b7add7f2f | ||
|
|
68d337cd55 | ||
|
|
51d1115328 | ||
|
|
05930c0991 | ||
|
|
3968fead9b | ||
|
|
4ed999c243 | ||
|
|
35f5ad8ddb | ||
|
|
95123392c4 | ||
|
|
17d57abcd0 | ||
|
|
b6afb4e434 | ||
|
|
e2dd680bde | ||
|
|
1e20d4fab7 | ||
|
|
bbe2e76efd | ||
|
|
7c821d148b | ||
|
|
4ae116b10c | ||
|
|
6d3b2d3a9e | ||
|
|
91403ec2fa | ||
|
|
13587e563a | ||
|
|
a8b3de707d | ||
|
|
0a3ad3140c | ||
|
|
1c6b175619 | ||
|
|
0b9c27ff5f | ||
|
|
86001d51f9 | ||
|
|
f17d0ef810 | ||
|
|
7d2a776cc2 | ||
|
|
fc6f85e451 | ||
|
|
3ed93aa19e | ||
|
|
417978e131 | ||
|
|
de0ce1b60f | ||
|
|
8ab4240f02 | ||
|
|
9d94f4a8ab | ||
|
|
2f252ace14 | ||
|
|
587a3bbfa2 | ||
|
|
3b980d8be9 | ||
|
|
96e9122f6a | ||
|
|
435c447052 | ||
|
|
d25905987a | ||
|
|
53d01d8a6a | ||
|
|
1258615a0c | ||
|
|
1e30b4cb43 | ||
|
|
9d1fdb412e | ||
|
|
5388f5f6d0 | ||
|
|
cbdbfe5b8e | ||
|
|
b0e0f9f329 | ||
|
|
8f79c51c7c | ||
|
|
3ac29194ba | ||
|
|
36d4f4fb0b | ||
|
|
dc463eaf30 | ||
|
|
2d1981b0b6 | ||
|
|
5ca7cf8d20 | ||
|
|
381d043206 | ||
|
|
9531e00117 | ||
|
|
caee633fe3 | ||
|
|
e4c7923bf3 | ||
|
|
fa3ae25c71 | ||
|
|
4f035fd0d6 | ||
|
|
d9089b9c1f | ||
|
|
4cd7f07b7a | ||
|
|
c8150607d9 | ||
|
|
933d4ac47c | ||
|
|
7cd805e629 | ||
|
|
b12ec88365 | ||
|
|
5e24929f93 | ||
|
|
803ea6d2cb | ||
|
|
7041a0bbd3 | ||
|
|
5a521d19d6 | ||
|
|
07f36e127f | ||
|
|
5d556c6afe | ||
|
|
d021c1412f | ||
|
|
388c4a6124 | ||
|
|
ff126bd39f | ||
|
|
7d6d119e4c | ||
|
|
46a8adcac9 | ||
|
|
c0a19a8417 | ||
|
|
3756afb8ca | ||
|
|
78d9b11794 | ||
|
|
4e0a093677 | ||
|
|
fe845de884 | ||
|
|
8461ef6804 | ||
|
|
7c6230b18e | ||
|
|
0eb64b1447 | ||
|
|
bd7b6282e3 | ||
|
|
8bb248ca9e | ||
|
|
f027236b55 | ||
|
|
3e7a722a4c | ||
|
|
e4d70b53d5 | ||
|
|
3a3f306f18 | ||
|
|
fc101c6733 | ||
|
|
3e55066b70 | ||
|
|
ff9bdbdc4c | ||
|
|
89b8833610 | ||
|
|
887cb97c2b | ||
|
|
1c90c962e9 | ||
|
|
5da0db89f4 | ||
|
|
f522d3f418 |
56
.ci-config/getNewAmendments.js
Normal file
56
.ci-config/getNewAmendments.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
const xahau = require("xahau");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const filePath = path.resolve(__dirname, "./rippled.cfg");
|
||||||
|
const existingConfig = fs.readFileSync(filePath, "utf-8");
|
||||||
|
|
||||||
|
const networkToEmulate = "wss://s.devnet.rippletest.net:51233/";
|
||||||
|
|
||||||
|
const amendmentsToIgnore = [
|
||||||
|
"86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90", // CryptoConditionsSuite is obsolete
|
||||||
|
];
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const client = new xahau.Client(networkToEmulate);
|
||||||
|
await client.connect();
|
||||||
|
|
||||||
|
// Looks up what amendments have been enabled via their hash
|
||||||
|
const request = {
|
||||||
|
command: "ledger_entry",
|
||||||
|
index: "7DB0788C020F02780A673DC74757F23823FA3014C1866E72CC4CD8B226CD6EF4",
|
||||||
|
ledger_index: "validated",
|
||||||
|
};
|
||||||
|
const response = await client.request(request);
|
||||||
|
|
||||||
|
const amendments = response.result.node.Amendments;
|
||||||
|
|
||||||
|
const newAmendments = [];
|
||||||
|
amendments.forEach((amendment) => {
|
||||||
|
if (
|
||||||
|
!existingConfig.includes(amendment) &&
|
||||||
|
!amendmentsToIgnore.includes(amendment)
|
||||||
|
) {
|
||||||
|
newAmendments.push(amendment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newAmendments.length > 0) {
|
||||||
|
console.log(
|
||||||
|
"New Amendment Hashes - Look up their names on https://xrpl.org/known-amendments.html"
|
||||||
|
);
|
||||||
|
newAmendments.forEach((amendment) => {
|
||||||
|
console.log(amendment);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`No new amendments to add!
|
||||||
|
Looking at network: ${networkToEmulate}.
|
||||||
|
Path to config: ${filePath}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => console.log(error));
|
||||||
169
.ci-config/xahaud.cfg
Normal file
169
.ci-config/xahaud.cfg
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
[server]
|
||||||
|
port_rpc_admin_local
|
||||||
|
port_ws_public
|
||||||
|
port_ws_admin_local
|
||||||
|
|
||||||
|
# port_peer
|
||||||
|
# port_ws_admin_local
|
||||||
|
# ssl_key = /etc/ssl/private/server.key
|
||||||
|
# ssl_cert = /etc/ssl/certs/server.crt
|
||||||
|
|
||||||
|
# IPs must be 0.0.0.0 instead of 127.0.0.1 to be accessed outside the docker container
|
||||||
|
|
||||||
|
[port_rpc_admin_local]
|
||||||
|
port = 5005
|
||||||
|
ip = 0.0.0.0
|
||||||
|
admin = 0.0.0.0
|
||||||
|
protocol = http
|
||||||
|
|
||||||
|
[port_ws_public]
|
||||||
|
port = 80
|
||||||
|
ip = 0.0.0.0
|
||||||
|
protocol = ws
|
||||||
|
|
||||||
|
# [port_peer]
|
||||||
|
# port = 51235
|
||||||
|
# ip = 0.0.0.0
|
||||||
|
# protocol = peer
|
||||||
|
|
||||||
|
[port_ws_admin_local]
|
||||||
|
port = 6006
|
||||||
|
ip = 0.0.0.0
|
||||||
|
admin = 0.0.0.0
|
||||||
|
protocol = ws
|
||||||
|
|
||||||
|
[node_size]
|
||||||
|
small
|
||||||
|
|
||||||
|
# tiny
|
||||||
|
# small
|
||||||
|
# medium
|
||||||
|
# large
|
||||||
|
# huge
|
||||||
|
|
||||||
|
[node_db]
|
||||||
|
type=NuDB
|
||||||
|
path=/var/lib/rippled/db/nudb
|
||||||
|
advisory_delete=0
|
||||||
|
|
||||||
|
# How many ledgers do we want to keep (history)?
|
||||||
|
# Integer value that defines the number of ledgers
|
||||||
|
# between online deletion events
|
||||||
|
online_delete=256
|
||||||
|
|
||||||
|
[ledger_history]
|
||||||
|
# How many ledgers do we want to keep (history)?
|
||||||
|
# Integer value (ledger count)
|
||||||
|
# or (if you have lots of TB SSD storage): 'full'
|
||||||
|
256
|
||||||
|
|
||||||
|
[database_path]
|
||||||
|
/var/lib/rippled/db
|
||||||
|
|
||||||
|
[debug_logfile]
|
||||||
|
/var/log/rippled/debug.log
|
||||||
|
|
||||||
|
|
||||||
|
[ips]
|
||||||
|
r.ripple.com 51235
|
||||||
|
|
||||||
|
# [validators_file]
|
||||||
|
# validators.txt
|
||||||
|
|
||||||
|
[rpc_startup]
|
||||||
|
{ "command": "log_level", "severity": "info" }
|
||||||
|
|
||||||
|
# severity (order: lots of information .. only errors)
|
||||||
|
# debug
|
||||||
|
# info
|
||||||
|
# warn
|
||||||
|
# error
|
||||||
|
# fatal
|
||||||
|
|
||||||
|
[ssl_verify]
|
||||||
|
1
|
||||||
|
|
||||||
|
# The [features] stanza does not currently work for standalone mode: https://github.com/XRPLF/xrpl-dev-portal/issues/1762#issuecomment-1441252450
|
||||||
|
|
||||||
|
|
||||||
|
# In order to enable an amendment which by default would vote "No", you must include its amendment id and name here.
|
||||||
|
# To add amendments specifically from the latest releases of rippled:
|
||||||
|
# 1. Go to https://xrpl.org/known-amendments.html
|
||||||
|
# 2. Find the first amendment in the latest releases of rippled which are not already in the list below
|
||||||
|
# 3. Click on each amendment to get their Amendment ID and name to add to this list manually.
|
||||||
|
# You will likely update the list with all amendments from a new release of rippled all at once.
|
||||||
|
|
||||||
|
# To get the list of amendments on a network (e.g. devnet) follow the steps in xahau.js's CONTRIBUTING.md for "Updating the Docker container".
|
||||||
|
# https://github.com/Xahau/xahau.js/blob/main/CONTRIBUTING.md
|
||||||
|
# (Running the script `getNewAmendments.js` should help you identify any new amendments that should be added.)
|
||||||
|
#
|
||||||
|
# Note: The version of rippled you use this config with must have an implementation for the amendments you attempt to enable or it will crash.
|
||||||
|
# If you need the version of rippled to be more up to date, you may need to make a comment on this repo: https://github.com/WietseWind/docker-rippled
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Amendments
|
||||||
|
NegativeUNL
|
||||||
|
fixRemoveNFTokenAutoTrustLine
|
||||||
|
NonFungibleTokensV1
|
||||||
|
CheckCashMakesTrustLine
|
||||||
|
fixRmSmallIncreasedQOffers
|
||||||
|
fixSTAmountCanonicalize
|
||||||
|
FlowSortStrands
|
||||||
|
TicketBatch
|
||||||
|
fixQualityUpperBound
|
||||||
|
FlowCross
|
||||||
|
HardenedValidations
|
||||||
|
DepositPreauth
|
||||||
|
MultiSignReserve
|
||||||
|
fix1623
|
||||||
|
fix1513
|
||||||
|
RequireFullyCanonicalSig
|
||||||
|
fix1543
|
||||||
|
fix1781
|
||||||
|
fixCheckThreading
|
||||||
|
fix1515
|
||||||
|
CryptoConditionsSuite
|
||||||
|
fixPayChanRecipientOwnerDir
|
||||||
|
fix1578
|
||||||
|
fix1571
|
||||||
|
fixAmendmentMajorityCalc
|
||||||
|
fixTakerDryOfferRemoval
|
||||||
|
fixMasterKeyAsRegularKey
|
||||||
|
Flow
|
||||||
|
DeletableAccounts
|
||||||
|
DepositAuth
|
||||||
|
Checks
|
||||||
|
NonFungibleTokensV1_1
|
||||||
|
DisallowIncoming
|
||||||
|
fixNonFungibleTokensV1_2
|
||||||
|
fixUniversalNumber
|
||||||
|
ImmediateOfferKilled
|
||||||
|
XRPFees
|
||||||
|
ExpandedSignerList
|
||||||
|
fixNFTokenRemint
|
||||||
|
# Additional Amendments
|
||||||
|
BalanceRewards
|
||||||
|
Hooks
|
||||||
|
HooksUpdate1
|
||||||
|
Import
|
||||||
|
Remit
|
||||||
|
URIToken
|
||||||
|
XahauGenesis
|
||||||
|
ZeroB2M
|
||||||
|
fix240819
|
||||||
|
fix240911
|
||||||
|
fixFloatDivide
|
||||||
|
fixNFTokenDirV1
|
||||||
|
fixNFTokenNegOffer
|
||||||
|
fixNSDelete
|
||||||
|
fixPageCap
|
||||||
|
fixReduceImport
|
||||||
|
fixXahauV1
|
||||||
|
fixXahauV2
|
||||||
|
fixXahauV3
|
||||||
|
PaychanAndEscrowForTokens
|
||||||
|
DeepFreeze
|
||||||
|
Clawback
|
||||||
|
|
||||||
|
[network_id]
|
||||||
|
21337
|
||||||
28
.coderabbit.yaml
Normal file
28
.coderabbit.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
||||||
|
language: "en-US"
|
||||||
|
reviews:
|
||||||
|
# Set the profile for reviews. Assertive profile yields more feedback, that may be considered nitpicky.
|
||||||
|
profile: "chill"
|
||||||
|
# Approve the review once CodeRabbit's comments are resolved. Note: In GitLab, all discussions must be resolved.
|
||||||
|
request_changes_workflow: false
|
||||||
|
# Generate a high level summary of the changes in the PR/MR description.
|
||||||
|
high_level_summary: false
|
||||||
|
# Generate a poem in the walkthrough comment.
|
||||||
|
poem: true
|
||||||
|
# Post review details on each review. Additionally, post a review status when a review is skipped in certain cases.
|
||||||
|
review_status: true
|
||||||
|
# Generate walkthrough in a markdown collapsible section.
|
||||||
|
collapse_walkthrough: false
|
||||||
|
# Abort the in-progress review if the pull request is closed or merged.
|
||||||
|
abort_on_close: true
|
||||||
|
auto_review:
|
||||||
|
# Automatic Review | Automatic code review
|
||||||
|
enabled: true
|
||||||
|
# Review draft PRs/MRs.
|
||||||
|
drafts: false
|
||||||
|
# Ignore reviewing if the title of the pull request contains any of these keywords (case-insensitive).
|
||||||
|
ignore_title_keywords:
|
||||||
|
- build(
|
||||||
|
chat:
|
||||||
|
# Enable the bot to reply automatically without requiring the user to tag it.
|
||||||
|
auto_reply: true
|
||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -3,6 +3,6 @@ updates:
|
|||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: weekly
|
||||||
time: "15:00"
|
time: "15:00"
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
|||||||
10
.github/pull_request_template.md
vendored
10
.github/pull_request_template.md
vendored
@@ -31,12 +31,10 @@ Please check relevant options, delete irrelevant ones.
|
|||||||
- [ ] Documentation Updates
|
- [ ] Documentation Updates
|
||||||
- [ ] Release
|
- [ ] Release
|
||||||
|
|
||||||
## Before / After
|
### Did you update HISTORY.md?
|
||||||
|
|
||||||
<!--
|
- [ ] Yes
|
||||||
If just refactoring / back-end changes, this can be just an in-English description of the change at a technical level.
|
- [ ] No, this change does not impact library users
|
||||||
If a UI change, screenshots should be included.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Test Plan
|
## Test Plan
|
||||||
|
|
||||||
@@ -47,4 +45,4 @@ Please describe the tests that you ran to verify your changes and provide instru
|
|||||||
<!--
|
<!--
|
||||||
## Future Tasks
|
## Future Tasks
|
||||||
For future tasks related to PR.
|
For future tasks related to PR.
|
||||||
-->
|
-->
|
||||||
|
|||||||
58
.github/workflows/codeql-analysis.yml
vendored
58
.github/workflows/codeql-analysis.yml
vendored
@@ -12,13 +12,11 @@
|
|||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches: [ develop, master ]
|
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ develop ]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '44 5 * * 6'
|
- cron: "44 5 * * 6"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
@@ -28,40 +26,40 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'javascript' ]
|
language: ["javascript"]
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||||
# Learn more:
|
# Learn more:
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
# and modify them (or add more) to build your code if your project
|
# and modify them (or add more) to build your code if your project
|
||||||
# uses a compiled language
|
# uses a compiled language
|
||||||
|
|
||||||
#- run: |
|
#- run: |
|
||||||
# make bootstrap
|
# make bootstrap
|
||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
219
.github/workflows/nodejs.yml
vendored
219
.github/workflows/nodejs.yml
vendored
@@ -3,101 +3,198 @@
|
|||||||
|
|
||||||
name: Node.js CI
|
name: Node.js CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
XAHAUD_DOCKER_IMAGE: xahauci/xahaud:2025.7.9
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ develop, master, 2.0 ]
|
branches: [main-xahau, 1.x]
|
||||||
pull_request:
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-lint:
|
build-and-lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [14.x]
|
node-version: [18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm install -g npm@7
|
|
||||||
- run: npm ci
|
- name: Setup npm version 9
|
||||||
- run: npm run build
|
run: |
|
||||||
- run: npm run lint
|
npm i -g npm@9 --registry=https://registry.npmjs.org
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache-nodemodules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
# caching node_modules
|
||||||
|
path: |
|
||||||
|
node_modules
|
||||||
|
*/*/node_modules
|
||||||
|
key: ${{ runner.os }}-deps-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-deps-${{ matrix.node-version }}-
|
||||||
|
${{ runner.os }}-deps-
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- run: npm run build
|
||||||
|
- run: npm run lint
|
||||||
|
|
||||||
unit:
|
unit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x, 14.x, 16.x]
|
node-version: [18.x, 20.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm install -g npm@7
|
|
||||||
- run: npm ci
|
- name: Setup npm version 9
|
||||||
- run: npm run build
|
run: |
|
||||||
- run: npm test
|
npm i -g npm@9 --registry=https://registry.npmjs.org
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache-nodemodules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
# caching node_modules
|
||||||
|
path: |
|
||||||
|
node_modules
|
||||||
|
*/*/node_modules
|
||||||
|
key: ${{ runner.os }}-deps-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-deps-${{ matrix.node-version }}-
|
||||||
|
${{ runner.os }}-deps-
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- run: npm run build
|
||||||
|
- run: npm test
|
||||||
|
|
||||||
integration:
|
integration:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x, 14.x, 16.x]
|
node-version: [18.x, 20.x]
|
||||||
|
|
||||||
services:
|
|
||||||
rippled:
|
|
||||||
image: natenichols/rippled-standalone:latest
|
|
||||||
ports:
|
|
||||||
- 6006:6006
|
|
||||||
options:
|
|
||||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v1
|
- name: Run docker in background
|
||||||
with:
|
run: |
|
||||||
node-version: ${{ matrix.node-version }}
|
docker run --detach --rm --name xahaud-service -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/opt/xahau/etc/" --health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true ${{ env.XAHAUD_DOCKER_IMAGE }} /opt/xahau/bin/xahaud -a --conf /opt/xahau/etc/xahaud.cfg
|
||||||
- run: npm install -g npm@7
|
|
||||||
- run: npm ci
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
- run: npm run build
|
uses: actions/setup-node@v3
|
||||||
- run: npm run test:integration
|
with:
|
||||||
env:
|
node-version: ${{ matrix.node-version }}
|
||||||
HOST: localhost
|
|
||||||
PORT: ${{ job.services.rippled.ports['6006'] }}
|
- name: Setup npm version 9
|
||||||
|
run: |
|
||||||
|
npm i -g npm@9 --registry=https://registry.npmjs.org
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache-nodemodules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
# caching node_modules
|
||||||
|
path: |
|
||||||
|
node_modules
|
||||||
|
*/*/node_modules
|
||||||
|
key: ${{ runner.os }}-deps-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-deps-${{ matrix.node-version }}-
|
||||||
|
${{ runner.os }}-deps-
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- run: npm run build
|
||||||
|
|
||||||
|
- name: Run integration test
|
||||||
|
run: npm run test:integration
|
||||||
|
|
||||||
|
- name: Stop docker container
|
||||||
|
if: always()
|
||||||
|
run: docker stop xahaud-service
|
||||||
|
|
||||||
browser:
|
browser:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [14.x] # This just needs to be compatible w/ puppeteer
|
node-version: [18.x]
|
||||||
|
|
||||||
services:
|
|
||||||
rippled:
|
|
||||||
image: natenichols/rippled-standalone:latest
|
|
||||||
ports:
|
|
||||||
- 6006:6006
|
|
||||||
options:
|
|
||||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v1
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
with:
|
uses: actions/setup-node@v3
|
||||||
node-version: ${{ matrix.node-version }}
|
with:
|
||||||
- run: npm install -g npm@7
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
|
||||||
- run: npm run build
|
- name: Run docker in background
|
||||||
- run: npm run test:browser
|
run: |
|
||||||
|
docker run --detach --rm --name xahaud-service -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/opt/xahau/etc/" --health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true ${{ env.XAHAUD_DOCKER_IMAGE }} /opt/xahau/bin/xahaud -a --conf /opt/xahau/etc/xahaud.cfg
|
||||||
|
|
||||||
|
- name: Setup npm version 9
|
||||||
|
run: |
|
||||||
|
npm i -g npm@9 --registry=https://registry.npmjs.org
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
id: cache-nodemodules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
# caching node_modules
|
||||||
|
path: |
|
||||||
|
node_modules
|
||||||
|
*/*/node_modules
|
||||||
|
key: ${{ runner.os }}-deps-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-deps-${{ matrix.node-version }}-
|
||||||
|
${{ runner.os }}-deps-
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- run: npm run build
|
||||||
|
|
||||||
|
- name: Run integration test
|
||||||
|
run: npm run test:browser
|
||||||
|
|
||||||
|
- name: Stop docker container
|
||||||
|
if: always()
|
||||||
|
run: docker stop xahaud-service
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -22,6 +22,7 @@ npm-shrinkwrap.json
|
|||||||
# Ignore object files.
|
# Ignore object files.
|
||||||
*.o
|
*.o
|
||||||
build/
|
build/
|
||||||
|
coverage/
|
||||||
tags
|
tags
|
||||||
bin/rippled
|
bin/rippled
|
||||||
Debug/*.*
|
Debug/*.*
|
||||||
@@ -39,8 +40,6 @@ db/*.db
|
|||||||
db/*.db-*
|
db/*.db-*
|
||||||
|
|
||||||
# Ignore customized configs
|
# Ignore customized configs
|
||||||
rippled.cfg
|
|
||||||
validators.txt
|
|
||||||
test/config.js
|
test/config.js
|
||||||
|
|
||||||
# Ignore coverage files
|
# Ignore coverage files
|
||||||
@@ -51,6 +50,7 @@ test/config.js
|
|||||||
|
|
||||||
# Ignore IntelliJ files
|
# Ignore IntelliJ files
|
||||||
.idea
|
.idea
|
||||||
|
*.iml
|
||||||
|
|
||||||
# Ignore npm-debug
|
# Ignore npm-debug
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|||||||
4
.ncurc.json
Normal file
4
.ncurc.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"reject": [
|
||||||
|
]
|
||||||
|
}
|
||||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.md
|
||||||
26
.vscode/settings.json
vendored
26
.vscode/settings.json
vendored
@@ -1,7 +1,21 @@
|
|||||||
{
|
{
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"secp256k1"
|
"altnet",
|
||||||
|
"Autofills",
|
||||||
|
"Clawback",
|
||||||
|
"hostid",
|
||||||
|
"keypair",
|
||||||
|
"keypairs",
|
||||||
|
"multisign",
|
||||||
|
"multisigned",
|
||||||
|
"multisigning",
|
||||||
|
"preauthorization",
|
||||||
|
"rippletest",
|
||||||
|
"secp256k1",
|
||||||
|
"Setf",
|
||||||
|
"Sidechains",
|
||||||
|
"xchain"
|
||||||
],
|
],
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
@@ -25,7 +39,7 @@
|
|||||||
"enable": true
|
"enable": true
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.trimFinalNewlines": true,
|
"files.trimFinalNewlines": true,
|
||||||
@@ -35,4 +49,12 @@
|
|||||||
"**/.git/subtree-cache/**": true,
|
"**/.git/subtree-cache/**": true,
|
||||||
"**/.hg/store/**": true
|
"**/.hg/store/**": true
|
||||||
},
|
},
|
||||||
|
"search.exclude": {
|
||||||
|
"**/.git": true,
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/tmp": true,
|
||||||
|
"**/docs/**/*.html": true,
|
||||||
|
"**/fixtures/**/*.json": true,
|
||||||
|
"**/docs/assets": true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
163
APPLICATIONS.md
163
APPLICATIONS.md
@@ -1,163 +0,0 @@
|
|||||||
# Applications using xrpl.js
|
|
||||||
|
|
||||||
A curated list of some of the projects and apps that leverage `xrpl.js` in some way.
|
|
||||||
|
|
||||||
**Have one to add?** Please edit this file and open a PR!
|
|
||||||
|
|
||||||
## Notice (disclaimer)
|
|
||||||
|
|
||||||
These sites are independent of Ripple and have not been authorized, endorsed, sponsored or otherwise approved by Ripple or its affiliates.
|
|
||||||
|
|
||||||
Warning: Use at your own risk.
|
|
||||||
|
|
||||||
## Exchanges
|
|
||||||
|
|
||||||
- **[The World Exchange](https://www.theworldexchange.net/)**
|
|
||||||
|
|
||||||
Trade, issue, and send directly on the XRP Ledger. A user interface for the XRPL's decentralized exchange.
|
|
||||||
|
|
||||||
- **[Bitso](https://bitso.com/)**
|
|
||||||
|
|
||||||
Exchange allowing clients to buy and sell XRP, based in Mexico.
|
|
||||||
|
|
||||||
## Explorers
|
|
||||||
|
|
||||||
- **[xrpintel - XRP Intelligence](https://xrpintel.com/)**
|
|
||||||
|
|
||||||
Monitor the XRP Network in real time and explore historical statistics.
|
|
||||||
|
|
||||||
- **[XRP Charts](https://xrpcharts.ripple.com/)** (xrpcharts.ripple.com)
|
|
||||||
|
|
||||||
XRP Charts provides information based on public data, including trade volume, top markets, metrics, transactions, and more.
|
|
||||||
|
|
||||||
- **[Ripple Live](https://gatehub.net/live)** (gatehub.net/live)
|
|
||||||
|
|
||||||
Visualize XRP network transactions.
|
|
||||||
|
|
||||||
- **[XRPL Dev. Dashboard](https://xrp.fans/)** (xrp.fans)
|
|
||||||
|
|
||||||
Debugging dashboard for `rippled-ws-client-pool`, transaction and query explorer, and transaction signing and submission tool.
|
|
||||||
|
|
||||||
- **[XRP Value](http://xrpvalue.com/)**
|
|
||||||
|
|
||||||
Real-time XRP price, trades, and orderbook data from the XRP Ledger.
|
|
||||||
|
|
||||||
- **[Bithomp - XRP Explorer](https://bithomp.com/explorer/)**
|
|
||||||
|
|
||||||
Look up information by entering an address, transaction hash, username, or PayID.
|
|
||||||
|
|
||||||
- **[Bithomp - XRPL validators](https://bithomp.com/validators)**
|
|
||||||
|
|
||||||
List of XRPL validators, nodes, and testnet validators.
|
|
||||||
|
|
||||||
- **[XRP Scan - XRP Ledger explorer](https://xrpscan.com)**
|
|
||||||
|
|
||||||
XRP Ledger explorer, metrics and analytics.
|
|
||||||
|
|
||||||
- **[xrplorer](https://xrplorer.com)**
|
|
||||||
|
|
||||||
XRP Ledger explorer, API, metrics, and analytics using a graph database that is synchronized live with the XRPL.
|
|
||||||
|
|
||||||
## Data monitoring
|
|
||||||
|
|
||||||
- **[zerptracker](https://zerptracker.com)**
|
|
||||||
|
|
||||||
Monitor the XRPL using powerful JSONPath expressions, and receive notifications via email, SMS, webhooks, and more.
|
|
||||||
|
|
||||||
- **[Utility-Scan](https://utility-scan.com)**
|
|
||||||
|
|
||||||
Attempts to detect RippleNet on-demand liquidity (ODL) transactions through known fiat corridors and report these transactions in real time.
|
|
||||||
|
|
||||||
- **[XRPL Rosetta](https://xrpl-rosetta-oepox.ondigitalocean.app)**
|
|
||||||
|
|
||||||
3D Globe written in three.js connected to a Node.js websocket server that is listening to exchanges and the XRPL. The visualization aims to show trading, ODL, and liquidity at exchanges, intra-exchange volume, and flows.
|
|
||||||
|
|
||||||
## Wallets and wallet tools
|
|
||||||
|
|
||||||
- **[XUMM](https://xumm.app/)**
|
|
||||||
|
|
||||||
Users can use the xumm application to track their accounts, balances and transactions. The true power of xumm is the platform available for developers.
|
|
||||||
|
|
||||||
- **[Xpring Wallet](https://xpring.io)** (uses `ripple-keypairs`)
|
|
||||||
|
|
||||||
Non-custodial XRP wallet.
|
|
||||||
|
|
||||||
- **[XRP Toolkit](https://www.xrptoolkit.com)**
|
|
||||||
|
|
||||||
A web interface to the XRP Ledger, supporting both hardware and software wallets.
|
|
||||||
|
|
||||||
- **[Toast Wallet](https://toastwallet.com/)**
|
|
||||||
|
|
||||||
A free, open source XRP Wallet for iOS, Android, Windows, Mac and Linux.
|
|
||||||
|
|
||||||
- **[Toastify Ledger](https://github.com/WietseWind/toastify-ledger)** (uses `ripple-keypairs`)
|
|
||||||
|
|
||||||
Add a Regular Key to a mnemonic XRP Wallet (e.g. Ledger Nano S) to use the account with a Family Seed (secret).
|
|
||||||
|
|
||||||
- **[Bithomp-submit](https://github.com/Bithomp/bithomp-submit)** (GitHub)
|
|
||||||
|
|
||||||
A tool to submit an offline-signed XRPL transaction.
|
|
||||||
|
|
||||||
- **[Kyte](https://kyteapp.co/)** (kyteapp.co) ([Source](https://github.com/WietseWind/Zerp-Wallet)) (Deprecated)
|
|
||||||
|
|
||||||
Web-based XRP wallet.
|
|
||||||
|
|
||||||
- **[XRP Vanity Address Generator](https://github.com/WietseWind/xrp-vanity-generator)** (Node.js)
|
|
||||||
|
|
||||||
A vanity address is a wallet address containing a few characters you like at the beginning or the end of the wallet address.
|
|
||||||
|
|
||||||
- **[XRP Account Mnemonic Recovery](https://github.com/WietseWind/xrp-mnemonic-recovery)** (uses `ripple-keypairs`)
|
|
||||||
|
|
||||||
Recover a 24 word mnemonic if one word is wrong or one word is missing.
|
|
||||||
|
|
||||||
## Send and request payments
|
|
||||||
|
|
||||||
- **[XRP Tip Bot](https://www.xrptipbot.com/)**
|
|
||||||
|
|
||||||
A bot that enables users on reddit, Twitter and Discord to send XRP to each other through reddit comments and Twitter tweets.
|
|
||||||
|
|
||||||
- **[XRP Text](https://xrptext.com/)**
|
|
||||||
|
|
||||||
Send XRP using SMS text messages.
|
|
||||||
|
|
||||||
- **[XRParrot](https://xrparrot.com/)** (uses `ripple-address-codec`)
|
|
||||||
|
|
||||||
Easy EUR (SEPA) to XRP transfer (currency conversion).
|
|
||||||
|
|
||||||
- **[XRP Payment](https://xrpayments.co/)** (xrpayments.co)
|
|
||||||
|
|
||||||
Tool for generating a XRP payment request URI in a QR code, with currency converter.
|
|
||||||
|
|
||||||
## Development tools
|
|
||||||
|
|
||||||
- **[XRP Faucets for Testnet and Devnet](https://xrpl.org/xrp-testnet-faucet.html)**
|
|
||||||
|
|
||||||
Get some test funds for development on the test network. The faucet uses `xrpl.js`.
|
|
||||||
|
|
||||||
## Code samples and libraries
|
|
||||||
|
|
||||||
- **[ilp-plugin-xrp-paychan](https://github.com/interledgerjs/ilp-plugin-xrp-paychan)**
|
|
||||||
|
|
||||||
Send ILP payments using XRP and payment channels (PayChan).
|
|
||||||
|
|
||||||
- **[RunKit: WietseWind](https://runkit.com/wietsewind/)**
|
|
||||||
|
|
||||||
XRP Ledger code samples for Node.js.
|
|
||||||
|
|
||||||
- **[GitHub Gist: WietseWind](https://gist.github.com/WietseWind)**
|
|
||||||
|
|
||||||
XRP Ledger code samples for Node.js and the web (mostly).
|
|
||||||
|
|
||||||
- **[rippled-ws-client-sign](https://github.com/WietseWind/rippled-ws-client-sign)**
|
|
||||||
|
|
||||||
Sign transactions, with support for MultiSign.
|
|
||||||
|
|
||||||
- **[ILP-enabled power switch](https://xrpcommunity.blog/raspberry-pi-interledger-xp-powerswitch-howto/)** ([video](https://www.youtube.com/watch?v=c-eS0HQUuJg)) (uses [`moneyd-uplink-xrp`](https://github.com/interledgerjs/moneyd-uplink-xrp))
|
|
||||||
|
|
||||||
For about $30 in parts (Raspberry Pi, 3.3V Relay board and a few wires) you can build your own power switch that will switch on if a streaming ILP payment comes in. When the payment stream stops, the power turns off.
|
|
||||||
|
|
||||||
## Related apps that do not appear to use xrpl.js
|
|
||||||
|
|
||||||
- **[XRP Stats](https://ledger.exposed/)** (ledger.exposed)
|
|
||||||
|
|
||||||
Rich list, live ledger stats and XRP distribution. Visualize escrows and flow of funds.
|
|
||||||
212
CONTRIBUTING.md
212
CONTRIBUTING.md
@@ -1,10 +1,18 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
### High Level Process to Contribute Code
|
||||||
|
|
||||||
|
- You should open a PR against `main` and ensure that all CI passes.
|
||||||
|
- Your changes should have [unit](#unit-tests) and/or [integration tests](#integration-tests).
|
||||||
|
- Your changes should [pass the linter](#run-the-linter).
|
||||||
|
- You should get a full code review from two of the maintainers.
|
||||||
|
- Then you can merge your changes. (Which will then be included in the next release)
|
||||||
|
|
||||||
## Set up your dev environment
|
## Set up your dev environment
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
We use Node v14 for development - that is the version that our linters require.
|
We use Node v18 for development - that is the version that our linters require.
|
||||||
You must also use `npm` v7. You can check your `npm` version with:
|
You must also use `npm` v7. You can check your `npm` version with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -38,7 +46,8 @@ npm run lint
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
For integration and browser tests, we use a `rippled` node in standalone mode to test xrpl.js code against. To set this up, you can either run `rippled` locally, or set up the Docker container `natenichols/rippled-standalone:latest` for this purpose. The latter will require you to [install Docker](https://docs.docker.com/get-docker/).
|
|
||||||
|
For integration and browser tests, we use a `xahaud` node in standalone mode to test xahau.js code against. To set this up, you can either configure and run `xahaud` locally, or set up the Docker container `xahauci/xahaud` by [following these instructions](#integration-tests). The latter will require you to [install Docker](https://docs.docker.com/get-docker/).
|
||||||
|
|
||||||
### Unit Tests
|
### Unit Tests
|
||||||
|
|
||||||
@@ -50,14 +59,24 @@ npm test
|
|||||||
|
|
||||||
### Integration Tests
|
### Integration Tests
|
||||||
|
|
||||||
|
From the top-level xahau.js folder (one level above `packages`), run the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
# sets up the rippled standalone Docker container - you can skip this step if you already have it set up
|
# sets up the xahaud standalone Docker container - you can skip this step if you already have it set up
|
||||||
docker run -p 6006:6006 -it natenichols/rippled-standalone:latest
|
docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/xahau/etc/ --platform linux/amd64 xahauci/xahaud:2025.2.6 /opt/xahau/bin/xahaud -a --conf /opt/xahau/etc/xahaud.cfg
|
||||||
npm run build
|
npm run build
|
||||||
npm run test:integration
|
npm run test:integration
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Breaking down the command:
|
||||||
|
* `docker run -p 6006:6006` starts a Docker container with an open port for admin WebSocket requests.
|
||||||
|
* `--interactive` allows you to interact with the container.
|
||||||
|
* `-t` starts a terminal in the container for you to send commands to.
|
||||||
|
* `--volume $PWD/.ci-config:/config/` identifies the `xahaud.cfg` and `validators.txt` to import. It must be an absolute path, so we use `$PWD` instead of `./`.
|
||||||
|
* `xahauci/xahaud` is an image that is regularly updated with the latest `xahaud` releases
|
||||||
|
* `/opt/xahau/bin/xahaud -a --conf /opt/xahau/etc/xahaud.cfg` starts `xahaud` in standalone mode
|
||||||
|
|
||||||
### Browser Tests
|
### Browser Tests
|
||||||
|
|
||||||
There are two ways to run browser tests.
|
There are two ways to run browser tests.
|
||||||
@@ -66,30 +85,82 @@ One is in the browser - run `npm run build:browserTests` and open `test/localInt
|
|||||||
|
|
||||||
The other is in the command line (this is what we use for CI) -
|
The other is in the command line (this is what we use for CI) -
|
||||||
|
|
||||||
|
This should be run from the `xahau.js` top level folder (one above the `packages` folder).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
# sets up the rippled standalone Docker container - you can skip this step if you already have it set up
|
# sets up the xahaud standalone Docker container - you can skip this step if you already have it set up
|
||||||
docker run -p 6006:6006 -it natenichols/rippled-standalone:latest
|
docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/xahau/etc/ --platform linux/amd64 xahauci/xahaud:2025.2.6 /opt/xahau/bin/xahaud -a --conf /opt/xahau/etc/xahaud.cfg
|
||||||
npm run test:browser
|
npm run test:browser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## High Level Architecture
|
||||||
|
|
||||||
|
This is a monorepo, which means that there are multiple packages in a single GitHub repository using [Lerna](https://lerna.js.org/).
|
||||||
|
|
||||||
|
The 4 packages currently here are:
|
||||||
|
|
||||||
|
1. xahau.js - The client library for interacting with the ledger.
|
||||||
|
2. xahau-binary-codec - A library for serializing and deserializing transactions for the ledger.
|
||||||
|
3. xahau-keypairs - A library for generating and using cryptographic keypairs.
|
||||||
|
4. xahau-address-codec - A library for encoding and decoding Xahau Ledger addresses and seeds.
|
||||||
|
5. isomorphic - A collection of isomorphic implementations of crypto and utility functions.
|
||||||
|
6. secret-numbers - Generate XRPL Accounts with a number-based secret: 8 chunks of 6 digits.
|
||||||
|
|
||||||
|
Each package has it's own README which dives deeper into what it's main purpose is, and the core functionality it offers.
|
||||||
|
They also run tests independently as they were originally in separate repositories.
|
||||||
|
|
||||||
|
These are managed in a monorepo because often a change in a lower-level library will also require a change in xahau.js, and so it makes sense to be able to allow for modifications of all packages at once without coordinating versions across multiple repositories.
|
||||||
|
|
||||||
|
Let's dive a bit into how xahau.js is structured!
|
||||||
|
|
||||||
|
### The File Structure
|
||||||
|
|
||||||
|
Within the xrpl package, each folder has a specific purpose:
|
||||||
|
|
||||||
|
**Client** - This contains logic for handling the websocket connection to xahaud servers.
|
||||||
|
**Models** - These types model LedgerObjects, Requests/Methods, and Transactions in order to give type hints and nice errors for users.
|
||||||
|
**Sugar** - This is where handy helper functions end up, like `submit`, `autofill`, and `getXAHBalance` amongst others.
|
||||||
|
**Utils** - These are shared functions which are useful for conversions, or internal implementation details within the library.
|
||||||
|
**Wallet** - This logic handles managing keys, addresses, and signing within xahau.js
|
||||||
|
|
||||||
|
### Writing Tests for xahau.js
|
||||||
|
|
||||||
|
For every file in `src`, we try to have a corresponding file in `test` with unit tests.
|
||||||
|
|
||||||
|
The goal is to maintain above 80% code coverage, and generally any new feature or bug fix should be accompanied by unit tests, and integration tests if applicable.
|
||||||
|
|
||||||
|
For an example of a unit test, check out the [autofill tests here](./packages/xahau/test/client/autofill.ts).
|
||||||
|
|
||||||
|
If your code connects to the ledger (ex. Adding a new transaction type) it's handy to write integration tests to ensure that you can successfully interact with the ledger. Integration tests are generally run against a docker instance of xahaud which contains the latest updates. Since standalone mode allows us to manually close ledgers, this allows us to run integration tests at a much faster rate than if we had to wait 4-5 seconds per transaction for the ledger to validate the transaction. [See above](#running-tests) for how to start up the docker container to run integration tests.
|
||||||
|
|
||||||
|
All integration tests should be written in the `test/integration` folder, with new `Requests` and `Transactions` tests being in their respective folders.
|
||||||
|
|
||||||
|
For an example of how to write an integration test for `xahau.js`, you can look at the [Payment integration test](./packages/xahau/test/integration/transactions/payment.ts).
|
||||||
|
|
||||||
## Generate reference docs
|
## Generate reference docs
|
||||||
|
|
||||||
You can see the complete reference documentation at [`xrpl.js` docs](js.xrpl.org). You can also generate them locally using `typedoc`:
|
You can see the complete reference documentation at [`xahau.js` docs](https://js.xrpl.org). You can also generate them locally using `typedoc`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run docgen
|
npm run docgen
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This updates `docs/` at the top level, where GitHub Pages looks for the docs.
|
||||||
|
|
||||||
|
## Update `definitions.json`
|
||||||
|
|
||||||
|
Use [this repo](https://github.com/RichardAH/xrpl-codec-gen) to generate a new `definitions.json` file from the xahaud source code. Instructions are available in that README.
|
||||||
|
|
||||||
## Adding and removing packages
|
## Adding and removing packages
|
||||||
|
|
||||||
`xrpl.js` uses `lerna` and `npm`'s workspaces features to manage a monorepo.
|
`xahau.js` uses `lerna` and `npm`'s workspaces features to manage a monorepo.
|
||||||
Adding and removing packages requires a slightly different process than normal
|
Adding and removing packages requires a slightly different process than normal
|
||||||
as a result.
|
as a result.
|
||||||
|
|
||||||
### Adding or removing development dependencies
|
### Adding or removing development dependencies
|
||||||
|
|
||||||
`xrpl.js` strives to use the same development dependencies in all packages.
|
`xahau.js` strives to use the same development dependencies in all packages.
|
||||||
You may add and remove dev dependencies like normal:
|
You may add and remove dev dependencies like normal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -104,46 +175,107 @@ npm uninstall --save-dev abbrev
|
|||||||
You need to specify which package is changing using the `-w` flag:
|
You need to specify which package is changing using the `-w` flag:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
### adding a new dependency to `xrpl`
|
### adding a new dependency to `xahau`
|
||||||
npm install abbrev -w xrpl
|
npm install abbrev -w xahau
|
||||||
### adding a new dependency to `ripple-keypairs`
|
### adding a new dependency to `xahau-keypairs`
|
||||||
npm install abbrev -w ripple-keypairs
|
npm install abbrev -w xahau-keypairs
|
||||||
### removing a dependency
|
### removing a dependency
|
||||||
npm uninstall abbrev -w xrpl
|
npm uninstall abbrev -w xahau
|
||||||
```
|
```
|
||||||
|
|
||||||
## Release process
|
## Updating the Docker container for CI
|
||||||
|
|
||||||
### Editing the Code
|
In order to test the library, we need to enable the latest amendments in the docker container.
|
||||||
|
This requires updating the `/.ci-config/xahaud.cfg` file with the hashes and names of new amendments.
|
||||||
|
|
||||||
* Your changes should have unit and/or integration tests.
|
In order to update the list, follow these steps from the top level of the library:
|
||||||
* Your changes should pass the linter.
|
1. Run `node ./.ci-config/getNewAmendments.js`
|
||||||
* Your code should pass all the tests on Github (which check the linter, unit and integration tests on Node 12/14/16, and browser tests).
|
2. If there are any new amendment hashes, add a comment to the end of `/.ci-config/xahaud.cfg` with the date
|
||||||
* Open a PR against `develop` and ensure that all CI passes.
|
- `Ex. "# Added August 9th, 2023"`
|
||||||
* Get a full code review from one of the maintainers.
|
3. For each hash printed out by the script, add the hash and name to the config file.
|
||||||
* Merge your changes.
|
- Ex. `B2A4DB846F0891BF2C76AB2F2ACC8F5B4EC64437135C6E56F3F859DE5FFD5856 ExpandedSignerList`
|
||||||
|
- You can look up the name by searching for the hash on https://xrpl.org/known-amendments.html
|
||||||
|
4. Push your changes
|
||||||
|
|
||||||
### Release
|
Note: The same updated config can be used to update xahau-py's CI as well.
|
||||||
|
|
||||||
|
## Updating `definitions.json`
|
||||||
|
|
||||||
|
This should almost always be done using the [`xrpl-codec-gen`](https://github.com/RichardAH/xrpl-codec-gen) script - if the output needs manual intervention afterwards, consider updating the script instead.
|
||||||
|
|
||||||
|
1. Clone / pull the latest changes from [xahaud](https://github.com/XRPLF/xahaud) - Specifically the `develop` branch is usually the right one.
|
||||||
|
2. Clone / pull the latest changes from [`xrpl-codec-gen`](https://github.com/RichardAH/xrpl-codec-gen)
|
||||||
|
3. From the `xrpl-codec-gen` tool, follow the steps in the `README.md` to generate a new `definitions.json` file.
|
||||||
|
4. Replace the `definitions.json` file in the `xahau-binary-codec` with the newly generated file.
|
||||||
|
5. Verify that the changes make sense by inspection before submitting, as there may be updates required for the `xrpl-codec-gen` tool depending on the latest amendments we're updating to match.
|
||||||
|
|
||||||
|
|
||||||
|
## Release process + checklist
|
||||||
|
|
||||||
|
## PR process
|
||||||
|
|
||||||
|
- [ ] Your changes should be on a branch.
|
||||||
|
- [ ] Your changes should have unit tests.
|
||||||
|
- [ ] Lint the code with `npm lint`
|
||||||
|
- [ ] Build your code with `npm build`
|
||||||
|
- [ ] Run the unit tests with `npm test`
|
||||||
|
- [ ] Get a full code review.
|
||||||
|
- [ ] Merge your branch into `main` and push to github.
|
||||||
|
- [ ] Ensure that all tests passed on the last CI that ran on `main`.
|
||||||
|
|
||||||
|
## Release
|
||||||
|
|
||||||
|
1. Checkout `main` (or your beta branch) and `git pull`.
|
||||||
|
2. Create a new branch (`git checkout -b <BRANCH_NAME>`) to capture updates that take place during this process.
|
||||||
|
3. Update `HISTORY.md` to reflect release changes.
|
||||||
|
|
||||||
|
- [ ] Update the version number and release date, and ensure it lists the changes since the previous release.
|
||||||
|
|
||||||
|
4. Run `npm run docgen` if the docs were modified in this release to update them (skip this step for a beta).
|
||||||
|
5. Run `npm run clean` to delete previously generated artifacts.
|
||||||
|
6. Run `npm run build` to triple check the build still works
|
||||||
|
7. Run `npx lerna version --no-git-tag-version` - This bumps the package versions.
|
||||||
|
|
||||||
|
- For each changed package, pick what the new version should be. Lerna will bump the versions, commit version bumps to `main`, and create a new git tag for each published package.
|
||||||
|
- If you do NOT want to update the package number, choose "Custom Version" and set the version to be the same as the existing version. Lerna will not publish any changes in this case.
|
||||||
|
- If publishing a beta, make sure that the versions are all of the form `a.b.c-beta.d`, where `a`, `b`, and `c` are identical to the last normal release except for one, which has been incremented by 1.
|
||||||
|
|
||||||
|
8. Run `npm i` to update the package-lock with the updated versions.
|
||||||
|
9. Create a new PR from this branch into `main` and merge it (you can directly merge into the beta branch for a beta).
|
||||||
|
10. Checkout `main` and `git pull` (you can skip this step for a beta since you already have the latest version of the beta branch).
|
||||||
|
11. Actually publish the packages with one of the following:
|
||||||
|
|
||||||
|
- Stable release: Run `npx lerna publish from-package --yes`
|
||||||
|
- Beta release: Run `npx lerna publish from-package --dist-tag beta --yes`
|
||||||
|
Notice this allows developers to install the package with `npm add xahau@beta`
|
||||||
|
|
||||||
|
12. If requested, enter your [npmjs.com](https://npmjs.com) OTP (one-time password) to complete publication.
|
||||||
|
|
||||||
|
NOW YOU HAVE PUBLISHED! But you're not done; we have to notify people!
|
||||||
|
|
||||||
|
13. Run `git tag <tagname> -m <tagname>`, where `<tagname>` is the new package and version (e.g. `xahau@2.1.1`), for each version released.
|
||||||
|
14. Run `git push --follow-tags`, to push the tags to Github.
|
||||||
|
15. On GitHub, click the "Releases" link on the right-hand side of the page.
|
||||||
|
|
||||||
|
16. Repeat for each release:
|
||||||
|
|
||||||
|
1. Click "Draft a new release"
|
||||||
|
2. Click "Choose a tag", and choose a tag that you just created.
|
||||||
|
3. Edit the name of the release to match the tag (IE \<package\>@\<version\>) and edit the description as you see fit.
|
||||||
|
|
||||||
|
17. Send an email to [xahau-announce](https://groups.google.com/g/xahau-announce).
|
||||||
|
18. Lastly, send a similar message to the Xahau Discord in the [`javascript` channel](https://discord.com/channels/1085202760548499486/1085203623111295068). The message should include:
|
||||||
|
1. The version changes for xahau libraries
|
||||||
|
2. A link to the more detailed changes
|
||||||
|
3. Highlights of important changes
|
||||||
|
|
||||||
1. Ensure that all tests passed on the last CI that ran on `develop`.
|
|
||||||
2. Open a PR to update the docs if docs were modified.
|
|
||||||
3. Create a branch off `develop` that ensures that `HISTORY.md` is updated appropriately for each package.
|
|
||||||
* Use `shasum -a 256 build/*` to get the SHA-256 checksums. Add these to `HISTORY.md` as well.
|
|
||||||
4. Merge this branch into `develop`.
|
|
||||||
5. If this is not a beta release: Merge `develop` into `master` (`--ff-only`) and push to github. This is important because we have docs telling developers to use master to get the latest release.
|
|
||||||
6. Create a new Github release/tag off of this branch.
|
|
||||||
7. Run `npm publish --dry-run` and make sure everything looks good.
|
|
||||||
8. Publish the release to `npm`.
|
|
||||||
* If this is a beta release, run `npm publish --tag beta`. This allows someone else to install this version of the package with `npm install xrpl@beta`.
|
|
||||||
* If this is a stable release, run `npm publish`.
|
|
||||||
* This will require entering `npm` login info.
|
|
||||||
9. Send an email to [xrpl-announce](https://groups.google.com/g/xrpl-announce).
|
|
||||||
|
|
||||||
## Mailing Lists
|
## Mailing Lists
|
||||||
We have a low-traffic mailing list for announcements of new `xrpl.js` releases. (About 1 email every couple of weeks)
|
|
||||||
|
|
||||||
+ [Subscribe to xrpl-announce](https://groups.google.com/g/xrpl-announce)
|
We have a low-traffic mailing list for announcements of new `xahau.js` releases. (About 1 email every couple of weeks)
|
||||||
|
|
||||||
If you're using the XRP Ledger in production, you should run a [rippled server](https://github.com/ripple/rippled) and subscribe to the ripple-server mailing list as well.
|
- [Subscribe to xahau-announce](https://groups.google.com/g/xahau-announce)
|
||||||
|
|
||||||
+ [Subscribe to ripple-server](https://groups.google.com/g/ripple-server)
|
If you're using the Xahau Ledger in production, you should run a [xahaud server](https://github.com/xahau/xahaud) and subscribe to the xahau-server mailing list as well.
|
||||||
|
|
||||||
|
- [Subscribe to xahau-server](https://groups.google.com/g/xahau-server)
|
||||||
|
|||||||
13
HISTORY.md
13
HISTORY.md
@@ -1,3 +1,12 @@
|
|||||||
# xrpl.js (ripple-lib) Release History
|
# Release History (Changelog)
|
||||||
|
|
||||||
Please see the individual HISTORY.md documents in each package for changes.
|
Please see the individual HISTORY.md and XAHAU_HISTORY.md documents in each package for changes:
|
||||||
|
|
||||||
|
* [Release History for **xahau.js**](packages/xrpl/HISTORY.md)
|
||||||
|
* The **xahau** package is a TypeScript/JavaScript library for interacting with the [Xahau Ledger](https://docs.xahau.network/).
|
||||||
|
* [Release History for **xahau-address-codec**](packages/xahau-address-codec/HISTORY.md)
|
||||||
|
* The **xahau-address-codec** package provides functions for encoding and decoding Xahau Ledger addresses and seeds.
|
||||||
|
* [Release History for **xahau-binary-codec**](packages/xahau-binary-codec/HISTORY.md)
|
||||||
|
* The **xahau-binary-codec** package provides functions to encode to, and decode from, the Xahau binary serialization format.
|
||||||
|
* [Release History for **xahau-keypairs**](packages/xahau-keypairs/HISTORY.md)
|
||||||
|
* The **xahau-keypairs** package implements Xahau cryptographic keypair and wallet generation, with support for rfc6979 and EdDSA deterministic signatures.
|
||||||
|
|||||||
161
README.md
161
README.md
@@ -1,146 +1,99 @@
|
|||||||
# xrpl.js
|
# xahau.js
|
||||||
|
|
||||||
A JavaScript/TypeScript library for interacting with the XRP Ledger
|
A JavaScript/TypeScript library for interacting with the Xahau Ledger
|
||||||
|
|
||||||
[](https://www.npmjs.org/package/xrpl)
|
[](https://www.npmjs.org/package/xahau)
|
||||||

|

|
||||||
|
|
||||||
This is the recommended library for integrating a JavaScript/TypeScript app with the XRP Ledger, especially if you intend to use advanced functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
This is the recommended library for integrating a JavaScript/TypeScript app with the Xahau Ledger, especially if you intend to use advanced functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
||||||
|
|
||||||
## [➡️ Reference Documentation](http://js.xrpl.org)
|
<!-- ## [➡️ Reference Documentation](http://js.xahau.org)
|
||||||
|
|
||||||
See the full reference documentation for all classes, methods, and utilities.
|
See the full reference documentation for all classes, methods, and utilities.
|
||||||
|
|
||||||
## [➡️ Applications and Projects](https://github.com/XRPLF/xrpl.js/blob/master/APPLICATIONS.md)
|
## Features
|
||||||
|
|
||||||
What is `xrpl.js` used for? The applications on the list linked above use `xrpl.js`. Open a PR to add your app or project to the list!
|
1. Managing keys & creating test credentials ([`Wallet`](https://js.xahau.org/classes/Wallet.html) && [`Client.fundWallet()`](https://js.xahau.org/classes/Client.html#fundWallet))
|
||||||
|
2. Submitting transactions to the Xahau Ledger ([`Client.submit(...)`](https://js.xahau.org/classes/Client.html#submit) & [transaction types](https://xahau.org/transaction-types.html))
|
||||||
|
3. Sending requests to observe the ledger ([`Client.request(...)`](https://js.xahau.org/classes/Client.html#request) using [public API methods](https://xahau.org/public-api-methods.html))
|
||||||
|
4. Subscribing to changes in the ledger ([Ex. ledger, transactions, & more...](https://xahau.org/subscribe.html))
|
||||||
|
5. Parsing ledger data into more convenient formats ([`xrpToDrops`](https://js.xahau.org/functions/xrpToDrops.html) and [`rippleTimeToISOTime`](https://js.xahau.org/functions/rippleTimeToISOTime.html))
|
||||||
|
|
||||||
### Features
|
All of which works in Node.js (tested for v18+) & web browsers (tested for Chrome). -->
|
||||||
|
|
||||||
+ Works in Node.js and in web browsers
|
# Quickstart
|
||||||
+ Helpers for creating requests and parsing responses for the [XRP Ledger APIs](https://xrpl.org/rippled-api.html)
|
|
||||||
+ Listen to events on the XRP Ledger (transactions, ledger, validations, etc.)
|
|
||||||
+ Sign and submit transactions to the XRP Ledger
|
|
||||||
+ Type definitions for TypeScript
|
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
+ **[Node.js v14](https://nodejs.org/)** is recommended. We also support v12 and v16. Other versions may work but are not frequently tested.
|
+ **[Node.js v18](https://nodejs.org/)** is recommended. We also support v20. Other versions may work but are not frequently tested.
|
||||||
|
|
||||||
## Getting Started
|
### Installing xahau.js
|
||||||
|
|
||||||
In an existing project (with `package.json`), install `xrpl.js`:
|
In an existing project (with package.json), install xahau.js with:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ npm install --save xrpl
|
$ npm install --save xahau
|
||||||
```
|
```
|
||||||
|
|
||||||
Or with `yarn`:
|
Or with `yarn`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ yarn add xrpl
|
$ yarn add xahau
|
||||||
```
|
```
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const xrpl = require("xrpl")
|
const xahau = require("xahau");
|
||||||
async function main() {
|
async function main() {
|
||||||
const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233")
|
const client = new xahau.Client("wss://xahau-test.net");
|
||||||
await client.connect()
|
await client.connect();
|
||||||
|
|
||||||
const response = await client.request({
|
const response = await client.request({
|
||||||
"command": "account_info",
|
command: "account_info",
|
||||||
"account": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
account: "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
|
||||||
"ledger_index": "validated"
|
ledger_index: "validated",
|
||||||
})
|
|
||||||
console.log(response)
|
|
||||||
|
|
||||||
client.disconnect()
|
|
||||||
}
|
|
||||||
main()
|
|
||||||
```
|
|
||||||
|
|
||||||
For more examples, see the [documentation](#documentation).
|
|
||||||
|
|
||||||
### Using xrpl.js with React Native
|
|
||||||
|
|
||||||
If you want to use `xrpl.js` with React Native you will need to install shims for core NodeJS modules. To help with this you can use a module like [rn-nodeify](https://github.com/tradle/rn-nodeify).
|
|
||||||
|
|
||||||
1. Install dependencies (you can use `yarn` as well):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
npm install react-native-crypto
|
|
||||||
npm install xrpl
|
|
||||||
# install peer deps
|
|
||||||
npm install react-native-randombytes
|
|
||||||
# install latest rn-nodeify
|
|
||||||
npm install rn-nodeify@latest --dev
|
|
||||||
```
|
|
||||||
|
|
||||||
2. After that, run the following command:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# install node core shims and recursively hack package.json files
|
|
||||||
# in ./node_modules to add/update the "browser"/"react-native" field with relevant mappings
|
|
||||||
./node_modules/.bin/rn-nodeify --hack --install
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Enable `crypto`:
|
|
||||||
|
|
||||||
`rn-nodeify` will create a `shim.js` file in the project root directory.
|
|
||||||
Open it and uncomment the line that requires the crypto module:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// If using the crypto shim, uncomment the following line to ensure
|
|
||||||
// crypto is loaded first, so it can populate global.crypto
|
|
||||||
require('crypto')
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Import `shim` in your project (it must be the first line):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import './shim'
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using xrpl.js with Deno
|
|
||||||
|
|
||||||
Until official support for [Deno](https://deno.land) is added, you can use the following work-around to use `xrpl.js` with Deno:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
import xrpl from 'https://dev.jspm.io/npm:xrpl';
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const api = new (xrpl as any).Client('wss://s.altnet.rippletest.net:51233');
|
|
||||||
const address = 'rH8NxV12EuV...khfJ5uw9kT';
|
|
||||||
|
|
||||||
api.connect().then(() => {
|
|
||||||
api.getBalances(address).then((balances: any) => {
|
|
||||||
console.log(JSON.stringify(balances, null, 2));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
})();
|
console.log(response);
|
||||||
|
|
||||||
|
await client.disconnect();
|
||||||
|
}
|
||||||
|
main();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It goes through:
|
||||||
|
|
||||||
|
1. Creating a new test account
|
||||||
|
2. Sending a payment transaction
|
||||||
|
3. And sending requests to see your account balance!
|
||||||
|
|
||||||
|
### Case by Case Setup Steps
|
||||||
|
|
||||||
|
If you're using xahau.js with React or Deno, you'll need to do a couple extra steps to set it up:
|
||||||
|
|
||||||
|
- [Using xahau.js with a CDN](https://github.com/XRPLF/xahau.js/blob/main/UNIQUE_SETUPS.md#using-xahaujs-from-a-cdn)
|
||||||
|
- [Using xahau.js with `create-react-app`](https://github.com/XRPLF/xahau.js/blob/main/UNIQUE_SETUPS.md#using-xahaujs-with-create-react-app)
|
||||||
|
- [Using xahau.js with `React Native`](https://github.com/XRPLF/xahau.js/blob/main/UNIQUE_SETUPS.md#using-xahaujs-with-react-native)
|
||||||
|
- [Using xahau.js with `Vite React`](https://github.com/XRPLF/xahau.js/blob/main/UNIQUE_SETUPS.md#using-xahaujs-with-vite-react)
|
||||||
|
- [Using xahau.js with `Deno`](https://github.com/XRPLF/xahau.js/blob/main/UNIQUE_SETUPS.md#using-xahaujs-with-deno)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
+ [Get Started in Node.js](https://xrpl.org/get-started-using-node-js.html)
|
As you develop with xahau.js, there's two sites you'll use extensively:
|
||||||
+ [Full Reference Documentation](https://js.xrpl.org)
|
|
||||||
+ [Code Samples](https://github.com/XRPLF/xrpl.js/tree/develop/packages/xrpl/snippets/src)
|
1. [docs.xahau.network](https://docs.xahau.network/technical/protocol-reference) is the primary source for:
|
||||||
|
- How the ledger works ([See Concepts](https://docs.xahau.network/))
|
||||||
|
- What kinds of transactions there are ([Transaction Types](https://docs.xahau.network/technical/protocol-reference/transactions/transaction-types))
|
||||||
|
- Requests you can send ([Public API Methods](https://docs.xahau.network/features/http-websocket-apis))
|
||||||
|
|
||||||
### Mailing Lists
|
### Mailing Lists
|
||||||
|
|
||||||
We have a low-traffic mailing list for announcements of new `xrpl.js` releases. (About 1 email per week)
|
If you want to hear when we release new versions of xahau.js, you can join our low-traffic mailing list (About 1 email per week):
|
||||||
|
|
||||||
+ [Subscribe to xrpl-announce](https://groups.google.com/g/xrpl-announce)
|
- [Subscribe to xahau-announce](https://groups.google.com/g/xahau-announce)
|
||||||
|
|
||||||
If you're using the XRP Ledger in production, you should run a [rippled server](https://github.com/ripple/rippled) and subscribe to the ripple-server mailing list as well.
|
If you're using the Xahau Ledger in production, you should run a [xahaud server](https://github.com/Xahau/xahaud) and subscribe to the xahau-server mailing list as well.
|
||||||
|
|
||||||
+ [Subscribe to ripple-server](https://groups.google.com/g/ripple-server)
|
- [Subscribe to xahau-server](https://groups.google.com/g/xahau-server)
|
||||||
|
|
||||||
## More Information
|
You are also welcome to create an [issue](https://github.com/Xahau/xahau.js/issues) here and we'll do our best to respond within 3 days.
|
||||||
|
|
||||||
+ [xrpl-announce mailing list](https://groups.google.com/g/xrpl-announce) - subscribe for release announcements
|
|
||||||
+ [xrpl.js API Reference](https://js.xrpl.org)
|
|
||||||
+ [XRP Ledger Dev Portal](https://xrpl.org)
|
|
||||||
|
|||||||
30
SECURITY.md
30
SECURITY.md
@@ -1,30 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
This table shows which versions of xrpl.js are currently supported with security updates:
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| ------- | ---------------------- |
|
|
||||||
| 2.x | :white_check_mark: Yes |
|
|
||||||
| 1.x | :white_check_mark: Yes |
|
|
||||||
| 0.x | :x: No |
|
|
||||||
|
|
||||||
## Responsible disclosure security policy
|
|
||||||
|
|
||||||
The responsible disclosure of vulnerabilities helps to protect users of the project. Vulnerabilities are first triaged in a private manner, and only publicly disclosed after a reasonable time period that allows patching the vulnerability and provides an upgrade path for users.
|
|
||||||
|
|
||||||
When contacting us directly via email, we will do our best to respond in a reasonable time to resolve the issue. Do not disclose the vulnerability until it has been patched and users have been given time to upgrade.
|
|
||||||
|
|
||||||
We kindly ask you to refrain from malicious acts that put our users, the project, or any of the project’s team members at risk.
|
|
||||||
|
|
||||||
## Reporting a security issue
|
|
||||||
|
|
||||||
Security is a top priority. But no matter how much effort we put into security, there can still be vulnerabilities present.
|
|
||||||
|
|
||||||
If you discover a security vulnerability, please use the following means of communications to report it to us:
|
|
||||||
|
|
||||||
- Report the security issue to bugs@ripple.com
|
|
||||||
- [Ripple Bug Bounty](https://ripple.com/bug-bounty/)
|
|
||||||
|
|
||||||
Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions.
|
|
||||||
81
UNIQUE_SETUPS.md
Normal file
81
UNIQUE_SETUPS.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Unique Setup Steps for Xahau.js
|
||||||
|
|
||||||
|
Starting in 3.0 xahau and all the packages in this repo no longer require custom configurations (ex. polyfills) to run.
|
||||||
|
|
||||||
|
### Using xahau.js from a CDN
|
||||||
|
|
||||||
|
You can avoid setting up your build system to handle `xahau.js` by using a cdn version that is prebuilt for the browser.
|
||||||
|
|
||||||
|
- unpkg `<script src="https://unpkg.com/xahau@2.3.0/build/xahau-latest-min.js"></script>`
|
||||||
|
- jsdelivr `<script src="https://cdn.jsdelivr.net/npm/xahau@2.3.0/build/xahau-latest-min.js"></script>`
|
||||||
|
|
||||||
|
Ensure that the full path is provided so the browser can find the sourcemaps.
|
||||||
|
|
||||||
|
### Using xahau.js with `create-react-app`
|
||||||
|
|
||||||
|
Starting in 3.0 xahau and its related packages no longer require custom configurations (ex. polyfills) to run.
|
||||||
|
|
||||||
|
This online template uses these steps to run xahau.js with React in the browser:
|
||||||
|
https://codesandbox.io/s/xahau-intro-pxgdjr?file=/src/App.js
|
||||||
|
|
||||||
|
### Using xahau.js with React Native
|
||||||
|
|
||||||
|
If you want to use `xahau.js` with React Native you will need to install polyfills for core NodeJS modules.
|
||||||
|
|
||||||
|
1. Install dependencies (you can use `yarn` as well):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm install xahau \
|
||||||
|
fast-text-encoding \
|
||||||
|
react-native-get-random-values
|
||||||
|
```
|
||||||
|
|
||||||
|
2. After that, run the following commands:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# compile `react-native-get-random-values` pods see https://www.npmjs.com/package/react-native-get-random-values#installation
|
||||||
|
npx pod-install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create `polyfills.js` and add
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Required for TextEncoder/TextDecoder
|
||||||
|
import 'fast-text-encoding'
|
||||||
|
// Required for `crypto.getRandomValues`
|
||||||
|
import 'react-native-get-random-values'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Import `polyfills` in index file your project (it must be the first line):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import './polyfills'
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using xahau.js with Vite React
|
||||||
|
|
||||||
|
Starting in 3.0 xahau and all the packages in this repo no longer require custom configurations (ex. polyfills) to run.
|
||||||
|
|
||||||
|
### Using xahau.js with Deno
|
||||||
|
|
||||||
|
Until official support for [Deno](https://deno.land) is added, you can use the following work-around to use `xahau.js` with Deno:
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The following is currently broken due to https://github.com/denoland/deno/issues/20516.
|
||||||
|
> Once that is fixed there could be other issues as well.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import xahau from 'https://dev.jspm.io/npm:xahau';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const api = new (xahau as any).Client('wss://xahau-test.net');
|
||||||
|
const address = 'rH8NxV12EuV...khfJ5uw9kT';
|
||||||
|
|
||||||
|
api.connect().then(() => {
|
||||||
|
api.getBalances(address).then((balances: any) => {
|
||||||
|
console.log(JSON.stringify(balances, null, 2));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
```
|
||||||
8
command.sh
Executable file
8
command.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
for file in $(git log --diff-filter=D --name-only --format="" | grep -E "oracle.*\.ts$"); do
|
||||||
|
commit=$(git rev-list -n 1 HEAD -- "$file")
|
||||||
|
if [ ! -z "$commit" ]; then
|
||||||
|
git checkout "$commit~1" -- "$file"
|
||||||
|
echo "restore: $file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
rsync -av packages/xrpl/ packages/xahau/ && rm -rf packages/xrpl/
|
||||||
20
jest.config.base.js
Normal file
20
jest.config.base.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const { TextDecoder, TextEncoder } = require("util");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
roots: ["<rootDir>/src"],
|
||||||
|
transform: {
|
||||||
|
"^.+\\.ts$": "ts-jest",
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||||
|
collectCoverage: true,
|
||||||
|
verbose: true,
|
||||||
|
testEnvironment: "node",
|
||||||
|
globals: {
|
||||||
|
TextDecoder: TextDecoder,
|
||||||
|
TextEncoder: TextEncoder,
|
||||||
|
error: console.error,
|
||||||
|
warn: console.warn,
|
||||||
|
info: console.info,
|
||||||
|
debug: console.debug,
|
||||||
|
},
|
||||||
|
};
|
||||||
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const base = require("./jest.config.base.js");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...base,
|
||||||
|
projects: ["<rootDir>/packages/**/jest.config.js"],
|
||||||
|
coverageDirectory: "<rootDir>/coverage/",
|
||||||
|
};
|
||||||
28
karma.config.js
Normal file
28
karma.config.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
plugins: ["karma-webpack", "karma-jasmine", "karma-chrome-launcher"],
|
||||||
|
|
||||||
|
// frameworks to use
|
||||||
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||||
|
frameworks: ["jasmine"],
|
||||||
|
|
||||||
|
// preprocess matching files before serving them to the browser
|
||||||
|
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||||
|
preprocessors: {
|
||||||
|
// Use webpack to bundle our test files
|
||||||
|
"test/**/*.test.ts": ["webpack"],
|
||||||
|
},
|
||||||
|
|
||||||
|
browsers: ["ChromeHeadless"],
|
||||||
|
// runs only one browser at a time
|
||||||
|
concurrency: 1,
|
||||||
|
// CI mode
|
||||||
|
singleRun: true,
|
||||||
|
client: {
|
||||||
|
jasmine: {
|
||||||
|
// ensures that tests are run in order instead of a random order
|
||||||
|
random: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
29315
package-lock.json
generated
29315
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@@ -1,76 +1,70 @@
|
|||||||
{
|
{
|
||||||
"name": "xrpl.js",
|
"name": "xahau.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"analyze": "lerna run analyze --stream",
|
||||||
"test": "lerna run test --stream",
|
"test": "lerna run test --stream",
|
||||||
"test:browser": "lerna run test:browser --stream",
|
"test:browser": "lerna run test:browser --stream",
|
||||||
"test:integration": "lerna run test:integration --stream",
|
"test:integration": "lerna run test:integration --stream",
|
||||||
"lint": "lerna run lint --stream",
|
"lint": "lerna run lint --stream",
|
||||||
"clean": "lerna run clean --stream",
|
"clean": "lerna run clean --stream",
|
||||||
"build": "lerna run build --stream",
|
"build": "lerna run build --stream",
|
||||||
"docgen": "lerna run docgen --stream"
|
"docgen": "lerna run docgen --stream",
|
||||||
|
"update:check": "npx npm-check-updates --configFileName .ncurc.json",
|
||||||
|
"update:confirm": "npx npm-check-updates --configFileName .ncurc.json -u"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ripple-address-codec": "file:packages/ripple-address-codec",
|
"@xrplf/isomorphic": "file:packages/isomorphic",
|
||||||
"ripple-binary-codec": "file:packages/ripple-binary-codec",
|
"@xrplf/secret-numbers": "file:packages/secret-numbers",
|
||||||
"ripple-keypairs": "file:packages/ripple-keypairs",
|
"xahau-address-codec": "file:packages/xahau-address-codec",
|
||||||
"xrpl": "file:packages/xrpl"
|
"xahau-binary-codec": "file:packages/xahau-binary-codec",
|
||||||
|
"xahau-keypairs": "file:packages/xahau-keypairs",
|
||||||
|
"xahau": "file:packages/xahau"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.21",
|
"@types/chai": "^4.2.21",
|
||||||
|
"@types/jest": "^29.2.2",
|
||||||
"@types/lodash": "^4.14.136",
|
"@types/lodash": "^4.14.136",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/node": "^18.19.29",
|
||||||
"@types/node": "^16.4.3",
|
|
||||||
"@types/puppeteer": "5.4.4",
|
|
||||||
"@types/ws": "^8.2.0",
|
"@types/ws": "^8.2.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||||
"@typescript-eslint/parser": "^4.0.0",
|
"@typescript-eslint/parser": "^5.28.0",
|
||||||
"@xrplf/eslint-config": "^1.6.0",
|
"@xrplf/eslint-config": "^1.9.1",
|
||||||
"@xrplf/prettier-config": "^1.5.0",
|
"@xrplf/prettier-config": "^1.9.1",
|
||||||
"assert": "^2.0.0",
|
|
||||||
"buffer": "^6.0.2",
|
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
"crypto-browserify": "^3.12.0",
|
"copyfiles": "^2.4.1",
|
||||||
"ejs": "^3.0.1",
|
"eslint": "^8.18.0",
|
||||||
"eslint": "^7.5.0",
|
|
||||||
"eslint-plugin-array-func": "^3.1.7",
|
"eslint-plugin-array-func": "^3.1.7",
|
||||||
"eslint-plugin-consistent-default-export-name": "^0.0.13",
|
"eslint-plugin-consistent-default-export-name": "^0.0.15",
|
||||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||||
"eslint-plugin-import": "^2.24.1",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-jsdoc": "^36.0.0",
|
"eslint-plugin-jsdoc": "^39.3.3",
|
||||||
"eslint-plugin-mocha": "^9.0.0",
|
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-tsdoc": "^0.2.14",
|
"eslint-plugin-tsdoc": "^0.2.16",
|
||||||
"eventemitter2": "^6.0.0",
|
"expect": "^29.3.1",
|
||||||
"https-browserify": "^1.0.0",
|
"jest": "^29.3.1",
|
||||||
"jest": "^26.0.1",
|
"jest-mock": "^29.3.1",
|
||||||
"lerna": "^4.0.0",
|
"lerna": "^4.0.0",
|
||||||
"mocha": "^9",
|
"lodash": "^4.17.21",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"nyc": "^15",
|
|
||||||
"path-browserify": "1.0.1",
|
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"puppeteer": "10.4.0",
|
"source-map-loader": "^5.0.0",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
"stream-browserify": "^3.0.0",
|
"ts-jest": "^29.0.3",
|
||||||
"stream-http": "3.2.0",
|
|
||||||
"ts-jest": "^26.4.4",
|
|
||||||
"ts-loader": "^9.2.5",
|
"ts-loader": "^9.2.5",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typedoc": "^0.22.5",
|
"typescript": "^5.1.6",
|
||||||
"typescript": "^4.4.2",
|
"webpack": "^5.81.0",
|
||||||
"url": "^0.11.0",
|
|
||||||
"webpack": "^5.6.0",
|
|
||||||
"webpack-bundle-analyzer": "^4.1.0",
|
"webpack-bundle-analyzer": "^4.1.0",
|
||||||
"webpack-cli": "^4.2.0"
|
"webpack-cli": "^5.0.1"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"./packages/*"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0",
|
"node": ">=18.0.0",
|
||||||
"npm": ">=7.0.0 < 8.0.0"
|
"npm": ">=7.10.0 < 10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
packages/isomorphic/.eslintignore
Normal file
14
packages/isomorphic/.eslintignore
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Don't ever lint node_modules
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Don't lint build output
|
||||||
|
dist
|
||||||
|
|
||||||
|
# don't lint nyc coverage output
|
||||||
|
coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Don't lint NYC configuration
|
||||||
|
nyc.config.js
|
||||||
|
|
||||||
|
.idea
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: false,
|
||||||
|
|
||||||
parser: '@typescript-eslint/parser', // Make ESLint compatible with TypeScript
|
parser: '@typescript-eslint/parser', // Make ESLint compatible with TypeScript
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
// Enable linting rules with type information from our tsconfig
|
// Enable linting rules with type information from our tsconfig
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
project: ['./tsconfig.json'],
|
project: ['./tsconfig.json', './tsconfig.eslint.json'],
|
||||||
|
|
||||||
sourceType: 'module', // Allow the use of imports / ES modules
|
sourceType: 'module', // Allow the use of imports / ES modules
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ module.exports = {
|
|||||||
browser: true, // Enable browser global variables
|
browser: true, // Enable browser global variables
|
||||||
node: true, // Enable node global variables & Node.js scoping
|
node: true, // Enable node global variables & Node.js scoping
|
||||||
es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
|
es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
|
||||||
|
jest: true, // Add Jest testing global variables
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [],
|
plugins: [],
|
||||||
@@ -29,57 +30,46 @@ module.exports = {
|
|||||||
// all of the below are turned off for now during the migration to a
|
// all of the below are turned off for now during the migration to a
|
||||||
// monorepo. They need to actually be addressed!
|
// monorepo. They need to actually be addressed!
|
||||||
// **
|
// **
|
||||||
'@typescript-eslint/no-for-in-array': 'off',
|
|
||||||
'@typescript-eslint/consistent-type-assertions': 'off',
|
|
||||||
'@typescript-eslint/no-unnecessary-condition': 'off',
|
|
||||||
'@typescript-eslint/prefer-for-of': 'off',
|
|
||||||
'@typescript-eslint/no-require-imports': 'off',
|
|
||||||
'@typescript-eslint/no-var-requires': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
'@typescript-eslint/no-unsafe-call': 'off',
|
'@typescript-eslint/no-unsafe-call': 'off',
|
||||||
'@typescript-eslint/no-magic-numbers': 'off',
|
'@typescript-eslint/no-magic-numbers': 'off',
|
||||||
'@typescript-eslint/ban-types': 'off',
|
'@typescript-eslint/ban-types': 'off',
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||||
'@typescript-eslint/no-unsafe-return': 'off',
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||||
'@typescript-eslint/promise-function-async': 'off',
|
'@typescript-eslint/promise-function-async': 'off',
|
||||||
|
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||||
'@typescript-eslint/naming-convention': 'off',
|
'@typescript-eslint/naming-convention': 'off',
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
'@typescript-eslint/array-type': 'off',
|
'@typescript-eslint/consistent-type-assertions': 'off',
|
||||||
'@typescript-eslint/restrict-plus-operands': 'off',
|
'import/no-unused-modules': 'off',
|
||||||
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
|
'import/prefer-default-export': 'off',
|
||||||
'jsdoc/require-returns': 'off',
|
|
||||||
'jsdoc/check-param-names': 'off',
|
|
||||||
'jsdoc/require-throws': 'off',
|
|
||||||
'jsdoc/require-hyphen-before-param-description': 'off',
|
|
||||||
'jsdoc/require-jsdoc': 'off',
|
'jsdoc/require-jsdoc': 'off',
|
||||||
|
'jsdoc/require-description': 'off',
|
||||||
|
'jsdoc/require-returns': 'off',
|
||||||
'jsdoc/require-description-complete-sentence': 'off',
|
'jsdoc/require-description-complete-sentence': 'off',
|
||||||
'jsdoc/require-param': 'off',
|
'jsdoc/check-tag-names': 'off',
|
||||||
|
'jsdoc/check-examples': 'off', // Not implemented in eslint 8
|
||||||
'jsdoc/no-types': 'off',
|
'jsdoc/no-types': 'off',
|
||||||
'tsdoc/syntax': 'off',
|
'tsdoc/syntax': 'off',
|
||||||
'import/no-commonjs': 'off',
|
|
||||||
'import/order': 'off',
|
'import/order': 'off',
|
||||||
'no-restricted-syntax': 'off',
|
'eslint-comments/require-description': 'off',
|
||||||
'guard-for-in': 'off',
|
'no-shadow': 'off',
|
||||||
'object-shorthand': 'off',
|
|
||||||
'no-negated-condition': 'off',
|
|
||||||
'no-loop-func': 'off',
|
|
||||||
'id-length': 'off',
|
|
||||||
'no-inline-comments': 'off',
|
|
||||||
'max-lines-per-function': 'off',
|
|
||||||
'max-len': 'off',
|
|
||||||
'no-nested-ternary': 'off',
|
|
||||||
'no-param-reassign': 'off',
|
|
||||||
'no-bitwise': 'off',
|
|
||||||
'multiline-comment-style': 'off',
|
'multiline-comment-style': 'off',
|
||||||
'id-blacklist': 'off',
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
'func-names': 'off',
|
|
||||||
'max-params': 'off',
|
|
||||||
'prefer-template': 'off',
|
|
||||||
'no-else-return': 'off',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['test/*.test.ts'],
|
||||||
|
// tests are importing through full module name to test in an isomorphic way
|
||||||
|
rules: {
|
||||||
|
'node/no-extraneous-import': 'off',
|
||||||
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
24
packages/isomorphic/HISTORY.md
Normal file
24
packages/isomorphic/HISTORY.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# @xrplf/isomorphic Release History
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
## 1.0.1 (2024-06-03)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Throw error if `hexToBytes` or `hexToString` is provided a string that is not in hex
|
||||||
|
|
||||||
|
## 1.0.0 (2024-02-01)
|
||||||
|
|
||||||
|
Initial release providing isomorphic and tree-shakable implementations of:
|
||||||
|
|
||||||
|
* ripemd160
|
||||||
|
* sha256
|
||||||
|
* sha512
|
||||||
|
* bytesToHash
|
||||||
|
* hashToBytes
|
||||||
|
* hexToString
|
||||||
|
* stringToHex
|
||||||
|
* randomBytes
|
||||||
|
* stringToHex
|
||||||
|
* ws
|
||||||
15
packages/isomorphic/LICENSE
Normal file
15
packages/isomorphic/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2023 The XRPL developers
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
124
packages/isomorphic/README.md
Normal file
124
packages/isomorphic/README.md
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
# @xrplf/isomorphic
|
||||||
|
|
||||||
|
A collection of isomorphic implementations of crypto and utility functions.
|
||||||
|
|
||||||
|
Browser implementations of cryptographic functions use `@noble/hashes` and `crypto` for node .
|
||||||
|
|
||||||
|
### Hashes
|
||||||
|
|
||||||
|
All hash functions operate similarly to `@noble/hashes` and have the following properties:
|
||||||
|
|
||||||
|
- They can be called directly by providing a Uint8Array or string which will be converted into a UInt8Array via UTF-8 encoding (not hex).
|
||||||
|
- They all return a UInt8Array.
|
||||||
|
|
||||||
|
```
|
||||||
|
function hash(message: Uint8Array | string): Uint8Array;
|
||||||
|
hash(new Uint8Array([1, 3]));
|
||||||
|
hash('string') == hash(new TextEncoder().encode('string'));
|
||||||
|
```
|
||||||
|
|
||||||
|
All hash functions can be constructed via `hash.create()` method:
|
||||||
|
|
||||||
|
- The result is `Hash` subclass instance, which has `update()` and `digest()` methods.
|
||||||
|
- `digest()` finalizes the hash and makes it no longer usable
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
hash
|
||||||
|
.create()
|
||||||
|
.update(new Uint8Array([1, 3]))
|
||||||
|
.digest();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `@xrplf/isomorphic/ripemd160`
|
||||||
|
```typescript
|
||||||
|
import { ripemd160 } from '@xrplf/isomorphic/ripemd160';
|
||||||
|
const hashA = ripemd160('abc');
|
||||||
|
const hashB = ripemd160
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([1, 2, 3]))
|
||||||
|
.digest();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `@xrplf/isomorphic/sha256`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { sha256 } from '@xrplf/isomorphic/sha256';
|
||||||
|
const hashA = sha256('abc');
|
||||||
|
const hashB = sha256
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([1, 2, 3]))
|
||||||
|
.digest();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `@xrplf/isomorphic/sha512`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { sha512 } from '@xrplf/isomorphic/sha512';
|
||||||
|
const hashA = sha512('abc');
|
||||||
|
const hashB = sha512
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([1, 2, 3]))
|
||||||
|
.digest();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
### `@xrplf/isomorphic/utils`
|
||||||
|
|
||||||
|
#### randomBytes
|
||||||
|
|
||||||
|
Create an UInt8Array of the supplied size
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { randomBytes } from @xrplf/isomorphic/utils
|
||||||
|
|
||||||
|
console.log(randomBytes(12)) // Uint8Array(12) [95, 236, 188, 55, 208, 128, 161, 249, 171, 57, 141, 7]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### bytesToHex
|
||||||
|
|
||||||
|
Convert an UInt8Array to hex.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { bytesToHex } from @xrplf/isomorphic/utils
|
||||||
|
|
||||||
|
console.log(bytesToHex([222, 173, 190, 239])) // "DEADBEEF"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hexToBytes
|
||||||
|
|
||||||
|
Convert hex to an UInt8Array.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { hexToBytes } from @xrplf/isomorphic/utils
|
||||||
|
|
||||||
|
console.log(hexToBytes('DEADBEEF')) // [222, 173, 190, 239]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hexToString
|
||||||
|
|
||||||
|
Converts hex to its string equivalent. Useful to read the Domain field and some Memos.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { hexToString } from @xrplf/isomorphic/utils
|
||||||
|
|
||||||
|
console.log(hexToString('6465616462656566D68D')) // "deadbeef֍"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### stringToHex
|
||||||
|
|
||||||
|
Converts a utf-8 to its hex equivalent. Useful for Memos.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { stringToHex } from @xrplf/isomorphic/utils
|
||||||
|
|
||||||
|
console.log(stringToHex('deadbeef֍')) // "6465616462656566D68D"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `@xrplf/isomorphic/ws`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import WebSocket from '@xrplf/isomorphic/ws'
|
||||||
|
|
||||||
|
const socket = new WebSocket('wss://localhost:8080')
|
||||||
|
```
|
||||||
8
packages/isomorphic/jest.config.js
Normal file
8
packages/isomorphic/jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Jest configuration for api
|
||||||
|
const base = require('../../jest.config.base.js')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...base,
|
||||||
|
roots: [...base.roots, '<rootDir>/test'],
|
||||||
|
displayName: '@xrplf/isomorphic',
|
||||||
|
}
|
||||||
15
packages/isomorphic/karma.config.js
Normal file
15
packages/isomorphic/karma.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const baseKarmaConfig = require('../../karma.config')
|
||||||
|
const webpackConfig = require('./test/webpack.config')
|
||||||
|
delete webpackConfig.entry
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
baseKarmaConfig(config)
|
||||||
|
|
||||||
|
config.set({
|
||||||
|
base: '',
|
||||||
|
webpack: webpackConfig,
|
||||||
|
|
||||||
|
// list of files / patterns to load in the browser
|
||||||
|
files: ['test/**/*.test.ts'],
|
||||||
|
})
|
||||||
|
}
|
||||||
48
packages/isomorphic/package.json
Normal file
48
packages/isomorphic/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"description": "A collection of isomorphic and tree-shakeable crypto hashes and utils for xahau.js",
|
||||||
|
"keywords": [
|
||||||
|
"crypto",
|
||||||
|
"isomorphic",
|
||||||
|
"xahau"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build ./tsconfig.build.json",
|
||||||
|
"test": "npm run build && jest --verbose false --silent=false ./test/*.test.ts",
|
||||||
|
"test:browser": "npm run build && karma start ./karma.config.js",
|
||||||
|
"clean": "rm -rf ./dist ./coverage ./test/testCompiledForWeb tsconfig.build.tsbuildinfo",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"prepublish": "npm run lint && npm test"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/*",
|
||||||
|
"sha256/*",
|
||||||
|
"sha512/*",
|
||||||
|
"ripemd160/*",
|
||||||
|
"src/*",
|
||||||
|
"utils/*",
|
||||||
|
"ws/*"
|
||||||
|
],
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "^1.0.0",
|
||||||
|
"eventemitter3": "5.0.1",
|
||||||
|
"ws": "^8.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.18.38",
|
||||||
|
"@types/ws": "^8.5.6"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@github.com:Xahau/xahau.js.git"
|
||||||
|
},
|
||||||
|
"license": "ISC",
|
||||||
|
"prettier": "@xrplf/prettier-config",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
packages/isomorphic/ripemd160/package.json
Normal file
7
packages/isomorphic/ripemd160/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic/ripemd160",
|
||||||
|
"private": true,
|
||||||
|
"main": "../dist/ripemd160",
|
||||||
|
"types": "../dist/ripemd160",
|
||||||
|
"browser": "../dist/ripemd160/browser.js"
|
||||||
|
}
|
||||||
7
packages/isomorphic/sha256/package.json
Normal file
7
packages/isomorphic/sha256/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic/sha256",
|
||||||
|
"private": true,
|
||||||
|
"main": "../dist/sha256",
|
||||||
|
"types": "../dist/sha256",
|
||||||
|
"browser": "../dist/sha256/browser.js"
|
||||||
|
}
|
||||||
7
packages/isomorphic/sha512/package.json
Normal file
7
packages/isomorphic/sha512/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic/sha512",
|
||||||
|
"private": true,
|
||||||
|
"main": "../dist/sha512",
|
||||||
|
"types": "../dist/sha512",
|
||||||
|
"browser": "../dist/sha512/browser.js"
|
||||||
|
}
|
||||||
11
packages/isomorphic/src/internal/normalizeInput.ts
Normal file
11
packages/isomorphic/src/internal/normalizeInput.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Input } from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a string, number array, or Uint8Array to a string or Uint8Array.
|
||||||
|
* Both node and noble lib functions accept these types.
|
||||||
|
*
|
||||||
|
* @param input - value to normalize
|
||||||
|
*/
|
||||||
|
export default function normalizeInput(input: Input): string | Uint8Array {
|
||||||
|
return Array.isArray(input) ? new Uint8Array(input) : input
|
||||||
|
}
|
||||||
33
packages/isomorphic/src/internal/types.ts
Normal file
33
packages/isomorphic/src/internal/types.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export type ByteEncodedString = string
|
||||||
|
export type Input = Uint8Array | number[] | ByteEncodedString
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stripped down isomorphic hash inspired by node's `crypto.Hash`
|
||||||
|
*/
|
||||||
|
export interface Hash {
|
||||||
|
/**
|
||||||
|
* Updates the hash content with the given data,
|
||||||
|
*
|
||||||
|
* @param data - a byte encoded string, an array of numbers or a Uint8Array
|
||||||
|
*/
|
||||||
|
update: (data: Input) => this
|
||||||
|
/**
|
||||||
|
* Calculates the digest of all the data passed to be hashed and returns a Uint8Array
|
||||||
|
*/
|
||||||
|
digest: () => Uint8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HashFn {
|
||||||
|
/**
|
||||||
|
* Produces a Uint8Array for the given hash contents
|
||||||
|
* Shorthand for calling `create`, `update`, and then `digest`
|
||||||
|
*
|
||||||
|
* @param data - a byte encoded string, an array of numbers or a Uint8Array
|
||||||
|
*/
|
||||||
|
(data: Input): Uint8Array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new empty `Hash`.
|
||||||
|
*/
|
||||||
|
create: () => Hash
|
||||||
|
}
|
||||||
32
packages/isomorphic/src/internal/wrapCryptoCreateHash.ts
Normal file
32
packages/isomorphic/src/internal/wrapCryptoCreateHash.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { createHash } from 'crypto'
|
||||||
|
import { Hash, HashFn, Input } from './types'
|
||||||
|
import normalizeInput from './normalizeInput'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap createHash from node to provide an interface that is isomorphic
|
||||||
|
*
|
||||||
|
* @param type - the hash name
|
||||||
|
* @param fn - {createHash} the hash factory
|
||||||
|
*/
|
||||||
|
export default function wrapCryptoCreateHash(
|
||||||
|
type: string,
|
||||||
|
fn: typeof createHash,
|
||||||
|
): HashFn {
|
||||||
|
function hashFn(input: Input): Uint8Array {
|
||||||
|
return fn(type).update(normalizeInput(input)).digest()
|
||||||
|
}
|
||||||
|
|
||||||
|
hashFn.create = (): Hash => {
|
||||||
|
const hash = fn(type)
|
||||||
|
return {
|
||||||
|
update(input: Input): Hash {
|
||||||
|
hash.update(normalizeInput(input))
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
digest(): Uint8Array {
|
||||||
|
return hash.digest()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hashFn
|
||||||
|
}
|
||||||
28
packages/isomorphic/src/internal/wrapNoble.ts
Normal file
28
packages/isomorphic/src/internal/wrapNoble.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { CHash } from '@noble/hashes/utils'
|
||||||
|
import { Hash, HashFn, Input } from './types'
|
||||||
|
import normalizeInput from './normalizeInput'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap a CHash object from @noble/hashes to provide a interface that is isomorphic
|
||||||
|
*
|
||||||
|
* @param chash - {CHash} hash function to wrap
|
||||||
|
*/
|
||||||
|
export default function wrapNoble(chash: CHash): HashFn {
|
||||||
|
function wrapped(input: Input): Uint8Array {
|
||||||
|
return chash(normalizeInput(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapped.create = (): Hash => {
|
||||||
|
const hash = chash.create()
|
||||||
|
return {
|
||||||
|
update(input: Input): Hash {
|
||||||
|
hash.update(normalizeInput(input))
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
digest(): Uint8Array {
|
||||||
|
return hash.digest()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wrapped
|
||||||
|
}
|
||||||
8
packages/isomorphic/src/ripemd160/browser.ts
Normal file
8
packages/isomorphic/src/ripemd160/browser.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ripemd160 as nobleImpl } from '@noble/hashes/ripemd160'
|
||||||
|
|
||||||
|
import wrapNoble from '../internal/wrapNoble'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap noble-libs's ripemd160 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const ripemd160 = wrapNoble(nobleImpl)
|
||||||
7
packages/isomorphic/src/ripemd160/index.ts
Normal file
7
packages/isomorphic/src/ripemd160/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHash } from 'crypto'
|
||||||
|
import wrapCryptoCreateHash from '../internal/wrapCryptoCreateHash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap node's native ripemd160 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const ripemd160 = wrapCryptoCreateHash('ripemd160', createHash)
|
||||||
8
packages/isomorphic/src/sha256/browser.ts
Normal file
8
packages/isomorphic/src/sha256/browser.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { sha256 as nobleImpl } from '@noble/hashes/sha256'
|
||||||
|
|
||||||
|
import wrapNoble from '../internal/wrapNoble'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap noble-libs's sha256 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const sha256 = wrapNoble(nobleImpl)
|
||||||
7
packages/isomorphic/src/sha256/index.ts
Normal file
7
packages/isomorphic/src/sha256/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHash } from 'crypto'
|
||||||
|
import wrapCryptoCreateHash from '../internal/wrapCryptoCreateHash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap node's native sha256 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const sha256 = wrapCryptoCreateHash('sha256', createHash)
|
||||||
8
packages/isomorphic/src/sha512/browser.ts
Normal file
8
packages/isomorphic/src/sha512/browser.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { sha512 as nobleImpl } from '@noble/hashes/sha512'
|
||||||
|
|
||||||
|
import wrapNoble from '../internal/wrapNoble'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap noble-libs's sha512 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const sha512 = wrapNoble(nobleImpl)
|
||||||
7
packages/isomorphic/src/sha512/index.ts
Normal file
7
packages/isomorphic/src/sha512/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { createHash } from 'crypto'
|
||||||
|
import wrapCryptoCreateHash from '../internal/wrapCryptoCreateHash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap node's native sha512 implementation in HashFn
|
||||||
|
*/
|
||||||
|
export const sha512 = wrapCryptoCreateHash('sha512', createHash)
|
||||||
54
packages/isomorphic/src/utils/browser.ts
Normal file
54
packages/isomorphic/src/utils/browser.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
bytesToHex as nobleBytesToHex,
|
||||||
|
randomBytes as nobleRandomBytes,
|
||||||
|
} from '@noble/hashes/utils'
|
||||||
|
import type {
|
||||||
|
BytesToHexFn,
|
||||||
|
HexToBytesFn,
|
||||||
|
HexToStringFn,
|
||||||
|
RandomBytesFn,
|
||||||
|
StringToHexFn,
|
||||||
|
} from './types'
|
||||||
|
import { HEX_REGEX } from './shared'
|
||||||
|
|
||||||
|
/* eslint-disable func-style -- Typed to ensure uniformity between node and browser implementations and docs */
|
||||||
|
export const bytesToHex: typeof BytesToHexFn = (bytes) => {
|
||||||
|
const hex = nobleBytesToHex(
|
||||||
|
bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes),
|
||||||
|
)
|
||||||
|
return hex.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A clone of hexToBytes from @noble/hashes without the length checks. This allows us to do our own checks.
|
||||||
|
export const hexToBytes: typeof HexToBytesFn = (hex): Uint8Array => {
|
||||||
|
const len = hex.length
|
||||||
|
const array = new Uint8Array(len / 2)
|
||||||
|
if (!HEX_REGEX.test(hex)) {
|
||||||
|
throw new Error('Invalid hex string')
|
||||||
|
}
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
const j = i * 2
|
||||||
|
const hexByte = hex.slice(j, j + 2)
|
||||||
|
const byte = Number.parseInt(hexByte, 16)
|
||||||
|
if (Number.isNaN(byte) || byte < 0) {
|
||||||
|
throw new Error('Invalid byte sequence')
|
||||||
|
}
|
||||||
|
array[i] = byte
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hexToString: typeof HexToStringFn = (
|
||||||
|
hex: string,
|
||||||
|
encoding = 'utf8',
|
||||||
|
): string => {
|
||||||
|
return new TextDecoder(encoding).decode(hexToBytes(hex))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stringToHex: typeof StringToHexFn = (string: string): string => {
|
||||||
|
return bytesToHex(new TextEncoder().encode(string))
|
||||||
|
}
|
||||||
|
/* eslint-enable func-style */
|
||||||
|
|
||||||
|
export const randomBytes: typeof RandomBytesFn = nobleRandomBytes
|
||||||
|
export * from './shared'
|
||||||
93
packages/isomorphic/src/utils/index.ts
Normal file
93
packages/isomorphic/src/utils/index.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { randomBytes as cryptoRandomBytes } from 'crypto'
|
||||||
|
import type { BytesToHexFn, HexToBytesFn, RandomBytesFn } from './types'
|
||||||
|
import { HexToStringFn, StringToHexFn } from './types'
|
||||||
|
import { HEX_REGEX } from './shared'
|
||||||
|
|
||||||
|
const OriginalBuffer = Symbol('OriginalBuffer')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extended Uint8Array that incorporates a reference to the original Node.js Buffer.
|
||||||
|
*
|
||||||
|
* When converting a Node.js Buffer to a Uint8Array, there's an optimization that shares
|
||||||
|
* the memory of the original Buffer with the resulting Uint8Array instead of copying data.
|
||||||
|
* The Uint8ArrayWithReference interface is used to attach a reference to the original Buffer, ensuring
|
||||||
|
* its persistence in memory (preventing garbage collection) as long as the Uint8Array exists.
|
||||||
|
* This strategy upholds the ownership semantics of the slice of the ArrayBuffer.
|
||||||
|
*/
|
||||||
|
interface Uint8ArrayWithReference extends Uint8Array {
|
||||||
|
[OriginalBuffer]: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Node.js Buffer to a Uint8Array for uniform behavior with browser implementations.
|
||||||
|
*
|
||||||
|
* Choices:
|
||||||
|
* 1. Directly returning the Buffer:
|
||||||
|
* - Operation: Return Buffer as is (a Buffer *IS* an instanceof Uint8Array).
|
||||||
|
* - Pros: Most memory and performance efficient.
|
||||||
|
* - Cons: Violates strict Uint8Array typing and may lead to issues where Buffer-specific features are [ab]used.
|
||||||
|
*
|
||||||
|
* 2. Using `new Uint8Array(buffer)` or `Uint8Array.from(buffer)`:
|
||||||
|
* - Operation: Copies the buffer's data into a new Uint8Array.
|
||||||
|
* - Pros: Ensures data isolation; memory-safe.
|
||||||
|
* - Cons: Less performant due to data duplication.
|
||||||
|
*
|
||||||
|
* 3. Using buf.buffer slice:
|
||||||
|
* - Operation: Shares memory between Buffer and Uint8Array.
|
||||||
|
* - Pros: Performant.
|
||||||
|
* - Cons: Risks with shared memory and potential for invalid references.
|
||||||
|
*
|
||||||
|
* 4. Using buf.buffer slice and keeping a Buffer reference for ownership semantics:
|
||||||
|
* - Operation: Shares memory and associates the original Buffer with the resulting Uint8Array.
|
||||||
|
* - Pros: Performant while ensuring the original Buffer isn't garbage collected.
|
||||||
|
* - Cons: Risks with shared memory but mitigates potential for invalid references.
|
||||||
|
*
|
||||||
|
* The chosen method (4) prioritizes performance by sharing memory while ensuring buffer ownership.
|
||||||
|
*
|
||||||
|
* @param {Buffer} buffer - The Node.js Buffer to convert.
|
||||||
|
* @returns {Uint8Array} Resulting Uint8Array sharing the same memory as the Buffer and maintaining a reference to it.
|
||||||
|
*/
|
||||||
|
function toUint8Array(buffer: Buffer): Uint8Array {
|
||||||
|
const u8Array = new Uint8Array(
|
||||||
|
buffer.buffer.slice(
|
||||||
|
buffer.byteOffset,
|
||||||
|
buffer.byteOffset + buffer.byteLength,
|
||||||
|
),
|
||||||
|
) as Uint8ArrayWithReference
|
||||||
|
u8Array[OriginalBuffer] = buffer
|
||||||
|
return u8Array
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable func-style -- Typed to ensure uniformity between node and browser implementations and docs */
|
||||||
|
export const bytesToHex: typeof BytesToHexFn = (bytes) => {
|
||||||
|
const buf = Buffer.from(bytes)
|
||||||
|
return buf.toString('hex').toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hexToBytes: typeof HexToBytesFn = (hex) => {
|
||||||
|
if (!HEX_REGEX.test(hex)) {
|
||||||
|
throw new Error('Invalid hex string')
|
||||||
|
}
|
||||||
|
return toUint8Array(Buffer.from(hex, 'hex'))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const randomBytes: typeof RandomBytesFn = (size) => {
|
||||||
|
return toUint8Array(cryptoRandomBytes(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hexToString: typeof HexToStringFn = (
|
||||||
|
hex: string,
|
||||||
|
encoding = 'utf8',
|
||||||
|
): string => {
|
||||||
|
if (!HEX_REGEX.test(hex)) {
|
||||||
|
throw new Error('Invalid hex string')
|
||||||
|
}
|
||||||
|
return new TextDecoder(encoding).decode(hexToBytes(hex))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stringToHex: typeof StringToHexFn = (string: string): string => {
|
||||||
|
return bytesToHex(new TextEncoder().encode(string))
|
||||||
|
}
|
||||||
|
/* eslint-enable func-style */
|
||||||
|
|
||||||
|
export * from './shared'
|
||||||
21
packages/isomorphic/src/utils/shared.ts
Normal file
21
packages/isomorphic/src/utils/shared.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { concatBytes } from '@noble/hashes/utils'
|
||||||
|
|
||||||
|
export const HEX_REGEX = /^[A-F0-9]*$/iu
|
||||||
|
|
||||||
|
export function concat(views: Uint8Array[]): Uint8Array {
|
||||||
|
return concatBytes(...views)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function equal(buf1: Uint8Array, buf2: Uint8Array): boolean {
|
||||||
|
if (buf1.byteLength !== buf2.byteLength) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const dv1 = new Int8Array(buf1)
|
||||||
|
const dv2 = new Int8Array(buf2)
|
||||||
|
for (let i = 0; i !== buf1.byteLength; i++) {
|
||||||
|
if (dv1[i] !== dv2[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
37
packages/isomorphic/src/utils/types.ts
Normal file
37
packages/isomorphic/src/utils/types.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Convert a UInt8Array to hex. The returned hex will be in all caps.
|
||||||
|
*
|
||||||
|
* @param bytes - {Uint8Array} to convert to hex
|
||||||
|
*/
|
||||||
|
export declare function BytesToHexFn(bytes: Uint8Array | number[]): string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert hex to a Uint8Array.
|
||||||
|
*
|
||||||
|
* @param hex - {string} to convert to a Uint8Array
|
||||||
|
*/
|
||||||
|
export declare function HexToBytesFn(hex: string): Uint8Array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Uint8Array of the supplied size.
|
||||||
|
*
|
||||||
|
* @param size - number of bytes to generate
|
||||||
|
*/
|
||||||
|
export declare function RandomBytesFn(size: number): Uint8Array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts hex to its string equivalent. Useful to read the Domain field and some Memos.
|
||||||
|
*
|
||||||
|
* @param hex - The hex to convert to a string.
|
||||||
|
* @param encoding - The encoding to use. Defaults to 'utf8' (UTF-8). 'ascii' is also allowed.
|
||||||
|
* @returns The converted string.
|
||||||
|
*/
|
||||||
|
export declare function HexToStringFn(hex: string, encoding?: string): string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a utf-8 to its hex equivalent. Useful for Memos.
|
||||||
|
*
|
||||||
|
* @param string - The string to convert to Hex.
|
||||||
|
* @returns The Hex equivalent of the string.
|
||||||
|
*/
|
||||||
|
export declare function StringToHexFn(string: string): string
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
/* eslint-disable import/no-unused-modules -- Used by webpack */
|
|
||||||
/* eslint-disable max-classes-per-file -- Needs to be a wrapper for ws */
|
/* eslint-disable max-classes-per-file -- Needs to be a wrapper for ws */
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'eventemitter3'
|
||||||
|
|
||||||
// Define the global WebSocket class found on the native browser
|
// Define the global WebSocket class found on the native browser
|
||||||
declare class WebSocket {
|
declare class WebSocket {
|
||||||
public onclose?: () => void
|
public onclose?: (closeEvent: CloseEvent) => void
|
||||||
public onopen?: () => void
|
public onopen?: (openEvent: Event) => void
|
||||||
public onerror?: (error: Error) => void
|
public onerror?: (error: Error) => void
|
||||||
public onmessage?: (message: MessageEvent) => void
|
public onmessage?: (message: MessageEvent) => void
|
||||||
public readyState: number
|
public readyState: number
|
||||||
public constructor(url: string)
|
public constructor(url: string)
|
||||||
public close(code?: number): void
|
public close(code?: number, reason?: Uint8Array): void
|
||||||
public send(message: string): void
|
public send(message: string): void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ export default class WSWrapper extends EventEmitter {
|
|||||||
public static CONNECTING = 0
|
public static CONNECTING = 0
|
||||||
public static OPEN = 1
|
public static OPEN = 1
|
||||||
public static CLOSING = 2
|
public static CLOSING = 2
|
||||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- magic number is being defined here
|
|
||||||
public static CLOSED = 3
|
public static CLOSED = 3
|
||||||
private readonly ws: WebSocket
|
private readonly ws: WebSocket
|
||||||
|
|
||||||
@@ -49,12 +48,16 @@ export default class WSWrapper extends EventEmitter {
|
|||||||
_websocketOptions: WSWrapperOptions,
|
_websocketOptions: WSWrapperOptions,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.setMaxListeners(Infinity)
|
|
||||||
|
|
||||||
this.ws = new WebSocket(url)
|
this.ws = new WebSocket(url)
|
||||||
|
|
||||||
this.ws.onclose = (): void => {
|
this.ws.onclose = (closeEvent: CloseEvent): void => {
|
||||||
this.emit('close')
|
let reason: Uint8Array | undefined
|
||||||
|
if (closeEvent.reason) {
|
||||||
|
const enc = new TextEncoder()
|
||||||
|
reason = enc.encode(closeEvent.reason)
|
||||||
|
}
|
||||||
|
this.emit('close', closeEvent.code, reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ws.onopen = (): void => {
|
this.ws.onopen = (): void => {
|
||||||
@@ -71,11 +74,23 @@ export default class WSWrapper extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the websocket.
|
* Get the ready state of the websocket.
|
||||||
|
*
|
||||||
|
* @returns The Websocket's ready state.
|
||||||
*/
|
*/
|
||||||
public close(): void {
|
public get readyState(): number {
|
||||||
|
return this.ws.readyState
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the websocket.
|
||||||
|
*
|
||||||
|
* @param code - Close code.
|
||||||
|
* @param reason - Close reason.
|
||||||
|
*/
|
||||||
|
public close(code?: number, reason?: Buffer): void {
|
||||||
if (this.readyState === 1) {
|
if (this.readyState === 1) {
|
||||||
this.ws.close()
|
this.ws.close(code, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,13 +102,4 @@ export default class WSWrapper extends EventEmitter {
|
|||||||
public send(message: string): void {
|
public send(message: string): void {
|
||||||
this.ws.send(message)
|
this.ws.send(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the ready state of the websocket.
|
|
||||||
*
|
|
||||||
* @returns The Websocket's ready state.
|
|
||||||
*/
|
|
||||||
public get readyState(): number {
|
|
||||||
return this.ws.readyState
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
10
packages/isomorphic/src/ws/index.ts
Normal file
10
packages/isomorphic/src/ws/index.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import WebSocket from 'ws'
|
||||||
|
|
||||||
|
export default class Socket extends WebSocket {
|
||||||
|
constructor(...args) {
|
||||||
|
super(args[0], args[1], args[2])
|
||||||
|
this.setMaxListeners(Infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientOptions = WebSocket.ClientOptions
|
||||||
16
packages/isomorphic/test/ripemd160.test.ts
Normal file
16
packages/isomorphic/test/ripemd160.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ripemd160 } from '@xrplf/isomorphic/ripemd160'
|
||||||
|
import { bytesToHex } from '@xrplf/isomorphic/utils'
|
||||||
|
|
||||||
|
describe('ripemd160', () => {
|
||||||
|
it('hashes', () => {
|
||||||
|
const hashA = ripemd160('abc')
|
||||||
|
const hashB = ripemd160
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([97, 98, 99]))
|
||||||
|
.digest()
|
||||||
|
const expectedHash = `8EB208F7E05D987A9B044A8E98C6B087F15A0BFC`
|
||||||
|
|
||||||
|
expect(bytesToHex(hashA)).toEqual(expectedHash)
|
||||||
|
expect(bytesToHex(hashB)).toEqual(expectedHash)
|
||||||
|
})
|
||||||
|
})
|
||||||
17
packages/isomorphic/test/sha256.test.ts
Normal file
17
packages/isomorphic/test/sha256.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { sha256 } from '@xrplf/isomorphic/sha256'
|
||||||
|
import { bytesToHex } from '@xrplf/isomorphic/utils'
|
||||||
|
|
||||||
|
describe('sha256', () => {
|
||||||
|
it('hashes', () => {
|
||||||
|
const hashA = sha256('abc')
|
||||||
|
const hashB = sha256
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([97, 98, 99]))
|
||||||
|
.digest()
|
||||||
|
const expectedHash =
|
||||||
|
'BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD'
|
||||||
|
|
||||||
|
expect(bytesToHex(hashA)).toEqual(expectedHash)
|
||||||
|
expect(bytesToHex(hashB)).toEqual(expectedHash)
|
||||||
|
})
|
||||||
|
})
|
||||||
17
packages/isomorphic/test/sha512.test.ts
Normal file
17
packages/isomorphic/test/sha512.test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { sha512 } from '@xrplf/isomorphic/sha512'
|
||||||
|
import { bytesToHex } from '@xrplf/isomorphic/utils'
|
||||||
|
|
||||||
|
describe('sha512', () => {
|
||||||
|
it('hashes', () => {
|
||||||
|
const hashA = sha512('abc')
|
||||||
|
const hashB = sha512
|
||||||
|
.create()
|
||||||
|
.update(Uint8Array.from([97, 98, 99]))
|
||||||
|
.digest()
|
||||||
|
const expectedHash =
|
||||||
|
'DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA20A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD454D4423643CE80E2A9AC94FA54CA49F'
|
||||||
|
|
||||||
|
expect(bytesToHex(hashA)).toEqual(expectedHash)
|
||||||
|
expect(bytesToHex(hashB)).toEqual(expectedHash)
|
||||||
|
})
|
||||||
|
})
|
||||||
61
packages/isomorphic/test/utils.test.ts
Normal file
61
packages/isomorphic/test/utils.test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
bytesToHex,
|
||||||
|
hexToBytes,
|
||||||
|
hexToString,
|
||||||
|
randomBytes,
|
||||||
|
stringToHex,
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
describe('utils', function () {
|
||||||
|
it('randomBytes', () => {
|
||||||
|
expect(randomBytes(16).byteLength).toEqual(16)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToBytes - empty', () => {
|
||||||
|
expect(hexToBytes('')).toEqual(new Uint8Array([]))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToBytes - zero', () => {
|
||||||
|
expect(hexToBytes('000000')).toEqual(new Uint8Array([0, 0, 0]))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToBytes - DEADBEEF', () => {
|
||||||
|
expect(hexToBytes('DEADBEEF')).toEqual(new Uint8Array([222, 173, 190, 239]))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToBytes - DEADBEEF', () => {
|
||||||
|
expect(hexToBytes('DEADBEEF')).toEqual(new Uint8Array([222, 173, 190, 239]))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bytesToHex - DEADBEEF', () => {
|
||||||
|
expect(bytesToHex([222, 173, 190, 239])).toEqual('DEADBEEF')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bytesToHex - bad hex', () => {
|
||||||
|
expect(() => hexToBytes('hello')).toThrow(new Error('Invalid hex string'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bytesToHex - 010203', () => {
|
||||||
|
expect(bytesToHex([1, 2, 3])).toEqual('010203')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bytesToHex - DEADBEEF (Uint8Array)', () => {
|
||||||
|
expect(bytesToHex(new Uint8Array([222, 173, 190, 239]))).toEqual('DEADBEEF')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToString - deadbeef+infinity symbol (HEX ASCII)', () => {
|
||||||
|
expect(hexToString('646561646265656658D', 'ascii')).toEqual('deadbeefX')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToString - deadbeef+infinity symbol (HEX)', () => {
|
||||||
|
expect(hexToString('6465616462656566D68D')).toEqual('deadbeef֍')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hexToString - bad hex', () => {
|
||||||
|
expect(() => hexToString('hello')).toThrow(new Error('Invalid hex string'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('stringToHex - deadbeef+infinity symbol (utf8)', () => {
|
||||||
|
expect(stringToHex('deadbeef֍')).toEqual('6465616462656566D68D')
|
||||||
|
})
|
||||||
|
})
|
||||||
9
packages/isomorphic/test/webpack.config.js
Normal file
9
packages/isomorphic/test/webpack.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
'use strict'
|
||||||
|
const { merge } = require('webpack-merge')
|
||||||
|
const { webpackForTest } = require('../../../weback.test.config')
|
||||||
|
const { getDefaultConfiguration } = require('../../../webpack.config')
|
||||||
|
|
||||||
|
module.exports = merge(
|
||||||
|
getDefaultConfiguration(),
|
||||||
|
webpackForTest('./test/index.ts', __dirname),
|
||||||
|
)
|
||||||
7
packages/isomorphic/tsconfig.build.json
Normal file
7
packages/isomorphic/tsconfig.build.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts", "./src/**/*.json"],
|
||||||
|
}
|
||||||
4
packages/isomorphic/tsconfig.eslint.json
Normal file
4
packages/isomorphic/tsconfig.eslint.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||||
|
}
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
"target": "es6",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"rootDir": "./src",
|
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"forceConsistentCasingInFileNames": true
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"references": [{
|
|
||||||
"path": "../ripple-address-codec/tsconfig.json"
|
|
||||||
}],
|
|
||||||
"include": ["src/**/*.ts"]
|
"include": ["src/**/*.ts"]
|
||||||
}
|
}
|
||||||
7
packages/isomorphic/utils/package.json
Normal file
7
packages/isomorphic/utils/package.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic/utils",
|
||||||
|
"private": true,
|
||||||
|
"main": "../dist/utils",
|
||||||
|
"types": "../dist/utils",
|
||||||
|
"browser": "../dist/utils/browser.js"
|
||||||
|
}
|
||||||
11
packages/isomorphic/ws/package.json
Normal file
11
packages/isomorphic/ws/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "@xrplf/isomorphic/ws",
|
||||||
|
"private": true,
|
||||||
|
"main": "../dist/ws",
|
||||||
|
"types": "../dist/ws",
|
||||||
|
"browser": "../dist/ws/browser.js",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^8.13.0",
|
||||||
|
"eventemitter3": "^5.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
61
packages/ripple-address-codec/.gitignore
vendored
61
packages/ripple-address-codec/.gitignore
vendored
@@ -1,61 +0,0 @@
|
|||||||
# .gitignore
|
|
||||||
|
|
||||||
# Ignore vim swap files.
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
# Ignore SCons support files.
|
|
||||||
.sconsign.dblite
|
|
||||||
|
|
||||||
# Ignore python compiled files.
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Ignore Macintosh Desktop Services Store files.
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ignore backup/temps
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Ignore object files.
|
|
||||||
*.o
|
|
||||||
build/
|
|
||||||
tags
|
|
||||||
bin/rippled
|
|
||||||
Debug/*.*
|
|
||||||
Release/*.*
|
|
||||||
|
|
||||||
# Ignore locally installed node_modules
|
|
||||||
node_modules
|
|
||||||
!test/node_modules
|
|
||||||
|
|
||||||
# Ignore tmp directory.
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# Ignore database directory.
|
|
||||||
db/*.db
|
|
||||||
db/*.db-*
|
|
||||||
|
|
||||||
# Ignore customized configs
|
|
||||||
rippled.cfg
|
|
||||||
validators.txt
|
|
||||||
test/config.js
|
|
||||||
|
|
||||||
# Ignore coverage files
|
|
||||||
/lib-cov
|
|
||||||
/src-cov
|
|
||||||
/coverage.html
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# Ignore IntelliJ files
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Ignore npm-debug
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# Ignore dist folder
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# Ignore flow output directory
|
|
||||||
out/
|
|
||||||
|
|
||||||
# Ignore perf test cache
|
|
||||||
scripts/cache
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
"roots": [
|
|
||||||
"<rootDir>/src"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.tsx?$": "ts-jest"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ripple-address-codec",
|
|
||||||
"version": "4.2.0",
|
|
||||||
"description": "encodes/decodes base58 encoded XRP Ledger identifiers",
|
|
||||||
"files": [
|
|
||||||
"dist/*",
|
|
||||||
"build/*"
|
|
||||||
],
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"base-x": "3.0.9",
|
|
||||||
"create-hash": "^1.1.2"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@github.com:XRPLF/xrpl.js.git"
|
|
||||||
},
|
|
||||||
"prepublish": "tsc -b",
|
|
||||||
"prepublishOnly": "tslint -b ./ && jest",
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc -b",
|
|
||||||
"test": "jest",
|
|
||||||
"lint": "eslint . --ext .ts",
|
|
||||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo"
|
|
||||||
},
|
|
||||||
"prettier": "@xrplf/prettier-config",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
import {
|
|
||||||
codec,
|
|
||||||
encodeSeed,
|
|
||||||
decodeSeed,
|
|
||||||
encodeAccountID,
|
|
||||||
decodeAccountID,
|
|
||||||
encodeNodePublic,
|
|
||||||
decodeNodePublic,
|
|
||||||
encodeAccountPublic,
|
|
||||||
decodeAccountPublic,
|
|
||||||
isValidClassicAddress,
|
|
||||||
} from './xrp-codec'
|
|
||||||
import * as assert from 'assert'
|
|
||||||
|
|
||||||
const PREFIX_BYTES = {
|
|
||||||
MAIN: Buffer.from([0x05, 0x44]), // 5, 68
|
|
||||||
TEST: Buffer.from([0x04, 0x93]), // 4, 147
|
|
||||||
}
|
|
||||||
|
|
||||||
function classicAddressToXAddress(
|
|
||||||
classicAddress: string,
|
|
||||||
tag: number | false,
|
|
||||||
test: boolean,
|
|
||||||
): string {
|
|
||||||
const accountId = decodeAccountID(classicAddress)
|
|
||||||
return encodeXAddress(accountId, tag, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeXAddress(
|
|
||||||
accountId: Buffer,
|
|
||||||
tag: number | false,
|
|
||||||
test: boolean,
|
|
||||||
): string {
|
|
||||||
if (accountId.length !== 20) {
|
|
||||||
// RIPEMD160 is 160 bits = 20 bytes
|
|
||||||
throw new Error('Account ID must be 20 bytes')
|
|
||||||
}
|
|
||||||
const MAX_32_BIT_UNSIGNED_INT = 4294967295
|
|
||||||
const flag = tag === false ? 0 : tag <= MAX_32_BIT_UNSIGNED_INT ? 1 : 2
|
|
||||||
if (flag === 2) {
|
|
||||||
throw new Error('Invalid tag')
|
|
||||||
}
|
|
||||||
if (tag === false) {
|
|
||||||
tag = 0
|
|
||||||
}
|
|
||||||
const bytes = Buffer.concat([
|
|
||||||
test ? PREFIX_BYTES.TEST : PREFIX_BYTES.MAIN,
|
|
||||||
accountId,
|
|
||||||
Buffer.from([
|
|
||||||
flag, // 0x00 if no tag, 0x01 if 32-bit tag
|
|
||||||
tag & 0xff, // first byte
|
|
||||||
(tag >> 8) & 0xff, // second byte
|
|
||||||
(tag >> 16) & 0xff, // third byte
|
|
||||||
(tag >> 24) & 0xff, // fourth byte
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0, // four zero bytes (reserved for 64-bit tags)
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
const xAddress = codec.encodeChecked(bytes)
|
|
||||||
return xAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
function xAddressToClassicAddress(xAddress: string): {
|
|
||||||
classicAddress: string
|
|
||||||
tag: number | false
|
|
||||||
test: boolean
|
|
||||||
} {
|
|
||||||
const { accountId, tag, test } = decodeXAddress(xAddress)
|
|
||||||
const classicAddress = encodeAccountID(accountId)
|
|
||||||
return {
|
|
||||||
classicAddress,
|
|
||||||
tag,
|
|
||||||
test,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeXAddress(xAddress: string): {
|
|
||||||
accountId: Buffer
|
|
||||||
tag: number | false
|
|
||||||
test: boolean
|
|
||||||
} {
|
|
||||||
const decoded = codec.decodeChecked(xAddress)
|
|
||||||
const test = isBufferForTestAddress(decoded)
|
|
||||||
const accountId = decoded.slice(2, 22)
|
|
||||||
const tag = tagFromBuffer(decoded)
|
|
||||||
return {
|
|
||||||
accountId,
|
|
||||||
tag,
|
|
||||||
test,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isBufferForTestAddress(buf: Buffer): boolean {
|
|
||||||
const decodedPrefix = buf.slice(0, 2)
|
|
||||||
if (PREFIX_BYTES.MAIN.equals(decodedPrefix)) {
|
|
||||||
return false
|
|
||||||
} else if (PREFIX_BYTES.TEST.equals(decodedPrefix)) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid X-address: bad prefix')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function tagFromBuffer(buf: Buffer): number | false {
|
|
||||||
const flag = buf[22]
|
|
||||||
if (flag >= 2) {
|
|
||||||
// No support for 64-bit tags at this time
|
|
||||||
throw new Error('Unsupported X-address')
|
|
||||||
}
|
|
||||||
if (flag === 1) {
|
|
||||||
// Little-endian to big-endian
|
|
||||||
return buf[23] + buf[24] * 0x100 + buf[25] * 0x10000 + buf[26] * 0x1000000
|
|
||||||
}
|
|
||||||
assert.strictEqual(flag, 0, 'flag must be zero to indicate no tag')
|
|
||||||
assert.ok(
|
|
||||||
Buffer.from('0000000000000000', 'hex').equals(buf.slice(23, 23 + 8)),
|
|
||||||
'remaining bytes must be zero',
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidXAddress(xAddress: string): boolean {
|
|
||||||
try {
|
|
||||||
decodeXAddress(xAddress)
|
|
||||||
} catch (e) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
codec, // Codec with XRP alphabet
|
|
||||||
encodeSeed, // Encode entropy as a "seed"
|
|
||||||
decodeSeed, // Decode a seed into an object with its version, type, and bytes
|
|
||||||
encodeAccountID, // Encode bytes as a classic address (r...)
|
|
||||||
decodeAccountID, // Decode a classic address to its raw bytes
|
|
||||||
encodeNodePublic, // Encode bytes to XRP Ledger node public key format
|
|
||||||
decodeNodePublic, // Decode an XRP Ledger node public key into its raw bytes
|
|
||||||
encodeAccountPublic, // Encode a public key, as for payment channels
|
|
||||||
decodeAccountPublic, // Decode a public key, as for payment channels
|
|
||||||
isValidClassicAddress, // Check whether a classic address (r...) is valid
|
|
||||||
classicAddressToXAddress, // Derive X-address from classic address, tag, and network ID
|
|
||||||
encodeXAddress, // Encode account ID, tag, and network ID to X-address
|
|
||||||
xAddressToClassicAddress, // Decode X-address to account ID, tag, and network ID
|
|
||||||
decodeXAddress, // Convert X-address to classic address, tag, and network ID
|
|
||||||
isValidXAddress, // Check whether an X-address (X...) is valid
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
const {seqEqual, concatArgs} = require('./utils')
|
|
||||||
|
|
||||||
test('two sequences are equal', () => {
|
|
||||||
expect(seqEqual([1, 2, 3], [1, 2, 3])).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('elements must be in the same order', () => {
|
|
||||||
expect(seqEqual([3, 2, 1], [1, 2, 3])).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('sequences do not need to be the same type', () => {
|
|
||||||
expect(seqEqual(Buffer.from([1, 2, 3]), [1, 2, 3])).toBe(true)
|
|
||||||
expect(seqEqual(Buffer.from([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('sequences with a single element', () => {
|
|
||||||
expect(seqEqual(Buffer.from([1]), [1])).toBe(true)
|
|
||||||
expect(seqEqual(Buffer.from([1]), new Uint8Array([1]))).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('empty sequences', () => {
|
|
||||||
expect(seqEqual(Buffer.from([]), [])).toBe(true)
|
|
||||||
expect(seqEqual(Buffer.from([]), new Uint8Array([]))).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('plain numbers are concatenated', () => {
|
|
||||||
expect(concatArgs(10, 20, 30, 40)).toStrictEqual([10, 20, 30, 40])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('a variety of values are concatenated', () => {
|
|
||||||
expect(concatArgs(1, [2, 3], Buffer.from([4,5]), new Uint8Array([6, 7]))).toStrictEqual([1,2,3,4,5,6,7])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('a single value is returned as an array', () => {
|
|
||||||
expect(concatArgs(Buffer.from([7]))).toStrictEqual([7])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('no arguments returns an empty array', () => {
|
|
||||||
expect(concatArgs()).toStrictEqual([])
|
|
||||||
})
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
type Sequence = number[] | Buffer | Uint8Array
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether two sequences (e.g. arrays of numbers) are equal.
|
|
||||||
*
|
|
||||||
* @param arr1 One of the arrays to compare.
|
|
||||||
* @param arr2 The other array to compare.
|
|
||||||
*/
|
|
||||||
export function seqEqual(arr1: Sequence, arr2: Sequence): boolean {
|
|
||||||
if (arr1.length !== arr2.length) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < arr1.length; i++) {
|
|
||||||
if (arr1[i] !== arr2[i]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether a value is a sequence (e.g. array of numbers).
|
|
||||||
*
|
|
||||||
* @param val The value to check.
|
|
||||||
*/
|
|
||||||
function isSequence(val: Sequence | number): val is Sequence {
|
|
||||||
return (val as Sequence).length !== undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Concatenate all `arguments` into a single array. Each argument can be either
|
|
||||||
* a single element or a sequence, which has a `length` property and supports
|
|
||||||
* element retrieval via sequence[ix].
|
|
||||||
*
|
|
||||||
* > concatArgs(1, [2, 3], Buffer.from([4,5]), new Uint8Array([6, 7]));
|
|
||||||
* [1,2,3,4,5,6,7]
|
|
||||||
*
|
|
||||||
* @returns {number[]} Array of concatenated arguments
|
|
||||||
*/
|
|
||||||
export function concatArgs(...args: (number | Sequence)[]): number[] {
|
|
||||||
const ret: number[] = []
|
|
||||||
|
|
||||||
args.forEach(function (arg) {
|
|
||||||
if (isSequence(arg)) {
|
|
||||||
for (let j = 0; j < arg.length; j++) {
|
|
||||||
ret.push(arg[j])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret.push(arg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
@@ -1,246 +0,0 @@
|
|||||||
const api = require('./xrp-codec')
|
|
||||||
|
|
||||||
function toHex(bytes) {
|
|
||||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
function toBytes(hex) {
|
|
||||||
return Buffer.from(hex, 'hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a test case for encoding data and a test case for decoding data.
|
|
||||||
*
|
|
||||||
* @param encoder Encoder function to test
|
|
||||||
* @param decoder Decoder function to test
|
|
||||||
* @param base58 Base58-encoded string to decode
|
|
||||||
* @param hex Hexadecimal representation of expected decoded data
|
|
||||||
*/
|
|
||||||
function makeEncodeDecodeTest(encoder, decoder, base58, hex) {
|
|
||||||
test(`can translate between ${hex} and ${base58}`, function() {
|
|
||||||
const actual = encoder(toBytes(hex))
|
|
||||||
expect(actual).toBe(base58)
|
|
||||||
})
|
|
||||||
test(`can translate between ${base58} and ${hex})`, function() {
|
|
||||||
const buf = decoder(base58)
|
|
||||||
expect(toHex(buf)).toBe(hex)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
makeEncodeDecodeTest(api.encodeAccountID, api.decodeAccountID, 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
|
||||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072')
|
|
||||||
|
|
||||||
makeEncodeDecodeTest(api.encodeNodePublic, api.decodeNodePublic,
|
|
||||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
|
||||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
|
||||||
|
|
||||||
makeEncodeDecodeTest(api.encodeAccountPublic, api.decodeAccountPublic,
|
|
||||||
'aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3',
|
|
||||||
'023693F15967AE357D0327974AD46FE3C127113B1110D6044FD41E723689F81CC6')
|
|
||||||
|
|
||||||
test('can decode arbitrary seeds', function() {
|
|
||||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
expect(toHex(decoded.bytes)).toBe('4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
expect(decoded.type).toBe('ed25519')
|
|
||||||
|
|
||||||
const decoded2 = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
expect(toHex(decoded2.bytes)).toBe('CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
|
||||||
expect(decoded2.type).toBe('secp256k1')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('can pass a type as second arg to encodeSeed', function() {
|
|
||||||
const edSeed = 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2'
|
|
||||||
const decoded = api.decodeSeed(edSeed)
|
|
||||||
const type = 'ed25519'
|
|
||||||
expect(toHex(decoded.bytes)).toBe('4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
expect(decoded.type).toBe(type)
|
|
||||||
expect(api.encodeSeed(decoded.bytes, type)).toBe(edSeed)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isValidClassicAddress - secp256k1 address valid', function() {
|
|
||||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw1')).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isValidClassicAddress - ed25519 address valid', function() {
|
|
||||||
expect(api.isValidClassicAddress('rLUEXYuLiQptky37CqLcm9USQpPiz5rkpD')).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isValidClassicAddress - invalid', function() {
|
|
||||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw2')).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isValidClassicAddress - empty', function() {
|
|
||||||
expect(api.isValidClassicAddress('')).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('encodeSeed', function() {
|
|
||||||
|
|
||||||
it('encodes a secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFF', 'hex'), 'secp256k1')
|
|
||||||
expect(result).toBe('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes low secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('00000000000000000000000000000000', 'hex'), 'secp256k1')
|
|
||||||
expect(result).toBe('sp6JS7f14BuwFY8Mw6bTtLKWauoUs')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes high secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'), 'secp256k1')
|
|
||||||
expect(result).toBe('saGwBRReqUNKuWNLpUAq8i8NkXEPN')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes an ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('4C3A1D213FBDFB14C7C28D609469B341', 'hex'), 'ed25519')
|
|
||||||
expect(result).toBe('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes low ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('00000000000000000000000000000000', 'hex'), 'ed25519')
|
|
||||||
expect(result).toBe('sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes high ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'), 'ed25519')
|
|
||||||
expect(result).toBe('sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('attempting to encode a seed with less than 16 bytes of entropy throws', function() {
|
|
||||||
expect(() => {
|
|
||||||
api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7B', 'hex'), 'secp256k1')
|
|
||||||
}).toThrow('entropy must have length 16')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('attempting to encode a seed with more than 16 bytes of entropy throws', function() {
|
|
||||||
expect(() => {
|
|
||||||
api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFFFF', 'hex'), 'secp256k1')
|
|
||||||
}).toThrow('entropy must have length 16')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('decodeSeed', function() {
|
|
||||||
|
|
||||||
it('can decode an Ed25519 seed', function() {
|
|
||||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
expect(toHex(decoded.bytes)).toBe('4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
expect(decoded.type).toBe('ed25519')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can decode a secp256k1 seed', function() {
|
|
||||||
const decoded = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
expect(toHex(decoded.bytes)).toBe('CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
|
||||||
expect(decoded.type).toBe('secp256k1')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('encodeAccountID', function() {
|
|
||||||
|
|
||||||
it('can encode an AccountID', function() {
|
|
||||||
const encoded = api.encodeAccountID(Buffer.from('BA8E78626EE42C41B46D46C3048DF3A1C3C87072', 'hex'))
|
|
||||||
expect(encoded).toBe('rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('unexpected length should throw', function() {
|
|
||||||
expect(() => {
|
|
||||||
api.encodeAccountID(Buffer.from('ABCDEF', 'hex'))
|
|
||||||
}).toThrow(
|
|
||||||
'unexpected_payload_length: bytes.length does not match expectedLength'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('decodeNodePublic', function() {
|
|
||||||
|
|
||||||
it('can decode a NodePublic', function() {
|
|
||||||
const decoded = api.decodeNodePublic('n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH')
|
|
||||||
expect(toHex(decoded)).toBe('0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('encodes 123456789 with version byte of 0', () => {
|
|
||||||
expect(api.codec.encode(Buffer.from('123456789'), {
|
|
||||||
versions: [0],
|
|
||||||
expectedLength: 9
|
|
||||||
})).toBe('rnaC7gW34M77Kneb78s')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('multiple versions with no expected length should throw', () => {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [0, 1]
|
|
||||||
})
|
|
||||||
}).toThrow('expectedLength is required because there are >= 2 possible versions')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('attempting to decode data with length < 5 should throw', () => {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('1234', {
|
|
||||||
versions: [0]
|
|
||||||
})
|
|
||||||
}).toThrow('invalid_input_size: decoded data must have length >= 5')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('attempting to decode data with unexpected version should throw', () => {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [2]
|
|
||||||
})
|
|
||||||
}).toThrow('version_invalid: version bytes do not match any of the provided version(s)')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('invalid checksum should throw', () => {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('123456789', {
|
|
||||||
versions: [0, 1]
|
|
||||||
})
|
|
||||||
}).toThrow('checksum_invalid')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('empty payload should throw', () => {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('', {
|
|
||||||
versions: [0, 1]
|
|
||||||
})
|
|
||||||
}).toThrow('invalid_input_size: decoded data must have length >= 5')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('decode data', () => {
|
|
||||||
expect(api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [0]
|
|
||||||
})).toStrictEqual({
|
|
||||||
version: [0],
|
|
||||||
bytes: Buffer.from('123456789'),
|
|
||||||
type: null
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('decode data with expected length', function() {
|
|
||||||
expect(api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [0],
|
|
||||||
expectedLength: 9
|
|
||||||
})
|
|
||||||
).toStrictEqual({
|
|
||||||
version: [0],
|
|
||||||
bytes: Buffer.from('123456789'),
|
|
||||||
type: null
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('decode data with wrong expected length should throw', function() {
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [0],
|
|
||||||
expectedLength: 8
|
|
||||||
})
|
|
||||||
}).toThrow(
|
|
||||||
'version_invalid: version bytes do not match any of the provided version(s)'
|
|
||||||
)
|
|
||||||
expect(() => {
|
|
||||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
|
||||||
versions: [0],
|
|
||||||
expectedLength: 10
|
|
||||||
})
|
|
||||||
}).toThrow(
|
|
||||||
'version_invalid: version bytes do not match any of the provided version(s)'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
@@ -1,241 +0,0 @@
|
|||||||
/**
|
|
||||||
* Codec class
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as baseCodec from 'base-x'
|
|
||||||
import { seqEqual, concatArgs } from './utils'
|
|
||||||
|
|
||||||
class Codec {
|
|
||||||
sha256: (bytes: Uint8Array) => Buffer
|
|
||||||
alphabet: string
|
|
||||||
codec: any
|
|
||||||
base: number
|
|
||||||
|
|
||||||
constructor(options: {
|
|
||||||
sha256: (bytes: Uint8Array) => Buffer
|
|
||||||
alphabet: string
|
|
||||||
}) {
|
|
||||||
this.sha256 = options.sha256
|
|
||||||
this.alphabet = options.alphabet
|
|
||||||
this.codec = baseCodec(this.alphabet)
|
|
||||||
this.base = this.alphabet.length
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encoder.
|
|
||||||
*
|
|
||||||
* @param bytes Buffer of data to encode.
|
|
||||||
* @param opts Options object including the version bytes and the expected length of the data to encode.
|
|
||||||
*/
|
|
||||||
encode(
|
|
||||||
bytes: Buffer,
|
|
||||||
opts: {
|
|
||||||
versions: number[]
|
|
||||||
expectedLength: number
|
|
||||||
},
|
|
||||||
): string {
|
|
||||||
const versions = opts.versions
|
|
||||||
return this.encodeVersioned(bytes, versions, opts.expectedLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeVersioned(
|
|
||||||
bytes: Buffer,
|
|
||||||
versions: number[],
|
|
||||||
expectedLength: number,
|
|
||||||
): string {
|
|
||||||
if (expectedLength && bytes.length !== expectedLength) {
|
|
||||||
throw new Error(
|
|
||||||
'unexpected_payload_length: bytes.length does not match expectedLength.' +
|
|
||||||
' Ensure that the bytes are a Buffer.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return this.encodeChecked(Buffer.from(concatArgs(versions, bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeChecked(buffer: Buffer): string {
|
|
||||||
const check = this.sha256(this.sha256(buffer)).slice(0, 4)
|
|
||||||
return this.encodeRaw(Buffer.from(concatArgs(buffer, check)))
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeRaw(bytes: Buffer): string {
|
|
||||||
return this.codec.encode(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decoder.
|
|
||||||
*
|
|
||||||
* @param base58string Base58Check-encoded string to decode.
|
|
||||||
* @param opts Options object including the version byte(s) and the expected length of the data after decoding.
|
|
||||||
*/
|
|
||||||
decode(
|
|
||||||
base58string: string,
|
|
||||||
opts: {
|
|
||||||
versions: (number | number[])[]
|
|
||||||
expectedLength?: number
|
|
||||||
versionTypes?: ['ed25519', 'secp256k1']
|
|
||||||
},
|
|
||||||
): {
|
|
||||||
version: number[]
|
|
||||||
bytes: Buffer
|
|
||||||
type: string | null
|
|
||||||
} {
|
|
||||||
const versions = opts.versions
|
|
||||||
const types = opts.versionTypes
|
|
||||||
|
|
||||||
const withoutSum = this.decodeChecked(base58string)
|
|
||||||
|
|
||||||
if (versions.length > 1 && !opts.expectedLength) {
|
|
||||||
throw new Error(
|
|
||||||
'expectedLength is required because there are >= 2 possible versions',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const versionLengthGuess =
|
|
||||||
typeof versions[0] === 'number' ? 1 : (versions[0] as number[]).length
|
|
||||||
const payloadLength =
|
|
||||||
opts.expectedLength || withoutSum.length - versionLengthGuess
|
|
||||||
const versionBytes = withoutSum.slice(0, -payloadLength)
|
|
||||||
const payload = withoutSum.slice(-payloadLength)
|
|
||||||
|
|
||||||
for (let i = 0; i < versions.length; i++) {
|
|
||||||
const version: number[] = Array.isArray(versions[i])
|
|
||||||
? (versions[i] as number[])
|
|
||||||
: [versions[i] as number]
|
|
||||||
if (seqEqual(versionBytes, version)) {
|
|
||||||
return {
|
|
||||||
version,
|
|
||||||
bytes: payload,
|
|
||||||
type: types ? types[i] : null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
'version_invalid: version bytes do not match any of the provided version(s)',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeChecked(base58string: string): Buffer {
|
|
||||||
const buffer = this.decodeRaw(base58string)
|
|
||||||
if (buffer.length < 5) {
|
|
||||||
throw new Error('invalid_input_size: decoded data must have length >= 5')
|
|
||||||
}
|
|
||||||
if (!this.verifyCheckSum(buffer)) {
|
|
||||||
throw new Error('checksum_invalid')
|
|
||||||
}
|
|
||||||
return buffer.slice(0, -4)
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeRaw(base58string: string): Buffer {
|
|
||||||
return this.codec.decode(base58string)
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyCheckSum(bytes: Buffer): boolean {
|
|
||||||
const computed = this.sha256(this.sha256(bytes.slice(0, -4))).slice(0, 4)
|
|
||||||
const checksum = bytes.slice(-4)
|
|
||||||
return seqEqual(computed, checksum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* XRP codec
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Pure JavaScript hash functions in the browser, native hash functions in Node.js
|
|
||||||
const createHash = require('create-hash')
|
|
||||||
|
|
||||||
// base58 encodings: https://xrpl.org/base58-encodings.html
|
|
||||||
const ACCOUNT_ID = 0 // Account address (20 bytes)
|
|
||||||
const ACCOUNT_PUBLIC_KEY = 0x23 // Account public key (33 bytes)
|
|
||||||
const FAMILY_SEED = 0x21 // 33; Seed value (for secret keys) (16 bytes)
|
|
||||||
const NODE_PUBLIC = 0x1c // 28; Validation public key (33 bytes)
|
|
||||||
|
|
||||||
const ED25519_SEED = [0x01, 0xe1, 0x4b] // [1, 225, 75]
|
|
||||||
|
|
||||||
const codecOptions = {
|
|
||||||
sha256: function (bytes: Uint8Array) {
|
|
||||||
return createHash('sha256').update(Buffer.from(bytes)).digest()
|
|
||||||
},
|
|
||||||
alphabet: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
|
||||||
}
|
|
||||||
|
|
||||||
const codecWithXrpAlphabet = new Codec(codecOptions)
|
|
||||||
|
|
||||||
export const codec = codecWithXrpAlphabet
|
|
||||||
|
|
||||||
// entropy is a Buffer of size 16
|
|
||||||
// type is 'ed25519' or 'secp256k1'
|
|
||||||
export function encodeSeed(
|
|
||||||
entropy: Buffer,
|
|
||||||
type: 'ed25519' | 'secp256k1',
|
|
||||||
): string {
|
|
||||||
if (entropy.length !== 16) {
|
|
||||||
throw new Error('entropy must have length 16')
|
|
||||||
}
|
|
||||||
const opts = {
|
|
||||||
expectedLength: 16,
|
|
||||||
|
|
||||||
// for secp256k1, use `FAMILY_SEED`
|
|
||||||
versions: type === 'ed25519' ? ED25519_SEED : [FAMILY_SEED],
|
|
||||||
}
|
|
||||||
|
|
||||||
// prefixes entropy with version bytes
|
|
||||||
return codecWithXrpAlphabet.encode(entropy, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decodeSeed(
|
|
||||||
seed: string,
|
|
||||||
opts: {
|
|
||||||
versionTypes: ['ed25519', 'secp256k1']
|
|
||||||
versions: (number | number[])[]
|
|
||||||
expectedLength: number
|
|
||||||
} = {
|
|
||||||
versionTypes: ['ed25519', 'secp256k1'],
|
|
||||||
versions: [ED25519_SEED, FAMILY_SEED],
|
|
||||||
expectedLength: 16,
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
return codecWithXrpAlphabet.decode(seed, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encodeAccountID(bytes: Buffer): string {
|
|
||||||
const opts = { versions: [ACCOUNT_ID], expectedLength: 20 }
|
|
||||||
return codecWithXrpAlphabet.encode(bytes, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const encodeAddress = encodeAccountID
|
|
||||||
|
|
||||||
export function decodeAccountID(accountId: string): Buffer {
|
|
||||||
const opts = { versions: [ACCOUNT_ID], expectedLength: 20 }
|
|
||||||
return codecWithXrpAlphabet.decode(accountId, opts).bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
export const decodeAddress = decodeAccountID
|
|
||||||
|
|
||||||
export function decodeNodePublic(base58string: string): Buffer {
|
|
||||||
const opts = { versions: [NODE_PUBLIC], expectedLength: 33 }
|
|
||||||
return codecWithXrpAlphabet.decode(base58string, opts).bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encodeNodePublic(bytes: Buffer): string {
|
|
||||||
const opts = { versions: [NODE_PUBLIC], expectedLength: 33 }
|
|
||||||
return codecWithXrpAlphabet.encode(bytes, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encodeAccountPublic(bytes: Buffer): string {
|
|
||||||
const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 }
|
|
||||||
return codecWithXrpAlphabet.encode(bytes, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decodeAccountPublic(base58string: string): Buffer {
|
|
||||||
const opts = { versions: [ACCOUNT_PUBLIC_KEY], expectedLength: 33 }
|
|
||||||
return codecWithXrpAlphabet.decode(base58string, opts).bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isValidClassicAddress(address: string): boolean {
|
|
||||||
try {
|
|
||||||
decodeAccountID(address)
|
|
||||||
} catch (e) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
64
packages/ripple-binary-codec/.gitignore
vendored
64
packages/ripple-binary-codec/.gitignore
vendored
@@ -1,64 +0,0 @@
|
|||||||
# .gitignore
|
|
||||||
|
|
||||||
# Ignore vim swap files.
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
# Ignore SCons support files.
|
|
||||||
.sconsign.dblite
|
|
||||||
|
|
||||||
# Ignore python compiled files.
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Ignore Macintosh Desktop Services Store files.
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ignore backup/temps
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Ignore object files.
|
|
||||||
*.o
|
|
||||||
build/
|
|
||||||
distrib/
|
|
||||||
tags
|
|
||||||
bin/rippled
|
|
||||||
Debug/*.*
|
|
||||||
Release/*.*
|
|
||||||
|
|
||||||
# Ignore locally installed node_modules
|
|
||||||
node_modules
|
|
||||||
!test/node_modules
|
|
||||||
|
|
||||||
# Ignore tmp directory.
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# Ignore database directory.
|
|
||||||
db/*.db
|
|
||||||
db/*.db-*
|
|
||||||
|
|
||||||
# Ignore customized configs
|
|
||||||
rippled.cfg
|
|
||||||
validators.txt
|
|
||||||
test/config.js
|
|
||||||
|
|
||||||
# Ignore coverage files
|
|
||||||
/lib-cov
|
|
||||||
/src-cov
|
|
||||||
/coverage.html
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# Ignore IntelliJ files
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Ignore npm-debug
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# Ignore dist folder, build for bower
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# Ignore flow output directory
|
|
||||||
out/
|
|
||||||
|
|
||||||
# Ignore perf test cache
|
|
||||||
scripts/cache
|
|
||||||
|
|
||||||
eslintrc
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
10.22.0
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# ripple-binary-codec Release History
|
|
||||||
|
|
||||||
## 1.1.3 (2021-06-11)
|
|
||||||
- Fix for case UInt64.from string allowing lowercase hex (#135)
|
|
||||||
- Fix for `ValidatorToReEnable` field code (#130)
|
|
||||||
|
|
||||||
## 1.1.2 (2021-03-10)
|
|
||||||
- Fix for case UInt64.from string '0' due to changes in rippled 1.7.0
|
|
||||||
|
|
||||||
## 1.1.1 (2021-02-12)
|
|
||||||
- PathSet.toJSON() does not return undefined values
|
|
||||||
- Add support for X-Addresses in Issued Currency Amounts
|
|
||||||
- Fix STArray error message
|
|
||||||
|
|
||||||
## 1.1.0 (2020-12-03)
|
|
||||||
- Add support for Tickets (TicketBatch amendment)
|
|
||||||
- Fix web browser compatibility
|
|
||||||
|
|
||||||
## 1.0.2 (2020-09-11)
|
|
||||||
- Allow currencies to be encoded from any 3 character ASCII code
|
|
||||||
|
|
||||||
## 1.0.1 (2020-09-08)
|
|
||||||
- Filter out fields with undefined values
|
|
||||||
|
|
||||||
## 1.0.0 (2020-08-17)
|
|
||||||
|
|
||||||
- Migrate to TypeScript
|
|
||||||
- Javascript classes used
|
|
||||||
- Generics for constructing core types
|
|
||||||
- Reduced dependencies
|
|
||||||
- Dependent on create-hash, decimal.js, ripple-address-codec
|
|
||||||
- Migrate testing to Jest and added tests
|
|
||||||
- Tests for pseudo-transactions
|
|
||||||
- Added support for NegativeUNL pseudo-transactions
|
|
||||||
|
|
||||||
## 0.2.6 (2019-12-31)
|
|
||||||
|
|
||||||
- Update dependencies
|
|
||||||
- decimal.js, fs-extra, mocha, handlebars, bn.js, babel-eslint, ripple-address-codec
|
|
||||||
|
|
||||||
## 0.2.5 (2019-12-14)
|
|
||||||
|
|
||||||
- Add support for AccountDelete (#37)
|
|
||||||
|
|
||||||
## 0.2.4 (2019-09-04)
|
|
||||||
|
|
||||||
- Update ripple-address-codec to 3.0.4
|
|
||||||
|
|
||||||
## 0.2.3 (2019-08-29)
|
|
||||||
|
|
||||||
- Expand node version compatibility (#32, #33)
|
|
||||||
|
|
||||||
## 0.2.2 (2019-07-26)
|
|
||||||
|
|
||||||
- Input validation - Amount and Fee should not allow fractional XRP drops ([#31](https://github.com/ripple/ripple-binary-codec/issues/31))
|
|
||||||
- Fix lint errors
|
|
||||||
- Update dependencies (including lodash and mocha)
|
|
||||||
- Require node 10 (.nvmrc)
|
|
||||||
- Remove assert-diff
|
|
||||||
- Remove codecov.io as it did not appear to work. The `package.json` script was:
|
|
||||||
- `"codecov": "cat ./coverage/coverage.json | ./node_modules/codecov.io/bin/codecov.io.js"`
|
|
||||||
|
|
||||||
## 0.2.1
|
|
||||||
|
|
||||||
- Add tecKILLED from amendment fix1578 (PR #27 fixes #25)
|
|
||||||
|
|
||||||
## 0.2.0
|
|
||||||
|
|
||||||
- Add DepositPreauth fields
|
|
||||||
- https://developers.ripple.com/depositauth.html
|
|
||||||
|
|
||||||
## 0.1.14
|
|
||||||
|
|
||||||
- Skip amount validation when deserializing f72c115
|
|
||||||
|
|
||||||
## 0.1.13
|
|
||||||
|
|
||||||
- Add Check, CheckCreate, CheckCash, CheckCancel
|
|
||||||
|
|
||||||
## 0.1.11
|
|
||||||
|
|
||||||
- Add ledger header decode function
|
|
||||||
|
|
||||||
## 0.1.8
|
|
||||||
|
|
||||||
## 0.1.7
|
|
||||||
|
|
||||||
## 0.1.6
|
|
||||||
|
|
||||||
## 0.1.3
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ripple-binary-codec",
|
|
||||||
"version": "1.2.0",
|
|
||||||
"description": "XRP Ledger binary codec",
|
|
||||||
"files": [
|
|
||||||
"dist/*",
|
|
||||||
"bin/*",
|
|
||||||
"test/*"
|
|
||||||
],
|
|
||||||
"main": "dist/",
|
|
||||||
"directories": {
|
|
||||||
"test": "test"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"assert": "^2.0.0",
|
|
||||||
"big-integer": "^1.6.48",
|
|
||||||
"buffer": "5.6.0",
|
|
||||||
"create-hash": "^1.2.0",
|
|
||||||
"decimal.js": "^10.2.0",
|
|
||||||
"ripple-address-codec": "^4.2.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc -b && cp ./src/enums/definitions.json ./dist/enums",
|
|
||||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo",
|
|
||||||
"prepare": "npm run build && npm test",
|
|
||||||
"test": "jest",
|
|
||||||
"lint": "eslint . --ext .ts --ext .test.js"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@github.com:XRPLF/xrpl.js.git"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/XRPLF/xrpl.js/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/XRPLF/xrpl.js/packages/ripple-binary-codec#readme",
|
|
||||||
"license": "ISC",
|
|
||||||
"readmeFilename": "README.md",
|
|
||||||
"prettier": "@xrplf/prettier-config",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.22.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# ripple-binary-codec
|
|
||||||
|
|
||||||
Serialize and deserialize transactions according to the XRP Ledger protocol.
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/* eslint-disable func-style */
|
|
||||||
|
|
||||||
import { coreTypes } from './types'
|
|
||||||
import { BinaryParser } from './serdes/binary-parser'
|
|
||||||
import { AccountID } from './types/account-id'
|
|
||||||
import { HashPrefix } from './hash-prefixes'
|
|
||||||
import { BinarySerializer, BytesList } from './serdes/binary-serializer'
|
|
||||||
import { sha512Half, transactionID } from './hashes'
|
|
||||||
import { FieldInstance } from './enums'
|
|
||||||
import { STObject } from './types/st-object'
|
|
||||||
import { JsonObject } from './types/serialized-type'
|
|
||||||
import { Buffer } from 'buffer/'
|
|
||||||
import * as bigInt from 'big-integer'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a BinaryParser
|
|
||||||
*
|
|
||||||
* @param bytes hex-string to construct BinaryParser from
|
|
||||||
* @returns A BinaryParser
|
|
||||||
*/
|
|
||||||
const makeParser = (bytes: string): BinaryParser => new BinaryParser(bytes)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse BinaryParser into JSON
|
|
||||||
*
|
|
||||||
* @param parser BinaryParser object
|
|
||||||
* @returns JSON for the bytes in the BinaryParser
|
|
||||||
*/
|
|
||||||
const readJSON = (parser: BinaryParser): JsonObject =>
|
|
||||||
(parser.readType(coreTypes.STObject) as STObject).toJSON()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a hex-string into its JSON interpretation
|
|
||||||
*
|
|
||||||
* @param bytes hex-string to parse into JSON
|
|
||||||
* @returns JSON
|
|
||||||
*/
|
|
||||||
const binaryToJSON = (bytes: string): JsonObject => readJSON(makeParser(bytes))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for passing parameters to SerializeObject
|
|
||||||
*
|
|
||||||
* @field set signingFieldOnly to true if you want to serialize only signing fields
|
|
||||||
*/
|
|
||||||
interface OptionObject {
|
|
||||||
prefix?: Buffer
|
|
||||||
suffix?: Buffer
|
|
||||||
signingFieldsOnly?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to serialize JSON object representing a transaction
|
|
||||||
*
|
|
||||||
* @param object JSON object to serialize
|
|
||||||
* @param opts options for serializing, including optional prefix, suffix, and signingFieldOnly
|
|
||||||
* @returns A Buffer containing the serialized object
|
|
||||||
*/
|
|
||||||
function serializeObject(object: JsonObject, opts: OptionObject = {}): Buffer {
|
|
||||||
const { prefix, suffix, signingFieldsOnly = false } = opts
|
|
||||||
const bytesList = new BytesList()
|
|
||||||
|
|
||||||
if (prefix) {
|
|
||||||
bytesList.put(prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
const filter = signingFieldsOnly
|
|
||||||
? (f: FieldInstance): boolean => f.isSigningField
|
|
||||||
: undefined
|
|
||||||
coreTypes.STObject.from(object, filter).toBytesSink(bytesList)
|
|
||||||
|
|
||||||
if (suffix) {
|
|
||||||
bytesList.put(suffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesList.toBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize an object for signing
|
|
||||||
*
|
|
||||||
* @param transaction Transaction to serialize
|
|
||||||
* @param prefix Prefix bytes to put before the serialized object
|
|
||||||
* @returns A Buffer with the serialized object
|
|
||||||
*/
|
|
||||||
function signingData(
|
|
||||||
transaction: JsonObject,
|
|
||||||
prefix: Buffer = HashPrefix.transactionSig,
|
|
||||||
): Buffer {
|
|
||||||
return serializeObject(transaction, { prefix, signingFieldsOnly: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface describing fields required for a Claim
|
|
||||||
*/
|
|
||||||
interface ClaimObject extends JsonObject {
|
|
||||||
channel: string
|
|
||||||
amount: string | number
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a signingClaim
|
|
||||||
*
|
|
||||||
* @param claim A claim object to serialize
|
|
||||||
* @returns the serialized object with appropriate prefix
|
|
||||||
*/
|
|
||||||
function signingClaimData(claim: ClaimObject): Buffer {
|
|
||||||
const num = bigInt(String(claim.amount))
|
|
||||||
const prefix = HashPrefix.paymentChannelClaim
|
|
||||||
const channel = coreTypes.Hash256.from(claim.channel).toBytes()
|
|
||||||
const amount = coreTypes.UInt64.from(num).toBytes()
|
|
||||||
|
|
||||||
const bytesList = new BytesList()
|
|
||||||
|
|
||||||
bytesList.put(prefix)
|
|
||||||
bytesList.put(channel)
|
|
||||||
bytesList.put(amount)
|
|
||||||
return bytesList.toBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a transaction object for multiSigning
|
|
||||||
*
|
|
||||||
* @param transaction transaction to serialize
|
|
||||||
* @param signingAccount Account to sign the transaction with
|
|
||||||
* @returns serialized transaction with appropriate prefix and suffix
|
|
||||||
*/
|
|
||||||
function multiSigningData(
|
|
||||||
transaction: JsonObject,
|
|
||||||
signingAccount: string | AccountID,
|
|
||||||
): Buffer {
|
|
||||||
const prefix = HashPrefix.transactionMultiSig
|
|
||||||
const suffix = coreTypes.AccountID.from(signingAccount).toBytes()
|
|
||||||
return serializeObject(transaction, {
|
|
||||||
prefix,
|
|
||||||
suffix,
|
|
||||||
signingFieldsOnly: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
BinaryParser,
|
|
||||||
BinarySerializer,
|
|
||||||
BytesList,
|
|
||||||
ClaimObject,
|
|
||||||
makeParser,
|
|
||||||
serializeObject,
|
|
||||||
readJSON,
|
|
||||||
multiSigningData,
|
|
||||||
signingData,
|
|
||||||
signingClaimData,
|
|
||||||
binaryToJSON,
|
|
||||||
sha512Half,
|
|
||||||
transactionID,
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# Definitions
|
|
||||||
|
|
||||||
## Types
|
|
||||||
|
|
||||||
These are the [types](https://xrpl.org/serialization.html#type-list) associated with a given Serialization Field. Each type has an arbitrary [type_code](https://xrpl.org/serialization.html#type-codes), with lower codes sorting first.
|
|
||||||
|
|
||||||
## Ledger Entry Types
|
|
||||||
|
|
||||||
Each ledger's state tree contain [ledger objects](https://xrpl.org/ledger-object-types.html), which represent all settings, balances, and relationships in the shared ledger.
|
|
||||||
|
|
||||||
## Fields
|
|
||||||
|
|
||||||
These are Serialization Fields (`sf`) [defined in rippled's SField.cpp](https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/impl/SField.cpp). Fields with undefined values are omitted before encoding.
|
|
||||||
|
|
||||||
### Key
|
|
||||||
|
|
||||||
The key is the string defined in the rippled source code, such as "LedgerEntry", "Transaction", etc.
|
|
||||||
|
|
||||||
### nth
|
|
||||||
|
|
||||||
`nth` is the sort code, meaning "nth of type." It is is combined with the type code in order to construct the Field ID of this field. The Field ID is only used for sorting the fields. Since there are multiple fields with the same data type, the `nth` is used to deterministically order each field among other fields of the same data type.
|
|
||||||
|
|
||||||
Each field has a Field ID, which is used to sort fields that have the same type as one another with lower codes sorting first.
|
|
||||||
|
|
||||||
- [Field definitions](https://github.com/ripple/rippled/blob/72e6005f562a8f0818bc94803d222ac9345e1e40/src/ripple/protocol/impl/SField.cpp#L72-L266)
|
|
||||||
- [Constructing the `SField` field codes](https://github.com/ripple/rippled/blob/eaff9a0e6aec0ad077f118501791c7684debcfd5/src/ripple/protocol/SField.h#L95-L98)
|
|
||||||
|
|
||||||
For example, the `Account` field has sort code (nth) `1`, so it comes before the `Destination` field which has sort code `3`.
|
|
||||||
|
|
||||||
Sort code numbers are reused for fields of different types, but different fields of the same type never have the same sort code. When you combine the type code with the sort code, you get the field's unique _Field ID_.
|
|
||||||
|
|
||||||
The unique [Field ID](https://xrpl.org/serialization.html#field-ids) is prefixed before the field in the final serialized blob. The size of the Field ID is one to three bytes depending on the type code and the field codes it combines.
|
|
||||||
|
|
||||||
### isVLEncoded
|
|
||||||
|
|
||||||
If true, the field is Variable Length encoded and [length-prefixed](https://xrpl.org/serialization.html#length-prefixing). The variable-length encoded fields are `STI_VL`/`Blob`, `STI_ACCOUNT`/`AccountID`, and `STI_VECTOR256`/`Vector256`.
|
|
||||||
|
|
||||||
### isSerialized
|
|
||||||
|
|
||||||
Fields are serialized if they are not [one of these](https://github.com/ripple/rippled/blob/eaff9a0e6aec0ad077f118501791c7684debcfd5/src/ripple/protocol/impl/SField.cpp#L71-L78) or if they are not an SField.
|
|
||||||
|
|
||||||
- https://github.com/ripple/ripple-binary-codec/blob/14e76e68ead7e4bcd83c942dbdc9064d5a66869b/src/enums/definitions.json#L832
|
|
||||||
- https://github.com/ripple/rippled/search?utf8=%E2%9C%93&q=taker_gets_funded&type=
|
|
||||||
|
|
||||||
### isSigningField
|
|
||||||
|
|
||||||
True unless the field is [specified with `SField::notSigning`](https://github.com/ripple/rippled/blob/eaff9a0e6aec0ad077f118501791c7684debcfd5/src/ripple/protocol/impl/SField.cpp#L198).
|
|
||||||
|
|
||||||
## Transaction Results
|
|
||||||
|
|
||||||
See:
|
|
||||||
|
|
||||||
- https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/TER.h
|
|
||||||
- https://xrpl.org/transaction-results.html
|
|
||||||
|
|
||||||
TODO: Write a script to read rippled's source file and generate the necessary mapping.
|
|
||||||
|
|
||||||
## Transaction Types
|
|
||||||
|
|
||||||
See https://github.com/ripple/rippled/blob/develop/src/ripple/protocol/TxFormats.h
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import * as enums from './definitions.json'
|
|
||||||
import { SerializedType } from '../types/serialized-type'
|
|
||||||
import { Buffer } from 'buffer/'
|
|
||||||
|
|
||||||
const TYPE_WIDTH = 2
|
|
||||||
const LEDGER_ENTRY_WIDTH = 2
|
|
||||||
const TRANSACTION_TYPE_WIDTH = 2
|
|
||||||
const TRANSACTION_RESULT_WIDTH = 1
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief: Serialize a field based on type_code and Field.nth
|
|
||||||
*/
|
|
||||||
function fieldHeader(type: number, nth: number): Buffer {
|
|
||||||
const header: Array<number> = []
|
|
||||||
if (type < 16) {
|
|
||||||
if (nth < 16) {
|
|
||||||
header.push((type << 4) | nth)
|
|
||||||
} else {
|
|
||||||
header.push(type << 4, nth)
|
|
||||||
}
|
|
||||||
} else if (nth < 16) {
|
|
||||||
header.push(nth, type)
|
|
||||||
} else {
|
|
||||||
header.push(0, type, nth)
|
|
||||||
}
|
|
||||||
return Buffer.from(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief: Bytes, name, and ordinal representing one type, ledger_type, transaction type, or result
|
|
||||||
*/
|
|
||||||
class Bytes {
|
|
||||||
readonly bytes: Uint8Array
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly name: string,
|
|
||||||
readonly ordinal: number,
|
|
||||||
readonly ordinalWidth: number,
|
|
||||||
) {
|
|
||||||
this.bytes = Buffer.alloc(ordinalWidth)
|
|
||||||
for (let i = 0; i < ordinalWidth; i++) {
|
|
||||||
this.bytes[ordinalWidth - i - 1] = (ordinal >>> (i * 8)) & 0xff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): string {
|
|
||||||
return this.name
|
|
||||||
}
|
|
||||||
|
|
||||||
toBytesSink(sink): void {
|
|
||||||
sink.put(this.bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
|
||||||
return this.bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief: Collection of Bytes objects, mapping bidirectionally
|
|
||||||
*/
|
|
||||||
class BytesLookup {
|
|
||||||
constructor(types: Record<string, number>, readonly ordinalWidth: number) {
|
|
||||||
Object.entries(types).forEach(([k, v]) => {
|
|
||||||
this[k] = new Bytes(k, v, ordinalWidth)
|
|
||||||
this[v.toString()] = this[k]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
from(value: Bytes | string): Bytes {
|
|
||||||
return value instanceof Bytes ? value : (this[value] as Bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fromParser(parser): Bytes {
|
|
||||||
return this.from(parser.readUIntN(this.ordinalWidth).toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* type FieldInfo is the type of the objects containing information about each field in definitions.json
|
|
||||||
*/
|
|
||||||
interface FieldInfo {
|
|
||||||
nth: number
|
|
||||||
isVLEncoded: boolean
|
|
||||||
isSerialized: boolean
|
|
||||||
isSigningField: boolean
|
|
||||||
type: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldInstance {
|
|
||||||
readonly nth: number
|
|
||||||
readonly isVariableLengthEncoded: boolean
|
|
||||||
readonly isSerialized: boolean
|
|
||||||
readonly isSigningField: boolean
|
|
||||||
readonly type: Bytes
|
|
||||||
readonly ordinal: number
|
|
||||||
readonly name: string
|
|
||||||
readonly header: Buffer
|
|
||||||
readonly associatedType: typeof SerializedType
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildField([name, info]: [string, FieldInfo]): FieldInstance {
|
|
||||||
const typeOrdinal = enums.TYPES[info.type]
|
|
||||||
const field = fieldHeader(typeOrdinal, info.nth)
|
|
||||||
return {
|
|
||||||
name: name,
|
|
||||||
nth: info.nth,
|
|
||||||
isVariableLengthEncoded: info.isVLEncoded,
|
|
||||||
isSerialized: info.isSerialized,
|
|
||||||
isSigningField: info.isSigningField,
|
|
||||||
ordinal: (typeOrdinal << 16) | info.nth,
|
|
||||||
type: new Bytes(info.type, typeOrdinal, TYPE_WIDTH),
|
|
||||||
header: field,
|
|
||||||
associatedType: SerializedType, // For later assignment in ./types/index.js
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @brief: The collection of all fields as defined in definitions.json
|
|
||||||
*/
|
|
||||||
class FieldLookup {
|
|
||||||
constructor(fields: Array<[string, FieldInfo]>) {
|
|
||||||
fields.forEach(([k, v]) => {
|
|
||||||
this[k] = buildField([k, v])
|
|
||||||
this[this[k].ordinal.toString()] = this[k]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fromString(value: string): FieldInstance {
|
|
||||||
return this[value] as FieldInstance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type = new BytesLookup(enums.TYPES, TYPE_WIDTH)
|
|
||||||
const LedgerEntryType = new BytesLookup(
|
|
||||||
enums.LEDGER_ENTRY_TYPES,
|
|
||||||
LEDGER_ENTRY_WIDTH,
|
|
||||||
)
|
|
||||||
const TransactionType = new BytesLookup(
|
|
||||||
enums.TRANSACTION_TYPES,
|
|
||||||
TRANSACTION_TYPE_WIDTH,
|
|
||||||
)
|
|
||||||
const TransactionResult = new BytesLookup(
|
|
||||||
enums.TRANSACTION_RESULTS,
|
|
||||||
TRANSACTION_RESULT_WIDTH,
|
|
||||||
)
|
|
||||||
const Field = new FieldLookup(enums.FIELDS as Array<[string, FieldInfo]>)
|
|
||||||
|
|
||||||
export {
|
|
||||||
Field,
|
|
||||||
FieldInstance,
|
|
||||||
Type,
|
|
||||||
LedgerEntryType,
|
|
||||||
TransactionResult,
|
|
||||||
TransactionType,
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
import * as assert from 'assert'
|
|
||||||
import { quality, binary } from './coretypes'
|
|
||||||
import { decodeLedgerData } from './ledger-hashes'
|
|
||||||
import { ClaimObject } from './binary'
|
|
||||||
import { JsonObject } from './types/serialized-type'
|
|
||||||
const {
|
|
||||||
signingData,
|
|
||||||
signingClaimData,
|
|
||||||
multiSigningData,
|
|
||||||
binaryToJSON,
|
|
||||||
serializeObject,
|
|
||||||
} = binary
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a transaction
|
|
||||||
*
|
|
||||||
* @param binary hex-string of the encoded transaction
|
|
||||||
* @returns the JSON representation of the transaction
|
|
||||||
*/
|
|
||||||
function decode(binary: string): JsonObject {
|
|
||||||
assert.ok(typeof binary === 'string', 'binary must be a hex string')
|
|
||||||
return binaryToJSON(binary)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a transaction
|
|
||||||
*
|
|
||||||
* @param json The JSON representation of a transaction
|
|
||||||
* @returns A hex-string of the encoded transaction
|
|
||||||
*/
|
|
||||||
function encode(json: object): string {
|
|
||||||
assert.ok(typeof json === 'object')
|
|
||||||
return serializeObject(json as JsonObject)
|
|
||||||
.toString('hex')
|
|
||||||
.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a transaction and prepare for signing
|
|
||||||
*
|
|
||||||
* @param json JSON object representing the transaction
|
|
||||||
* @param signer string representing the account to sign the transaction with
|
|
||||||
* @returns a hex string of the encoded transaction
|
|
||||||
*/
|
|
||||||
function encodeForSigning(json: object): string {
|
|
||||||
assert.ok(typeof json === 'object')
|
|
||||||
return signingData(json as JsonObject)
|
|
||||||
.toString('hex')
|
|
||||||
.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a transaction and prepare for signing with a claim
|
|
||||||
*
|
|
||||||
* @param json JSON object representing the transaction
|
|
||||||
* @param signer string representing the account to sign the transaction with
|
|
||||||
* @returns a hex string of the encoded transaction
|
|
||||||
*/
|
|
||||||
function encodeForSigningClaim(json: object): string {
|
|
||||||
assert.ok(typeof json === 'object')
|
|
||||||
return signingClaimData(json as ClaimObject)
|
|
||||||
.toString('hex')
|
|
||||||
.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a transaction and prepare for multi-signing
|
|
||||||
*
|
|
||||||
* @param json JSON object representing the transaction
|
|
||||||
* @param signer string representing the account to sign the transaction with
|
|
||||||
* @returns a hex string of the encoded transaction
|
|
||||||
*/
|
|
||||||
function encodeForMultisigning(json: object, signer: string): string {
|
|
||||||
assert.ok(typeof json === 'object')
|
|
||||||
assert.equal(json['SigningPubKey'], '')
|
|
||||||
return multiSigningData(json as JsonObject, signer)
|
|
||||||
.toString('hex')
|
|
||||||
.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a quality value
|
|
||||||
*
|
|
||||||
* @param value string representation of a number
|
|
||||||
* @returns a hex-string representing the quality
|
|
||||||
*/
|
|
||||||
function encodeQuality(value: string): string {
|
|
||||||
assert.ok(typeof value === 'string')
|
|
||||||
return quality.encode(value).toString('hex').toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a quality value
|
|
||||||
*
|
|
||||||
* @param value hex-string of a quality
|
|
||||||
* @returns a string representing the quality
|
|
||||||
*/
|
|
||||||
function decodeQuality(value: string): string {
|
|
||||||
assert.ok(typeof value === 'string')
|
|
||||||
return quality.decode(value).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
export = {
|
|
||||||
decode,
|
|
||||||
encode,
|
|
||||||
encodeForSigning,
|
|
||||||
encodeForSigningClaim,
|
|
||||||
encodeForMultisigning,
|
|
||||||
encodeQuality,
|
|
||||||
decodeQuality,
|
|
||||||
decodeLedgerData,
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Hash } from './hash'
|
|
||||||
import { Buffer } from 'buffer/'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash with a width of 128 bits
|
|
||||||
*/
|
|
||||||
class Hash128 extends Hash {
|
|
||||||
static readonly width = 16
|
|
||||||
static readonly ZERO_128: Hash128 = new Hash128(Buffer.alloc(Hash128.width))
|
|
||||||
|
|
||||||
constructor(bytes: Buffer) {
|
|
||||||
super(bytes ?? Hash128.ZERO_128.bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Hash128 }
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import {
|
|
||||||
Field,
|
|
||||||
TransactionResult,
|
|
||||||
TransactionType,
|
|
||||||
LedgerEntryType,
|
|
||||||
} from '../enums'
|
|
||||||
import { AccountID } from './account-id'
|
|
||||||
import { Amount } from './amount'
|
|
||||||
import { Blob } from './blob'
|
|
||||||
import { Currency } from './currency'
|
|
||||||
import { Hash128 } from './hash-128'
|
|
||||||
import { Hash160 } from './hash-160'
|
|
||||||
import { Hash256 } from './hash-256'
|
|
||||||
import { PathSet } from './path-set'
|
|
||||||
import { STArray } from './st-array'
|
|
||||||
import { STObject } from './st-object'
|
|
||||||
import { UInt16 } from './uint-16'
|
|
||||||
import { UInt32 } from './uint-32'
|
|
||||||
import { UInt64 } from './uint-64'
|
|
||||||
import { UInt8 } from './uint-8'
|
|
||||||
import { Vector256 } from './vector-256'
|
|
||||||
|
|
||||||
const coreTypes = {
|
|
||||||
AccountID,
|
|
||||||
Amount,
|
|
||||||
Blob,
|
|
||||||
Currency,
|
|
||||||
Hash128,
|
|
||||||
Hash160,
|
|
||||||
Hash256,
|
|
||||||
PathSet,
|
|
||||||
STArray,
|
|
||||||
STObject,
|
|
||||||
UInt8,
|
|
||||||
UInt16,
|
|
||||||
UInt32,
|
|
||||||
UInt64,
|
|
||||||
Vector256,
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.values(Field).forEach((field) => {
|
|
||||||
field.associatedType = coreTypes[field.type.name]
|
|
||||||
})
|
|
||||||
|
|
||||||
Field['TransactionType'].associatedType = TransactionType
|
|
||||||
Field['TransactionResult'].associatedType = TransactionResult
|
|
||||||
Field['LedgerEntryType'].associatedType = LedgerEntryType
|
|
||||||
|
|
||||||
export { coreTypes }
|
|
||||||
Binary file not shown.
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"binary": "120066240000000026000003006840000000000000007300701321ED9D593004CC501CACD261BD8E31E863F2B3F6CA69505E7FD54DA8F5690BEFB7AE8114000000000000000000000000000000000000000000101101",
|
|
||||||
"tx": {
|
|
||||||
"UNLModifyDisabling": 1,
|
|
||||||
"LedgerSequence": 768,
|
|
||||||
"UNLModifyValidator": "ED9D593004CC501CACD261BD8E31E863F2B3F6CA69505E7FD54DA8F5690BEFB7AE",
|
|
||||||
"TransactionType": "UNLModify",
|
|
||||||
"Account": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
|
|
||||||
"Sequence": 0,
|
|
||||||
"Fee": "0",
|
|
||||||
"SigningPubKey": ""}
|
|
||||||
}
|
|
||||||
@@ -1,547 +0,0 @@
|
|||||||
{
|
|
||||||
"NFTokenMint": {
|
|
||||||
"tx": {
|
|
||||||
"json": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Fee": "12",
|
|
||||||
"Flags": 9,
|
|
||||||
"LastLedgerSequence": 22,
|
|
||||||
"Sequence": 5,
|
|
||||||
"SigningPubKey": "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
|
||||||
"TokenTaxon": 0,
|
|
||||||
"TransactionType": "NFTokenMint",
|
|
||||||
"TransferFee": 50,
|
|
||||||
"TxnSignature": "3045022100DAB7343B26035702FF7E0736E04A092AC9512964E41C3CA64926CDFBE777946602202F295A3BB1282E0AF98F9F5978D4037D75EDEB0EC642519D966D7B9B5826E61B",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
},
|
|
||||||
"binary": "12001914003222000000092400000005201B00000016202A0000000068400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074473045022100DAB7343B26035702FF7E0736E04A092AC9512964E41C3CA64926CDFBE777946602202F295A3BB1282E0AF98F9F5978D4037D75EDEB0EC642519D966D7B9B5826E61B751868747470733A2F2F677265677765697362726F642E636F6D8114B5F762798A53D543A014CAF8B297CFF8F2F937E8"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"json": {
|
|
||||||
"AffectedNodes": [
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Balance": "99999999999999940",
|
|
||||||
"Flags": 0,
|
|
||||||
"MintedTokens": 5,
|
|
||||||
"OwnerCount": 1,
|
|
||||||
"Sequence": 6
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "99999999999999952",
|
|
||||||
"MintedTokens": 4,
|
|
||||||
"Sequence": 5
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "E9BFEE7C403F74445B59B8FA8E972ABD642A360E1FBC15C53BAA717573BEC462",
|
|
||||||
"PreviousTxnLgrSeq": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Flags": 0,
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F00000004",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenPage",
|
|
||||||
"LedgerIndex": "B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFF",
|
|
||||||
"PreviousFields": {
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "E9BFEE7C403F74445B59B8FA8E972ABD642A360E1FBC15C53BAA717573BEC462",
|
|
||||||
"PreviousTxnLgrSeq": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 4,
|
|
||||||
"TransactionResult": "tesSUCCESS"
|
|
||||||
},
|
|
||||||
"binary": "201C00000004F8E5110061250000000355E9BFEE7C403F74445B59B8FA8E972ABD642A360E1FBC15C53BAA717573BEC462562B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8E62400000005202B0000000462416345785D89FFD0E1E7220000000024000000062D00000001202B0000000562416345785D89FFC48114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E5110050250000000355E9BFEE7C403F74445B59B8FA8E972ABD642A360E1FBC15C53BAA717573BEC46256B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFFE6FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E72200000000FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F00000004751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E1F1031000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NFTokenBurn": {
|
|
||||||
"tx": {
|
|
||||||
"json": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Fee": "12",
|
|
||||||
"Flags": 0,
|
|
||||||
"LastLedgerSequence": 23,
|
|
||||||
"Sequence": 6,
|
|
||||||
"SigningPubKey": "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F00000004",
|
|
||||||
"TransactionType": "NFTokenBurn",
|
|
||||||
"TxnSignature": "3045022100D614E1F0A1C41A05652B8998FC2C4DC8658B95BFD89F4A0DEBE3FFDCB75CE1D8022027DF89138FC442C803DC2BEC07636CEB8D0EC6297E23262A729B83D32F93FD3D"
|
|
||||||
},
|
|
||||||
"binary": "12001A22000000002400000006201B000000175A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F0000000468400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074473045022100D614E1F0A1C41A05652B8998FC2C4DC8658B95BFD89F4A0DEBE3FFDCB75CE1D8022027DF89138FC442C803DC2BEC07636CEB8D0EC6297E23262A729B83D32F93FD3D8114B5F762798A53D543A014CAF8B297CFF8F2F937E8"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"json": {
|
|
||||||
"AffectedNodes": [
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Balance": "99999999999999928",
|
|
||||||
"BurnedTokens": 1,
|
|
||||||
"Flags": 0,
|
|
||||||
"MintedTokens": 5,
|
|
||||||
"OwnerCount": 1,
|
|
||||||
"Sequence": 7
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "99999999999999940",
|
|
||||||
"Sequence": 6
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "5F0E3A19F1D5B028A31B8A0FDE8533B0FD185E4AE306F79CFF51D18E68985231",
|
|
||||||
"PreviousTxnLgrSeq": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Flags": 0,
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenPage",
|
|
||||||
"LedgerIndex": "B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFF",
|
|
||||||
"PreviousFields": {
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F00000004",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "5F0E3A19F1D5B028A31B8A0FDE8533B0FD185E4AE306F79CFF51D18E68985231",
|
|
||||||
"PreviousTxnLgrSeq": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS"
|
|
||||||
},
|
|
||||||
"binary": "201C00000000F8E51100612500000003555F0E3A19F1D5B028A31B8A0FDE8533B0FD185E4AE306F79CFF51D18E68985231562B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8E6240000000662416345785D89FFC4E1E7220000000024000000072D00000001202B00000005202C0000000162416345785D89FFB88114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E51100502500000003555F0E3A19F1D5B028A31B8A0FDE8533B0FD185E4AE306F79CFF51D18E6898523156B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFFE6FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E85B974D9F00000004751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E72200000000FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E1F1031000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NFTokenCreateOffer": {
|
|
||||||
"tx": {
|
|
||||||
"json": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Amount": "100",
|
|
||||||
"Destination": "rV3WAvwwXgvPrYiUgSoytn9w3mejtPgLo",
|
|
||||||
"Expiration": 999999999,
|
|
||||||
"Fee": "12",
|
|
||||||
"Flags": 1,
|
|
||||||
"LastLedgerSequence": 26,
|
|
||||||
"Sequence": 9,
|
|
||||||
"SigningPubKey": "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"TransactionType": "NFTokenCreateOffer",
|
|
||||||
"TxnSignature": "3045022100BCEEEF98B9DB3A2ACA7CE80AB91B9398E3429B93F660BB8A063F134AA798AE970220667C560D16E555DF5EF30536804C082DAEC7A446DF2C7533ADB1E2437AF2CCB0"
|
|
||||||
},
|
|
||||||
"binary": "12001B220000000124000000092A3B9AC9FF201B0000001A5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B0000000061400000000000006468400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074473045022100BCEEEF98B9DB3A2ACA7CE80AB91B9398E3429B93F660BB8A063F134AA798AE970220667C560D16E555DF5EF30536804C082DAEC7A446DF2C7533ADB1E2437AF2CCB08114B5F762798A53D543A014CAF8B297CFF8F2F937E883140551EBD684BF2ADE0EF093A92B6E2C55D15BD9AE"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"json": {
|
|
||||||
"AffectedNodes": [
|
|
||||||
{
|
|
||||||
"CreatedNode": {
|
|
||||||
"LedgerEntryType": "NFTokenOffer",
|
|
||||||
"LedgerIndex": "1DEF39A07F364CB73BF5F8306BE22628D0AE517A2CB0AE5341269B1E671F2052",
|
|
||||||
"NewFields": {
|
|
||||||
"Amount": "100",
|
|
||||||
"Expiration": 999999999,
|
|
||||||
"Flags": 1,
|
|
||||||
"Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Balance": "99999999999999892",
|
|
||||||
"BurnedTokens": 1,
|
|
||||||
"Flags": 0,
|
|
||||||
"MintedTokens": 5,
|
|
||||||
"OwnerCount": 2,
|
|
||||||
"Sequence": 10
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "99999999999999904",
|
|
||||||
"OwnerCount": 1,
|
|
||||||
"Sequence": 9
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "D73C0ACEA1D4C9186A4AF1EAE2A4BD0A719A78AF6CE897C289AB297D371BEF53",
|
|
||||||
"PreviousTxnLgrSeq": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CreatedNode": {
|
|
||||||
"LedgerEntryType": "DirectoryNode",
|
|
||||||
"LedgerIndex": "86992C19DA69763CAA9A4FC1697508140A995F15173EB00892C8EE23D5FD8FC3",
|
|
||||||
"NewFields": {
|
|
||||||
"Flags": 2,
|
|
||||||
"RootIndex": "86992C19DA69763CAA9A4FC1697508140A995F15173EB00892C8EE23D5FD8FC3",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CreatedNode": {
|
|
||||||
"LedgerEntryType": "DirectoryNode",
|
|
||||||
"LedgerIndex": "D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204",
|
|
||||||
"NewFields": {
|
|
||||||
"Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"RootIndex": "D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS"
|
|
||||||
},
|
|
||||||
"binary": "201C00000000F8E3110037561DEF39A07F364CB73BF5F8306BE22628D0AE517A2CB0AE5341269B1E671F2052E822000000012A3B9AC9FF5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B000000006140000000000000648214B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E5110061250000000655D73C0ACEA1D4C9186A4AF1EAE2A4BD0A719A78AF6CE897C289AB297D371BEF53562B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8E624000000092D0000000162416345785D89FFA0E1E72200000000240000000A2D00000002202B00000005202C0000000162416345785D89FF948114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E31100645686992C19DA69763CAA9A4FC1697508140A995F15173EB00892C8EE23D5FD8FC3E822000000025886992C19DA69763CAA9A4FC1697508140A995F15173EB00892C8EE23D5FD8FC35A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000E1E1E311006456D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204E858D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA6352048214B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1F1031000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NFTokenCancelOffer": {
|
|
||||||
"tx": {
|
|
||||||
"json": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Fee": "12",
|
|
||||||
"Flags": 0,
|
|
||||||
"LastLedgerSequence": 27,
|
|
||||||
"Sequence": 10,
|
|
||||||
"SigningPubKey": "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
|
||||||
"TokenIDs": [
|
|
||||||
"00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000"
|
|
||||||
],
|
|
||||||
"TransactionType": "NFTokenCancelOffer",
|
|
||||||
"TxnSignature": "3044022075EAF267D19A626B3D970A96C363380FC7CCFB81178D75F723C5F097309B9FBC022027A896B759E882BFCB810C8AB5B3D819AEBFCAD6E0AF17F909BB3AFA9085EBEE"
|
|
||||||
},
|
|
||||||
"binary": "12001C2200000000240000000A201B0000001B68400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074463044022075EAF267D19A626B3D970A96C363380FC7CCFB81178D75F723C5F097309B9FBC022027A896B759E882BFCB810C8AB5B3D819AEBFCAD6E0AF17F909BB3AFA9085EBEE8114B5F762798A53D543A014CAF8B297CFF8F2F937E804132000090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"json": {
|
|
||||||
"AffectedNodes": [
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Balance": "99999999999999880",
|
|
||||||
"BurnedTokens": 1,
|
|
||||||
"Flags": 0,
|
|
||||||
"MintedTokens": 5,
|
|
||||||
"OwnerCount": 2,
|
|
||||||
"Sequence": 11
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "99999999999999892",
|
|
||||||
"Sequence": 10
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "4D43E602582E492DC685408C36B18CFB326FBC06C03DA46580F03356F91D590D",
|
|
||||||
"PreviousTxnLgrSeq": 7
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS"
|
|
||||||
},
|
|
||||||
"binary": "201C00000000F8E51100612500000007554D43E602582E492DC685408C36B18CFB326FBC06C03DA46580F03356F91D590D562B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8E6240000000A62416345785D89FF94E1E72200000000240000000B2D00000002202B00000005202C0000000162416345785D89FF888114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1F1031000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NFTokenAcceptOffer": {
|
|
||||||
"tx": {
|
|
||||||
"json": {
|
|
||||||
"Account": "rKBmBAi3cWzuj6iZYx6K7F3xF1LSs3br3o",
|
|
||||||
"Fee": "12",
|
|
||||||
"Flags": 0,
|
|
||||||
"LastLedgerSequence": 37,
|
|
||||||
"SellOffer": "AED08CC1F50DD5F23A1948AF86153A3F3B7593E5EC77D65A02BB1B29E05AB6AF",
|
|
||||||
"Sequence": 9,
|
|
||||||
"SigningPubKey": "ED3C94824B6696F16CA54F0E3085A5ED8867D19DC4BA572086E03DCAB30B094D79",
|
|
||||||
"TransactionType": "NFTokenAcceptOffer",
|
|
||||||
"TxnSignature": "D6653C5BDA53E29CFF05905BE4EA36A913D28D8730EAAB339FFC57D0484230349E195303A301490DDF5DC3BC01AD22047D9A0BBC37983D96ED06DBBFE43F9A08"
|
|
||||||
},
|
|
||||||
"binary": "12001D22000000002400000009201B00000025501DAED08CC1F50DD5F23A1948AF86153A3F3B7593E5EC77D65A02BB1B29E05AB6AF68400000000000000C7321ED3C94824B6696F16CA54F0E3085A5ED8867D19DC4BA572086E03DCAB30B094D797440D6653C5BDA53E29CFF05905BE4EA36A913D28D8730EAAB339FFC57D0484230349E195303A301490DDF5DC3BC01AD22047D9A0BBC37983D96ED06DBBFE43F9A088114C77B4F8423139A8F0939DDEB9EB076CC74F4A3B3"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"json": {
|
|
||||||
"AffectedNodes": [
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"Balance": "99997999999999932",
|
|
||||||
"BurnedTokens": 1,
|
|
||||||
"Flags": 0,
|
|
||||||
"MintedTokens": 5,
|
|
||||||
"OwnerCount": 3,
|
|
||||||
"Sequence": 15
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "99997999999999832",
|
|
||||||
"OwnerCount": 4
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "BB2608749900A1762B2C7E60AFB437091F56810B39E85545E9541A5F33FD2785",
|
|
||||||
"PreviousTxnLgrSeq": 16
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DeletedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Flags": 2,
|
|
||||||
"RootIndex": "45D3F54AACBCD7F8A5FB0737DA561BE9F0EA924276B2FDB0B8EA8D7FE4319632",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "DirectoryNode",
|
|
||||||
"LedgerIndex": "45D3F54AACBCD7F8A5FB0737DA561BE9F0EA924276B2FDB0B8EA8D7FE4319632"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Account": "rKBmBAi3cWzuj6iZYx6K7F3xF1LSs3br3o",
|
|
||||||
"Balance": "1999999999888",
|
|
||||||
"Flags": 0,
|
|
||||||
"OwnerCount": 1,
|
|
||||||
"Sequence": 10
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "AccountRoot",
|
|
||||||
"LedgerIndex": "4BDDA92DDF4C9702E2952261A181E7E1EFF37E9C2AC9D390D0E81AF34FC8A325",
|
|
||||||
"PreviousFields": {
|
|
||||||
"Balance": "2000000000000",
|
|
||||||
"OwnerCount": 0,
|
|
||||||
"Sequence": 9
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "2B07B79F67C41680E07ED8EDCA5BC20827F1694D51207EFD800F5174FE76ADB3",
|
|
||||||
"PreviousTxnLgrSeq": 13
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"DeletedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Amount": "100",
|
|
||||||
"Flags": 1,
|
|
||||||
"OfferNode": "0000000000000000",
|
|
||||||
"Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"OwnerNode": "0000000000000000",
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenOffer",
|
|
||||||
"LedgerIndex": "AED08CC1F50DD5F23A1948AF86153A3F3B7593E5EC77D65A02BB1B29E05AB6AF"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Flags": 0,
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenPage",
|
|
||||||
"LedgerIndex": "B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFF",
|
|
||||||
"PreviousFields": {
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"PreviousTxnID": "EF14A0EB3B1E7E928A07F4B6A659EEA4FB0F839499397B224E19DE61CAA884C7",
|
|
||||||
"PreviousTxnLgrSeq": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CreatedNode": {
|
|
||||||
"LedgerEntryType": "NFTokenPage",
|
|
||||||
"LedgerIndex": "C77B4F8423139A8F0939DDEB9EB076CC74F4A3B3FFFFFFFFFFFFFFFFFFFFFFFF",
|
|
||||||
"NewFields": {
|
|
||||||
"NonFungibleTokens": [
|
|
||||||
{
|
|
||||||
"NonFungibleToken": {
|
|
||||||
"TokenID": "00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003",
|
|
||||||
"URI": "68747470733A2F2F677265677765697362726F642E636F6D"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ModifiedNode": {
|
|
||||||
"FinalFields": {
|
|
||||||
"Flags": 0,
|
|
||||||
"Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
|
||||||
"RootIndex": "D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "DirectoryNode",
|
|
||||||
"LedgerIndex": "D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS"
|
|
||||||
},
|
|
||||||
"binary": "201C00000000F8E5110061250000001055BB2608749900A1762B2C7E60AFB437091F56810B39E85545E9541A5F33FD2785562B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8E62D0000000462416343A6B43FDF58E1E72200000000240000000F2D00000003202B00000005202C0000000162416343A6B43FDFBC8114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E41100645645D3F54AACBCD7F8A5FB0737DA561BE9F0EA924276B2FDB0B8EA8D7FE4319632E722000000025845D3F54AACBCD7F8A5FB0737DA561BE9F0EA924276B2FDB0B8EA8D7FE43196325A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003E1E1E5110061250000000D552B07B79F67C41680E07ED8EDCA5BC20827F1694D51207EFD800F5174FE76ADB3564BDDA92DDF4C9702E2952261A181E7E1EFF37E9C2AC9D390D0E81AF34FC8A325E624000000092D0000000062400001D1A94A2000E1E72200000000240000000A2D0000000162400001D1A94A1F908114C77B4F8423139A8F0939DDEB9EB076CC74F4A3B3E1E1E411003756AED08CC1F50DD5F23A1948AF86153A3F3B7593E5EC77D65A02BB1B29E05AB6AFE722000000013400000000000000003C00000000000000005A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E000000036140000000000000648214B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1E5110050250000000455EF14A0EB3B1E7E928A07F4B6A659EEA4FB0F839499397B224E19DE61CAA884C756B5F762798A53D543A014CAF8B297CFF8F2F937E8FFFFFFFFFFFFFFFFFFFFFFFFE6FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E72200000000FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E80000099B00000000751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E816E5DA9C00000001751868747470733A2F2F677265677765697362726F642E636F6DE1EC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E82DCBAB9D00000002751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E1E311005056C77B4F8423139A8F0939DDEB9EB076CC74F4A3B3FFFFFFFFFFFFFFFFFFFFFFFFE8FAEC5A00090032B5F762798A53D543A014CAF8B297CFF8F2F937E844B17C9E00000003751868747470733A2F2F677265677765697362726F642E636F6DE1F1E1E1E511006456D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204E7220000000058D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA6352048214B5F762798A53D543A014CAF8B297CFF8F2F937E8E1E1F1031000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
const { coreTypes } = require('../dist/types')
|
|
||||||
const { Hash160, Hash256, AccountID, Currency } = coreTypes
|
|
||||||
const { Buffer } = require('buffer/')
|
|
||||||
|
|
||||||
describe('Hash160', function () {
|
|
||||||
test('has a static width member', function () {
|
|
||||||
expect(Hash160.width).toBe(20)
|
|
||||||
})
|
|
||||||
test('inherited by subclasses', function () {
|
|
||||||
expect(AccountID.width).toBe(20)
|
|
||||||
expect(Currency.width).toBe(20)
|
|
||||||
})
|
|
||||||
test('can be compared against another', function () {
|
|
||||||
const h1 = Hash160.from('1000000000000000000000000000000000000000')
|
|
||||||
const h2 = Hash160.from('2000000000000000000000000000000000000000')
|
|
||||||
const h3 = Hash160.from('0000000000000000000000000000000000000003')
|
|
||||||
expect(h1.lt(h2)).toBe(true)
|
|
||||||
expect(h3.lt(h2)).toBe(true)
|
|
||||||
})
|
|
||||||
test('throws when constructed from invalid hash length', () => {
|
|
||||||
expect(() =>
|
|
||||||
Hash160.from('10000000000000000000000000000000000000'),
|
|
||||||
).toThrow('Invalid Hash length 19')
|
|
||||||
expect(() =>
|
|
||||||
Hash160.from('100000000000000000000000000000000000000000'),
|
|
||||||
).toThrow('Invalid Hash length 21')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Hash256', function () {
|
|
||||||
test('has a static width member', function () {
|
|
||||||
expect(Hash256.width).toBe(32)
|
|
||||||
})
|
|
||||||
test('has a ZERO_256 member', function () {
|
|
||||||
expect(Hash256.ZERO_256.toJSON()).toBe(
|
|
||||||
'0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('supports getting the nibblet values at given positions', function () {
|
|
||||||
const h = Hash256.from(
|
|
||||||
'1359BD0000000000000000000000000000000000000000000000000000000000',
|
|
||||||
)
|
|
||||||
expect(h.nibblet(0)).toBe(0x1)
|
|
||||||
expect(h.nibblet(1)).toBe(0x3)
|
|
||||||
expect(h.nibblet(2)).toBe(0x5)
|
|
||||||
expect(h.nibblet(3)).toBe(0x9)
|
|
||||||
expect(h.nibblet(4)).toBe(0x0b)
|
|
||||||
expect(h.nibblet(5)).toBe(0xd)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Currency', function () {
|
|
||||||
test('Will throw an error for dodgy XRP ', function () {
|
|
||||||
expect(() =>
|
|
||||||
Currency.from('0000000000000000000000005852500000000000'),
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
test('Currency with lowercase letters decode to hex', () => {
|
|
||||||
expect(Currency.from('xRp').toJSON()).toBe(
|
|
||||||
'0000000000000000000000007852700000000000',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('Currency codes with symbols decode to hex', () => {
|
|
||||||
expect(Currency.from('x|p').toJSON()).toBe(
|
|
||||||
'000000000000000000000000787C700000000000',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('Currency codes with uppercase and 0-9 decode to ISO codes', () => {
|
|
||||||
expect(Currency.from('X8P').toJSON()).toBe('X8P')
|
|
||||||
expect(Currency.from('USD').toJSON()).toBe('USD')
|
|
||||||
})
|
|
||||||
test('can be constructed from a Buffer', function () {
|
|
||||||
const xrp = new Currency(Buffer.alloc(20))
|
|
||||||
expect(xrp.iso()).toBe('XRP')
|
|
||||||
})
|
|
||||||
test('Can handle non-standard currency codes', () => {
|
|
||||||
const currency = '015841551A748AD2C1F76FF6ECB0CCCD00000000'
|
|
||||||
expect(Currency.from(currency).toJSON()).toBe(currency)
|
|
||||||
})
|
|
||||||
test('throws on invalid reprs', function () {
|
|
||||||
expect(() => Currency.from(Buffer.alloc(19))).toThrow()
|
|
||||||
expect(() => Currency.from(1)).toThrow()
|
|
||||||
expect(() =>
|
|
||||||
Currency.from('00000000000000000000000000000000000000m'),
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
const { loadFixture } = require('./utils')
|
|
||||||
const {
|
|
||||||
transactionTreeHash,
|
|
||||||
ledgerHash,
|
|
||||||
accountStateHash,
|
|
||||||
} = require('../dist/ledger-hashes')
|
|
||||||
|
|
||||||
describe('Ledger Hashes', function () {
|
|
||||||
function testFactory(ledgerFixture) {
|
|
||||||
describe(`can calculate hashes for ${ledgerFixture}`, function () {
|
|
||||||
const ledger = loadFixture(ledgerFixture)
|
|
||||||
test('computes correct account state hash', function () {
|
|
||||||
expect(accountStateHash(ledger.accountState).toHex()).toBe(
|
|
||||||
ledger.account_hash,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('computes correct transaction tree hash', function () {
|
|
||||||
expect(transactionTreeHash(ledger.transactions).toHex()).toBe(
|
|
||||||
ledger.transaction_hash,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('computes correct ledger header hash', function () {
|
|
||||||
expect(ledgerHash(ledger).toHex()).toBe(ledger.hash)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
testFactory('ledger-full-40000.json')
|
|
||||||
testFactory('ledger-full-38129.json')
|
|
||||||
})
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
const {
|
|
||||||
encodeForSigning,
|
|
||||||
encodeForSigningClaim,
|
|
||||||
encodeForMultisigning,
|
|
||||||
} = require('../dist')
|
|
||||||
|
|
||||||
const tx_json = {
|
|
||||||
Account: 'r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ',
|
|
||||||
Amount: '1000',
|
|
||||||
Destination: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
|
||||||
Fee: '10',
|
|
||||||
Flags: 2147483648,
|
|
||||||
Sequence: 1,
|
|
||||||
TransactionType: 'Payment',
|
|
||||||
TxnSignature:
|
|
||||||
'30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1' +
|
|
||||||
'E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80' +
|
|
||||||
'ECA3CD7B9B',
|
|
||||||
Signature:
|
|
||||||
'30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E72' +
|
|
||||||
'1B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA' +
|
|
||||||
'3CD7B9B',
|
|
||||||
SigningPubKey:
|
|
||||||
'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A',
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Signing data', function () {
|
|
||||||
test('can create single signing blobs', function () {
|
|
||||||
const actual = encodeForSigning(tx_json)
|
|
||||||
expect(actual).toBe(
|
|
||||||
[
|
|
||||||
'53545800', // signingPrefix
|
|
||||||
// TransactionType
|
|
||||||
'12',
|
|
||||||
'0000',
|
|
||||||
// Flags
|
|
||||||
'22',
|
|
||||||
'80000000',
|
|
||||||
// Sequence
|
|
||||||
'24',
|
|
||||||
'00000001',
|
|
||||||
// Amount
|
|
||||||
'61',
|
|
||||||
// native amount
|
|
||||||
'40000000000003E8',
|
|
||||||
// Fee
|
|
||||||
'68',
|
|
||||||
// native amount
|
|
||||||
'400000000000000A',
|
|
||||||
// SigningPubKey
|
|
||||||
'73',
|
|
||||||
// VLLength
|
|
||||||
'21',
|
|
||||||
'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A',
|
|
||||||
// Account
|
|
||||||
'81',
|
|
||||||
// VLLength
|
|
||||||
'14',
|
|
||||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
|
||||||
// Destination
|
|
||||||
'83',
|
|
||||||
// VLLength
|
|
||||||
'14',
|
|
||||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8',
|
|
||||||
].join(''),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('can create multi signing blobs', function () {
|
|
||||||
const signingAccount = 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN'
|
|
||||||
const signingJson = Object.assign({}, tx_json, { SigningPubKey: '' })
|
|
||||||
const actual = encodeForMultisigning(signingJson, signingAccount)
|
|
||||||
expect(actual).toBe(
|
|
||||||
[
|
|
||||||
'534D5400', // signingPrefix
|
|
||||||
// TransactionType
|
|
||||||
'12',
|
|
||||||
'0000',
|
|
||||||
// Flags
|
|
||||||
'22',
|
|
||||||
'80000000',
|
|
||||||
// Sequence
|
|
||||||
'24',
|
|
||||||
'00000001',
|
|
||||||
// Amount
|
|
||||||
'61',
|
|
||||||
// native amount
|
|
||||||
'40000000000003E8',
|
|
||||||
// Fee
|
|
||||||
'68',
|
|
||||||
// native amount
|
|
||||||
'400000000000000A',
|
|
||||||
// SigningPubKey
|
|
||||||
'73',
|
|
||||||
// VLLength
|
|
||||||
'00',
|
|
||||||
// '',
|
|
||||||
// Account
|
|
||||||
'81',
|
|
||||||
// VLLength
|
|
||||||
'14',
|
|
||||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
|
||||||
// Destination
|
|
||||||
'83',
|
|
||||||
// VLLength
|
|
||||||
'14',
|
|
||||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8',
|
|
||||||
// signingAccount suffix
|
|
||||||
'C0A5ABEF242802EFED4B041E8F2D4A8CC86AE3D1',
|
|
||||||
].join(''),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('can create claim blob', function () {
|
|
||||||
const channel =
|
|
||||||
'43904CBFCDCEC530B4037871F86EE90BF799DF8D2E0EA564BC8A3F332E4F5FB1'
|
|
||||||
const amount = '1000'
|
|
||||||
const json = { channel, amount }
|
|
||||||
const actual = encodeForSigningClaim(json)
|
|
||||||
expect(actual).toBe(
|
|
||||||
[
|
|
||||||
// hash prefix
|
|
||||||
'434C4D00',
|
|
||||||
// channel ID
|
|
||||||
'43904CBFCDCEC530B4037871F86EE90BF799DF8D2E0EA564BC8A3F332E4F5FB1',
|
|
||||||
// amount as a uint64
|
|
||||||
'00000000000003E8',
|
|
||||||
].join(''),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
const { coreTypes } = require('../dist/types')
|
|
||||||
const { SerializedType } = require('../dist/types/serialized-type')
|
|
||||||
|
|
||||||
describe('SerializedType interfaces', () => {
|
|
||||||
Object.entries(coreTypes).forEach(([name, Value]) => {
|
|
||||||
test(`${name} has a \`from\` static constructor`, () => {
|
|
||||||
expect(Value.from && Value.from !== Array.from).toBe(true)
|
|
||||||
})
|
|
||||||
test(`${name} has a default constructor`, () => {
|
|
||||||
expect(new Value()).not.toBe(undefined)
|
|
||||||
})
|
|
||||||
test(`${name}.from will return the same object`, () => {
|
|
||||||
const instance = new Value()
|
|
||||||
expect(Value.from(instance) === instance).toBe(true)
|
|
||||||
})
|
|
||||||
test(`${name} instances have toBytesSink`, () => {
|
|
||||||
expect(new Value().toBytesSink).not.toBe(undefined)
|
|
||||||
})
|
|
||||||
test(`${name} instances have toJSON`, () => {
|
|
||||||
expect(new Value().toJSON).not.toBe(undefined)
|
|
||||||
})
|
|
||||||
test(`${name}.from(json).toJSON() == json`, () => {
|
|
||||||
const newJSON = new Value().toJSON()
|
|
||||||
expect(Value.from(newJSON).toJSON()).toEqual(newJSON)
|
|
||||||
})
|
|
||||||
describe(`${name} supports all methods of the SerializedType mixin`, () => {
|
|
||||||
Object.keys(SerializedType.prototype).forEach((k) => {
|
|
||||||
test(`new ${name}.prototype.${k} !== undefined`, () => {
|
|
||||||
expect(Value.prototype[k]).not.toBe(undefined)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
const fs = require("fs");
|
|
||||||
const { Buffer } = require('buffer/')
|
|
||||||
|
|
||||||
function hexOnly(hex) {
|
|
||||||
return hex.replace(/[^a-fA-F0-9]/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function unused() {}
|
|
||||||
|
|
||||||
function parseHexOnly(hex) {
|
|
||||||
return Buffer.from(hexOnly(hex), "hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFixture(relativePath) {
|
|
||||||
const fn = __dirname + "/fixtures/" + relativePath;
|
|
||||||
return require(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFixtureText(relativePath) {
|
|
||||||
const fn = __dirname + "/fixtures/" + relativePath;
|
|
||||||
return fs.readFileSync(fn).toString("utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
hexOnly,
|
|
||||||
parseHexOnly,
|
|
||||||
loadFixture,
|
|
||||||
loadFixtureText,
|
|
||||||
unused,
|
|
||||||
};
|
|
||||||
72
packages/ripple-keypairs/.gitignore
vendored
72
packages/ripple-keypairs/.gitignore
vendored
@@ -1,72 +0,0 @@
|
|||||||
# .gitignore
|
|
||||||
|
|
||||||
# Ignore package locks other than npm
|
|
||||||
yarn.lock
|
|
||||||
npm-shrinkwrap.json
|
|
||||||
|
|
||||||
# Ignore vim swap files.
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
# Ignore SCons support files.
|
|
||||||
.sconsign.dblite
|
|
||||||
|
|
||||||
# Ignore python compiled files.
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Ignore Macintosh Desktop Services Store files.
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Ignore backup/temps
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Ignore object files.
|
|
||||||
*.o
|
|
||||||
build/
|
|
||||||
tags
|
|
||||||
bin/rippled
|
|
||||||
Debug/*.*
|
|
||||||
Release/*.*
|
|
||||||
|
|
||||||
# Ignore locally installed node_modules
|
|
||||||
node_modules
|
|
||||||
!test/node_modules
|
|
||||||
|
|
||||||
# Ignore tmp directory.
|
|
||||||
tmp
|
|
||||||
|
|
||||||
# Ignore database directory.
|
|
||||||
db/*.db
|
|
||||||
db/*.db-*
|
|
||||||
|
|
||||||
# Ignore customized configs
|
|
||||||
rippled.cfg
|
|
||||||
validators.txt
|
|
||||||
test/config.js
|
|
||||||
|
|
||||||
# Ignore coverage files
|
|
||||||
/lib-cov
|
|
||||||
/src-cov
|
|
||||||
/coverage.html
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# Ignore IntelliJ files
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Ignore npm-debug
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# Ignore dist folder, build for bower
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# Ignore flow output directory
|
|
||||||
out/
|
|
||||||
|
|
||||||
# Ignore perf test cache
|
|
||||||
scripts/cache
|
|
||||||
|
|
||||||
eslintrc
|
|
||||||
|
|
||||||
distrib/
|
|
||||||
|
|
||||||
# nyc (istanbul)
|
|
||||||
.nyc_output
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
opts: false,
|
|
||||||
slow: 500,
|
|
||||||
timeout: 5000,
|
|
||||||
|
|
||||||
// Required to get proper coverage on TypeScript files
|
|
||||||
// transpile-only is required if we use custom types
|
|
||||||
require: ['ts-node/register/transpile-only', 'source-map-support/register'],
|
|
||||||
|
|
||||||
// Look for tests in subdirectories
|
|
||||||
recursive: true,
|
|
||||||
|
|
||||||
// Check for global variable leaks
|
|
||||||
'check-leaks': true,
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extension: ['.js', '.ts'],
|
|
||||||
|
|
||||||
exclude: [
|
|
||||||
'**/*.d.ts',
|
|
||||||
'*.js',
|
|
||||||
'test/**/*',
|
|
||||||
'coverage/**/*',
|
|
||||||
],
|
|
||||||
|
|
||||||
// Assert we remain at 100% code coverage
|
|
||||||
'check-coverage': true,
|
|
||||||
'branches': 100,
|
|
||||||
'lines': 100,
|
|
||||||
'functions': 100,
|
|
||||||
'statements': 100,
|
|
||||||
|
|
||||||
// Required to get coverage reported on every file, including those that aren't tested
|
|
||||||
all: true,
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "ripple-keypairs",
|
|
||||||
"version": "1.1.0",
|
|
||||||
"description": "Cryptographic key pairs for the XRP Ledger",
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc -b",
|
|
||||||
"test": "tsc -b && nyc mocha",
|
|
||||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo",
|
|
||||||
"lint": "eslint . --ext .ts",
|
|
||||||
"prepublish": "npm run lint && npm test"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist/*"
|
|
||||||
],
|
|
||||||
"main": "dist/",
|
|
||||||
"directories": {
|
|
||||||
"test": "test"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"bn.js": "^5.1.1",
|
|
||||||
"brorand": "^1.0.5",
|
|
||||||
"elliptic": "^6.5.4",
|
|
||||||
"hash.js": "^1.0.3",
|
|
||||||
"ripple-address-codec": "^4.2.0"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@github.com:XRPLF/xrpl.js.git"
|
|
||||||
},
|
|
||||||
"license": "ISC",
|
|
||||||
"prettier": "@xrplf/prettier-config",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/* eslint-disable no-bitwise --
|
|
||||||
* lots of bitwise operators necessary for this */
|
|
||||||
import * as hashjs from 'hash.js'
|
|
||||||
import * as BigNum from 'bn.js'
|
|
||||||
|
|
||||||
export default class Sha512 {
|
|
||||||
// TODO: type of `hash`?
|
|
||||||
hash: any
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.hash = hashjs.sha512()
|
|
||||||
}
|
|
||||||
|
|
||||||
add(bytes) {
|
|
||||||
this.hash.update(bytes)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
addU32(i) {
|
|
||||||
return this.add([
|
|
||||||
(i >>> 24) & 0xff,
|
|
||||||
(i >>> 16) & 0xff,
|
|
||||||
(i >>> 8) & 0xff,
|
|
||||||
i & 0xff,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
finish() {
|
|
||||||
return this.hash.digest()
|
|
||||||
}
|
|
||||||
|
|
||||||
first256() {
|
|
||||||
return this.finish().slice(0, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
first256BN() {
|
|
||||||
return new BigNum(this.first256())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
import * as assert from 'assert'
|
|
||||||
import * as brorand from 'brorand'
|
|
||||||
import * as hashjs from 'hash.js'
|
|
||||||
import * as elliptic from 'elliptic'
|
|
||||||
|
|
||||||
import * as addressCodec from 'ripple-address-codec'
|
|
||||||
import { derivePrivateKey, accountPublicFromPublicGenerator } from './secp256k1'
|
|
||||||
import * as utils from './utils'
|
|
||||||
|
|
||||||
const Ed25519 = elliptic.eddsa('ed25519')
|
|
||||||
const Secp256k1 = elliptic.ec('secp256k1')
|
|
||||||
|
|
||||||
const { hexToBytes } = utils
|
|
||||||
const { bytesToHex } = utils
|
|
||||||
|
|
||||||
function generateSeed(
|
|
||||||
options: {
|
|
||||||
entropy?: Uint8Array
|
|
||||||
algorithm?: 'ed25519' | 'ecdsa-secp256k1'
|
|
||||||
} = {},
|
|
||||||
): string {
|
|
||||||
assert.ok(
|
|
||||||
!options.entropy || options.entropy.length >= 16,
|
|
||||||
'entropy too short',
|
|
||||||
)
|
|
||||||
const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16)
|
|
||||||
const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1'
|
|
||||||
return addressCodec.encodeSeed(entropy, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hash(message): number[] {
|
|
||||||
return hashjs.sha512().update(message).digest().slice(0, 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
const secp256k1 = {
|
|
||||||
deriveKeypair(
|
|
||||||
entropy: Uint8Array,
|
|
||||||
options?: object,
|
|
||||||
): {
|
|
||||||
privateKey: string
|
|
||||||
publicKey: string
|
|
||||||
} {
|
|
||||||
const prefix = '00'
|
|
||||||
|
|
||||||
const privateKey =
|
|
||||||
prefix + derivePrivateKey(entropy, options).toString(16, 64).toUpperCase()
|
|
||||||
|
|
||||||
const publicKey = bytesToHex(
|
|
||||||
Secp256k1.keyFromPrivate(privateKey.slice(2))
|
|
||||||
.getPublic()
|
|
||||||
.encodeCompressed(),
|
|
||||||
)
|
|
||||||
return { privateKey, publicKey }
|
|
||||||
},
|
|
||||||
|
|
||||||
sign(message, privateKey): string {
|
|
||||||
return bytesToHex(
|
|
||||||
Secp256k1.sign(hash(message), hexToBytes(privateKey), {
|
|
||||||
canonical: true,
|
|
||||||
}).toDER(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
verify(message, signature, publicKey): boolean {
|
|
||||||
return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const ed25519 = {
|
|
||||||
deriveKeypair(entropy: Uint8Array): {
|
|
||||||
privateKey: string
|
|
||||||
publicKey: string
|
|
||||||
} {
|
|
||||||
const prefix = 'ED'
|
|
||||||
const rawPrivateKey = hash(entropy)
|
|
||||||
const privateKey = prefix + bytesToHex(rawPrivateKey)
|
|
||||||
const publicKey =
|
|
||||||
prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes())
|
|
||||||
return { privateKey, publicKey }
|
|
||||||
},
|
|
||||||
|
|
||||||
sign(message, privateKey): string {
|
|
||||||
// caution: Ed25519.sign interprets all strings as hex, stripping
|
|
||||||
// any non-hex characters without warning
|
|
||||||
assert.ok(Array.isArray(message), 'message must be array of octets')
|
|
||||||
return bytesToHex(
|
|
||||||
Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toBytes(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
verify(message, signature, publicKey): boolean {
|
|
||||||
return Ed25519.verify(
|
|
||||||
message,
|
|
||||||
hexToBytes(signature),
|
|
||||||
hexToBytes(publicKey).slice(1),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function select(algorithm): any {
|
|
||||||
const methods = { 'ecdsa-secp256k1': secp256k1, ed25519 }
|
|
||||||
return methods[algorithm]
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveKeypair(
|
|
||||||
seed: string,
|
|
||||||
options?: object,
|
|
||||||
): {
|
|
||||||
publicKey: string
|
|
||||||
privateKey: string
|
|
||||||
} {
|
|
||||||
const decoded = addressCodec.decodeSeed(seed)
|
|
||||||
const algorithm = decoded.type === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1'
|
|
||||||
const method = select(algorithm)
|
|
||||||
const keypair = method.deriveKeypair(decoded.bytes, options)
|
|
||||||
const messageToVerify = hash('This test message should verify.')
|
|
||||||
const signature = method.sign(messageToVerify, keypair.privateKey)
|
|
||||||
/* istanbul ignore if */
|
|
||||||
if (method.verify(messageToVerify, signature, keypair.publicKey) !== true) {
|
|
||||||
throw new Error('derived keypair did not generate verifiable signature')
|
|
||||||
}
|
|
||||||
return keypair
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAlgorithmFromKey(key): 'ed25519' | 'ecdsa-secp256k1' {
|
|
||||||
const bytes = hexToBytes(key)
|
|
||||||
return bytes.length === 33 && bytes[0] === 0xed
|
|
||||||
? 'ed25519'
|
|
||||||
: 'ecdsa-secp256k1'
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign(messageHex, privateKey): string {
|
|
||||||
const algorithm = getAlgorithmFromKey(privateKey)
|
|
||||||
return select(algorithm).sign(hexToBytes(messageHex), privateKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify(messageHex, signature, publicKey): boolean {
|
|
||||||
const algorithm = getAlgorithmFromKey(publicKey)
|
|
||||||
return select(algorithm).verify(hexToBytes(messageHex), signature, publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveAddressFromBytes(publicKeyBytes: Buffer): string {
|
|
||||||
return addressCodec.encodeAccountID(
|
|
||||||
utils.computePublicKeyHash(publicKeyBytes),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveAddress(publicKey): string {
|
|
||||||
return deriveAddressFromBytes(Buffer.from(hexToBytes(publicKey)))
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveNodeAddress(publicKey): string {
|
|
||||||
const generatorBytes = addressCodec.decodeNodePublic(publicKey)
|
|
||||||
const accountPublicBytes = accountPublicFromPublicGenerator(generatorBytes)
|
|
||||||
return deriveAddressFromBytes(accountPublicBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { decodeSeed } = addressCodec
|
|
||||||
|
|
||||||
export = {
|
|
||||||
generateSeed,
|
|
||||||
deriveKeypair,
|
|
||||||
sign,
|
|
||||||
verify,
|
|
||||||
deriveAddress,
|
|
||||||
deriveNodeAddress,
|
|
||||||
decodeSeed,
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import * as assert from 'assert'
|
|
||||||
import * as hashjs from 'hash.js'
|
|
||||||
import * as BN from 'bn.js'
|
|
||||||
|
|
||||||
function bytesToHex(a): string {
|
|
||||||
return a
|
|
||||||
.map((byteValue) => {
|
|
||||||
const hex = byteValue.toString(16).toUpperCase()
|
|
||||||
return hex.length > 1 ? hex : `0${hex}`
|
|
||||||
})
|
|
||||||
.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
function hexToBytes(a): number[] {
|
|
||||||
assert.ok(a.length % 2 === 0)
|
|
||||||
return new BN(a, 16).toArray(null, a.length / 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
function computePublicKeyHash(publicKeyBytes: Buffer): Buffer {
|
|
||||||
const hash256 = hashjs.sha256().update(publicKeyBytes).digest()
|
|
||||||
|
|
||||||
const hash160 = hashjs.ripemd160().update(hash256).digest()
|
|
||||||
return Buffer.from(hash160)
|
|
||||||
}
|
|
||||||
|
|
||||||
export { bytesToHex, hexToBytes, computePublicKeyHash }
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
'use strict' // eslint-disable-line strict
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const fixtures = require('./fixtures/api.json')
|
|
||||||
const api = require('../dist')
|
|
||||||
const decodeSeed = api.decodeSeed
|
|
||||||
const entropy = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
|
|
||||||
|
|
||||||
describe('api', () => {
|
|
||||||
it('generateSeed - secp256k1', () => {
|
|
||||||
assert.strictEqual(api.generateSeed({entropy}), fixtures.secp256k1.seed)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('generateSeed - secp256k1, random', () => {
|
|
||||||
const seed = api.generateSeed()
|
|
||||||
assert(seed.charAt(0) === 's')
|
|
||||||
const {type, bytes} = decodeSeed(seed)
|
|
||||||
assert(type === 'secp256k1')
|
|
||||||
assert(bytes.length === 16)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('generateSeed - ed25519', () => {
|
|
||||||
assert.strictEqual(api.generateSeed({entropy, algorithm: 'ed25519'}),
|
|
||||||
fixtures.ed25519.seed)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('generateSeed - ed25519, random', () => {
|
|
||||||
const seed = api.generateSeed({algorithm: 'ed25519'})
|
|
||||||
assert(seed.slice(0, 3) === 'sEd')
|
|
||||||
const {type, bytes} = decodeSeed(seed)
|
|
||||||
assert(type === 'ed25519')
|
|
||||||
assert(bytes.length === 16)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveKeypair - secp256k1', () => {
|
|
||||||
const keypair = api.deriveKeypair(fixtures.secp256k1.seed)
|
|
||||||
assert.deepEqual(keypair, fixtures.secp256k1.keypair)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveKeypair - ed25519', () => {
|
|
||||||
const keypair = api.deriveKeypair(fixtures.ed25519.seed)
|
|
||||||
assert.deepEqual(keypair, fixtures.ed25519.keypair)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveKeypair - secp256k1 - validator', () => {
|
|
||||||
const keypair = api.deriveKeypair(fixtures.secp256k1.seed, {validator: true})
|
|
||||||
assert.deepEqual(keypair, fixtures.secp256k1.validatorKeypair)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveKeypair - ed25519 - validator', () => {
|
|
||||||
const keypair = api.deriveKeypair(fixtures.ed25519.seed, {validator: true})
|
|
||||||
assert.deepEqual(keypair, fixtures.ed25519.validatorKeypair)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveAddress - secp256k1 public key', () => {
|
|
||||||
const address = api.deriveAddress(fixtures.secp256k1.keypair.publicKey)
|
|
||||||
assert.strictEqual(address, fixtures.secp256k1.address)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveAddress - ed25519 public key', () => {
|
|
||||||
const address = api.deriveAddress(fixtures.ed25519.keypair.publicKey)
|
|
||||||
assert.strictEqual(address, fixtures.ed25519.address)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('sign - secp256k1', () => {
|
|
||||||
const privateKey = fixtures.secp256k1.keypair.privateKey
|
|
||||||
const message = fixtures.secp256k1.message
|
|
||||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
|
||||||
const signature = api.sign(messageHex, privateKey)
|
|
||||||
assert.strictEqual(signature, fixtures.secp256k1.signature)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('verify - secp256k1', () => {
|
|
||||||
const signature = fixtures.secp256k1.signature
|
|
||||||
const publicKey = fixtures.secp256k1.keypair.publicKey
|
|
||||||
const message = fixtures.secp256k1.message
|
|
||||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
|
||||||
assert(api.verify(messageHex, signature, publicKey))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('sign - ed25519', () => {
|
|
||||||
const privateKey = fixtures.ed25519.keypair.privateKey
|
|
||||||
const message = fixtures.ed25519.message
|
|
||||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
|
||||||
const signature = api.sign(messageHex, privateKey)
|
|
||||||
assert.strictEqual(signature, fixtures.ed25519.signature)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('verify - ed25519', () => {
|
|
||||||
const signature = fixtures.ed25519.signature
|
|
||||||
const publicKey = fixtures.ed25519.keypair.publicKey
|
|
||||||
const message = fixtures.ed25519.message
|
|
||||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
|
||||||
assert(api.verify(messageHex, signature, publicKey))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('deriveNodeAddress', () => {
|
|
||||||
const x = 'n9KHn8NfbBsZV5q8bLfS72XyGqwFt5mgoPbcTV4c6qKiuPTAtXYk'
|
|
||||||
const y = 'rU7bM9ENDkybaxNrefAVjdLTyNLuue1KaJ'
|
|
||||||
assert.strictEqual(api.deriveNodeAddress(x), y)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Random Address', () => {
|
|
||||||
const seed = api.generateSeed()
|
|
||||||
const keypair = api.deriveKeypair(seed)
|
|
||||||
const address = api.deriveAddress(keypair.publicKey)
|
|
||||||
assert(address[0] === 'r')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/* eslint-disable no-unused-expressions/no-unused-expressions */
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const api = require('ripple-address-codec')
|
|
||||||
|
|
||||||
function toHex(bytes) {
|
|
||||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
function toBytes(hex) {
|
|
||||||
return Buffer.from(hex, 'hex').toJSON().data
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('ripple-address-codec', function() {
|
|
||||||
function makeTest(type, base58, hex) {
|
|
||||||
it('can translate between ' + hex + ' and ' + base58 + ' (encode ' + type + ')', function() {
|
|
||||||
const actual = api['encode' + type](toBytes(hex))
|
|
||||||
assert.equal(actual, base58)
|
|
||||||
})
|
|
||||||
it('can translate between ' + base58 + ' and ' + hex + ' (decode ' + type + ')', function() {
|
|
||||||
const buf = api['decode' + type](base58)
|
|
||||||
assert.equal(toHex(buf), hex)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
makeTest('AccountID', 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
|
||||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072')
|
|
||||||
|
|
||||||
makeTest(
|
|
||||||
'NodePublic',
|
|
||||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
|
||||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
|
||||||
|
|
||||||
it('can decode arbitrary seeds', function() {
|
|
||||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
assert.equal(decoded.type, 'ed25519')
|
|
||||||
|
|
||||||
const decoded2 = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
assert.equal(toHex(decoded2.bytes), 'CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
|
||||||
assert.equal(decoded2.type, 'secp256k1')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can pass a type as second arg to encodeSeed', function() {
|
|
||||||
const edSeed = 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2'
|
|
||||||
const decoded = api.decodeSeed(edSeed)
|
|
||||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
assert.equal(decoded.type, 'ed25519')
|
|
||||||
assert.equal(api.encodeSeed(decoded.bytes, decoded.type), edSeed)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
'use strict' // eslint-disable-line strict
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const utils = require('../dist/utils')
|
|
||||||
|
|
||||||
describe('utils', () => {
|
|
||||||
it('hexToBytes - zero', () => {
|
|
||||||
assert.deepEqual(utils.hexToBytes('000000'), [0, 0, 0])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('hexToBytes - DEADBEEF', () => {
|
|
||||||
assert.deepEqual(utils.hexToBytes('DEADBEEF'), [222, 173, 190, 239])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
/* eslint-disable no-unused-expressions/no-unused-expressions */
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const api = require('ripple-address-codec')
|
|
||||||
|
|
||||||
function toHex(bytes) {
|
|
||||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
function toBytes(hex) {
|
|
||||||
return Buffer.from(hex, 'hex').toJSON().data
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('ripple-address-codec', function() {
|
|
||||||
|
|
||||||
describe('encodeSeed', function() {
|
|
||||||
|
|
||||||
it('encodes a secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('CF2DE378FBDD7E2EE87D486DFB5A7BFF'), 'secp256k1')
|
|
||||||
assert.equal(result, 'sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes low secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('00000000000000000000000000000000'), 'secp256k1')
|
|
||||||
assert.equal(result, 'sp6JS7f14BuwFY8Mw6bTtLKWauoUs')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes high secp256k1 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 'secp256k1')
|
|
||||||
assert.equal(result, 'saGwBRReqUNKuWNLpUAq8i8NkXEPN')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes an ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('4C3A1D213FBDFB14C7C28D609469B341'), 'ed25519')
|
|
||||||
assert.equal(result, 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes low ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('00000000000000000000000000000000'), 'ed25519')
|
|
||||||
assert.equal(result, 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('encodes high ed25519 seed', function() {
|
|
||||||
const result = api.encodeSeed(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 'ed25519')
|
|
||||||
assert.equal(result, 'sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('decodeSeed', function() {
|
|
||||||
|
|
||||||
it('can decode an Ed25519 seed', function() {
|
|
||||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
|
||||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
|
||||||
assert.equal(decoded.type, 'ed25519')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('can decode a secp256k1 seed', function() {
|
|
||||||
const decoded = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
|
||||||
assert.equal(toHex(decoded.bytes), 'CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
|
||||||
assert.equal(decoded.type, 'secp256k1')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('encodeAccountID', function() {
|
|
||||||
|
|
||||||
it('can encode an AccountID', function() {
|
|
||||||
const encoded = api.encodeAccountID(toBytes('BA8E78626EE42C41B46D46C3048DF3A1C3C87072'))
|
|
||||||
assert.equal(encoded, 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('decodeNodePublic', function() {
|
|
||||||
|
|
||||||
it('can decode a NodePublic', function() {
|
|
||||||
const decoded = api.decodeNodePublic('n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH')
|
|
||||||
assert.equal(toHex(decoded), '0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user