mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 00:52:01 -07:00
Compare commits
688
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7649eeffec | ||
|
|
f7d79dea28 | ||
|
|
2f1d6995a6 | ||
|
|
462885e97b | ||
|
|
24774a706e | ||
|
|
d5f7e6aa7f | ||
|
|
04e3346086 | ||
|
|
cb2a90fc93 | ||
|
|
66c2d3ab22 | ||
|
|
89d13fe241 | ||
|
|
ad0b1b5ddb | ||
|
|
ae30b89d68 | ||
|
|
2e88d26ff2 | ||
|
|
b1745d37e0 | ||
|
|
9733c07e04 | ||
|
|
430f5f015e | ||
|
|
9e76b34d05 | ||
|
|
61078b2e91 | ||
|
|
82733a7e1a | ||
|
|
3685ea1541 | ||
|
|
5f217af3e9 | ||
|
|
e2ca3cfb00 | ||
|
|
690520d72e | ||
|
|
36660f3bc9 | ||
|
|
80ee5c8d31 | ||
|
|
b7e04522ce | ||
|
|
19f243428d | ||
|
|
16079c8d04 | ||
|
|
6743ca5fa9 | ||
|
|
ec046d3ef7 | ||
|
|
85405d8e70 | ||
|
|
1989b72b2a | ||
|
|
f1f2bf42d9 | ||
|
|
348a073714 | ||
|
|
e822b91e2c | ||
|
|
29a5173036 | ||
|
|
0bffff6f9b | ||
|
|
8c603d3886 | ||
|
|
542e59461f | ||
|
|
0912aadc1a | ||
|
|
638804de42 | ||
|
|
fe33ba3381 | ||
|
|
6f913cf3ad | ||
|
|
4d94ad13ff | ||
|
|
c02080d12a | ||
|
|
cc1809b242 | ||
|
|
13a7917d9b | ||
|
|
1573e0bc4c | ||
|
|
3fb5377acd | ||
|
|
a1ba2ac2f7 | ||
|
|
7dc29230da | ||
|
|
3b013b6215 | ||
|
|
9129c594dd | ||
|
|
64d99531de | ||
|
|
c9a8c5008e | ||
|
|
7f08789c6a | ||
|
|
63dcddb3ab | ||
|
|
c2f150bf81 | ||
|
|
889ffcfe1d | ||
|
|
26a6175889 | ||
|
|
07de91afa2 | ||
|
|
246da25f91 | ||
|
|
f24d36ccee | ||
|
|
f80cce96b1 | ||
|
|
1c6939ab43 | ||
|
|
bcb236a66b | ||
|
|
b66ff3cb59 | ||
|
|
2dad11180b | ||
|
|
22e13ef654 | ||
|
|
4dabdac792 | ||
|
|
857b57240e | ||
|
|
7b9aba3738 | ||
|
|
2c49af2de3 | ||
|
|
3c0590ba23 | ||
|
|
b20a034bc9 | ||
|
|
12e5143d9a | ||
|
|
8e3fa6dcf1 | ||
|
|
76442208a2 | ||
|
|
2cf8905852 | ||
|
|
d134aff9f4 | ||
|
|
005b628301 | ||
|
|
f3c2168435 | ||
|
|
71b8bb089d | ||
|
|
2333229e54 | ||
|
|
13c69593b2 | ||
|
|
8316c4eabb | ||
|
|
30cf010e80 | ||
|
|
f28eeea5b0 | ||
|
|
9758eb9995 | ||
|
|
4260a74d91 | ||
|
|
01625abb37 | ||
|
|
b34040cd08 | ||
|
|
01a4d0f1aa | ||
|
|
7c7c6395e1 | ||
|
|
3408a8fd3b | ||
|
|
86a1cadfac | ||
|
|
7000a83a37 | ||
|
|
b331450473 | ||
|
|
af7c8ffa6d | ||
|
|
2e04548ade | ||
|
|
412fa5e1ce | ||
|
|
a08a4236d6 | ||
|
|
e700a22560 | ||
|
|
4573c63ca3 | ||
|
|
bd18e8a755 | ||
|
|
cf8f48ffe1 | ||
|
|
fc96b04a64 | ||
|
|
661a759f80 | ||
|
|
ba915e84cf | ||
|
|
eae7172e3d | ||
|
|
6dd2a0590f | ||
|
|
3152e87b5d | ||
|
|
4b66f3841d | ||
|
|
9f98a47682 | ||
|
|
0833e8ca78 | ||
|
|
e01a18b65e | ||
|
|
055cd7feca | ||
|
|
906d9b2094 | ||
|
|
9326fd6e59 | ||
|
|
1d91eed5df | ||
|
|
62e56c832b | ||
|
|
d5b7e4ab1d | ||
|
|
2137a75fce | ||
|
|
8966cac210 | ||
|
|
9d32f828bc | ||
|
|
91a7106818 | ||
|
|
44e671dfbe | ||
|
|
979fb481de | ||
|
|
66253ed5a6 | ||
|
|
6ef5ac0f7c | ||
|
|
21c322d0df | ||
|
|
1b0e9ef0ff | ||
|
|
78776bee93 | ||
|
|
a6e3155f37 | ||
|
|
47f60bccde | ||
|
|
d0a3b27682 | ||
|
|
372d1b099d | ||
|
|
dbfaf9c4ab | ||
|
|
eaa08f74e7 | ||
|
|
9ac620ee64 | ||
|
|
ac0a4b0407 | ||
|
|
686012037f | ||
|
|
cfb7b9f779 | ||
|
|
06e5af4b5d | ||
|
|
249e93a3f4 | ||
|
|
761a2d226c | ||
|
|
5e5e636831 | ||
|
|
2adc2dfebb | ||
|
|
f9bc44ccb7 | ||
|
|
298b8f56ff | ||
|
|
139da8cf7b | ||
|
|
08e03c14d8 | ||
|
|
7da010e7af | ||
|
|
7b6eebe7c7 | ||
|
|
843f5eacef | ||
|
|
416e302ca5 | ||
|
|
1c66da70b6 | ||
|
|
59e92adf0b | ||
|
|
f369411ccb | ||
|
|
0504cf7b8d | ||
|
|
88ae47cade | ||
|
|
d144b13500 | ||
|
|
30d58bc337 | ||
|
|
651ae3f6ca | ||
|
|
04cd90f0f4 | ||
|
|
552ee31b79 | ||
|
|
affa9619bb | ||
|
|
39bff131af | ||
|
|
a84fecfe41 | ||
|
|
0736c9f2a1 | ||
|
|
aa898b31e7 | ||
|
|
29c3381cc5 | ||
|
|
f7ba6bb3ae | ||
|
|
62056e1ac4 | ||
|
|
a60e2fa2da | ||
|
|
37fe679800 | ||
|
|
4250d841c5 | ||
|
|
31079afc35 | ||
|
|
62b44d5058 | ||
|
|
150295cb43 | ||
|
|
5de2f71ea5 | ||
|
|
50a1d60229 | ||
|
|
f3e25a0338 | ||
|
|
640c582724 | ||
|
|
b472fa2673 | ||
|
|
ea83335fd2 | ||
|
|
3050819784 | ||
|
|
4ca9b1c00b | ||
|
|
bbc1a45a9e | ||
|
|
469d08e2b1 | ||
|
|
dcadad189b | ||
|
|
02f81ee203 | ||
|
|
972bf604d1 | ||
|
|
82f084edec | ||
|
|
4764463fc5 | ||
|
|
857458db57 | ||
|
|
5f8f05d194 | ||
|
|
e455d9efec | ||
|
|
ad4e05c289 | ||
|
|
0c1eda8e5a | ||
|
|
f15bc7e454 | ||
|
|
e9113d864e | ||
|
|
277c9ce0fd | ||
|
|
e0c07d1542 | ||
|
|
56bc942e87 | ||
|
|
34b9cefb65 | ||
|
|
6a35091eec | ||
|
|
02e3ff4dcd | ||
|
|
64f8adea25 | ||
|
|
bcf817f5c7 | ||
|
|
541377e548 | ||
|
|
84b44b299a | ||
|
|
b6456df1f4 | ||
|
|
b1826c04cb | ||
|
|
71755964c8 | ||
|
|
bd1c3b622d | ||
|
|
bdced13230 | ||
|
|
c925bfce46 | ||
|
|
056a47f6b2 | ||
|
|
51038681d3 | ||
|
|
1a2f144baf | ||
|
|
6d70568082 | ||
|
|
0b1cf57852 | ||
|
|
48f7f75c9c | ||
|
|
0bc96fded5 | ||
|
|
9869211572 | ||
|
|
1056c3724c | ||
|
|
6b838ef9cd | ||
|
|
f09573554d | ||
|
|
4a62ae64de | ||
|
|
78de9e390e | ||
|
|
7d8d0c2d69 | ||
|
|
656b0f6809 | ||
|
|
48808aec14 | ||
|
|
d50f4a0d50 | ||
|
|
f09f96d365 | ||
|
|
7f4745f83c | ||
|
|
dd5de1424c | ||
|
|
30604591bd | ||
|
|
aeb1efe23e | ||
|
|
6e43f795d7 | ||
|
|
e07a5b9da0 | ||
|
|
5ada76eace | ||
|
|
e605f6f386 | ||
|
|
eb9d0bea2b | ||
|
|
f185f5de16 | ||
|
|
d795174916 | ||
|
|
1294bc2149 | ||
|
|
8117448fd3 | ||
|
|
c7b51a38ff | ||
|
|
07d490134a | ||
|
|
a8a4eb68a6 | ||
|
|
c05e2f14e2 | ||
|
|
3540d98910 | ||
|
|
d0129c01f3 | ||
|
|
ad40607e5b | ||
|
|
a4f0ee6e42 | ||
|
|
0b70ea108b | ||
|
|
cef9a3cf52 | ||
|
|
c216f6c075 | ||
|
|
b53f6bcfc6 | ||
|
|
cbc834cc1a | ||
|
|
1cb5fe069b | ||
|
|
425e2cbbc1 | ||
|
|
9e92f77523 | ||
|
|
dc85bb4cfe | ||
|
|
92960b6957 | ||
|
|
6f34ea286a | ||
|
|
26f39023c4 | ||
|
|
4a3baca72d | ||
|
|
fe37c3ca43 | ||
|
|
ff4ffab670 | ||
|
|
400e7b31a8 | ||
|
|
360fca0475 | ||
|
|
ef7056ee7a | ||
|
|
d221077786 | ||
|
|
046ab78c6a | ||
|
|
29e90915e7 | ||
|
|
3d85dbfb22 | ||
|
|
2c0fa9dfbd | ||
|
|
6ca1d729dc | ||
|
|
c095af8a29 | ||
|
|
89e4ea1635 | ||
|
|
85a34a8305 | ||
|
|
ef6cfe4409 | ||
|
|
a29d6ed26a | ||
|
|
fa58a0764c | ||
|
|
2c2fbeaa46 | ||
|
|
1e4cd5f99e | ||
|
|
52c1ebdafe | ||
|
|
2e56d66f48 | ||
|
|
9cf6c7e7a7 | ||
|
|
43043672c6 | ||
|
|
4f4b2c864d | ||
|
|
951fea8f9b | ||
|
|
6d75dceffb | ||
|
|
3a491c7aba | ||
|
|
f03c020e4f | ||
|
|
4aeb1148e5 | ||
|
|
f03ee13a1e | ||
|
|
cb98773d6a | ||
|
|
c593d68ee5 | ||
|
|
dc7d3a9339 | ||
|
|
6f4cc0f439 | ||
|
|
7aa0fa865e | ||
|
|
e8b6d7fb36 | ||
|
|
b13ef94ea3 | ||
|
|
912f899f4e | ||
|
|
9063d8da0b | ||
|
|
f4e2bd96f0 | ||
|
|
1c898a4ad3 | ||
|
|
c4db813f76 | ||
|
|
38b96b441f | ||
|
|
c94f78f510 | ||
|
|
6de2b712ec | ||
|
|
6f681fd7b0 | ||
|
|
9a82effb2b | ||
|
|
955a5e428e | ||
|
|
44c3bf44f9 | ||
|
|
31ce84abb0 | ||
|
|
40fa4b6bc1 | ||
|
|
8c5e111d60 | ||
|
|
8965dfc57f | ||
|
|
93904a632f | ||
|
|
851d4c404c | ||
|
|
44bac45c32 | ||
|
|
95ee8054be | ||
|
|
547b28cac8 | ||
|
|
40ba334a41 | ||
|
|
ffd8ccaf2b | ||
|
|
9a16c0c6d6 | ||
|
|
52a891fd78 | ||
|
|
c09df08429 | ||
|
|
4dc88c0af3 | ||
|
|
85caa9556b | ||
|
|
bbdc417eb2 | ||
|
|
cc3bc002e7 | ||
|
|
a75e1ab633 | ||
|
|
8c5f1e2274 | ||
|
|
c30e76c942 | ||
|
|
a575621c50 | ||
|
|
d3a0c5948a | ||
|
|
61bb509195 | ||
|
|
a18e549694 | ||
|
|
7d53f47d8a | ||
|
|
e8e615795d | ||
|
|
5759b096d5 | ||
|
|
5b300f68b2 | ||
|
|
3cce6f9754 | ||
|
|
3da04d9327 | ||
|
|
79091815a2 | ||
|
|
258144d26d | ||
|
|
4c4a45841b | ||
|
|
010afd10a3 | ||
|
|
82b4d2ab9e | ||
|
|
8eb6a21dc2 | ||
|
|
5f2a762d4f | ||
|
|
24e2c78d03 | ||
|
|
eab02fdbc9 | ||
|
|
f7bd9c75db | ||
|
|
2a96f3a4f5 | ||
|
|
3dc4a8ba6f | ||
|
|
65916f2c5c | ||
|
|
18188bfdce | ||
|
|
45acc2b0b7 | ||
|
|
31913c3fb7 | ||
|
|
2311643398 | ||
|
|
7501aa0238 | ||
|
|
92a89a543d | ||
|
|
cdf8171994 | ||
|
|
385baf0afd | ||
|
|
9a9aababd2 | ||
|
|
c228acd28a | ||
|
|
c1c140bdce | ||
|
|
54d9b80f26 | ||
|
|
961a190309 | ||
|
|
2d2a91dfde | ||
|
|
fbac18d456 | ||
|
|
62ae67ceab | ||
|
|
8e64d854cf | ||
|
|
5fe8e515ff | ||
|
|
f11297339b | ||
|
|
61d52c8ebe | ||
|
|
d89bbe0bf6 | ||
|
|
2a1c2500e6 | ||
|
|
8879a5170a | ||
|
|
b29cb7c12b | ||
|
|
69fa10d3a6 | ||
|
|
d16431d165 | ||
|
|
faa2972460 | ||
|
|
5d3ea2b559 | ||
|
|
40fa6440f4 | ||
|
|
ce951afddb | ||
|
|
d2e847d3fb | ||
|
|
fe697455c0 | ||
|
|
25832d52fd | ||
|
|
9d0840c2fe | ||
|
|
74dc3e7e1d | ||
|
|
171786a470 | ||
|
|
5cbd685060 | ||
|
|
ef05aff7a3 | ||
|
|
3f01fdba7c | ||
|
|
232747280e | ||
|
|
e96da1615b | ||
|
|
9538501f01 | ||
|
|
0a4fee5716 | ||
|
|
cec79be4fa | ||
|
|
c839f0b891 | ||
|
|
24d629ebe1 | ||
|
|
f729918385 | ||
|
|
b7ab02c0ed | ||
|
|
1c291880e0 | ||
|
|
09b79c4d3e | ||
|
|
828f6a1b1c | ||
|
|
1b8727da55 | ||
|
|
f84862d875 | ||
|
|
179d924899 | ||
|
|
676fb85abf | ||
|
|
d59c1fe445 | ||
|
|
0a34611557 | ||
|
|
c13b029e99 | ||
|
|
b038e9a7fd | ||
|
|
7b728fb45d | ||
|
|
d4dbce8df0 | ||
|
|
2419aeceaa | ||
|
|
158b009069 | ||
|
|
f3c3250a69 | ||
|
|
5ae4db88c3 | ||
|
|
33e161df26 | ||
|
|
81c910361c | ||
|
|
9751a39538 | ||
|
|
7058f77ee9 | ||
|
|
ad580d67a4 | ||
|
|
acc0300a44 | ||
|
|
4e1b806230 | ||
|
|
357e350176 | ||
|
|
f6140acc76 | ||
|
|
df65b12aab | ||
|
|
754b4cd807 | ||
|
|
3c6079e54c | ||
|
|
18621cd76c | ||
|
|
456159a4cc | ||
|
|
33e723befe | ||
|
|
30fdee1920 | ||
|
|
feb336c331 | ||
|
|
8bacc0a991 | ||
|
|
d22f1ec8a6 | ||
|
|
7eb3a796b8 | ||
|
|
9919f811b0 | ||
|
|
e7854481a0 | ||
|
|
0b406c467e | ||
|
|
e41217b710 | ||
|
|
c22ce8d57d | ||
|
|
bf3612583e | ||
|
|
ef56452ec9 | ||
|
|
b3be470c0e | ||
|
|
7274475898 | ||
|
|
7bb347a833 | ||
|
|
fadceedd00 | ||
|
|
7f4ef60f8e | ||
|
|
81e54e0590 | ||
|
|
1d5e3da976 | ||
|
|
2cff305f46 | ||
|
|
9495381cf4 | ||
|
|
32f89346c3 | ||
|
|
e8582fd0ab | ||
|
|
7049b96df1 | ||
|
|
9cc6824591 | ||
|
|
bb82d520ed | ||
|
|
fef05124f1 | ||
|
|
978db07526 | ||
|
|
68bab5dd26 | ||
|
|
20e685206d | ||
|
|
e90afac701 | ||
|
|
96f96f837e | ||
|
|
03be1d5838 | ||
|
|
6b6bfa2ae2 | ||
|
|
da3af3656a | ||
|
|
26fdb5c5d5 | ||
|
|
86d71d0f49 | ||
|
|
86351aaf4f | ||
|
|
36432af7d5 | ||
|
|
95c88b5f29 | ||
|
|
28f563b58c | ||
|
|
79ea4b77b9 | ||
|
|
d37a2f8139 | ||
|
|
3b873b6064 | ||
|
|
687b5bca02 | ||
|
|
b80697500c | ||
|
|
626d27033e | ||
|
|
ce2ee3fc1b | ||
|
|
4768dcadc6 | ||
|
|
5fef09edbf | ||
|
|
37109cd7ef | ||
|
|
83d523260d | ||
|
|
8768a810c7 | ||
|
|
8832bfc8df | ||
|
|
a5208110b9 | ||
|
|
99ad7cd87f | ||
|
|
68ae084fb0 | ||
|
|
b0b0e17abc | ||
|
|
a54306e382 | ||
|
|
1860232fa6 | ||
|
|
d8437cf3fb | ||
|
|
338788c46d | ||
|
|
cad2c6378e | ||
|
|
2e6938e21f | ||
|
|
1e13c71715 | ||
|
|
d4b0807d16 | ||
|
|
611c7977ef | ||
|
|
39ac634675 | ||
|
|
278a54bd9b | ||
|
|
30b1f71d81 | ||
|
|
e7d7fc1019 | ||
|
|
524da532e2 | ||
|
|
aef56c5c14 | ||
|
|
e2ba265878 | ||
|
|
2472dc3606 | ||
|
|
2df36124b4 | ||
|
|
c422d8db34 | ||
|
|
ec2d8ea80a | ||
|
|
fc527834ea | ||
|
|
3ceebc1f12 | ||
|
|
2ff46c1f76 | ||
|
|
6926bbe113 | ||
|
|
360f1509dd | ||
|
|
d5d2e43ad0 | ||
|
|
1012f4c1bf | ||
|
|
3cedd251e5 | ||
|
|
bdb4de0353 | ||
|
|
1de0bdcf14 | ||
|
|
3a9e4f64b5 | ||
|
|
ea57f52b20 | ||
|
|
903aae1091 | ||
|
|
1af766cbda | ||
|
|
60ee10ef4d | ||
|
|
2d4bc6127e | ||
|
|
a2c2d41833 | ||
|
|
0e7db7bf39 | ||
|
|
46de4f6a25 | ||
|
|
2acf47a550 | ||
|
|
7fea5c2630 | ||
|
|
b297743521 | ||
|
|
6c33222172 | ||
|
|
9a36a8261a | ||
|
|
1433c77fcc | ||
|
|
b1c3311d50 | ||
|
|
b3e224e481 | ||
|
|
06e2dad45c | ||
|
|
d475f2220f | ||
|
|
10520a2e08 | ||
|
|
05b654d54a | ||
|
|
3c4051c3a5 | ||
|
|
0f3cc1e9e8 | ||
|
|
b9873f7ed8 | ||
|
|
82cd9270fa | ||
|
|
ac6b45b162 | ||
|
|
66a99c0560 | ||
|
|
17e4a32ff4 | ||
|
|
6a430b4da6 | ||
|
|
77ab090bc2 | ||
|
|
fdff4a1afb | ||
|
|
12701cf3dc | ||
|
|
1032625ac8 | ||
|
|
586c2972da | ||
|
|
3d41e41192 | ||
|
|
af8e0cdf45 | ||
|
|
10f6f58ec5 | ||
|
|
fd9f399f41 | ||
|
|
98582edd0b | ||
|
|
07b7cb0afd | ||
|
|
dda9658375 | ||
|
|
358dad533d | ||
|
|
7a63573143 | ||
|
|
333ced5c79 | ||
|
|
c6ceae1283 | ||
|
|
4e8a376720 | ||
|
|
cee348cb3e | ||
|
|
75e56e7aa7 | ||
|
|
d6ae6ffb6b | ||
|
|
12f6547167 | ||
|
|
d1847d2eca | ||
|
|
1f01e6ae47 | ||
|
|
1375601a81 | ||
|
|
e97a8e7cf5 | ||
|
|
3df211301c | ||
|
|
a81dec068c | ||
|
|
4af6716631 | ||
|
|
98f7cb8f3a | ||
|
|
348a67a79b | ||
|
|
a9c128f96b | ||
|
|
bdf1c50c9d | ||
|
|
ccc545b1be | ||
|
|
c6301055fa | ||
|
|
13d9f89a82 | ||
|
|
f5de593f89 | ||
|
|
3b0ec95fba | ||
|
|
0a328090ea | ||
|
|
315cb4f254 | ||
|
|
28f08f02ac | ||
|
|
ad5fabf2e6 | ||
|
|
f8f6db069b | ||
|
|
df620e0302 | ||
|
|
19f2e87528 | ||
|
|
5949e0f6f8 | ||
|
|
617bb16ddb | ||
|
|
83f14f5522 | ||
|
|
b8e22e1c99 | ||
|
|
72d560e407 | ||
|
|
248b2fa2a6 | ||
|
|
563cb297fe | ||
|
|
4c020240e1 | ||
|
|
e070409a95 | ||
|
|
fbbd855145 | ||
|
|
731987b35b | ||
|
|
1e8e7b79f2 | ||
|
|
b512b222dd | ||
|
|
650e3b53fc | ||
|
|
099eb25e88 | ||
|
|
12a7c666c6 | ||
|
|
e482e09e77 | ||
|
|
7ca6498ca0 | ||
|
|
f89b52ac86 | ||
|
|
905658ca28 | ||
|
|
b0c91c407d | ||
|
|
d2c68efc0c | ||
|
|
7990d0bffd | ||
|
|
c0eaaf77c8 | ||
|
|
f424047a69 | ||
|
|
fc0d8b7f8c | ||
|
|
4551d1ca99 | ||
|
|
b8083c6b88 | ||
|
|
0ef8555837 | ||
|
|
a7564ce611 | ||
|
|
601b87cb4a | ||
|
|
edfcc92b7b | ||
|
|
a32e0da9be | ||
|
|
a1bf617d91 | ||
|
|
33d2da503d | ||
|
|
c2ff1e2d68 | ||
|
|
cc9e8c0319 | ||
|
|
83389129d6 | ||
|
|
360d56e283 | ||
|
|
53a0da4ef1 | ||
|
|
6d4534f177 | ||
|
|
90ec06adaa | ||
|
|
a6759cdae5 | ||
|
|
078f157e4e | ||
|
|
98029f74f8 | ||
|
|
9348ccb7de | ||
|
|
6a6f5fa5fa | ||
|
|
9863fad6a6 | ||
|
|
3a2f38bcf0 | ||
|
|
bc5c7052a1 | ||
|
|
89d5171e92 | ||
|
|
c80a0dcde4 | ||
|
|
a2f7ae7fb4 | ||
|
|
4c2c7a4f6f | ||
|
|
6efb08565a | ||
|
|
8a8dcbe593 | ||
|
|
ae58895d71 | ||
|
|
a50ace320b | ||
|
|
b1d583cfe4 | ||
|
|
0b86a19726 | ||
|
|
eb3bc7bcff | ||
|
|
c5361e0516 | ||
|
|
54d9233f41 | ||
|
|
37a5f0e6fa | ||
|
|
2180b14e1f | ||
|
|
40e1704572 | ||
|
|
9539119c57 | ||
|
|
b397381c7d | ||
|
|
6cdb9a0867 | ||
|
|
01f45d7103 | ||
|
|
ba38d3e107 | ||
|
|
cbb0d0a8ca | ||
|
|
7ec8442540 | ||
|
|
be3094fc77 | ||
|
|
d35f0e5473 | ||
|
|
79088fc68d | ||
|
|
fe84729737 | ||
|
|
2ec72e73c1 | ||
|
|
a2c121bf3b | ||
|
|
fd6f58fd45 | ||
|
|
e8b2f88ff5 | ||
|
|
4a11db7c45 | ||
|
|
af118f8a62 | ||
|
|
1f41b67e3b |
@@ -0,0 +1,30 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
# A special property that should be specified at the top of the file outside of
|
||||
# any sections. Set to true to stop .editor config file search on current file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
# Indentation style
|
||||
# Possible values - tab, space
|
||||
indent_style = space
|
||||
|
||||
# Indentation size in single-spaced characters
|
||||
# Possible values - an integer, tab
|
||||
indent_size = 2
|
||||
|
||||
# Line ending file format
|
||||
# Possible values - lf, crlf, cr
|
||||
end_of_line = lf
|
||||
|
||||
# File character encoding
|
||||
# Possible values - latin1, utf-8, utf-16be, utf-16le
|
||||
charset = utf-8
|
||||
|
||||
# Denotes whether to trim whitespace at the end of lines
|
||||
# Possible values - true, false
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Denotes whether file should end with a newline
|
||||
# Possible values - true, false
|
||||
insert_final_newline = true
|
||||
+6
-51
@@ -1,55 +1,10 @@
|
||||
{
|
||||
"root": true,
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
"extends": "standard",
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"no-debugger": ["warn"],
|
||||
"no-regex-spaces": ["error"],
|
||||
"no-unsafe-negation": ["error"],
|
||||
"curly": ["error", "multi-or-nest", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"dot-notation": ["error"],
|
||||
"eqeqeq": ["error", "smart"],
|
||||
"no-else-return": ["error"],
|
||||
"no-extra-bind": ["error"],
|
||||
"no-extra-label": ["error"],
|
||||
"no-floating-decimal": ["error"],
|
||||
"no-implicit-coercion": ["error", { "allow": ["!!"] }],
|
||||
"wrap-iife": ["error", "inside"],
|
||||
"strict": ["error", "global"],
|
||||
"func-call-spacing": ["error", "never"],
|
||||
"comma-style": ["error", "last"],
|
||||
"keyword-spacing": ["error"],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"new-parens": ["error"],
|
||||
"no-lonely-if": ["error"],
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }],
|
||||
"no-whitespace-before-property": ["error"],
|
||||
"semi": ["error", "never"],
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"arrow-parens": ["error", "as-needed"],
|
||||
"arrow-spacing": ["error"],
|
||||
"no-useless-computed-key": ["error"],
|
||||
"no-useless-rename": ["error"],
|
||||
"no-var": ["off"],
|
||||
"prefer-spread": ["off"],
|
||||
"prefer-template": ["error"],
|
||||
"rest-spread-spacing": ["error", "never"],
|
||||
"prefer-const": ["warn", { "destructuring": "all" }],
|
||||
"no-unreachable": ["warn"],
|
||||
"no-unused-vars": ["warn", { "args": "none" }],
|
||||
|
||||
"prettier/prettier": [
|
||||
"warn",
|
||||
{
|
||||
"printWidth": 80,
|
||||
"trailingComma": "none",
|
||||
"singleQuote": false,
|
||||
"semi": false
|
||||
}
|
||||
]
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ node_modules/*
|
||||
.nyc_output/*
|
||||
wallet-info.txt
|
||||
wallet.json
|
||||
integration-test-sweet.sh
|
||||
integration-test-ss.sh
|
||||
integration-test-bitfinex.sh
|
||||
integration-test-pearson.sh
|
||||
|
||||
docs/
|
||||
coverage/
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"srcDir": "",
|
||||
"destDir": "",
|
||||
"files": "**/*.js",
|
||||
"command": "npm run lint"
|
||||
}
|
||||
]
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2020 Chris Troutner
|
||||
Copyright 2022-2026 Permissionless Software Foundation contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -1,134 +1,141 @@
|
||||
# bch-js
|
||||
|
||||
[](https://travis-ci.org/christroutner/bch-js)
|
||||
[](https://www.npmjs.com/package/@chris.troutner/bch-js)
|
||||
[](https://npmjs.org/package/@chris.troutner/bch-js)
|
||||
[](https://github.com/christroutner/bch-js/blob/master/LICENSE.md)
|
||||
[](https://www.npmjs.com/package/@psf/bch-js)
|
||||
[](https://npmjs.org/package/@psf/bch-js)
|
||||
[](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
|
||||
[](https://github.com/feross/standard) [](https://gitter.im/Permissionless-Software-Foundation/bch-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[bch-js](https://www.npmjs.com/package/@chris.troutner/bch-js) is a JavaScript npm library for creating web and mobile apps for interacting with the Bitcoin Cash (BCH) blockchain. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Find out more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
|
||||
|
||||
### Quick Start Videos:
|
||||
Here are two YouTube walk-through videos to help you get started:
|
||||
- [Introduction to bch-js and the bch-js-examples repository](https://youtu.be/GD2i1ZUiyrk)
|
||||
- [Working with the FullStack.cash JWT token](https://youtu.be/GD2i1ZUiyrk)
|
||||
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchain. bch-js contains a toolbox of handy tools, and an easy API for talking with [psf-bch-api REST API](https://github.com/Permissionless-Software-Foundation/psf-bch-api). [FullStack.cash](https://fullstack.cash) offers paid cloud access to psf-bch-api. You can run your own infrastructure by following documentation on [CashStack.info](https://cashstack.info).
|
||||
|
||||
### Quick Links
|
||||
- [npm Library](https://www.npmjs.com/package/@chris.troutner/bch-js)
|
||||
- [Documentation](https://bchjs.fullstack.cash/)
|
||||
- [Examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples)
|
||||
- [api.fullstack.cash](https://api.fullstack.cash) - The REST API this library talks to by default.
|
||||
- [FullStack.cash Account](https://fullstack.cash/login) - Get your API key to unlock increased rate limits.
|
||||
- [FullStack.cash](https://fullstack.cash) - cloud-based infrastructure for application
|
||||
developers.
|
||||
- [Permissionless Software Foundation](https://psfoundation.cash) - The organization that maintains this library.
|
||||
|
||||
- [Code Examples](https://github.com/Permissionless-Software-Foundation/psf-js-examples) using bch-js
|
||||
- [npm Library](https://www.npmjs.com/package/@psf/bch-js)
|
||||
- [API Reference](https://bchjs.fullstack.cash/)
|
||||
- [x402-bch.fullstack.cash](https://x402-bch.fullstack.cash) - The REST API this library talks to by default.
|
||||
- [FullStack.cash](https://fullstack.cash) - cloud-based infrastructure for application developers.
|
||||
- [CashStack.info](https://cashstack.info) - bch-js is part of the Cash Stack, a JavaScript framework for writing web 2 and web 3 business applications.
|
||||
- [Permissionless Software Foundation](https://psfoundation.info) - The organization that maintains this library.
|
||||
|
||||
|
||||
### Quick Notes
|
||||
|
||||
- Install library: `npm install @chris.troutner/bch-js`
|
||||
- Install library: `npm install @psf/bch-js`
|
||||
|
||||
- Instantiate the library in your code:
|
||||
```
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
let bchjs = new BCHJS()
|
||||
|
||||
// testnet
|
||||
bchjs = new BCHJS({ restURL: 'https://tapi.fullstack.cash/v3/' })
|
||||
```javascript
|
||||
import BCHJS from "@psf/bch-js"
|
||||
let bchjs = new BCHJS() // Defaults to BCHN network.
|
||||
```
|
||||
|
||||
This is a fork of the [BITBOX SDK](https://github.com/Bitcoin-com/bitbox-sdk) (which is maintained by Bitcoin.com). This library is intended to be paired with
|
||||
the [bch-api](https://github.com/christroutner/bch-api) REST API.
|
||||
This library is intended to be paired with
|
||||
the [psf-bch-api](https://github.com/Permissionless-Software-Foundation/psf-bch-api) REST API, and the infrastructure provided by [FullStack.cash](https://fullstack.cash). The `restURL` property can be changed to work with different Bitcoin Cash networks:
|
||||
|
||||
If you need a backward-compatible instance of this library, you can use a
|
||||
'shim'. Do it like this:
|
||||
```
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
const bitbox = BCHJS.BitboxShim({ restURL: 'https://api.fullstack.cash/v3/' })
|
||||
```
|
||||
- BCHN Mainnet REST API server: https://x402-bch.fullstack.cash/v7/
|
||||
- Check server status: https://metrics.fullstack.cash
|
||||
|
||||
### API Key
|
||||
The [bch-api](https://github.com/christroutner/bch-api) REST API hosted by [FullStack.cash](https://fullstack.cash) uses JWT tokens to pay for increased
|
||||
rate limits when interacting with the back end server. See [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) if you want to understand the system-as-a-whole. The JWT token can be fed to bch-js *implicitly* or *explicitly*.
|
||||
## Configuration
|
||||
|
||||
- Implicitly: bch-js will detect your JWT token by setting the `BCHJSTOKEN` environment variable.
|
||||
- Explicitly: You can directly feed in the JWT token with the `apiToken` property when instantiating the library. Here is an example:
|
||||
bch-js can be configured through constructor options or environment variables. Configuration options passed to the constructor take precedence over environment variables.
|
||||
|
||||
```
|
||||
const BCHJS = require("@chris.troutner/bch-js")
|
||||
let bchjs = new BCHJS({
|
||||
restURL: 'https://api.fullstack.cash/v3/',
|
||||
apiToken: 'eyJhbGciO...' // Your JWT token here.
|
||||
### Constructor Options
|
||||
|
||||
When instantiating BCHJS, you can pass a configuration object:
|
||||
|
||||
```javascript
|
||||
import BCHJS from "@psf/bch-js"
|
||||
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
bearerToken: 'your-bearer-token',
|
||||
wif: 'your-private-key-wif',
|
||||
paymentAmountSats: 20000,
|
||||
bchServerURL: 'https://bch.fullstack.cash'
|
||||
})
|
||||
```
|
||||
|
||||
### Gatsby
|
||||
bch-js is included in this [gatsby-ipfs-template](https://github.com/Permissionless-Software-Foundation/gatsby-ipfs-template) for building uncensorable web apps that can interact with the blockchain. When building a Gatsby (or other front-end app that uses Webpack), you'll need to add these lines to your `gatsby-node.js` file, as per [this issue](https://github.com/gatsbyjs/gatsby/issues/564):
|
||||
```
|
||||
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||
actions.setWebpackConfig({
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
})
|
||||
}
|
||||
### Configuration Options
|
||||
|
||||
| Option | Type | Required | Default | Description |
|
||||
|--------|------|----------|---------|-------------|
|
||||
| `restURL` | string | Yes* | - | The REST API server URL for making API calls. Must include trailing slash. *Required unless `RESTURL` environment variable is set. |
|
||||
| `bearerToken` | string | No | `''` | Bearer token for authentication with the REST API server. |
|
||||
| `wif` | string | No | `''` | Private key in WIF format. When provided, enables automatic x402 payment handling. |
|
||||
| `paymentAmountSats` | number | No | `20000` | Default amount of satoshis to send when making x402 payments. |
|
||||
| `bchServerURL` | string | No | `'https://bch.fullstack.cash'` | BCH server URL used for broadcasting payment transactions to the blockchain. This is separate from `restURL` and is specifically for x402 payment processing. |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
You can also configure bch-js using environment variables:
|
||||
|
||||
| Environment Variable | Config Option | Description |
|
||||
|---------------------|---------------|-------------|
|
||||
| `RESTURL` | `restURL` | REST API server URL for making API calls. |
|
||||
| `BCHJSBEARERTOKEN` | `bearerToken` | Bearer token for API authentication. |
|
||||
| `BCHJSWIF` | `wif` | Private key in WIF format for x402 payments. |
|
||||
| `BCHJSBCHSERVERURL` | `bchServerURL` | BCH server URL for x402 payment transactions. |
|
||||
|
||||
### Understanding restURL vs bchServerURL
|
||||
|
||||
These two configuration options serve different purposes:
|
||||
|
||||
- **`restURL`**: The REST API server used for all regular API calls (utxo queries, transaction history, etc.). This can be any bch-api compatible server, such as `https://x402-bch.fullstack.cash/v5/` or `https://bch.fullstack.cash/v5/`.
|
||||
|
||||
- **`bchServerURL`**: The BCH infrastructure server used specifically for broadcasting x402 payment transactions to the blockchain. This defaults to `https://bch.fullstack.cash` and should typically remain unchanged unless you have specific infrastructure requirements.
|
||||
|
||||
**Example Use Case**: Most users will use `https://x402-bch.fullstack.cash/v5/` as their `restURL` to access x402-protected APIs. However, when bch-js needs to make an x402 payment, it uses the `bchServerURL` (default: `https://bch.fullstack.cash`) to broadcast the payment transaction. This ensures payment transactions are sent through a reliable BCH infrastructure endpoint.
|
||||
|
||||
```javascript
|
||||
// Use x402-bch server for API calls, but bch.fullstack.cash for payments
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
wif: 'your-private-key-wif'
|
||||
// bchServerURL defaults to 'https://bch.fullstack.cash'
|
||||
})
|
||||
|
||||
// Or explicitly set both
|
||||
const bchjs2 = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
bchServerURL: 'https://bch.fullstack.cash',
|
||||
wif: 'your-private-key-wif'
|
||||
})
|
||||
```
|
||||
|
||||
This is because the new IPFS class in bch-js uses the fs library for uploading files, which is not supported by Gatsby.
|
||||
### Web Apps
|
||||
|
||||
## Features
|
||||
This library sets itself apart from BITBOX with the following features:
|
||||
|
||||
- [ECMAScript 2017 standard JavaScript](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017) used instead of TypeScript. Works
|
||||
natively with node.js v10 or higher.
|
||||
|
||||
- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully support all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs).
|
||||
|
||||
- [Semantic Release](https://github.com/semantic-release/semantic-release) for
|
||||
continuous delivery using semantic versioning.
|
||||
|
||||
- [Greenkeeper](https://greenkeeper.io/) automatic dependency management for
|
||||
automatically maintaining the latest, most secure dependencies.
|
||||
|
||||
- [IPFS uploads](https://ipfs.io) of all files and dependencies, to backup
|
||||
dependencies in case they are ever inaccessible from GitHub or npm.
|
||||
[minimal-slp-wallet](https://www.npmjs.com/package/minimal-slp-wallet) is a minimal wallet 'engine' that incorporates bch-js. It's compiled with Browserify for front end apps.
|
||||
|
||||
[This gist](https://gist.github.com/christroutner/6cb9d1b615f3f9363af79723157bc434) shows how to include minimal-slp-wallet into a basic web page without using a framework.
|
||||
|
||||
[bch-wallet-web3-spa](https://github.com/Permissionless-Software-Foundation/bch-wallet-web3-spa) is a React web app template using bch-js and minimal-slp-wallet.
|
||||
|
||||
## Documentation:
|
||||
|
||||
Full documentation for this library can be found here:
|
||||
- [Documentation](https://bchjs.fullstack.cash/)
|
||||
|
||||
Original documentation on BITBOX is available at:
|
||||
|
||||
- [General docs](https://developer.bitcoin.com)
|
||||
- [BITBOX Introduction](https://developer.bitcoin.com/bitbox)
|
||||
- [BITBOX API Reference](https://developer.bitcoin.com/bitbox/docs/getting-started)
|
||||
|
||||
- [API Reference](https://bchjs.fullstack.cash/)
|
||||
|
||||
bch-js uses [APIDOC](http://apidocjs.com/) so that documentation and working code
|
||||
live in the same repository. To generate the documentation:
|
||||
|
||||
- `npm run docs`
|
||||
- Open the generated `docs/index.html` file in a web browser.
|
||||
|
||||
## Support
|
||||
|
||||
Have questions? Need help? Join our community support
|
||||
[Telegram channel](https://t.me/bch_js_toolkit)
|
||||
|
||||
## IPFS Releases
|
||||
## Donate
|
||||
|
||||
I will periodically publish IPFS releases of this repository, including all
|
||||
dependencies in the `node_modules` folder. This ensures working copies of this
|
||||
repository can be retrieved in case there is any drift in dependency files, or
|
||||
if dependencies are pulled from npm or GitHub.
|
||||
This open source software is developed and maintained by the [Permissionless Software Foundation](https://psfoundation.cash). If this library provides value to you, please consider making a donation to support the PSF developers:
|
||||
|
||||
- Initial fork on 5/9/2019:
|
||||
- without node_modules folder: QmQFHfbBQdEHfhtiRLbXtX1NcgnfL45hZb7TbQimTXAuzG (4 MB)
|
||||
- with node_modules folder: QmXq9Ds6Qdkg9xbRhcF8pay9KabA6QN2y7bx3wvSqiXifk (107 MB)
|
||||
|
||||
- v1.0.0 - refactored to pure JavaScript:
|
||||
- without node_modules folder: QmNjFsiTZRMAUa9rZpXqZqivv9JLaNicwLSPHjyLB7PVDk (1 MB)
|
||||
- with node_modules folder: Qma9ScApwBtuL7dpdSk7jpBFTxbqRdiR921WjyP75SU7bT (100 MB)
|
||||
<div align="center">
|
||||
<img src="./img/donation-qr.png" />
|
||||
<p>bitcoincash:qqsrke9lh257tqen99dkyy2emh4uty0vky9y0z0lsr</p>
|
||||
</div>
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.md)
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
Examples have been moved to this repository:
|
||||
Examples have been moved to this repository
|
||||
|
||||
https://github.com/Permissionless-Software-Foundation/bch-js-examples
|
||||
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to improve contrast in auto-generated API documentation
|
||||
# The code examples in api-doc use grey-on-grey colors that are hard to read.
|
||||
# This fixes that issue by improving text contrast for better readability.
|
||||
# Run this script *after* running 'npm run docs'
|
||||
|
||||
FILE="./docs/assets/prism.css"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$FILE" ]; then
|
||||
echo "Error: $FILE not found. Make sure to run 'npm run docs' first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a backup
|
||||
#cp "$FILE" "$FILE.bak"
|
||||
|
||||
# Improve contrast:
|
||||
# - Change default text color from #ccc to #e8e8e8 (brighter light gray)
|
||||
# - Change comment color from #999 to #b5b5b5 (lighter for better visibility)
|
||||
# - Change punctuation color from #ccc to #e8e8e8 (consistent with text)
|
||||
|
||||
# Replace default text color in code[class*="language-"] and pre[class*="language-"]
|
||||
sed -i 's/color: #ccc;/color: #000000;/g' "$FILE"
|
||||
|
||||
# Replace comment color
|
||||
sed -i 's/color: #999;/color: #b5b5b5;/g' "$FILE"
|
||||
|
||||
|
||||
# Replace punctuation color (already handled by first sed, but explicit for clarity)
|
||||
# The punctuation token also uses #ccc, so the first sed command handles it
|
||||
|
||||
echo "✓ Improved contrast in prism.css:"
|
||||
echo " - Default text: #ccc → #e8e8e8"
|
||||
echo " - Comments: #999 → #b5b5b5"
|
||||
echo " - Punctuation: #ccc → #e8e8e8"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
+10451
File diff suppressed because it is too large
Load Diff
Generated
+15914
-11047
File diff suppressed because it is too large
Load Diff
+63
-73
@@ -1,25 +1,28 @@
|
||||
{
|
||||
"name": "@psf/bch-js",
|
||||
"version": "3.4.0",
|
||||
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
|
||||
"version": "7.0.0",
|
||||
"type": "module",
|
||||
"description": "A JavaScript library for working with Bitcoin Cash and SLP Tokens",
|
||||
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
||||
"contributors": [
|
||||
"Gabriel Cardona <gabriel@bitcoin.com>"
|
||||
"Gabriel Cardona <gabriel@bitcoin.com>",
|
||||
"Stoyan Zhekov <zh@zhware.net>"
|
||||
],
|
||||
"main": "src/bch-js",
|
||||
"main": "src/bch-js.js",
|
||||
"scripts": {
|
||||
"test": "nyc mocha --timeout 30000 test/unit/",
|
||||
"test:integration": "mocha --timeout 30000 test/integration/",
|
||||
"test:integration:testnet": "mocha --timeout 30000 test/integration/testnet/",
|
||||
"test:integration:local": "RESTURL=http://localhost:3000/v3/ mocha --timeout 30000 test/integration",
|
||||
"test:integration:testnet:local": "RESTURL=http://localhost:4000/v3/ mocha --timeout 30000 test/integration/testnet",
|
||||
"test:integration:free": "bash test/integration/test-free-tier.sh",
|
||||
"test:integration:free:testnet": "bash test/integration/testnet/test-free-tier.sh",
|
||||
"test:temp": "mocha --timeout 30000 -g '#hydrateUtxos' test/integration/slp.js",
|
||||
"test:integration:api": "RESTURL=https://api.fullstack.cash/v3/ mocha --timeout 30000 test/integration",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs"
|
||||
"test": "export RESTURL=http://localhost:5942/v6 && c8 mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
|
||||
"test:custom": "export RESTURL=http://localhost:5942/v6 && c8 mocha --trace-warnings --unhandled-rejections=strict --timeout 60000 ${MOCHA_GREP:+--grep $MOCHA_GREP} test/unit/",
|
||||
"test:integration": "npm run test:integration:local:noauth",
|
||||
"test:integration:fullstack:free": "export RESTURL=https://bch.fullstack.cash/v6 && mocha --timeout 60000 test/integration/",
|
||||
"test:integration:fullstack:x402": "export RESTURL=https://x402-bch.fullstack.cash/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 120000 test/integration/",
|
||||
"test:integration:fullstack:auth": "export RESTURL=http://dev.fullstack.cash/v6 && export BCHJSBEARERTOKEN=test01 && mocha --timeout 30000 test/integration/",
|
||||
"test:integration:local:noauth": "export RESTURL=http://192.168.1.65:5942/v6 && mocha --timeout 30000 test/integration/",
|
||||
"test:integration:local:auth": "export RESTURL=http://192.168.1.115:5942/v6 && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
|
||||
"test:integration:local:x402": "export RESTURL=http://localhost:5942/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 1200000 test/integration/",
|
||||
"test:integration:decatur": "export RESTURL=http://192.168.1.65:5942/v6 && mocha --timeout 30000 test/integration/",
|
||||
"coverage": "nyc --reporter=html mocha --timeout 25000 test/unit/",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs && ./fix-docs-contrast.sh",
|
||||
"lint": "standard --env mocha --fix"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/Permissionless-Software-Foundation/bch-js",
|
||||
@@ -28,69 +31,56 @@
|
||||
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/core": "^1.10.4",
|
||||
"@uppy/tus": "^1.5.12",
|
||||
"apidoc": "^0.17.7",
|
||||
"assert": "^2.0.0",
|
||||
"axios": "^0.19.0",
|
||||
"bc-bip68": "^1.0.5",
|
||||
"bch-wallet-bridge.js": "github:web3bch/bch-wallet-bridge.js#master",
|
||||
"bchaddrjs-slp": "^0.2.5",
|
||||
"bigi": "^1.4.2",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"bip-schnorr": "^0.3.0",
|
||||
"bip21": "Bitcoin-com/bip21",
|
||||
"bip32-utils": "Bitcoin-com/bip32-utils#0.13.1",
|
||||
"bip38": "^2.0.2",
|
||||
"bip39": "^3.0.2",
|
||||
"bip66": "^1.1.5",
|
||||
"bitcoincash-ops": "christroutner/bitcoincash-ops",
|
||||
"bitcoincashjs-lib": "christroutner/bitcoincashjs-lib#master",
|
||||
"bitcoinjs-message": "^2.0.0",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer": "^5.1.0",
|
||||
"cashaddrjs": "^0.3.3",
|
||||
"chalk": "^2.3.0",
|
||||
"clear": "0.1.0",
|
||||
"coininfo": "Bitcoin-com/coininfo",
|
||||
"commander": "^3.0.0",
|
||||
"cp-file": "^7.0.0",
|
||||
"ecurve": "^1.0.6",
|
||||
"figlet": "^1.2.0",
|
||||
"git-clone": "^0.1.0",
|
||||
"ini": "^1.3.5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-cmd": "^3.0.0",
|
||||
"node-emoji": "^1.8.1",
|
||||
"qrcode": "^1.4.1",
|
||||
"randombytes": "^2.0.6",
|
||||
"repl.history": "^0.1.4",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"satoshi-bitcoin": "^1.0.4",
|
||||
"slp-mdm": "0.0.6",
|
||||
"@chris.troutner/bip32-utils": "1.0.5",
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@psf/bitcoincashjs-lib": "4.0.3",
|
||||
"@psf/coininfo": "4.0.0",
|
||||
"axios": "1.13.2",
|
||||
"bc-bip68": "1.0.5",
|
||||
"bchaddrjs-slp": "0.2.14",
|
||||
"bigi": "1.4.2",
|
||||
"bignumber.js": "9.3.1",
|
||||
"bip-schnorr": "0.6.7",
|
||||
"bip38": "3.1.1",
|
||||
"bip39": "3.1.0",
|
||||
"bip66": "1.1.5",
|
||||
"bitcoinjs-message": "2.2.0",
|
||||
"bs58": "4.0.1",
|
||||
"ecashaddrjs": "1.0.7",
|
||||
"ini": "6.0.0",
|
||||
"randombytes": "2.1.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"satoshi-bitcoin": "1.0.5",
|
||||
"slp-mdm": "0.0.7",
|
||||
"slp-parser": "0.0.4",
|
||||
"socket.io": "^2.1.1",
|
||||
"socket.io-client": "^2.1.1",
|
||||
"touch": "^3.1.0",
|
||||
"wif": "^2.0.6"
|
||||
"wif": "2.0.6",
|
||||
"x402-bch-axios": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.1.2",
|
||||
"coveralls": "^3.0.2",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"mocha": "^6.1.4",
|
||||
"nock": "^10.0.6",
|
||||
"node-mocks-http": "^1.7.0",
|
||||
"nyc": "^14.1.0",
|
||||
"prettier": "^1.18.2",
|
||||
"semantic-release": "^15.13.19",
|
||||
"sinon": "^7.3.2"
|
||||
"apidoc": "1.2.0",
|
||||
"assert": "2.1.0",
|
||||
"c8": "10.1.3",
|
||||
"chai": "4.1.2",
|
||||
"husky": "4.3.8",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"mocha": "11.7.5",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "24.2.9",
|
||||
"sinon": "9.2.2",
|
||||
"standard": "17.1.2"
|
||||
},
|
||||
"apidoc": {
|
||||
"title": "bch-js",
|
||||
"url": "bchjs."
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run lint"
|
||||
}
|
||||
},
|
||||
"standard": {
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+211
-87
@@ -1,16 +1,22 @@
|
||||
//const axios = require("axios")
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const cashaddr = require("cashaddrjs")
|
||||
const coininfo = require("coininfo")
|
||||
// import axios from "axios"
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import cashaddr from 'ecashaddrjs'
|
||||
import coininfo from '@psf/coininfo'
|
||||
|
||||
class Address {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
const tmp = {}
|
||||
if (!config || !config.restURL) tmp.restURL = `https://api.bchjs.cash/v3/`
|
||||
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v5/'
|
||||
else tmp.restURL = config.restURL
|
||||
|
||||
this.restURL = tmp.restURL
|
||||
this.apiToken = tmp.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,34 +43,34 @@ class Address {
|
||||
* // mqc1tmwY2368LLGktnePzEyPAsgADxbksi
|
||||
*/
|
||||
// Translate address from any address format into a specific format.
|
||||
toLegacyAddress(address) {
|
||||
toLegacyAddress (address) {
|
||||
const { prefix, type, hash } = this._decode(address)
|
||||
|
||||
let bitcoincash
|
||||
switch (prefix) {
|
||||
case "bitcoincash":
|
||||
case 'bitcoincash':
|
||||
bitcoincash = coininfo.bitcoincash.main
|
||||
break
|
||||
case "bchtest":
|
||||
case 'bchtest':
|
||||
bitcoincash = coininfo.bitcoincash.test
|
||||
break
|
||||
case "bchreg":
|
||||
case 'bchreg':
|
||||
bitcoincash = coininfo.bitcoincash.regtest
|
||||
break
|
||||
default:
|
||||
throw `unsupported prefix : ${prefix}`
|
||||
throw new Error(`unsupported prefix : ${prefix}`)
|
||||
}
|
||||
|
||||
let version
|
||||
switch (type) {
|
||||
case "P2PKH":
|
||||
case 'P2PKH':
|
||||
version = bitcoincash.versions.public
|
||||
break
|
||||
case "P2SH":
|
||||
case 'P2SH':
|
||||
version = bitcoincash.versions.scripthash
|
||||
break
|
||||
default:
|
||||
throw `unsupported address type : ${type}`
|
||||
throw new Error(`unsupported address type : ${type}`)
|
||||
}
|
||||
|
||||
const hashBuf = Buffer.from(hash)
|
||||
@@ -95,11 +101,11 @@ class Address {
|
||||
* bchjs.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
|
||||
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3
|
||||
*/
|
||||
toCashAddress(address, prefix = true, regtest = false) {
|
||||
toCashAddress (address, prefix = true, regtest = false) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
let prefixString
|
||||
if (regtest) prefixString = "bchreg"
|
||||
if (regtest) prefixString = 'bchreg'
|
||||
else prefixString = decoded.prefix
|
||||
|
||||
const cashAddress = cashaddr.encode(
|
||||
@@ -109,14 +115,115 @@ class Address {
|
||||
)
|
||||
|
||||
if (prefix) return cashAddress
|
||||
return cashAddress.split(":")[1]
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toEcashAddress() toEcashAddress()
|
||||
* @apiName toEcashAddress
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert legacy to eCash (XEC) format
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // mainnet
|
||||
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
|
||||
* // ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
* // mainnet no prefix
|
||||
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
|
||||
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
*/
|
||||
toEcashAddress (address, prefix = true) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
const ecashAddress = cashaddr.encode(
|
||||
'ecash',
|
||||
decoded.type,
|
||||
decoded.hash
|
||||
)
|
||||
|
||||
if (prefix) return ecashAddress
|
||||
return ecashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toEtokenAddress() toEtokenAddress()
|
||||
* @apiName toEtokenAddress
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert legacy to eToken (XEC) format
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // mainnet
|
||||
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
|
||||
* // etoken:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
* // mainnet no prefix
|
||||
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
|
||||
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
*/
|
||||
toEtokenAddress (address, prefix = true) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
const etokenAddress = cashaddr.encode(
|
||||
'etoken',
|
||||
decoded.type,
|
||||
decoded.hash
|
||||
)
|
||||
|
||||
if (prefix) return etokenAddress
|
||||
return etokenAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
|
||||
* @apiName ecashtoCashAddress
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert legacy to cashAddress format
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // mainnet
|
||||
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da')
|
||||
* // bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
|
||||
*
|
||||
* // mainnet no prefix
|
||||
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da', false)
|
||||
* // qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
|
||||
*/
|
||||
ecashtoCashAddress (address, prefix = true) {
|
||||
const decoded = this._decodeEcashAddress(address)
|
||||
|
||||
const cashAddress = cashaddr.encode(
|
||||
'bitcoincash',
|
||||
decoded.type,
|
||||
decoded.hash
|
||||
)
|
||||
|
||||
if (prefix) return cashAddress
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toHash160() toHash160()
|
||||
* @apiName toHash160
|
||||
* @apiGroup Address
|
||||
* @apiDescription Converts any address format to hash160
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // cash address mainnet p2pkh
|
||||
* bchjs.Address.toHash160("bitcoincash:qptnmya5wkly7xf97wm5ak23yqdsz3l2cyj7k9vyyh")
|
||||
* // 573d93b475be4f1925f3b74ed951201b0147eac1
|
||||
*
|
||||
* // cash address mainnet p2sh
|
||||
* bchjs.Address.toHash160("bitcoincash:pp7ushdxf5we8mcpaa3wqgsuqt639cu59ur5xu5fug")
|
||||
* // 7dc85da64d1d93ef01ef62e0221c02f512e3942f
|
||||
*/
|
||||
// Converts any address format to hash160
|
||||
toHash160(address) {
|
||||
toHash160 (address) {
|
||||
const legacyAddress = this.toLegacyAddress(address)
|
||||
const bytes = Bitcoin.address.fromBase58Check(legacyAddress)
|
||||
return bytes.hash.toString("hex")
|
||||
return bytes.hash.toString('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +246,8 @@ class Address {
|
||||
* // mhTg9sgNgvAGfmJs192oUzQWqAXHH5nqLE
|
||||
*/
|
||||
// Converts hash160 to Legacy Address
|
||||
hash160ToLegacy(hash160, network = Bitcoin.networks.bitcoin.pubKeyHash) {
|
||||
const buffer = Buffer.from(hash160, "hex")
|
||||
hash160ToLegacy (hash160, network = Bitcoin.networks.bitcoin.pubKeyHash) {
|
||||
const buffer = Buffer.from(hash160, 'hex')
|
||||
const legacyAddress = Bitcoin.address.toBase58Check(buffer, network)
|
||||
return legacyAddress
|
||||
}
|
||||
@@ -149,7 +256,7 @@ class Address {
|
||||
* @api Address.hash160ToCash() hash160ToCash()
|
||||
* @apiName hash160ToCash
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert hash160 to cash address.
|
||||
* @apiDescription Convert hash160 to cash address. Accepts either hexadecimal or buffer.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1")
|
||||
@@ -160,7 +267,7 @@ class Address {
|
||||
* 'bchtest:qq24rpar9qas3vc9r8d4p0prhwaf7jmx2u22nzt946'
|
||||
*/
|
||||
// Converts hash160 to Cash Address
|
||||
hash160ToCash(
|
||||
hash160ToCash (
|
||||
hash160,
|
||||
network = Bitcoin.networks.bitcoin.pubKeyHash,
|
||||
regtest = false
|
||||
@@ -169,7 +276,7 @@ class Address {
|
||||
return this.toCashAddress(legacyAddress, true, regtest)
|
||||
}
|
||||
|
||||
_decode(address) {
|
||||
_decode (address) {
|
||||
try {
|
||||
return this._decodeLegacyAddress(address)
|
||||
} catch (error) {}
|
||||
@@ -185,56 +292,56 @@ class Address {
|
||||
throw new Error(`Unsupported address format : ${address}`)
|
||||
}
|
||||
|
||||
_decodeLegacyAddress(address) {
|
||||
_decodeLegacyAddress (address) {
|
||||
const { version, hash } = Bitcoin.address.fromBase58Check(address)
|
||||
const info = coininfo.bitcoincash
|
||||
|
||||
switch (version) {
|
||||
case info.main.versions.public:
|
||||
return {
|
||||
prefix: "bitcoincash",
|
||||
type: "P2PKH",
|
||||
hash: hash,
|
||||
format: "legacy"
|
||||
prefix: 'bitcoincash',
|
||||
type: 'P2PKH',
|
||||
hash,
|
||||
format: 'legacy'
|
||||
}
|
||||
case info.main.versions.scripthash:
|
||||
return {
|
||||
prefix: "bitcoincash",
|
||||
type: "P2SH",
|
||||
hash: hash,
|
||||
format: "legacy"
|
||||
prefix: 'bitcoincash',
|
||||
type: 'P2SH',
|
||||
hash,
|
||||
format: 'legacy'
|
||||
}
|
||||
case info.test.versions.public:
|
||||
return {
|
||||
prefix: "bchtest",
|
||||
type: "P2PKH",
|
||||
hash: hash,
|
||||
format: "legacy"
|
||||
prefix: 'bchtest',
|
||||
type: 'P2PKH',
|
||||
hash,
|
||||
format: 'legacy'
|
||||
}
|
||||
case info.test.versions.scripthash:
|
||||
return {
|
||||
prefix: "bchtest",
|
||||
type: "P2SH",
|
||||
hash: hash,
|
||||
format: "legacy"
|
||||
prefix: 'bchtest',
|
||||
type: 'P2SH',
|
||||
hash,
|
||||
format: 'legacy'
|
||||
}
|
||||
default:
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
}
|
||||
|
||||
_decodeCashAddress(address) {
|
||||
if (address.indexOf(":") !== -1) {
|
||||
_decodeCashAddress (address) {
|
||||
if (address.indexOf(':') !== -1) {
|
||||
const decoded = cashaddr.decode(address)
|
||||
decoded.format = "cashaddr"
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
}
|
||||
|
||||
const prefixes = ["bitcoincash", "bchtest", "bchreg"]
|
||||
const prefixes = ['bitcoincash', 'bchtest', 'bchreg']
|
||||
for (let i = 0; i < prefixes.length; ++i) {
|
||||
try {
|
||||
const decoded = cashaddr.decode(`${prefixes[i]}:${address}`)
|
||||
decoded.format = "cashaddr"
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
} catch (error) {}
|
||||
}
|
||||
@@ -242,18 +349,34 @@ class Address {
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
|
||||
_encodeAddressFromHash160(address) {
|
||||
_encodeAddressFromHash160 (address) {
|
||||
try {
|
||||
return {
|
||||
legacyAddress: this.hash160ToLegacy(address),
|
||||
cashAddress: this.hash160ToCash(address),
|
||||
format: "hash160"
|
||||
format: 'hash160'
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
|
||||
_decodeEcashAddress (address) {
|
||||
if (address.indexOf(':') !== -1) {
|
||||
const decoded = cashaddr.decode(address)
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = cashaddr.decode(`ecash:${address}`)
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
} catch (error) {}
|
||||
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.isLegacyAddress() isLegacyAddress()
|
||||
* @apiName isLegacyAddress
|
||||
@@ -286,8 +409,8 @@ class Address {
|
||||
* // true
|
||||
*/
|
||||
// Test for address format.
|
||||
isLegacyAddress(address) {
|
||||
return this.detectAddressFormat(address) === "legacy"
|
||||
isLegacyAddress (address) {
|
||||
return this.detectAddressFormat(address) === 'legacy'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,8 +444,8 @@ class Address {
|
||||
* bchjs.Address.isCashAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // false
|
||||
*/
|
||||
isCashAddress(address) {
|
||||
return this.detectAddressFormat(address) === "cashaddr"
|
||||
isCashAddress (address) {
|
||||
return this.detectAddressFormat(address) === 'cashaddr'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,8 +463,8 @@ class Address {
|
||||
* bchjs.Address.isHash160(notHash160Address);
|
||||
* // false
|
||||
*/
|
||||
isHash160(address) {
|
||||
return this.detectAddressFormat(address) === "hash160"
|
||||
isHash160 (address) {
|
||||
return this.detectAddressFormat(address) === 'hash160'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,11 +499,11 @@ class Address {
|
||||
* // false
|
||||
*/
|
||||
// Test for address network.
|
||||
isMainnetAddress(address) {
|
||||
if (address[0] === "x") return true
|
||||
else if (address[0] === "t") return false
|
||||
isMainnetAddress (address) {
|
||||
if (address[0] === 'x') return true
|
||||
else if (address[0] === 't') return false
|
||||
|
||||
return this.detectAddressNetwork(address) === "mainnet"
|
||||
return this.detectAddressNetwork(address) === 'mainnet'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,11 +537,11 @@ class Address {
|
||||
* bchjs.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // true
|
||||
*/
|
||||
isTestnetAddress(address) {
|
||||
if (address[0] === "x") return false
|
||||
else if (address[0] === "t") return true
|
||||
isTestnetAddress (address) {
|
||||
if (address[0] === 'x') return false
|
||||
else if (address[0] === 't') return true
|
||||
|
||||
return this.detectAddressNetwork(address) === "testnet"
|
||||
return this.detectAddressNetwork(address) === 'testnet'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,8 +579,8 @@ class Address {
|
||||
* bchjs.Address.isRegTestAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggt9t0a6zy')
|
||||
* // false
|
||||
*/
|
||||
isRegTestAddress(address) {
|
||||
return this.detectAddressNetwork(address) === "regtest"
|
||||
isRegTestAddress (address) {
|
||||
return this.detectAddressNetwork(address) === 'regtest'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,8 +616,8 @@ class Address {
|
||||
*/
|
||||
|
||||
// Test for address type.
|
||||
isP2PKHAddress(address) {
|
||||
return this.detectAddressType(address) === "p2pkh"
|
||||
isP2PKHAddress (address) {
|
||||
return this.detectAddressType(address) === 'p2pkh'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -529,8 +652,8 @@ class Address {
|
||||
* // false
|
||||
*/
|
||||
|
||||
isP2SHAddress(address) {
|
||||
return this.detectAddressType(address) === "p2sh"
|
||||
isP2SHAddress (address) {
|
||||
return this.detectAddressType(address) === 'p2sh'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -565,7 +688,7 @@ class Address {
|
||||
* // legacy
|
||||
*/
|
||||
// Detect address format.
|
||||
detectAddressFormat(address) {
|
||||
detectAddressFormat (address) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
return decoded.format
|
||||
@@ -603,19 +726,19 @@ class Address {
|
||||
* // testnet
|
||||
*/
|
||||
// Detect address network.
|
||||
detectAddressNetwork(address) {
|
||||
if (address[0] === "x") return "mainnet"
|
||||
else if (address[0] === "t") return "testnet"
|
||||
detectAddressNetwork (address) {
|
||||
if (address[0] === 'x') return 'mainnet'
|
||||
else if (address[0] === 't') return 'testnet'
|
||||
|
||||
const decoded = this._decode(address)
|
||||
|
||||
switch (decoded.prefix) {
|
||||
case "bitcoincash":
|
||||
return "mainnet"
|
||||
case "bchtest":
|
||||
return "testnet"
|
||||
case "bchreg":
|
||||
return "regtest"
|
||||
case 'bitcoincash':
|
||||
return 'mainnet'
|
||||
case 'bchtest':
|
||||
return 'testnet'
|
||||
case 'bchreg':
|
||||
return 'regtest'
|
||||
default:
|
||||
throw new Error(`Invalid prefix : ${decoded.prefix}`)
|
||||
}
|
||||
@@ -653,7 +776,7 @@ class Address {
|
||||
* // p2pkh
|
||||
*/
|
||||
// Detect address type.
|
||||
detectAddressType(address) {
|
||||
detectAddressType (address) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
return decoded.type.toLowerCase()
|
||||
@@ -688,7 +811,7 @@ class Address {
|
||||
* // bchtest:qzd7dvlnfukggjqsf5ju0qqwwltakfumjsck33js6m
|
||||
* // bchtest:qq322ataqeas4n0pdn4gz2sdereh5ae43ylk4qdvus
|
||||
*/
|
||||
fromXPub(xpub, path = "0/0") {
|
||||
fromXPub (xpub, path = '0/0') {
|
||||
const HDNode = Bitcoin.HDNode.fromBase58(
|
||||
xpub,
|
||||
Bitcoin.networks[this.detectAddressNetwork(xpub)]
|
||||
@@ -704,13 +827,13 @@ class Address {
|
||||
* @apiDescription Detect an addess from an OutputScript..
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* const script = bchjs.Script.encode([
|
||||
* const scriptBuffer = bchjs.Script.encode([
|
||||
* Buffer.from("BOX", "ascii"),
|
||||
* bchjs.Script.opcodes.OP_CAT,
|
||||
* Buffer.from("BITBOX", "ascii"),
|
||||
* bchjs.Script.opcodes.OP_EQUAL
|
||||
* ]);
|
||||
* const p2sh_hash160 = bchjs.Crypto.hash160(script);
|
||||
* const p2sh_hash160 = bchjs.Crypto.hash160(scriptBuffer);
|
||||
* const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160);
|
||||
*
|
||||
* // mainnet address from output script
|
||||
@@ -721,12 +844,13 @@ class Address {
|
||||
* bchjs.Address.fromOutputScript(scriptPubKey, 'testnet');
|
||||
* // bchtest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqh2hmtpyr
|
||||
*/
|
||||
fromOutputScript(scriptPubKey, network = "mainnet") {
|
||||
fromOutputScript (scriptPubKey, network = 'mainnet') {
|
||||
let netParam
|
||||
if (network !== "bitcoincash" && network !== "mainnet")
|
||||
if (network !== 'bitcoincash' && network !== 'mainnet') {
|
||||
netParam = Bitcoin.networks.testnet
|
||||
}
|
||||
|
||||
const regtest = network === "bchreg"
|
||||
const regtest = network === 'bchreg'
|
||||
|
||||
return this.toCashAddress(
|
||||
Bitcoin.address.fromOutputScript(scriptPubKey, netParam),
|
||||
@@ -736,4 +860,4 @@ class Address {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Address
|
||||
export default Address
|
||||
|
||||
+117
-129
@@ -7,69 +7,127 @@
|
||||
*/
|
||||
|
||||
// bch-api mainnet.
|
||||
// const DEFAULT_REST_API = "https://api.bchjs.cash/v3/"
|
||||
const DEFAULT_REST_API = "https://api.fullstack.cash/v3/"
|
||||
// const DEFAULT_REST_API = "http://localhost:3000/v5/"
|
||||
|
||||
// Global npm libraries
|
||||
import axios from 'axios'
|
||||
import {
|
||||
createSigner,
|
||||
withPaymentInterceptor,
|
||||
createPaymentHeader,
|
||||
selectPaymentRequirements
|
||||
} from 'x402-bch-axios'
|
||||
|
||||
// local deps
|
||||
const BitcoinCash = require("./bitcoincash")
|
||||
const Crypto = require("./crypto")
|
||||
const Util = require("./util")
|
||||
const Blockchain = require("./blockchain")
|
||||
const Control = require("./control")
|
||||
const Generating = require("./generating")
|
||||
const Mining = require("./mining")
|
||||
const RawTransactions = require("./raw-transactions")
|
||||
const Mnemonic = require("./mnemonic")
|
||||
const Address = require("./address")
|
||||
const HDNode = require("./hdnode")
|
||||
const TransactionBuilder = require("./transaction-builder")
|
||||
const ECPair = require("./ecpair")
|
||||
const Script = require("./script")
|
||||
const Price = require("./price")
|
||||
const Socket = require("./socket")
|
||||
const Wallet = require("./wallet")
|
||||
const Schnorr = require("./schnorr")
|
||||
const SLP = require("./slp/slp")
|
||||
const IPFS = require("./ipfs")
|
||||
const Encryption = require("./encryption")
|
||||
import BitcoinCash from './bitcoincash.js'
|
||||
import Crypto from './crypto.js'
|
||||
import Util from './util.js'
|
||||
import Blockchain from './blockchain.js'
|
||||
import Control from './control.js'
|
||||
import Generating from './generating.js'
|
||||
import Mining from './mining.js'
|
||||
import RawTransactions from './raw-transactions.js'
|
||||
import Mnemonic from './mnemonic.js'
|
||||
import Address from './address.js'
|
||||
import HDNode from './hdnode.js'
|
||||
import TransactionBuilder from './transaction-builder.js'
|
||||
import ECPair from './ecpair.js'
|
||||
import Script from './script.js'
|
||||
import Price from './price.js'
|
||||
import Schnorr from './schnorr.js'
|
||||
import SLP from './slp/slp.js'
|
||||
import Encryption from './encryption.js'
|
||||
import Utxo from './utxo.js'
|
||||
import Transaction from './transaction.js'
|
||||
import DSProof from './dsproof.js'
|
||||
import Ecash from './ecash.js'
|
||||
|
||||
// Indexers
|
||||
const Blockbook = require("./blockbook")
|
||||
const OpenBazaar = require("./openbazaar")
|
||||
const Ninsight = require("./ninsight")
|
||||
const Electrumx = require("./electrumx")
|
||||
import Electrumx from './electrumx.js'
|
||||
import PsfSlpIndexer from './psf-slp-indexer.js'
|
||||
|
||||
class BCHJS {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
// Try to retrieve the REST API URL from different sources.
|
||||
if (config && config.restURL && config.restURL !== "")
|
||||
if (config && config.restURL && config.restURL !== '') {
|
||||
this.restURL = config.restURL
|
||||
else if (process.env.RESTURL && process.env.RESTURL !== "")
|
||||
} else if (process.env.RESTURL && process.env.RESTURL !== '') {
|
||||
this.restURL = process.env.RESTURL
|
||||
else this.restURL = DEFAULT_REST_API
|
||||
} else {
|
||||
throw new Error(
|
||||
'REST API URL is required. Provide config.restURL or set the RESTURL environment variable.'
|
||||
)
|
||||
}
|
||||
|
||||
// Retrieve the apiToken
|
||||
this.apiToken = "" // default value.
|
||||
if (config && config.apiToken && config.apiToken !== "")
|
||||
this.apiToken = config.apiToken
|
||||
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "")
|
||||
this.apiToken = process.env.BCHJSTOKEN
|
||||
// Normalize restURL to always have a trailing slash
|
||||
if (!this.restURL.endsWith('/')) {
|
||||
this.restURL = this.restURL + '/'
|
||||
}
|
||||
|
||||
// Retrieve the Bearer token for simple token authentication.
|
||||
this.bearerToken = '' // default value.
|
||||
if (config && config.bearerToken && config.bearerToken !== '') {
|
||||
this.bearerToken = config.bearerToken
|
||||
} else if (process.env.BCHJSBEARERTOKEN && process.env.BCHJSBEARERTOKEN !== '') {
|
||||
this.bearerToken = process.env.BCHJSBEARERTOKEN
|
||||
}
|
||||
|
||||
// Generate the authentication token for the authorization header.
|
||||
this.authToken = ''
|
||||
if (this.bearerToken) {
|
||||
this.authToken = `Bearer ${this.bearerToken}`
|
||||
}
|
||||
|
||||
// x402 payment configuration
|
||||
// If a WIF private key is provided, enable x402 automatic payment handling
|
||||
this.wif = ''
|
||||
if (config && config.wif && config.wif !== '') {
|
||||
this.wif = config.wif
|
||||
} else if (process.env.BCHJSWIF && process.env.BCHJSWIF !== '') {
|
||||
this.wif = process.env.BCHJSWIF
|
||||
}
|
||||
|
||||
// Most TXs cost about 250 sats. So we'll set the default to 25000 sats,
|
||||
// so the mining fee is 1% of the transaction.
|
||||
this.paymentAmountSats = (config && config.paymentAmountSats) || 25000
|
||||
|
||||
// BCH server URL for x402 payments (separate from REST API server)
|
||||
// This is used when broadcasting payment transactions to the blockchain
|
||||
if (config && config.bchServerURL && config.bchServerURL !== '') {
|
||||
this.bchServerURL = config.bchServerURL
|
||||
} else if (process.env.BCHJSBCHSERVERURL && process.env.BCHJSBCHSERVERURL !== '') {
|
||||
this.bchServerURL = process.env.BCHJSBCHSERVERURL
|
||||
} else {
|
||||
this.bchServerURL = 'https://bch.fullstack.cash/v6'
|
||||
}
|
||||
|
||||
const libConfig = {
|
||||
restURL: this.restURL,
|
||||
apiToken: this.apiToken
|
||||
authToken: this.authToken
|
||||
}
|
||||
|
||||
// console.log(`apiToken: ${this.apiToken}`)
|
||||
// If WIF is provided, create an axios instance with x402 payment interceptor
|
||||
// Otherwise, let sub-modules use their own axios import for backwards compatibility
|
||||
if (this.wif) {
|
||||
let axiosInstance = axios.create({
|
||||
baseURL: this.restURL,
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
})
|
||||
|
||||
// Populate Blockbook endpoints.
|
||||
this.Blockbook = new Blockbook(libConfig)
|
||||
const signer = createSigner(this.wif, this.paymentAmountSats)
|
||||
axiosInstance = withPaymentInterceptor(
|
||||
axiosInstance,
|
||||
signer,
|
||||
{
|
||||
apiType: 'rest-api',
|
||||
bchServerURL: this.bchServerURL
|
||||
}
|
||||
)
|
||||
|
||||
// Populate OpenBazaar endpoints
|
||||
this.OpenBazaar = new OpenBazaar(libConfig)
|
||||
|
||||
// Bitcoin.com Ninsight indexer
|
||||
this.Ninsight = new Ninsight(config)
|
||||
libConfig.axios = axiosInstance
|
||||
}
|
||||
|
||||
// ElectrumX indexer
|
||||
this.Electrumx = new Electrumx(libConfig)
|
||||
@@ -90,102 +148,32 @@ class BCHJS {
|
||||
this.Generating = new Generating(libConfig)
|
||||
this.HDNode = new HDNode(this.Address)
|
||||
this.Mnemonic = new Mnemonic(this.Address)
|
||||
this.Price = new Price()
|
||||
this.Price = new Price(libConfig)
|
||||
this.Script = new Script()
|
||||
this.TransactionBuilder = TransactionBuilder
|
||||
this.TransactionBuilder.setAddress(this.Address)
|
||||
this.Util = new Util(libConfig)
|
||||
this.Socket = Socket
|
||||
this.Wallet = Wallet
|
||||
this.Schnorr = new Schnorr(libConfig)
|
||||
|
||||
this.SLP = new SLP(libConfig)
|
||||
this.SLP.HDNode = this.HDNode
|
||||
|
||||
this.IPFS = new IPFS()
|
||||
}
|
||||
this.Utxo = new Utxo(libConfig)
|
||||
this.Transaction = new Transaction(libConfig)
|
||||
|
||||
static BitboxShim() {
|
||||
return BitboxShim
|
||||
}
|
||||
}
|
||||
this.DSProof = new DSProof(libConfig)
|
||||
this.eCash = new Ecash()
|
||||
|
||||
// Creat a shim so that all the endpoints match BITBOX SDK and this library
|
||||
// can be used as a drop-in replacement for BITBOX.
|
||||
class BitboxShim {
|
||||
constructor(config) {
|
||||
// Try to retrieve the REST API URL from different sources.
|
||||
if (config && config.restURL && config.restURL !== "")
|
||||
this.restURL = config.restURL
|
||||
else if (process.env.RESTURL && process.env.RESTURL !== "")
|
||||
this.restURL = process.env.RESTURL
|
||||
else this.restURL = DEFAULT_REST_API
|
||||
this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
|
||||
|
||||
// Retrieve the apiToken
|
||||
this.apiToken = "" // default value.
|
||||
if (config && config.apiToken && config.apiToken !== "")
|
||||
this.apiToken = config.apiToken
|
||||
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "")
|
||||
this.apiToken = process.env.BCHJSTOKEN
|
||||
|
||||
const libConfig = {
|
||||
restURL: this.restURL,
|
||||
apiToken: this.apiToken
|
||||
// Expose x402 helper functions for advanced use cases
|
||||
this.x402 = {
|
||||
createSigner,
|
||||
withPaymentInterceptor,
|
||||
createPaymentHeader,
|
||||
selectPaymentRequirements
|
||||
}
|
||||
|
||||
// Populate utility functions
|
||||
this.Address = new Address(libConfig)
|
||||
this.BitcoinCash = new BitcoinCash(this.Address)
|
||||
this.Blockchain = new Blockchain(libConfig)
|
||||
this.Crypto = Crypto
|
||||
this.ECPair = ECPair
|
||||
this.ECPair.setAddress(this.Address)
|
||||
this.Generating = new Generating(libConfig)
|
||||
this.HDNode = new HDNode(this.Address)
|
||||
this.Mnemonic = new Mnemonic(this.Address)
|
||||
this.Price = new Price()
|
||||
this.Script = new Script()
|
||||
this.TransactionBuilder = TransactionBuilder
|
||||
this.TransactionBuilder.setAddress(this.Address)
|
||||
this.Util = new Util(libConfig)
|
||||
this.Socket = Socket
|
||||
this.Wallet = Wallet
|
||||
this.Schnorr = new Schnorr(libConfig)
|
||||
|
||||
// Populate the SLP endpoints.
|
||||
this.SLP = new SLP(libConfig)
|
||||
this.Address = this.SLP.Address
|
||||
|
||||
this.ECPair.toSLPAddress = this.SLP.ECPair.toSLPAddress
|
||||
|
||||
this.Util.list = this.SLP.Utils.list
|
||||
this.Util.balancesForAddress = this.SLP.Utils.balancesForAddress
|
||||
this.Util.balancesForToken = this.SLP.Utils.balancesForToken
|
||||
this.Util.balance = this.SLP.Utils.balance
|
||||
this.Util.validateTxid = this.SLP.Utils.validateTxid
|
||||
this.Util.tokenStats = this.SLP.Utils.tokenStats
|
||||
this.Util.transactions = this.SLP.Utils.transactions
|
||||
this.Util.burnTotal = this.SLP.Utils.burnTotal
|
||||
this.Util.decodeOpReturn = this.SLP.Utils.decodeOpReturn
|
||||
this.Util.isTokenUtxo = this.SLP.Utils.isTokenUtxo
|
||||
this.Util.tokenUtxoDetails = this.SLP.Utils.tokenUtxoDetails
|
||||
this.Util.txDetails = this.SLP.Utils.txDetails
|
||||
|
||||
// Populate Full Node
|
||||
this.Control = new Control(libConfig)
|
||||
this.Mining = new Mining(libConfig)
|
||||
this.RawTransactions = new RawTransactions(libConfig)
|
||||
|
||||
// Populate Blockbook endpoints.
|
||||
this.Blockbook = new Blockbook(libConfig)
|
||||
|
||||
// Populate OpenBazaar endpoints
|
||||
this.OpenBazaar = new OpenBazaar(libConfig)
|
||||
|
||||
// Bitcoin.com Ninsight indexer
|
||||
this.Ninsight = new Ninsight()
|
||||
this.Electrumx = new Electrumx(libConfig)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BCHJS
|
||||
export default BCHJS
|
||||
|
||||
+41
-42
@@ -1,16 +1,15 @@
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const sb = require("satoshi-bitcoin")
|
||||
const bitcoinMessage = require("bitcoinjs-message")
|
||||
const bs58 = require("bs58")
|
||||
const bip21 = require("bip21")
|
||||
const coininfo = require("coininfo")
|
||||
const bip38 = require("bip38")
|
||||
const wif = require("wif")
|
||||
|
||||
const Buffer = require("safe-buffer").Buffer
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import sb from 'satoshi-bitcoin'
|
||||
import bitcoinMessage from 'bitcoinjs-message'
|
||||
import bs58 from 'bs58'
|
||||
import bip21 from '@psf/bip21'
|
||||
import coininfo from '@psf/coininfo'
|
||||
import bip38 from 'bip38'
|
||||
import wif from 'wif'
|
||||
import { Buffer } from 'safe-buffer'
|
||||
|
||||
class BitcoinCash {
|
||||
constructor(address) {
|
||||
constructor (address) {
|
||||
this._address = address
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ class BitcoinCash {
|
||||
* // 50700000000
|
||||
*/
|
||||
// Translate coins to satoshi value
|
||||
toSatoshi(coins) {
|
||||
toSatoshi (coins) {
|
||||
return sb.toSatoshi(coins)
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ class BitcoinCash {
|
||||
* // 507
|
||||
*/
|
||||
// Translate satoshi to coin value
|
||||
toBitcoinCash(satoshis) {
|
||||
toBitcoinCash (satoshis) {
|
||||
return sb.toBitcoin(satoshis)
|
||||
}
|
||||
|
||||
@@ -105,13 +104,13 @@ class BitcoinCash {
|
||||
* // 0.123
|
||||
*/
|
||||
// Translate satoshi to bits denomination
|
||||
toBits(satoshis) {
|
||||
toBits (satoshis) {
|
||||
return parseFloat(satoshis) / 100
|
||||
}
|
||||
|
||||
// Translate satoshi to bits denomination
|
||||
// TODO remove in 2.0
|
||||
satsToBits(satoshis) {
|
||||
satsToBits (satoshis) {
|
||||
return parseFloat(satoshis) / 100
|
||||
}
|
||||
|
||||
@@ -147,10 +146,10 @@ class BitcoinCash {
|
||||
* // IIYVhlo2Z6TWFjYX1+YM+7vQKz0m+zYdSe4eYpFLuAQDEZXqll7lZC8Au22VI2LLP5x+IerZckVk3QQPsA3e8/8=
|
||||
*/
|
||||
// sign message
|
||||
signMessageWithPrivKey(privateKeyWIF, message) {
|
||||
const network = privateKeyWIF.charAt(0) === "c" ? "testnet" : "mainnet"
|
||||
signMessageWithPrivKey (privateKeyWIF, message) {
|
||||
const network = privateKeyWIF.charAt(0) === 'c' ? 'testnet' : 'mainnet'
|
||||
let bitcoincash
|
||||
if (network === "mainnet") bitcoincash = coininfo.bitcoincash.main
|
||||
if (network === 'mainnet') bitcoincash = coininfo.bitcoincash.main
|
||||
else bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
@@ -161,7 +160,7 @@ class BitcoinCash {
|
||||
const privateKey = keyPair.d.toBuffer(32)
|
||||
return bitcoinMessage
|
||||
.sign(message, privateKey, keyPair.compressed)
|
||||
.toString("base64")
|
||||
.toString('base64')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +179,7 @@ class BitcoinCash {
|
||||
* // true
|
||||
*/
|
||||
// verify message
|
||||
verifyMessage(address, signature, message) {
|
||||
verifyMessage (address, signature, message) {
|
||||
return bitcoinMessage.verify(
|
||||
message,
|
||||
this._address.toLegacyAddress(address),
|
||||
@@ -222,8 +221,8 @@ class BitcoinCash {
|
||||
* // 1Ly4gqPddveYHMNkfjoXHanVszXpD3duKg
|
||||
*/
|
||||
// encode base58Check
|
||||
encodeBase58Check(hex) {
|
||||
return bs58.encode(Buffer.from(hex, "hex"))
|
||||
encodeBase58Check (hex) {
|
||||
return bs58.encode(Buffer.from(hex, 'hex'))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,8 +259,8 @@ class BitcoinCash {
|
||||
* // 00db04c2e6f104997cb04c956bf25da6078e559d303127f08b
|
||||
*/
|
||||
// decode base58Check
|
||||
decodeBase58Check(address) {
|
||||
return bs58.decode(address).toString("hex")
|
||||
decodeBase58Check (address) {
|
||||
return bs58.decode(address).toString('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +297,7 @@ class BitcoinCash {
|
||||
* // bitcoincash:qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03?amount=42&label=no%20prefix
|
||||
*/
|
||||
// encode bip21 url
|
||||
encodeBIP21(address, options, regtest = false) {
|
||||
encodeBIP21 (address, options, regtest = false) {
|
||||
return bip21.encode(
|
||||
this._address.toCashAddress(address, true, regtest),
|
||||
options
|
||||
@@ -335,7 +334,7 @@ class BitcoinCash {
|
||||
* // { address: 'qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03', options: { amount: 42, label: 'no prefix' } }
|
||||
*/
|
||||
// decode bip21 url
|
||||
decodeBIP21(url) {
|
||||
decodeBIP21 (url) {
|
||||
return bip21.decode(url)
|
||||
}
|
||||
|
||||
@@ -405,19 +404,19 @@ class BitcoinCash {
|
||||
* bchjs.BitcoinCash.getByteCount(inputs, outputs)
|
||||
* // 1780
|
||||
*/
|
||||
getByteCount(inputs, outputs) {
|
||||
getByteCount (inputs, outputs) {
|
||||
// from https://github.com/bitcoinjs/bitcoinjs-lib/issues/921#issuecomment-354394004
|
||||
let totalWeight = 0
|
||||
let hasWitness = false
|
||||
// assumes compressed pubkeys in all cases.
|
||||
const types = {
|
||||
inputs: {
|
||||
"MULTISIG-P2SH": 49 * 4,
|
||||
"MULTISIG-P2WSH": 6 + 41 * 4,
|
||||
"MULTISIG-P2SH-P2WSH": 6 + 76 * 4,
|
||||
'MULTISIG-P2SH': 49 * 4,
|
||||
'MULTISIG-P2WSH': 6 + 41 * 4,
|
||||
'MULTISIG-P2SH-P2WSH': 6 + 76 * 4,
|
||||
P2PKH: 148 * 4,
|
||||
P2WPKH: 108 + 41 * 4,
|
||||
"P2SH-P2WPKH": 108 + 64 * 4
|
||||
'P2SH-P2WPKH': 108 + 64 * 4
|
||||
},
|
||||
outputs: {
|
||||
P2SH: 32 * 4,
|
||||
@@ -427,26 +426,26 @@ class BitcoinCash {
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(inputs).forEach(function(key) {
|
||||
if (key.slice(0, 8) === "MULTISIG") {
|
||||
Object.keys(inputs).forEach(function (key) {
|
||||
if (key.slice(0, 8) === 'MULTISIG') {
|
||||
// ex. "MULTISIG-P2SH:2-3" would mean 2 of 3 P2SH MULTISIG
|
||||
const keyParts = key.split(":")
|
||||
const keyParts = key.split(':')
|
||||
if (keyParts.length !== 2) throw new Error(`invalid input: ${key}`)
|
||||
const newKey = keyParts[0]
|
||||
const mAndN = keyParts[1].split("-").map(function(item) {
|
||||
const mAndN = keyParts[1].split('-').map(function (item) {
|
||||
return parseInt(item)
|
||||
})
|
||||
|
||||
totalWeight += types.inputs[newKey] * inputs[key]
|
||||
const multiplyer = newKey === "MULTISIG-P2SH" ? 4 : 1
|
||||
const multiplyer = newKey === 'MULTISIG-P2SH' ? 4 : 1
|
||||
totalWeight += (73 * mAndN[0] + 34 * mAndN[1]) * multiplyer
|
||||
} else {
|
||||
totalWeight += types.inputs[key] * inputs[key]
|
||||
}
|
||||
if (key.indexOf("W") >= 0) hasWitness = true
|
||||
if (key.indexOf('W') >= 0) hasWitness = true
|
||||
})
|
||||
|
||||
Object.keys(outputs).forEach(function(key) {
|
||||
Object.keys(outputs).forEach(function (key) {
|
||||
totalWeight += types.outputs[key] * outputs[key]
|
||||
})
|
||||
|
||||
@@ -479,7 +478,7 @@ class BitcoinCash {
|
||||
* )
|
||||
* // 6PYUAPLwLSEjWSAfoe9NTSPkMZXnJA8j8EFJtKaeSnP18RCouutBrS2735
|
||||
*/
|
||||
encryptBIP38(privKeyWIF, passphrase) {
|
||||
encryptBIP38 (privKeyWIF, passphrase) {
|
||||
const decoded = wif.decode(privKeyWIF)
|
||||
|
||||
return bip38.encrypt(decoded.privateKey, decoded.compressed, passphrase)
|
||||
@@ -509,14 +508,14 @@ class BitcoinCash {
|
||||
* )
|
||||
* // cSx7KzdH9EcvDEireu2WYpGnXdFYpta7sJUNt5kVCJgA7kcAU8Gm
|
||||
*/
|
||||
decryptBIP38(encryptedKey, passphrase, network = "mainnet") {
|
||||
decryptBIP38 (encryptedKey, passphrase, network = 'mainnet') {
|
||||
const decryptedKey = bip38.decrypt(encryptedKey, passphrase)
|
||||
let prefix
|
||||
if (network === "testnet") prefix = 0xef
|
||||
if (network === 'testnet') prefix = 0xef
|
||||
else prefix = 0x80
|
||||
|
||||
return wif.encode(prefix, decryptedKey.privateKey, decryptedKey.compressed)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BitcoinCash
|
||||
export default BitcoinCash
|
||||
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
This library interacts with the REST API endpoints in bch-api that communicate
|
||||
with the Blockbook API.
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
|
||||
let _this
|
||||
|
||||
class Blockbook {
|
||||
constructor(config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
// console.log(`Blockbook apiToken: ${this.apiToken}`)
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Blockbook.balance() balance()
|
||||
* @apiName Blockbook Balance
|
||||
* @apiGroup Blockbook
|
||||
* @apiDescription Return Balance about an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Blockbook.balance('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* //{
|
||||
* // "page": 1,
|
||||
* // "totalPages": 1,
|
||||
* // "itemsOnPage": 1000,
|
||||
* // "address": "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c",
|
||||
* // "balance": "6000000",
|
||||
* // "totalReceived": "10185868",
|
||||
* // "totalSent": "4185868",
|
||||
* // "unconfirmedBalance": "0",
|
||||
* // "unconfirmedTxs": 0,
|
||||
* // "txs": 41,
|
||||
* // "txids": [
|
||||
* // "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "2f902dec880568511cefa87b9dd761563edeba9c8ba784dc9fca2f7c8c4e6f97",
|
||||
* // "eea57285462dd70dadcd431fc814857b3f81fe4d0a059a8c02c12fd7d33c02d1",
|
||||
* // "282b3b296b6aed7122586ed69f7a57d35584eaf94a4d1b1ad7d1b05d36cb79d1",
|
||||
* // "ac444896b3e32d17824fa6573eed3b89768c5c9085b7a71f3ba88e9d5ba67355",
|
||||
* // "a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1",
|
||||
* // "5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e",
|
||||
* // "54edaa42ff3d6559884a84ebb9bf5ef255635902f5f23b4854245d6b093d41d4",
|
||||
* // "2b0825188e909410a20a6fbdc58ff5ccf368844273f93f551222c91e6d0fa888",
|
||||
* // "7a12ea2c83d0c8a5d0f643974b0f04bc19be185c9011ed8fc33255a61d3198bb",
|
||||
* // "bd6aeeb0748251bb5dba7252ac766fb208c9909d7663c099568225d3bc998a7f",
|
||||
* // "7898bb1a8c5d933f9c2b24270522e8705b897c2f8d5b1c3477a6b952f2fe22ae",
|
||||
* // "8d35668a6838de8faa8dff5d8dedeb114bd8ffe6ae73d926264bc8328b2c46b5",
|
||||
* // "741b4692a14088438ab142cf0865fcbf3977aa7dbd2aafa9bf5f45e75e5e199a",
|
||||
* // "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
|
||||
* // "376de807f63253584c3edb80f92832ea90e8fdd7d8e68b2602e230d1b015e311",
|
||||
* // "eb5d0902e4a303f38223ecebb06bbf14da8a07d1ebb2d77a89dfb9e00e286c10",
|
||||
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
|
||||
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
|
||||
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
|
||||
* // "1cafaacdfb85b0c33f496bf2c03c4f2ede508479bbad56ae99164dfd823a823f",
|
||||
* // "788add6a5cd961bdf7ca6145a3112462e0e51523d84a4a48047407a795433947",
|
||||
* // "49f09b616824a05eb14ae50c9fbc5d6a0c414d66a7d0b217aa75340c00ed8b85",
|
||||
* // "00f075805adf2a34563ecca33071d660a4c1e91d7d7045282c54554ee4844739",
|
||||
* // "8d048fd00cf375ba9bbea43730da1ce86fc3cb4026e1dd0e751c07fa50a652c6",
|
||||
* // "ac0e82ea84f93444602a99199dd80793f79a8ece5ac86156d2fff34f0bad44b2",
|
||||
* // "342f6815845797e0748f4716c923a0dc9b87de649b69925b36422f6d2dc23b7f",
|
||||
* // "0839a16b2411a1220c64c4d32a4c9b1e77ac5f47558921cf7c0a2adf25e0eb41",
|
||||
* // "62f42995711cb0308dac4203c5deaa2985f120559131e66aebc5164426fb6cee",
|
||||
* // "2ada093fe13c9c04da9582bb304923ce08312eaec92560b9128cdb9e93c5f52f",
|
||||
* // "e0aadd861a06993e39af932bb0b9ad69e7b37ef5843a13c6724789e1c94f3513",
|
||||
* // "925490ce60334aca204d82a61b634d89fe4cc9de429c7b6cb5ec420df3e3be5e",
|
||||
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
|
||||
* // "369a589173969c1c882cfb2d82b1e0ec90076ec827ff9d0f32ffc115690c93c3",
|
||||
* // "43324ef3f5fdd55b645ba14de8c0667be9b223d60b3d1dcd76cf8fdeb0fd32e4",
|
||||
* // "d2985c9b1c5c18fc2f6a963b7cb850606c4a18fcb85619057211ce3c8bcec696",
|
||||
* // "dded59fe377517e52918deae8912a096658ebf5ae61992d39953c8bc3932a11b",
|
||||
* // "2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d",
|
||||
* // "544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026",
|
||||
* // "81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3"
|
||||
* // ]
|
||||
* //}
|
||||
*
|
||||
*(async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Blockbook.balance(['bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c','1BFHGm4HzqgXXyNX8n7DsQno5DAC4iLMRA']);
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* //[
|
||||
* // {
|
||||
* // "page": 1,
|
||||
* // "totalPages": 1,
|
||||
* // "itemsOnPage": 1000,
|
||||
* // "address": "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c",
|
||||
* // "balance": "6000000",
|
||||
* // "totalReceived": "10185868",
|
||||
* // "totalSent": "4185868",
|
||||
* // "unconfirmedBalance": "0",
|
||||
* // "unconfirmedTxs": 0,
|
||||
* // "txs": 41,
|
||||
* // "txids": [
|
||||
* // "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "2f902dec880568511cefa87b9dd761563edeba9c8ba784dc9fca2f7c8c4e6f97",
|
||||
* // "eea57285462dd70dadcd431fc814857b3f81fe4d0a059a8c02c12fd7d33c02d1",
|
||||
* // "282b3b296b6aed7122586ed69f7a57d35584eaf94a4d1b1ad7d1b05d36cb79d1",
|
||||
* // "ac444896b3e32d17824fa6573eed3b89768c5c9085b7a71f3ba88e9d5ba67355",
|
||||
* // "a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1",
|
||||
* // "5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e",
|
||||
* // "54edaa42ff3d6559884a84ebb9bf5ef255635902f5f23b4854245d6b093d41d4",
|
||||
* // "2b0825188e909410a20a6fbdc58ff5ccf368844273f93f551222c91e6d0fa888",
|
||||
* // "7a12ea2c83d0c8a5d0f643974b0f04bc19be185c9011ed8fc33255a61d3198bb",
|
||||
* // "bd6aeeb0748251bb5dba7252ac766fb208c9909d7663c099568225d3bc998a7f",
|
||||
* // "7898bb1a8c5d933f9c2b24270522e8705b897c2f8d5b1c3477a6b952f2fe22ae",
|
||||
* // "8d35668a6838de8faa8dff5d8dedeb114bd8ffe6ae73d926264bc8328b2c46b5",
|
||||
* // "741b4692a14088438ab142cf0865fcbf3977aa7dbd2aafa9bf5f45e75e5e199a",
|
||||
* // "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
|
||||
* // "376de807f63253584c3edb80f92832ea90e8fdd7d8e68b2602e230d1b015e311",
|
||||
* // "eb5d0902e4a303f38223ecebb06bbf14da8a07d1ebb2d77a89dfb9e00e286c10",
|
||||
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
|
||||
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
|
||||
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
|
||||
* // "1cafaacdfb85b0c33f496bf2c03c4f2ede508479bbad56ae99164dfd823a823f",
|
||||
* // "788add6a5cd961bdf7ca6145a3112462e0e51523d84a4a48047407a795433947",
|
||||
* // "49f09b616824a05eb14ae50c9fbc5d6a0c414d66a7d0b217aa75340c00ed8b85",
|
||||
* // "00f075805adf2a34563ecca33071d660a4c1e91d7d7045282c54554ee4844739",
|
||||
* // "8d048fd00cf375ba9bbea43730da1ce86fc3cb4026e1dd0e751c07fa50a652c6",
|
||||
* // "ac0e82ea84f93444602a99199dd80793f79a8ece5ac86156d2fff34f0bad44b2",
|
||||
* // "342f6815845797e0748f4716c923a0dc9b87de649b69925b36422f6d2dc23b7f",
|
||||
* // "0839a16b2411a1220c64c4d32a4c9b1e77ac5f47558921cf7c0a2adf25e0eb41",
|
||||
* // "62f42995711cb0308dac4203c5deaa2985f120559131e66aebc5164426fb6cee",
|
||||
* // "2ada093fe13c9c04da9582bb304923ce08312eaec92560b9128cdb9e93c5f52f",
|
||||
* // "e0aadd861a06993e39af932bb0b9ad69e7b37ef5843a13c6724789e1c94f3513",
|
||||
* // "925490ce60334aca204d82a61b634d89fe4cc9de429c7b6cb5ec420df3e3be5e",
|
||||
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
|
||||
* // "369a589173969c1c882cfb2d82b1e0ec90076ec827ff9d0f32ffc115690c93c3",
|
||||
* // "43324ef3f5fdd55b645ba14de8c0667be9b223d60b3d1dcd76cf8fdeb0fd32e4",
|
||||
* // "d2985c9b1c5c18fc2f6a963b7cb850606c4a18fcb85619057211ce3c8bcec696",
|
||||
* // "dded59fe377517e52918deae8912a096658ebf5ae61992d39953c8bc3932a11b",
|
||||
* // "2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d",
|
||||
* // "544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026",
|
||||
* // "81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3"
|
||||
* // ]
|
||||
* // },
|
||||
* // {
|
||||
* // "page": 1,
|
||||
* // "totalPages": 1,
|
||||
* // "itemsOnPage": 1000,
|
||||
* // "address": "bitcoincash:qpcxf2sv9hjw08nvpgffpamfus9nmksm3chv5zqtnz",
|
||||
* // "balance": "0",
|
||||
* // "totalReceived": "36781097",
|
||||
* // "totalSent": "36781097",
|
||||
* // "unconfirmedBalance": "0",
|
||||
* // "unconfirmedTxs": 0,
|
||||
* // "txs": 11,
|
||||
* // "txids": [
|
||||
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
|
||||
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
|
||||
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
|
||||
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
|
||||
* // "f737485aaee3c10b13013fa109bb6294b099246134ca9885f4cc332dbc6c9bb4",
|
||||
* // "decd5b9c0c959e4e543182093e8f7f8bc7a6ecd96a8a062daaeff3667f8feca7",
|
||||
* // "94e69a627a34ae27fca81d15fff4323a7ce1f7c275c7485762ce018221017632",
|
||||
* // "e67c70787af7f3506263c9eda007f3d2d24bd750ff95b5c50a120d9118dfd807",
|
||||
* // "8e5e00704a147d54028f94d52df7730e821b9c6cd4bd29494e5636f49c199d6a",
|
||||
* // "15102827c108566ea5daf725c09079c1a3f42ef99d1eb68ea8c584f7b16ab87a",
|
||||
* // "cc27be8846276612dfce5924b7be96556212f0f0e62bd17641732175edb9911e"
|
||||
* // ]
|
||||
* // }
|
||||
* //
|
||||
*
|
||||
*/
|
||||
|
||||
async balance(address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockbook/balance/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockbook/balance`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Blockbook.utxo() utxo()
|
||||
* @apiName Blockbook Utxo
|
||||
* @apiGroup Blockbook
|
||||
* @apiDescription Return list of uxto for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Blockbook.utxo('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* [{
|
||||
* "txid": "7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
|
||||
* "vout": 0,
|
||||
* "value": "1000",
|
||||
* "height": 604392,
|
||||
* "confirmations": 25530,
|
||||
* "satoshis": 1000
|
||||
* }]
|
||||
*
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Blockbook.utxo([
|
||||
* "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
* "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"
|
||||
* ]);
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* [
|
||||
* [{
|
||||
* "txid": "7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
|
||||
* "vout": 0,
|
||||
* "value": "1000",
|
||||
* "height": 604392,
|
||||
* "confirmations": 25530,
|
||||
* "satoshis": 1000
|
||||
* }],
|
||||
* [{
|
||||
* "txid": "d5228d2cdc77fbe5a9aa79f19b0933b6802f9f0067f42847fc4fe343664723e5",
|
||||
* "vout": 0,
|
||||
* "value": "6000",
|
||||
* "confirmations": 0,
|
||||
* "satoshis": 6000
|
||||
* }]
|
||||
* ]
|
||||
*/
|
||||
async utxo(address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockbook/utxos/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockbook/utxos`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Blockbook.tx() tx()
|
||||
* @apiName Blockbook Tx
|
||||
* @apiGroup Blockbook
|
||||
* @apiDescription Get details on a transaction.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txDetails = await bchjs.Blockbook.tx(`5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392`);
|
||||
* console.log(txDetails);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
*/
|
||||
async tx(txid) {
|
||||
try {
|
||||
// Handle single txid.
|
||||
if (typeof txid === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockbook/tx/${txid}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(txid)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockbook/tx`,
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input txid must be a string or array of strings.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Blockbook
|
||||
+251
-178
@@ -3,23 +3,22 @@
|
||||
- Add blockhash functionality back into getTxOutProof
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Blockchain {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,16 +39,19 @@ class Blockchain {
|
||||
* })()
|
||||
* // 241decef88889efac8e6ce428a8ac696fdde5972eceed97e1fb58d6106af31d5
|
||||
*/
|
||||
async getBestBlockHash() {
|
||||
async getBestBlockHash () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBestBlockHash`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getBestBlockHash`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +60,8 @@ class Blockchain {
|
||||
* @apiName getBlock
|
||||
* @apiGroup Blockchain
|
||||
* @apiDescription
|
||||
* If verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is true, returns an Object with information about block hash.
|
||||
*
|
||||
* If verbose is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is 1, returns an Object with information about block hash.
|
||||
* If verbose is 2, returns an Object with information about block hash and information about tx.
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
@@ -70,34 +72,47 @@ class Blockchain {
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // { hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
* // confirmations: 528236,
|
||||
* // size: 216,
|
||||
* // height: 1000,
|
||||
* // version: 1,
|
||||
* // versionHex: '00000001',
|
||||
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
* // tx:
|
||||
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
|
||||
* // time: 1232346882,
|
||||
* // mediantime: 1232344831,
|
||||
* // nonce: 2595206198,
|
||||
* // bits: '1d00ffff',
|
||||
* // difficulty: 1,
|
||||
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
|
||||
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }
|
||||
* // {
|
||||
* // hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
* // confirmations: 528236,
|
||||
* // size: 216,
|
||||
* // height: 1000,
|
||||
* // version: 1,
|
||||
* // versionHex: '00000001',
|
||||
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
* // tx:
|
||||
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
|
||||
* // time: 1232346882,
|
||||
* // mediantime: 1232344831,
|
||||
* // nonce: 2595206198,
|
||||
* // bits: '1d00ffff',
|
||||
* // difficulty: 1,
|
||||
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
|
||||
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
|
||||
* // }
|
||||
*/
|
||||
async getBlock(blockhash, verbose = true) {
|
||||
async getBlock (blockhash, verbosity = 1) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
// Input validation
|
||||
if (!blockhash || typeof blockhash !== 'string') {
|
||||
throw new Error('blockhash must be a string')
|
||||
}
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/getblock`,
|
||||
{
|
||||
blockhash,
|
||||
verbosity
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,16 +153,19 @@ class Blockchain {
|
||||
* // timeout: 1493596800,
|
||||
* // since: 419328 } } }
|
||||
*/
|
||||
async getBlockchainInfo() {
|
||||
async getBlockchainInfo () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockchainInfo`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getBlockchainInfo`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,16 +187,22 @@ class Blockchain {
|
||||
* })()
|
||||
* // 529235
|
||||
*/
|
||||
async getBlockCount() {
|
||||
async getBlockCount () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockCount`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getBlockCount`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
console.log('Error in bch-js/blockchain.js/getBlockCount()')
|
||||
console.log('blockchain.js restURL: ', this.restURL)
|
||||
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,18 +224,21 @@ class Blockchain {
|
||||
* })()
|
||||
* // [ '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' ]
|
||||
*/
|
||||
async getBlockHash(height = 1) {
|
||||
if (typeof height !== "string") height = JSON.stringify(height)
|
||||
async getBlockHash (height = 1) {
|
||||
if (typeof height !== 'string') height = JSON.stringify(height)
|
||||
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockHash/${height}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getBlockHash/${height}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,13 +274,13 @@ class Blockchain {
|
||||
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }]
|
||||
*/
|
||||
async getBlockHeader(hash, verbose = true) {
|
||||
async getBlockHeader (hash, verbose = true) {
|
||||
try {
|
||||
// Handle single hash.
|
||||
if (typeof hash === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
if (typeof hash === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -261,22 +288,27 @@ class Blockchain {
|
||||
// Handle array of hashes.
|
||||
} else if (Array.isArray(hash)) {
|
||||
// Dev note: must use axios.post for unit test stubbing.
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getBlockHeader`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/getBlockHeader`,
|
||||
{
|
||||
hashes: hash,
|
||||
verbose: verbose
|
||||
verbose
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input hash must be a string or array of strings.`)
|
||||
throw new Error('Input hash must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log('Error in bch-js/blockchain.js/getBlockHeader() error: ', error)
|
||||
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,16 +342,19 @@ class Blockchain {
|
||||
* // branchlen: 1,
|
||||
* // status: 'valid-headers' } ]
|
||||
*/
|
||||
async getChainTips() {
|
||||
async getChainTips () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getChainTips`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getChainTips`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,47 +377,56 @@ class Blockchain {
|
||||
*
|
||||
* // 702784497476.8376
|
||||
*/
|
||||
async getDifficulty() {
|
||||
async getDifficulty () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getDifficulty`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getDifficulty`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// getMempoolAncestors
|
||||
async getMempoolAncestors(txid, verbose = false) {
|
||||
if (typeof txid !== "string") txid = JSON.stringify(txid)
|
||||
async getMempoolAncestors (txid, verbose = false) {
|
||||
if (typeof txid !== 'string') txid = JSON.stringify(txid)
|
||||
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getMempoolDescendants(txid, verbose = false) {
|
||||
if (typeof txid !== "string") txid = JSON.stringify(txid)
|
||||
async getMempoolDescendants (txid, verbose = false) {
|
||||
if (typeof txid !== 'string') txid = JSON.stringify(txid)
|
||||
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,34 +511,37 @@ class Blockchain {
|
||||
* // }
|
||||
* // ]
|
||||
*/
|
||||
async getMempoolEntry(txid) {
|
||||
//if (typeof txid !== "string") txid = JSON.stringify(txid)
|
||||
async getMempoolEntry (txid) {
|
||||
// if (typeof txid !== "string") txid = JSON.stringify(txid)
|
||||
|
||||
try {
|
||||
if (typeof txid === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolEntry/${txid}`,
|
||||
_this.axiosOptions
|
||||
if (typeof txid === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getMempoolEntry/${txid}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
// Dev note: must use axios.post for unit test stubbing.
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getMempoolEntry`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/getMempoolEntry`,
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,16 +568,19 @@ class Blockchain {
|
||||
* // maxmempool: 300000000,
|
||||
* // mempoolminfee: 0 }
|
||||
*/
|
||||
async getMempoolInfo() {
|
||||
async getMempoolInfo () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolInfo`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getMempoolInfo`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,16 +618,19 @@ class Blockchain {
|
||||
* // depends:
|
||||
* // [ 'e25682caafc7000645d59f4c11d8d594b2943979b9d8fafb9f946e2b35c21b7e' ] },]
|
||||
*/
|
||||
async getRawMempool(verbose = false) {
|
||||
async getRawMempool (verbose = false) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getRawMempool?vebose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/getRawMempool?vebose=${verbose}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,36 +653,41 @@ class Blockchain {
|
||||
*
|
||||
* // null
|
||||
*/
|
||||
async getTxOut(txid, n, include_mempool = true) {
|
||||
async getTxOut (txid, n, includeMempool = true) {
|
||||
try {
|
||||
// Input validation
|
||||
if (typeof txid !== "string" || txid.length !== 64)
|
||||
throw new Error(`txid needs to be a proper transaction ID`)
|
||||
if (typeof txid !== 'string' || txid.length !== 64) {
|
||||
throw new Error('txid needs to be a proper transaction ID')
|
||||
}
|
||||
|
||||
if (isNaN(n)) throw new Error(`n must be an integer`)
|
||||
if (isNaN(n)) throw new Error('n must be an integer')
|
||||
|
||||
if (typeof include_mempool !== "boolean")
|
||||
throw new Error(`include_mempool input must be of type boolean`)
|
||||
if (typeof includeMempool !== 'boolean') {
|
||||
throw new Error('includeMempool input must be of type boolean')
|
||||
}
|
||||
|
||||
// Send the request to the REST API.
|
||||
// const response = await axios.get(
|
||||
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}`,
|
||||
// _this.axiosOptions
|
||||
// const response = await this.axios.get(
|
||||
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?includeMempool=${includeMempool}`,
|
||||
// this.axiosOptions
|
||||
// )
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getTxOut`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/getTxOut`,
|
||||
{
|
||||
txid: txid,
|
||||
txid,
|
||||
vout: n,
|
||||
mempool: include_mempool
|
||||
mempool: includeMempool
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,73 +727,85 @@ class Blockchain {
|
||||
* // "010000007de867cc8adc5cc8fb6b898ca4462cf9fd667d7830a275277447e60800000000338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe021f7449ffff001d36b4af9a0100000001338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe0101"
|
||||
* // ]
|
||||
*/
|
||||
async getTxOutProof(txids) {
|
||||
async getTxOutProof (txids) {
|
||||
try {
|
||||
// Single txid.
|
||||
if (typeof txids === "string") {
|
||||
const path = `${this.restURL}blockchain/getTxOutProof/${txids}`
|
||||
//if (blockhash) path = `${path}?blockhash=${blockhash}`
|
||||
if (typeof txids === 'string') {
|
||||
const path = `${this.restURL}full-node/blockchain/getTxOutProof/${txids}`
|
||||
// if (blockhash) path = `${path}?blockhash=${blockhash}`
|
||||
|
||||
const response = await axios.get(path, _this.axiosOptions)
|
||||
const response = await this.axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
|
||||
// Array of txids.
|
||||
} else if (Array.isArray(txids)) {
|
||||
// Dev note: must use axios.post for unit test stubbing.
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getTxOutProof`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/getTxOutProof`,
|
||||
{
|
||||
txids: txids
|
||||
txids
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
async preciousBlock(blockhash) {
|
||||
async preciousBlock (blockhash) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/preciousBlock/${blockhash}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/preciousBlock/${blockhash}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
async pruneBlockchain(height) {
|
||||
async pruneBlockchain (height) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/pruneBlockchain/${height}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/pruneBlockchain/${height}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
async verifyChain(checklevel = 3, nblocks = 6) {
|
||||
async verifyChain (checklevel = 3, nblocks = 6) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -776,36 +846,39 @@ class Blockchain {
|
||||
* // "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
* // ]
|
||||
*/
|
||||
async verifyTxOutProof(proof) {
|
||||
async verifyTxOutProof (proof) {
|
||||
try {
|
||||
// Single block
|
||||
if (typeof proof === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/verifyTxOutProof/${proof}`,
|
||||
_this.axiosOptions
|
||||
if (typeof proof === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/blockchain/verifyTxOutProof/${proof}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Array of hashes.
|
||||
} else if (Array.isArray(proof)) {
|
||||
// Dev note: must use axios.post for unit test stubbing.
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/verifyTxOutProof`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}full-node/blockchain/verifyTxOutProof`,
|
||||
{
|
||||
proofs: proof
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Blockchain
|
||||
export default Blockchain
|
||||
|
||||
+16
-17
@@ -2,23 +2,22 @@
|
||||
API endpoints for basic control and information of the full node.
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this // Global reference to the instance of this class.
|
||||
// let _this // Global reference to the instance of this class.
|
||||
|
||||
class Control {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,11 +66,11 @@ class Control {
|
||||
* warnings:
|
||||
* 'Warning: Unknown block versions being mined! It\'s possible unknown rules are in effect' }}
|
||||
*/
|
||||
async getNetworkInfo() {
|
||||
async getNetworkInfo () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}control/getNetworkInfo`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/control/getNetworkInfo`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -80,11 +79,11 @@ class Control {
|
||||
}
|
||||
}
|
||||
|
||||
async getMemoryInfo() {
|
||||
async getMemoryInfo () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}control/getMemoryInfo`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/control/getMemoryInfo`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -105,4 +104,4 @@ class Control {
|
||||
// }
|
||||
}
|
||||
|
||||
module.exports = Control
|
||||
export default Control
|
||||
|
||||
+11
-11
@@ -1,5 +1,5 @@
|
||||
const randomBytes = require("randombytes")
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
import randomBytes from 'randombytes'
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
|
||||
class Crypto {
|
||||
/**
|
||||
@@ -22,11 +22,11 @@ class Crypto {
|
||||
* // buffer from hex
|
||||
* let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
|
||||
* bchjs.Crypto.sha256(buffer)
|
||||
* // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
|
||||
* // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
|
||||
*
|
||||
* */
|
||||
// Translate address from any address format into a specific format.
|
||||
static sha256(buffer) {
|
||||
static sha256 (buffer) {
|
||||
return Bitcoin.crypto.sha256(buffer)
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class Crypto {
|
||||
* bchjs.Crypto.ripemd160(buffer)
|
||||
* // <Buffer 5f 95 6a 88 86 30 51 ea 52 15 d8 97 0c ed 8e 21 8e b6 15 cf>
|
||||
* */
|
||||
static ripemd160(buffer) {
|
||||
static ripemd160 (buffer) {
|
||||
return Bitcoin.crypto.ripemd160(buffer)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class Crypto {
|
||||
* @api Crypto.hash256() hash256()
|
||||
* @apiName hash256
|
||||
* @apiGroup Crypto
|
||||
* @apiDescription Utility for creating double sha256 hash digests of data.
|
||||
* @apiDescription Utility for creating double sha256 hash digests of buffer encoded data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // buffer from hex
|
||||
@@ -78,7 +78,7 @@ class Crypto {
|
||||
* bchjs.Crypto.hash256(buffer)
|
||||
* // <Buffer 68 8f 1d 02 9e d5 4c 34 d0 32 0b 83 8b f6 fc 64 f6 2f 38 a6 e9 30 a0 af 5b db 4e 27 d1 a6 84 cd>
|
||||
* */
|
||||
static hash256(buffer) {
|
||||
static hash256 (buffer) {
|
||||
return Bitcoin.crypto.hash256(buffer)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class Crypto {
|
||||
* @api Crypto.hash160() hash160()
|
||||
* @apiName hash160
|
||||
* @apiGroup Crypto
|
||||
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of data.
|
||||
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of buffer encoded data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // buffer from hex
|
||||
@@ -104,7 +104,7 @@ class Crypto {
|
||||
* bchjs.Crypto.hash160(buffer)
|
||||
*
|
||||
* */
|
||||
static hash160(buffer) {
|
||||
static hash160 (buffer) {
|
||||
return Bitcoin.crypto.hash160(buffer)
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ class Crypto {
|
||||
* bchjs.Crypto.randomBytes(32)
|
||||
* // <Buffer ec 44 73 72 ea 48 3e 08 a5 0a 62 b8 40 0f 69 64 a7 75 35 af 20 3d e1 6d ce 3b f9 37 11 19 2b c6>
|
||||
* */
|
||||
static randomBytes(size = 16) {
|
||||
static randomBytes (size = 16) {
|
||||
return randomBytes(size)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Crypto
|
||||
export default Crypto
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
|
||||
class DSProof {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api DSProof.getDSProof() getDSProof()
|
||||
* @apiName getDSProof
|
||||
* @apiGroup DSProof
|
||||
* @apiDescription Checks if a transaction generated a double-spend proof.
|
||||
*
|
||||
* If a double-spend is attempted, one of the transactions will generate a
|
||||
* 'double spend proof'. This call can be used to check if a transaction
|
||||
* generated such a proof.
|
||||
*
|
||||
* Merchants should wait 3-5 seconds after receiving notification of a
|
||||
* transaction before calling this endpoint, to see if the TXID generated a
|
||||
* proof. If this method returns no data, then the TX can be considered 'safe'
|
||||
* and not a double spend. If proof data is returned by this method, then
|
||||
* the transaction generated a proof and can be considered a 'double spend'.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
* const result = await bchjs.DSProof.getDSProof(txid)
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* null
|
||||
*/
|
||||
async getDSProof (txid) {
|
||||
try {
|
||||
if (!txid) {
|
||||
throw new Error('txid is required')
|
||||
}
|
||||
if (txid.length !== 64) {
|
||||
throw new Error(`txid must be of length 64 (not ${txid.length})`)
|
||||
}
|
||||
const response = await _this.axios.get(
|
||||
`${this.restURL}full-node/dsproof/getdsproof/${txid}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DSProof
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Utility library for converting units with eCash.
|
||||
*/
|
||||
|
||||
class eCash {
|
||||
/**
|
||||
* @api eCash.toSatoshi() toSatoshi()
|
||||
* @apiName toSatoshi
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert XEC units into satoshi units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 10,704.35 XEC to satoshis:
|
||||
* bchjs.eCash.toSatoshi(10704.35)
|
||||
* // 1070435
|
||||
*/
|
||||
toSatoshi (xec) {
|
||||
if (typeof xec !== 'number') {
|
||||
throw new Error('input must be a floating number representing XEC')
|
||||
}
|
||||
|
||||
return Math.floor(xec * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api eCash.toXec() toXec()
|
||||
* @apiName toXec
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert satoshi units to XEC units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 1,070,435 satoshis to XEC:
|
||||
* bchjs.eCash.toSatoshi(1070435)
|
||||
* // 10704.35
|
||||
*/
|
||||
toXec (sats) {
|
||||
if (typeof sats !== 'number') {
|
||||
throw new Error('input must be a floating number representing satoshis')
|
||||
}
|
||||
|
||||
return sats / 100
|
||||
}
|
||||
}
|
||||
|
||||
export default eCash
|
||||
+20
-16
@@ -1,10 +1,11 @@
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const coininfo = require("coininfo")
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import coininfo from '@psf/coininfo'
|
||||
|
||||
class ECPair {
|
||||
static setAddress(address) {
|
||||
static setAddress (address) {
|
||||
ECPair._address = address
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.fromWIF() fromWIF()
|
||||
* @apiName fromWIF
|
||||
@@ -20,20 +21,19 @@ class ECPair {
|
||||
* let wif = 'cSNLj6xeg3Yg2rfcgKoWNx4MiAgn9ugCUUro37UDEhn6CzeYqjWW'
|
||||
* bchjs.ECPair.fromWIF(wif)
|
||||
* */
|
||||
static fromWIF(privateKeyWIF) {
|
||||
static fromWIF (privateKeyWIF) {
|
||||
let network
|
||||
if (privateKeyWIF[0] === "L" || privateKeyWIF[0] === "K")
|
||||
network = "mainnet"
|
||||
else if (privateKeyWIF[0] === "c") network = "testnet"
|
||||
if (privateKeyWIF[0] === 'L' || privateKeyWIF[0] === 'K') { network = 'mainnet' } else if (privateKeyWIF[0] === 'c') network = 'testnet'
|
||||
|
||||
let bitcoincash
|
||||
if (network === "mainnet") bitcoincash = coininfo.bitcoincash.main
|
||||
if (network === 'mainnet') bitcoincash = coininfo.bitcoincash.main
|
||||
else bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
|
||||
return Bitcoin.ECPair.fromWIF(privateKeyWIF, bitcoincashBitcoinJSLib)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.toWIF() toWIF()
|
||||
* @apiName toWIF
|
||||
@@ -57,17 +57,18 @@ class ECPair {
|
||||
* bchjs.ECPair.toWIF(ecpair);
|
||||
* // cT3tJP7BnjFJSAHbooMXrY8E9t2AFj37amSBAYFMeHfqPqPgD4ZA
|
||||
* */
|
||||
static toWIF(ecpair) {
|
||||
static toWIF (ecpair) {
|
||||
return ecpair.toWIF()
|
||||
}
|
||||
|
||||
static sign(ecpair, buffer) {
|
||||
static sign (ecpair, buffer) {
|
||||
return ecpair.sign(buffer)
|
||||
}
|
||||
|
||||
static verify(ecpair, buffer, signature) {
|
||||
static verify (ecpair, buffer, signature) {
|
||||
return ecpair.verify(buffer, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.fromPublicKey() fromPublicKey()
|
||||
* @apiName fromPublicKey
|
||||
@@ -83,9 +84,10 @@ class ECPair {
|
||||
* let pubkeyBuffer = Buffer.from("024a6d0737a23c472d078d78c1cbc3c2bbf8767b48e72684ff03a911b463da7fa6", 'hex');
|
||||
* bchjs.ECPair.fromPublicKey(pubkeyBuffer);
|
||||
* */
|
||||
static fromPublicKey(pubkeyBuffer) {
|
||||
static fromPublicKey (pubkeyBuffer) {
|
||||
return Bitcoin.ECPair.fromPublicKeyBuffer(pubkeyBuffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.toPublicKey() toPublicKey()
|
||||
* @apiName toPublicKey
|
||||
@@ -105,9 +107,10 @@ class ECPair {
|
||||
* bchjs.ECPair.toPublicKey(ecpair);
|
||||
* //
|
||||
* */
|
||||
static toPublicKey(ecpair) {
|
||||
static toPublicKey (ecpair) {
|
||||
return ecpair.getPublicKeyBuffer()
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.toLegacyAddress() toLegacyAddress()
|
||||
* @apiName toLegacyAddress
|
||||
@@ -131,9 +134,10 @@ class ECPair {
|
||||
* bchjs.ECPair.toLegacyAddress(ecpair);
|
||||
* // mg4PygFcXoyNJGJkM2Dcpe25av9wXzz1My
|
||||
* */
|
||||
static toLegacyAddress(ecpair) {
|
||||
static toLegacyAddress (ecpair) {
|
||||
return ecpair.getAddress()
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ecpair.toCashAddress() toCashAddress()
|
||||
* @apiName toCashAddress
|
||||
@@ -157,9 +161,9 @@ class ECPair {
|
||||
* bchjs.ECPair.toCashAddress(ecpair);
|
||||
* // bchtest:qqzly4vrcxcjw62u4yq4nv86ltk2mc9v0yvq8mvj6m
|
||||
* */
|
||||
static toCashAddress(ecpair, regtest = false) {
|
||||
static toCashAddress (ecpair, regtest = false) {
|
||||
return ECPair._address.toCashAddress(ecpair.getAddress(), true, regtest)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ECPair
|
||||
export default ECPair
|
||||
|
||||
+267
-75
@@ -2,25 +2,30 @@
|
||||
This library interacts with the ElectrumX bch-api REST API endpoints operated
|
||||
by FullStack.cash
|
||||
*/
|
||||
// Public npm libraries
|
||||
import axios from 'axios'
|
||||
|
||||
const axios = require("axios")
|
||||
// Local libraries.
|
||||
import Blockchain from './blockchain.js'
|
||||
// const Address = require('./address')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class ElectrumX {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// Encapsulate dependencies
|
||||
this.blockchain = new Blockchain(config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +35,7 @@ class ElectrumX {
|
||||
* @apiDescription Return a list of uxtos for an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Electrumx.utxo('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
|
||||
* console.log(utxo);
|
||||
@@ -88,33 +93,38 @@ class ElectrumX {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async utxo(address) {
|
||||
async utxo (address) {
|
||||
try {
|
||||
// console.log(`electrumx.js/utxo() restURL: ${this.restURL}`)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/utxos/${address}`,
|
||||
_this.axiosOptions
|
||||
if (typeof address === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/utxos/${address}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/utxos`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/utxos`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +138,7 @@ class ElectrumX {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
|
||||
* console.log(utxo);
|
||||
* console.log(balance);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -145,7 +155,7 @@ class ElectrumX {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
|
||||
* console.log(utxo);
|
||||
* console.log(balance);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -172,33 +182,36 @@ class ElectrumX {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async balance(address) {
|
||||
async balance (address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/balance/${address}`,
|
||||
_this.axiosOptions
|
||||
if (typeof address === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/balance/${address}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/balance`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/balance`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,33 +283,39 @@ class ElectrumX {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async transactions(address) {
|
||||
async transactions (address, usrObj = null, allTxs = false) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/transactions/${address}`,
|
||||
_this.axiosOptions
|
||||
if (typeof address === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/transactions/${address}/${allTxs}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/transactions`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/transactions`,
|
||||
{
|
||||
addresses: address
|
||||
addresses: address,
|
||||
usrObj, // pass user data when making an internal call.
|
||||
allTxs
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log('Error in transactions(): ', error)
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,33 +381,36 @@ class ElectrumX {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async unconfirmed(address) {
|
||||
async unconfirmed (address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/unconfirmed/${address}`,
|
||||
_this.axiosOptions
|
||||
if (typeof address === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/unconfirmed/${address}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/unconfirmed`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/unconfirmed`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,16 +455,26 @@ class ElectrumX {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async blockHeader(height, count = 1) {
|
||||
async blockHeader (height, count = 1) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/block/headers/${height}?count=${count}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log("error: ", error)
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
if (errorData.error) {
|
||||
throw new Error(errorData.error)
|
||||
} else {
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,34 +547,194 @@ class ElectrumX {
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
async txData(txid) {
|
||||
*/
|
||||
async txData (txid) {
|
||||
try {
|
||||
// Handle single transaction.
|
||||
if (typeof txid === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/tx/data/${txid}`,
|
||||
_this.axiosOptions
|
||||
if (typeof txid === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}fulcrum/tx/data/${txid}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/tx/data`,
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/tx/data`,
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input txId must be a string or array of strings.`)
|
||||
throw new Error('Input txId must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.broadcast() broadcast()
|
||||
* @apiName ElectrumX Broadcast
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure.
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
* let result = await bchjs.Electrumx.broadcast(txHex)
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* result = {
|
||||
* "success": true,
|
||||
* "txid": "..."
|
||||
* }
|
||||
*/
|
||||
async broadcast (txHex) {
|
||||
try {
|
||||
if (typeof txHex === 'string') {
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}fulcrum/tx/broadcast`,
|
||||
{ txHex },
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input txHex must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
const errorData = error.response.data
|
||||
const errorMessage = typeof errorData === 'string' ? errorData : (errorData.error || errorData.message || JSON.stringify(errorData))
|
||||
throw new Error(errorMessage)
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.sortConfTxs() sortConfTxs()
|
||||
* @apiName ElectrumX sortConfTxs
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Sort the output of Electrum.transactions() by block height.
|
||||
*
|
||||
* A simple sort function for the output of Electrum.transactions(). Ignores
|
||||
* unconfirmed transactions.
|
||||
*
|
||||
* Sorts in 'DESCENDING' order by default, or 'ASCENDING' can be specified.
|
||||
* Descending makes the first element the newest (largest block height).
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* const txs = await bchjs.Electrumx.transactions('bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v')
|
||||
* const sortedTxs = bchjs.Electrumx.sortConfTxs(txs.transactions, 'ASCENDING')
|
||||
* console.log(sortedTxs)
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "height": 560430,
|
||||
* // "tx_hash": "3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851"
|
||||
* // },
|
||||
* // {
|
||||
* // "height": 560534,
|
||||
* // "tx_hash": "4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280"
|
||||
* // }
|
||||
* // ]
|
||||
*/
|
||||
// Sort confirmed Transactions by the block height
|
||||
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
|
||||
|
||||
// Filter out unconfirmed transactions, with a height of 0 or less.
|
||||
txs = txs.filter(elem => elem.height > 0)
|
||||
|
||||
if (sortingOrder === 'DESCENDING') {
|
||||
// console.log('Sorting in descending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return b.height - a.height
|
||||
})
|
||||
}
|
||||
|
||||
// console.log('Sorting in ascending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return a.height - b.height
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Error in util.js/sortConfTxs()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.sortAllTxs() sortAllTxs()
|
||||
* @apiName ElectrumX sortAllTxs
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Sort the output of Electrum.transactions() by block height.
|
||||
*
|
||||
* A simple sort function for the output of Electrum.transactions().
|
||||
* Assumes that unconfirmed transactions will make it into the next block. Any
|
||||
* unconfirmed transactions have their block height with the height of the next
|
||||
* block. Returns a Promise.
|
||||
*
|
||||
* Sorts in 'ASCENDING' order by default, or 'DESCENDING' can be specified.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* const txs = await bchjs.Electrumx.transactions('bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v')
|
||||
* const sortedTxs = await bchjs.Electrumx.sortAllTxs(txs.transactions, 'ASCENDING')
|
||||
* console.log(sortedTxs)
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "height": 560430,
|
||||
* // "tx_hash": "3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851"
|
||||
* // },
|
||||
* // {
|
||||
* // "height": 560534,
|
||||
* // "tx_hash": "4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280"
|
||||
* // }
|
||||
* // ]
|
||||
*/
|
||||
// Substitute zero-conf txs with the current block-height + 1
|
||||
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortingOrder: ${sortingOrder}`)
|
||||
|
||||
// Calculate the height of the next block
|
||||
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
||||
|
||||
// Replace the height of any zero-conf transactions with the height of
|
||||
// the next block.
|
||||
const modifiedTxs = txs.map(elem => {
|
||||
if (elem.height <= 0) elem.height = nextBlock
|
||||
return elem
|
||||
})
|
||||
// console.log(`modifiedTxs: ${JSON.stringify(modifiedTxs, null, 2)}`)
|
||||
|
||||
// Sort the modified array of transactions.
|
||||
return this.sortConfTxs(modifiedTxs, sortingOrder)
|
||||
} catch (err) {
|
||||
console.log('Error in util.js/sort0ConfTxs')
|
||||
console.log('electrumx.js restURL: ', this.restURL)
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ElectrumX
|
||||
export default ElectrumX
|
||||
|
||||
+35
-12
@@ -2,36 +2,59 @@
|
||||
This library contains useful functions that deal with encryption.
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
|
||||
class Encryption {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.axios = axios
|
||||
// console.log(`Blockbook apiToken: ${this.apiToken}`)
|
||||
this.authToken = config.authToken
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api encryption.getPubKey() getPubKey()
|
||||
* @apiName Encryption getPubKey()
|
||||
* @apiGroup Encryption
|
||||
* @apiDescription Get the public key for an address
|
||||
* Given an address, the command will search the blockchain for a public
|
||||
* key associated with that address. The address needs to have made at least
|
||||
* one spend transaction, in order for its public key to be retrievable.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* const addr = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
|
||||
* const pubkey = await bchjs.encryption.getPubKey(addr);
|
||||
* console.log(pubkey);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
*/
|
||||
|
||||
// Search the blockchain for a public key associated with a BCH address.
|
||||
async getPubKey(addr) {
|
||||
async getPubKey (addr) {
|
||||
try {
|
||||
if (!addr || typeof addr !== "string")
|
||||
throw new Error(`Input must be a valid Bitcoin Cash address.`)
|
||||
if (!addr || typeof addr !== 'string') {
|
||||
throw new Error('Input must be a valid Bitcoin Cash address.')
|
||||
}
|
||||
|
||||
const response = await _this.axios.get(
|
||||
`${this.restURL}encryption/publickey/${addr}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -41,4 +64,4 @@ class Encryption {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Encryption
|
||||
export default Encryption
|
||||
|
||||
+11
-12
@@ -1,27 +1,26 @@
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Generating {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
async generateToAddress(blocks, address, maxtries = 1000000) {
|
||||
async generateToAddress (blocks, address, maxtries = 1000000) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -31,4 +30,4 @@ class Generating {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Generating
|
||||
export default Generating
|
||||
|
||||
+57
-57
@@ -1,10 +1,10 @@
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const coininfo = require("coininfo")
|
||||
const bip32utils = require("bip32-utils")
|
||||
const bchaddrjs = require("bchaddrjs-slp")
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import coininfo from '@psf/coininfo'
|
||||
import bip32utils from '@chris.troutner/bip32-utils'
|
||||
import bchaddrjs from 'bchaddrjs-slp'
|
||||
|
||||
class HDNode {
|
||||
constructor(address) {
|
||||
constructor (address) {
|
||||
this._address = address
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* bchjs.HDNode.fromSeed(seedBuffer);
|
||||
*
|
||||
@@ -28,15 +28,15 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* bchjs.HDNode.fromSeed(seedBuffer);
|
||||
*/
|
||||
fromSeed(rootSeedBuffer, network = "mainnet") {
|
||||
fromSeed (rootSeedBuffer, network = 'mainnet') {
|
||||
let bitcoincash
|
||||
if (network === "bitcoincash" || network === "mainnet")
|
||||
if (network === 'bitcoincash' || network === 'mainnet') {
|
||||
bitcoincash = coininfo.bitcoincash.main
|
||||
else bitcoincash = coininfo.bitcoincash.test
|
||||
} else bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
return Bitcoin.HDNode.fromSeedBuffer(
|
||||
@@ -56,7 +56,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to legacy address
|
||||
@@ -68,14 +68,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
* bchjs.HDNode.toLegacyAddress(hdNode);
|
||||
* // 14mVsq3H5Ep2Jb6AqoKsmY1BFHKCBGPDLi
|
||||
*/
|
||||
toLegacyAddress(hdNode) {
|
||||
toLegacyAddress (hdNode) {
|
||||
return hdNode.getAddress()
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -102,28 +102,28 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
* bchjs.HDNode.toCashAddress(hdNode);
|
||||
* // bitcoincash:qq549jxsjv66kw0smdju4es2axnk7hhe9cquhjg4gt
|
||||
*/
|
||||
toCashAddress(hdNode, regtest = false) {
|
||||
toCashAddress (hdNode, regtest = false) {
|
||||
return this._address.toCashAddress(hdNode.getAddress(), true, regtest)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.HDNode.toSLPAddress() toSLPAddress()
|
||||
* @apiName toSLPAddress
|
||||
* @apiGroup SLP
|
||||
* @apiGroup HDNode
|
||||
* @apiDescription Get slp address of HDNode.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
@@ -135,14 +135,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
|
||||
* // to cash address
|
||||
* bchjs.SLP.HDNode.toSLPAddress(hdNode);
|
||||
* // simpleledger:qqxh2z2z397m4c6u9s5x6wjtku742q8rpvm6al2nrf
|
||||
*/
|
||||
toSLPAddress(hdNode) {
|
||||
toSLPAddress (hdNode) {
|
||||
const cashAddr = this.toCashAddress(hdNode)
|
||||
return bchaddrjs.toSlpAddress(cashAddr)
|
||||
}
|
||||
@@ -158,7 +158,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to WIF
|
||||
@@ -170,14 +170,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to WIF
|
||||
* bchjs.HDNode.toWIF(hdNode);
|
||||
* // KwobPFhv3AuXc3ps6YtWfMVRpLBDBA7jnJddurfELTyTNcFhZYpJ
|
||||
*/
|
||||
toWIF(hdNode) {
|
||||
toWIF (hdNode) {
|
||||
return hdNode.keyPair.toWIF()
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended public key
|
||||
@@ -204,14 +204,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended public key
|
||||
* bchjs.HDNode.toXPub(hdNode);
|
||||
* // xpub661MyMwAqRbcFuMLeHkSbTNwNHG9MQyrAZqV1Q4MEAsmj9MYa5sxg8WC2LKqW6EHviHVucBjWi1n38juZpDDeX3U6YrsMeACdcNSTHkM8BQ
|
||||
*/
|
||||
toXPub(hdNode) {
|
||||
toXPub (hdNode) {
|
||||
return hdNode.neutered().toBase58()
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended private key
|
||||
@@ -238,14 +238,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // to extended private key
|
||||
* bchjs.HDNode.toXPriv(hdNode);
|
||||
* // xprv9s21ZrQH143K2b5GPP6zHz22E6LeCgQXJtwNbC3MA3Kz7Se7tveKo96EhqwFtSkYWkyenVcMqM7uq35PcUNG8cUdpsJEgwKG3dvfP7TmL3v
|
||||
*/
|
||||
toXPriv(hdNode) {
|
||||
toXPriv (hdNode) {
|
||||
return hdNode.toBase58()
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer from mnemonic
|
||||
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from root seed
|
||||
* let hdNode = bchjs.HDNode.fromSeed(rootSeed);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -271,13 +271,13 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // create public key buffer from HDNode
|
||||
* bchjs.HDNode.toKeyPair(hdNode);
|
||||
*/
|
||||
toKeyPair(hdNode) {
|
||||
toKeyPair (hdNode) {
|
||||
return hdNode.keyPair
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer from mnemonic
|
||||
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from root seed
|
||||
* let hdNode = bchjs.HDNode.fromSeed(rootSeed);
|
||||
* // create public key buffer from HDNode
|
||||
@@ -304,14 +304,14 @@ class HDNode {
|
||||
* // create mnemonic from entropy
|
||||
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // create public key buffer from HDNode
|
||||
* bchjs.HDNode.toPublicKey(hdNode);
|
||||
* // <Buffer 02 d2 26 74 6e 78 03 ac 11 e0 96 c6 24 de e8 dd 62 52 e7 8e 51 56 8a c1 18 62 aa 2a 72 50 1d ea 7d>
|
||||
*/
|
||||
toPublicKey(hdNode) {
|
||||
toPublicKey (hdNode) {
|
||||
return hdNode.getPublicKeyBuffer()
|
||||
}
|
||||
|
||||
@@ -329,10 +329,10 @@ class HDNode {
|
||||
* // testnet xpriv
|
||||
* bchjs.HDNode.fromXPriv('tprv8gQ3zr1F5pRHMebqqhorrorYNvUG3XkcZjSWVs2cEtRwwJy1TRhgRx4XcF8dYHM2eyTbTCcdKYNhqgyBQphxwRoVyVKr9zuyoA8WxNDRvom');
|
||||
*/
|
||||
fromXPriv(xpriv) {
|
||||
fromXPriv (xpriv) {
|
||||
let bitcoincash
|
||||
if (xpriv[0] === "x") bitcoincash = coininfo.bitcoincash.main
|
||||
else if (xpriv[0] === "t") bitcoincash = coininfo.bitcoincash.test
|
||||
if (xpriv[0] === 'x') bitcoincash = coininfo.bitcoincash.main
|
||||
else if (xpriv[0] === 't') bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
return Bitcoin.HDNode.fromBase58(xpriv, bitcoincashBitcoinJSLib)
|
||||
@@ -352,10 +352,10 @@ class HDNode {
|
||||
* // testnet xpub
|
||||
* bchjs.HDNode.fromXPub('tpubDD669G3VEC6xF7ddjMUTGDWewwzCCrwX933HnP4ufAELmoDn5pXGcSgPnLodjFvWQwRXkG94f77BatEDA8dfQ99yy97kRYynUpNLENEqTBo');
|
||||
*/
|
||||
fromXPub(xpub) {
|
||||
fromXPub (xpub) {
|
||||
let bitcoincash
|
||||
if (xpub[0] === "x") bitcoincash = coininfo.bitcoincash.main
|
||||
else if (xpub[0] === "t") bitcoincash = coininfo.bitcoincash.test
|
||||
if (xpub[0] === 'x') bitcoincash = coininfo.bitcoincash.main
|
||||
else if (xpub[0] === 't') bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
return Bitcoin.HDNode.fromBase58(xpub, bitcoincashBitcoinJSLib)
|
||||
@@ -372,13 +372,13 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive hardened child HDNode
|
||||
* bchjs.HDNode.derivePath(hdNode, "m/44'/145'/0'");
|
||||
*/
|
||||
derivePath(hdnode, path) {
|
||||
derivePath (hdnode, path) {
|
||||
return hdnode.derivePath(path)
|
||||
}
|
||||
|
||||
@@ -393,13 +393,13 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive unhardened child HDNode
|
||||
* bchjs.HDNode.derive(hdNode, 0);
|
||||
*/
|
||||
derive(hdnode, path) {
|
||||
derive (hdnode, path) {
|
||||
return hdnode.derive(path)
|
||||
}
|
||||
|
||||
@@ -414,13 +414,13 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create seed buffer from mnemonic
|
||||
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create HDNode from seed buffer
|
||||
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
|
||||
* // derive hardened child HDNode
|
||||
* bchjs.HDNode.deriveHardened(hdNode, 0);
|
||||
*/
|
||||
deriveHardened(hdnode, path) {
|
||||
deriveHardened (hdnode, path) {
|
||||
return hdnode.deriveHardened(path)
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class HDNode {
|
||||
* // sign
|
||||
* bchjs.HDNode.sign(hdnode, buf);
|
||||
*/
|
||||
sign(hdnode, buffer) {
|
||||
sign (hdnode, buffer) {
|
||||
return hdnode.sign(buffer)
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ class HDNode {
|
||||
* bchjs.HDNode.verify(hdnode2, buf, signature);
|
||||
* // false
|
||||
*/
|
||||
verify(hdnode, buffer, signature) {
|
||||
verify (hdnode, buffer, signature) {
|
||||
return hdnode.verify(buffer, signature)
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ class HDNode {
|
||||
* bchjs.HDNode.isPublic(node);
|
||||
* // false
|
||||
*/
|
||||
isPublic(hdnode) {
|
||||
isPublic (hdnode) {
|
||||
return hdnode.isNeutered()
|
||||
}
|
||||
|
||||
@@ -566,7 +566,7 @@ class HDNode {
|
||||
* bchjs.HDNode.isPrivate(node);
|
||||
* // true
|
||||
*/
|
||||
isPrivate(hdnode) {
|
||||
isPrivate (hdnode) {
|
||||
return !hdnode.isNeutered()
|
||||
}
|
||||
|
||||
@@ -598,11 +598,11 @@ class HDNode {
|
||||
* bchjs.Crypto.hash160(publicKeyBuffer);
|
||||
* // <Buffer e1 8e 20 e3 f8 f1 c0 53 e6 1f 9e 3a 58 8e 71 f5 0b 8d 2d c4>
|
||||
*/
|
||||
toIdentifier(hdnode) {
|
||||
toIdentifier (hdnode) {
|
||||
return hdnode.getIdentifier()
|
||||
}
|
||||
|
||||
fromBase58(base58, network) {
|
||||
fromBase58 (base58, network) {
|
||||
return Bitcoin.HDNode.fromBase58(base58, network)
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ class HDNode {
|
||||
* // create mnemonic
|
||||
* let mnemonic = bchjs.Mnemonic.generate(128);
|
||||
* // create root seed buffer
|
||||
* let rootSeedBuffer = bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* let rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
|
||||
* // create master hd node
|
||||
* let masterHDNode = bchjs.HDNode.fromSeed(rootSeedBuffer);
|
||||
* // derive child node
|
||||
@@ -625,16 +625,16 @@ class HDNode {
|
||||
* // create account
|
||||
* let account = bchjs.HDNode.createAccount([childNode]);
|
||||
*/
|
||||
createAccount(hdNodes) {
|
||||
createAccount (hdNodes) {
|
||||
const arr = hdNodes.map(
|
||||
(item, index) => new bip32utils.Chain(item.neutered())
|
||||
)
|
||||
return new bip32utils.Account(arr)
|
||||
}
|
||||
|
||||
createChain(hdNode) {
|
||||
createChain (hdNode) {
|
||||
return new bip32utils.Chain(hdNode)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HDNode
|
||||
export default HDNode
|
||||
|
||||
-444
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
This library interacts with the IPFS REST API at FullStack.cash for uploading
|
||||
and downloading data from the IPFS network.
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
const Uppy = require("@uppy/core")
|
||||
const Tus = require("@uppy/tus")
|
||||
const fs = require("fs")
|
||||
|
||||
let _this
|
||||
|
||||
class IPFS {
|
||||
constructor(config) {
|
||||
this.IPFS_API = process.env.IPFS_API
|
||||
? process.env.IPFS_API
|
||||
: // : `http://localhost:5001`
|
||||
`https://ipfs-api.fullstack.cash`
|
||||
|
||||
// Default options when calling axios.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
// authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000 // Timeout if the server does not respond in time.
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
|
||||
_this.axios = axios
|
||||
_this.fs = fs
|
||||
|
||||
// Initialize Uppy
|
||||
_this.uppy = _this.initUppy()
|
||||
}
|
||||
|
||||
// Initializes Uppy, which is used for file uploads.
|
||||
initUppy() {
|
||||
const uppy = Uppy({
|
||||
allowMultipleUploads: false,
|
||||
meta: { test: "avatar" },
|
||||
debug: false,
|
||||
restrictions: {
|
||||
maxFileSize: null,
|
||||
maxNumberOfFiles: 1,
|
||||
minNumberOfFiles: 1,
|
||||
allowedFileTypes: null //type of files allowed to load
|
||||
}
|
||||
})
|
||||
uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` })
|
||||
|
||||
return uppy
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.createFileModelServer() createFileModelServer()
|
||||
* @apiName createFileModelServer()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.createFileModelServer(path);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelServer(path) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path))
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
// console.log(`fileBuf: `, fileBuf)
|
||||
|
||||
// console.log(`Buffer length: ${fileBuf.length}`)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split("/")
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = fileName.split(".")
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: fileBuf.length,
|
||||
fileName: fileName,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error(`Error in createFileModel()`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileServer() uploadFileServer()
|
||||
* @apiName uploadFile()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileServer(path, modelId) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path))
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
`Must include a file model ID in order to upload a file.`
|
||||
)
|
||||
}
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split("/")
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: fileName,
|
||||
data: fileBuf,
|
||||
source: "Local",
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length)
|
||||
throw new Error("The file could not be uploaded")
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error(`Error in bch-js/src/ipfs.js/upload(): `)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.getStatus() getStatus()
|
||||
* @apiName getStatus()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Gets the status of the uploaded file. Will return an object
|
||||
* that indicates the payment status. If the file is paid, it should contain
|
||||
* an IPFS hash.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const modelId = "5ec7392c2acfe57aa62e945a"
|
||||
* let fileData = await bchjs.IPFS.getStatus(modelId)
|
||||
* console.log(fileData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "hasBeenPaid": true,
|
||||
* "satCost": 4403,
|
||||
* "bchAddr": "bchtest:qz5z82u0suqh80x5tfx4ht8kdrkkw664vcy44uz0wk",
|
||||
* "ipfsHash": "QmRDHPhY5hCNVRMVQvS2H9uty8P1skdwgLaHpUAkEvsjcE",
|
||||
* "fileId": "5ec7392c2acfe57aa62e945a",
|
||||
* "fileName": "ipfs-e2e.js"
|
||||
* }
|
||||
*/
|
||||
async getStatus(modelId) {
|
||||
try {
|
||||
if (!modelId) throw new Error(`Must include a file model ID.`)
|
||||
|
||||
const fileData = await _this.axios.get(
|
||||
`${this.IPFS_API}/files/${modelId}`
|
||||
)
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
hasBeenPaid: fileData.data.file.hasBeenPaid,
|
||||
satCost: fileData.data.file.hostingCost,
|
||||
bchAddr: fileData.data.file.bchAddr,
|
||||
ipfsHash: fileData.data.file.payloadLink,
|
||||
fileId: fileData.data.file._id,
|
||||
fileName: fileData.data.file.fileName
|
||||
}
|
||||
|
||||
return fileObj
|
||||
} catch (err) {
|
||||
console.error(`Error in getStatus()`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @api IPFS.createFileModelWeb() createFileModelWeb()
|
||||
* @apiName createFileModelWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.createFileModelWeb(file);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelWeb(file) {
|
||||
try {
|
||||
if (!file) throw new Error(`File is required`)
|
||||
|
||||
const { name, size } = file
|
||||
|
||||
if (!name || typeof name !== "string")
|
||||
throw new Error(`File should have the property 'name' of string type`)
|
||||
|
||||
if (!size || typeof size !== "number")
|
||||
throw new Error(`File should have the property 'size' of number type`)
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = name.split(".")
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: size,
|
||||
fileName: name,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error(`Error in createFileModelWeb()`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileWeb() uploadFileWeb()
|
||||
* @apiName uploadFileWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* * const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.uploadFile(file, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileWeb(file, modelId) {
|
||||
try {
|
||||
if (!file) throw new Error(`File is required`)
|
||||
|
||||
const { name, type, size } = file
|
||||
|
||||
if (!name || typeof name !== "string")
|
||||
throw new Error(`File should have the property 'name' of string type`)
|
||||
|
||||
if (!type || typeof type !== "string")
|
||||
throw new Error(`File should have the property 'type' of string type`)
|
||||
|
||||
if (!size || typeof size !== "number")
|
||||
throw new Error(`File should have the property 'size' of number type`)
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
`Must include a file model ID in order to upload a file.`
|
||||
)
|
||||
}
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: name,
|
||||
data: file,
|
||||
source: "Local",
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length)
|
||||
throw new Error("The file could not be uploaded")
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error(`Error in bch-js/src/ipfs.js/uploadFileWeb(): `)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IPFS
|
||||
+20
-21
@@ -1,27 +1,26 @@
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Mining {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
async getBlockTemplate(template_request) {
|
||||
async getBlockTemplate (templateRequest) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}mining/getBlockTemplate/${template_request}`,
|
||||
_this.axiosOptions
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}mining/getBlockTemplate/${templateRequest}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -30,11 +29,11 @@ class Mining {
|
||||
}
|
||||
}
|
||||
|
||||
async getMiningInfo() {
|
||||
async getMiningInfo () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}mining/getMiningInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -43,11 +42,11 @@ class Mining {
|
||||
}
|
||||
}
|
||||
|
||||
async getNetworkHashps(nblocks = 120, height = 1) {
|
||||
async getNetworkHashps (nblocks = 120, height = 1) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -56,12 +55,12 @@ class Mining {
|
||||
}
|
||||
}
|
||||
|
||||
async submitBlock(hex, parameters) {
|
||||
async submitBlock (hex, parameters) {
|
||||
let path = `${this.restURL}mining/submitBlock/${hex}`
|
||||
if (parameters) path = `${path}?parameters=${parameters}`
|
||||
|
||||
try {
|
||||
const response = await axios.post(path, _this.axiosOptions)
|
||||
const response = await this.axios.post(path, this.axiosOptions)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
@@ -70,4 +69,4 @@ class Mining {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Mining
|
||||
export default Mining
|
||||
|
||||
+33
-36
@@ -1,11 +1,11 @@
|
||||
const BIP39 = require("bip39")
|
||||
const randomBytes = require("randombytes")
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const Buffer = require("safe-buffer").Buffer
|
||||
const wif = require("wif")
|
||||
import BIP39 from 'bip39'
|
||||
import randomBytes from 'randombytes'
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import { Buffer } from 'safe-buffer'
|
||||
import wif from 'wif'
|
||||
|
||||
class Mnemonic {
|
||||
constructor(address) {
|
||||
constructor (address) {
|
||||
this._address = address
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Mnemonic {
|
||||
* bchjs.Mnemonic.generate(256, bitbox.Mnemonic.wordLists().korean)
|
||||
* // 기능 단추 교육 비난 시집 근육 운동 코미디 숟가락 과목 한동안 유적 시리즈 삼월 앞날 유난히 흰색 사실 논문 장사 어른 논문 의논 장차
|
||||
*/
|
||||
generate(bits = 128, wordlist) {
|
||||
generate (bits = 128, wordlist) {
|
||||
return BIP39.generateMnemonic(bits, randomBytes, wordlist)
|
||||
}
|
||||
|
||||
@@ -95,11 +95,8 @@ class Mnemonic {
|
||||
* // generate 16 bytes of entropy
|
||||
* let entropy = bchjs.Crypto.randomBytes(16);
|
||||
* //
|
||||
* // turn entropy to 12 japanese word mnemonic
|
||||
* bchjs.Mnemonic.fromEntropy(entropy.toString('hex'), bchjs.Mnemonic.wordLists().japanese)
|
||||
* // ぱそこん にあう にんめい きどく ちそう せんきょ かいが きおく いれる いねむり しいく きかんしゃ
|
||||
*/
|
||||
fromEntropy(bytes, wordlist) {
|
||||
fromEntropy (bytes, wordlist) {
|
||||
return BIP39.entropyToMnemonic(bytes, wordlist)
|
||||
}
|
||||
|
||||
@@ -136,8 +133,8 @@ class Mnemonic {
|
||||
* bchjs.Mnemonic.toEntropy(mnemonic)
|
||||
* // <Buffer f3 79 da 02 cc 42 6e 6e 26 43 0d 25 e6 cc 37 2d fd 0a 1a 2e 4a 33 ac 4d c6 ae 6d 56 01 7f 64 2d>
|
||||
*/
|
||||
toEntropy(mnemonic, wordlist) {
|
||||
return Buffer.from(BIP39.mnemonicToEntropy(mnemonic, wordlist), "hex")
|
||||
toEntropy (mnemonic, wordlist) {
|
||||
return Buffer.from(BIP39.mnemonicToEntropy(mnemonic, wordlist), 'hex')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,11 +154,11 @@ class Mnemonic {
|
||||
* bchjs.Mnemonic.validate('boil lonely casino manage habit where total glory muffin name limit mansion boil lonely casino manage habit where total glory muffin name limit mansion', bitbox.Mnemonic.wordLists().english)
|
||||
* // Invalid mnemonic
|
||||
*/
|
||||
validate(mnemonic, wordlist) {
|
||||
validate (mnemonic, wordlist) {
|
||||
// Preprocess the words
|
||||
const words = mnemonic.split(" ")
|
||||
const words = mnemonic.split(' ')
|
||||
// Detect blank phrase
|
||||
if (words.length === 0) return "Blank mnemonic"
|
||||
if (words.length === 0) return 'Blank mnemonic'
|
||||
|
||||
// Check each word
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
@@ -173,11 +170,11 @@ class Mnemonic {
|
||||
}
|
||||
}
|
||||
// Check the words are valid
|
||||
//const properPhrase = words.join()
|
||||
// const properPhrase = words.join()
|
||||
const isValid = BIP39.validateMnemonic(mnemonic, wordlist)
|
||||
if (!isValid) return "Invalid mnemonic"
|
||||
if (!isValid) return 'Invalid mnemonic'
|
||||
|
||||
return "Valid mnemonic"
|
||||
return 'Valid mnemonic'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,7 +200,7 @@ class Mnemonic {
|
||||
* await bchjs.Mnemonic.toSeed('frost deliver coin clutch upon round scene wonder various wise luggage country', 'yayayayay');
|
||||
* // <Buffer 1d 00 9f a3 a8 86 51 a4 04 d5 03 3d eb 6d b1 01 e2 f1 3b c3 c8 6d 1f b9 93 b4 d1 33 dc 84 21 12 2c 9b 52 10 ba d8 96 15 e0 b0 9a 34 33 52 f8 07 c8 c4 ... >
|
||||
*/
|
||||
toSeed(mnemonic, password = "") {
|
||||
toSeed (mnemonic, password = '') {
|
||||
return BIP39.mnemonicToSeed(mnemonic, password)
|
||||
}
|
||||
|
||||
@@ -229,7 +226,7 @@ class Mnemonic {
|
||||
* // spanish: []
|
||||
* // }
|
||||
*/
|
||||
wordLists() {
|
||||
wordLists () {
|
||||
return BIP39.wordlists
|
||||
}
|
||||
|
||||
@@ -260,10 +257,10 @@ class Mnemonic {
|
||||
* // { privateKeyWIF: 'L5gB66JqhfouEtZG5aRMQ9JaVS2ggkK3YozGfzZegBupaPXqdfaz',
|
||||
* // address: 'bitcoincash:qphwlpu2wzjxrjts94pn4wh778fwsu2afg2aj5her9' } ]
|
||||
*/
|
||||
async toKeypairs(mnemonic, numberOfKeypairs = 1, regtest = false) {
|
||||
const rootSeedBuffer = await this.toSeed(mnemonic, "")
|
||||
async toKeypairs (mnemonic, numberOfKeypairs = 1, regtest = false) {
|
||||
const rootSeedBuffer = await this.toSeed(mnemonic, '')
|
||||
const hdNode = Bitcoin.HDNode.fromSeedBuffer(rootSeedBuffer)
|
||||
const HDPath = `44'/145'/0'/0/`
|
||||
const HDPath = "44'/145'/0'/0/"
|
||||
|
||||
const accounts = []
|
||||
|
||||
@@ -321,7 +318,7 @@ class Mnemonic {
|
||||
* bchjs.Mnemonic.findNearestWord(word, wordlist);
|
||||
* // neve
|
||||
*/
|
||||
findNearestWord(word, wordlist) {
|
||||
findNearestWord (word, wordlist) {
|
||||
let minDistance = 99
|
||||
let closestWord = wordlist[0]
|
||||
for (let i = 0; i < wordlist.length; i++) {
|
||||
@@ -338,7 +335,7 @@ class Mnemonic {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Mnemonic
|
||||
export default Mnemonic
|
||||
|
||||
// The following code is from: https://raw.githubusercontent.com/iancoleman/bip39/7ff86d4c983f1e8c80b87b31acfd69fcf98c1b82/src/js/levenshtein.js
|
||||
|
||||
@@ -352,11 +349,11 @@ module.exports = Mnemonic
|
||||
* @return Object the final object.
|
||||
*/
|
||||
|
||||
const _extend = function(dst) {
|
||||
const _extend = function (dst) {
|
||||
const sources = Array.prototype.slice.call(arguments, 1)
|
||||
for (let i = 0; i < sources.length; ++i) {
|
||||
const src = sources[i]
|
||||
for (const p in src) if (src.hasOwnProperty(p)) dst[p] = src[p]
|
||||
for (const p in src) if (Object.prototype.hasOwnProperty.call(src, p)) dst[p] = src[p]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
@@ -365,8 +362,8 @@ const _extend = function(dst) {
|
||||
* Defer execution of given function.
|
||||
* @param {Function} func
|
||||
*/
|
||||
const _defer = function(func) {
|
||||
if (typeof setImmediate === "function") return setImmediate(func)
|
||||
const _defer = function (func) {
|
||||
if (typeof setImmediate === 'function') return setImmediate(func)
|
||||
|
||||
return setTimeout(func, 0)
|
||||
}
|
||||
@@ -374,7 +371,7 @@ const _defer = function(func) {
|
||||
/**
|
||||
* Based on the algorithm at http://en.wikipedia.org/wiki/Levenshtein_distance.
|
||||
*/
|
||||
var Levenshtein = {
|
||||
const Levenshtein = {
|
||||
/**
|
||||
* Calculate levenshtein distance of the two strings.
|
||||
*
|
||||
@@ -382,7 +379,7 @@ var Levenshtein = {
|
||||
* @param str2 String the second string.
|
||||
* @return Integer the levenshtein distance (0 and above).
|
||||
*/
|
||||
get: function(str1, str2) {
|
||||
get: function (str1, str2) {
|
||||
// base cases
|
||||
if (str1 === str2) return 0
|
||||
if (str1.length === 0) return str2.length
|
||||
@@ -432,7 +429,7 @@ var Levenshtein = {
|
||||
* @param [options] Object additional options.
|
||||
* @param [options.progress] Function progress callback with signature: function(percentComplete)
|
||||
*/
|
||||
getAsync: function(str1, str2, cb, options) {
|
||||
getAsync: function (str1, str2, cb, options) {
|
||||
options = _extend(
|
||||
{},
|
||||
{
|
||||
@@ -457,7 +454,7 @@ var Levenshtein = {
|
||||
i = 0
|
||||
j = -1
|
||||
|
||||
var __calculate = function() {
|
||||
const __calculate = function () {
|
||||
// reset timer
|
||||
startTime = new Date().valueOf()
|
||||
currentTime = startTime
|
||||
@@ -499,11 +496,11 @@ var Levenshtein = {
|
||||
}
|
||||
|
||||
// send a progress update?
|
||||
if (null !== options.progress) {
|
||||
if (options.progress !== null) {
|
||||
try {
|
||||
options.progress.call(null, (i * 100.0) / str1.length)
|
||||
} catch (err) {
|
||||
return cb(`Progress callback: ${err.toString()}`)
|
||||
return cb(new Error(`Progress callback: ${err.toString()}`))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
This library interacts with the ninsight REST API endpoints operated by
|
||||
Bitcoin.com
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
|
||||
let _this
|
||||
|
||||
class Ninsight {
|
||||
constructor(config) {
|
||||
// this.restURL = config.restURL
|
||||
// this.apiToken = config.apiToken
|
||||
|
||||
// this.ninsightURL = `https://bch-explorer.api.bitcoin.com/v1/`
|
||||
if (config) {
|
||||
this.ninsightURL = config.ninsightURL
|
||||
? config.ninsightURL
|
||||
: `https://rest.bitcoin.com/v2`
|
||||
}
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.utxo() utxo()
|
||||
* @apiName Ninsight Utxo
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return list of uxto for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Ninsight.utxo('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "vout": 1,
|
||||
* // "value": "1000000",
|
||||
* // "height": 585570,
|
||||
* // "confirmations": 10392
|
||||
* // },
|
||||
* // {
|
||||
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "vout": 0,
|
||||
* // "value": "1000000",
|
||||
* // "height": 577125,
|
||||
* // "confirmations": 18837
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async utxo(address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string or array of strings.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Ninsight
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
This library interacts with the public REST API endpoints that OpenBazaar
|
||||
provides for their application.
|
||||
*/
|
||||
|
||||
const axios = require("axios")
|
||||
|
||||
let _this
|
||||
|
||||
class OpenBazaar {
|
||||
constructor(config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api OpenBazaar.balance() balance()
|
||||
* @apiName OpenBazaar Balance
|
||||
* @apiGroup OpenBazaar
|
||||
* @apiDescription Return Balance about an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.OpenBazaar.balance('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* page: 1,
|
||||
* totalPages: 1,
|
||||
* itemsOnPage: 1000,
|
||||
* addrStr: "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9",
|
||||
* balance: "0.00001",
|
||||
* totalReceived: "0.00001",
|
||||
* totalSent: "0",
|
||||
* unconfirmedBalance: "0",
|
||||
* unconfirmedTxApperances: 0,
|
||||
* txApperances: 1,
|
||||
* transactions: [
|
||||
* "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async balance(address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
let response
|
||||
if (process.env.NETWORK === "testnet") {
|
||||
response = await axios.get(
|
||||
`https://tbch.blockbook.api.openbazaar.org/api/address/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
} else {
|
||||
response = await axios.get(
|
||||
`https://bch.blockbook.api.openbazaar.org/api/address/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input address must be a string.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api OpenBazaar.utxo() utxo()
|
||||
* @apiName OpenBazaar Utxo
|
||||
* @apiGroup OpenBazaar
|
||||
* @apiDescription Return list of uxto for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.OpenBazaar.utxo('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* [
|
||||
* {
|
||||
* txid: "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
|
||||
* vout: 0,
|
||||
* amount: "0.00001",
|
||||
* satoshis: 1000,
|
||||
* height: 602405,
|
||||
* confirmations: 11
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
async utxo(address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
let response
|
||||
if (process.env.NETWORK === "testnet") {
|
||||
response = await axios.get(
|
||||
`https://tbch.blockbook.api.openbazaar.org/api/utxo/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
} else {
|
||||
response = await axios.get(
|
||||
`https://bch.blockbook.api.openbazaar.org/api/utxo/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
}
|
||||
return response.data
|
||||
}
|
||||
throw new Error(`Input address must be a string.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api OpenBazaar.tx() tx()
|
||||
* @apiName OpenBazaar Tx
|
||||
* @apiGroup OpenBazaar
|
||||
* @apiDescription Get details on a transaction.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txDetails = await bchjs.OpenBazaar.tx(`2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7`);
|
||||
* console.log(txDetails);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
*{
|
||||
* txid: "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
|
||||
* version: 2,
|
||||
* vin: [
|
||||
* {
|
||||
* txid: "5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac",
|
||||
* vout: 1,
|
||||
* sequence: 4294967295,
|
||||
* n: 0,
|
||||
* scriptSig: {
|
||||
* hex:
|
||||
* "473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9"
|
||||
* },
|
||||
* addresses: ["bitcoincash:qqxy8hycqe89j7wa79gnggq6z3gaqu2uvqy26xehfe"],
|
||||
* value: "0.00047504"
|
||||
* }
|
||||
* ],
|
||||
* vout: [
|
||||
* {
|
||||
* value: "0.00001",
|
||||
* n: 0,
|
||||
* scriptPubKey: {
|
||||
* hex: "76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac",
|
||||
* addresses: ["bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"]
|
||||
* },
|
||||
* spent: false
|
||||
* },
|
||||
* {
|
||||
* value: "0.00046256",
|
||||
* n: 1,
|
||||
* scriptPubKey: {
|
||||
* hex: "76a9142dbf5e1804c39a497b908c876097d63210c8490288ac",
|
||||
* addresses: ["bitcoincash:qqkm7hscqnpe5jtmjzxgwcyh6ceppjzfqg3jdn422e"]
|
||||
* },
|
||||
* spent: false
|
||||
* }
|
||||
* ],
|
||||
* blockhash: "0000000000000000010903a1fc4274499037c9339be9ec7338ee980331c20ce5",
|
||||
* blockheight: 602405,
|
||||
* confirmations: 11,
|
||||
* blocktime: 1569792892,
|
||||
* valueOut: "0.00047256",
|
||||
* valueIn: "0.00047504",
|
||||
* fees: "0.00000248",
|
||||
* hex:
|
||||
* "0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000"
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async tx(txid) {
|
||||
try {
|
||||
// Handle single txid.
|
||||
if (typeof txid === "string") {
|
||||
let response
|
||||
if (process.env.NETWORK === "testnet") {
|
||||
response = await axios.get(
|
||||
`https://tbch.blockbook.api.openbazaar.org/api/tx/${txid}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
} else {
|
||||
response = await axios.get(
|
||||
`https://bch.blockbook.api.openbazaar.org/api/tx/${txid}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
}
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input txid must be a string.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OpenBazaar
|
||||
+163
-27
@@ -1,34 +1,170 @@
|
||||
const axios = require("axios")
|
||||
/**
|
||||
* @api price.current() current()
|
||||
* @apiName Price.
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of BCH in multiple currencies.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* let current = await bchjs.Price.current('usd');
|
||||
* console.log(current);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 26681
|
||||
*/
|
||||
import axios from 'axios'
|
||||
|
||||
// let _this
|
||||
|
||||
class Price {
|
||||
async current(currency = "usd") {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.authToken = config.authToken
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
}
|
||||
|
||||
/**
|
||||
* @api price.getUsd() getUsd()
|
||||
* @apiName Price getUsd()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of BCH in USD.
|
||||
* This endpoint gets the USD price of BCH from the Coinbase API. The price
|
||||
* comes from bch-api, so it has a better chance of working in Tor.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* let current = await bchjs.Price.getUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 266.81
|
||||
*/
|
||||
async getUsd () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/bchusd`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data.price
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
return response.data.usd
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data) throw err.response.data
|
||||
else throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api price.rates() rates()
|
||||
* @apiName Price rates()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of BCH in several different currencies.
|
||||
* This endpoint gets the price of BCH from the Coinbase API in many different
|
||||
* currencies. The price
|
||||
* comes from bch-api, so it has a better chance of working in Tor.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* let current = await bchjs.Price.rates();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* {
|
||||
* AED: "915.049218",
|
||||
* AFN: "19144.48874646",
|
||||
* ALGO: "826.6633482661356600405",
|
||||
* ...
|
||||
* ZRX: "644.844402797695193656",
|
||||
* ZWL: "80215.03"
|
||||
* }
|
||||
*/
|
||||
async rates () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/rates`,
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
return response.data
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data) throw err.response.data
|
||||
else throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api price.getBchUsd() getBchUsd()
|
||||
* @apiName Price getBchUsd()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of BCH in USD.
|
||||
* This endpoint gets the USD price of BCH from the Coinex API. The price
|
||||
* comes from bch-api, so it has a better chance of working in Tor.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* let current = await bchjs.Price.getBchUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 512.81
|
||||
*/
|
||||
async getBchUsd () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/bchusd`,
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
return response.data.usd
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data) throw err.response.data
|
||||
else throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api price.getPsffppPrice() getPsffppPrice()
|
||||
* @apiName Price getPsffppPrice()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return the cost in PSF tokens to write 1MB of data to the PSFFPP
|
||||
* Find out more at PSFFPP.com. This is a IPFS pinning service that can pin
|
||||
* up to 100MB per transaction into its network. The cost is denominated in
|
||||
* PSF SLP tokens. The endpoint returns the cost to pin 1MB of data to the
|
||||
* PSFFPP network.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
* try {
|
||||
* let current = await bchjs.Price.getPsffppPrice();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 0.08335233
|
||||
*/
|
||||
async getPsffppPrice () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/psffpp`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data.writePrice
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data) throw err.response.data
|
||||
else throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Price
|
||||
export default Price
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
This library interacts with the PSF slp indexer REST API endpoints operated
|
||||
by FullStack.cash
|
||||
|
||||
TODO:
|
||||
- detect TXs from tokens in the blacklist.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
import axios from 'axios'
|
||||
|
||||
// Local libraries
|
||||
import RawTransaction from './raw-transactions.js'
|
||||
import SlpUtils from './slp/utils.js'
|
||||
import Blockchain from './blockchain.js'
|
||||
|
||||
// let _this
|
||||
|
||||
class PsfSlpIndexer {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.authToken = config.authToken
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
this.blockchain = new Blockchain(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.status() status()
|
||||
* @apiName Status
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return status from psf slp indexer.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let status = await bchjs.PsfSlpIndexer.status()
|
||||
* console.log(status)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "status": {
|
||||
* "startBlockHeight": 543376,
|
||||
* "syncedBlockHeight": 723249,
|
||||
* "chainBlockHeight": 722679
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async status () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}slp/status`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.balance() balance()
|
||||
* @apiName SLP Balance
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp balance for a single address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n')
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* balance: {
|
||||
* utxos: [
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* vout: 1,
|
||||
* type: 'token',
|
||||
* qty: '1800',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
* }
|
||||
* ],
|
||||
* txs: [
|
||||
* {
|
||||
* txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
* height: 717796
|
||||
* },
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* height: 717832
|
||||
* }
|
||||
* ],
|
||||
* balances: [
|
||||
* {
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* qty: '1800'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async balance (address) {
|
||||
try {
|
||||
// console.log('balance() address: ', address)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}slp/address`,
|
||||
{ address },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input address must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tokenStats() tokenStats()
|
||||
* @apiName Token Stats
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
* The second input is a Boolean, which determins the the transaction history
|
||||
* of the token is included in the returned data. The default is false.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', true)
|
||||
* console.log(tokenStats)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* tokenData: {
|
||||
* type: 1,
|
||||
* ticker: 'TROUT',
|
||||
* name: "Trout's test token",
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* documentUri: 'troutsblog.com',
|
||||
* documentHash: '',
|
||||
* decimals: 2,
|
||||
* mintBatonIsActive: true,
|
||||
* tokensInCirculationBN: '100098953386',
|
||||
* tokensInCirculationStr: '100098953386',
|
||||
* blockCreated: 622414,
|
||||
* totalBurned: '1046614',
|
||||
* totalMinted: '100100000000'
|
||||
* txs: [
|
||||
* {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* height: 622414,
|
||||
* type: 'GENESIS',
|
||||
* qty: '100000000000'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
async tokenStats (tokenId, withTxHistory = false) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}slp/token`,
|
||||
{ tokenId, withTxHistory },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tx() tx()
|
||||
* @apiName SLP Transaction Data
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp transaction data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
|
||||
* console.log(txData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* txData: {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* version: 2,
|
||||
* size: 339,
|
||||
* locktime: 0,
|
||||
* vin: [
|
||||
* {
|
||||
* txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
* vout: 0,
|
||||
* scriptSig: {
|
||||
* asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
* hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
* },
|
||||
* sequence: 4294967295,
|
||||
* address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
* value: 0.00051303,
|
||||
* tokenQty: 0,
|
||||
* tokenQtyStr: '0',
|
||||
* tokenId: null
|
||||
* }
|
||||
* ],
|
||||
* vout: [
|
||||
* {
|
||||
* value: 0,
|
||||
* n: 0,
|
||||
* scriptPubKey: {
|
||||
* asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
* hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
* type: 'nulldata'
|
||||
* },
|
||||
* tokenQtyStr: '0',
|
||||
* tokenQty: 0
|
||||
* }
|
||||
* ],
|
||||
* hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
* blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
* confirmations: 97398,
|
||||
* time: 1581773131,
|
||||
* blocktime: 1581773131,
|
||||
* blockheight: 622414,
|
||||
* isSlpTx: true,
|
||||
* tokenTxType: 'GENESIS',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* tokenType: 1,
|
||||
* tokenTicker: 'TROUT',
|
||||
* tokenName: "Trout's test token",
|
||||
* tokenDecimals: 2,
|
||||
* tokenUri: 'troutsblog.com',
|
||||
* tokenDocHash: '',
|
||||
* isValidSlp: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async tx (txid) {
|
||||
try {
|
||||
// console.log('txid: ', txid)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof txid === 'string') {
|
||||
const response = await this.axios.post(
|
||||
`${this.restURL}slp/txid`,
|
||||
{ txid },
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log('response: ', response)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input txid must be a string.')
|
||||
} catch (error) {
|
||||
// console.log('error: ', error)
|
||||
|
||||
// Case: txid is not stored in the psf-slp-indexer tx database.
|
||||
// Response: If it's not in the database, then it can be assumed the TX
|
||||
// is not a token TX?
|
||||
if (
|
||||
error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.error &&
|
||||
error.response.data.error.includes('Key not found in database')
|
||||
) {
|
||||
// console.log(
|
||||
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
|
||||
// )
|
||||
|
||||
// Check if this txid belongs to a blacklisted token.
|
||||
const isInBlacklist = await this.checkBlacklist(txid)
|
||||
// console.log('isInBlacklist: ', isInBlacklist)
|
||||
|
||||
// Get the TX Details from the full node.
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
// Add blockheight, height, time, and blocktime to the txDetails object.
|
||||
if (txDetails.height === null) {
|
||||
// Get current block height and increment by 1
|
||||
const currentBlockHeight = await this.blockchain.getBlockCount()
|
||||
const estimatedHeight = currentBlockHeight + 1
|
||||
|
||||
// Set height and blockheight
|
||||
txDetails.height = estimatedHeight
|
||||
txDetails.blockheight = estimatedHeight
|
||||
|
||||
// Set time and blocktime to current unix timestamp
|
||||
const currentTime = Math.floor(Date.now() / 1000)
|
||||
txDetails.time = currentTime
|
||||
txDetails.blocktime = currentTime
|
||||
}
|
||||
|
||||
if (isInBlacklist) {
|
||||
txDetails.isValidSlp = null
|
||||
} else {
|
||||
txDetails.isValidSlp = false
|
||||
}
|
||||
|
||||
const outObj = {
|
||||
txData: txDetails
|
||||
}
|
||||
|
||||
return outObj
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the txid has an OP_RETURN containing a tokenID that is in the
|
||||
// blacklist. In that case, the isValidSlp property should be marked as
|
||||
// null, and not false.
|
||||
async checkBlacklist (txid) {
|
||||
try {
|
||||
// TODO: Add endpoint to psf-slp-indexer to retrieve current blacklist.
|
||||
// This should be done once at startup, and not each time this function
|
||||
// is called.
|
||||
const blacklist = [
|
||||
'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9'
|
||||
]
|
||||
|
||||
const outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log('outTokenData: ', outTokenData)
|
||||
|
||||
// Loop through each token in the blacklist.
|
||||
for (let i = 0; i < blacklist.length; i++) {
|
||||
// If a match is found, return true.
|
||||
if (outTokenData.tokenId === blacklist[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// By default, return false.
|
||||
return false
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
// Exit quietly.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.getTokenData() getTokenData()
|
||||
* @apiName Token Data
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Get mutable and immutable data if the token contains them.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
|
||||
* console.log(tokenData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* genesisData: {
|
||||
* type: 1,
|
||||
* ticker: 'TROUT',
|
||||
* name: "Trout's test token",
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* documentUri: 'troutsblog.com',
|
||||
* documentHash: '',
|
||||
* decimals: 2,
|
||||
* mintBatonIsActive: true,
|
||||
* tokensInCirculationBN: '100098953386',
|
||||
* tokensInCirculationStr: '100098953386',
|
||||
* blockCreated: 622414,
|
||||
* totalBurned: '1046614',
|
||||
* totalMinted: '100100000000'
|
||||
* ]
|
||||
* },
|
||||
* immutableData :{
|
||||
* issuer:"FullStack.cash.",
|
||||
* website:"https://fullstack.cash/",
|
||||
* dateCreated:"2022-01-11"
|
||||
* },
|
||||
* mutableData :{
|
||||
* "tokenIcon":"https://gateway.ipfs.io/ipfs/bafybeiehitanirn5gmhqjg44xrmdtomn4n5lu5yjoepsvgpswk5mggaw6i/LP_logo-1.png",
|
||||
* "about":"Mutable data managed with npm package: https://www.npmjs.com/package/slp-mutable-data"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async getTokenData (tokenId, withTxHistory = false) {
|
||||
try {
|
||||
const url = `${this.restURL}slp/token/data`
|
||||
// console.log(`url: ${url}`)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await this.axios.post(
|
||||
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
|
||||
url,
|
||||
{ tokenId, withTxHistory },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.getTokenData2() getTokenData2()
|
||||
* @apiName Token Data
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Get token icon and other media associated with a token.
|
||||
*
|
||||
* Get the icon for a token, given it's token ID.
|
||||
* This function expects a string input of a token ID property.
|
||||
* This function returns an object with a tokenIcon property that contains
|
||||
* the URL to the icon.
|
||||
*
|
||||
* The output object always have these properties:
|
||||
* - tokenIcon: A url to the token icon, if it exists.
|
||||
* - tokenStats: Data about the token from psf-slp-indexer.
|
||||
* - optimizedTokenIcon: An alternative, potentially more optimal, url to the token icon, if it exists.
|
||||
* - iconRepoCompatible: true if the token icon is available via token.bch.sx
|
||||
* - ps002Compatible: true if the token icon is compatible with PS007 specification.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData2('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
|
||||
* console.log(tokenData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* tokenStats: {
|
||||
* type: 1,
|
||||
* ticker: 'CTAIA006',
|
||||
* name: 'CTAIA006 - AI Art by Chris Troutner',
|
||||
* tokenId: '0e4543f820699294ab57e02ee2b1815a8bbc7b17a4333e4a138034e4b2324a61',
|
||||
* documentUri: 'ipfs://bafybeia5yuq7rg6jmwquako7t277cwrobcunz7cumqrv4wn6bgfvthemku',
|
||||
* documentHash: '78a00e9db312b8fff4e5c37cf592be83e6bab7f3bd5a54c9545bad5d4f3ee0f5',
|
||||
* decimals: 0,
|
||||
* mintBatonIsActive: false,
|
||||
* tokensInCirculationBN: '1',
|
||||
* tokensInCirculationStr: '1',
|
||||
* blockCreated: 757507,
|
||||
* totalBurned: '0',
|
||||
* totalMinted: '1'
|
||||
* },
|
||||
* mutableData: {
|
||||
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
|
||||
* fullSizedUrl: '',
|
||||
* about: 'This NFT was created using the PSF Token Studio at https://nft-creator.fullstack.cash',
|
||||
* userData: ''
|
||||
* },
|
||||
* immutableData: {
|
||||
* issuer: 'http://psfoundation.cash',
|
||||
* website: 'https://nft-creator.fullstack.cash',
|
||||
* dateCreated: '9/12/2022, 5:17:38 PM',
|
||||
* userData: '{\n' +
|
||||
* ' "title": "CTAIA006 - AI Art by Chris Troutner",\n' +
|
||||
* ' "about": "AI generated art. Generated from DALL-E at https://labs.openai.com",\n' +
|
||||
* ' "prompt": "whale swimming through a sky full of stars",\n' +
|
||||
* ' "algorithm": "DALL-E (stable diffusion)",\n' +
|
||||
* ' "set": "1-of-2"\n' +
|
||||
* '}'
|
||||
* },
|
||||
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
|
||||
* fullSizedUrl: '',
|
||||
* optimizedTokenIcon: 'https://p2wdb-gateway-678.fullstack.cash/ipfs/bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm/whale-night-sky-01.png',
|
||||
* optimizedFullSizedUrl: '',
|
||||
* iconRepoCompatible: false,
|
||||
* ps002Compatible: true
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async getTokenData2 (tokenId, updateCache = false) {
|
||||
try {
|
||||
const url = `${this.restURL}slp/token/data2`
|
||||
// console.log(`url: ${url}`)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await this.axios.post(
|
||||
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
|
||||
url,
|
||||
{ tokenId, updateCache },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
// console.log('error: ', error)
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PsfSlpIndexer
|
||||
+253
-68
@@ -1,20 +1,60 @@
|
||||
const axios = require("axios")
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class RawTransactions {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
// Retry configuration for transient network failures during broadcast.
|
||||
this.maxBroadcastRetries = 2
|
||||
this.broadcastRetryDelayMs = 250
|
||||
}
|
||||
|
||||
_sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
_isTransientNetworkError (error) {
|
||||
if (!error) return false
|
||||
|
||||
const msg = String(error.message || '').toLowerCase()
|
||||
const causeMsg = String(error?.cause?.message || '').toLowerCase()
|
||||
const stack = String(error.stack || '').toLowerCase()
|
||||
const code = String(error.code || error?.cause?.code || '').toUpperCase()
|
||||
|
||||
if (['ECONNRESET', 'EPIPE', 'ETIMEDOUT'].includes(code)) return true
|
||||
if (msg.includes('socket hang up')) return true
|
||||
if (causeMsg.includes('socket hang up')) return true
|
||||
if (stack.includes('socket hang up')) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async _postSendRawTransaction (hexes) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: `${this.restURL}full-node/rawtransactions/sendRawTransaction`,
|
||||
data: {
|
||||
hexes
|
||||
},
|
||||
headers: {
|
||||
...this.axiosOptions.headers,
|
||||
Connection: 'close'
|
||||
}
|
||||
}
|
||||
|
||||
return this.axios(options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,13 +119,13 @@ class RawTransactions {
|
||||
* // vin: [ [Object] ],
|
||||
* // vout: [ [Object] ] } ]
|
||||
*/
|
||||
async decodeRawTransaction(hex) {
|
||||
async decodeRawTransaction (hex) {
|
||||
try {
|
||||
// Single hex
|
||||
if (typeof hex === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/decodeRawTransaction/${hex}`,
|
||||
_this.axiosOptions
|
||||
if (typeof hex === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/rawtransactions/decodeRawTransaction/${hex}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -93,23 +133,22 @@ class RawTransactions {
|
||||
// Array of hexes
|
||||
} else if (Array.isArray(hex)) {
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: `${this.restURL}rawtransactions/decodeRawTransaction`,
|
||||
method: 'POST',
|
||||
url: `${this.restURL}full-node/rawtransactions/decodeRawTransaction`,
|
||||
data: {
|
||||
hexes: hex
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${_this.apiToken}`
|
||||
}
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
const response = await this.axios(options)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -147,36 +186,35 @@ class RawTransactions {
|
||||
* // type: 'nonstandard',
|
||||
* // p2sh: 'bitcoincash:pqwndulzwft8dlmqrteqyc9hf823xr3lcc7ypt74ts' }]
|
||||
*/
|
||||
async decodeScript(script) {
|
||||
//if (typeof script !== "string") script = JSON.stringify(script)
|
||||
async decodeScript (script) {
|
||||
// if (typeof script !== "string") script = JSON.stringify(script)
|
||||
|
||||
try {
|
||||
if (typeof script === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/decodeScript/${script}`,
|
||||
_this.axiosOptions
|
||||
if (typeof script === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/rawtransactions/decodeScript/${script}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(script)) {
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: `${this.restURL}rawtransactions/decodeScript`,
|
||||
method: 'POST',
|
||||
url: `${this.restURL}full-node/rawtransactions/decodeScript`,
|
||||
data: {
|
||||
hexes: script
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
const response = await this.axios(options)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -245,36 +283,170 @@ class RawTransactions {
|
||||
* // time: 1547752564,
|
||||
* // blocktime: 1547752564 } ]
|
||||
*/
|
||||
async getRawTransaction(txid, verbose = false) {
|
||||
async getRawTransaction (txid, verbose = false, usrObj = null) {
|
||||
try {
|
||||
if (typeof txid === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
if (typeof txid === 'string') {
|
||||
// console.log(
|
||||
// 'getRawTransaction() this.axiosOptions: ',
|
||||
// this.axiosOptions
|
||||
// )
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: `${this.restURL}rawtransactions/getRawTransaction`,
|
||||
method: 'POST',
|
||||
url: `${this.restURL}full-node/rawtransactions/getRawTransaction`,
|
||||
data: {
|
||||
txids: txid,
|
||||
verbose: verbose
|
||||
verbose,
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
const response = await this.axios(options)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Given verbose transaction details, this function retrieves the transaction
|
||||
// data for the inputs (the parent transactions). It returns an array of
|
||||
// objects. Each object corresponds to a transaction input, and contains
|
||||
// the address that generated that input UTXO.
|
||||
//
|
||||
// Assumes a single TX. Does not yet work with an array of TXs.
|
||||
// This function returns an array of objects, each object if formated as follows:
|
||||
// {
|
||||
// vin: 0, // The position of the input for the given txid
|
||||
// address: bitcoincash:qzhrpmu7nruyfcemeanqh5leuqcnf6zkjq4qm9nqh0
|
||||
// }
|
||||
async _getInputAddrs (txDetails) {
|
||||
try {
|
||||
const retArray = [] // Return array
|
||||
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
// The first input represents the sender of the BCH or tokens.
|
||||
const vin = txDetails.vin[i]
|
||||
const inputTxid = vin.txid
|
||||
const inputVout = vin.vout
|
||||
|
||||
// TODO: Coinbase TXs have no input transaction. Figure out how to
|
||||
// handle this corner case.
|
||||
|
||||
// Get the TX details for the input, in order to retrieve the address of
|
||||
// the sender.
|
||||
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
|
||||
// console.log(
|
||||
// `txDetailsParent: ${JSON.stringify(txDetailsParent, null, 2)}`
|
||||
// )
|
||||
|
||||
// The vout from the previous tx that represents the sender.
|
||||
const voutSender = txDetailsParent.vout[inputVout]
|
||||
|
||||
retArray.push({
|
||||
vin: i,
|
||||
address: voutSender.scriptPubKey.addresses[0],
|
||||
value: voutSender.value
|
||||
})
|
||||
}
|
||||
|
||||
return retArray
|
||||
} catch (error) {
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api RawTransactions.getTxData() getTxData()
|
||||
* @apiName getTxData
|
||||
* @apiGroup RawTransactions
|
||||
* @apiDescription
|
||||
* Returns an object of transaction data, including addresses for input UTXOs.
|
||||
*
|
||||
* This function is equivalent to running `getRawTransaction (txid, true)`,
|
||||
* execept the `vin` array will be populated with an `address` property that
|
||||
* contains the `bitcoincash:` address of the sender for each input.
|
||||
*
|
||||
* This function will only work with a single txid. It does not yet support an
|
||||
* array of TXIDs.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.RawTransactions.getTxData("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
// Equivalent to running: async getRawTransaction (txid, verbose = true)
|
||||
// Only handles a single TXID (not arrays).
|
||||
// Appends the BCH address to the inputs of the transaction.
|
||||
async getTxData (txid) {
|
||||
try {
|
||||
// console.log('getTxData() txid: ', txid)
|
||||
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error(
|
||||
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
|
||||
)
|
||||
}
|
||||
|
||||
// Get the TX details for the transaction under consideration.
|
||||
const txDetails = await this.getRawTransaction(txid, true)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
try {
|
||||
const inAddrs = await this._getInputAddrs(txDetails)
|
||||
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
|
||||
|
||||
// Add the input address to the transaction data.
|
||||
for (let i = 0; i < inAddrs.length; i++) {
|
||||
txDetails.vin[i].address = inAddrs[i].address
|
||||
txDetails.vin[i].value = inAddrs[i].value
|
||||
}
|
||||
} catch (err) {
|
||||
// Coinbase transactions will throw an error. Just ignore them and
|
||||
// pass back the raw transaction data.
|
||||
/* exit quietly */
|
||||
}
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
// console.log('error: ', error)
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,16 +482,22 @@ class RawTransactions {
|
||||
* })()
|
||||
* // ['0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098']
|
||||
*/
|
||||
async sendRawTransaction(hex, allowhighfees = false) {
|
||||
async sendRawTransaction (hex, allowhighfees = false) {
|
||||
try {
|
||||
// Single tx hex.
|
||||
if (typeof hex === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/sendRawTransaction/${hex}`,
|
||||
_this.axiosOptions
|
||||
if (typeof hex === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}full-node/rawtransactions/sendRawTransaction/${hex}`,
|
||||
{
|
||||
...this.axiosOptions,
|
||||
headers: {
|
||||
...this.axiosOptions.headers,
|
||||
Connection: 'close'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (response.data === "66: insufficient priority") {
|
||||
if (response.data === '66: insufficient priority') {
|
||||
console.warn(
|
||||
`WARN: Insufficient Priority! This is likely due to a fee that is too low, or insufficient funds.
|
||||
Please ensure that there is BCH in the given wallet. If you are running on the testnet, get some
|
||||
@@ -331,27 +509,34 @@ class RawTransactions {
|
||||
|
||||
// Array input
|
||||
} else if (Array.isArray(hex)) {
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: `${this.restURL}rawtransactions/sendRawTransaction`,
|
||||
data: {
|
||||
hexes: hex
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
let lastErr
|
||||
|
||||
for (let attempt = 0; attempt <= this.maxBroadcastRetries; attempt++) {
|
||||
try {
|
||||
const response = await this._postSendRawTransaction(hex)
|
||||
return response.data
|
||||
} catch (err) {
|
||||
lastErr = err
|
||||
|
||||
const isLastAttempt = attempt >= this.maxBroadcastRetries
|
||||
const shouldRetry = this._isTransientNetworkError(err) && !isLastAttempt
|
||||
if (!shouldRetry) throw err
|
||||
|
||||
const delay = this.broadcastRetryDelayMs * Math.pow(2, attempt)
|
||||
await this._sleep(delay)
|
||||
}
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
return response.data
|
||||
throw lastErr
|
||||
}
|
||||
|
||||
throw new Error(`Input hex must be a string or array of strings.`)
|
||||
throw new Error('Input hex must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RawTransactions
|
||||
export default RawTransactions
|
||||
|
||||
+21
-22
@@ -1,14 +1,13 @@
|
||||
const schnorr = require("bip-schnorr")
|
||||
import schnorr from 'bip-schnorr'
|
||||
|
||||
class Schnorr {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +35,7 @@ class Schnorr {
|
||||
* console.log("The signature is: " + createdSignature.toString("hex"))
|
||||
* // The signature is: 2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd
|
||||
*/
|
||||
sign(privateKey, message) {
|
||||
sign (privateKey, message) {
|
||||
return schnorr.sign(privateKey, message)
|
||||
}
|
||||
|
||||
@@ -68,7 +67,7 @@ class Schnorr {
|
||||
* console.error("The signature verification failed: " + e)
|
||||
* }
|
||||
*/
|
||||
verify(publicKey, message, signatureToVerify) {
|
||||
verify (publicKey, message, signatureToVerify) {
|
||||
return schnorr.verify(publicKey, message, signatureToVerify)
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ class Schnorr {
|
||||
* console.error("The signature verification failed: " + e)
|
||||
* }
|
||||
*/
|
||||
batchVerify(publicKeys, messages, signaturesToVerify) {
|
||||
batchVerify (publicKeys, messages, signaturesToVerify) {
|
||||
return schnorr.batchVerify(publicKeys, messages, signaturesToVerify)
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ class Schnorr {
|
||||
* console.error("The signature verification failed: " + e)
|
||||
* }
|
||||
*/
|
||||
nonInteractive(privateKeys, message) {
|
||||
nonInteractive (privateKeys, message) {
|
||||
return schnorr.muSig.nonInteractive(privateKeys, message)
|
||||
}
|
||||
|
||||
@@ -253,7 +252,7 @@ class Schnorr {
|
||||
* // -----------------------------------------------------------------------
|
||||
* publicData.pubKeyHash = bchjs.Schnorr.computeEll(publicData.pubKeys)
|
||||
*/
|
||||
computeEll(publicKeys) {
|
||||
computeEll (publicKeys) {
|
||||
return schnorr.muSig.computeEll(publicKeys)
|
||||
}
|
||||
|
||||
@@ -271,7 +270,7 @@ class Schnorr {
|
||||
* publicData.pubKeyHash
|
||||
* )
|
||||
*/
|
||||
publicKeyCombine(publicKeys, publicKeyHash) {
|
||||
publicKeyCombine (publicKeys, publicKeyHash) {
|
||||
return schnorr.muSig.pubKeyCombine(publicKeys, publicKeyHash)
|
||||
}
|
||||
|
||||
@@ -303,7 +302,7 @@ class Schnorr {
|
||||
* })
|
||||
* const signerSession = signerPrivateData[0].session
|
||||
*/
|
||||
sessionInitialize(sessionId, privateKey, message, pubKeyCombined, ell, idx) {
|
||||
sessionInitialize (sessionId, privateKey, message, pubKeyCombined, ell, idx) {
|
||||
return schnorr.muSig.sessionInitialize(
|
||||
sessionId,
|
||||
privateKey,
|
||||
@@ -355,7 +354,7 @@ class Schnorr {
|
||||
* data => (data.session.nonceIsNegated = signerSession.nonceIsNegated)
|
||||
* )
|
||||
*/
|
||||
sessionNonceCombine(session, nonces) {
|
||||
sessionNonceCombine (session, nonces) {
|
||||
return schnorr.muSig.sessionNonceCombine(session, nonces)
|
||||
}
|
||||
|
||||
@@ -382,7 +381,7 @@ class Schnorr {
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
partialSign(session, message, nonceCombined, pubKeyCombined) {
|
||||
partialSign (session, message, nonceCombined, pubKeyCombined) {
|
||||
return schnorr.muSig.partialSign(
|
||||
session,
|
||||
message,
|
||||
@@ -426,7 +425,7 @@ class Schnorr {
|
||||
* )
|
||||
* }
|
||||
*/
|
||||
partialSignatureVerify(
|
||||
partialSignatureVerify (
|
||||
session,
|
||||
partialSignature,
|
||||
nonceCombined,
|
||||
@@ -474,29 +473,29 @@ class Schnorr {
|
||||
* publicData.signature
|
||||
* )
|
||||
*/
|
||||
partialSignaturesCombine(nonceCombined, partialSignatures) {
|
||||
partialSignaturesCombine (nonceCombined, partialSignatures) {
|
||||
return schnorr.muSig.partialSigCombine(nonceCombined, partialSignatures)
|
||||
}
|
||||
|
||||
bufferToInt(buffer) {
|
||||
bufferToInt (buffer) {
|
||||
return schnorr.convert.bufferToInt(buffer)
|
||||
}
|
||||
|
||||
intToBuffer(bigInteger) {
|
||||
intToBuffer (bigInteger) {
|
||||
return schnorr.convert.intToBuffer(bigInteger)
|
||||
}
|
||||
|
||||
hash(buffer) {
|
||||
hash (buffer) {
|
||||
return schnorr.convert.hash(buffer)
|
||||
}
|
||||
|
||||
pointToBuffer(point) {
|
||||
pointToBuffer (point) {
|
||||
return schnorr.convert.pointToBuffer(point)
|
||||
}
|
||||
|
||||
pubKeyToPoint(publicKey) {
|
||||
pubKeyToPoint (publicKey) {
|
||||
return schnorr.convert.pubKeyToPoint(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Schnorr
|
||||
export default Schnorr
|
||||
|
||||
+136
-11
@@ -1,8 +1,133 @@
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const opcodes = require("bitcoincash-ops")
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
|
||||
// Inline opcodes data to avoid fs/import.meta issues in browser builds.
|
||||
// This data is from @psf/bitcoincash-ops/index.json
|
||||
const opcodes = {
|
||||
OP_FALSE: 0,
|
||||
OP_0: 0,
|
||||
OP_PUSHDATA1: 76,
|
||||
OP_PUSHDATA2: 77,
|
||||
OP_PUSHDATA4: 78,
|
||||
OP_1NEGATE: 79,
|
||||
OP_RESERVED: 80,
|
||||
OP_TRUE: 81,
|
||||
OP_1: 81,
|
||||
OP_2: 82,
|
||||
OP_3: 83,
|
||||
OP_4: 84,
|
||||
OP_5: 85,
|
||||
OP_6: 86,
|
||||
OP_7: 87,
|
||||
OP_8: 88,
|
||||
OP_9: 89,
|
||||
OP_10: 90,
|
||||
OP_11: 91,
|
||||
OP_12: 92,
|
||||
OP_13: 93,
|
||||
OP_14: 94,
|
||||
OP_15: 95,
|
||||
OP_16: 96,
|
||||
OP_NOP: 97,
|
||||
OP_VER: 98,
|
||||
OP_IF: 99,
|
||||
OP_NOTIF: 100,
|
||||
OP_VERIF: 101,
|
||||
OP_VERNOTIF: 102,
|
||||
OP_ELSE: 103,
|
||||
OP_ENDIF: 104,
|
||||
OP_VERIFY: 105,
|
||||
OP_RETURN: 106,
|
||||
OP_TOALTSTACK: 107,
|
||||
OP_FROMALTSTACK: 108,
|
||||
OP_2DROP: 109,
|
||||
OP_2DUP: 110,
|
||||
OP_3DUP: 111,
|
||||
OP_2OVER: 112,
|
||||
OP_2ROT: 113,
|
||||
OP_2SWAP: 114,
|
||||
OP_IFDUP: 115,
|
||||
OP_DEPTH: 116,
|
||||
OP_DROP: 117,
|
||||
OP_DUP: 118,
|
||||
OP_NIP: 119,
|
||||
OP_OVER: 120,
|
||||
OP_PICK: 121,
|
||||
OP_ROLL: 122,
|
||||
OP_ROT: 123,
|
||||
OP_SWAP: 124,
|
||||
OP_TUCK: 125,
|
||||
OP_CAT: 126,
|
||||
OP_SPLIT: 127,
|
||||
OP_NUM2BIN: 128,
|
||||
OP_BIN2NUM: 129,
|
||||
OP_SIZE: 130,
|
||||
OP_INVERT: 131,
|
||||
OP_AND: 132,
|
||||
OP_OR: 133,
|
||||
OP_XOR: 134,
|
||||
OP_EQUAL: 135,
|
||||
OP_EQUALVERIFY: 136,
|
||||
OP_RESERVED1: 137,
|
||||
OP_RESERVED2: 138,
|
||||
OP_1ADD: 139,
|
||||
OP_1SUB: 140,
|
||||
OP_2MUL: 141,
|
||||
OP_2DIV: 142,
|
||||
OP_NEGATE: 143,
|
||||
OP_ABS: 144,
|
||||
OP_NOT: 145,
|
||||
OP_0NOTEQUAL: 146,
|
||||
OP_ADD: 147,
|
||||
OP_SUB: 148,
|
||||
OP_MUL: 149,
|
||||
OP_DIV: 150,
|
||||
OP_MOD: 151,
|
||||
OP_LSHIFT: 152,
|
||||
OP_RSHIFT: 153,
|
||||
OP_BOOLAND: 154,
|
||||
OP_BOOLOR: 155,
|
||||
OP_NUMEQUAL: 156,
|
||||
OP_NUMEQUALVERIFY: 157,
|
||||
OP_NUMNOTEQUAL: 158,
|
||||
OP_LESSTHAN: 159,
|
||||
OP_GREATERTHAN: 160,
|
||||
OP_LESSTHANOREQUAL: 161,
|
||||
OP_GREATERTHANOREQUAL: 162,
|
||||
OP_MIN: 163,
|
||||
OP_MAX: 164,
|
||||
OP_WITHIN: 165,
|
||||
OP_RIPEMD160: 166,
|
||||
OP_SHA1: 167,
|
||||
OP_SHA256: 168,
|
||||
OP_HASH160: 169,
|
||||
OP_HASH256: 170,
|
||||
OP_CODESEPARATOR: 171,
|
||||
OP_CHECKSIG: 172,
|
||||
OP_CHECKSIGVERIFY: 173,
|
||||
OP_CHECKMULTISIG: 174,
|
||||
OP_CHECKMULTISIGVERIFY: 175,
|
||||
OP_NOP1: 176,
|
||||
OP_NOP2: 177,
|
||||
OP_CHECKLOCKTIMEVERIFY: 177,
|
||||
OP_NOP3: 178,
|
||||
OP_CHECKSEQUENCEVERIFY: 178,
|
||||
OP_NOP4: 179,
|
||||
OP_NOP5: 180,
|
||||
OP_NOP6: 181,
|
||||
OP_NOP7: 182,
|
||||
OP_NOP8: 183,
|
||||
OP_NOP9: 184,
|
||||
OP_NOP10: 185,
|
||||
OP_CHECKDATASIG: 186,
|
||||
OP_CHECKDATASIGVERIFY: 187,
|
||||
OP_REVERSEBYTES: 188,
|
||||
OP_PUBKEYHASH: 253,
|
||||
OP_PUBKEY: 254,
|
||||
OP_INVALIDOPCODE: 255
|
||||
}
|
||||
|
||||
class Script {
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.opcodes = opcodes
|
||||
this.nullData = Bitcoin.script.nullData
|
||||
this.multisig = {
|
||||
@@ -58,7 +183,7 @@ class Script {
|
||||
* bchjs.Script.classifyInput(bchjs.Script.fromASM(scripthashInput));
|
||||
* // scripthash
|
||||
*/
|
||||
classifyInput(script) {
|
||||
classifyInput (script) {
|
||||
return Bitcoin.script.classifyInput(script)
|
||||
}
|
||||
|
||||
@@ -90,7 +215,7 @@ class Script {
|
||||
* bchjs.Script.classifyOutput(bchjs.Script.fromASM(scripthashOutput));
|
||||
* // scripthash
|
||||
*/
|
||||
classifyOutput(script) {
|
||||
classifyOutput (script) {
|
||||
return Bitcoin.script.classifyOutput(script)
|
||||
}
|
||||
|
||||
@@ -116,7 +241,7 @@ class Script {
|
||||
* // 136,
|
||||
* // 172 ]
|
||||
*/
|
||||
decode(scriptBuffer) {
|
||||
decode (scriptBuffer) {
|
||||
return Bitcoin.script.decompile(scriptBuffer)
|
||||
}
|
||||
|
||||
@@ -150,7 +275,7 @@ class Script {
|
||||
* bchjs.Script.encode(scriptPubKey);
|
||||
* // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac>
|
||||
*/
|
||||
encode(scriptChunks) {
|
||||
encode (scriptChunks) {
|
||||
const arr = []
|
||||
scriptChunks.forEach(chunk => {
|
||||
arr.push(chunk)
|
||||
@@ -187,7 +312,7 @@ class Script {
|
||||
* bchjs.Script.encode2(scriptPubKey);
|
||||
* // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac>
|
||||
*/
|
||||
encode2(scriptChunks) {
|
||||
encode2 (scriptChunks) {
|
||||
const arr = []
|
||||
scriptChunks.forEach(chunk => {
|
||||
arr.push(chunk)
|
||||
@@ -213,7 +338,7 @@ class Script {
|
||||
* bchjs.Script.toASM(scriptBuffer);
|
||||
* // OP_DUP OP_HASH160 bee4182d9fbc8931a728410a0cd3e0f340f2995a OP_EQUALVERIFY OP_CHECKSIG
|
||||
*/
|
||||
toASM(buffer) {
|
||||
toASM (buffer) {
|
||||
return Bitcoin.script.toASM(buffer)
|
||||
}
|
||||
|
||||
@@ -235,9 +360,9 @@ class Script {
|
||||
* bchjs.Script.fromASM(scriptPubKeyASM);
|
||||
* // <Buffer 76 a9 14 be e4 18 2d 9f bc 89 31 a7 28 41 0a 0c d3 e0 f3 40 f2 99 5a 88 ac>
|
||||
*/
|
||||
fromASM(asm) {
|
||||
fromASM (asm) {
|
||||
return Bitcoin.script.fromASM(asm)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Script
|
||||
export default Script
|
||||
|
||||
+29
-23
@@ -1,11 +1,11 @@
|
||||
const BCHJSAddress = require("../address")
|
||||
import BCHJSAddress from '../address.js'
|
||||
|
||||
import bchaddrjs from 'bchaddrjs-slp'
|
||||
// const bchAddress = new BCHJSAddress()
|
||||
let bchAddress
|
||||
|
||||
const bchaddrjs = require("bchaddrjs-slp")
|
||||
|
||||
class Address extends BCHJSAddress {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
super(config)
|
||||
|
||||
this.restURL = config.restURL
|
||||
@@ -36,7 +36,7 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toSLPAddress('qzm47qz5ue99y9yl4aca7jnz7dwgdenl85jkfx3znl')
|
||||
* // simpleledger:qzm47qz5ue99y9yl4aca7jnz7dwgdenl857dzayzdp
|
||||
*
|
||||
* // tesnet legacy
|
||||
* // testnet legacy
|
||||
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx')
|
||||
* // slptest:qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
|
||||
*
|
||||
@@ -52,11 +52,11 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
|
||||
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
|
||||
*/
|
||||
toSLPAddress(address, prefix = true, regtest = false) {
|
||||
toSLPAddress (address, prefix = true, regtest = false) {
|
||||
this._ensureValidAddress(address)
|
||||
const slpAddress = bchaddrjs.toSlpAddress(address)
|
||||
if (prefix) return slpAddress
|
||||
return slpAddress.split(":")[1]
|
||||
return slpAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,11 +98,11 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
|
||||
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3
|
||||
*/
|
||||
toCashAddress(address, prefix = true, regtest = false) {
|
||||
toCashAddress (address, prefix = true, regtest = false) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddress = bchaddrjs.toCashAddress(address)
|
||||
if (prefix) return cashAddress
|
||||
return cashAddress.split(":")[1]
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,18 +145,18 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toLegacyAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggs3v58dse')
|
||||
* // mqc1tmwY2368LLGktnePzEyPAsgADxbksi
|
||||
*/
|
||||
toLegacyAddress(address) {
|
||||
toLegacyAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.toLegacyAddress(cashAddr)
|
||||
}
|
||||
|
||||
isLegacyAddress(address) {
|
||||
isLegacyAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
return bchAddress.isLegacyAddress(address)
|
||||
}
|
||||
|
||||
isCashAddress(address) {
|
||||
isCashAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
if (bchaddrjs.isSlpAddress(address)) return false
|
||||
|
||||
@@ -195,7 +195,7 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.isSLPAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // false
|
||||
*/
|
||||
isSLPAddress(address) {
|
||||
isSLPAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
return bchaddrjs.isSlpAddress(address)
|
||||
}
|
||||
@@ -248,7 +248,7 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.isMainnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // false
|
||||
*/
|
||||
isMainnetAddress(address) {
|
||||
isMainnetAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashaddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.isMainnetAddress(cashaddr)
|
||||
@@ -301,11 +301,12 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // true
|
||||
*/
|
||||
isTestnetAddress(address) {
|
||||
isTestnetAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.isTestnetAddress(cashAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Address.isP2PKHAddress() isP2PKHAddress()
|
||||
* @apiName isP2PKHAddress
|
||||
@@ -353,11 +354,12 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.isP2PKHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // true
|
||||
*/
|
||||
isP2PKHAddress(address) {
|
||||
isP2PKHAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.isP2PKHAddress(cashAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Address.isP2SHAddress() isP2SHAddress()
|
||||
* @apiName isP2SHAddress
|
||||
@@ -405,11 +407,12 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.isP2SHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // false
|
||||
*/
|
||||
isP2SHAddress(address) {
|
||||
isP2SHAddress (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.isP2SHAddress(cashAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Address.detectAddressFormat() detectAddressFormat()
|
||||
* @apiName detectAddressFormat
|
||||
@@ -457,12 +460,13 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.detectAddressFormat('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // legacy
|
||||
*/
|
||||
detectAddressFormat(address) {
|
||||
detectAddressFormat (address) {
|
||||
this._ensureValidAddress(address)
|
||||
if (bchaddrjs.isSlpAddress(address)) return "slpaddr"
|
||||
if (bchaddrjs.isSlpAddress(address)) return 'slpaddr'
|
||||
|
||||
return bchAddress.detectAddressFormat(address)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Address.detectAddressNetwork() detectAddressNetwork()
|
||||
* @apiName detectAddressNetwork
|
||||
@@ -510,11 +514,12 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.detectAddressNetwork('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
|
||||
* // testnet
|
||||
*/
|
||||
detectAddressNetwork(address) {
|
||||
detectAddressNetwork (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.detectAddressNetwork(cashAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Address.detectAddressType() detectAddressType()
|
||||
* @apiName detectAddressType
|
||||
@@ -562,11 +567,12 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.detectAddressType('mqc1tmwY2368LLGktnePzEyPAsgADxbksi');
|
||||
* // p2pkh
|
||||
*/
|
||||
detectAddressType(address) {
|
||||
detectAddressType (address) {
|
||||
this._ensureValidAddress(address)
|
||||
const cashAddr = bchaddrjs.toCashAddress(address)
|
||||
return bchAddress.detectAddressType(cashAddr)
|
||||
}
|
||||
|
||||
/*
|
||||
async details(address) {
|
||||
let tmpBITBOX
|
||||
@@ -644,7 +650,7 @@ class Address extends BCHJSAddress {
|
||||
return tmpBITBOX.Address.transactions(address)
|
||||
}
|
||||
*/
|
||||
_ensureValidAddress(address) {
|
||||
_ensureValidAddress (address) {
|
||||
try {
|
||||
bchaddrjs.toCashAddress(address)
|
||||
} catch (err) {
|
||||
@@ -655,4 +661,4 @@ class Address extends BCHJSAddress {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Address
|
||||
export default Address
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
//const BCHJS = require("../bch-js")
|
||||
//const bchjs = new BCHJS()
|
||||
// import BCHJS from "../bch-js.js"
|
||||
// const bchjs = new BCHJS()
|
||||
|
||||
const BCHJSECPair = require("../ecpair")
|
||||
import BCHJSECPair from '../ecpair.js'
|
||||
|
||||
const bchaddrjs = require("bchaddrjs-slp")
|
||||
import bchaddrjs from 'bchaddrjs-slp'
|
||||
|
||||
class ECPair extends BCHJSECPair {
|
||||
/*
|
||||
@@ -25,10 +25,10 @@ class ECPair extends BCHJSECPair {
|
||||
* bchjs.SLP.ECPair.toSLPAddress(ecpair);
|
||||
* // slptest:qq835u5srlcqwrtwt6xm4efwan30fxg9hcqag6fk03
|
||||
*/
|
||||
static toSLPAddress(ecpair) {
|
||||
static toSLPAddress (ecpair) {
|
||||
const slpAddress = bchaddrjs.toSlpAddress(this.toCashAddress(ecpair))
|
||||
return slpAddress
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ECPair
|
||||
export default ECPair
|
||||
|
||||
+69
-56
@@ -8,23 +8,30 @@
|
||||
(Parent) token.
|
||||
*/
|
||||
|
||||
const Address = require("./address")
|
||||
// Public npm libraries
|
||||
// import axios from 'axios'
|
||||
|
||||
const BigNumber = require("bignumber.js")
|
||||
const slpMdm = require("slp-mdm")
|
||||
// Local libraries.
|
||||
import Address from './address.js'
|
||||
|
||||
// import BigNumber from 'bignumber.js'
|
||||
import slpMdm from 'slp-mdm'
|
||||
import TransactionBuilder from '../transaction-builder.js'
|
||||
|
||||
// let _this
|
||||
// const addy = new Address()
|
||||
let addy
|
||||
const TransactionBuilder = require("../transaction-builder")
|
||||
|
||||
class TokenType1 {
|
||||
constructor(config) {
|
||||
class Nft1 {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
|
||||
addy = new Address(config)
|
||||
|
||||
// Instantiate the transaction builder.
|
||||
TransactionBuilder.setAddress(addy)
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,12 +66,12 @@ class TokenType1 {
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
|
||||
*
|
||||
*/
|
||||
newNFTGroupOpReturn(configObj) {
|
||||
newNFTGroupOpReturn (configObj) {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
// Prevent error if user fails to add the document hash.
|
||||
if (!configObj.documentHash) configObj.documentHash = ""
|
||||
if (!configObj.documentHash) configObj.documentHash = ''
|
||||
|
||||
// If mint baton is not specified, then replace it with null.
|
||||
if (!configObj.mintBatonVout) configObj.mintBatonVout = null
|
||||
@@ -81,7 +88,7 @@ class TokenType1 {
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
console.log(`Error in generateNFTParentOpReturn()`)
|
||||
console.log('Error in generateNFTParentOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -130,43 +137,47 @@ class TokenType1 {
|
||||
* // See additional code here:
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
|
||||
*/
|
||||
mintNFTGroupOpReturn(tokenUtxos, mintQty, destroyBaton = false) {
|
||||
try {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(tokenUtxos))
|
||||
throw new Error(`tokenUtxos must be an array.`)
|
||||
|
||||
// Loop through the tokenUtxos array and find the minting baton.
|
||||
let mintBatonUtxo
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
if (tokenUtxos[i].utxoType === "minting-baton")
|
||||
mintBatonUtxo = tokenUtxos[i]
|
||||
}
|
||||
|
||||
// Throw an error if the minting baton could not be found.
|
||||
if (!mintBatonUtxo)
|
||||
throw new Error(`Minting baton could not be found in tokenUtxos array.`)
|
||||
|
||||
const tokenId = mintBatonUtxo.tokenId
|
||||
|
||||
if (!tokenId)
|
||||
throw new Error(`tokenId property not found in mint-baton UTXO.`)
|
||||
|
||||
// Signal that the baton should be passed or detroyed.
|
||||
let batonVout = 2
|
||||
if (destroyBaton) batonVout = null
|
||||
|
||||
const script = slpMdm.NFT1.Group.mint(
|
||||
tokenId,
|
||||
batonVout,
|
||||
new slpMdm.BN(mintQty)
|
||||
)
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
// console.log(`Error in generateMintOpReturn()`)
|
||||
throw err
|
||||
mintNFTGroupOpReturn (tokenUtxos, mintQty, destroyBaton = false) {
|
||||
// try {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(tokenUtxos)) {
|
||||
throw new Error('tokenUtxos must be an array.')
|
||||
}
|
||||
|
||||
// Loop through the tokenUtxos array and find the minting baton.
|
||||
let mintBatonUtxo
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
|
||||
mintBatonUtxo = tokenUtxos[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Throw an error if the minting baton could not be found.
|
||||
if (!mintBatonUtxo) {
|
||||
throw new Error('Minting baton could not be found in tokenUtxos array.')
|
||||
}
|
||||
|
||||
const tokenId = mintBatonUtxo.tokenId
|
||||
|
||||
if (!tokenId) {
|
||||
throw new Error('tokenId property not found in mint-baton UTXO.')
|
||||
}
|
||||
|
||||
// Signal that the baton should be passed or detroyed.
|
||||
let batonVout = 2
|
||||
if (destroyBaton) batonVout = null
|
||||
|
||||
const script = slpMdm.NFT1.Group.mint(
|
||||
tokenId,
|
||||
batonVout,
|
||||
new slpMdm.BN(mintQty)
|
||||
)
|
||||
|
||||
return script
|
||||
// } catch (err) {
|
||||
// // console.log(`Error in generateMintOpReturn()`)
|
||||
// throw err
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,12 +211,12 @@ class TokenType1 {
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
|
||||
*
|
||||
*/
|
||||
generateNFTChildGenesisOpReturn(configObj) {
|
||||
generateNFTChildGenesisOpReturn (configObj) {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
// Prevent error if user fails to add the document hash.
|
||||
if (!configObj.documentHash) configObj.documentHash = ""
|
||||
if (!configObj.documentHash) configObj.documentHash = ''
|
||||
|
||||
// If mint baton is not specified, then replace it with null.
|
||||
if (!configObj.mintBatonVout) configObj.mintBatonVout = null
|
||||
@@ -217,12 +228,12 @@ class TokenType1 {
|
||||
configObj.documentHash,
|
||||
0,
|
||||
configObj.mintBatonVout,
|
||||
new slpMdm.BN("1")
|
||||
new slpMdm.BN('1')
|
||||
)
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
console.log(`Error in generateNFTChildGenesisOpReturn()`)
|
||||
console.log('Error in generateNFTChildGenesisOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -270,7 +281,7 @@ class TokenType1 {
|
||||
* // See additional code here:
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
|
||||
*/
|
||||
generateNFTChildSendOpReturn(tokenUtxos, sendQty) {
|
||||
generateNFTChildSendOpReturn (tokenUtxos, sendQty) {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
@@ -278,8 +289,9 @@ class TokenType1 {
|
||||
|
||||
// Calculate the total amount of tokens owned by the wallet.
|
||||
let totalTokens = 0
|
||||
for (let i = 0; i < tokenUtxos.length; i++)
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
totalTokens += tokenUtxos[i].tokenQty
|
||||
}
|
||||
|
||||
const change = totalTokens - sendQty
|
||||
|
||||
@@ -312,7 +324,7 @@ class TokenType1 {
|
||||
|
||||
return { script, outputs }
|
||||
} catch (err) {
|
||||
console.log(`Error in generateNFTChildSendOpReturn()`)
|
||||
console.log('Error in generateNFTChildSendOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -360,7 +372,7 @@ class TokenType1 {
|
||||
* // See additional code here:
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
|
||||
*/
|
||||
generateNFTGroupSendOpReturn(tokenUtxos, sendQty) {
|
||||
generateNFTGroupSendOpReturn (tokenUtxos, sendQty) {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
@@ -368,8 +380,9 @@ class TokenType1 {
|
||||
|
||||
// Calculate the total amount of tokens owned by the wallet.
|
||||
let totalTokens = 0
|
||||
for (let i = 0; i < tokenUtxos.length; i++)
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
totalTokens += tokenUtxos[i].tokenQty
|
||||
}
|
||||
|
||||
const change = totalTokens - sendQty
|
||||
|
||||
@@ -402,10 +415,10 @@ class TokenType1 {
|
||||
|
||||
return { script, outputs }
|
||||
} catch (err) {
|
||||
console.log(`Error in generateNFTGroupSendOpReturn()`)
|
||||
console.log('Error in generateNFTGroupSendOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenType1
|
||||
export default Nft1
|
||||
|
||||
+20
-22
@@ -7,34 +7,32 @@
|
||||
|
||||
// imports
|
||||
// require deps
|
||||
//const BCHJS = require("../bch-js")
|
||||
const Address = require("./address")
|
||||
const ECPair = require("./ecpair")
|
||||
// const HDNode = require("./hdnode")
|
||||
const TokenType1 = require("./tokentype1")
|
||||
const NFT1 = require("./nft1")
|
||||
const Utils = require("./utils")
|
||||
// import BCHJS from "../bch-js.js"
|
||||
import Address from './address.js'
|
||||
import ECPair from './ecpair.js'
|
||||
// import HDNode from "./hdnode.js"
|
||||
import TokenType1 from './tokentype1.js'
|
||||
import NFT1 from './nft1.js'
|
||||
import Utils from './utils.js'
|
||||
|
||||
// SLP is a superset of BITBOX
|
||||
class SLP {
|
||||
constructor(config) {
|
||||
const tmp = {}
|
||||
if (!config || !config.restURL) {
|
||||
tmp.restURL = `https://api.fullstack.cash/v3/`
|
||||
} else {
|
||||
tmp.restURL = config.restURL
|
||||
tmp.apiToken = config.apiToken
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.authToken = config.authToken
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
this.restURL = tmp.restURL
|
||||
this.apiToken = tmp.apiToken
|
||||
|
||||
this.Address = new Address(tmp)
|
||||
this.Address = new Address(config)
|
||||
this.ECPair = ECPair
|
||||
this.TokenType1 = new TokenType1(tmp)
|
||||
this.NFT1 = new NFT1(this.restURL)
|
||||
this.Utils = new Utils(tmp)
|
||||
this.TokenType1 = new TokenType1(config)
|
||||
this.NFT1 = new NFT1(config)
|
||||
this.Utils = new Utils(config)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SLP
|
||||
export default SLP
|
||||
|
||||
+103
-63
@@ -1,34 +1,38 @@
|
||||
//const BCHJS = require("../bch-js")
|
||||
//const bchjs = new BCHJS()
|
||||
/*
|
||||
This library handles the OP_RETURN of SLP TokenType1 transactions.
|
||||
*/
|
||||
|
||||
const Address = require("./address")
|
||||
const Script = require("../script")
|
||||
// import BCHJS from "../bch-js.js"
|
||||
// const bchjs = new BCHJS()
|
||||
|
||||
const BigNumber = require("bignumber.js")
|
||||
const slpMdm = require("slp-mdm")
|
||||
const axios = require("axios")
|
||||
import Address from './address.js'
|
||||
import Script from '../script.js'
|
||||
|
||||
import BigNumber from 'bignumber.js'
|
||||
import slpMdm from 'slp-mdm'
|
||||
import axios from 'axios'
|
||||
import TransactionBuilder from '../transaction-builder.js'
|
||||
|
||||
// const addy = new Address()
|
||||
let addy
|
||||
const TransactionBuilder = require("../transaction-builder")
|
||||
|
||||
let _this // local global
|
||||
|
||||
class TokenType1 {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
addy = new Address(config)
|
||||
this.Script = new Script()
|
||||
|
||||
this.axios = axios
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate the transaction builder.
|
||||
TransactionBuilder.setAddress(addy)
|
||||
@@ -77,17 +81,21 @@ class TokenType1 {
|
||||
* // See additional code here:
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/send-token/send-token.js
|
||||
*/
|
||||
generateSendOpReturn(tokenUtxos, sendQty) {
|
||||
generateSendOpReturn (tokenUtxos, sendQty) {
|
||||
try {
|
||||
const tokenId = tokenUtxos[0].tokenId
|
||||
const decimals = tokenUtxos[0].decimals
|
||||
|
||||
// Calculate the total amount of tokens owned by the wallet.
|
||||
let totalTokens = 0
|
||||
for (let i = 0; i < tokenUtxos.length; i++)
|
||||
totalTokens += tokenUtxos[i].tokenQty
|
||||
const sendQtyBig = new BigNumber(sendQty).times(10 ** decimals)
|
||||
|
||||
const change = totalTokens - sendQty
|
||||
// Calculate the total amount of tokens owned by the wallet.
|
||||
const totalTokens = tokenUtxos.reduce(
|
||||
(tot, txo) =>
|
||||
tot.plus(new BigNumber(txo.tokenQty).times(10 ** decimals)),
|
||||
new BigNumber(0)
|
||||
)
|
||||
|
||||
const change = totalTokens.minus(sendQtyBig)
|
||||
// console.log(`change: ${change}`)
|
||||
|
||||
let script
|
||||
@@ -98,18 +106,22 @@ class TokenType1 {
|
||||
outputs = 2
|
||||
|
||||
// Convert the send quantity to the format expected by slp-mdm.
|
||||
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
|
||||
baseQty = baseQty.absoluteValue()
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
// console.log(`baseQty: `, baseQty)
|
||||
const baseQty = sendQtyBig.toString()
|
||||
// console.log('baseQty: ', baseQty)
|
||||
|
||||
// Convert the change quantity to the format expected by slp-mdm.
|
||||
let baseChange = new BigNumber(change).times(10 ** decimals)
|
||||
baseChange = baseChange.absoluteValue()
|
||||
baseChange = Math.floor(baseChange)
|
||||
baseChange = baseChange.toString()
|
||||
// console.log(`baseChange: `, baseChange)
|
||||
const baseChange = change.toString()
|
||||
// console.log('baseChange: ', baseChange)
|
||||
|
||||
// Check for potential burns
|
||||
const outputQty = new BigNumber(baseChange).plus(new BigNumber(baseQty))
|
||||
const inputQty = new BigNumber(totalTokens)
|
||||
const tokenOutputDelta = outputQty.minus(inputQty).toString() !== '0'
|
||||
if (tokenOutputDelta) {
|
||||
throw new Error(
|
||||
'Token transaction inputs do not match outputs, cannot send transaction'
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the OP_RETURN as a Buffer.
|
||||
script = slpMdm.TokenType1.send(tokenId, [
|
||||
@@ -120,13 +132,19 @@ class TokenType1 {
|
||||
|
||||
// Corner case, when there is no token change to send back.
|
||||
} else {
|
||||
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
|
||||
baseQty = baseQty.absoluteValue()
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
const baseQty = sendQtyBig.toString()
|
||||
// console.log(`baseQty: `, baseQty)
|
||||
|
||||
// console.log(`baseQty: ${baseQty.toString()}`)
|
||||
// Check for potential burns
|
||||
const noChangeOutputQty = new BigNumber(baseQty)
|
||||
const noChangeInputQty = new BigNumber(totalTokens)
|
||||
const tokenSingleOutputError =
|
||||
noChangeOutputQty.minus(noChangeInputQty).toString() !== '0'
|
||||
if (tokenSingleOutputError) {
|
||||
throw new Error(
|
||||
'Token transaction inputs do not match outputs, cannot send transaction'
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the OP_RETURN as a Buffer.
|
||||
script = slpMdm.TokenType1.send(tokenId, [new slpMdm.BN(baseQty)])
|
||||
@@ -134,7 +152,7 @@ class TokenType1 {
|
||||
|
||||
return { script, outputs }
|
||||
} catch (err) {
|
||||
console.log(`Error in generateSendOpReturn()`)
|
||||
console.log('Error in generateSendOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -185,15 +203,21 @@ class TokenType1 {
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/burn-tokens/burn-tokens.js
|
||||
*
|
||||
*/
|
||||
generateBurnOpReturn(tokenUtxos, burnQty) {
|
||||
generateBurnOpReturn (tokenUtxos, burnQty) {
|
||||
try {
|
||||
const tokenId = tokenUtxos[0].tokenId
|
||||
const decimals = tokenUtxos[0].decimals
|
||||
|
||||
// Calculate the total amount of tokens owned by the wallet.
|
||||
let totalTokens = 0
|
||||
for (let i = 0; i < tokenUtxos.length; i++)
|
||||
totalTokens += tokenUtxos[i].tokenQty
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
totalTokens += parseFloat(tokenUtxos[i].tokenQty)
|
||||
}
|
||||
|
||||
// Make sure burn quantity isn't bigger than the total amount in tokens
|
||||
if (burnQty > totalTokens) {
|
||||
burnQty = totalTokens
|
||||
}
|
||||
|
||||
const remainder = totalTokens - burnQty
|
||||
|
||||
@@ -209,7 +233,7 @@ class TokenType1 {
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
console.log(`Error in generateBurnOpReturn()`)
|
||||
console.log('Error in generateBurnOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -248,19 +272,25 @@ class TokenType1 {
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/create-token/create-token.js
|
||||
*
|
||||
*/
|
||||
generateGenesisOpReturn(configObj) {
|
||||
generateGenesisOpReturn (configObj) {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
let baseQty = new BigNumber(configObj.initialQty).times(
|
||||
10 ** configObj.decimals
|
||||
)
|
||||
let baseQty
|
||||
if (configObj.decimals !== 0) {
|
||||
baseQty = new BigNumber(configObj.initialQty).times(
|
||||
10 ** configObj.decimals
|
||||
)
|
||||
} else {
|
||||
baseQty = new BigNumber(configObj.initialQty)
|
||||
}
|
||||
|
||||
baseQty = baseQty.absoluteValue()
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
|
||||
// Prevent error if user fails to add the document hash.
|
||||
if (!configObj.documentHash) configObj.documentHash = ""
|
||||
if (!configObj.documentHash) configObj.documentHash = ''
|
||||
|
||||
// If mint baton is not specified, then replace it with null.
|
||||
if (!configObj.mintBatonVout) configObj.mintBatonVout = null
|
||||
@@ -277,7 +307,7 @@ class TokenType1 {
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
console.log(`Error in generateGenesisOpReturn()`)
|
||||
console.log('Error in generateGenesisOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -328,36 +358,46 @@ class TokenType1 {
|
||||
* // See additional code here:
|
||||
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/mint-token/mint-token.js
|
||||
*/
|
||||
generateMintOpReturn(tokenUtxos, mintQty, destroyBaton = false) {
|
||||
generateMintOpReturn (tokenUtxos, mintQty, destroyBaton = false) {
|
||||
try {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(tokenUtxos))
|
||||
throw new Error(`tokenUtxos must be an array.`)
|
||||
if (!Array.isArray(tokenUtxos)) {
|
||||
throw new Error('tokenUtxos must be an array.')
|
||||
}
|
||||
if (!mintQty) {
|
||||
throw new Error('mintQty must be a positive number.')
|
||||
}
|
||||
|
||||
// Loop through the tokenUtxos array and find the minting baton.
|
||||
let mintBatonUtxo
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
if (tokenUtxos[i].utxoType === "minting-baton")
|
||||
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
|
||||
mintBatonUtxo = tokenUtxos[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Throw an error if the minting baton could not be found.
|
||||
if (!mintBatonUtxo)
|
||||
throw new Error(`Minting baton could not be found in tokenUtxos array.`)
|
||||
if (!mintBatonUtxo) {
|
||||
throw new Error('Minting baton could not be found in tokenUtxos array.')
|
||||
}
|
||||
|
||||
const tokenId = mintBatonUtxo.tokenId
|
||||
const decimals = mintBatonUtxo.decimals
|
||||
|
||||
if (!tokenId)
|
||||
throw new Error(`tokenId property not found in mint-baton UTXO.`)
|
||||
if (!decimals)
|
||||
throw new Error(`decimals property not found in mint-baton UTXO.`)
|
||||
if (!tokenId) {
|
||||
throw new Error('tokenId property not found in mint-baton UTXO.')
|
||||
}
|
||||
if (!decimals && decimals !== 0) {
|
||||
throw new Error('decimals property not found in mint-baton UTXO.')
|
||||
}
|
||||
|
||||
let baseQty = new BigNumber(mintQty).times(10 ** decimals)
|
||||
baseQty = baseQty.absoluteValue()
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
|
||||
if (isNaN(baseQty)) throw new Error('baseQty is non a number!')
|
||||
|
||||
// Signal that the baton should be passed or detroyed.
|
||||
let batonVout = 2
|
||||
if (destroyBaton) batonVout = null
|
||||
@@ -370,7 +410,7 @@ class TokenType1 {
|
||||
|
||||
return script
|
||||
} catch (err) {
|
||||
// console.log(`Error in generateMintOpReturn()`)
|
||||
console.log('Error in generateMintOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -402,7 +442,7 @@ class TokenType1 {
|
||||
* "outputs": 2
|
||||
* }
|
||||
*/
|
||||
async getHexOpReturn(tokenUtxos, sendQty) {
|
||||
async getHexOpReturn (tokenUtxos, sendQty) {
|
||||
try {
|
||||
// TODO: Add input filtering.
|
||||
|
||||
@@ -414,7 +454,7 @@ class TokenType1 {
|
||||
const result = await _this.axios.post(
|
||||
`${this.restURL}slp/generatesendopreturn`,
|
||||
data,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
const slpSendObj = result.data
|
||||
@@ -426,10 +466,10 @@ class TokenType1 {
|
||||
|
||||
return slpSendObj
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
console.log('Error in getHexOpReturn()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TokenType1
|
||||
export default TokenType1
|
||||
|
||||
+76
-1090
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
const io = require("socket.io-client")
|
||||
|
||||
class Socket {
|
||||
constructor(config = {}) {
|
||||
if (typeof config === "string") {
|
||||
// TODO remove this check in v2.0
|
||||
this.socket = io(`${config}`)
|
||||
} else {
|
||||
if (config.restURL) {
|
||||
this.socket = io(`${config.restURL}`)
|
||||
} else {
|
||||
const restURL = "https://rest.bitcoin.com"
|
||||
this.socket = io(`${restURL}`)
|
||||
}
|
||||
|
||||
if (config.callback) config.callback()
|
||||
}
|
||||
}
|
||||
|
||||
listen(endpoint, cb) {
|
||||
this.socket.emit(endpoint)
|
||||
|
||||
if (endpoint === "blocks") this.socket.on("blocks", msg => cb(msg))
|
||||
else if (endpoint === "transactions")
|
||||
this.socket.on("transactions", msg => cb(msg))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Socket
|
||||
+20
-18
@@ -1,18 +1,16 @@
|
||||
const Bitcoin = require("bitcoincashjs-lib")
|
||||
const coininfo = require("coininfo")
|
||||
const bip66 = require("bip66")
|
||||
const bip68 = require("bc-bip68")
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import coininfo from '@psf/coininfo'
|
||||
import bip66 from 'bip66'
|
||||
import bip68 from 'bc-bip68'
|
||||
|
||||
class TransactionBuilder {
|
||||
static setAddress(address) {
|
||||
static setAddress (address) {
|
||||
TransactionBuilder._address = address
|
||||
}
|
||||
|
||||
constructor(network = "mainnet") {
|
||||
constructor (network = 'mainnet') {
|
||||
let bitcoincash
|
||||
if (network === "bitcoincash" || network === "mainnet")
|
||||
bitcoincash = coininfo.bitcoincash.main
|
||||
else bitcoincash = coininfo.bitcoincash.test
|
||||
if (network === 'bitcoincash' || network === 'mainnet') { bitcoincash = coininfo.bitcoincash.main } else bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
this.transaction = new Bitcoin.TransactionBuilder(bitcoincashBitcoinJSLib)
|
||||
@@ -33,7 +31,7 @@ class TransactionBuilder {
|
||||
this.bip66 = bip66
|
||||
this.bip68 = bip68
|
||||
this.p2shInput = false
|
||||
this.tx
|
||||
// this.tx
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,23 +46,24 @@ class TransactionBuilder {
|
||||
* // add input with txid and index of vout
|
||||
* transactionBuilder.addInput(txid, 0);
|
||||
*/
|
||||
addInput(txHash, vout, sequence = this.DEFAULT_SEQUENCE, prevOutScript) {
|
||||
addInput (txHash, vout, sequence = this.DEFAULT_SEQUENCE, prevOutScript) {
|
||||
this.transaction.addInput(txHash, vout, sequence, prevOutScript)
|
||||
}
|
||||
|
||||
addInputScript(vout, script) {
|
||||
addInputScript (vout, script) {
|
||||
this.tx = this.transaction.buildIncomplete()
|
||||
this.tx.setInputScript(vout, script)
|
||||
this.p2shInput = true
|
||||
}
|
||||
|
||||
addInputScripts(scripts) {
|
||||
addInputScripts (scripts) {
|
||||
this.tx = this.transaction.buildIncomplete()
|
||||
scripts.forEach(script => {
|
||||
this.tx.setInputScript(script.vout, script.script)
|
||||
})
|
||||
this.p2shInput = true
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction-Builder.addOutput() addOutput()
|
||||
* @apiName AddOutput
|
||||
@@ -79,7 +78,7 @@ class TransactionBuilder {
|
||||
* // add output w/ address and amount to send
|
||||
* transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount);
|
||||
*/
|
||||
addOutput(scriptPubKey, amount) {
|
||||
addOutput (scriptPubKey, amount) {
|
||||
try {
|
||||
this.transaction.addOutput(
|
||||
TransactionBuilder._address.toLegacyAddress(scriptPubKey),
|
||||
@@ -89,6 +88,7 @@ class TransactionBuilder {
|
||||
this.transaction.addOutput(scriptPubKey, amount)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction-Builder.setLockTime() setLockTime()
|
||||
* @apiName SetLockTime
|
||||
@@ -104,9 +104,10 @@ class TransactionBuilder {
|
||||
* transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount);
|
||||
* transactionBuilder.setLockTime(50000)
|
||||
*/
|
||||
setLockTime(locktime) {
|
||||
setLockTime (locktime) {
|
||||
this.transaction.setLockTime(locktime)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction-Builder.sign() sign()
|
||||
* @apiName Sign.
|
||||
@@ -124,7 +125,7 @@ class TransactionBuilder {
|
||||
* // sign w/ keyPair
|
||||
* transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount, transactionBuilder.signatureAlgorithms.SCHNORR);
|
||||
*/
|
||||
sign(
|
||||
sign (
|
||||
vin,
|
||||
keyPair,
|
||||
redeemScript,
|
||||
@@ -144,6 +145,7 @@ class TransactionBuilder {
|
||||
signatureAlgorithm
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction-Builder.build() build()
|
||||
* @apiName Build.
|
||||
@@ -154,11 +156,11 @@ class TransactionBuilder {
|
||||
* // build tx
|
||||
* let tx = bchjs.transactionBuilder.build();
|
||||
*/
|
||||
build() {
|
||||
build () {
|
||||
if (this.p2shInput === true) return this.tx
|
||||
|
||||
return this.transaction.build()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TransactionBuilder
|
||||
export default TransactionBuilder
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
High-level functions for working with Transactions
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
// const BigNumber = require('bignumber.js')
|
||||
|
||||
// Local libraries
|
||||
import RawTransaction from './raw-transactions.js'
|
||||
import SlpUtils from './slp/utils.js'
|
||||
import Blockchain from './blockchain.js'
|
||||
import PsfSlpIndexer from './psf-slp-indexer.js'
|
||||
|
||||
class Transaction {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.blockchain = new Blockchain(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction.get() get()
|
||||
* @apiName get
|
||||
* @apiGroup Transaction
|
||||
* @apiDescription
|
||||
* Returns an object of transaction data, including addresses for input UTXOs.
|
||||
* If it is a SLP token transaction, the token information for inputs and
|
||||
* outputs will also be included.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
async get (txid) {
|
||||
// console.log('transaction.get() txid: ', txid)
|
||||
return await this.psfSlpIndexer.tx(txid)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction.getTokenInfo() getTokenInfo()
|
||||
* @apiName getTokenInfo
|
||||
* @apiGroup Transaction
|
||||
* @apiDescription
|
||||
* Given the TXID of a token transaction, it will return data about that
|
||||
* token by retrieving the data from the Genesis transaction and docoding
|
||||
* the OP_RETURN.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.Transaction.getTokenInfo("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
|
||||
// Returns the token data if the txid is an SLP tx.
|
||||
async getTokenInfo (txid) {
|
||||
try {
|
||||
const tokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
return tokenData
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Transaction
|
||||
+177
-62
@@ -1,100 +1,215 @@
|
||||
const axios = require("axios")
|
||||
// import axios from 'axios'
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Util {
|
||||
constructor(config) {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.validateAddress() validateAddress()
|
||||
* @apiName Validate Address.
|
||||
* @api Util.floor8() floor8()
|
||||
* @apiName floor8
|
||||
* @apiGroup Util
|
||||
* @apiDescription Return information about the given bitcoin address.
|
||||
* @apiDescription Round a number down to 8 decimal places.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let validateAddress = await bchjs.Util.validateAddress("bitcoincash:qzc86hrdufhcwlyzk7k82x77kfs2myekn57nv9cw5f");
|
||||
* console.log(validateAddress);
|
||||
* const num = 1.234567891111
|
||||
* const result = bchjs.Util.floor8(num)
|
||||
* console.log(result)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // { isvalid: true,
|
||||
* // address: '17fshh33qUze2yifiJ2sXgijSMzJ2KNEwu',
|
||||
* // scriptPubKey: '76a914492ae280d70af33acf0ae7cd329b961e65e9cbd888ac',
|
||||
* // ismine: true,
|
||||
* // iswatchonly: false,
|
||||
* // isscript: false,
|
||||
* // pubkey: '0312eeb9ae5f14c3cf43cece11134af860c2ef7d775060e3a578ceec888acada31',
|
||||
* // iscompressed: true,
|
||||
* // account: 'Test' }
|
||||
* // returns
|
||||
* 1.23456789
|
||||
*/
|
||||
// floor8 - round to 8 decimal places
|
||||
// Takes a number and returns it, rounded to the nearest 8 decimal place.
|
||||
floor8 (num) {
|
||||
const thisNum = Number(num)
|
||||
|
||||
if (isNaN(thisNum)) throw new Error('input must be a number')
|
||||
|
||||
let tempNum = thisNum * 100000000
|
||||
tempNum = Math.floor(tempNum)
|
||||
tempNum = tempNum / 100000000
|
||||
|
||||
return tempNum
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.floor2() floor2()
|
||||
* @apiName floor2
|
||||
* @apiGroup Util
|
||||
* @apiDescription Round a number down to 2 decimal places.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let validateAddress = await bchjs.Util.validateAddress(["bitcoincash:qzc86hrdufhcwlyzk7k82x77kfs2myekn57nv9cw5f"]);
|
||||
* console.log(validateAddress);
|
||||
* const num = 1.234567891111
|
||||
* const result = bchjs.Util.floor2(num)
|
||||
* console.log(result)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [{ isvalid: true,
|
||||
* // address: '17fshh33qUze2yifiJ2sXgijSMzJ2KNEwu',
|
||||
* // scriptPubKey: '76a914492ae280d70af33acf0ae7cd329b961e65e9cbd888ac',
|
||||
* // ismine: true,
|
||||
* // iswatchonly: false,
|
||||
* // isscript: false,
|
||||
* // pubkey: '0312eeb9ae5f14c3cf43cece11134af860c2ef7d775060e3a578ceec888acada31',
|
||||
* // iscompressed: true,
|
||||
* // account: 'Test' }]
|
||||
* // returns
|
||||
* 1.23
|
||||
*/
|
||||
async validateAddress(address) {
|
||||
// floor2 - round down to 2 decimal places
|
||||
// Takes a number and returns it, rounded to the nearest 2 decimal place.
|
||||
floor2 (num) {
|
||||
const thisNum = Number(num)
|
||||
|
||||
if (isNaN(thisNum)) throw new Error('input must be a number')
|
||||
|
||||
let tempNum = thisNum * 100
|
||||
tempNum = Math.floor(tempNum)
|
||||
tempNum = tempNum / 100
|
||||
|
||||
return tempNum
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.chunk20() chunk20()
|
||||
* @apiName chunk20
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 20 elements each.
|
||||
* Input: arrayToSlice - a one-dimensional array of elements.
|
||||
* Returns a two-dimensional array. An array of 20-element arrays.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]
|
||||
*
|
||||
* const chunked = bchjs.Util.chunk20(bigArray)
|
||||
* console.log(chunked)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],
|
||||
* [20,21,22,23,24,25,26]
|
||||
* ]
|
||||
*/
|
||||
// chunk20 - chunk up an array into multiple arrays of 20 elements each.
|
||||
// Input: arrayToSlice - a one-dimensional array of elements.
|
||||
// Returns a two-dimensional array. An array of 20-element arrays.
|
||||
chunk20 (arrayToSlice) {
|
||||
try {
|
||||
// Single block
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}util/validateAddress/${address}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Array of blocks.
|
||||
} else if (Array.isArray(address)) {
|
||||
const options = {
|
||||
method: "POST",
|
||||
url: `${this.restURL}util/validateAddress`,
|
||||
data: {
|
||||
addresses: address
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
return response.data
|
||||
// Validate inputs
|
||||
if (!Array.isArray(arrayToSlice)) {
|
||||
throw new Error('input must be an array')
|
||||
}
|
||||
|
||||
throw new Error(`Input must be a string or array of strings.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
let offset = 0
|
||||
const result = []
|
||||
|
||||
// Loop over the array and slice off chunks of 20 elements.
|
||||
while (offset < arrayToSlice.length) {
|
||||
const chunk = arrayToSlice.slice(offset, offset + 20)
|
||||
result.push(chunk)
|
||||
offset = offset + 20
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in chunk20()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.chunk100() chunk100()
|
||||
* @apiName chunk100
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
|
||||
* Input: arrayToSlice - a one-dimensional array of elements.
|
||||
* Returns a two-dimensional array. An array of 100-element arrays.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
|
||||
*
|
||||
* const chunked = bchjs.Util.chunk20(bigArray)
|
||||
* console.log(chunked)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
|
||||
* [100,101,102,...,148,149,150]
|
||||
* ]
|
||||
*/
|
||||
chunk100 (arrayToSlice) {
|
||||
try {
|
||||
// Validate inputs
|
||||
if (!Array.isArray(arrayToSlice)) {
|
||||
throw new Error('input must be an array')
|
||||
}
|
||||
|
||||
let offset = 0
|
||||
const result = []
|
||||
|
||||
// Loop over the array and slice off chunks of 100 elements.
|
||||
while (offset < arrayToSlice.length) {
|
||||
const chunk = arrayToSlice.slice(offset, offset + 100)
|
||||
result.push(chunk)
|
||||
offset = offset + 100
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in chunk100()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.sleep() sleep()
|
||||
* @apiName sleep
|
||||
* @apiGroup Util
|
||||
* @apiDescription Promise-based delay.
|
||||
* Expects an integer as input, which represents milliseconds. This function
|
||||
* will return a Promise that resolves that many milliseconds later.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const tenSeconds = 10000
|
||||
* await bchjs.Util.sleep(tenSeconds)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
*/
|
||||
sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Util
|
||||
export default Util
|
||||
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
High-level functions for working with UTXOs
|
||||
|
||||
TODO:
|
||||
- Make a getWL() clone of get(), but uses hydrateUtxosWL()
|
||||
*/
|
||||
|
||||
// Local libraries
|
||||
import Electrumx from './electrumx.js'
|
||||
import Slp from './slp/slp.js'
|
||||
import PsfSlpIndexer from './psf-slp-indexer.js'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import Blockchain from './blockchain.js'
|
||||
|
||||
class UTXO {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies for easier mocking.
|
||||
this.electrumx = new Electrumx(config)
|
||||
this.slp = new Slp(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
this.BigNumber = BigNumber
|
||||
this.blockchain = new Blockchain(config)
|
||||
|
||||
// Bind 'this' object to all subfunctions.
|
||||
this.get = this.get.bind(this)
|
||||
this.hydrateTokenData = this.hydrateTokenData.bind(this)
|
||||
this.findBiggestUtxo = this.findBiggestUtxo.bind(this)
|
||||
this.isValid = this.isValid.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.get() get()
|
||||
* @apiName get
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Get UTXOs for an address (from psf-slp-indexer)
|
||||
*
|
||||
* Given an address, this function will return an object with thre following
|
||||
* properties:
|
||||
* - address: "" - the address these UTXOs are associated with
|
||||
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
|
||||
* - infoUtxos: [] - UTXOs under of 1000 sats or less that can not be categorized
|
||||
* as another type of UTXO (like a token).
|
||||
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
|
||||
* not spent, to be safe.
|
||||
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
|
||||
* - type1: {}
|
||||
* - tokens: [] - SLP token Type 1 tokens.
|
||||
* - mintBatons: [] - SLP token Type 1 mint batons.
|
||||
* - nft: {}
|
||||
* - tokens: [] - NFT tokens
|
||||
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
||||
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
||||
* console.log(utxos);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* {
|
||||
* "address": "bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv",
|
||||
* "bchUtxos": [
|
||||
* {
|
||||
* "height": 674513,
|
||||
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "tx_pos": 3,
|
||||
* "value": 38134,
|
||||
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "vout": 3,
|
||||
* "isValid": false
|
||||
* }
|
||||
* ],
|
||||
*/
|
||||
// This version of get() uses the psf-slp-indexer. It will replace the older
|
||||
// get() function that uses SLPDB.
|
||||
// TODO: NFT UTXOs are identified as non-token UTXOs, which will cause a wallet
|
||||
// to burn them. The psf-slp-indexer needs to be updated to mark these UTXOs.
|
||||
async get (address) {
|
||||
try {
|
||||
// Convert address to an array if it is a string.
|
||||
if (typeof address !== 'string') {
|
||||
throw new Error('address input must be a string')
|
||||
}
|
||||
|
||||
// Ensure the address is a BCH address.
|
||||
let addr = address
|
||||
if (!addr.includes('ecash')) {
|
||||
addr = this.slp.Address.toCashAddress(address)
|
||||
}
|
||||
|
||||
// Get the UTXOs associated with the address.
|
||||
const utxoData = await this.electrumx.utxo(addr)
|
||||
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
||||
const utxos = utxoData.utxos
|
||||
|
||||
let slpUtxos = []
|
||||
|
||||
// Get SLP UTXOs from the psf-slp-indexer
|
||||
try {
|
||||
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
|
||||
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
|
||||
|
||||
slpUtxos = slpUtxoData.balance.utxos
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
|
||||
// Exit quietly if address has no SLP UTXOs. Otherwise, throw the error.
|
||||
if (err.error && !err.error.includes('Key not found in database')) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the sync status of the SLP indexer
|
||||
const syncStatus = await this.psfSlpIndexer.status()
|
||||
const slpIndexerHeight = syncStatus.status.syncedBlockHeight
|
||||
const chainBlockHeight = syncStatus.status.chainBlockHeight
|
||||
|
||||
// Loop through the Fulcrum UTXOs.
|
||||
for (let i = 0; i < utxos.length; i++) {
|
||||
const thisUtxo = utxos[i]
|
||||
|
||||
// Loop through the UTXOs from psf-slp-indexer.
|
||||
for (let j = 0; j < slpUtxos.length; j++) {
|
||||
const thisSlpUtxo = slpUtxos[j]
|
||||
|
||||
// If the non-hydrated UTXO matches the SLP UTXO, then combine the data
|
||||
// and mark the UTXO as an SLP token.
|
||||
if (
|
||||
thisUtxo.tx_hash === thisSlpUtxo.txid &&
|
||||
thisUtxo.tx_pos === thisSlpUtxo.vout
|
||||
) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.isSlp = true
|
||||
thisUtxo.type = thisSlpUtxo.type
|
||||
thisUtxo.qty = thisSlpUtxo.qty
|
||||
thisUtxo.tokenId = thisSlpUtxo.tokenId
|
||||
thisUtxo.address = thisSlpUtxo.address
|
||||
thisUtxo.tokenType = thisSlpUtxo.tokenType
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no match, then this is a normal BCH UTXO. Mark it as such.
|
||||
if (!thisUtxo.isSlp) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.address = addr
|
||||
|
||||
// Check the transaction to see if its a 'null' token, ignored by
|
||||
// the indexer.
|
||||
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
|
||||
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
|
||||
|
||||
// txData.txData === undefined if the SLP indexer is syncing.
|
||||
if (!txData.txData || txData.txData.isValidSlp === null) {
|
||||
thisUtxo.isSlp = null
|
||||
} else {
|
||||
thisUtxo.isSlp = false
|
||||
}
|
||||
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
|
||||
|
||||
// If the SLP indexer more than 1 block behind the full node, then
|
||||
// move any BCH UTXOs of 600 sats or less into the null array. This
|
||||
// protects token UTXOs from being burned accidentally.
|
||||
if (slpIndexerHeight < chainBlockHeight - 1) {
|
||||
if (thisUtxo.value < 601) {
|
||||
thisUtxo.isSlp = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get token UTXOs
|
||||
let type1TokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 1
|
||||
)
|
||||
|
||||
// Hydrate the UTXOs with additional token data.
|
||||
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
|
||||
|
||||
// Collect BCH UTXOs above 1000 sats
|
||||
const bchUtxos = utxos.filter(x => x.isSlp === false && x.value > 1000)
|
||||
|
||||
// Collect all BCH UTXOs at 1000 sats or smaller
|
||||
// The prevents SLP tokens, BCMR, and other 'colored' UTXOs from being
|
||||
// accidentally included into normal BCH transactions.
|
||||
const infoUtxos = utxos.filter(x => x.isSlp === false && x.value <= 1000)
|
||||
|
||||
// Collect and hydrate any type1 baton UTXOs
|
||||
let type1BatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 1
|
||||
)
|
||||
type1BatonUtxos = await this.hydrateTokenData(type1BatonUtxos)
|
||||
|
||||
// Collect and hydrate NFT Group tokens
|
||||
let nftGroupTokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 129
|
||||
)
|
||||
nftGroupTokenUtxos = await this.hydrateTokenData(nftGroupTokenUtxos)
|
||||
|
||||
// Collect and hydrate any Group baton UTXOs
|
||||
let groupBatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 129
|
||||
)
|
||||
groupBatonUtxos = await this.hydrateTokenData(groupBatonUtxos)
|
||||
|
||||
// Collect and hydrate NFT child tokens
|
||||
let nftChildTokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 65
|
||||
)
|
||||
nftChildTokenUtxos = await this.hydrateTokenData(nftChildTokenUtxos)
|
||||
|
||||
// Isolate any UTXOs that are marked null by the SLP indexer.
|
||||
const nullUtxos = utxos.filter(x => x.isSlp === null)
|
||||
|
||||
const outObj = {
|
||||
address: addr,
|
||||
bchUtxos,
|
||||
infoUtxos,
|
||||
slpUtxos: {
|
||||
type1: {
|
||||
tokens: type1TokenUtxos,
|
||||
mintBatons: type1BatonUtxos
|
||||
},
|
||||
group: {
|
||||
tokens: nftGroupTokenUtxos,
|
||||
mintBatons: groupBatonUtxos
|
||||
},
|
||||
nft: {
|
||||
tokens: nftChildTokenUtxos
|
||||
}
|
||||
},
|
||||
nullUtxos
|
||||
}
|
||||
|
||||
return outObj
|
||||
} catch (err) {
|
||||
console.error('Error in bchjs.Utxo.get(): ', err)
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Hydrate an array of token UTXOs with token information.
|
||||
// Returns an array of token UTXOs with additional data.
|
||||
async hydrateTokenData (utxoAry) {
|
||||
try {
|
||||
// console.log('utxoAry: ', utxoAry)
|
||||
|
||||
// Create a list of token IDs without duplicates.
|
||||
let tokenIds = utxoAry.map(x => x.tokenId)
|
||||
|
||||
// Remove duplicates. https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array
|
||||
tokenIds = [...new Set(tokenIds)]
|
||||
// console.log('tokenIds: ', tokenIds)
|
||||
|
||||
// Get Genesis data for each tokenId
|
||||
const genesisData = []
|
||||
for (let i = 0; i < tokenIds.length; i++) {
|
||||
const thisTokenId = tokenIds[i]
|
||||
const thisTokenData = await this.psfSlpIndexer.tokenStats(thisTokenId)
|
||||
// console.log('thisTokenData: ', thisTokenData)
|
||||
|
||||
genesisData.push(thisTokenData)
|
||||
}
|
||||
// console.log('genesisData: ', genesisData)
|
||||
|
||||
// Hydrate each token UTXO with data from the genesis transaction.
|
||||
for (let i = 0; i < utxoAry.length; i++) {
|
||||
const thisUtxo = utxoAry[i]
|
||||
|
||||
// Get the genesis data for this token.
|
||||
const genData = genesisData.filter(
|
||||
x => x.tokenData.tokenId === thisUtxo.tokenId
|
||||
)
|
||||
// console.log('genData: ', genData)
|
||||
|
||||
thisUtxo.ticker = genData[0].tokenData.ticker
|
||||
thisUtxo.name = genData[0].tokenData.name
|
||||
thisUtxo.documentUri = genData[0].tokenData.documentUri
|
||||
thisUtxo.documentHash = genData[0].tokenData.documentHash
|
||||
thisUtxo.decimals = genData[0].tokenData.decimals
|
||||
|
||||
if (thisUtxo.type !== 'baton') {
|
||||
// Calculate the real token quantity
|
||||
const qty = new BigNumber(thisUtxo.qty).dividedBy(
|
||||
10 ** parseInt(thisUtxo.decimals)
|
||||
)
|
||||
thisUtxo.qtyStr = qty.toString()
|
||||
|
||||
// tokenQty is property expected by SLP.tokentype1.js library
|
||||
thisUtxo.tokenQty = thisUtxo.qtyStr
|
||||
}
|
||||
}
|
||||
|
||||
return utxoAry
|
||||
} catch (err) {
|
||||
console.log('Error in hydrateTokenData()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
|
||||
* @apiName findBiggestUtxo
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Get the biggest UTXO in an array.
|
||||
*
|
||||
* Given an array of BCH UTXOs, this method will return the biggest UTXO.
|
||||
* This is often the simplest way to pick a UTXO for generating a transaction.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
|
||||
* const utxo = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* {
|
||||
* "height": 655431,
|
||||
* "tx_hash": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
|
||||
* "tx_pos": 0,
|
||||
* "value": 800,
|
||||
* "txid": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
|
||||
* "vout": 0,
|
||||
* "isValid": false,
|
||||
* "satoshis": 800
|
||||
* }
|
||||
*/
|
||||
// Returns the utxo with the biggest balance from an array of utxos.
|
||||
findBiggestUtxo (utxos) {
|
||||
let largestAmount = 0
|
||||
let largestIndex = 0
|
||||
|
||||
if (!Array.isArray(utxos)) {
|
||||
throw new Error('utxos input to findBiggestUtxo() must be an array')
|
||||
}
|
||||
|
||||
for (let i = 0; i < utxos.length; i++) {
|
||||
const thisUtxo = utxos[i]
|
||||
|
||||
// Give Elecrumx utxos a satoshis property.
|
||||
if (thisUtxo.value) {
|
||||
if (!thisUtxo.satoshis) thisUtxo.satoshis = Number(thisUtxo.value)
|
||||
}
|
||||
|
||||
if (!thisUtxo.satoshis) {
|
||||
throw new Error(
|
||||
'UTXOs require a satoshis or value property for findBiggestUtxo()'
|
||||
)
|
||||
}
|
||||
|
||||
if (thisUtxo.satoshis > largestAmount) {
|
||||
largestAmount = thisUtxo.satoshis
|
||||
largestIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
return utxos[largestIndex]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.isValid() isValid()
|
||||
* @apiName isValid
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Validate that UTXO exists and is still spendable.
|
||||
*
|
||||
* Given a UTXO, this method will return true if the UTXO is still in the
|
||||
* mempool and still valid for spending. It will return false if the UTXO
|
||||
* has been spent.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
|
||||
* const isValid = bchjs.Utxo.isValid(utxos.bchUtxos[0])
|
||||
* console.log(isValid);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* true
|
||||
*/
|
||||
async isValid (utxo) {
|
||||
try {
|
||||
// console.log('utxo: ', utxo)
|
||||
|
||||
// Convert different properties from different indexers
|
||||
const txid = utxo.txid || utxo.tx_hash
|
||||
const vout = utxo.vout | utxo.tx_pos
|
||||
|
||||
// Query the full node
|
||||
const txOut = await this.blockchain.getTxOut(txid, vout, true)
|
||||
// console.log('txOut: ', txOut)
|
||||
|
||||
// Simplify results to either true or false.
|
||||
let isValid = null
|
||||
if (txOut === null) {
|
||||
isValid = false
|
||||
} else {
|
||||
isValid = true
|
||||
}
|
||||
|
||||
return isValid
|
||||
} catch (err) {
|
||||
console.log('Error in Utxo.isValid()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UTXO
|
||||
@@ -1,77 +0,0 @@
|
||||
// import BCHWalletBridge from "bch-wallet-bridge.js"
|
||||
class Wallet {
|
||||
constructor(walletProvider) {
|
||||
// this.bchWalletBridge = new BCHWalletBridge(walletProvider)
|
||||
}
|
||||
|
||||
setWalletProvider(walletProvider) {
|
||||
this.bchWalletBridge.walletProvider = walletProvider
|
||||
}
|
||||
|
||||
getAddress(changeType, index, dAppId) {
|
||||
return this.bchWalletBridge.getAddress(changeType, index, dAppId)
|
||||
}
|
||||
|
||||
getAddressIndex(changeType, dAppId) {
|
||||
return this.bchWalletBridge.getAddressIndex(changeType, dAppId)
|
||||
}
|
||||
|
||||
getAddresses(changeType, startIndex, size, dAppId) {
|
||||
return this.bchWalletBridge.getAddresses(
|
||||
changeType,
|
||||
startIndex,
|
||||
size,
|
||||
dAppId
|
||||
)
|
||||
}
|
||||
|
||||
getRedeemScript(p2shAddress, dAppId) {
|
||||
return this.bchWalletBridge.getRedeemScript(p2shAddress, dAppId)
|
||||
}
|
||||
|
||||
getRedeemScripts(dAppId) {
|
||||
return this.bchWalletBridge.getRedeemScripts(dAppId)
|
||||
}
|
||||
|
||||
addRedeemScript(redeemScript, dAppId) {
|
||||
return this.bchWalletBridge.addRedeemScript(redeemScript, dAppId)
|
||||
}
|
||||
|
||||
getUtxos(dAppId) {
|
||||
return this.bchWalletBridge.getUtxos(dAppId)
|
||||
}
|
||||
|
||||
getBalance(dAppId) {
|
||||
return this.bchWalletBridge.getBalance(dAppId)
|
||||
}
|
||||
|
||||
sign(address, dataToSign) {
|
||||
return this.bchWalletBridge.sign(address, dataToSign)
|
||||
}
|
||||
|
||||
buildTransaction(outputs, dAppId) {
|
||||
return this.bchWalletBridge.buildTransaction(outputs, dAppId)
|
||||
}
|
||||
|
||||
getProtocolVersion() {
|
||||
return this.bchWalletBridge.getProtocolVersion()
|
||||
}
|
||||
|
||||
getNetwork() {
|
||||
return this.bchWalletBridge.getNetwork()
|
||||
}
|
||||
|
||||
getFeePerByte() {
|
||||
return this.bchWalletBridge.getFeePerByte()
|
||||
}
|
||||
|
||||
getDefaultDAppId() {
|
||||
return this.bchWalletBridge.getDefaultDAppId()
|
||||
}
|
||||
|
||||
setDefaultDAppId(dAppId) {
|
||||
return this.bchWalletBridge.setDefaultDAppId(dAppId)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Wallet
|
||||
@@ -2,17 +2,18 @@
|
||||
A Mocha test file for running end-to-end (e2e) tests.
|
||||
*/
|
||||
|
||||
//const mocha = require("mocha")
|
||||
const assert = require("chai").assert
|
||||
// import mocha from "mocha"
|
||||
import chai from 'chai'
|
||||
|
||||
const sendToken = require("./send-token/send-token")
|
||||
import sendToken from './send-token/send-token.js'
|
||||
const { assert } = chai
|
||||
|
||||
describe("#end-to-end tests", () => {
|
||||
describe("#send-tokens", () => {
|
||||
it("SLPDB should update balances in less than 10 seconds", async () => {
|
||||
describe('#end-to-end tests', () => {
|
||||
describe('#send-tokens', () => {
|
||||
it('SLPDB should update balances in less than 10 seconds', async () => {
|
||||
const result = await sendToken.sendTokenTest()
|
||||
|
||||
assert(result, true, "True expected if test passed successfully.")
|
||||
assert(result, true, 'True expected if test passed successfully.')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,15 +3,21 @@
|
||||
IPFS is working.
|
||||
*/
|
||||
|
||||
process.env.IPFS_API = `http://localhost:5001`
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname } from 'path'
|
||||
// process.env.IPFS_API = `https://ipfs-api.fullstack.cash`
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
|
||||
process.env.IPFS_API = 'http://localhost:5001'
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#IPFS`, () => {
|
||||
it("should upload a file to the server", async () => {
|
||||
const path = `${__dirname}/ipfs-e2e.js`
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
describe('#IPFS', () => {
|
||||
it('should upload a file to the server', async () => {
|
||||
const path = `${__dirname.toString()}/ipfs-e2e.js`
|
||||
|
||||
const fileModel = await bchjs.IPFS.createFileModel(path)
|
||||
console.log(`fileModel: ${JSON.stringify(fileModel, null, 2)}`)
|
||||
|
||||
@@ -4,38 +4,46 @@
|
||||
|
||||
To Run Test:
|
||||
- Update the RESTURL for bch-api you want to test against.
|
||||
- Ensure the BCHJSTOKEN environment variable is set to an empty string.
|
||||
- If working with bch-api locally, eliminate the local IP address from the
|
||||
whitelist in bch-api/src/middleware/route-ratelimit.js
|
||||
|
||||
Run this test with this command:
|
||||
mocha --timeout=30000 anonymous-rate-limits.js
|
||||
*/
|
||||
|
||||
const assert = require("chai").assert
|
||||
import chai from 'chai'
|
||||
// const RESTURL = `http://localhost:3000/v5/`
|
||||
|
||||
const RESTURL = `https://api.fullstack.cash/v3/`
|
||||
// const RESTURL = `http://localhost:3000/v3/`
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const RESTURL = `https://abc.fullstack.cash/v5/`
|
||||
const RESTURL = 'https://bchn.fullstack.cash/v5/'
|
||||
const bchjs = new BCHJS({ restURL: RESTURL })
|
||||
|
||||
describe("#anonymous rate limits", () => {
|
||||
it("should allow an anonymous call to a full node endpoint", async () => {
|
||||
describe('#anonymous rate limits', () => {
|
||||
it('should allow an anonymous call to a full node endpoint', async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
|
||||
assert.property(result, "version")
|
||||
assert.property(result, 'version')
|
||||
}).timeout(5000)
|
||||
|
||||
it("should allow an anonymous call to an indexer", async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
it('should allow an anonymous call to an indexer', async () => {
|
||||
const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result, 'balance')
|
||||
}).timeout(5000)
|
||||
|
||||
it("should throw error when rate limit exceeded", async () => {
|
||||
it('should throw error when rate limit exceeded', async () => {
|
||||
try {
|
||||
for (let i = 0; i < 5; i++) await bchjs.Control.getNetworkInfo()
|
||||
for (let i = 0; i < 35; i++) await bchjs.Control.getNetworkInfo()
|
||||
|
||||
assert.equal(true, false, "unexpected result")
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert.include(err.error, "Too many requests")
|
||||
assert.include(err.error, 'Too many requests')
|
||||
}
|
||||
}).timeout(5000)
|
||||
}).timeout(20000)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Mocha test that verifies that the bch-api server is enforcing its
|
||||
access rules for Basic Authentication.
|
||||
|
||||
To Run Test:
|
||||
- Update the restURL for bch-api you want to test against.
|
||||
- Update the PRO_PASS value with a current PRO_PASSES string used by bch-api.
|
||||
*/
|
||||
|
||||
import chai from 'chai'
|
||||
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const PRO_PASS = 'testpassword'
|
||||
const bchjs = new BCHJS({
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https://abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
authPass: PRO_PASS
|
||||
})
|
||||
|
||||
describe('#Basic Authentication rate limits', () => {
|
||||
it('should allow more than 20 RPM to full node', async () => {
|
||||
for (let i = 0; i < 22; i++) {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, 'version', 'more than 3 calls allowed')
|
||||
}
|
||||
|
||||
if (i === 15) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, 'version', 'more than 10 calls allowed')
|
||||
}
|
||||
}
|
||||
}).timeout(45000)
|
||||
|
||||
it('should allow more than 20 RPM to an indexer', async () => {
|
||||
const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
|
||||
|
||||
for (let i = 0; i < 22; i++) {
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, 'balance', 'more than 3 calls allowed')
|
||||
}
|
||||
|
||||
if (i === 15) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, 'balance', 'more than 10 calls allowed')
|
||||
}
|
||||
}
|
||||
}).timeout(45000)
|
||||
})
|
||||
@@ -1,4 +1,8 @@
|
||||
/*
|
||||
CT 11/11/2020:
|
||||
There is no longer a free tier. These tests have been deprecated.
|
||||
|
||||
--------
|
||||
Mocha test that verifies that the bch-api server is enforcing its
|
||||
FREE access rules
|
||||
|
||||
@@ -7,72 +11,60 @@
|
||||
- Update the JWT_TOKEN value with a current free-level JWT token.
|
||||
*/
|
||||
|
||||
const assert = require("chai").assert
|
||||
import chai from 'chai'
|
||||
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const JWT_TOKEN =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE1OTA1Mjk2NzgsImV4cCI6MTU5MzEyMTY3OH0.4q5MFDCcVx0FusivJPiEIj3w8nhFG7ZV5IhJS7faUlc"
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE2MDQ4NTQ2OTEsImV4cCI6MTYwNzQ0NjY5MX0.iwse0z0KDKHx9graCxcOwj6lSlfKQAb1zLhmjvygvts'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: `https://api.fullstack.cash/v3/`,
|
||||
// restURL: `http://localhost:3000/v3/`,
|
||||
restURL: 'https://api.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
describe("#free rate limits", () => {
|
||||
it("should allow an free call to an indexer", async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
describe('#free rate limits', () => {
|
||||
it('should allow an free call to an indexer', async () => {
|
||||
const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result, 'balance')
|
||||
}).timeout(5000)
|
||||
|
||||
// it("should throw error when rate limit exceeded 3 RPM for indexer endpoints", async () => {
|
||||
// const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
//
|
||||
// try {
|
||||
// for (let i = 0; i < 5; i++) await bchjs.Blockbook.balance(addr)
|
||||
//
|
||||
// assert.equal(true, false, "unexpected result")
|
||||
// } catch (err) {
|
||||
// // console.log(`err: `, err)
|
||||
// assert.include(err.error, "Too many requests")
|
||||
// }
|
||||
// }).timeout(10000)
|
||||
|
||||
it("should allow up to 10 RPM to indexer, then throw error", async () => {
|
||||
it('should allow up to 10 RPM to indexer, then throw error', async () => {
|
||||
try {
|
||||
const addr = "bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"
|
||||
const addr = 'bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0'
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const result = await await bchjs.Blockbook.balance(addr)
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "address", "more than 3 calls allowed")
|
||||
assert.property(result, 'address', 'more than 3 calls allowed')
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.error,
|
||||
"currently 10 requests",
|
||||
"more than 10 not allowed"
|
||||
'currently 10 requests',
|
||||
'more than 10 not allowed'
|
||||
)
|
||||
assert.include(err.error, 10)
|
||||
assert.notInclude(err.error, 3)
|
||||
}
|
||||
}).timeout(20000)
|
||||
|
||||
it("should allow up to 10 RPM to full node, then throw error", async () => {
|
||||
it('should allow up to 10 RPM to full node, then throw error', async () => {
|
||||
try {
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "version", "more than 3 calls allowed")
|
||||
assert.property(result, 'version', 'more than 3 calls allowed')
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -80,11 +72,22 @@ describe("#free rate limits", () => {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.error,
|
||||
"currently 10 requests",
|
||||
"more than 10 not allowed"
|
||||
'currently 10 requests',
|
||||
'more than 10 not allowed'
|
||||
)
|
||||
assert.include(err.error, 10)
|
||||
assert.notInclude(err.error, 3)
|
||||
}
|
||||
}).timeout(20000)
|
||||
|
||||
it('should throw error when rate limit exceeded 20 RPM for indexer endpoints', async () => {
|
||||
try {
|
||||
for (let i = 0; i < 22; i++) await bchjs.Control.getNetworkInfo()
|
||||
|
||||
assert.fail('unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, 'Too many requests')
|
||||
}
|
||||
}).timeout(20000)
|
||||
})
|
||||
|
||||
@@ -1,70 +1,78 @@
|
||||
/*
|
||||
CT 11/11/2020:
|
||||
There is no longer a free tier. These tests have been deprecated.
|
||||
|
||||
----
|
||||
Mocha test that verifies that the bch-api server is enforcing its
|
||||
full-node access rules
|
||||
|
||||
To Run Test:
|
||||
- Update the restURL for bch-api you want to test against.
|
||||
- Update the JWT_TOKEN value with a current full-node-level JWT token.
|
||||
- Update the JWT_TOKEN value with a paid tier JWT token.
|
||||
*/
|
||||
|
||||
const assert = require("chai").assert
|
||||
import chai from 'chai'
|
||||
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const JWT_TOKEN =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYmY4MjA1YTYwODliMjliYTlhZjc1OSIsImlhdCI6MTU3MjgzMTgzOSwiZXhwIjoxNTc1NDIzODM5fQ.sFFS4AF04U2aUwmKkBIfnMQIUoygzV9UMVzzQuwctpY"
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: `https://api.bchjs.cash/v3/`,
|
||||
// restURL: `http://localhost:3000/v3/`,
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https:/abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
describe("#full node rate limits", () => {
|
||||
it("should allow an free call to an indexer", async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
describe('#full node rate limits', () => {
|
||||
it('should allow an free call to an indexer', async () => {
|
||||
const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result, 'balance')
|
||||
}).timeout(5000)
|
||||
|
||||
it("should throw error when rate limit exceeded 3 RPM for indexer endpoints", async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
// CT 11/8/20: New rate limits make this test invalid.
|
||||
// it("should throw error when rate limit exceeded 20 RPM for indexer endpoints", async () => {
|
||||
// const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
//
|
||||
// try {
|
||||
// for (let i = 0; i < 22; i++) await bchjs.Electrumx.balance(addr)
|
||||
//
|
||||
// assert.fail("unexpected result")
|
||||
// } catch (err) {
|
||||
// // console.log(`err: `, err)
|
||||
// assert.include(err.error, "Too many requests")
|
||||
// }
|
||||
// }).timeout(10000)
|
||||
|
||||
try {
|
||||
for (let i = 0; i < 5; i++) await bchjs.Blockbook.balance(addr)
|
||||
|
||||
assert.equal(true, false, "unexpected result")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, "Too many requests")
|
||||
}
|
||||
}).timeout(10000)
|
||||
|
||||
it("should allow more than 10 RPM to full node", async () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
it('should allow more than 20 RPM to full node', async () => {
|
||||
for (let i = 0; i < 22; i++) {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "version", "more than 3 calls allowed")
|
||||
assert.property(result, 'version', 'more than 3 calls allowed')
|
||||
}
|
||||
|
||||
if (i === 15) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "version", "more than 10 calls allowed")
|
||||
assert.property(result, 'version', 'more than 10 calls allowed')
|
||||
}
|
||||
}
|
||||
}).timeout(10000)
|
||||
}).timeout(20000)
|
||||
|
||||
it("should throw error for more than 100 RPM to fullnode", async () => {
|
||||
it('should throw error for more than 100 RPM to fullnode', async () => {
|
||||
try {
|
||||
console.log("This test usually doesn't pass, because of latency.")
|
||||
for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo()
|
||||
} catch (err) {
|
||||
// console.log(`validating after 10th call`)
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, "Too many requests")
|
||||
assert.include(err.error, 'Too many requests')
|
||||
assert.include(err.error, 100)
|
||||
}
|
||||
}).timeout(30000)
|
||||
}).timeout(45000)
|
||||
})
|
||||
|
||||
@@ -7,50 +7,52 @@
|
||||
- Update the JWT_TOKEN value with a current indexer-level JWT token.
|
||||
*/
|
||||
|
||||
const assert = require("chai").assert
|
||||
import chai from 'chai'
|
||||
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const JWT_TOKEN =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTU5MDQzMjIxNywiZXhwIjoxNTkzMDI0MjE3fQ.R0Euhutd5FHCxLlSzjq1TYsGjI6iua_AguWIeLK2xCU"
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: `https://api.fullstack.cash/v3/`,
|
||||
// restURL: `http://localhost:3000/v3/`,
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https://abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
describe("#full node rate limits", () => {
|
||||
it("should allow more than 10 RPM to full node", async () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
describe('#Indexer rate limits', () => {
|
||||
it('should allow more than 20 RPM to full node', async () => {
|
||||
for (let i = 0; i < 22; i++) {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "version", "more than 3 calls allowed")
|
||||
assert.property(result, 'version', 'more than 3 calls allowed')
|
||||
}
|
||||
|
||||
if (i === 15) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "version", "more than 10 calls allowed")
|
||||
assert.property(result, 'version', 'more than 10 calls allowed')
|
||||
}
|
||||
}
|
||||
}).timeout(30000)
|
||||
}).timeout(45000)
|
||||
|
||||
it("should allow more than 10 RPM to an indexer", async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
it('should allow more than 20 RPM to an indexer', async () => {
|
||||
const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
for (let i = 0; i < 22; i++) {
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
|
||||
if (i === 5) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "balance", "more than 3 calls allowed")
|
||||
assert.property(result, 'balance', 'more than 3 calls allowed')
|
||||
}
|
||||
|
||||
if (i === 15) {
|
||||
// console.log(`validating 5th call: ${i}`)
|
||||
assert.property(result, "balance", "more than 10 calls allowed")
|
||||
assert.property(result, 'balance', 'more than 10 calls allowed')
|
||||
}
|
||||
}
|
||||
}).timeout(30000)
|
||||
}).timeout(45000)
|
||||
})
|
||||
|
||||
@@ -13,32 +13,40 @@
|
||||
*/
|
||||
|
||||
// Replace the address below with the address you want to send the BCH to.
|
||||
const RECV_ADDR1 = `bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05`
|
||||
const RECV_ADDR2 = `bchtest:qz6yw0kqgfkknfy6jh2jvlfnkzmre3lt2u0pgcckdk`
|
||||
const RECV_ADDR1 = 'bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05'
|
||||
const RECV_ADDR2 = 'bchtest:qz6yw0kqgfkknfy6jh2jvlfnkzmre3lt2u0pgcckdk'
|
||||
const SATOSHIS_TO_SEND = 1000
|
||||
|
||||
// Instantiate BITBOX.
|
||||
const bitboxLib = "../../../lib/BITBOX"
|
||||
const BITBOXSDK = require(bitboxLib)
|
||||
const BITBOX = new BITBOXSDK({ restURL: "https://trest.bitcoin.com/v2/" })
|
||||
//const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
|
||||
|
||||
const util = require("util")
|
||||
// Note: Dynamic require with variable - use dynamic import in ESM
|
||||
const bitboxLib = '../../../lib/BITBOX.js'
|
||||
let BITBOXSDK, BITBOX, walletInfo
|
||||
|
||||
// Open the wallet generated with create-wallet.
|
||||
try {
|
||||
var walletInfo = require(`./wallet.json`)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
`Could not open wallet.json. Generate a wallet with create-wallet first.`
|
||||
)
|
||||
process.exit(0)
|
||||
let SEND_ADDR, SEND_MNEMONIC
|
||||
|
||||
// Load dependencies asynchronously
|
||||
async function loadDependencies () {
|
||||
BITBOXSDK = (await import(bitboxLib)).default
|
||||
BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
|
||||
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
|
||||
|
||||
try {
|
||||
const { createRequire } = await import('module')
|
||||
const require = createRequire(import.meta.url)
|
||||
walletInfo = require('./wallet.json')
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Could not open wallet.json. Generate a wallet with create-wallet first.'
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
SEND_ADDR = walletInfo.cashAddress
|
||||
SEND_MNEMONIC = walletInfo.mnemonic
|
||||
}
|
||||
|
||||
const SEND_ADDR = walletInfo.cashAddress
|
||||
const SEND_MNEMONIC = walletInfo.mnemonic
|
||||
|
||||
async function testSend() {
|
||||
async function testSend () {
|
||||
try {
|
||||
const hex1 = await buildTx1(RECV_ADDR1)
|
||||
const hex2 = await buildTx2(RECV_ADDR2)
|
||||
@@ -51,15 +59,18 @@ async function testSend() {
|
||||
hex2
|
||||
])
|
||||
console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`)
|
||||
console.log(`Should return an array of TXID strings.`)
|
||||
console.log('Should return an array of TXID strings.')
|
||||
} catch (err) {
|
||||
console.log(`Error in testSend: `, err)
|
||||
console.log('Error in testSend: ', err)
|
||||
}
|
||||
}
|
||||
testSend()
|
||||
// Load dependencies then run test
|
||||
loadDependencies().then(() => {
|
||||
testSend()
|
||||
})
|
||||
|
||||
// Build a TX hex with the largest UTXO.
|
||||
async function buildTx1(recAddr) {
|
||||
async function buildTx1 (recAddr) {
|
||||
try {
|
||||
// Get the balance of the sending address.
|
||||
const balance = await getBCHBalance(SEND_ADDR, false)
|
||||
@@ -68,7 +79,7 @@ async function buildTx1(recAddr) {
|
||||
|
||||
// Exit if the balance is zero.
|
||||
if (balance <= 0.0) {
|
||||
console.log(`Balance of sending address is zero. Exiting.`)
|
||||
console.log('Balance of sending address is zero. Exiting.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
@@ -77,16 +88,17 @@ async function buildTx1(recAddr) {
|
||||
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
|
||||
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
|
||||
|
||||
const balance2 = await getBCHBalance(recAddr, false)
|
||||
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
// const balance2 = await getBCHBalance(recAddr, false)
|
||||
// console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
await getBCHBalance(recAddr, false)
|
||||
|
||||
const u = await BITBOX.Address.utxo(SEND_ADDR)
|
||||
//console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
// console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
const utxo = findBiggestUtxo(u.utxos)
|
||||
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
// instance of transaction builder
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder("testnet")
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
|
||||
|
||||
const satoshisToSend = SATOSHIS_TO_SEND
|
||||
const originalAmount = utxo.satoshis
|
||||
@@ -137,13 +149,13 @@ async function buildTx1(recAddr) {
|
||||
|
||||
return hex
|
||||
} catch (err) {
|
||||
console.log(`Error in buildTx().`)
|
||||
console.log('Error in buildTx().')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Build a TX hex with the SECOND largest UTXO.
|
||||
async function buildTx2(recAddr) {
|
||||
async function buildTx2 (recAddr) {
|
||||
try {
|
||||
// Get the balance of the sending address.
|
||||
const balance = await getBCHBalance(SEND_ADDR, false)
|
||||
@@ -152,7 +164,7 @@ async function buildTx2(recAddr) {
|
||||
|
||||
// Exit if the balance is zero.
|
||||
if (balance <= 0.0) {
|
||||
console.log(`Balance of sending address is zero. Exiting.`)
|
||||
console.log('Balance of sending address is zero. Exiting.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
@@ -161,16 +173,17 @@ async function buildTx2(recAddr) {
|
||||
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
|
||||
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
|
||||
|
||||
const balance2 = await getBCHBalance(recAddr, false)
|
||||
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
// const balance2 = await getBCHBalance(recAddr, false)
|
||||
// console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
await getBCHBalance(recAddr, false)
|
||||
|
||||
const u = await BITBOX.Address.utxo(SEND_ADDR)
|
||||
//console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
// console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
const utxo = findNextBiggestUtxo(u.utxos)
|
||||
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
// instance of transaction builder
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder("testnet")
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
|
||||
|
||||
const satoshisToSend = SATOSHIS_TO_SEND
|
||||
const originalAmount = utxo.satoshis
|
||||
@@ -221,30 +234,30 @@ async function buildTx2(recAddr) {
|
||||
|
||||
return hex
|
||||
} catch (err) {
|
||||
console.log(`Error in buildTx().`)
|
||||
console.log('Error in buildTx().')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a change address from a Mnemonic of a private key.
|
||||
function changeAddrFromMnemonic(mnemonic) {
|
||||
function changeAddrFromMnemonic (mnemonic) {
|
||||
// root seed buffer
|
||||
const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic)
|
||||
|
||||
// master HDNode
|
||||
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, "testnet")
|
||||
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, 'testnet')
|
||||
|
||||
// HDNode of BIP44 account
|
||||
const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
|
||||
|
||||
// derive the first external change address HDNode which is going to spend utxo
|
||||
const change = BITBOX.HDNode.derivePath(account, "0/0")
|
||||
const change = BITBOX.HDNode.derivePath(account, '0/0')
|
||||
|
||||
return change
|
||||
}
|
||||
|
||||
// Get the balance in BCH of a BCH address.
|
||||
async function getBCHBalance(addr, verbose) {
|
||||
async function getBCHBalance (addr, verbose) {
|
||||
try {
|
||||
const result = await BITBOX.Address.details(addr)
|
||||
|
||||
@@ -254,16 +267,16 @@ async function getBCHBalance(addr, verbose) {
|
||||
|
||||
return bchBalance.balance
|
||||
} catch (err) {
|
||||
console.error(`Error in getBCHBalance: `, err)
|
||||
console.error('Error in getBCHBalance: ', err)
|
||||
console.log(`addr: ${addr}`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the utxo with the biggest balance from an array of utxos.
|
||||
function findBiggestUtxo(utxos) {
|
||||
function findBiggestUtxo (utxos) {
|
||||
// Sort the utxos by the amount of satoshis, largest first.
|
||||
utxos.sort(function(a, b) {
|
||||
utxos.sort(function (a, b) {
|
||||
return b.satoshis - a.satoshis
|
||||
})
|
||||
|
||||
@@ -271,9 +284,9 @@ function findBiggestUtxo(utxos) {
|
||||
}
|
||||
|
||||
// Returns the utxo with the 2nd biggest balance from an array of utxos.
|
||||
function findNextBiggestUtxo(utxos) {
|
||||
function findNextBiggestUtxo (utxos) {
|
||||
// Sort the utxos by the amount of satoshis, largest first.
|
||||
utxos.sort(function(a, b) {
|
||||
utxos.sort(function (a, b) {
|
||||
return b.satoshis - a.satoshis
|
||||
})
|
||||
|
||||
|
||||
@@ -13,50 +13,61 @@
|
||||
*/
|
||||
|
||||
// Replace the address below with the address you want to send the BCH to.
|
||||
const RECV_ADDR1 = `bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05`
|
||||
const RECV_ADDR1 = 'bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05'
|
||||
const SATOSHIS_TO_SEND = 1000
|
||||
|
||||
// Instantiate BITBOX.
|
||||
const bitboxLib = "../../../lib/BITBOX"
|
||||
const BITBOXSDK = require(bitboxLib)
|
||||
const BITBOX = new BITBOXSDK({ restURL: "https://trest.bitcoin.com/v2/" })
|
||||
//const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
|
||||
|
||||
const util = require("util")
|
||||
// Note: Dynamic require with variable - use dynamic import in ESM
|
||||
const bitboxLib = '../../../lib/BITBOX.js'
|
||||
let BITBOXSDK, BITBOX, walletInfo
|
||||
|
||||
// Open the wallet generated with create-wallet.
|
||||
try {
|
||||
var walletInfo = require(`./wallet.json`)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
`Could not open wallet.json. Generate a wallet with create-wallet first.`
|
||||
)
|
||||
process.exit(0)
|
||||
let SEND_ADDR, SEND_MNEMONIC
|
||||
|
||||
// Load dependencies asynchronously
|
||||
async function loadDependencies () {
|
||||
BITBOXSDK = (await import(bitboxLib)).default
|
||||
BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
|
||||
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
|
||||
|
||||
try {
|
||||
const { createRequire } = await import('module')
|
||||
const require = createRequire(import.meta.url)
|
||||
walletInfo = require('./wallet.json')
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Could not open wallet.json. Generate a wallet with create-wallet first.'
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
SEND_ADDR = walletInfo.cashAddress
|
||||
SEND_MNEMONIC = walletInfo.mnemonic
|
||||
}
|
||||
|
||||
const SEND_ADDR = walletInfo.cashAddress
|
||||
const SEND_MNEMONIC = walletInfo.mnemonic
|
||||
|
||||
async function testSend() {
|
||||
async function testSend () {
|
||||
try {
|
||||
const hex1 = await buildTx1(RECV_ADDR1)
|
||||
//const hex2 = await buildTx2(RECV_ADDR2)
|
||||
// const hex2 = await buildTx2(RECV_ADDR2)
|
||||
|
||||
console.log(`hex1: ${hex1}\n\n`)
|
||||
//console.log(`hex2: ${hex2}\n\n`)
|
||||
// console.log(`hex2: ${hex2}\n\n`)
|
||||
|
||||
const broadcast = await BITBOX.RawTransactions.sendRawTransaction(hex1)
|
||||
|
||||
console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`)
|
||||
console.log(`Should return a TXID string.`)
|
||||
console.log('Should return a TXID string.')
|
||||
} catch (err) {
|
||||
console.log(`Error in testSend: `, err)
|
||||
console.log('Error in testSend: ', err)
|
||||
}
|
||||
}
|
||||
testSend()
|
||||
// Load dependencies then run test
|
||||
loadDependencies().then(() => {
|
||||
testSend()
|
||||
})
|
||||
|
||||
// Build a TX hex with the largest UTXO.
|
||||
async function buildTx1(recAddr) {
|
||||
async function buildTx1 (recAddr) {
|
||||
try {
|
||||
// Get the balance of the sending address.
|
||||
const balance = await getBCHBalance(SEND_ADDR, false)
|
||||
@@ -65,7 +76,7 @@ async function buildTx1(recAddr) {
|
||||
|
||||
// Exit if the balance is zero.
|
||||
if (balance <= 0.0) {
|
||||
console.log(`Balance of sending address is zero. Exiting.`)
|
||||
console.log('Balance of sending address is zero. Exiting.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
@@ -74,16 +85,17 @@ async function buildTx1(recAddr) {
|
||||
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
|
||||
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
|
||||
|
||||
const balance2 = await getBCHBalance(recAddr, false)
|
||||
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
// const balance2 = await getBCHBalance(recAddr, false)
|
||||
// console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
|
||||
await getBCHBalance(recAddr, false)
|
||||
|
||||
const u = await BITBOX.Address.utxo(SEND_ADDR)
|
||||
//console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
// console.log(`u: ${JSON.stringify(u, null, 2)}`)
|
||||
const utxo = findBiggestUtxo(u.utxos)
|
||||
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
// instance of transaction builder
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder("testnet")
|
||||
const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
|
||||
|
||||
const satoshisToSend = SATOSHIS_TO_SEND
|
||||
const originalAmount = utxo.satoshis
|
||||
@@ -134,30 +146,30 @@ async function buildTx1(recAddr) {
|
||||
|
||||
return hex
|
||||
} catch (err) {
|
||||
console.log(`Error in buildTx().`)
|
||||
console.log('Error in buildTx().')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a change address from a Mnemonic of a private key.
|
||||
function changeAddrFromMnemonic(mnemonic) {
|
||||
function changeAddrFromMnemonic (mnemonic) {
|
||||
// root seed buffer
|
||||
const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic)
|
||||
|
||||
// master HDNode
|
||||
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, "testnet")
|
||||
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, 'testnet')
|
||||
|
||||
// HDNode of BIP44 account
|
||||
const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
|
||||
|
||||
// derive the first external change address HDNode which is going to spend utxo
|
||||
const change = BITBOX.HDNode.derivePath(account, "0/0")
|
||||
const change = BITBOX.HDNode.derivePath(account, '0/0')
|
||||
|
||||
return change
|
||||
}
|
||||
|
||||
// Get the balance in BCH of a BCH address.
|
||||
async function getBCHBalance(addr, verbose) {
|
||||
async function getBCHBalance (addr, verbose) {
|
||||
try {
|
||||
const result = await BITBOX.Address.details(addr)
|
||||
|
||||
@@ -167,16 +179,16 @@ async function getBCHBalance(addr, verbose) {
|
||||
|
||||
return bchBalance.balance
|
||||
} catch (err) {
|
||||
console.error(`Error in getBCHBalance: `, err)
|
||||
console.error('Error in getBCHBalance: ', err)
|
||||
console.log(`addr: ${addr}`)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the utxo with the biggest balance from an array of utxos.
|
||||
function findBiggestUtxo(utxos) {
|
||||
function findBiggestUtxo (utxos) {
|
||||
// Sort the utxos by the amount of satoshis, largest first.
|
||||
utxos.sort(function(a, b) {
|
||||
utxos.sort(function (a, b) {
|
||||
return b.satoshis - a.satoshis
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
*/
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
import util from 'util'
|
||||
|
||||
import lib from '../util/e2e-util.js'
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
@@ -18,22 +20,20 @@ util.inspect.defaultOptions = {
|
||||
// const SLPSDK = require("../../../lib/SLP")
|
||||
// const slpsdk = new SLPSDK()
|
||||
|
||||
const WALLET1 = `../wallet1.json`
|
||||
const WALLET2 = `../wallet2.json`
|
||||
|
||||
const lib = require("../util/e2e-util")
|
||||
const WALLET1 = '../wallet1.json'
|
||||
const WALLET2 = '../wallet2.json'
|
||||
|
||||
// The main test function.
|
||||
// Sends a token and reports on how long it takes to show up in SLPDB production.
|
||||
async function sendTokenTest() {
|
||||
async function sendTokenTest () {
|
||||
try {
|
||||
// Open the sending wallet.
|
||||
const sendWallet = await lib.openWallet(WALLET1)
|
||||
//console.log(`sendWallet: ${JSON.stringify(walletInfo, null, 2)}`)
|
||||
// console.log(`sendWallet: ${JSON.stringify(walletInfo, null, 2)}`)
|
||||
|
||||
// Open the recieving wallet.
|
||||
const recvWallet = await lib.openWallet(WALLET2)
|
||||
//console.log(`recvWallet: ${JSON.stringify(walletInfo, null, 2)}`)
|
||||
// console.log(`recvWallet: ${JSON.stringify(walletInfo, null, 2)}`)
|
||||
|
||||
// Get the balance of the recieving wallet.
|
||||
// const testTokens = recvWallet.tokenBalance.filter(
|
||||
@@ -46,7 +46,7 @@ async function sendTokenTest() {
|
||||
|
||||
// Send a token to the recieving wallet.
|
||||
await lib.sendToken(sendWallet, recvWallet)
|
||||
console.log(`Sent test token.`)
|
||||
console.log('Sent test token.')
|
||||
|
||||
// Track the time until the balance for the recieving wallet has been updated.
|
||||
const startTime = new Date()
|
||||
@@ -56,7 +56,7 @@ async function sendTokenTest() {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
await sleep(waitTime) // Wait for a while before checking
|
||||
|
||||
console.log(`Checking token balance...`)
|
||||
console.log('Checking token balance...')
|
||||
newBalance = await lib.getTestTokenBalance(recvWallet)
|
||||
|
||||
// Break out of the loop once a new balance is detected.
|
||||
@@ -65,12 +65,12 @@ async function sendTokenTest() {
|
||||
// Provide high-level warnings.
|
||||
const secondsPassed = (i * waitTime) / 1000
|
||||
if (secondsPassed > 60 * 10) {
|
||||
console.log(`More than 10 minutes passed.`)
|
||||
console.log('More than 10 minutes passed.')
|
||||
return false // Fail the test.
|
||||
} else if (secondsPassed > 60 * 5) {
|
||||
console.log(`More than 5 minutes passed.`)
|
||||
console.log('More than 5 minutes passed.')
|
||||
} else if (secondsPassed > 60) {
|
||||
console.log(`More than 1 minute passed.`)
|
||||
console.log('More than 1 minute passed.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,16 +85,16 @@ async function sendTokenTest() {
|
||||
|
||||
return deltaTime // Return the time in minutes it took for SLPDB to update.
|
||||
} catch (err) {
|
||||
console.log(`Error in e2e/send-token.js/sendTokenTest(): `, err)
|
||||
console.log('Error in e2e/send-token.js/sendTokenTest(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Promise based sleep function.
|
||||
function sleep(ms) {
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
sendTokenTest
|
||||
}
|
||||
|
||||
+24
-20
@@ -3,26 +3,29 @@
|
||||
tests.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
import SLPSDK from '../../../lib/SLP.js'
|
||||
|
||||
export default {
|
||||
openWallet,
|
||||
sendToken,
|
||||
getTestTokenBalance,
|
||||
threeDecimals
|
||||
}
|
||||
|
||||
const SLPSDK = require("../../../lib/SLP")
|
||||
const slpsdk = new SLPSDK()
|
||||
|
||||
const testTokenId =
|
||||
"cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67"
|
||||
'cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67'
|
||||
|
||||
// Open a wallet and return an object with its address, BCH balance, and SLP
|
||||
// token balance.
|
||||
async function openWallet(filename) {
|
||||
async function openWallet (filename) {
|
||||
try {
|
||||
walletInfo = require(filename)
|
||||
// Note: Dynamic imports for JSON files - use createRequire
|
||||
const { createRequire } = await import('module')
|
||||
const require = createRequire(import.meta.url)
|
||||
const walletInfo = require(filename)
|
||||
|
||||
//const walletBalance = await getBalance(walletInfo)
|
||||
// const walletBalance = await getBalance(walletInfo)
|
||||
// const walletBalance = await slpsdk.Utils.balancesForAddress(
|
||||
// walletInfo.slpAddress
|
||||
// )
|
||||
@@ -40,7 +43,7 @@ async function openWallet(filename) {
|
||||
}
|
||||
|
||||
// Send a token from wallet1 to wallet2.
|
||||
async function sendToken(wallet1, wallet2) {
|
||||
async function sendToken (wallet1, wallet2) {
|
||||
try {
|
||||
const mnemonic = wallet1.mnemonic
|
||||
|
||||
@@ -53,11 +56,11 @@ async function sendToken(wallet1, wallet2) {
|
||||
// HDNode of BIP44 account
|
||||
const account = slpsdk.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
|
||||
|
||||
const change = slpsdk.HDNode.derivePath(account, "0/0")
|
||||
const change = slpsdk.HDNode.derivePath(account, '0/0')
|
||||
|
||||
// get the cash address
|
||||
//const cashAddress = slpsdk.HDNode.toCashAddress(change)
|
||||
//const slpAddress = slpsdk.HDNode.toSLPAddress(change)
|
||||
// const cashAddress = slpsdk.HDNode.toCashAddress(change)
|
||||
// const slpAddress = slpsdk.HDNode.toSLPAddress(change)
|
||||
|
||||
const fundingAddress = wallet1.slpAddress
|
||||
const fundingWif = slpsdk.HDNode.toWIF(change) // <-- compressed WIF format
|
||||
@@ -71,25 +74,26 @@ async function sendToken(wallet1, wallet2) {
|
||||
tokenReceiverAddress,
|
||||
bchChangeReceiverAddress,
|
||||
tokenId:
|
||||
"cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67",
|
||||
'cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67',
|
||||
amount: 1
|
||||
}
|
||||
|
||||
//console.log(`createConfig: ${util.inspect(createConfig)}`)
|
||||
// console.log(`createConfig: ${util.inspect(createConfig)}`)
|
||||
|
||||
// Generate, sign, and broadcast a hex-encoded transaction for sending
|
||||
// the tokens.
|
||||
const sendTxId = await slpsdk.TokenType1.send(sendConfig)
|
||||
// const sendTxId = await slpsdk.TokenType1.send(sendConfig)
|
||||
await slpsdk.TokenType1.send(sendConfig)
|
||||
|
||||
//console.log(`sendTxId: ${sendTxId}`)
|
||||
// console.log(`sendTxId: ${sendTxId}`)
|
||||
} catch (err) {
|
||||
console.log(`Error in e2e-util.js/sendToken()`)
|
||||
console.log('Error in e2e-util.js/sendToken()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns just the test token balance for a wallet.
|
||||
async function getTestTokenBalance(walletData) {
|
||||
async function getTestTokenBalance (walletData) {
|
||||
try {
|
||||
const tokenBalance = await slpsdk.Util.balancesForAddress(
|
||||
walletData.slpAddress
|
||||
@@ -100,20 +104,20 @@ async function getTestTokenBalance(walletData) {
|
||||
|
||||
return testTokens[0].balance
|
||||
} catch (err) {
|
||||
console.log(`Error in e2e-util.js/getTestTokenBalance()`)
|
||||
console.log('Error in e2e-util.js/getTestTokenBalance()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Round a number to three decimal places.
|
||||
function threeDecimals(inNum) {
|
||||
function threeDecimals (inNum) {
|
||||
try {
|
||||
let tempNum = inNum * 1000
|
||||
tempNum = Math.round(tempNum)
|
||||
tempNum = tempNum / 1000
|
||||
return tempNum
|
||||
} catch (err) {
|
||||
console.log(`Error in e2e-util.js/threeDecimals()`)
|
||||
console.log('Error in e2e-util.js/threeDecimals()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
This test is used to interrogate the behavior of bch-js when a psf-slp-indexer
|
||||
panicks and starts indexing from SLP genesis. In this corner-case, bch-js
|
||||
should detect the indexer is out of sync, and protect any token UTXOs by
|
||||
moving any UTXO under 600 sats into the null array.
|
||||
|
||||
TO RUN THIS TEST:
|
||||
- Reset a local instance of psf-slp-indexer to sync from genesis.
|
||||
- Start a local copy of bch-api
|
||||
*/
|
||||
|
||||
import BCHJS from '../../../src/bch-js.js'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
async function startTest () {
|
||||
try {
|
||||
const addr = 'bitcoincash:qzkvas58zag9tjry693mflkjze2m20ps7vx7uw7z9d'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
} catch (err) {
|
||||
console.error('Error in startTest(): ', err)
|
||||
}
|
||||
}
|
||||
startTest()
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
Integration tests for the Blockbook library.
|
||||
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
//const axios = require("axios")
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#Blockbook`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result.txids)
|
||||
})
|
||||
|
||||
it(`should POST request balances for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result[0].txids)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.balance(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7"
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
|
||||
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0])
|
||||
assert.hasAnyKeys(result[0][0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
+143
-144
@@ -7,119 +7,109 @@
|
||||
of an e2e test to be properly tested.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
import chai from 'chai'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
import util from 'util'
|
||||
const assert = chai.assert
|
||||
const bchjs = new BCHJS()
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#blockchain`, () => {
|
||||
describe(`#getBestBlockHash`, () => {
|
||||
it(`should GET best block hash`, async () => {
|
||||
describe('#blockchain', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getBestBlockHash', () => {
|
||||
it('should GET best block hash', async () => {
|
||||
const result = await bchjs.Blockchain.getBestBlockHash()
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result.length, 64, "Specific hash length")
|
||||
assert.equal(result.length, 64, 'Specific hash length')
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockHeader", () => {
|
||||
it(`should GET block header for a single hash`, async () => {
|
||||
describe('#getBlockHeader', () => {
|
||||
it('should GET block header for a single hash', async () => {
|
||||
const hash =
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
|
||||
'000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201'
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
'hash',
|
||||
'confirmations',
|
||||
'height',
|
||||
'version',
|
||||
'versionHex',
|
||||
'merkleroot',
|
||||
'time',
|
||||
'mediantime',
|
||||
'nonce',
|
||||
'bits',
|
||||
'difficulty',
|
||||
'chainwork',
|
||||
'previousblockhash',
|
||||
'nextblockhash',
|
||||
'nTx'
|
||||
])
|
||||
})
|
||||
|
||||
it(`should GET block headers for an array of hashes`, async () => {
|
||||
it('should GET block headers for an array of hashes', async () => {
|
||||
const hash = [
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201",
|
||||
"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
|
||||
'000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201',
|
||||
'00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3'
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
'hash',
|
||||
'confirmations',
|
||||
'height',
|
||||
'version',
|
||||
'versionHex',
|
||||
'merkleroot',
|
||||
'time',
|
||||
'mediantime',
|
||||
'nonce',
|
||||
'bits',
|
||||
'difficulty',
|
||||
'chainwork',
|
||||
'previousblockhash',
|
||||
'nextblockhash',
|
||||
'nTx'
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const hash = 12345
|
||||
|
||||
await bchjs.Blockchain.getBlockHeader(hash)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hash must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
|
||||
'000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201'
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolEntry", () => {
|
||||
describe('#getMempoolEntry', () => {
|
||||
/*
|
||||
// To run this test, the txid must be unconfirmed.
|
||||
const txid =
|
||||
@@ -171,119 +161,77 @@ describe(`#blockchain`, () => {
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw an error if txid is not in mempool`, async () => {
|
||||
it('should throw an error if txid is not in mempool', async () => {
|
||||
try {
|
||||
const txid =
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
'03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, `Transaction not in mempool`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Transaction not in mempool')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#getTxOutProof`, () => {
|
||||
it(`should get single tx out proof`, async () => {
|
||||
describe('#getTxOutProof', () => {
|
||||
it('should get single tx out proof', async () => {
|
||||
const txid =
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
'03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it(`should get an array of tx out proofs`, async () => {
|
||||
it('should get an array of tx out proofs', async () => {
|
||||
const txid = [
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7",
|
||||
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"
|
||||
'03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7',
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#verifyTxOutProof`, () => {
|
||||
describe('#verifyTxOutProof', () => {
|
||||
const mockTxOutProof =
|
||||
"0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01"
|
||||
'0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01'
|
||||
|
||||
it(`should verify a single proof`, async () => {
|
||||
it('should verify a single proof', async () => {
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
'03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
|
||||
)
|
||||
})
|
||||
|
||||
it(`should verify an array of proofs`, async () => {
|
||||
it('should verify an array of proofs', async () => {
|
||||
const proofs = [mockTxOutProof, mockTxOutProof]
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
'03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
|
||||
)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.verifyTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
|
||||
@@ -291,35 +239,35 @@ describe(`#blockchain`, () => {
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getTxOut", () => {
|
||||
it("should get information on an unspent tx", async () => {
|
||||
describe('#getTxOut', () => {
|
||||
it('should get information on an unspent tx', async () => {
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8",
|
||||
'62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8',
|
||||
0,
|
||||
true
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"bestblock",
|
||||
"confirmations",
|
||||
"value",
|
||||
"scriptPubKey",
|
||||
"coinbase"
|
||||
'bestblock',
|
||||
'confirmations',
|
||||
'value',
|
||||
'scriptPubKey',
|
||||
'coinbase'
|
||||
])
|
||||
})
|
||||
|
||||
it("should get information on a spent tx", async () => {
|
||||
it('should get information on a spent tx', async () => {
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871",
|
||||
'87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871',
|
||||
0,
|
||||
true
|
||||
)
|
||||
@@ -328,4 +276,55 @@ describe(`#blockchain`, () => {
|
||||
assert.equal(result, null)
|
||||
})
|
||||
})
|
||||
describe('#getBlock', () => {
|
||||
it('should get block information with default verbosity', async () => {
|
||||
const blockhash =
|
||||
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
|
||||
const result = await bchjs.Blockchain.getBlock(blockhash)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
'hash',
|
||||
'confirmations',
|
||||
'size',
|
||||
'height',
|
||||
'version',
|
||||
'versionHex',
|
||||
'merkleroot',
|
||||
'tx',
|
||||
'time',
|
||||
'mediantime',
|
||||
'nonce',
|
||||
'bits',
|
||||
'difficulty',
|
||||
'chainwork',
|
||||
'nTx',
|
||||
'previousblockhash',
|
||||
'nextblockhash'
|
||||
])
|
||||
assert.isArray(result.tx)
|
||||
})
|
||||
|
||||
it('should get block information with verbosity 0', async () => {
|
||||
const blockhash =
|
||||
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
|
||||
const verbosity = 0
|
||||
const result = await bchjs.Blockchain.getBlock(blockhash, verbosity)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getBlockchainInfo', () => {
|
||||
it('should get info about the blockchain', async () => {
|
||||
const result = await bchjs.Blockchain.getBlockchainInfo()
|
||||
|
||||
// console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'blocks')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -2,18 +2,26 @@
|
||||
Integration tests for bchjs control library.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
import chai from 'chai'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#control`, () => {
|
||||
describe(`#getNetworkInfo`, () => {
|
||||
it("should get info on the full node", async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
describe('#control', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
assert.property(result, "version")
|
||||
describe('#getNetworkInfo', () => {
|
||||
it('should get info on the full node', async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'version')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Integration tests for bchjs dsproof library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import chai from 'chai'
|
||||
|
||||
// Local libraries
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#DSProof', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
describe('#getDSProof', () => {
|
||||
it('should get TX info from the full node', async () => {
|
||||
const txid =
|
||||
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
const result = await bchjs.DSProof.getDSProof(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
+184
-125
@@ -1,185 +1,199 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
import chai from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const assert = chai.assert
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
describe('#ElectrumX', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
describe('#utxo', () => {
|
||||
it('should GET utxos for a single address', async () => {
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0], "value")
|
||||
assert.property(result.utxos[0], 'height')
|
||||
assert.property(result.utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0], 'tx_pos')
|
||||
assert.property(result.utxos[0], 'value')
|
||||
})
|
||||
|
||||
it(`should POST request for UTXOs for an array of addresses`, async () => {
|
||||
it('should POST request for UTXOs for an array of addresses', async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.property(result.utxos[0], 'utxos')
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
assert.property(result.utxos[0], 'address')
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0].utxos[0], "value")
|
||||
assert.property(result.utxos[0].utxos[0], 'height')
|
||||
assert.property(result.utxos[0].utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0].utxos[0], 'tx_pos')
|
||||
assert.property(result.utxos[0].utxos[0], 'value')
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
|
||||
}
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// const result = await bchjs.Electrumx.utxo(addr)
|
||||
await bchjs.Electrumx.utxo(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
describe('#balance', () => {
|
||||
it('should GET balance for a single address', async () => {
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result.balance, "confirmed")
|
||||
assert.property(result.balance, "unconfirmed")
|
||||
assert.property(result, 'balance')
|
||||
assert.property(result.balance, 'confirmed')
|
||||
assert.property(result.balance, 'unconfirmed')
|
||||
})
|
||||
|
||||
it(`should POST request for balances for an array of addresses`, async () => {
|
||||
it('should POST request for balances for an array of addresses', async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balances")
|
||||
assert.property(result, 'balances')
|
||||
assert.isArray(result.balances)
|
||||
|
||||
assert.property(result.balances[0], "address")
|
||||
assert.property(result.balances[0], "balance")
|
||||
assert.property(result.balances[0].balance, "confirmed")
|
||||
assert.property(result.balances[0].balance, "unconfirmed")
|
||||
assert.property(result.balances[0], 'address')
|
||||
assert.property(result.balances[0], 'balance')
|
||||
assert.property(result.balances[0].balance, 'confirmed')
|
||||
assert.property(result.balances[0].balance, 'unconfirmed')
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
|
||||
}
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
await bchjs.Electrumx.balance(addr)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#transactions`, () => {
|
||||
it(`should GET transaction history for a single address`, async () => {
|
||||
const addr = "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
describe('#transactions', () => {
|
||||
it('should GET transaction history for a single address', async () => {
|
||||
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
assert.property(result.transactions[0], "height")
|
||||
assert.property(result.transactions[0], "tx_hash")
|
||||
assert.property(result.transactions[0], 'height')
|
||||
assert.property(result.transactions[0], 'tx_hash')
|
||||
})
|
||||
|
||||
it(`should POST request for transaction history for an array of addresses`, async () => {
|
||||
it('should POST request for transaction history for an array of addresses', async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "address")
|
||||
assert.property(result.transactions[0], "transactions")
|
||||
assert.property(result.transactions[0], 'address')
|
||||
assert.property(result.transactions[0], 'transactions')
|
||||
assert.isArray(result.transactions[0].transactions)
|
||||
assert.property(result.transactions[0].transactions[0], "height")
|
||||
assert.property(result.transactions[0].transactions[0], "tx_hash")
|
||||
assert.property(result.transactions[0].transactions[0], 'height')
|
||||
assert.property(result.transactions[0].transactions[0], 'tx_hash')
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0')
|
||||
}
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
await bchjs.Electrumx.transactions(addr)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#unconfirmed`, () => {
|
||||
describe('#unconfirmed', () => {
|
||||
// These tests won't work because unconfirmed transactions are transient in nature.
|
||||
/*
|
||||
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
|
||||
@@ -224,111 +238,156 @@ describe(`#ElectrumX`, () => {
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.unconfirmed(addr)
|
||||
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should GET block headers for given height and count`, async () => {
|
||||
describe('#blockHeader', () => {
|
||||
it('should GET block headers for given height and count', async () => {
|
||||
const height = 42
|
||||
const count = 2
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height, count)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.property(result, 'headers')
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 2)
|
||||
})
|
||||
|
||||
it(`should GET block headers for given height with default count = 1`, async () => {
|
||||
it('should GET block headers for given height with default count = 1', async () => {
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.property(result, 'headers')
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
describe('#txData', () => {
|
||||
it('should GET details for a single transaction', async () => {
|
||||
const txid =
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.property(result, 'details')
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.property(result.details, 'blockhash')
|
||||
assert.property(result.details, 'hash')
|
||||
assert.property(result.details, 'hex')
|
||||
assert.property(result.details, 'vin')
|
||||
assert.property(result.details, 'vout')
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
it('should POST details for an array of transactions', async () => {
|
||||
const txids = [
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
assert.property(result.transactions[0], 'txid')
|
||||
assert.property(result.transactions[0], 'details')
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
assert.property(result.transactions[0].details, 'blockhash')
|
||||
assert.property(result.transactions[0].details, 'hash')
|
||||
assert.property(result.transactions[0].details, 'hex')
|
||||
assert.property(result.transactions[0].details, 'vin')
|
||||
assert.property(result.transactions[0].details, 'vout')
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
txids.push("4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
txids.push(
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
)
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.instanceOf(err, Error)
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should broadcast a single transaction', async () => {
|
||||
const txHex =
|
||||
'020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000'
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
// console.error('err: ', err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'the transaction was rejected by network rules'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#sortAllTxs', () => {
|
||||
it('should GET transaction history for a single address', async () => {
|
||||
// Add delay for this endpoint.
|
||||
await sleep(6000)
|
||||
|
||||
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
|
||||
const txs = await bchjs.Electrumx.transactions(addr)
|
||||
const sortedTransactions = await bchjs.Electrumx.sortAllTxs(
|
||||
txs.transactions
|
||||
)
|
||||
// console.log(
|
||||
// `sortedTransactions: ${JSON.stringify(sortedTransactions, null, 2)}`
|
||||
// )
|
||||
|
||||
assert.isAbove(sortedTransactions[0].height, sortedTransactions[1].height)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -1,31 +1,48 @@
|
||||
const assert = require("chai").assert
|
||||
// const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
// const BCHJS = require('../../src/bch-js')
|
||||
// const bchjs = new BCHJS()
|
||||
|
||||
describe("#Encryption", () => {
|
||||
describe("#getPubKey", () => {
|
||||
it("should get a public key", async () => {
|
||||
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
|
||||
describe('#Encryption', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getPubKey', () => {
|
||||
// Commenting out these tests since they are failing in BVT due to 429 errors
|
||||
/*
|
||||
it('should get a public key', async () => {
|
||||
// Add delay for this endpoint.
|
||||
await sleep(8000)
|
||||
|
||||
const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
assert.property(result, "publicKey")
|
||||
assert.property(result, 'publicKey')
|
||||
})
|
||||
|
||||
it("should report when public key can not be found", async () => {
|
||||
const addr = "bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh"
|
||||
it('should report when public key can not be found', async () => {
|
||||
// Add delay for this endpoint.
|
||||
await sleep(8000)
|
||||
|
||||
const addr = 'bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh'
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, false)
|
||||
assert.property(result, "publicKey")
|
||||
assert.equal(result.publicKey, "not found")
|
||||
assert.property(result, 'publicKey')
|
||||
assert.equal(result.publicKey, 'not found')
|
||||
})
|
||||
*/
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
|
||||
|
||||
describe(`#Ninsight`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0].utxos[0], "txid")
|
||||
assert.property(result[0].utxos[0], "vout")
|
||||
assert.property(result[0].utxos[0], "amount")
|
||||
assert.property(result[0].utxos[0], "satoshis")
|
||||
assert.property(result[0].utxos[0], "height")
|
||||
assert.property(result[0].utxos[0], "confirmations")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
|
||||
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
tests for OpenBazaar library.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#OpenBazaar`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.balance(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.OpenBazaar.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"addrStr",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxApperances",
|
||||
"txApperances",
|
||||
"transactions"
|
||||
])
|
||||
assert.isArray(result.transactions)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.OpenBazaar.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"amount",
|
||||
"height",
|
||||
"confirmations",
|
||||
"satoshis"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.tx(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txid must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET transactions for a single address`, async () => {
|
||||
const addr =
|
||||
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
|
||||
|
||||
const result = await bchjs.OpenBazaar.tx(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"txid",
|
||||
"version",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"blockheight",
|
||||
"confirmations",
|
||||
"blocktime",
|
||||
"valueOut",
|
||||
"valueIn",
|
||||
"fees",
|
||||
"hex",
|
||||
"time"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,14 +1,32 @@
|
||||
const assert = require("assert")
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import chai from 'chai'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe("#price", () => {
|
||||
describe("#current", () => {
|
||||
describe("#single currency", () => {
|
||||
it("should get current price for single currency", async () => {
|
||||
const result = await bchjs.Price.current("usd")
|
||||
assert.notEqual(0, result)
|
||||
})
|
||||
describe('#price', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getBchUsd', () => {
|
||||
it('should get the USD price of BCH', async () => {
|
||||
const result = await bchjs.Price.getBchUsd()
|
||||
console.log(result)
|
||||
|
||||
assert.isNumber(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getPsffppPrice', () => {
|
||||
it('should get the price to write 1MB to the PSFFPP', async () => {
|
||||
const result = await bchjs.Price.getPsffppPrice()
|
||||
// console.log(result)
|
||||
|
||||
assert.isNumber(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Integration tests for the psf-slp-indexer.js library, specific to the BCH
|
||||
blockchain.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import chai from 'chai'
|
||||
|
||||
// Local libraries
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
let bchjs
|
||||
|
||||
describe('#psf-slp-indexer', () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#status', () => {
|
||||
it('should return the status of the indexer.', async () => {
|
||||
const result = await bchjs.PsfSlpIndexer.status()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.status, 'startBlockHeight')
|
||||
assert.property(result.status, 'syncedBlockHeight')
|
||||
assert.property(result.status, 'chainBlockHeight')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#balance', () => {
|
||||
it('should get balance data for an address.', async () => {
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.balance, 'utxos')
|
||||
assert.property(result.balance, 'txs')
|
||||
assert.property(result.balance, 'balances')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should get stats on a token, without tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
|
||||
it('should get stats on a token, with tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId, true)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tx', () => {
|
||||
it('should get hydrated tx data for an SLP transaction', async () => {
|
||||
const txid =
|
||||
'947ccb2a0d62ca287bc4b0993874ab0f9f6afd454193e631e2bf84dca66731fc'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, true)
|
||||
})
|
||||
|
||||
it('should mark non-SLP transaction as false', async () => {
|
||||
const txid =
|
||||
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
// FlexUSD transactions
|
||||
// FlexUSD is blacklisted by the SLP indexer.
|
||||
it('should mark blacklisted token as null', async () => {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should throw error for non-existent txid', async () => {
|
||||
try {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85e'
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert.include(err.message, 'No such mempool or blockchain transaction')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTokenData', () => {
|
||||
it('should get token data', async () => {
|
||||
const tokenId =
|
||||
'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
|
||||
|
||||
// bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
|
||||
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result, 'genesisData')
|
||||
assert.property(result, 'immutableData')
|
||||
assert.property(result, 'mutableData')
|
||||
|
||||
assert.isObject(result.genesisData)
|
||||
assert.isString(result.immutableData)
|
||||
assert.isString(result.mutableData)
|
||||
})
|
||||
|
||||
it('should get token data with a transaction history', async () => {
|
||||
const tokenId = '43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId, true)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.isArray(result.genesisData.txs)
|
||||
})
|
||||
})
|
||||
|
||||
// This test is commented out because it can not succeed in the BVT without
|
||||
// tripping rate limits.
|
||||
// describe('#getTokenData2', () => {
|
||||
// it('should get token data', async () => {
|
||||
// const tokenId =
|
||||
// 'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
|
||||
//
|
||||
// // bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
|
||||
// const result = await bchjs.PsfSlpIndexer.getTokenData2(tokenId)
|
||||
// // console.log('result: ', result)
|
||||
//
|
||||
// assert.property(result, 'tokenStats')
|
||||
// assert.property(result, 'mutableData')
|
||||
// assert.property(result, 'immutableData')
|
||||
// assert.property(result, 'tokenIcon')
|
||||
// assert.property(result, 'fullSizedUrl')
|
||||
// assert.property(result, 'optimizedTokenIcon')
|
||||
// assert.property(result, 'optimizedFullSizedUrl')
|
||||
// assert.property(result, 'iconRepoCompatible')
|
||||
// assert.property(result, 'ps002Compatible')
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
+171
-150
@@ -1,151 +1,139 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
Integration tests for the bchjs.
|
||||
|
||||
TODO
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
import chai from 'chai'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
import util from 'util'
|
||||
const assert = chai.assert
|
||||
const bchjs = new BCHJS()
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe("#rawtransaction", () => {
|
||||
describe("#decodeRawTransaction", () => {
|
||||
it("should decode tx for a single hex", async () => {
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#decodeRawTransaction', () => {
|
||||
it('should decode tx for a single hex', async () => {
|
||||
const hex =
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
'txid',
|
||||
'hash',
|
||||
'size',
|
||||
'version',
|
||||
'locktime',
|
||||
'vin',
|
||||
'vout'
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of tx hexes", async () => {
|
||||
it('should decode an array of tx hexes', async () => {
|
||||
const hexes = [
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000",
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000',
|
||||
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
'txid',
|
||||
'hash',
|
||||
'size',
|
||||
'version',
|
||||
'locktime',
|
||||
'vin',
|
||||
'vout'
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.hasAnyKeys(err, ['error'])
|
||||
assert.include(err.error, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getRawTransaction", () => {
|
||||
it("should decode a single txid, with concise output", async () => {
|
||||
describe('#getRawTransaction', () => {
|
||||
it('should decode a single txid, with concise output', async () => {
|
||||
const txid =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
'23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
|
||||
const verbose = false
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it("should decode a single txid, with verbose output", async () => {
|
||||
it('should decode a single txid, with verbose output', async () => {
|
||||
const txid =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
'23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
|
||||
const verbose = true
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
'hex',
|
||||
'txid',
|
||||
'hash',
|
||||
'size',
|
||||
'version',
|
||||
'locktime',
|
||||
'vin',
|
||||
'vout',
|
||||
'blockhash',
|
||||
'confirmations',
|
||||
'time',
|
||||
'blocktime'
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a concise output", async () => {
|
||||
it('should decode an array of txids, with a concise output', async () => {
|
||||
const txid = [
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
|
||||
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
|
||||
'23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1',
|
||||
'b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f'
|
||||
]
|
||||
const verbose = false
|
||||
|
||||
@@ -153,16 +141,16 @@ describe("#rawtransaction", () => {
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a verbose output", async () => {
|
||||
it('should decode an array of txids, with a verbose output', async () => {
|
||||
const txid = [
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
|
||||
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
|
||||
'23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1',
|
||||
'b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f'
|
||||
]
|
||||
const verbose = true
|
||||
|
||||
@@ -170,54 +158,55 @@ describe("#rawtransaction", () => {
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
'hex',
|
||||
'txid',
|
||||
'hash',
|
||||
'size',
|
||||
'version',
|
||||
'locktime',
|
||||
'vin',
|
||||
'vout',
|
||||
'blockhash',
|
||||
'confirmations',
|
||||
'time',
|
||||
'blocktime'
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
'23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeScript", () => {
|
||||
it("should decode script for a single hex", async () => {
|
||||
describe('#decodeScript', () => {
|
||||
it('should decode script for a single hex', async () => {
|
||||
const hex =
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
|
||||
'4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16'
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeScript(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["asm", "type", "p2sh"])
|
||||
assert.property(result, 'asm')
|
||||
assert.property(result, 'type')
|
||||
assert.property(result, 'p2sh')
|
||||
})
|
||||
|
||||
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
|
||||
@@ -232,22 +221,6 @@ describe("#rawtransaction", () => {
|
||||
const result = await bchjs.RawTransactions.decodeScript(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
*/
|
||||
/*
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
@@ -257,29 +230,29 @@ describe("#rawtransaction", () => {
|
||||
below expect error messages returned from the server, but at least test
|
||||
that the server is responding on those endpoints, and responds consistently.
|
||||
*/
|
||||
describe("sendRawTransaction", () => {
|
||||
it("should send a single transaction hex", async () => {
|
||||
describe('#sendRawTransaction', () => {
|
||||
it('should send a single transaction hex', async () => {
|
||||
try {
|
||||
const hex =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
|
||||
it("should send an array of tx hexes", async () => {
|
||||
it('should send an array of tx hexes', async () => {
|
||||
try {
|
||||
const hexes = [
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000",
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
|
||||
@@ -287,41 +260,89 @@ describe("#rawtransaction", () => {
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hex must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
assert.hasAnyKeys(err, ['error'])
|
||||
assert.include(err.error, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_getInputAddrs', () => {
|
||||
it('should return an array of input addresses', async () => {
|
||||
// const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112'
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const txDetails = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
true
|
||||
)
|
||||
|
||||
const result = await bchjs.RawTransactions._getInputAddrs(txDetails)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 1)
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'value')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTxData', () => {
|
||||
it('should return tx data with input addresses', async () => {
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
})
|
||||
})
|
||||
|
||||
// describe('#getTxDataSlp', () => {
|
||||
// it('should return tx data with SLP information', async () => {
|
||||
// const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
|
||||
//
|
||||
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.property(result.vin[0], 'address')
|
||||
// assert.property(result.vin[0], 'tokenQty')
|
||||
//
|
||||
// assert.equal(result.vin[0].tokenQty, null)
|
||||
// assert.equal(result.vin[1].tokenQty, '100000000000')
|
||||
// })
|
||||
//
|
||||
// it('should handle non-slp tx', async () => {
|
||||
// const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
|
||||
//
|
||||
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.equal(result, false)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
+616
-584
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export RESTURL=https://free-main.fullstack.cash/v3/
|
||||
#export RESTURL=http://localhost:3000/v3/
|
||||
|
||||
cd test/integration/
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
Integration tests for the Blockbook library.
|
||||
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v3/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
// const bchjs = new BCHJS({ restURL: RESTURL })
|
||||
//const axios = require("axios")
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#Blockbook`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result.txids)
|
||||
})
|
||||
|
||||
it(`should POST request balances for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result[0].txids)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.balance(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0])
|
||||
assert.hasAnyKeys(result[0][0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,308 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
- getMempoolEntry() only works on TXs in the mempool, so it needs to be part
|
||||
of an e2e test to be properly tested.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v3/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#blockchain`, () => {
|
||||
describe(`#getBestBlockHash`, () => {
|
||||
it(`should GET best block hash`, async () => {
|
||||
const result = await bchjs.Blockchain.getBestBlockHash()
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result.length, 64, "Specific hash length")
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockHeader", () => {
|
||||
it(`should GET block header for a single hash`, async () => {
|
||||
const hash =
|
||||
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf"
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should GET block headers for an array of hashes`, async () => {
|
||||
const hash = [
|
||||
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf",
|
||||
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const hash = 12345
|
||||
|
||||
await bchjs.Blockchain.getBlockHeader(hash)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hash must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2"
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(data)
|
||||
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolEntry", () => {
|
||||
/*
|
||||
// To run this test, the txid must be unconfirmed.
|
||||
const txid =
|
||||
"defea04c38ee00cf73ad402984714ed22dc0dd99b2ae5cb50d791d94343ba79b"
|
||||
|
||||
it(`should GET single mempool entry`, async () => {
|
||||
const result = await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"size",
|
||||
"fee",
|
||||
"modifiedfee",
|
||||
"time",
|
||||
"height",
|
||||
"startingpriority",
|
||||
"currentpriority",
|
||||
"descendantcount",
|
||||
"descendantsize",
|
||||
"descendantfees",
|
||||
"ancestorcount",
|
||||
"ancestorsize",
|
||||
"ancestorfees",
|
||||
"depends"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should get an array of mempool entries`, async () => {
|
||||
const result = await bchjs.Blockchain.getMempoolEntry([txid, txid])
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"size",
|
||||
"fee",
|
||||
"modifiedfee",
|
||||
"time",
|
||||
"height",
|
||||
"startingpriority",
|
||||
"currentpriority",
|
||||
"descendantcount",
|
||||
"descendantsize",
|
||||
"descendantfees",
|
||||
"ancestorcount",
|
||||
"ancestorsize",
|
||||
"ancestorfees",
|
||||
"depends"
|
||||
])
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw an error if txid is not in mempool`, async () => {
|
||||
try {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, `Transaction not in mempool`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#getTxOutProof`, () => {
|
||||
it(`should get single tx out proof`, async () => {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it(`should get an array of tx out proofs`, async () => {
|
||||
const txid = [
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04",
|
||||
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#verifyTxOutProof`, () => {
|
||||
const mockTxOutProof =
|
||||
"00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135"
|
||||
|
||||
it(`should verify a single proof`, async () => {
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752"
|
||||
)
|
||||
})
|
||||
|
||||
it(`should verify an array of proofs`, async () => {
|
||||
const proofs = [mockTxOutProof, mockTxOutProof]
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752"
|
||||
)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.verifyTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
|
||||
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
Integration tests for bchjs control library.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#control`, () => {
|
||||
describe(`#getNetworkInfo`, () => {
|
||||
it("should get info on the full node", async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "version")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,338 +0,0 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0], "value")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0].utxos[0], "value")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result.balance, "confirmed")
|
||||
assert.property(result.balance, "unconfirmed")
|
||||
})
|
||||
|
||||
it(`should POST request for balances for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balances")
|
||||
assert.isArray(result.balances)
|
||||
|
||||
assert.property(result.balances[0], "address")
|
||||
assert.property(result.balances[0], "balance")
|
||||
assert.property(result.balances[0].balance, "confirmed")
|
||||
assert.property(result.balances[0].balance, "unconfirmed")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#transactions`, () => {
|
||||
it(`should GET transaction history for a single address`, async () => {
|
||||
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
assert.property(result.transactions[0], "height")
|
||||
assert.property(result.transactions[0], "tx_hash")
|
||||
})
|
||||
|
||||
it(`should POST request for transaction history for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
|
||||
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "address")
|
||||
assert.property(result.transactions[0], "transactions")
|
||||
assert.isArray(result.transactions[0].transactions)
|
||||
assert.property(result.transactions[0].transactions[0], "height")
|
||||
assert.property(result.transactions[0].transactions[0], "tx_hash")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#unconfirmed`, () => {
|
||||
// These tests won't work because unconfirmed transactions are transient in nature.
|
||||
/*
|
||||
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
|
||||
const addr = "bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd"
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "fee")
|
||||
})
|
||||
|
||||
it(`should POST unconfirmed UTXO details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd",
|
||||
"bchtest:qpkl3xylrjx4jup6m66e7zg7whlaucsxeudxeqawdj"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "fee")
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
await bchjs.Electrumx.unconfirmed(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should GET block headers for given height and count`, async () => {
|
||||
const height = 42
|
||||
const count = 2
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height, count)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 2)
|
||||
})
|
||||
|
||||
it(`should GET block headers for given height with default count = 1`, async () => {
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid = "76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
const txids = [
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee",
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
txids.push("76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee")
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,65 +0,0 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ ninsightURL: "https://trest.bitcoin.com/v2" })
|
||||
|
||||
describe(`#Ninsight`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr"
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0].utxos[0], "txid")
|
||||
assert.property(result[0].utxos[0], "vout")
|
||||
assert.property(result[0].utxos[0], "amount")
|
||||
assert.property(result[0].utxos[0], "satoshis")
|
||||
assert.property(result[0].utxos[0], "height")
|
||||
assert.property(result[0].utxos[0], "confirmations")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
|
||||
"bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
// assert.hasAnyKeys(result[0][0], [
|
||||
// "txid",
|
||||
// "vout",
|
||||
// "value",
|
||||
// "height",
|
||||
// "confirmations"
|
||||
// ])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
tests for OpenBazaar library.
|
||||
*/
|
||||
|
||||
// Force testnet call to OB1 servers.
|
||||
process.env.NETWORK = "testnet"
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#OpenBazaar`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
// it(`should GET balance for a single address`, async () => {
|
||||
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.balance(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAllKeys(result, [
|
||||
// "page",
|
||||
// "totalPages",
|
||||
// "itemsOnPage",
|
||||
// "addrStr",
|
||||
// "balance",
|
||||
// "totalReceived",
|
||||
// "totalSent",
|
||||
// "unconfirmedBalance",
|
||||
// "unconfirmedTxApperances",
|
||||
// "txApperances",
|
||||
// "transactions"
|
||||
// ])
|
||||
// assert.isArray(result.transactions)
|
||||
// })
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.balance(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
// it(`should GET utxos for a single address`, async () => {
|
||||
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.utxo(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.isArray(result)
|
||||
// assert.hasAllKeys(result[0], [
|
||||
// "txid",
|
||||
// "vout",
|
||||
// "amount",
|
||||
// "height",
|
||||
// "confirmations",
|
||||
// "satoshis"
|
||||
// ])
|
||||
// })
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.tx(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txid must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
// it(`should GET transactions for a single address`, async () => {
|
||||
// const addr =
|
||||
// "ed4692f50a4553527dd26cd8674ca06a0ab2d366f3135ca3668310467ead3cbf"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.tx(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAnyKeys(result, [
|
||||
// "txid",
|
||||
// "version",
|
||||
// "vin",
|
||||
// "vout",
|
||||
// "blockhash",
|
||||
// "blockheight",
|
||||
// "confirmations",
|
||||
// "blocktime",
|
||||
// "valueOut",
|
||||
// "valueIn",
|
||||
// "fees",
|
||||
// "hex"
|
||||
// ])
|
||||
// assert.isArray(result.vin)
|
||||
// assert.isArray(result.vout)
|
||||
// })
|
||||
})
|
||||
})
|
||||
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v3/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe("#rawtransaction", () => {
|
||||
describe("#decodeRawTransaction", () => {
|
||||
it("should decode tx for a single hex", async () => {
|
||||
const hex =
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of tx hexes", async () => {
|
||||
const hexes = [
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000",
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getRawTransaction", () => {
|
||||
it("should decode a single txid, with concise output", async () => {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
const verbose = false
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it("should decode a single txid, with verbose output", async () => {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
const verbose = true
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a concise output", async () => {
|
||||
const txid = [
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04",
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
]
|
||||
const verbose = false
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a verbose output", async () => {
|
||||
const txid = [
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04",
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
]
|
||||
const verbose = true
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw error on array size limit`, async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeScript", () => {
|
||||
it("should decode script for a single hex", async () => {
|
||||
const hex =
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeScript(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["asm", "type", "p2sh"])
|
||||
})
|
||||
|
||||
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
|
||||
// https://github.com/Bitcoin-com/rest.bitcoin.com/pull/312
|
||||
/*
|
||||
it("should decode an array of tx hexes", async () => {
|
||||
const hexes = [
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16",
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeScript(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
*/
|
||||
/*
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
/*
|
||||
Testing sentRawTransaction isn't really possible with an integration test,
|
||||
as the endpoint really needs an e2e test to be properly tested. The tests
|
||||
below expect error messages returned from the server, but at least test
|
||||
that the server is responding on those endpoints, and responds consistently.
|
||||
*/
|
||||
describe("sendRawTransaction", () => {
|
||||
it("should send a single transaction hex", async () => {
|
||||
try {
|
||||
const hex =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it("should send an array of tx hexes", async () => {
|
||||
try {
|
||||
const hexes = [
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000",
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hex must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs covering SLP tokens.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v3/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 1
|
||||
}
|
||||
|
||||
describe(`#SLP`, () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#util", () => {
|
||||
it(`should get information on the Oasis token`, async () => {
|
||||
const tokenId = `a371e9934c7695d08a5eb7f31d3bceb4f3644860cc67520cda1e149423b9ec39`
|
||||
|
||||
const result = await bchjs.SLP.Utils.list(tokenId)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"decimals",
|
||||
"timestamp",
|
||||
"timestamp_unix",
|
||||
"versionType",
|
||||
"documentUri",
|
||||
"symbol",
|
||||
"name",
|
||||
"containsBaton",
|
||||
"id",
|
||||
"documentHash",
|
||||
"initialTokenQty",
|
||||
"blockCreated",
|
||||
"blockLastActiveSend",
|
||||
"blockLastActiveMint",
|
||||
"txnsSinceGenesis",
|
||||
"validAddress",
|
||||
"totalMinted",
|
||||
"totalBurned",
|
||||
"circulatingSupply",
|
||||
"mintingBatonStatus"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeOpReturn", () => {
|
||||
it("should decode the OP_RETURN for a SEND txid", async () => {
|
||||
const txid =
|
||||
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1"
|
||||
|
||||
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["amounts", "tokenType", "tokenId", "txType"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#balancesForAddress", () => {
|
||||
it(`should throw an error if input is not a string or array of strings`, async () => {
|
||||
try {
|
||||
const address = 1234
|
||||
|
||||
await bchjs.SLP.Utils.balancesForAddress(address)
|
||||
|
||||
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
//console.log(`Error: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
||||
// Mock the call to rest.bitcoin.com
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
.stub(axios, "get")
|
||||
.resolves({ data: mockData.balancesForAddress })
|
||||
}
|
||||
|
||||
const balances = await bchjs.SLP.Utils.balancesForAddress(
|
||||
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
|
||||
)
|
||||
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
assert.isArray(balances)
|
||||
assert.hasAllKeys(balances[0], [
|
||||
"tokenId",
|
||||
"balanceString",
|
||||
"balance",
|
||||
"decimalCount",
|
||||
"slpAddress"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should fetch balances for multiple addresses`, async () => {
|
||||
const addresses = [
|
||||
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9",
|
||||
"slptest:qr5uy4h8ysyhwkp9s245scckdnc7teyjqc5ft2z43h"
|
||||
]
|
||||
|
||||
// Mock the call to rest.bitcoin.com
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
.stub(axios, "post")
|
||||
.resolves({ data: mockData.balancesForAddresses })
|
||||
}
|
||||
|
||||
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
|
||||
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
assert.isArray(balances)
|
||||
assert.isArray(balances[0])
|
||||
assert.hasAllKeys(balances[0][0], [
|
||||
"tokenId",
|
||||
"balanceString",
|
||||
"balance",
|
||||
"decimalCount",
|
||||
"slpAddress"
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export RESTURL=https://free-test.fullstack.cash/v3/
|
||||
export NETWORK=testnet
|
||||
#export RESTURL=http://localhost:3000/v3/
|
||||
|
||||
cd test/integration/testnet/
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js openbazaar.js rawtransaction.js slp.js util.js
|
||||
#mocha --timeout 30000 blockchain.js
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v3/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#util`, () => {
|
||||
describe(`#validateAddress`, () => {
|
||||
it(`should return false for testnet addr on mainnet`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should return false for bad address`, async () => {
|
||||
const address = `bchtest:qqqk4y6lsl5da64sg53xezmplyu5kmpyz2ysaa5y`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should validate valid address`, async () => {
|
||||
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
assert.equal(result.isvalid, true)
|
||||
})
|
||||
|
||||
it(`should validate an array of addresses`, async () => {
|
||||
const address = [
|
||||
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`,
|
||||
`bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh`
|
||||
]
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const address = 15432
|
||||
|
||||
await bchjs.Util.validateAddress(address)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock = `bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh`
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.Util.validateAddress(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Integration tests for the transaction.js library.
|
||||
*/
|
||||
|
||||
import chai from 'chai'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#Transaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
|
||||
const txid =
|
||||
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'txid')
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should handle a coinbase transaction', async () => {
|
||||
const txid = 'cca1d2dd3a533d2f501448dec03face2cb2814afd59a533a611e9a2909f2302b'
|
||||
|
||||
const details = await bchjs.Transaction.get(txid)
|
||||
// console.log(`details: ${JSON.stringify(details, null, 2)}`)
|
||||
|
||||
assert.property(details.txData, 'txid')
|
||||
})
|
||||
})
|
||||
|
||||
if (process.env.TESTSLP) {
|
||||
describe('#getOld', () => {
|
||||
it('should get details about a non-SLP transaction', async () => {
|
||||
const txid =
|
||||
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[0].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, false)
|
||||
})
|
||||
|
||||
it('should get details about a SLP transaction', async () => {
|
||||
const txid =
|
||||
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vout[0], 'tokenQty')
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, true)
|
||||
})
|
||||
|
||||
// it('should get problematic transaction', async () => {
|
||||
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
|
||||
//
|
||||
// const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// })
|
||||
|
||||
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
|
||||
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
|
||||
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
|
||||
// but for separate tokens.
|
||||
it('should get problematic transaction', async () => {
|
||||
const txid =
|
||||
'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// The token ID should equal the txid for this Vin.
|
||||
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#util`, () => {
|
||||
describe(`#validateAddress`, () => {
|
||||
it(`should return false for testnet addr on mainnet`, async () => {
|
||||
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should return false for bad address`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should validate valid address`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
assert.equal(result.isvalid, true)
|
||||
})
|
||||
|
||||
it(`should validate an array of addresses`, async () => {
|
||||
const address = [
|
||||
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`,
|
||||
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
]
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const address = 15432
|
||||
|
||||
await bchjs.Util.validateAddress(address)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.Util.validateAddress(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Integration tests for the utxo.js library.
|
||||
*/
|
||||
|
||||
import chai from 'chai'
|
||||
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const { assert } = chai
|
||||
const bchjs = new BCHJS()
|
||||
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
// sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.hydrateTokenData(utxos)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result[0], 'ticker')
|
||||
assert.property(result[0], 'name')
|
||||
assert.property(result[0], 'qtyStr')
|
||||
assert.property(result[0], 'documentUri')
|
||||
assert.property(result[0], 'documentHash')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should hydrate address with BCH and SLP UTXOs', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert expected properties exist.
|
||||
assert.property(result, 'address')
|
||||
assert.property(result, 'bchUtxos')
|
||||
assert.property(result, 'slpUtxos')
|
||||
assert.property(result.slpUtxos, 'type1')
|
||||
assert.property(result.slpUtxos, 'nft')
|
||||
assert.property(result, 'nullUtxos')
|
||||
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.isAbove(result.infoUtxos.length, 0)
|
||||
assert.isAbove(result.slpUtxos.type1.tokens.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should handle Type1 minting batons', async () => {
|
||||
const addr = 'simpleledger:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpv4awvv7m7'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that minting batons are correctly identified.
|
||||
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should return UTXOs for address with no SLP tokens', async () => {
|
||||
const addr = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
})
|
||||
|
||||
it('should filter Group tokens and mint batons', async () => {
|
||||
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result.slpUtxos.group.tokens.length, 0)
|
||||
assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should filter NFTs', async () => {
|
||||
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result.slpUtxos.nft.tokens.length, 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#isValid', () => {
|
||||
it('should return true for valid UTXO with fullnode properties', async () => {
|
||||
const utxo = {
|
||||
txid: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
|
||||
vout: 0
|
||||
}
|
||||
|
||||
const result = await bchjs.Utxo.isValid(utxo)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return true for valid UTXO with fulcrum properties', async () => {
|
||||
const utxo = {
|
||||
tx_hash: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
|
||||
tx_pos: 0
|
||||
}
|
||||
|
||||
const result = await bchjs.Utxo.isValid(utxo)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return true for valid UTXO with fullnode properties', async () => {
|
||||
const utxo = {
|
||||
txid: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
|
||||
vout: 0
|
||||
}
|
||||
|
||||
const result = await bchjs.Utxo.isValid(utxo)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return true for valid UTXO with fulcrum properties', async () => {
|
||||
const utxo = {
|
||||
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
|
||||
tx_pos: 0
|
||||
}
|
||||
|
||||
const result = await bchjs.Utxo.isValid(utxo)
|
||||
// console.log('result: ', result
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should process output of Utxo.get()', async () => {
|
||||
const utxo = await bchjs.Utxo.get('bitcoincash:qqgahqa5zkknmhmvsc98jndpwn3r77gqgc02x03jce')
|
||||
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
const result = await bchjs.Utxo.isValid(utxo.bchUtxos[0])
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
+852
-776
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
|
||||
// Used for debugging and iterrogating JS objects.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = { depth: 1 }
|
||||
|
||||
describe(`#BitboxShim`, () => {
|
||||
it("should create the shim library", () => {
|
||||
const BitboxShim = BCHJS.BitboxShim()
|
||||
const bitbox = new BitboxShim()
|
||||
|
||||
//console.log(`bitbox.Address: ${util.inspect(bitbox.Address)}`)
|
||||
|
||||
assert.hasAnyKeys(bitbox.Address, [
|
||||
// "details",
|
||||
// "utxo",
|
||||
// "unconfirmed",
|
||||
// "transactions",
|
||||
// "toLegacyAddress"
|
||||
"restURL"
|
||||
])
|
||||
})
|
||||
})
|
||||
+86
-76
@@ -1,7 +1,15 @@
|
||||
const fixtures = require("./fixtures/bitcoincash.json")
|
||||
const assert = require("assert")
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
// Public npm libraries
|
||||
import assert from 'assert'
|
||||
|
||||
// Mocks
|
||||
import { createRequire } from 'module'
|
||||
|
||||
// Unit under test (uut)
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const require = createRequire(import.meta.url)
|
||||
const fixtures = require('./fixtures/bitcoincash.json')
|
||||
// const bchjs = new BCHJS()
|
||||
let bchjs
|
||||
|
||||
// TODO
|
||||
// 1. generate testnet p2sh
|
||||
@@ -19,29 +27,27 @@ const bchjs = new BCHJS()
|
||||
// * confirm xpriv generates WIF
|
||||
// 6. More error test cases.
|
||||
|
||||
describe("#BitcoinCash", () => {
|
||||
describe("price conversion", () => {
|
||||
describe("#toBitcoinCash", () => {
|
||||
describe('#BitcoinCash', () => {
|
||||
beforeEach(() => {
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('price conversion', () => {
|
||||
it('should exercise toBitcoinCash', () => {
|
||||
fixtures.conversion.toBCH.satoshis.forEach(satoshi => {
|
||||
it(`should convert ${satoshi[0]} Satoshis to ${
|
||||
satoshi[1]
|
||||
} $BCH`, () => {
|
||||
it(`should convert ${satoshi[0]} Satoshis to ${satoshi[1]} $BCH`, () => {
|
||||
assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1])
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.conversion.toBCH.strings.forEach(satoshi => {
|
||||
it(`should convert "${satoshi[0]}" Satoshis as a string to ${
|
||||
satoshi[1]
|
||||
} $BCH`, () => {
|
||||
it(`should convert "${satoshi[0]}" Satoshis as a string to ${satoshi[1]} $BCH`, () => {
|
||||
assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1])
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.conversion.toBCH.not.forEach(bch => {
|
||||
it(`converts ${bch[0]} to Bitcoin Cash, not to ${
|
||||
bch[1]
|
||||
} Satoshi`, () => {
|
||||
it(`converts ${bch[0]} to Bitcoin Cash, not to ${bch[1]} Satoshi`, () => {
|
||||
assert.notEqual(bchjs.BitcoinCash.toBitcoinCash(bch[0]), bch[1])
|
||||
})
|
||||
})
|
||||
@@ -53,7 +59,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toSatoshi", () => {
|
||||
it('should exercise toSatoshi', () => {
|
||||
fixtures.conversion.toSatoshi.bch.forEach(bch => {
|
||||
it(`should convert ${bch[0]} $BCH to ${bch[1]} Satoshis`, () => {
|
||||
assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1])
|
||||
@@ -61,17 +67,13 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
|
||||
fixtures.conversion.toSatoshi.strings.forEach(bch => {
|
||||
it(`should convert "${bch[0]}" $BCH as a string to ${
|
||||
bch[1]
|
||||
} Satoshis`, () => {
|
||||
it(`should convert "${bch[0]}" $BCH as a string to ${bch[1]} Satoshis`, () => {
|
||||
assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1])
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.conversion.toSatoshi.not.forEach(satoshi => {
|
||||
it(`converts ${satoshi[0]} to Satoshi, not to ${
|
||||
satoshi[1]
|
||||
} Bitcoin Cash`, () => {
|
||||
it(`converts ${satoshi[0]} to Satoshi, not to ${satoshi[1]} Bitcoin Cash`, () => {
|
||||
assert.notEqual(bchjs.BitcoinCash.toSatoshi(satoshi[0]), satoshi[1])
|
||||
})
|
||||
})
|
||||
@@ -83,7 +85,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#satsToBits", () => {
|
||||
it('should exercise satsToBits', () => {
|
||||
fixtures.conversion.satsToBits.bch.forEach(bch => {
|
||||
it(`should convert ${bch[0]} BCH to ${bch[1]} bits`, () => {
|
||||
assert.equal(
|
||||
@@ -94,9 +96,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
|
||||
fixtures.conversion.satsToBits.strings.forEach(bch => {
|
||||
it(`should convert "${bch[0]}" BCH as a string to ${
|
||||
bch[1]
|
||||
} bits`, () => {
|
||||
it(`should convert "${bch[0]}" BCH as a string to ${bch[1]} bits`, () => {
|
||||
assert.equal(
|
||||
bchjs.BitcoinCash.satsToBits(bchjs.BitcoinCash.toSatoshi(bch[0])),
|
||||
bch[1]
|
||||
@@ -120,8 +120,8 @@ describe("#BitcoinCash", () => {
|
||||
// });
|
||||
})
|
||||
|
||||
describe("sign and verify messages", () => {
|
||||
describe("#signMessageWithPrivKey", () => {
|
||||
describe('sign and verify messages', () => {
|
||||
it('should exercise signMessageWithPrivKey', () => {
|
||||
fixtures.signatures.sign.forEach(sign => {
|
||||
it(`should sign a message w/ ${sign.network} ${sign.privateKeyWIF}`, () => {
|
||||
const privateKeyWIF = sign.privateKeyWIF
|
||||
@@ -135,7 +135,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#verifyMessage", () => {
|
||||
it('shoudl exercise verifyMessage', () => {
|
||||
fixtures.signatures.verify.forEach(sign => {
|
||||
it(`should verify a valid signed message from ${sign.network} cashaddr address ${sign.address}`, () => {
|
||||
assert.equal(
|
||||
@@ -149,28 +149,30 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.signatures.verify.forEach(sign => {
|
||||
const legacyAddress = bchjs.Address.toLegacyAddress(sign.address)
|
||||
it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => {
|
||||
assert.equal(
|
||||
bchjs.BitcoinCash.verifyMessage(
|
||||
legacyAddress,
|
||||
sign.signature,
|
||||
sign.message
|
||||
),
|
||||
true
|
||||
)
|
||||
it('should verify legacy addresses', () => {
|
||||
fixtures.signatures.verify.forEach(sign => {
|
||||
const legacyAddress = bchjs.Address.toLegacyAddress(sign.address)
|
||||
it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => {
|
||||
assert.equal(
|
||||
bchjs.BitcoinCash.verifyMessage(
|
||||
legacyAddress,
|
||||
sign.signature,
|
||||
sign.message
|
||||
),
|
||||
true
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.signatures.verify.forEach(sign => {
|
||||
const legacyAddress = bchjs.Address.toLegacyAddress(sign.address)
|
||||
// const legacyAddress = bchjs.Address.toLegacyAddress(sign.address)
|
||||
it(`should not verify an invalid signed message from ${sign.network} cashaddr address ${sign.address}`, () => {
|
||||
assert.equal(
|
||||
bchjs.BitcoinCash.verifyMessage(
|
||||
sign.address,
|
||||
sign.signature,
|
||||
"nope"
|
||||
'nope'
|
||||
),
|
||||
false
|
||||
)
|
||||
@@ -179,8 +181,8 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("encode and decode to base58Check", () => {
|
||||
describe("#encodeBase58Check", () => {
|
||||
describe('encode and decode to base58Check', () => {
|
||||
describe('#encodeBase58Check', () => {
|
||||
fixtures.encodeBase58Check.forEach((base58Check, i) => {
|
||||
it(`encode ${base58Check.hex} as base58Check ${base58Check.base58Check}`, () => {
|
||||
assert.equal(
|
||||
@@ -191,7 +193,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeBase58Check", () => {
|
||||
describe('#decodeBase58Check', () => {
|
||||
fixtures.encodeBase58Check.forEach((base58Check, i) => {
|
||||
it(`decode ${base58Check.base58Check} as ${base58Check.hex}`, () => {
|
||||
assert.equal(
|
||||
@@ -203,8 +205,8 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("encode and decode BIP21 urls", () => {
|
||||
describe("#encodeBIP21", () => {
|
||||
describe('encode and decode BIP21 urls', () => {
|
||||
describe('#encodeBIP21', () => {
|
||||
fixtures.bip21.valid.forEach((bip21, i) => {
|
||||
it(`encode ${bip21.address} as url`, () => {
|
||||
const url = bchjs.BitcoinCash.encodeBIP21(
|
||||
@@ -226,7 +228,7 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeBIP21", () => {
|
||||
describe('#decodeBIP21', () => {
|
||||
fixtures.bip21.valid.forEach((bip21, i) => {
|
||||
it(`decodes ${bip21.url}`, () => {
|
||||
const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url)
|
||||
@@ -252,9 +254,9 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getByteCount", () => {
|
||||
describe('#getByteCount', () => {
|
||||
fixtures.getByteCount.forEach(fixture => {
|
||||
it(`get byte count`, () => {
|
||||
it('get byte count', () => {
|
||||
const byteCount = bchjs.BitcoinCash.getByteCount(
|
||||
fixture.inputs,
|
||||
fixture.outputs
|
||||
@@ -264,10 +266,15 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#bip38", () => {
|
||||
describe("#encryptBIP38", () => {
|
||||
describe('#bip38', function () {
|
||||
// Increase timeout for BIP38 tests as they use CPU-intensive scrypt operations
|
||||
// Each encrypt/decrypt operation takes ~5-6 seconds in Node.js v22
|
||||
this.timeout(60000)
|
||||
|
||||
describe('#encryptBIP38', () => {
|
||||
fixtures.bip38.encrypt.mainnet.forEach(fixture => {
|
||||
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => {
|
||||
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, function () {
|
||||
this.timeout(60000)
|
||||
const encryptedKey = bchjs.BitcoinCash.encryptBIP38(
|
||||
fixture.wif,
|
||||
fixture.password
|
||||
@@ -276,39 +283,42 @@ describe("#BitcoinCash", () => {
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.bip38.encrypt.testnet.forEach(fixture => {
|
||||
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on testnet`, () => {
|
||||
const encryptedKey = bchjs.BitcoinCash.encryptBIP38(
|
||||
fixture.wif,
|
||||
fixture.password
|
||||
)
|
||||
assert.equal(encryptedKey, fixture.encryptedKey)
|
||||
})
|
||||
})
|
||||
// fixtures.bip38.encrypt.testnet.forEach(fixture => {
|
||||
// it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on testnet`, function () {
|
||||
// this.timeout(60000)
|
||||
// const encryptedKey = bchjs.BitcoinCash.encryptBIP38(
|
||||
// fixture.wif,
|
||||
// fixture.password
|
||||
// )
|
||||
// assert.equal(encryptedKey, fixture.encryptedKey)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
describe("#decryptBIP38", () => {
|
||||
describe('#decryptBIP38', () => {
|
||||
fixtures.bip38.decrypt.mainnet.forEach(fixture => {
|
||||
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => {
|
||||
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, function () {
|
||||
this.timeout(60000)
|
||||
const wif = bchjs.BitcoinCash.decryptBIP38(
|
||||
fixture.encryptedKey,
|
||||
fixture.password,
|
||||
"mainnet"
|
||||
'mainnet'
|
||||
)
|
||||
assert.equal(wif, fixture.wif)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.bip38.decrypt.testnet.forEach(fixture => {
|
||||
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on testnet`, () => {
|
||||
const wif = bchjs.BitcoinCash.decryptBIP38(
|
||||
fixture.encryptedKey,
|
||||
fixture.password,
|
||||
"testnet"
|
||||
)
|
||||
assert.equal(wif, fixture.wif)
|
||||
})
|
||||
})
|
||||
// fixtures.bip38.decrypt.testnet.forEach(fixture => {
|
||||
// it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on testnet`, function () {
|
||||
// this.timeout(60000)
|
||||
// const wif = bchjs.BitcoinCash.decryptBIP38(
|
||||
// fixture.encryptedKey,
|
||||
// fixture.password,
|
||||
// 'testnet'
|
||||
// )
|
||||
// assert.equal(wif, fixture.wif)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
const axios = require("axios")
|
||||
const sinon = require("sinon")
|
||||
|
||||
const mockData = require("./fixtures/blockbook-mock")
|
||||
|
||||
describe(`#Blockbook`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#Balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.balance })
|
||||
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result.txids)
|
||||
})
|
||||
|
||||
it(`should POST request balances for an array of addresses`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox
|
||||
.stub(axios, "post")
|
||||
.resolves({ data: [mockData.balance, mockData.balance] })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result[0].txids)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox
|
||||
.stub(axios, "post")
|
||||
.throws(`Input address must be a string or array of strings`)
|
||||
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.balance(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
/*
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").throws(`Array too large`)
|
||||
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
|
||||
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0])
|
||||
assert.hasAnyKeys(result[0][0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox
|
||||
.stub(axios, "post")
|
||||
.throws(`Input address must be a string or array of strings`)
|
||||
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
/*
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should GET tx details for a single txid`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
|
||||
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
// it(`should POST utxo details for an array of addresses`, async () => {
|
||||
// // Stub the network call.
|
||||
// sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
|
||||
//
|
||||
// const addr = [
|
||||
// "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
// "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
// ]
|
||||
//
|
||||
// const result = await bchjs.Blockbook.utxo(addr)
|
||||
// //console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.isArray(result)
|
||||
// assert.isArray(result[0])
|
||||
// assert.hasAnyKeys(result[0][0], [
|
||||
// "txid",
|
||||
// "vout",
|
||||
// "value",
|
||||
// "height",
|
||||
// "confirmations"
|
||||
// ])
|
||||
// })
|
||||
|
||||
// it(`should throw an error for improper input`, async () => {
|
||||
// try {
|
||||
// // Stub the network call.
|
||||
// sandbox
|
||||
// .stub(axios, "post")
|
||||
// .throws(`Input address must be a string or array of strings`)
|
||||
//
|
||||
// const addr = 12345
|
||||
//
|
||||
// await bchjs.Blockbook.utxo(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
// } catch (err) {
|
||||
// //console.log(`err: `, err)
|
||||
// assert.include(
|
||||
// err.message,
|
||||
// `Input address must be a string or array of strings`
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
|
||||
/*
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
})
|
||||
+174
-149
@@ -1,102 +1,127 @@
|
||||
const assert = require("assert")
|
||||
const assert2 = require("chai").assert
|
||||
const axios = require("axios")
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import assert from 'assert'
|
||||
import chai from 'chai'
|
||||
import axios from 'axios'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
import sinon from 'sinon'
|
||||
|
||||
import mockData from './fixtures/blockchain-mock.js'
|
||||
const { assert: assert2 } = chai
|
||||
const bchjs = new BCHJS()
|
||||
const sinon = require("sinon")
|
||||
|
||||
const mockData = require("./fixtures/blockchain-mock")
|
||||
|
||||
describe("#Blockchain", () => {
|
||||
describe("#getBestBlockHash", () => {
|
||||
describe('#Blockchain', () => {
|
||||
describe('#getBestBlockHash', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
it("should get best block hash", done => {
|
||||
const resolved = new Promise(r =>
|
||||
r({
|
||||
it('should get best block hash', done => {
|
||||
const resolved = new Promise(resolve =>
|
||||
resolve({
|
||||
data:
|
||||
"0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d"
|
||||
'0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d'
|
||||
})
|
||||
)
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBestBlockHash()
|
||||
.then(result => {
|
||||
const hash =
|
||||
"0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d"
|
||||
assert.equal(hash, result)
|
||||
'0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d'
|
||||
assert.strictEqual(hash, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlock", () => {
|
||||
describe('#getBlock', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = {
|
||||
hash: "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09",
|
||||
hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
confirmations: 526807,
|
||||
size: 216,
|
||||
height: 1000,
|
||||
version: 1,
|
||||
versionHex: "00000001",
|
||||
versionHex: '00000001',
|
||||
merkleroot:
|
||||
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33",
|
||||
tx: ["fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"],
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
tx: ['fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'],
|
||||
time: 1232346882,
|
||||
mediantime: 1232344831,
|
||||
nonce: 2595206198,
|
||||
bits: "1d00ffff",
|
||||
bits: '1d00ffff',
|
||||
difficulty: 1,
|
||||
chainwork:
|
||||
"000000000000000000000000000000000000000000000000000003e903e903e9",
|
||||
'000000000000000000000000000000000000000000000000000003e903e903e9',
|
||||
previousblockhash:
|
||||
"0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d",
|
||||
'0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
nextblockhash:
|
||||
"00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6"
|
||||
'00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
|
||||
}
|
||||
|
||||
it("should get block by hash", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get block by hash', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'post').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlock(
|
||||
"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"
|
||||
)
|
||||
const blockhash =
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
|
||||
bchjs.Blockchain.getBlock(blockhash)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
it('should throw error if blockhash is not provided', async () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getBlock()
|
||||
assert2.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert2.include(err.message, 'blockhash must be a string')
|
||||
}
|
||||
})
|
||||
it('should handle response error', async () => {
|
||||
try {
|
||||
const error = new Error()
|
||||
error.response = {
|
||||
data: 'Test Error'
|
||||
}
|
||||
sandbox.stub(axios, 'post').throws(error)
|
||||
|
||||
const blockhash =
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
|
||||
await bchjs.Blockchain.getBlock(blockhash)
|
||||
assert2.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert2.include(err.message, 'Test Error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockchainInfo", () => {
|
||||
describe('#getBlockchainInfo', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = {
|
||||
chain: "main",
|
||||
chain: 'main',
|
||||
blocks: 527810,
|
||||
headers: 527810,
|
||||
bestblockhash:
|
||||
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0",
|
||||
'000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
|
||||
difficulty: 576023394804.6666,
|
||||
mediantime: 1524878499,
|
||||
verificationprogress: 0.9999990106793685,
|
||||
chainwork:
|
||||
"00000000000000000000000000000000000000000096da5b040913fa09249b4e",
|
||||
'00000000000000000000000000000000000000000096da5b040913fa09249b4e',
|
||||
pruned: false,
|
||||
softforks: [
|
||||
{ id: "bip34", version: 2, reject: [Object] },
|
||||
{ id: "bip66", version: 3, reject: [Object] },
|
||||
{ id: "bip65", version: 4, reject: [Object] }
|
||||
{ id: 'bip34', version: 2, reject: [Object] },
|
||||
{ id: 'bip66', version: 3, reject: [Object] },
|
||||
{ id: 'bip65', version: 4, reject: [Object] }
|
||||
],
|
||||
bip9_softforks: {
|
||||
csv: {
|
||||
status: "active",
|
||||
status: 'active',
|
||||
startTime: 1462060800,
|
||||
timeout: 1493596800,
|
||||
since: 419328
|
||||
@@ -104,178 +129,178 @@ describe("#Blockchain", () => {
|
||||
}
|
||||
}
|
||||
|
||||
it("should get blockchain info", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get blockchain info', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlockchainInfo()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockCount", () => {
|
||||
describe('#getBlockCount', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = 527810
|
||||
|
||||
it("should get block count", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get block count', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlockCount()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockHash", () => {
|
||||
describe('#getBlockHash', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data =
|
||||
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0"
|
||||
'000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0'
|
||||
|
||||
it("should get block hash by height", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get block hash by height', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlockHash(527810)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockHeader", () => {
|
||||
describe('#getBlockHeader', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = {
|
||||
hash: "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0",
|
||||
hash: '000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
|
||||
confirmations: 1,
|
||||
height: 527810,
|
||||
version: 536870912,
|
||||
versionHex: "20000000",
|
||||
versionHex: '20000000',
|
||||
merkleroot:
|
||||
"9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b",
|
||||
'9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b',
|
||||
time: 1524881438,
|
||||
mediantime: 1524878499,
|
||||
nonce: 3326843941,
|
||||
bits: "1801e8a5",
|
||||
bits: '1801e8a5',
|
||||
difficulty: 576023394804.6666,
|
||||
chainwork:
|
||||
"00000000000000000000000000000000000000000096da5b040913fa09249b4e",
|
||||
'00000000000000000000000000000000000000000096da5b040913fa09249b4e',
|
||||
previousblockhash:
|
||||
"000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789"
|
||||
'000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789'
|
||||
}
|
||||
|
||||
it("should get block header by hash", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get block header by hash', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlockHeader(
|
||||
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0",
|
||||
'000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
|
||||
true
|
||||
)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getDifficulty", () => {
|
||||
describe('#getDifficulty', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = "577528469277.1339"
|
||||
const data = '577528469277.1339'
|
||||
|
||||
it("should get difficulty", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get difficulty', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getDifficulty()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolAncestors", () => {
|
||||
describe('#getMempoolAncestors', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = "Transaction not in mempool"
|
||||
const data = 'Transaction not in mempool'
|
||||
|
||||
it("should get mempool ancestors", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get mempool ancestors', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getMempoolAncestors(
|
||||
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88",
|
||||
'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
|
||||
true
|
||||
)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolDescendants", () => {
|
||||
describe('#getMempoolDescendants', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = {
|
||||
result: "Transaction not in mempool"
|
||||
result: 'Transaction not in mempool'
|
||||
}
|
||||
|
||||
it("should get mempool descendants", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get mempool descendants', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getMempoolDescendants(
|
||||
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88",
|
||||
'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
|
||||
true
|
||||
)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolEntry", () => {
|
||||
describe('#getMempoolEntry', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = {
|
||||
result: "Transaction not in mempool"
|
||||
result: 'Transaction not in mempool'
|
||||
}
|
||||
|
||||
it("should get mempool entry", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get mempool entry', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getMempoolEntry(
|
||||
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88"
|
||||
'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88'
|
||||
)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolInfo", () => {
|
||||
describe('#getMempoolInfo', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
@@ -289,19 +314,19 @@ describe("#Blockchain", () => {
|
||||
}
|
||||
}
|
||||
|
||||
it("should get mempool info", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get mempool info', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getMempoolInfo()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getRawMempool", () => {
|
||||
describe('#getRawMempool', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
@@ -310,7 +335,7 @@ describe("#Blockchain", () => {
|
||||
transactions: [
|
||||
{
|
||||
txid:
|
||||
"ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96",
|
||||
'ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96',
|
||||
size: 191,
|
||||
fee: 0.00047703,
|
||||
modifiedfee: 0.00047703,
|
||||
@@ -330,19 +355,19 @@ describe("#Blockchain", () => {
|
||||
}
|
||||
}
|
||||
|
||||
it("should get mempool info", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get mempool info', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getRawMempool()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getTxOut", () => {
|
||||
describe('#getTxOut', () => {
|
||||
// TODO finish this test
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
@@ -351,63 +376,63 @@ describe("#Blockchain", () => {
|
||||
// result: {}
|
||||
// }
|
||||
|
||||
it("should throw an error for improper txid.", async () => {
|
||||
it('should throw an error for improper txid.', async () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getTxOut("badtxid")
|
||||
await bchjs.Blockchain.getTxOut('badtxid')
|
||||
} catch (err) {
|
||||
assert2.include(err.message, "txid needs to be a proper transaction ID")
|
||||
assert2.include(err.message, 'txid needs to be a proper transaction ID')
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw an error if no vout value is provided.", async () => {
|
||||
it('should throw an error if no vout value is provided.', async () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getTxOut(
|
||||
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88"
|
||||
'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88'
|
||||
)
|
||||
} catch (err) {
|
||||
assert2.include(err.message, "n must be an integer")
|
||||
assert2.include(err.message, 'n must be an integer')
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw an error if include_mempool is not a boolean", async () => {
|
||||
it('should throw an error if include_mempool is not a boolean', async () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getTxOut(
|
||||
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88",
|
||||
'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
|
||||
0,
|
||||
"bad value"
|
||||
'bad value'
|
||||
)
|
||||
} catch (err) {
|
||||
assert2.include(
|
||||
err.message,
|
||||
"include_mempool input must be of type boolean"
|
||||
'includeMempool input must be of type boolean'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should get information on an unspent tx", async () => {
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.txOutUnspent })
|
||||
it('should get information on an unspent tx', async () => {
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.txOutUnspent })
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8",
|
||||
'62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8',
|
||||
0,
|
||||
true
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert2.hasAllKeys(result, [
|
||||
"bestblock",
|
||||
"confirmations",
|
||||
"value",
|
||||
"scriptPubKey",
|
||||
"coinbase"
|
||||
'bestblock',
|
||||
'confirmations',
|
||||
'value',
|
||||
'scriptPubKey',
|
||||
'coinbase'
|
||||
])
|
||||
})
|
||||
|
||||
it("should get information on a spent tx", async () => {
|
||||
sandbox.stub(axios, "post").resolves({ data: null })
|
||||
it('should get information on a spent tx', async () => {
|
||||
sandbox.stub(axios, 'post').resolves({ data: null })
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871",
|
||||
'87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871',
|
||||
0,
|
||||
true
|
||||
)
|
||||
@@ -417,7 +442,7 @@ describe("#Blockchain", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#preciousBlock", () => {
|
||||
describe('#preciousBlock', () => {
|
||||
// TODO finish this test
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
@@ -426,67 +451,67 @@ describe("#Blockchain", () => {
|
||||
result: {}
|
||||
}
|
||||
|
||||
it("should get TODO", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should get TODO', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.preciousBlock()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#pruneBlockchain", () => {
|
||||
describe('#pruneBlockchain', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = "Cannot prune blocks because node is not in prune mode."
|
||||
const data = 'Cannot prune blocks because node is not in prune mode.'
|
||||
|
||||
it("should prune blockchain", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "post").returns(resolved)
|
||||
it('should prune blockchain', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'post').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.pruneBlockchain(507)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#verifyChain", () => {
|
||||
describe('#verifyChain', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = true
|
||||
|
||||
it("should verify blockchain", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should verify blockchain', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.verifyChain(3, 6)
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#verifyTxOutProof", () => {
|
||||
describe('#verifyTxOutProof', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
const data = "proof must be hexadecimal string (not '')"
|
||||
|
||||
it("should verify utxo proof", done => {
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
it('should verify utxo proof', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.verifyTxOutProof("3")
|
||||
bchjs.Blockchain.verifyTxOutProof('3')
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
|
||||
+20
-24
@@ -1,46 +1,42 @@
|
||||
const assert = require("assert")
|
||||
const axios = require("axios")
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import assert from 'assert'
|
||||
import axios from 'axios'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
import sinon from 'sinon'
|
||||
const bchjs = new BCHJS()
|
||||
const sinon = require("sinon")
|
||||
|
||||
describe("#Control", () => {
|
||||
describe("#getNetworkInfo", () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
describe('#Control', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
it("should get info", done => {
|
||||
describe('#getNetworkInfo', () => {
|
||||
it('should get info', done => {
|
||||
const data = {
|
||||
version: 170000,
|
||||
protocolversion: 70015,
|
||||
blocks: 527813,
|
||||
timeoffset: 0,
|
||||
connections: 21,
|
||||
proxy: "",
|
||||
proxy: '',
|
||||
difficulty: 581086703759.5878,
|
||||
testnet: false,
|
||||
paytxfee: 0,
|
||||
relayfee: 0.00001,
|
||||
errors: ""
|
||||
errors: ''
|
||||
}
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Control.getNetworkInfo()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMemoryInfo", () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
it("should get memory info", done => {
|
||||
describe('#getMemoryInfo', () => {
|
||||
it('should get memory info', done => {
|
||||
const data = {
|
||||
locked: {
|
||||
used: 0,
|
||||
@@ -51,12 +47,12 @@ describe("#Control", () => {
|
||||
chunks_free: 1
|
||||
}
|
||||
}
|
||||
const resolved = new Promise(r => r({ data: data }))
|
||||
sandbox.stub(axios, "get").returns(resolved)
|
||||
const resolved = new Promise(resolve => resolve({ data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
|
||||
bchjs.Control.getMemoryInfo()
|
||||
.then(result => {
|
||||
assert.deepEqual(data, result)
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
|
||||
+37
-35
@@ -1,97 +1,99 @@
|
||||
const fixtures = require("./fixtures/crypto.json")
|
||||
const assert = require("assert")
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import { createRequire } from 'module'
|
||||
import assert from 'assert'
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
import { Buffer } from 'safe-buffer'
|
||||
const require = createRequire(import.meta.url)
|
||||
const fixtures = require('./fixtures/crypto.json')
|
||||
const bchjs = new BCHJS()
|
||||
const Buffer = require("safe-buffer").Buffer
|
||||
|
||||
describe("#Crypto", () => {
|
||||
describe("#sha256", () => {
|
||||
describe('#Crypto', () => {
|
||||
describe('#sha256', () => {
|
||||
fixtures.sha256.forEach(fixture => {
|
||||
it(`should create SHA256Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const sha256Hash = bchjs.Crypto.sha256(data).toString("hex")
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const sha256Hash = bchjs.Crypto.sha256(data).toString('hex')
|
||||
assert.equal(sha256Hash, fixture.hash)
|
||||
})
|
||||
|
||||
it(`should create 64 character SHA256Hash hex encoded`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const sha256Hash = bchjs.Crypto.sha256(data).toString("hex")
|
||||
it('should create 64 character SHA256Hash hex encoded', () => {
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const sha256Hash = bchjs.Crypto.sha256(data).toString('hex')
|
||||
assert.equal(sha256Hash.length, 64)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#ripemd160", () => {
|
||||
describe('#ripemd160', () => {
|
||||
fixtures.ripemd160.forEach(fixture => {
|
||||
it(`should create RIPEMD160Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex")
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const ripemd160 = bchjs.Crypto.ripemd160(data).toString('hex')
|
||||
assert.equal(ripemd160, fixture.hash)
|
||||
})
|
||||
|
||||
it(`should create 64 character RIPEMD160Hash hex encoded`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex")
|
||||
it('should create 64 character RIPEMD160Hash hex encoded', () => {
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const ripemd160 = bchjs.Crypto.ripemd160(data).toString('hex')
|
||||
assert.equal(ripemd160.length, 40)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hash256", () => {
|
||||
describe('#hash256', () => {
|
||||
fixtures.hash256.forEach(fixture => {
|
||||
it(`should create double SHA256 Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const hash256 = bchjs.Crypto.hash256(data).toString("hex")
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const hash256 = bchjs.Crypto.hash256(data).toString('hex')
|
||||
assert.equal(hash256, fixture.hash)
|
||||
})
|
||||
|
||||
it(`should create 64 character SHA256 Hash hex encoded`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const hash256 = bchjs.Crypto.hash256(data).toString("hex")
|
||||
it('should create 64 character SHA256 Hash hex encoded', () => {
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const hash256 = bchjs.Crypto.hash256(data).toString('hex')
|
||||
assert.equal(hash256.length, 64)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hash160", () => {
|
||||
describe('#hash160', () => {
|
||||
fixtures.hash160.forEach(fixture => {
|
||||
it(`should create RIPEMD160(SHA256()) hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const hash160 = bchjs.Crypto.hash160(data).toString("hex")
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const hash160 = bchjs.Crypto.hash160(data).toString('hex')
|
||||
assert.equal(hash160, fixture.hash)
|
||||
})
|
||||
|
||||
it(`should create 64 character SHA256Hash hex encoded`, () => {
|
||||
const data = Buffer.from(fixture.hex, "hex")
|
||||
const hash160 = bchjs.Crypto.hash160(data).toString("hex")
|
||||
it('should create 64 character SHA256Hash hex encoded', () => {
|
||||
const data = Buffer.from(fixture.hex, 'hex')
|
||||
const hash160 = bchjs.Crypto.hash160(data).toString('hex')
|
||||
assert.equal(hash160.length, 40)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#randomBytes", () => {
|
||||
describe('#randomBytes', () => {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
it("should return 16 bytes of entropy hex encoded", () => {
|
||||
it('should return 16 bytes of entropy hex encoded', () => {
|
||||
const entropy = bchjs.Crypto.randomBytes(16)
|
||||
assert.equal(Buffer.byteLength(entropy), 16)
|
||||
})
|
||||
|
||||
it("should return 20 bytes of entropy hex encoded", () => {
|
||||
it('should return 20 bytes of entropy hex encoded', () => {
|
||||
const entropy = bchjs.Crypto.randomBytes(20)
|
||||
assert.equal(Buffer.byteLength(entropy), 20)
|
||||
})
|
||||
|
||||
it("should return 24 bytes of entropy hex encoded", () => {
|
||||
it('should return 24 bytes of entropy hex encoded', () => {
|
||||
const entropy = bchjs.Crypto.randomBytes(24)
|
||||
assert.equal(Buffer.byteLength(entropy), 24)
|
||||
})
|
||||
|
||||
it("should return 28 bytes of entropy hex encoded", () => {
|
||||
it('should return 28 bytes of entropy hex encoded', () => {
|
||||
const entropy = bchjs.Crypto.randomBytes(28)
|
||||
assert.equal(Buffer.byteLength(entropy), 28)
|
||||
})
|
||||
|
||||
it("should return 32 bytes of entropy hex encoded", () => {
|
||||
it('should return 32 bytes of entropy hex encoded', () => {
|
||||
const entropy = bchjs.Crypto.randomBytes(32)
|
||||
assert.equal(Buffer.byteLength(entropy), 32)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Public npm libraries
|
||||
import chai from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
// Unit under test (uut)
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
import mockData from './fixtures/dsproof-mock.js'
|
||||
const { assert } = chai
|
||||
|
||||
let bchjs
|
||||
const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
describe('#DSProof', () => {
|
||||
let sandbox
|
||||
beforeEach(() => {
|
||||
bchjs = new BCHJS()
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#getDSProof', () => {
|
||||
it('should throw error if input is not provided', async () => {
|
||||
try {
|
||||
await bchjs.DSProof.getDSProof()
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'txid is required')
|
||||
}
|
||||
})
|
||||
it('should throw error txid is invalid', async () => {
|
||||
try {
|
||||
await bchjs.DSProof.getDSProof('txid')
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'txid must be of length 64')
|
||||
}
|
||||
})
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.DSProof.axios, 'get').throws(new Error('test error'))
|
||||
await bchjs.DSProof.getDSProof(txid)
|
||||
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should get double spend proof', async () => {
|
||||
try {
|
||||
sandbox
|
||||
.stub(bchjs.DSProof.axios, 'get')
|
||||
.resolves({ data: mockData.dsproof })
|
||||
const result = await bchjs.DSProof.getDSProof(txid)
|
||||
|
||||
assert.property(result, 'dspid')
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'outpoint')
|
||||
assert.property(result, 'path')
|
||||
assert.property(result, 'descendants')
|
||||
|
||||
assert.property(result.outpoint, 'txid')
|
||||
assert.property(result.outpoint, 'vout')
|
||||
} catch (error) {
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Unit tests for eCash library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import chai from 'chai'
|
||||
import Ecash from '../../src/ecash.js'
|
||||
const { assert } = chai
|
||||
const uut = new Ecash()
|
||||
|
||||
describe('#eCash', () => {
|
||||
describe('#toSatoshi', () => {
|
||||
it('should convert XEC to satoshis', () => {
|
||||
const xec = 10704.35
|
||||
|
||||
const result = uut.toSatoshi(xec)
|
||||
|
||||
assert.equal(result, 1070435)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toSatoshi('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing XEC'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#toXec', () => {
|
||||
it('should convert satoshis to XEC', () => {
|
||||
const sats = 1070435
|
||||
|
||||
const result = uut.toXec(sats)
|
||||
|
||||
assert.equal(result, 10704.35)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toXec('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing satoshis'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
+39
-30
@@ -1,18 +1,27 @@
|
||||
const assert = require("assert")
|
||||
// Public npm libraries.
|
||||
import assert from 'assert'
|
||||
import { Buffer } from 'safe-buffer'
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
// Mocks
|
||||
import { createRequire } from 'module'
|
||||
|
||||
const Buffer = require("safe-buffer").Buffer
|
||||
// Unit under test (uut)
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
const require = createRequire(import.meta.url)
|
||||
const fixtures = require('./fixtures/ecpair.json')
|
||||
// const bchjs = new BCHJS()
|
||||
let bchjs
|
||||
|
||||
const fixtures = require("./fixtures/ecpair.json")
|
||||
describe('#ECPair', () => {
|
||||
beforeEach(() => {
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe("#ECPair", () => {
|
||||
describe("#fromWIF", () => {
|
||||
describe('#fromWIF', () => {
|
||||
fixtures.fromWIF.forEach(fixture => {
|
||||
it(`should create ECPair from WIF ${fixture.privateKeyWIF}`, () => {
|
||||
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
|
||||
assert.equal(typeof ecpair, "object")
|
||||
assert.equal(typeof ecpair, 'object')
|
||||
})
|
||||
|
||||
it(`should get ${fixture.legacy} legacy address`, () => {
|
||||
@@ -35,7 +44,7 @@ describe("#ECPair", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toWIF", () => {
|
||||
describe('#toWIF', () => {
|
||||
fixtures.toWIF.forEach(fixture => {
|
||||
it(`should get WIF ${fixture.privateKeyWIF} from ECPair`, () => {
|
||||
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
|
||||
@@ -45,32 +54,32 @@ describe("#ECPair", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#fromPublicKey", () => {
|
||||
describe('#fromPublicKey', () => {
|
||||
fixtures.fromPublicKey.forEach(fixture => {
|
||||
it(`should create ECPair from public key buffer`, () => {
|
||||
it('should create ECPair from public key buffer', () => {
|
||||
const ecpair = bchjs.ECPair.fromPublicKey(
|
||||
Buffer.from(fixture.pubkeyHex, "hex")
|
||||
Buffer.from(fixture.pubkeyHex, 'hex')
|
||||
)
|
||||
assert.equal(typeof ecpair, "object")
|
||||
assert.equal(typeof ecpair, 'object')
|
||||
})
|
||||
|
||||
it(`should get ${fixture.legacy} legacy address`, () => {
|
||||
const ecpair = bchjs.ECPair.fromPublicKey(
|
||||
Buffer.from(fixture.pubkeyHex, "hex")
|
||||
Buffer.from(fixture.pubkeyHex, 'hex')
|
||||
)
|
||||
assert.equal(bchjs.HDNode.toLegacyAddress(ecpair), fixture.legacy)
|
||||
})
|
||||
|
||||
it(`should get ${fixture.cashAddr} cash address`, () => {
|
||||
const ecpair = bchjs.ECPair.fromPublicKey(
|
||||
Buffer.from(fixture.pubkeyHex, "hex")
|
||||
Buffer.from(fixture.pubkeyHex, 'hex')
|
||||
)
|
||||
assert.equal(bchjs.HDNode.toCashAddress(ecpair), fixture.cashAddr)
|
||||
})
|
||||
|
||||
it(`should get ${fixture.regtestAddr} cash address`, () => {
|
||||
const ecpair = bchjs.ECPair.fromPublicKey(
|
||||
Buffer.from(fixture.pubkeyHex, "hex")
|
||||
Buffer.from(fixture.pubkeyHex, 'hex')
|
||||
)
|
||||
assert.equal(
|
||||
bchjs.HDNode.toCashAddress(ecpair, true),
|
||||
@@ -80,19 +89,19 @@ describe("#ECPair", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toPublicKey", () => {
|
||||
describe('#toPublicKey', () => {
|
||||
fixtures.toPublicKey.forEach(fixture => {
|
||||
it(`should create a public key buffer from an ECPair`, () => {
|
||||
it('should create a public key buffer from an ECPair', () => {
|
||||
const ecpair = bchjs.ECPair.fromPublicKey(
|
||||
Buffer.from(fixture.pubkeyHex, "hex")
|
||||
Buffer.from(fixture.pubkeyHex, 'hex')
|
||||
)
|
||||
const pubkeyBuffer = bchjs.ECPair.toPublicKey(ecpair)
|
||||
assert.equal(typeof pubkeyBuffer, "object")
|
||||
assert.equal(typeof pubkeyBuffer, 'object')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toLegacyAddress", () => {
|
||||
describe('#toLegacyAddress', () => {
|
||||
fixtures.toLegacyAddress.forEach(fixture => {
|
||||
it(`should create legacy address ${fixture.legacy} from an ECPair`, () => {
|
||||
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
|
||||
@@ -102,7 +111,7 @@ describe("#ECPair", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#toCashAddress", () => {
|
||||
describe('#toCashAddress', () => {
|
||||
fixtures.toCashAddress.forEach(fixture => {
|
||||
it(`should create cash address ${fixture.cashAddr} from an ECPair`, () => {
|
||||
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
|
||||
@@ -120,23 +129,23 @@ describe("#ECPair", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#sign", () => {
|
||||
describe('#sign', () => {
|
||||
fixtures.sign.forEach(fixture => {
|
||||
it(`should sign 32 byte hash buffer`, () => {
|
||||
it('should sign 32 byte hash buffer', () => {
|
||||
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
|
||||
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex")
|
||||
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), 'hex')
|
||||
const signatureBuf = bchjs.ECPair.sign(ecpair, buf)
|
||||
assert.equal(typeof signatureBuf, "object")
|
||||
assert.equal(typeof signatureBuf, 'object')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#verify", () => {
|
||||
describe('#verify', () => {
|
||||
fixtures.verify.forEach(fixture => {
|
||||
it(`should verify signed 32 byte hash buffer`, () => {
|
||||
it('should verify signed 32 byte hash buffer', () => {
|
||||
const ecpair1 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF1)
|
||||
//const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2)
|
||||
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex")
|
||||
// const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2)
|
||||
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), 'hex')
|
||||
const signature = bchjs.ECPair.sign(ecpair1, buf)
|
||||
const verify = bchjs.ECPair.verify(ecpair1, buf, signature)
|
||||
assert.equal(verify, true)
|
||||
|
||||
+258
-135
@@ -1,294 +1,315 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const axios = require("axios")
|
||||
const sinon = require("sinon")
|
||||
import chai from 'chai'
|
||||
import axios from 'axios'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
|
||||
import mockData from './fixtures/electrumx-mock.js'
|
||||
const assert = chai.assert
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require("./fixtures/electrumx-mock")
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
describe('#ElectrumX', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
describe('#utxo', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Electrumx.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
'Input address must be a string or array of strings'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
it('should GET utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.utxo })
|
||||
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0], "value")
|
||||
assert.property(result.utxos[0], 'height')
|
||||
assert.property(result.utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0], 'tx_pos')
|
||||
assert.property(result.utxos[0], 'value')
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.utxos })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.property(result.utxos[0], 'utxos')
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
assert.property(result.utxos[0], 'address')
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0].utxos[0], "value")
|
||||
assert.property(result.utxos[0].utxos[0], 'height')
|
||||
assert.property(result.utxos[0].utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0].utxos[0], 'tx_pos')
|
||||
assert.property(result.utxos[0].utxos[0], 'value')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#balance`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
describe('#balance', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Electrumx.balance(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
'Input address must be a string or array of strings'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
it('should GET balance for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.balance })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.balance })
|
||||
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result.balance, "confirmed")
|
||||
assert.property(result.balance, "unconfirmed")
|
||||
assert.property(result, 'balance')
|
||||
assert.property(result.balance, 'confirmed')
|
||||
assert.property(result.balance, 'unconfirmed')
|
||||
})
|
||||
|
||||
it(`should POST balance for an array of addresses`, async () => {
|
||||
it('should POST balance for an array of addresses', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.balances })
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.balances })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balances")
|
||||
assert.property(result, 'balances')
|
||||
assert.isArray(result.balances)
|
||||
|
||||
assert.property(result.balances[0], "address")
|
||||
assert.property(result.balances[0], "balance")
|
||||
assert.property(result.balances[0].balance, "confirmed")
|
||||
assert.property(result.balances[0].balance, "unconfirmed")
|
||||
assert.property(result.balances[0], 'address')
|
||||
assert.property(result.balances[0], 'balance')
|
||||
assert.property(result.balances[0].balance, 'confirmed')
|
||||
assert.property(result.balances[0].balance, 'unconfirmed')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#transactions`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
describe('#transactions', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Electrumx.transactions(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
'Input address must be a string or array of strings'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET transactions for a single address`, async () => {
|
||||
it('should GET transactions for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.transaction })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.transaction })
|
||||
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
assert.property(result.transactions[0], "height")
|
||||
assert.property(result.transactions[0], "tx_hash")
|
||||
assert.property(result.transactions[0], 'height')
|
||||
assert.property(result.transactions[0], 'tx_hash')
|
||||
})
|
||||
|
||||
it(`should POST transaction history for an array of addresses`, async () => {
|
||||
it('should POST transaction history for an array of addresses', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.transactions })
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.transactions })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "address")
|
||||
assert.property(result.transactions[0], "transactions")
|
||||
assert.property(result.transactions[0], 'address')
|
||||
assert.property(result.transactions[0], 'transactions')
|
||||
assert.isArray(result.transactions[0].transactions)
|
||||
assert.property(result.transactions[0].transactions[0], "height")
|
||||
assert.property(result.transactions[0].transactions[0], "tx_hash")
|
||||
assert.property(result.transactions[0].transactions[0], 'height')
|
||||
assert.property(result.transactions[0].transactions[0], 'tx_hash')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#unconfirmed`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
describe('#unconfirmed', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Electrumx.unconfirmed(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
'Input address must be a string or array of strings'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET unconfirmed utxos for a single address`, async () => {
|
||||
it('should GET unconfirmed utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.unconfirmed })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.unconfirmed })
|
||||
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "fee")
|
||||
assert.property(result.utxos[0], 'height')
|
||||
assert.property(result.utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0], 'fee')
|
||||
})
|
||||
|
||||
it(`should POST unconfirmed utxo details for an array of addresses`, async () => {
|
||||
it('should POST unconfirmed utxo details for an array of addresses', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedArray })
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmedArray })
|
||||
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, 'utxos')
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.property(result.utxos[0], 'utxos')
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
assert.property(result.utxos[0], 'address')
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "fee")
|
||||
assert.property(result.utxos[0].utxos[0], 'height')
|
||||
assert.property(result.utxos[0].utxos[0], 'tx_hash')
|
||||
assert.property(result.utxos[0].utxos[0], 'fee')
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should throw an error for improper height input`, async () => {
|
||||
describe('#blockHeader', () => {
|
||||
it('should throw an error for improper height input', async () => {
|
||||
try {
|
||||
// Mock network calls.
|
||||
sandbox.stub(axios, 'get').rejects({
|
||||
response: {
|
||||
data: {
|
||||
success: false,
|
||||
error: 'height must be a positive number'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const height = -10
|
||||
await bchjs.Electrumx.blockHeader(height)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, `height must be a positive number`)
|
||||
assert.include(err.message, 'height must be a positive number')
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper count input`, async () => {
|
||||
it('should throw an error for improper count input', async () => {
|
||||
try {
|
||||
// Mock network calls.
|
||||
sandbox.stub(axios, 'get').rejects({
|
||||
response: {
|
||||
data: {
|
||||
success: false,
|
||||
error: 'count must be a positive number'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const height = 42
|
||||
const count = -10
|
||||
|
||||
await bchjs.Electrumx.blockHeader(height, count)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, `count must be a positive number`)
|
||||
assert.include(err.message, 'count must be a positive number')
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET block headers for a given height`, async () => {
|
||||
it('should GET block headers for a given height', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.blockHeaders })
|
||||
|
||||
const height = 42
|
||||
|
||||
@@ -299,73 +320,175 @@ describe(`#ElectrumX`, () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
describe('#txData', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Electrumx.txData(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input txId must be a string or array of strings`
|
||||
'Input txId must be a string or array of strings'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET details data for a single transaction`, async () => {
|
||||
it('should GET details data for a single transaction', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.details })
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.details })
|
||||
|
||||
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
const txid =
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.property(result, 'details')
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.property(result.details, 'blockhash')
|
||||
assert.property(result.details, 'hash')
|
||||
assert.property(result.details, 'hex')
|
||||
assert.property(result.details, 'vin')
|
||||
assert.property(result.details, 'vout')
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
it('should POST details for an array of transactions', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray })
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.detailsArray })
|
||||
|
||||
const txids = [
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.property(result, 'transactions')
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
assert.property(result.transactions[0], 'txid')
|
||||
assert.property(result.transactions[0], 'details')
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
assert.property(result.transactions[0].details, 'blockhash')
|
||||
assert.property(result.transactions[0].details, 'hash')
|
||||
assert.property(result.transactions[0].details, 'hex')
|
||||
assert.property(result.transactions[0].details, 'vin')
|
||||
assert.property(result.transactions[0].details, 'vout')
|
||||
|
||||
assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs")
|
||||
assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txHex = 12345
|
||||
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input txHex must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should broadcast a single transaction', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.broadcast })
|
||||
|
||||
const tx = mockData.details
|
||||
const txid = tx.details.txid
|
||||
const result = await bchjs.Electrumx.broadcast(tx.details.hex)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, 'txid')
|
||||
assert.equal(result.txid, txid)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#sortConfTxs', () => {
|
||||
it('should sort in ascending order', () => {
|
||||
const result = bchjs.Electrumx.sortConfTxs(mockData.transaction.transactions, 'ASCENDING')
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isBelow(result[0].height, result[1].height)
|
||||
})
|
||||
|
||||
it('should sort in descending order by default', () => {
|
||||
const result = bchjs.Electrumx.sortConfTxs(mockData.transaction.transactions)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result[0].height, result[1].height)
|
||||
})
|
||||
|
||||
it('should ignore unconfirmed txs', () => {
|
||||
const result = bchjs.Electrumx.sortConfTxs(mockData.txHistoryWithUnconfirmed.transactions)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result[0].height, 0)
|
||||
})
|
||||
|
||||
it('should handle errors', () => {
|
||||
try {
|
||||
bchjs.Electrumx.sortConfTxs('abc')
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err.message)
|
||||
assert.equal(err.message, 'txs.filter is not a function')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// These tests use mocked data that contains unconfirmed transactions.
|
||||
describe('#sortAllTxs', () => {
|
||||
it('should sort in ascending', async () => {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions, 'ASCENDING')
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isBelow(result[0].height, result[1].height)
|
||||
})
|
||||
|
||||
it('should sort in descending order by default', async () => {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isAbove(result[1].height, result[2].height)
|
||||
})
|
||||
|
||||
it('should handle errors', async () => {
|
||||
try {
|
||||
// Stub network calls
|
||||
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
|
||||
|
||||
await bchjs.Electrumx.sortAllTxs('abc')
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err.message)
|
||||
assert.equal(err.message, 'txs.map is not a function')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+22
-21
@@ -1,13 +1,14 @@
|
||||
const assert = require("chai").assert
|
||||
const sinon = require("sinon")
|
||||
import chai from 'chai'
|
||||
import sinon from 'sinon'
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
import BCHJS from '../../src/bch-js.js'
|
||||
|
||||
import mockData from './fixtures/encryption-mock.js'
|
||||
const { assert } = chai
|
||||
// const bchjs = new BCHJS()
|
||||
let bchjs
|
||||
|
||||
const mockData = require("./fixtures/encryption-mock")
|
||||
|
||||
describe("#Encryption", () => {
|
||||
describe('#Encryption', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -17,52 +18,52 @@ describe("#Encryption", () => {
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe("#getPubKey", () => {
|
||||
it("should throw error if BCH address is not provided.", async () => {
|
||||
describe('#getPubKey', () => {
|
||||
it('should throw error if BCH address is not provided.', async () => {
|
||||
try {
|
||||
await bchjs.encryption.getPubKey()
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
"Input must be a valid Bitcoin Cash address"
|
||||
'Input must be a valid Bitcoin Cash address'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should report when public key can not be found", async () => {
|
||||
it('should report when public key can not be found', async () => {
|
||||
// Stub the network call.
|
||||
sandbox
|
||||
.stub(bchjs.encryption.axios, "get")
|
||||
.stub(bchjs.encryption.axios, 'get')
|
||||
.resolves({ data: mockData.failureMock })
|
||||
|
||||
const addr = "bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx"
|
||||
const addr = 'bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx'
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, false)
|
||||
assert.property(result, "publicKey")
|
||||
assert.equal(result.publicKey, "not found")
|
||||
assert.property(result, 'publicKey')
|
||||
assert.equal(result.publicKey, 'not found')
|
||||
})
|
||||
|
||||
it("should get a public key", async () => {
|
||||
it('should get a public key', async () => {
|
||||
// Stub the network call.
|
||||
sandbox
|
||||
.stub(bchjs.encryption.axios, "get")
|
||||
.stub(bchjs.encryption.axios, 'get')
|
||||
.resolves({ data: mockData.successMock })
|
||||
|
||||
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
|
||||
const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
assert.property(result, "publicKey")
|
||||
assert.property(result, 'publicKey')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -406,11 +406,6 @@
|
||||
"wif": "L1XHKhaBAfkr2FJQn3pTfCMxz652WYfmvKj8xDCHCEDV9tWGcbYj",
|
||||
"password": "1EBPIyj55eR8bVUov9",
|
||||
"encryptedKey": "6PYWWnBNfNpSqEJZKcfwbrYgTTdb9PNiGjQJ8r9V6cvsZNKLfcZD8YefQc"
|
||||
},
|
||||
{
|
||||
"wif": "L1phBREbhL4vb1uHHHCAse8bdGE5c7ic2PFjRxMawLzQCsiFVbvu",
|
||||
"password": "9GKVkabAHBMyAf",
|
||||
"encryptedKey": "6PYU2fDHRVF2194gKDGkbFbeu4mFgkWtVvg2RPd2Sp6KmZx3RCHFpgBB2G"
|
||||
}
|
||||
],
|
||||
"testnet": [
|
||||
@@ -432,11 +427,6 @@
|
||||
"wif": "L1XHKhaBAfkr2FJQn3pTfCMxz652WYfmvKj8xDCHCEDV9tWGcbYj",
|
||||
"password": "1EBPIyj55eR8bVUov9",
|
||||
"encryptedKey": "6PYWWnBNfNpSqEJZKcfwbrYgTTdb9PNiGjQJ8r9V6cvsZNKLfcZD8YefQc"
|
||||
},
|
||||
{
|
||||
"wif": "L1phBREbhL4vb1uHHHCAse8bdGE5c7ic2PFjRxMawLzQCsiFVbvu",
|
||||
"password": "9GKVkabAHBMyAf",
|
||||
"encryptedKey": "6PYU2fDHRVF2194gKDGkbFbeu4mFgkWtVvg2RPd2Sp6KmZx3RCHFpgBB2G"
|
||||
}
|
||||
],
|
||||
"testnet": [
|
||||
|
||||
@@ -6,40 +6,40 @@ const balance = { confirmed: 230000, unconfirmed: 0, balance: 230000 }
|
||||
|
||||
const utxo = [
|
||||
{
|
||||
_id: "5cecdd39a9f1235e2a3d409a",
|
||||
chain: "BCH",
|
||||
network: "mainnet",
|
||||
_id: '5cecdd39a9f1235e2a3d409a',
|
||||
chain: 'BCH',
|
||||
network: 'mainnet',
|
||||
coinbase: false,
|
||||
mintIndex: 0,
|
||||
spentTxid: "",
|
||||
spentTxid: '',
|
||||
mintTxid:
|
||||
"27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
|
||||
'27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c',
|
||||
mintHeight: 560430,
|
||||
spentHeight: -2,
|
||||
address: "qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
script: "76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac",
|
||||
address: 'qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
script: '76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac',
|
||||
value: 100000,
|
||||
confirmations: -1
|
||||
},
|
||||
{
|
||||
_id: "5cecdd1ca9f1235e2a3b6349",
|
||||
chain: "BCH",
|
||||
network: "mainnet",
|
||||
_id: '5cecdd1ca9f1235e2a3b6349',
|
||||
chain: 'BCH',
|
||||
network: 'mainnet',
|
||||
coinbase: false,
|
||||
mintIndex: 0,
|
||||
spentTxid: "",
|
||||
spentTxid: '',
|
||||
mintTxid:
|
||||
"6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
|
||||
'6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d',
|
||||
mintHeight: 560039,
|
||||
spentHeight: -2,
|
||||
address: "qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
script: "76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac",
|
||||
address: 'qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
|
||||
script: '76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac',
|
||||
value: 130000,
|
||||
confirmations: -1
|
||||
}
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
balance,
|
||||
utxo
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user