Compare commits
881 Commits
release/0.
...
frost
Author | SHA1 | Date | |
---|---|---|---|
|
f2a29c5b05 | ||
|
34543311bb | ||
|
323eb08350 | ||
|
7e5897bd1c | ||
|
ffe0c8c1a4 | ||
|
cddd5f25d8 | ||
|
f4366ac49f | ||
|
e2abc3620a | ||
|
c66b48467a | ||
|
33026108a7 | ||
|
92d40a9f46 | ||
|
3b89af5a6e | ||
|
f66f8417cf | ||
|
92aeeab436 | ||
|
a2794f25b0 | ||
|
1a1920e7e3 | ||
|
6642c5808b | ||
|
e97e9b731c | ||
|
94d31ff7ed | ||
|
84f1329e84 | ||
|
efef60082b | ||
|
53afd9c238 | ||
|
b9128b0e6a | ||
|
9d3733389d | ||
|
65702f401b | ||
|
19b4e1159a | ||
|
e5e7aba208 | ||
|
9790d60324 | ||
|
093eb1fc7e | ||
|
5ef2bf8a1e | ||
|
7fc8da5451 | ||
|
5d41984377 | ||
|
89f803a753 | ||
|
1f7c782d49 | ||
|
af9346c4eb | ||
|
5cf25a50c4 | ||
|
c6174199dd | ||
|
9c45254c3e | ||
|
260a0a65b3 | ||
|
72985f14ad | ||
|
5e3e24906f | ||
|
c702894143 | ||
|
ecdd7c239b | ||
|
ca8a3d0471 | ||
|
8f4c80cb98 | ||
|
4aec4b0434 | ||
|
1913c45ef9 | ||
|
815fe5f62d | ||
|
8d30c86076 | ||
|
c88b33473b | ||
|
79e7ab73ea | ||
|
f169b1a52f | ||
|
97d9bb6fbf | ||
|
f27bada9c9 | ||
|
1b0b50a954 | ||
|
6e207802b2 | ||
|
ac15ed7380 | ||
|
e9a76287c8 | ||
|
72b5bfd4c9 | ||
|
19723240b7 | ||
|
7d951578d0 | ||
|
b7fe91b003 | ||
|
330dc96b8a | ||
|
5557bb94ea | ||
|
9b5b96710e | ||
|
75d155c67a | ||
|
6522dfdd26 | ||
|
ebaa6fda2f | ||
|
5e8271e158 | ||
|
431ab90f04 | ||
|
d4736a64d1 | ||
|
f31678bf37 | ||
|
8130a419f2 | ||
|
262704751c | ||
|
00a8e1ba8b | ||
|
d0514f678e | ||
|
f6cc63539d | ||
|
df64a96dd2 | ||
|
4cd6a80ce0 | ||
|
e48af63fe6 | ||
|
eff4abcbfb | ||
|
e1a93379ce | ||
|
e609b57bff | ||
|
282fcfce0a | ||
|
4dd4e91ccd | ||
|
4f8b7f4ba5 | ||
|
4d737d3393 | ||
|
e14124b454 | ||
|
0a75fc1279 | ||
|
6ac386c8df | ||
|
126bc61df6 | ||
|
c63e7ad392 | ||
|
edea8e8c80 | ||
|
6bc974ed2e | ||
|
2f7652b979 | ||
|
fa17a862ce | ||
|
77cfee718c | ||
|
aa1c0de244 | ||
|
aee84f9634 | ||
|
a35fdaee61 | ||
|
dccc85d8ff | ||
|
f1744d192f | ||
|
cacb78f4dc | ||
|
54f3235254 | ||
|
806e29b93c | ||
|
97a104fd5f | ||
|
0e617bc986 | ||
|
ab2e97e782 | ||
|
84f2497aeb | ||
|
ab87355f9d | ||
|
a40702ebd9 | ||
|
0eadff1327 | ||
|
78ef936369 | ||
|
7b9e71714f | ||
|
f698d46392 | ||
|
ab9763bb58 | ||
|
aa035588a0 | ||
|
e774c94998 | ||
|
5e41275f29 | ||
|
15ac8c8ffb | ||
|
358b43c31e | ||
|
08f1957fe0 | ||
|
a8128e5056 | ||
|
b879bf4a50 | ||
|
43ff1fb394 | ||
|
a8541ecd40 | ||
|
c666bb247a | ||
|
d066007d9c | ||
|
c6d17b77ec | ||
|
9a996b1a94 | ||
|
c68c13fbfa | ||
|
0b851d441f | ||
|
267685bd58 | ||
|
896303f487 | ||
|
9ea9e9384c | ||
|
2e0cd058f4 | ||
|
c3313fb922 | ||
|
5fc30c6c26 | ||
|
38ccbb17be | ||
|
89f36a6c88 | ||
|
6ea0518a54 | ||
|
738f53e991 | ||
|
61e58240fc | ||
|
06fa9d751b | ||
|
145652beaa | ||
|
2dd6ee33f1 | ||
|
b249dae875 | ||
|
43c1ca66b8 | ||
|
68a9eb693d | ||
|
6022a703c6 | ||
|
99a3d74a4a | ||
|
50f102bbd3 | ||
|
141705e2ed | ||
|
619884eaed | ||
|
794f2bc995 | ||
|
991de09219 | ||
|
6f4b4c9155 | ||
|
34c76feb71 | ||
|
8e9d2ddc14 | ||
|
7319aea562 | ||
|
54beb23f13 | ||
|
ccf5fbda6e | ||
|
4f6c198168 | ||
|
3789c1dcd6 | ||
|
fc25cd709a | ||
|
cdec63efa3 | ||
|
bcb77c45f4 | ||
|
a1a45996fc | ||
|
bbc6e1a43c | ||
|
252bcaa444 | ||
|
643d254ab6 | ||
|
c79a39914e | ||
|
3df92527e9 | ||
|
fe65c70915 | ||
|
ce219e4ac4 | ||
|
c9019e9fcf | ||
|
53b59d5100 | ||
|
4a3cf49f24 | ||
|
40940c38af | ||
|
46e0324f28 | ||
|
6b177f0893 | ||
|
05ce7dad31 | ||
|
e5ded1a726 | ||
|
022e29faaa | ||
|
0c0dfd5f42 | ||
|
24e09a40cb | ||
|
93d50dd34d | ||
|
5ecbf64e60 | ||
|
084f0f713a | ||
|
1ef1c5cc6e | ||
|
0956999283 | ||
|
e70dedce61 | ||
|
15c1f19c96 | ||
|
26352edfbe | ||
|
787152e0b4 | ||
|
e79ce98295 | ||
|
b8778cdaa5 | ||
|
f2cd561f25 | ||
|
96e7479ce9 | ||
|
a9c6aac6b9 | ||
|
fc4240ca38 | ||
|
fd85d1d754 | ||
|
d37b2f37b5 | ||
|
13c751cebc | ||
|
1521811e9b | ||
|
372f79a10f | ||
|
00cd55bb46 | ||
|
7463fa7720 | ||
|
790aee9b3b | ||
|
743862fb60 | ||
|
b7e38b18be | ||
|
ef73aa3490 | ||
|
fbec953149 | ||
|
0b07b8ed05 | ||
|
b59327e5ea | ||
|
e6500baae7 | ||
|
85f220b953 | ||
|
106d31c9c3 | ||
|
4ae169f860 | ||
|
7717ebb097 | ||
|
c3e8469686 | ||
|
faf23b7d25 | ||
|
1da01b4a0b | ||
|
f1ba03bf50 | ||
|
bc182c7164 | ||
|
5fc189717d | ||
|
849bfe79c1 | ||
|
dc79b78b2d | ||
|
7cc08f1d6f | ||
|
031fcb02da | ||
|
5f9b5682e5 | ||
|
d0a7315c9d | ||
|
0bfc56b0e8 | ||
|
3dd6c203e8 | ||
|
76acbf575b | ||
|
039b64de5c | ||
|
fe7e4e21c0 | ||
|
77f89afc68 | ||
|
25033f6bd6 | ||
|
3cb2c2c394 | ||
|
5092987b26 | ||
|
aea25dbf21 | ||
|
ed67eba910 | ||
|
90606b2455 | ||
|
49e8fe461e | ||
|
de88184b8c | ||
|
3be2c0495f | ||
|
2c4c64515f | ||
|
17323d3184 | ||
|
b382511a9e | ||
|
d3895441d3 | ||
|
269512a673 | ||
|
d27206787a | ||
|
c1b1fd6f5d | ||
|
7062fbd047 | ||
|
0e34a6bacf | ||
|
89e85a20cf | ||
|
d8718c3f05 | ||
|
871a06d1ce | ||
|
b820d6a2ba | ||
|
79d9fa2909 | ||
|
a0e0467d39 | ||
|
f2296704e6 | ||
|
b8b60dda87 | ||
|
a50e19e7e0 | ||
|
fab9ae8ae5 | ||
|
478b12c489 | ||
|
63b85b9100 | ||
|
0e6b472793 | ||
|
beb75dd552 | ||
|
5ee8698e0a | ||
|
ac600a1312 | ||
|
f26031db80 | ||
|
e7e1a6057e | ||
|
2b7c104f11 | ||
|
6bab5a159d | ||
|
b6c8b145bb | ||
|
67610abeb6 | ||
|
4e5537acd2 | ||
|
6be4ddaf7b | ||
|
cd10c75e96 | ||
|
cbd44249f3 | ||
|
20c31d5383 | ||
|
616cb21738 | ||
|
d3a6453eda | ||
|
cba69e681a | ||
|
35d8fb3139 | ||
|
f003a6275e | ||
|
2f62377eec | ||
|
81e208222a | ||
|
3dc6596aa2 | ||
|
2342265c26 | ||
|
6c561228c2 | ||
|
e86909ab3d | ||
|
8e51756a3a | ||
|
40263b425e | ||
|
9437051668 | ||
|
7557e214c8 | ||
|
40ca62086c | ||
|
e0506deffa | ||
|
d3e183a498 | ||
|
1e9ecfbe52 | ||
|
d48bacd29b | ||
|
c1243f9e1c | ||
|
9c6069e389 | ||
|
488edf8bd2 | ||
|
90763d42a2 | ||
|
44b2ef1382 | ||
|
25617d1f23 | ||
|
2fcafe2b80 | ||
|
5728b50100 | ||
|
d08317775b | ||
|
c93f292b0e | ||
|
a75c868eb2 | ||
|
974ff66caf | ||
|
2309b19209 | ||
|
3128fad690 | ||
|
a1b112cbbb | ||
|
553c337241 | ||
|
90d12a96c5 | ||
|
5ca1d17adb | ||
|
f121372c73 | ||
|
44a78cc459 | ||
|
2dbad2ddd5 | ||
|
ec71ef58be | ||
|
4ca7919ca9 | ||
|
07aa1f8950 | ||
|
d42789db9b | ||
|
10f893a4b3 | ||
|
13043065b6 | ||
|
5fa0b916a2 | ||
|
7611a65620 | ||
|
458a162c2a | ||
|
d15783dba0 | ||
|
2723577a84 | ||
|
7fefb8a7b9 | ||
|
aa842c3844 | ||
|
df019c11ec | ||
|
11d7d6b80f | ||
|
85f8a8a526 | ||
|
5e75c856c5 | ||
|
b854c78dde | ||
|
f5d4750ae4 | ||
|
f75ead02ff | ||
|
8e54ada436 | ||
|
302ad8dea8 | ||
|
561e93f6dd | ||
|
b8230799cf | ||
|
d2bec60046 | ||
|
aadc622006 | ||
|
202dcfa2b5 | ||
|
46bd9a1f15 | ||
|
6fcb8985f1 | ||
|
dd58a9d548 | ||
|
d2a4e2adba | ||
|
cbc740c407 | ||
|
f50ecdb7e7 | ||
|
f01e0e30f3 | ||
|
5f7d8a9077 | ||
|
d77f5b84c3 | ||
|
6555170d28 | ||
|
c763609b8b | ||
|
340fa19bb8 | ||
|
ff2734663e | ||
|
f399b799e4 | ||
|
cdde26a8ea | ||
|
34af88df6f | ||
|
12e9a18357 | ||
|
bada9b82e0 | ||
|
41f15fe80f | ||
|
54953fa208 | ||
|
b7ccf81c7a | ||
|
3792a98426 | ||
|
5dd79e9632 | ||
|
9ae938ca8c | ||
|
05aa7157df | ||
|
275bd94148 | ||
|
146cb039c3 | ||
|
41fdadb09c | ||
|
4ed6e364e6 | ||
|
d0cd3b0f38 | ||
|
f216417fd2 | ||
|
7d1a4500ef | ||
|
a408387bff | ||
|
8c62905d39 | ||
|
3cd17bb908 | ||
|
17b3712000 | ||
|
a925ddfc47 | ||
|
5dd5828e88 | ||
|
2645f69853 | ||
|
f834da11c5 | ||
|
8c934e9bfc | ||
|
9c0606e036 | ||
|
832387d32d | ||
|
84f5677abb | ||
|
e790764915 | ||
|
b9bb5962ae | ||
|
13cbe89167 | ||
|
07f5b5f8ba | ||
|
a12b1f8d7a | ||
|
97d011acc0 | ||
|
b7efb37632 | ||
|
3edda27ef2 | ||
|
e017d76351 | ||
|
427816fd9a | ||
|
29614b5b78 | ||
|
063f69222a | ||
|
95a36bc9e2 | ||
|
8857c851f6 | ||
|
632f48e75a | ||
|
6a9c2eb80e | ||
|
be058e11d9 | ||
|
ce9ca63bc7 | ||
|
16ab09d7df | ||
|
738ed656fc | ||
|
e6708d4c5b | ||
|
af77b9b9b1 | ||
|
0c1a9d7f1d | ||
|
4f544e465b | ||
|
64c496f815 | ||
|
8bb8c00a6b | ||
|
26ef0d4a34 | ||
|
8240d8dc99 | ||
|
da4c792046 | ||
|
b06528d10c | ||
|
662270364b | ||
|
413ca8bdeb | ||
|
2a97218991 | ||
|
ec7b3769b5 | ||
|
f07473e1ca | ||
|
3be23ad7f9 | ||
|
ffd85e6bd1 | ||
|
073c89db4b | ||
|
04aae0486a | ||
|
929147f182 | ||
|
eb9241d315 | ||
|
0648075555 | ||
|
3f81346e6b | ||
|
a25fb1348d | ||
|
ae1ea99ed3 | ||
|
9a381f6d32 | ||
|
3a07b4838a | ||
|
29de6f2d06 | ||
|
73ba73fd03 | ||
|
82126ece78 | ||
|
9866649fdc | ||
|
4ff3f71cb8 | ||
|
4abda332de | ||
|
c2aecb0597 | ||
|
ba71a7a27c | ||
|
c8436f519c | ||
|
beca56ba3c | ||
|
036e790a75 | ||
|
5991b07385 | ||
|
7ba5f3757d | ||
|
ee6ee8139a | ||
|
f820b4fd6f | ||
|
3bec5d2cab | ||
|
b9aa0a2cf1 | ||
|
65f2c0fcf1 | ||
|
c7d0803000 | ||
|
1a4b9b440d | ||
|
1f914c2b4d | ||
|
fadb316451 | ||
|
3fefd3c1fb | ||
|
2cbb314d0b | ||
|
535fc70433 | ||
|
10fa276bec | ||
|
e5e38d7f77 | ||
|
20134bb96a | ||
|
1cc9afaeb3 | ||
|
6f5e621561 | ||
|
1dd6f2d9f8 | ||
|
f92b45db6a | ||
|
3a0fe79dd8 | ||
|
6598df9ed9 | ||
|
c971d54aea | ||
|
2abccafb8f | ||
|
75d0415bec | ||
|
4ff428a9a6 | ||
|
d539d8eaf8 | ||
|
96889b43a8 | ||
|
02a572cbc6 | ||
|
3f35a18d41 | ||
|
b9c283c89b | ||
|
3e96aad10e | ||
|
a671c4f86b | ||
|
157b1875c5 | ||
|
af89ebaeea | ||
|
4259f260a9 | ||
|
4d973e7ab6 | ||
|
d603932e23 | ||
|
db64f372f2 | ||
|
810e22dc9c | ||
|
a3cbc4477f | ||
|
6b993b22db | ||
|
b8f9d199a8 | ||
|
b5ff0a7914 | ||
|
485f4f72ce | ||
|
37dddd05f6 | ||
|
dfb350e206 | ||
|
4e14e8d22a | ||
|
222d1594ca | ||
|
3c6075ad96 | ||
|
4e15badb14 | ||
|
3cd252f877 | ||
|
b259d376b0 | ||
|
fc288bc92b | ||
|
7e25684399 | ||
|
a0adc8fc74 | ||
|
8b042ef470 | ||
|
db4ad1a78d | ||
|
f05a6648a7 | ||
|
297680b7c2 | ||
|
8166f820b4 | ||
|
4f20966ddd | ||
|
d447aac9ae | ||
|
9c485a952f | ||
|
159e7ab4af | ||
|
3750a7ebd6 | ||
|
bfe03b91b2 | ||
|
3b028ecab1 | ||
|
989b7339a0 | ||
|
d00813e1d6 | ||
|
aa004201b2 | ||
|
eed5554551 | ||
|
88427e4a05 | ||
|
8248660c52 | ||
|
25963ec982 | ||
|
7da28658a5 | ||
|
5944756b78 | ||
|
58fea6b205 | ||
|
4977cb6d68 | ||
|
930a1f1eb4 | ||
|
973013cbdf | ||
|
d38737669d | ||
|
fbf9792b38 | ||
|
184dcba882 | ||
|
d916fbd7be | ||
|
66376f05ec | ||
|
d633d6d2a9 | ||
|
7a9a6f5169 | ||
|
b19f776e68 | ||
|
4396ebaa72 | ||
|
697b58d33e | ||
|
b8129ccd15 | ||
|
6896097eb7 | ||
|
d50e2a6832 | ||
|
8df13adac7 | ||
|
d978993d06 | ||
|
5862b13dac | ||
|
5db9b1bc7b | ||
|
fe59f29cbc | ||
|
0dfaf3fd35 | ||
|
c4b1985076 | ||
|
d213266c52 | ||
|
29f09e7cd8 | ||
|
860130f08c | ||
|
d8c3ddca16 | ||
|
df67ded9f1 | ||
|
5f2297ed7c | ||
|
de9fde0d9c | ||
|
fe045c13f4 | ||
|
b76bdfcb22 | ||
|
9b8cc006ba | ||
|
c9c85875a5 | ||
|
a9e868cb7e | ||
|
5622b07047 | ||
|
e6223be905 | ||
|
cecf973777 | ||
|
108fcd46ec | ||
|
44b724ea9f | ||
|
03b2097173 | ||
|
29821069d8 | ||
|
3ae2f48846 | ||
|
cc730b00b2 | ||
|
d23dfb831b | ||
|
711747efc0 | ||
|
45216c7563 | ||
|
a9f42dd945 | ||
|
d8cfa1110d | ||
|
e41bc9a84f | ||
|
0ab14264c0 | ||
|
14622ef75b | ||
|
4dc4182236 | ||
|
12e04a634b | ||
|
51f978e78e | ||
|
b8e1282eba | ||
|
758608419b | ||
|
e566c4017c | ||
|
35f097542b | ||
|
06d3f96706 | ||
|
a8d0bf52d2 | ||
|
1bcacece65 | ||
|
25863c527f | ||
|
1905d8804d | ||
|
65b9a3f9b6 | ||
|
43865b0ad0 | ||
|
907f67eb83 | ||
|
f205269d97 | ||
|
0aa9db450d | ||
|
4f121f8289 | ||
|
42b8db8609 | ||
|
61295bf7ac | ||
|
f99ba7f992 | ||
|
fa1b94da10 | ||
|
16e6a4b170 | ||
|
aea8d703e1 | ||
|
bb9d0869ac | ||
|
aa13e113fa | ||
|
6332e78375 | ||
|
f1f69c6fdf | ||
|
e139e3d999 | ||
|
fadaef5105 | ||
|
bb1e69e73f | ||
|
dc9ad20d99 | ||
|
e9111f74c5 | ||
|
322b5b4343 | ||
|
4e4d2c64b4 | ||
|
a1b4d66f47 | ||
|
a495bd9605 | ||
|
b0b44550a1 | ||
|
5b760717bf | ||
|
e48a0d9b54 | ||
|
1ff345ab1d | ||
|
7c9a624eab | ||
|
8b50e8d3ad | ||
|
6f848254bc | ||
|
680d3ccc86 | ||
|
e42e2c15e2 | ||
|
d41f787efd | ||
|
e851f42aee | ||
|
2ebd4979b0 | ||
|
91b290e474 | ||
|
61c75b24bd | ||
|
2cd5fa6934 | ||
|
453dc6e7ea | ||
|
1ec6d2538e | ||
|
54a28d65b8 | ||
|
9123cbbaef | ||
|
9e35866a47 | ||
|
a21b69a217 | ||
|
933af8c706 | ||
|
5514dc577e | ||
|
6195ba0896 | ||
|
9a3f930148 | ||
|
e7e4b0f48b | ||
|
74f6d2bb9e | ||
|
6df9a98fb6 | ||
|
19e88e3e67 | ||
|
95ef8eb3de | ||
|
25f5bd26b4 | ||
|
4e66758048 | ||
|
bd13eca0f3 | ||
|
4656cf50b5 | ||
|
77a5353469 | ||
|
4de866de20 | ||
|
472edb6e8c | ||
|
7982a73aa7 | ||
|
a6943af686 | ||
|
be87629aba | ||
|
c364b20dd9 | ||
|
acb8380b61 | ||
|
d2bbecc5f5 | ||
|
9ef075c67e | ||
|
c917a77639 | ||
|
e26d7b9c9c | ||
|
ba51cbf64e | ||
|
62db844911 | ||
|
530031e088 | ||
|
f601a17daa | ||
|
438f05a39a | ||
|
7b060f2e7e | ||
|
68dea8b258 | ||
|
9bb629d0a8 | ||
|
2a658d2ff6 | ||
|
6c1de427ff | ||
|
6967ee0ace | ||
|
7ab48613c3 | ||
|
67ecd89d5f | ||
|
cd55d01f72 | ||
|
e4a7e4efa1 | ||
|
e409856d62 | ||
|
d0be3bd6da | ||
|
0f42ba7590 | ||
|
964b7ae5f0 | ||
|
d946e53c60 | ||
|
3aef401092 | ||
|
662c425743 | ||
|
65bd7ea428 | ||
|
9f3194d315 | ||
|
5e82625b76 | ||
|
c750af1b9a | ||
|
5e376db385 | ||
|
45cdd6911a | ||
|
f898bc0351 | ||
|
5fd491a4e7 | ||
|
145c526fc1 | ||
|
247340632b | ||
|
58f189f987 | ||
|
f2b857a609 | ||
|
e450668b9d | ||
|
a33e856b48 | ||
|
250df250ff | ||
|
dcefae806d | ||
|
05a6a21e9e | ||
|
ee56748348 | ||
|
09ce971708 | ||
|
aa84f5583e | ||
|
18a2efa8da | ||
|
14c10b6631 | ||
|
a74f5caaff | ||
|
71496cd56a | ||
|
2ccd89ed2e | ||
|
38403e00b4 | ||
|
743ba939ca | ||
|
af101d0b41 | ||
|
581787a775 | ||
|
9fa6fd5133 | ||
|
3f28be0854 | ||
|
02dd2af0d3 | ||
|
0d68d2341b | ||
|
9131c37d8e | ||
|
597d0685ae | ||
|
df5bb9b722 | ||
|
62d7d6fbd5 | ||
|
5512cee539 | ||
|
613f25a79e | ||
|
52529b7d05 | ||
|
5ecfab3c0e | ||
|
39554e11be | ||
|
152e2147e6 | ||
|
a4e32e7833 | ||
|
fb3bfbde70 | ||
|
cfde899b2c | ||
|
dc7339a174 | ||
|
931461e10d | ||
|
1edc4ec878 | ||
|
4e5741f55d | ||
|
b2bdb5c818 | ||
|
bfe38d9890 | ||
|
87a8af9457 | ||
|
e738126bed | ||
|
de47771c12 | ||
|
578771ffe1 | ||
|
683a817c55 | ||
|
d190008f2c | ||
|
f4e9af18b5 | ||
|
620d65e217 | ||
|
202f6c71e6 | ||
|
ba12b632b4 | ||
|
850ef7208d | ||
|
1888c2e2a1 | ||
|
076de31dcc | ||
|
498fabf97d | ||
|
862029a0ba | ||
|
12628a62d8 | ||
|
b6835364b3 | ||
|
ae2294c83b | ||
|
a8f69fbd2b | ||
|
fa1c6ffa33 | ||
|
99affffd32 | ||
|
fea6a3d1ee | ||
|
cafc15197c | ||
|
2fc37eef61 | ||
|
fec67b7622 | ||
|
b7703e30f9 | ||
|
28d13b57d6 | ||
|
95074b4834 | ||
|
f5be0fae3d | ||
|
a946a0cb44 | ||
|
8406ff04b7 | ||
|
f04c1f7fa8 | ||
|
925d92c4b8 | ||
|
6fc53fdfe7 | ||
|
9757b9900e | ||
|
33f37ae593 | ||
|
6a25dbfb05 | ||
|
d833ebadfc | ||
|
091f5df97d | ||
|
cda099a2f2 | ||
|
3b9df0d110 | ||
|
852f7a6468 | ||
|
79e691e4f3 | ||
|
be24f18d84 | ||
|
e20a41a186 | ||
|
360d2c9005 | ||
|
fd03eb95eb | ||
|
db4f2db748 | ||
|
e9f00dcb75 | ||
|
5ece17d67a | ||
|
e64b1f67c1 | ||
|
c9e8368694 | ||
|
f556f611b0 | ||
|
0b500d8e05 | ||
|
b91d96278b | ||
|
6122051431 | ||
|
9b4419088c | ||
|
b47c3c482d | ||
|
50dc701ec4 | ||
|
87437fbddc | ||
|
25977408df | ||
|
9d3b31b56e | ||
|
d343bce815 | ||
|
69efddafec | ||
|
ca0a2cba15 | ||
|
58d774a3f3 | ||
|
320771d7f8 | ||
|
9d6229df58 | ||
|
33a291f760 | ||
|
d4c832b8de | ||
|
0b3fda9da8 | ||
|
3f620ecf19 | ||
|
85d803afcf | ||
|
2dab31209e | ||
|
95980c5e14 | ||
|
31db42ae0e | ||
|
830cbd852e | ||
|
c15c69fb08 | ||
|
5ab47ef815 | ||
|
9ba0625824 | ||
|
2907eb074d | ||
|
40a4b58757 | ||
|
0fc04fc34e | ||
|
038c9ef23c | ||
|
fffb2e2cbc | ||
|
58ef298a42 | ||
|
6f01c38a71 | ||
|
23c17ca841 | ||
|
a66e8eb8ed | ||
|
42582158bf | ||
|
1864a3e6ba | ||
|
07b35bb20f | ||
|
a8a2de9d24 | ||
|
da24bb2bfc | ||
|
9ea2494940 | ||
|
5459ad3e7e | ||
|
b94620819c | ||
|
290db0105f | ||
|
a7f7ab0ef9 | ||
|
87a4e09862 | ||
|
121e2b34b5 | ||
|
3ccf780ed3 | ||
|
a92aa66358 | ||
|
84d28a0476 | ||
|
cdb90aa35c | ||
|
aa63457d9c | ||
|
6f46e2deb6 | ||
|
c11e17b5e2 | ||
|
e5a74344f3 | ||
|
24e71b5a39 | ||
|
b437b78668 | ||
|
cd813a14b1 | ||
|
adadcbc982 | ||
|
8443265142 | ||
|
d00cc73261 | ||
|
62f18bdc2c | ||
|
f8365cc939 | ||
|
273cad8318 | ||
|
f5dd87b02a | ||
|
1249a4c491 | ||
|
757113c002 | ||
|
110dfbd13c | ||
|
9fd29871ca | ||
|
c921120216 | ||
|
d248bca299 | ||
|
90c4fd3328 | ||
|
e6fabc81b3 | ||
|
adb54e3b87 | ||
|
f1c1524e61 | ||
|
a830d9b082 | ||
|
9e5aac759d | ||
|
87c823d497 | ||
|
610d393923 | ||
|
bec53bd836 | ||
|
4682fb3ec8 | ||
|
e266634560 | ||
|
a37bae5b9d | ||
|
a5ad4cd0a5 | ||
|
8deb39ac76 | ||
|
8aa18fbf20 |
@ -1,29 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.rs]
|
||||
indent_size = 4
|
||||
|
||||
[*.kt]
|
||||
indent_size = 4
|
||||
|
||||
[*.gradle]
|
||||
indent_size = 4
|
||||
|
||||
[tests/**/*.rs]
|
||||
charset = utf-8
|
||||
end_of_line = unset
|
||||
indent_size = unset
|
||||
indent_style = unset
|
||||
trim_trailing_whitespace = unset
|
||||
insert_final_newline = unset
|
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Enhancement request
|
||||
about: Request a new feature or change to an existing feature
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the enhancement**
|
||||
<!-- A clear and concise description of what you would like added or changed. -->
|
||||
|
||||
**Use case**
|
||||
<!-- Tell us how you or others will use this new feature or change to an existing feature. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the enhancement here. -->
|
95
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
95
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
---
|
||||
name: Release
|
||||
about: Create a new release [for release managers only]
|
||||
title: 'Release MAJOR.MINOR.PATCH'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Part 1: Bump BDK Rust Version
|
||||
|
||||
1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary.
|
||||
2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge.
|
||||
|
||||
# Part 2: Prepare Libraries for Release Branch
|
||||
|
||||
### _Android_
|
||||
|
||||
3. - [ ] Update the API docs to reflect the changes in the API
|
||||
4. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in the bdk-android directory to make sure you're building the library from scratch.
|
||||
5. - [ ] Build the library and run the offline and live tests, and adjust them if necessary (note that you'll need an Android emulator running).
|
||||
```shell
|
||||
# start an emulator prior to running the tests
|
||||
cd ./bdk-android/
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
6. - [ ] Update the readme if necessary
|
||||
|
||||
### _JVM_
|
||||
|
||||
7. - [ ] Update the API docs to reflect the changes in the API
|
||||
8. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in bdk-jvm directory to make sure you're building the library from scratch.
|
||||
9. - [ ] Build the library and run the tests, and adjust if necessary
|
||||
```shell
|
||||
cd ./bdk-jvm/
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
10. - [ ] Update the readme if necessary
|
||||
|
||||
### _Swift_
|
||||
|
||||
11. - [ ] Delete the `target` directory in bdk-ffi
|
||||
12. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
cd ./bdk-swift/
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
13. - [ ] Update the readme if necessary
|
||||
|
||||
### _Python_
|
||||
|
||||
14. - [ ] Delete the `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches
|
||||
15. - [ ] Build the library
|
||||
```shell
|
||||
cd ./bdk-python/
|
||||
just clean
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
16. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
|
||||
python -m unittest --verbose
|
||||
```
|
||||
17. - [ ] Update the readme and `setup.py` if necessary
|
||||
18. - [ ] Update the Android, JVM, Python, and Swift libraries as per the _Specific Libraries' Workflows_ section above. Open a single PR on master for all of these changes called `Prepare language bindings libraries for 0.X release`. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/315).
|
||||
|
||||
## Part 3: Release Workflow
|
||||
|
||||
19. - [ ] Create a new branch off of `master` called `release/<feature version>`, e.g. `release/0.31`
|
||||
20. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
||||
21. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
||||
22. - [ ] Update bdk-python version from `.dev` version to release version
|
||||
23. - [ ] Open a PR to that release branch that updates the Android, JVM, and Python libraries' versions in the three steps above. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316).
|
||||
24. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions
|
||||
25. - [ ] Create the tag for the release and make sure to add the changelog info to the tag (works better if you prepare the tag message on the side in a text editor). Push the tag to GitHub.
|
||||
```shell
|
||||
git tag v0.6.0 --sign --edit
|
||||
git push upstream v0.6.0
|
||||
```
|
||||
26. - [ ] Trigger manual releases for all 4 libraries (for Swift, go on the [bdk-swift](https://github.com/bitcoindevkit/bdk-swift) trigger the release on `master` and simply add the version number and tag name in the text fields when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`, but the tag will have it, i.e. `v0.26.0`).
|
||||
27. - [ ] Make sure the released libraries work and contain the artifacts you would expect
|
||||
28. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file
|
||||
29. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
|
||||
30. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any
|
||||
31. - [ ] Open a PR on master with the changes in steps 29, 30, and 31. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/317). Get a review and merge the PR.
|
||||
32. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
|
||||
33. - [ ] Post in the announcement channel
|
||||
34. - [ ] Tweet about the library
|
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@ -1,4 +1,4 @@
|
||||
<!-- You can erase any parts of this template not applicable to your Pull Request. -->
|
||||
<!-- Erase any parts of this template not applicable to your Pull Request. -->
|
||||
|
||||
### Description
|
||||
|
||||
@ -9,6 +9,11 @@
|
||||
<!-- In this section you can include notes directed to the reviewers, like explaining why some parts
|
||||
of the PR were done in a specific way -->
|
||||
|
||||
### Changelog notice
|
||||
|
||||
<!-- Notice the release manager should include in the release tag message changelog -->
|
||||
<!-- See https://keepachangelog.com/en/1.0.0/ for examples -->
|
||||
|
||||
### Checklists
|
||||
|
||||
#### All Submissions:
|
||||
@ -21,7 +26,6 @@ of the PR were done in a specific way -->
|
||||
|
||||
* [ ] I've added tests for the new feature
|
||||
* [ ] I've added docs for the new feature
|
||||
* [ ] I've updated `CHANGELOG.md`
|
||||
|
||||
#### Bugfixes:
|
||||
|
||||
|
16
.github/workflows/audit.yml
vendored
16
.github/workflows/audit.yml
vendored
@ -9,11 +9,17 @@ on:
|
||||
- cron: '0 0 * * 0' # Once per week
|
||||
|
||||
jobs:
|
||||
|
||||
security_audit:
|
||||
name: Security audit
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Run audit"
|
||||
run: |
|
||||
cargo install cargo-audit
|
||||
cargo-audit audit
|
||||
|
85
.github/workflows/cont_integration.yml
vendored
85
.github/workflows/cont_integration.yml
vendored
@ -1,61 +1,84 @@
|
||||
on: [push, pull_request]
|
||||
|
||||
name: CI
|
||||
name: Rust layer CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
|
||||
jobs:
|
||||
|
||||
build-test:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-latest
|
||||
name: "Build and test"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.60.0 # STABLE
|
||||
- version: 1.77.1
|
||||
clippy: true
|
||||
- version: 1.57.0 # MSRV
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Generate cache key
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Generate cache key"
|
||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
- name: Set default toolchain
|
||||
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default ${{ matrix.rust.version }}
|
||||
- name: Set profile
|
||||
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
- name: Add clippy
|
||||
|
||||
- name: "Add clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: rustup component add clippy
|
||||
- name: Update toolchain
|
||||
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
- name: Build
|
||||
|
||||
- name: "Build"
|
||||
run: cargo build
|
||||
- name: Clippy
|
||||
|
||||
- name: "Clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
- name: Test
|
||||
run: cargo test
|
||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
||||
|
||||
- name: "Test"
|
||||
run: CLASSPATH=./tests/jna/jna-5.14.0.jar cargo test --features uniffi/bindgen-tests
|
||||
|
||||
fmt:
|
||||
name: Rust fmt
|
||||
runs-on: ubuntu-latest
|
||||
name: "Rust fmt"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set default toolchain
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default nightly
|
||||
- name: Set profile
|
||||
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
- name: Add rustfmt
|
||||
|
||||
- name: "Add rustfmt"
|
||||
run: rustup component add rustfmt
|
||||
- name: Update toolchain
|
||||
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
- name: Check fmt
|
||||
|
||||
- name: "Check fmt"
|
||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
||||
|
96
.github/workflows/live-tests.yaml
vendored
Normal file
96
.github/workflows/live-tests.yaml
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
name: Run All Live Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Once per week
|
||||
|
||||
jobs:
|
||||
jvm-tests:
|
||||
name: "Build and test JVM library on Linux"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: "Run live JVM tests"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew test
|
||||
|
||||
swift-tests:
|
||||
name: "Build and test iOS library on macOS"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Build Swift package"
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-xcframework.sh
|
||||
|
||||
- name: "Run live Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: swift test
|
||||
|
||||
python-tests:
|
||||
name: "Build and test Python library on Linux"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- cp310-cp310
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
|
||||
- name: "Run live Python tests"
|
||||
run: ${PYBIN}/python -m unittest --verbose
|
48
.github/workflows/publish-android.yaml
vendored
Normal file
48
.github/workflows/publish-android.yaml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: Publish bdk-android to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
- name: "Build bdk-android library"
|
||||
run: |
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
|
||||
- name: "Publish to Maven Central"
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }}
|
||||
ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }}
|
||||
run: |
|
||||
cd bdk-android
|
||||
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
|
132
.github/workflows/publish-jvm.yaml
vendored
Normal file
132
.github/workflows/publish-jvm.yaml
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
name: Publish bdk-jvm to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build-macOS-native-libs:
|
||||
name: "Create M1 and x86_64 native binaries"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install aarch64 Rust target"
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: "Upload macOS native libraries for reuse in publishing job"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-macos
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
|
||||
|
||||
build-windows-native-lib:
|
||||
name: "Create Windows native binaries"
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install x86_64-pc-windows-msvc Rust target"
|
||||
run: rustup target add x86_64-pc-windows-msvc
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: "Upload Windows native libraries for reuse in publishing job"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-windows
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-jvm\lib\src\main\resources\
|
||||
|
||||
build-full-library:
|
||||
name: Create full bdk-jvm library
|
||||
needs: [build-macOS-native-libs, build-windows-native-lib]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: "Download macOS native binaries from previous job"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifact-macos
|
||||
path: ./bdk-jvm/lib/src/main/resources/
|
||||
|
||||
- name: "Download Windows native libraries from previous job"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifact-windows
|
||||
path: ./bdk-jvm/lib/src/main/resources/
|
||||
|
||||
- name: "Upload library code and binaries"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-full
|
||||
path: ./bdk-jvm/lib/
|
||||
|
||||
- name: "Publish to Maven Central"
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }}
|
||||
ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }}
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
|
189
.github/workflows/publish-python.yaml
vendored
Normal file
189
.github/workflows/publish-python.yaml
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
name: Publish bdkpython to PyPI
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build-manylinux_2_28-x86_64-wheels:
|
||||
name: "Build Manylinux 2.28 x86_64 wheel"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||
env:
|
||||
PLAT: manylinux_2_28_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python: # Update this list whenever the docker image is updated (check /opt/python/)
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
- cp311-cp311
|
||||
- cp312-cp312
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-arm64-wheels:
|
||||
name: "Build macOS arm64 wheel"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-arm64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
|
||||
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-arm64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-x86_64-wheels:
|
||||
name: "Build macOS x86_64 wheel"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-x86_64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-x86_64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-windows-wheels:
|
||||
name: "Build Windows wheel"
|
||||
runs-on: windows-2022
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-windows.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-win-${{ matrix.python }}
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
|
||||
|
||||
publish-pypi:
|
||||
name: "Publish on PyPI"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
needs: [build-manylinux_2_28-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Download artifacts in dist/ directory"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: dist/
|
||||
|
||||
# - name: "Publish on test PyPI"
|
||||
# uses: pypa/gh-action-pypi-publish@release/v1
|
||||
# with:
|
||||
# user: __token__
|
||||
# password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
||||
# repository_url: https://test.pypi.org/legacy/
|
||||
# packages_dir: dist/*/
|
||||
|
||||
- name: "Publish on PyPI"
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: dist/*/
|
57
.github/workflows/test-android.yaml
vendored
Normal file
57
.github/workflows/test-android.yaml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
name: Test Android
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-android/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-android/**"
|
||||
|
||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Show default version of NDK"
|
||||
run: echo $ANDROID_NDK_ROOT
|
||||
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
- name: "Build Android library"
|
||||
run: |
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib --console=plain
|
||||
|
||||
# There are currently no unit tests for bdk-android (see the tests in bdk-jvm instead) and the
|
||||
# integration tests require the macOS image which is not working with the older NDK version we
|
||||
# are using, so for now we just make sure that the library builds and omit the connectedTest
|
||||
# - name: "Run Android connected tests"
|
||||
# run: |
|
||||
# cd bdk-android
|
||||
# ./gradlew connectedAndroidTest --console=plain
|
41
.github/workflows/test-jvm.yaml
vendored
Normal file
41
.github/workflows/test-jvm.yaml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Test Kotlin/JVM
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-jvm/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-jvm/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Run JVM tests"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew test -P excludeConnectedTests
|
194
.github/workflows/test-python.yaml
vendored
Normal file
194
.github/workflows/test-python.yaml
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
name: Test Python
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
|
||||
jobs:
|
||||
build-manylinux_2_28-x86_64-wheels:
|
||||
name: "Build and test Manylinux 2.28 x86_64 wheels"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux_2_28_x86_64
|
||||
env:
|
||||
PLAT: manylinux_2_28_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
- cp311-cp311
|
||||
- cp312-cp312
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Rust 1.77.1"
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.77.1
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_28_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
run: ${PYBIN}/python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-arm64-wheels:
|
||||
name: "Build and test macOS arm64 wheels"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-arm64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
|
||||
|
||||
# You can't install the arm64 wheel on the CI, so we skip these steps and simply test that the wheel builds
|
||||
# - name: "Install wheel and run tests"
|
||||
# run: |
|
||||
# pip3 install ./dist/*.whl
|
||||
# python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-arm64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-x86_64-wheels:
|
||||
name: "Build and test macOS x86_64 wheels"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-x86_64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: pip3 install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
run: python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-x86_64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-windows-wheels:
|
||||
name: "Build and test Windows wheels"
|
||||
runs-on: windows-2022
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-windows.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-windows-${{ matrix.python }}
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: Get-ChildItem 'D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl' | ForEach-Object {pip install $_.FullName}
|
||||
shell: powershell
|
||||
|
||||
- name: "Run tests"
|
||||
run: python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
27
.github/workflows/test-swift.yaml
vendored
Normal file
27
.github/workflows/test-swift.yaml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Test Swift
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-swift/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-swift/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build and test"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Build Swift package"
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-xcframework.sh
|
||||
|
||||
- name: "Run Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests
|
25
.gitignore
vendored
25
.gitignore
vendored
@ -1,7 +1,5 @@
|
||||
target
|
||||
build
|
||||
Cargo.lock
|
||||
/bindings/bdk-kotlin/local.properties
|
||||
.gradle
|
||||
wallet_db
|
||||
bdk_ffi_test
|
||||
@ -15,3 +13,26 @@ xcuserdata
|
||||
.lsp
|
||||
.clj-kondo
|
||||
.idea/
|
||||
.editorconfig
|
||||
bdk.kt
|
||||
|
||||
# Swift related
|
||||
/.build
|
||||
.swiftpm
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
bdkFFI.xcframework.zip
|
||||
bdkFFI
|
||||
libbdkffi.a
|
||||
bdkFFI.h
|
||||
BitcoinDevKit.swift
|
||||
bdk.swift
|
||||
.build
|
||||
*.xcframework/
|
||||
Info.plist
|
||||
|
||||
# Python related
|
||||
__pycache__
|
237
CHANGELOG.md
237
CHANGELOG.md
@ -1,15 +1,221 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
Changelog information can also be found in each release's git tag (which can be viewed with `git tag -ln100 "v*"`), as well as on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
- Update BDK to version 0.20.0
|
||||
## [v1.0.0-alpha.11]
|
||||
This release brings the latest alpha 11 release of the Rust bdk_wallet library, as well as the new Electrum client, the new memory wallet, and a whole lot of new types and APIs across the library. Also of note are the much simpler-to-use full_scan and sync workflows for syncing wallets.
|
||||
|
||||
Added:
|
||||
- `Amount` type [#533]
|
||||
- `TxIn` type [#536]
|
||||
- `Transaction.input()` method [#536]
|
||||
- `Transaction.output()` method [#536]
|
||||
- `Transaction.lock_time()` method [#536]
|
||||
- `Electrum` client [#535]
|
||||
- Memory wallet [#528]
|
||||
|
||||
[#528]: https://github.com/bitcoindevkit/bdk-ffi/pull/528
|
||||
[#533]: https://github.com/bitcoindevkit/bdk-ffi/pull/533
|
||||
[#535]: https://github.com/bitcoindevkit/bdk-ffi/pull/535
|
||||
[#536]: https://github.com/bitcoindevkit/bdk-ffi/pull/536
|
||||
|
||||
## [v1.0.0-alpha.7]
|
||||
This release brings back into the 1.0 API a number of APIs from the 0.31 release, and adds the new flat file persistence feature, as well as more fine-grain errors.
|
||||
|
||||
## [v1.0.0-alpha.2a]
|
||||
This release is the first alpha release of the 1.0 API for the bindings libraries. Here is what is now available:
|
||||
- Create and recover wallets using descriptors, including the four descriptor templates
|
||||
- Sync a wallet using a blocking Esplora client
|
||||
- Query the wallet for balance and addresses
|
||||
- Create and sign transactions using the transaction builder
|
||||
- Broadcast transactions
|
||||
|
||||
## [v0.31.0]
|
||||
This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2.
|
||||
|
||||
- APIs Changed:
|
||||
- `BumpFeeTxBuilder.allow_shrinking()` now takes a `Script` as its argument [#443]
|
||||
- The `Address` constructor now takes a `Network` argument [#443]
|
||||
- The `Payload::PubkeyHash` and `Payload::ScriptHash` now have string arguments instead of byte arrays [#443]
|
||||
- APIs Added:
|
||||
- The `Address` type now has the `is_valid_for_network()` method [#443]
|
||||
|
||||
[#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443
|
||||
|
||||
## [v0.30.0]
|
||||
This release has a new API and a few internal optimizations and refactorings.
|
||||
|
||||
- APIs Added
|
||||
- `TxBuilder.add_data(data: Vec<u8>)`
|
||||
- `Wallet.list_unspent()` returns `Vec<LocalUtxo>`
|
||||
- Add BIP-86 descriptor templates [#388]
|
||||
|
||||
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
|
||||
|
||||
## [v0.29.0]
|
||||
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
|
||||
|
||||
Changelog
|
||||
- Add support for Windows in bdk-jvm [#336]
|
||||
- Add support for older version of Linux distros in bdk-jvm [#345]
|
||||
- APIs added
|
||||
- Expose `is_mine()` method on the `Wallet` type [#355]
|
||||
- Expose `to_bytes()` method on the `Script` type [#369]
|
||||
|
||||
[#336]: https://github.com/bitcoindevkit/bdk-ffi/pull/336
|
||||
[#345]: https://github.com/bitcoindevkit/bdk-ffi/pull/345
|
||||
[#355]: https://github.com/bitcoindevkit/bdk-ffi/pull/355
|
||||
[#369]: https://github.com/bitcoindevkit/bdk-ffi/pull/369
|
||||
|
||||
## [v0.28.0]
|
||||
- Update BDK to version 0.28.0 [#341]
|
||||
- Drop support of pypy releases of Python libraries [#351]
|
||||
- Drop support for Python 3.6 and 3.7 [#351]
|
||||
- Drop support for very old Linux versions that do not support the manylinux_2_17_x86_64 platform tag [#351]
|
||||
- APIs changed:
|
||||
- Expose Address payload and network properties. [#325]
|
||||
- Add SignOptions to Wallet.sign() params. [#326]
|
||||
- address field on `AddressInfo` type is now of type `Address` [#333]
|
||||
- new PartiallySignedTransaction.json_serialize() function to get JSON serialized value of all PSBT fields. [#334]
|
||||
- Add from_script constructor to `Address` type [#337]
|
||||
|
||||
[#325]: https://github.com/bitcoindevkit/bdk-ffi/pull/325
|
||||
[#326]: https://github.com/bitcoindevkit/bdk-ffi/pull/326
|
||||
[#333]: https://github.com/bitcoindevkit/bdk-ffi/pull/333
|
||||
[#334]: https://github.com/bitcoindevkit/bdk-ffi/pull/334
|
||||
[#337]: https://github.com/bitcoindevkit/bdk-ffi/pull/337
|
||||
[#341]: https://github.com/bitcoindevkit/bdk-ffi/pull/341
|
||||
[#351]: https://github.com/bitcoindevkit/bdk-ffi/pull/351
|
||||
|
||||
## [v0.27.1]
|
||||
- Update BDK to version 0.27.1 [#312]
|
||||
- APIs changed
|
||||
- `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of the transaction bytes. [#296]
|
||||
- `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`. [#296]
|
||||
- APIs added
|
||||
- New `Transaction` structure that can be created from or serialized to consensus encoded bytes. [#296]
|
||||
- Add Wallet.get_internal_address() API [#304]
|
||||
- Add `AddressIndex::Peek(index)` and `AddressIndex::Reset(index)` APIs [#305]
|
||||
|
||||
[#296]: https://github.com/bitcoindevkit/bdk-ffi/pull/296
|
||||
[#304]: https://github.com/bitcoindevkit/bdk-ffi/pull/304
|
||||
[#305]: https://github.com/bitcoindevkit/bdk-ffi/pull/305
|
||||
[#312]: https://github.com/bitcoindevkit/bdk-ffi/pull/312
|
||||
|
||||
## [v0.26.0]
|
||||
- Update BDK to version 0.26.0 [#288]
|
||||
- APIs changed
|
||||
- The descriptor and change_descriptor arguments on the wallet constructor now take a `Descriptor` instead of a `String`. [#260]
|
||||
- TxBuilder.drain_to() argument is now `Script` instead of address `String`. [#279]
|
||||
- APIs added
|
||||
- Added RpcConfig, BlockchainConfig::Rpc, and Auth [#125]
|
||||
- Added Descriptor type in [#260] with the following methods:
|
||||
- Default constructor requires a descriptor in String format and a Network
|
||||
- new_bip44 constructor returns a Descriptor with structure pkh(key/44'/{0,1}'/0'/{0,1}/*)
|
||||
- new_bip44_public constructor returns a Descriptor with structure pkh(key/{0,1}/*)
|
||||
- new_bip49 constructor returns a Descriptor with structure sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*))
|
||||
- new_bip49_public constructor returns a Descriptor with structure sh(wpkh(key/{0,1}/*))
|
||||
- new_bip84 constructor returns a Descriptor with structure wpkh(key/84'/{0,1}'/0'/{0,1}/*)
|
||||
- new_bip84_public constructor returns a Descriptor with structure wpkh(key/{0,1}/*)
|
||||
- as_string returns the public version of the output descriptor
|
||||
- as_string_private returns the private version of the output descriptor if available, otherwise return the public version
|
||||
|
||||
[#125]: https://github.com/bitcoindevkit/bdk-ffi/pull/125
|
||||
[#260]: https://github.com/bitcoindevkit/bdk-ffi/pull/260
|
||||
[#279]: https://github.com/bitcoindevkit/bdk-ffi/pull/279
|
||||
[#288]: https://github.com/bitcoindevkit/bdk-ffi/pull/288
|
||||
|
||||
## [v0.25.0]
|
||||
- Update BDK to version 0.25.0 [#272]
|
||||
- APIs Added:
|
||||
- from_string() constructors now available on DescriptorSecretKey and DescriptorPublicKey [#247]
|
||||
|
||||
[#247]: https://github.com/bitcoindevkit/bdk-ffi/pull/247
|
||||
[#272]: https://github.com/bitcoindevkit/bdk-ffi/pull/272
|
||||
|
||||
## [v0.11.0]
|
||||
- Update BDK to version 0.24.0 [#221]
|
||||
- APIs changed
|
||||
- The constructor on the DescriptorSecretKey type now takes a Mnemonic instead of a String.
|
||||
- APIs added
|
||||
- Added Mnemonic struct [#219] with following methods:
|
||||
- new(word_count: WordCount) generates and returns Mnemonic with random entropy
|
||||
- from_string(mnemonic: String) converts string Mnemonic to Mnemonic type with error
|
||||
- from_entropy(entropy: Vec<u8>) generates and returns Mnemonic with given entropy
|
||||
- as_string() view Mnemonic as string
|
||||
- APIs removed
|
||||
- generate_mnemonic(word_count: WordCount)
|
||||
|
||||
[#219]: https://github.com/bitcoindevkit/bdk-ffi/pull/219
|
||||
[#221]: https://github.com/bitcoindevkit/bdk-ffi/pull/221
|
||||
|
||||
## [v0.10.0]
|
||||
- Update BDK to version 0.23.0 [#204]
|
||||
- Update uniffi-rs to latest version 0.21.0 [#216]
|
||||
- Breaking Changes
|
||||
- Changed `TxBuilder.finish()` to return new `TxBuilderResult` [#209]
|
||||
- `TxBuilder.add_recipient()` now takes a `Script` instead of an `Address` [#192]
|
||||
- `AddressAmount` is now `ScriptAmount` [#192]
|
||||
- APIs Added
|
||||
- Added `TxBuilderResult` with PSBT and TransactionDetails [#209]
|
||||
- `Address` and `Script` structs have been added [#192]
|
||||
- Add `PartiallySignedBitcoinTransaction.extract_tx()` function [#192]
|
||||
- Add `secret_bytes()` method on the `DescriptorSecretKey` [#199]
|
||||
- Add `PartiallySignedBitcoinTransaction.combine()` method [#200]
|
||||
|
||||
[#192]: https://github.com/bitcoindevkit/bdk-ffi/pull/192
|
||||
[#199]: https://github.com/bitcoindevkit/bdk-ffi/pull/199
|
||||
[#200]: https://github.com/bitcoindevkit/bdk-ffi/pull/200
|
||||
[#204]: https://github.com/bitcoindevkit/bdk-ffi/pull/204
|
||||
[#209]: https://github.com/bitcoindevkit/bdk-ffi/pull/209
|
||||
[#216]: https://github.com/bitcoindevkit/bdk-ffi/pull/216
|
||||
|
||||
## [v0.9.0]
|
||||
- Breaking Changes
|
||||
- Rename `get_network()` method on `Wallet` interface to `network()` [#185]
|
||||
- Rename `get_transactions()` method on `Wallet` interface to `list_transactions()` [#185]
|
||||
- Remove `generate_extended_key`, returned ExtendedKeyInfo [#154]
|
||||
- Remove `restore_extended_key`, returned ExtendedKeyInfo [#154]
|
||||
- Remove dictionary `ExtendedKeyInfo {mnenonic, xprv, fingerprint}` [#154]
|
||||
- Remove interface `Transaction` [#190]
|
||||
- Changed `Wallet` interface `list_transaction()` to return array of `TransactionDetails` [#190]
|
||||
- Update `bdk` dependency version to 0.22 [#193]
|
||||
- APIs Added [#154]
|
||||
- `generate_mnemonic()`, returns string mnemonic
|
||||
- `interface DescriptorSecretKey`
|
||||
- `new(Network, string_mnenoinc, password)`, constructs DescriptorSecretKey
|
||||
- `derive(DerivationPath)`, derives and returns child DescriptorSecretKey
|
||||
- `extend(DerivationPath)`, extends and returns DescriptorSecretKey
|
||||
- `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey
|
||||
- `as_string()`, returns DescriptorSecretKey as String
|
||||
- `interface DescriptorPublicKey`
|
||||
- `derive(DerivationPath)` derives and returns child DescriptorPublicKey
|
||||
- `extend(DerivationPath)` extends and returns DescriptorPublicKey
|
||||
- `as_string()` returns DescriptorPublicKey as String
|
||||
- Add to `interface Blockchain` the `get_height()` and `get_block_hash()` methods [#184]
|
||||
- Add to `interface TxBuilder` the `set_recipients(recipient: Vec<AddressAmount>)` method [#186]
|
||||
- Add to `dictionary TransactionDetails` the `confirmation_time` field [#190]
|
||||
- Interfaces Added [#154]
|
||||
- `DescriptorSecretKey`
|
||||
- `DescriptorPublicKey`
|
||||
- `DerivationPath`
|
||||
|
||||
[#154]: https://github.com/bitcoindevkit/bdk-ffi/pull/154
|
||||
[#184]: https://github.com/bitcoindevkit/bdk-ffi/pull/184
|
||||
[#185]: https://github.com/bitcoindevkit/bdk-ffi/pull/185
|
||||
[#193]: https://github.com/bitcoindevkit/bdk-ffi/pull/193
|
||||
|
||||
## [v0.8.0]
|
||||
- Update BDK to version 0.20.0 [#169]
|
||||
- APIs Added
|
||||
- `TxBuilder.add_data(data: Vec<u8>)` [#163]
|
||||
- `Wallet.list_unspent()` returns `Vec<LocalUtxo>` [#158]
|
||||
- Add coin control methods on TxBuilder [#164]
|
||||
|
||||
[#163]: https://github.com/bitcoindevkit/bdk-ffi/pull/163
|
||||
[#158]: https://github.com/bitcoindevkit/bdk-ffi/pull/158
|
||||
[#164]: https://github.com/bitcoindevkit/bdk-ffi/pull/164
|
||||
[#169]: https://github.com/bitcoindevkit/bdk-ffi/pull/169
|
||||
[#190]: https://github.com/bitcoindevkit/bdk-ffi/pull/190
|
||||
|
||||
## [v0.7.0]
|
||||
- Update BDK to version 0.19.0
|
||||
@ -56,9 +262,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
||||
|
||||
## [v0.2.0]
|
||||
|
||||
[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.7.0...HEAD
|
||||
[v1.0.0-alpha.11]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.7...v1.0.0-alpha.11
|
||||
[v1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
|
||||
[v1.0.0-alpha.2a]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.31.0...v1.0.0-alpha.2a
|
||||
[v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0
|
||||
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
|
||||
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
|
||||
[v0.28.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.27.1...v0.28.0
|
||||
[v0.27.1]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.26.0...v0.27.1
|
||||
[v0.26.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.25.0...v0.26.0
|
||||
[v0.25.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.11.0...v0.25.0
|
||||
[v0.11.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.10.0...v0.11.0
|
||||
[v0.10.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...v0.10.0
|
||||
[v0.9.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.8.0...v0.9.0
|
||||
[v0.8.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.7.0...v0.8.0
|
||||
[v0.7.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...v0.7.0
|
||||
[v0.6.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.5.0...v0.6.0
|
||||
[v0.5.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.4.0...v0.5.0
|
||||
|
23
Cargo.toml
23
Cargo.toml
@ -1,23 +0,0 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "0.7.0"
|
||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[workspace]
|
||||
members = [".","bdk-ffi-bindgen"]
|
||||
default-members = [".", "bdk-ffi-bindgen"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
name = "bdkffi"
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "0.20", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
||||
|
||||
uniffi_macros = { version = "0.19.3", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "0.19.3", features = ["builtin-bindgen"] }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.19.3", features = ["builtin-bindgen"] }
|
35
DEVELOPMENT_CYCLE.md
Normal file
35
DEVELOPMENT_CYCLE.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Development Cycle
|
||||
|
||||
This project follows a regular releasing schedule similar to the one [used by the Rust language]
|
||||
except releases always follow the latest [`bdk`] release by one to two weeks. In short, this means
|
||||
that a new release is made at a regular cadence, with all the feature/bugfixes that made it to
|
||||
`master` in time. This ensures that we don't keep delaying releases waiting for
|
||||
"just one more little thing".
|
||||
|
||||
After making a new `bdk-ffi` release tag all downstream language bindings should also be updated.
|
||||
|
||||
This project uses [Semantic Versioning], but is currently at MAJOR version zero (0.y.z) meaning it
|
||||
is still in initial development. Anything MAY change at any time. The public API SHOULD NOT be
|
||||
considered stable. Until we reach version `1.0.0` we will do our best to document any breaking API
|
||||
changes in the changelog info attached to each release tag.
|
||||
|
||||
We decided to maintain a faster release cycle while the library is still in "beta", i.e. before
|
||||
release `1.0.0`: since we are constantly adding new features and, even more importantly, fixing
|
||||
issues, we want developers to have access to those updates as fast as possible. For this reason we
|
||||
will make a release **every 4 weeks**.
|
||||
|
||||
Once the project reaches a more mature state (>= `1.0.0`), we will very likely switch to longer
|
||||
release cycles of **6 weeks**.
|
||||
|
||||
The "feature freeze" will happen when [`bdk`] releases a release candidate. This project will then
|
||||
be updated and tested with [`bdk`] release candidates until a final release is published. This
|
||||
means a new branch will be created originating from the `master` tip at that time, and in that
|
||||
branch we will stop adding new features and only focus on ensuring the ones we've added are working
|
||||
properly.
|
||||
|
||||
To create a new release a release manager will create a new issue using a `Release` template and
|
||||
follow the template instructions.
|
||||
|
||||
[used by the Rust language]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
|
||||
[Semantic Versioning]: https://semver.org/
|
||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
14
PGP-BDK-BINDINGS.asc
Normal file
14
PGP-BDK-BINDINGS.asc
Normal file
@ -0,0 +1,14 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEYw6xkRYJKwYBBAHaRw8BAQdAg+VLXuidDqeP015H/QMlESJyQeIntTUoQkbk
|
||||
+IFu+jO0M2JpdGNvaW5kZXZraXQtYmluZGluZ3MgPGJpbmRpbmdzQGJpdGNvaW5k
|
||||
ZXZraXQub3JnPoiTBBMWCgA7FiEEiK2TrEWJ/QkP87jRJ2jEPogDxqMFAmMOsZEC
|
||||
GwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQJ2jEPogDxqPQTgEA292D
|
||||
RQaxDTJ4k91D0w50Vrd0NSNUwlsERz9XJ64abWABAP99vGMmq2pfrngTQqjLgLe8
|
||||
0YhQ+VML2x/B0LSN6MgNuDgEYw6xkRIKKwYBBAGXVQEFAQEHQEkUJv+/Wzx7nNiX
|
||||
eti3HkeT6ZNAuCExPE4F7jxHNQ1TAwEIB4h4BBgWCgAgFiEEiK2TrEWJ/QkP87jR
|
||||
J2jEPogDxqMFAmMOsZECGwwACgkQJ2jEPogDxqObPQEA/B0xNew03KM0JP630efG
|
||||
QT/3Caq/jx86pLwnB7XqWI8BAOKmqrOEiwCBjhaIpzC3/1M+aZuPRUL3V91uPxpM
|
||||
jFAJ
|
||||
=vvmK
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
183
README.md
183
README.md
@ -1,68 +1,155 @@
|
||||
# Native language bindings for BDK
|
||||
|
||||
The workspace in this repository creates the `libbdkffi` multi-language library for the rust based
|
||||
[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for
|
||||
generating the actual language binding code used to access the `libbdkffi` library.
|
||||
<p>
|
||||
<a href="https://github.com/bitcoindevkit/bdk-ffi/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
|
||||
<a href="https://github.com/bitcoindevkit/bdk-ffi/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk-ffi/workflows/CI/badge.svg"></a>
|
||||
<a href="https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html"><img alt="Rustc Version 1.61.0+" src="https://img.shields.io/badge/rustc-1.61.0%2B-lightgrey.svg"/></a>
|
||||
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
|
||||
</p>
|
||||
|
||||
Each supported language has its own repository that includes this project as a [git submodule].
|
||||
The rust code in this project is a wrapper around the [bdk] library to expose it's APIs in a
|
||||
uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
|
||||
## 🚨 Warning 🚨
|
||||
The `master` branch of this repository is being migrated to the [bdk 1.0 API](https://github.com/bitcoindevkit/bdk) and is incomplete. For production-ready libraries, use the [`0.31.X`](https://github.com/bitcoindevkit/bdk-ffi/tree/release/0.30) releases.
|
||||
|
||||
## Readme
|
||||
The workspace in this repository creates the `libbdkffi` multi-language library for the Rust-based
|
||||
[bdk] library from the [Bitcoin Dev Kit] project.
|
||||
|
||||
Each supported language and the platform(s) it's packaged for has its own directory. The Rust code in this project is in the bdk-ffi directory and is a wrapper around the [bdk] library to expose its APIs in a uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
|
||||
|
||||
## Supported target languages and platforms
|
||||
The below directories (a separate repository in the case of bdk-swift) include instructions for using, building, and publishing the native language binding for [bdk] supported by this project.
|
||||
|
||||
The below repositories include instructions for using, building, and publishing the native
|
||||
language binding for [bdk] supported by this project.
|
||||
| Language | Platform | Published Package | Building Documentation | API Docs |
|
||||
| -------- |-----------------------|-------------------------------|------------------------|-----------------------|
|
||||
| Kotlin | JVM | [bdk-jvm (Maven Central)] | [Readme bdk-jvm] | [Kotlin JVM API Docs] |
|
||||
| Kotlin | Android | [bdk-android (Maven Central)] | [Readme bdk-android] | [Android API Docs] |
|
||||
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
|
||||
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
|
||||
|
||||
| Language | Platform | Repository |
|
||||
| -------- | ------------ | ------------ |
|
||||
| Kotlin | jvm | [bdk-kotlin] |
|
||||
| Kotlin | android | [bdk-kotlin] |
|
||||
| Swift | iOS, macOS | [bdk-swift] |
|
||||
| Python | linux, macOS | [bdk-python] |
|
||||
## Building and Testing the Libraries
|
||||
If you are familiar with the build tools for the specific languages you wish to build the libraries for, you can use their normal build/test workflows. We also include some [just](https://just.systems/) files to simplify the work across different languages. If you have the just tool installed on your system, you can simply call the commands defined in the `justfile`s, for example:
|
||||
```sh
|
||||
cd bdk-android
|
||||
|
||||
## Language bindings generator tool
|
||||
|
||||
Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages.
|
||||
To run `bdk-ffi-bindgen` and see the available options use the command:
|
||||
```shell
|
||||
cargo run -p bdk-ffi-bindgen -- --help
|
||||
just build
|
||||
just offlinetests
|
||||
just publishlocal
|
||||
```
|
||||
|
||||
[bdk]: https://github.com/bitcoindevkit/bdk
|
||||
[Bitcoin Dev Kit]: https://github.com/bitcoindevkit
|
||||
[git submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
|
||||
[uniffi-rs]: https://github.com/mozilla/uniffi-rs
|
||||
|
||||
[bdk-kotlin]: https://github.com/bitcoindevkit/bdk-kotlin
|
||||
[bdk-swift]: https://github.com/bitcoindevkit/bdk-swift
|
||||
[bdk-python]: https://github.com/thunderbiscuit/bdk-python
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
This library should compile with any combination of features with Rust 1.77.1.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Adding new structs and functions
|
||||
|
||||
See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
|
||||
|
||||
#### For pass by value objects
|
||||
|
||||
1. create new rust struct with only fields that are supported UniFFI types
|
||||
1. update mapping `bdk.udl` file with new `dictionary`
|
||||
|
||||
#### For pass by reference values
|
||||
|
||||
1. create wrapper rust struct/impl with only fields that are `Sync + Send`
|
||||
1. update mapping `bdk.udl` file with new `interface`
|
||||
To add new structs and functions, see the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) and the [uniffi-examples](https://thunderbiscuit.github.io/uniffi-examples/) repository.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Language bindings should feel idiomatic in target languages/platforms
|
||||
1. Adding new targets should be easy
|
||||
1. Getting up and running should be easy
|
||||
1. Contributing should be easy
|
||||
1. Get it right, then automate
|
||||
2. Adding new targets should be easy
|
||||
3. Getting up and running should be easy
|
||||
4. Contributing should be easy
|
||||
5. Get it right, then automate
|
||||
|
||||
## Using the libraries
|
||||
### bdk-android
|
||||
```kotlin
|
||||
// build.gradle.kts
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
### bdk-jvm
|
||||
```kotlin
|
||||
// build.gradle.kts
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
_Note:_ We also publish snapshot versions of bdk-jvm and bdk-android. See the specific readmes for instructions on how to use those.
|
||||
|
||||
### bdk-python
|
||||
```shell
|
||||
pip3 install bdkpython
|
||||
```
|
||||
|
||||
### bdk-swift
|
||||
Add bdk-swift to your dependencies in XCode.
|
||||
|
||||
## Developing language bindings using uniffi-rs
|
||||
If you are interested in better understanding the base structure we use here in order to build your own Rust-to-Kotlin/Swift/Python language bindings, check out the [uniffi-bindings-template](https://github.com/thunderbiscuit/uniffi-bindings-template) repository. We maintain it as an example and starting point for other projects that wish to leverage the tech stack used in producing the BDK language bindings.
|
||||
|
||||
## Verifying Signatures
|
||||
Both libraries and all their corresponding artifacts are signed with a PGP key you can find in the
|
||||
root of this repository. To verify the signatures follow the below steps:
|
||||
|
||||
1. Import the PGP key in your keyring.
|
||||
```shell
|
||||
# Navigate to the root of the repository and import the ./PGP-BDK-BINDINGS.asc public key
|
||||
gpg --import ./PGP-BDK-BINDINGS.asc
|
||||
|
||||
# Alternatively, you can import the key directly from a public key server
|
||||
gpg --keyserver keyserver.ubuntu.com --receive-key 2768C43E8803C6A3
|
||||
|
||||
# Verify that the correct key was imported
|
||||
gpg --list-keys
|
||||
# You should see the below output
|
||||
pub ed25519 2022-08-31 [SC]
|
||||
88AD93AC4589FD090FF3B8D12768C43E8803C6A3
|
||||
uid [ unknown] bitcoindevkit-bindings <bindings@bitcoindevkit.org>
|
||||
sub cv25519 2022-08-31 [E]
|
||||
```
|
||||
|
||||
2. Download the binary artifacts and corresponding signature files.
|
||||
- from [bdk-jvm]
|
||||
- `bdk-jvm-<version>.jar`
|
||||
- `bdk-jvm-<version>.jar.asc`
|
||||
- from [bdk-android]
|
||||
- `bdk-android-<version>.aar`
|
||||
- `bdk-android-<version>.aar.asc`
|
||||
|
||||
3. Verify the signatures.
|
||||
```shell
|
||||
gpg --verify bdk-jvm-<version>.jar.asc
|
||||
gpg --verify bdk-android-<version>.aar.asc
|
||||
|
||||
# you should see a "Good signature" result
|
||||
gpg: Good signature from "bitcoindevkit-bindings <bindings@bitcoindevkit.org>" [unknown]
|
||||
```
|
||||
|
||||
### PGP Metadata
|
||||
Full key ID: `88AD 93AC 4589 FD09 0FF3 B8D1 2768 C43E 8803 C6A3`
|
||||
Fingerprint: `2768C43E8803C6A3`
|
||||
Name: `bitcoindevkit-bindings`
|
||||
Email: `bindings@bitcoindevkit.org`
|
||||
|
||||
## Thanks
|
||||
|
||||
This project is made possible thanks to the wonderful work by the [mozilla/uniffi-rs] team.
|
||||
|
||||
[Kotlin]: https://kotlinlang.org/
|
||||
[Android Studio]: https://developer.android.com/studio/
|
||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
||||
["Getting Started (Developer)"]: https://github.com/bitcoindevkit/bdk-ffi#getting-started-developer
|
||||
[bdk-jvm]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-jvm/0.11.0/jar
|
||||
[bdk-android]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-android/0.11.0/aar
|
||||
[bdk-jvm (Maven Central)]: https://central.sonatype.dev/artifact/org.bitcoindevkit/bdk-jvm/0.11.0
|
||||
[bdk-android (Maven Central)]: https://central.sonatype.dev/artifact/org.bitcoindevkit/bdk-android/0.11.0
|
||||
[bdk-swift (GitHub)]: https://github.com/bitcoindevkit/bdk-swift
|
||||
[bdk-python (PyPI)]: https://pypi.org/project/bdkpython/
|
||||
[mozilla/uniffi-rs]: https://github.com/mozilla/uniffi-rs
|
||||
[bdk]: https://github.com/bitcoindevkit/bdk
|
||||
[Bitcoin Dev Kit]: https://github.com/bitcoindevkit
|
||||
[uniffi-rs]: https://github.com/mozilla/uniffi-rs
|
||||
[Readme bdk-jvm]: https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-jvm
|
||||
[Readme bdk-android]: https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-android
|
||||
[Readme bdk-swift]: https://github.com/bitcoindevkit/bdk-swift
|
||||
[Readme bdk-python]: https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-python
|
||||
[Kotlin JVM API Docs]: https://bitcoindevkit.org/jvm/
|
||||
[Android API Docs]: https://bitcoindevkit.org/android/
|
||||
|
108
bdk-android/README.md
Normal file
108
bdk-android/README.md
Normal file
@ -0,0 +1,108 @@
|
||||
# bdk-android
|
||||
This project builds an .aar package for the Android platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the [`bdk-ffi`] project which is included in the root of this repository.
|
||||
|
||||
## How to Use
|
||||
To use the Kotlin language bindings for [`bdk`] in your Android project add the following to your gradle dependencies:
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
### Snapshot releases
|
||||
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
|
||||
}
|
||||
```
|
||||
|
||||
### Example Projects
|
||||
* [bdk-kotlin-example-wallet](https://github.com/bitcoindevkit/bdk-kotlin-example-wallet)
|
||||
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
|
||||
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
||||
|
||||
### How to build
|
||||
_Note that Kotlin version `1.9.23` or later is required to build the library._
|
||||
|
||||
1. Clone this repository.
|
||||
```shell
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.77.1
|
||||
```
|
||||
4. Install required targets
|
||||
```sh
|
||||
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
```
|
||||
5. Install Android SDK and Build-Tools for API level 30+
|
||||
6. Setup `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT` path variables which are required by the build tool. Note that currently, NDK version 25.2.9519653 or above is required. For example:
|
||||
```shell
|
||||
# macOS
|
||||
export ANDROID_SDK_ROOT=~/Library/Android/sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
|
||||
# linux
|
||||
export ANDROID_SDK_ROOT=/usr/local/lib/android/sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
```
|
||||
|
||||
7. Build kotlin bindings
|
||||
```sh
|
||||
# build Android library
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
```
|
||||
1. Start android emulator and run tests
|
||||
```sh
|
||||
./gradlew connectedAndroidTest
|
||||
```
|
||||
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-android
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
```
|
||||
|
||||
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
|
||||
```properties
|
||||
signing.gnupg.keyName=<YOUR_GNUPG_ID>
|
||||
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
|
||||
```
|
||||
|
||||
and use the `publishToMavenLocal` task without the `localBuild` flag:
|
||||
```shell
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
|
||||
## Known issues
|
||||
### JNA dependency
|
||||
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
|
||||
```shell
|
||||
class file for com.sun.jna.Pointer not found
|
||||
```
|
||||
The solution is to add JNA as a dependency like so:
|
||||
```kotlin
|
||||
dependencies {
|
||||
// ...
|
||||
implementation("net.java.dev.jna:jna:5.12.1")
|
||||
}
|
||||
```
|
||||
|
||||
### x86 emulators
|
||||
For some older versions of macOS, Android Studio will recommend users install the x86 version of the emulator by default. This will not work with the bdk-android library, as we do not support 32-bit architectures. Make sure you install an x86_64 emulator to work with bdk-android.
|
||||
|
||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
32
bdk-android/build.gradle.kts
Normal file
32
bdk-android/build.gradle.kts
Normal file
@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
id("com.android.library").version("8.3.1").apply(false)
|
||||
id("org.jetbrains.kotlin.android").version("1.9.23").apply(false)
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
id("org.bitcoindevkit.plugins.generate-android-bindings").apply(false)
|
||||
id("io.github.gradle-nexus.publish-plugin").version("1.1.0").apply(true)
|
||||
}
|
||||
|
||||
// library version is defined in gradle.properties
|
||||
val libraryVersion: String by project
|
||||
|
||||
// These properties are required here so that the nexus publish-plugin
|
||||
// finds a staging profile with the correct group (group is otherwise set as "")
|
||||
// and knows whether to publish to a SNAPSHOT repository or not
|
||||
// https://github.com/gradle-nexus/publish-plugin#applying-the-plugin
|
||||
group = "org.bitcoindevkit"
|
||||
version = libraryVersion
|
||||
|
||||
nexusPublishing {
|
||||
repositories {
|
||||
create("sonatype") {
|
||||
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
|
||||
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
||||
|
||||
val ossrhUsername: String? by project
|
||||
val ossrhPassword: String? by project
|
||||
username.set(ossrhUsername)
|
||||
password.set(ossrhPassword)
|
||||
}
|
||||
}
|
||||
}
|
5
bdk-android/gradle.properties
Normal file
5
bdk-android/gradle.properties
Normal file
@ -0,0 +1,5 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
BIN
bdk-android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
bdk-android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
bdk-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
bdk-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
249
bdk-android/gradlew
vendored
Executable file
249
bdk-android/gradlew
vendored
Executable file
@ -0,0 +1,249 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
92
bdk-android/gradlew.bat
vendored
Normal file
92
bdk-android/gradlew.bat
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
20
bdk-android/justfile
Normal file
20
bdk-android/justfile
Normal file
@ -0,0 +1,20 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
./gradlew buildAndroidLib
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
rm -rf ./build/
|
||||
rm -rf ./lib/build/
|
||||
rm -rf ./plugins/build/
|
||||
|
||||
publish-local:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
test:
|
||||
./gradlew connectedAndroidTest
|
||||
|
||||
test-specific TEST:
|
||||
./gradlew test --tests {{TEST}}
|
135
bdk-android/lib/build.gradle.kts
Normal file
135
bdk-android/lib/build.gradle.kts
Normal file
@ -0,0 +1,135 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
// library version is defined in gradle.properties
|
||||
val libraryVersion: String by project
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-android-bindings")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.bitcoindevkit"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(file("proguard-android-optimize.txt"), file("proguard-rules.pro"))
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.java.dev.jna:jna:5.14.0@aar")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
|
||||
implementation("androidx.appcompat:appcompat:1.4.0")
|
||||
implementation("androidx.core:core-ktx:1.7.0")
|
||||
api("org.slf4j:slf4j-api:1.7.30")
|
||||
|
||||
androidTestImplementation("com.github.tony19:logback-android:2.0.0")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test:1.6.10")
|
||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.6.10")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = "org.bitcoindevkit"
|
||||
artifactId = "bdk-android"
|
||||
version = libraryVersion
|
||||
|
||||
from(components["release"])
|
||||
pom {
|
||||
name.set("bdk-android")
|
||||
description.set("Bitcoin Dev Kit Kotlin language bindings.")
|
||||
url.set("https://bitcoindevkit.org")
|
||||
licenses {
|
||||
license {
|
||||
name.set("APACHE 2.0")
|
||||
url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE")
|
||||
}
|
||||
license {
|
||||
name.set("MIT")
|
||||
url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("bdkdevelopers")
|
||||
name.set("Bitcoin Dev Kit Developers")
|
||||
email.set("dev@bitcoindevkit.org")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git")
|
||||
developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git")
|
||||
url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is required because we must ensure the moveNativeAndroidLibs task is executed after
|
||||
// the mergeReleaseJniLibFolders (hard requirement introduced by our upgrade to Gradle 8.7)
|
||||
tasks.named("mergeReleaseJniLibFolders") {
|
||||
dependsOn(":lib:moveNativeAndroidLibs")
|
||||
}
|
||||
tasks.named("mergeDebugJniLibFolders") {
|
||||
dependsOn(":lib:moveNativeAndroidLibs")
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
if (project.hasProperty("localBuild")) {
|
||||
isRequired = false
|
||||
}
|
||||
|
||||
val signingKeyId: String? by project
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
||||
sign(publishing.publications)
|
||||
}
|
||||
|
||||
// This task dependency ensures that we build the bindings binaries before running the tests
|
||||
tasks.withType<KotlinCompile> {
|
||||
dependsOn("buildAndroidLib")
|
||||
}
|
28
bdk-android/lib/proguard-rules.pro
vendored
Normal file
28
bdk-android/lib/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
# for JNA
|
||||
-dontwarn java.awt.*
|
||||
-keep class com.sun.jna.* { *; }
|
||||
-keep class org.bitcoindevkit.* { *; }
|
||||
-keepclassmembers class * extends org.bitcoindevkit.* { public *; }
|
||||
-keepclassmembers class * extends com.sun.jna.* { public *; }
|
14
bdk-android/lib/src/androidTest/assets/logback.xml
Normal file
14
bdk-android/lib/src/androidTest/assets/logback.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
|
||||
<tagEncoder>
|
||||
<pattern>%logger{12}</pattern>
|
||||
</tagEncoder>
|
||||
<encoder>
|
||||
<pattern>[%-20thread] %msg</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="logcat" />
|
||||
</root>
|
||||
</configuration>
|
@ -0,0 +1,82 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.sqlite"
|
||||
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun complexTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.setRecipients(allRecipients)
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
|
||||
.enableRbf()
|
||||
.finish(wallet)
|
||||
|
||||
wallet.sign(psbt)
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Test
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence2.sqlite"
|
||||
private val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
private val changeDescriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
val balance: Balance = wallet.balance()
|
||||
println("Balance: $balance")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
|
||||
val walletDidSign = wallet.sign(psbt)
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
println("Txid is: ${tx.computeTxid()}")
|
||||
|
||||
val txFee: Amount = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee.toSat()}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineDescriptorTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.toString()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineWalletTest {
|
||||
private val persistenceFilePath = InstrumentationRegistry
|
||||
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence1.sqlite"
|
||||
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
actual = addressInfo.address.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.balance().total.toSat()
|
||||
)
|
||||
}
|
||||
}
|
3
bdk-android/lib/src/main/AndroidManifest.xml
Normal file
3
bdk-android/lib/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
17
bdk-android/plugins/README.md
Normal file
17
bdk-android/plugins/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Readme
|
||||
The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by `bdk-android`, and building the language bindings files.
|
||||
|
||||
The plugin is applied to the `build.gradle.kts` file in `bdk-android` through the `plugins` block:
|
||||
```kotlin
|
||||
// bdk-android
|
||||
plugins {
|
||||
id("org.bitcoindevkit.plugins.generate-android-bindings")
|
||||
}
|
||||
```
|
||||
|
||||
It adds a series of tasks which are brought together into an aggregate task called `buildAndroidLib`.
|
||||
|
||||
This aggregate task:
|
||||
1. Builds the native libraries using `bdk-ffi`
|
||||
2. Places them in the correct resource directories
|
||||
3. Builds the bindings file
|
13
bdk-android/plugins/build.gradle.kts
Normal file
13
bdk-android/plugins/build.gradle.kts
Normal file
@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("java-gradle-plugin")
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("uniFfiAndroidBindings") {
|
||||
id = "org.bitcoindevkit.plugins.generate-android-bindings"
|
||||
implementationClass = "org.bitcoindevkit.plugins.UniFfiAndroidPlugin"
|
||||
}
|
||||
}
|
||||
}
|
8
bdk-android/plugins/settings.gradle.kts
Normal file
8
bdk-android/plugins/settings.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
// include(":plugins")
|
@ -0,0 +1,13 @@
|
||||
package org.bitcoindevkit.plugins
|
||||
|
||||
val operatingSystem: OS = when {
|
||||
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
|
||||
System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
|
||||
else -> OS.OTHER
|
||||
}
|
||||
|
||||
enum class OS {
|
||||
MAC,
|
||||
LINUX,
|
||||
OTHER,
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package org.bitcoindevkit.plugins
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.Exec
|
||||
import org.gradle.kotlin.dsl.environment
|
||||
import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = target.run {
|
||||
val llvmArchPath = when (operatingSystem) {
|
||||
OS.MAC -> "darwin-x86_64"
|
||||
OS.LINUX -> "linux-x86_64"
|
||||
OS.OTHER -> throw Error("Cannot build Android library from current architecture")
|
||||
}
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set, stop build
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
throw IllegalStateException("ANDROID_NDK_ROOT environment variable is not set; cannot build library")
|
||||
}
|
||||
|
||||
// arm64-v8a is the most popular hardware architecture for Android
|
||||
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
|
||||
|
||||
workingDir("${projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android24-clang"),
|
||||
Pair("CC", "aarch64-linux-android24-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
println("Native library for bdk-android on aarch64 built successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// the x86_64 version of the library is mostly used by emulators
|
||||
val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android24-clang"),
|
||||
Pair("CC", "x86_64-linux-android24-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
println("Native library for bdk-android on x86_64 built successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// armeabi-v7a version of the library for older 32-bit Android hardware
|
||||
val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi24-clang"),
|
||||
Pair("CC", "armv7a-linux-androideabi24-clang")
|
||||
)
|
||||
|
||||
doLast {
|
||||
println("Native library for bdk-android on armv7 built successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// move the native libs build by cargo from target/<architecture>/release/
|
||||
// to their place in the bdk-android library
|
||||
// the task only copies the available binaries built using the buildAndroid<architecture>Binary tasks
|
||||
val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") {
|
||||
|
||||
dependsOn(buildAndroidAarch64Binary)
|
||||
dependsOn(buildAndroidArmv7Binary)
|
||||
dependsOn(buildAndroidX86_64Binary)
|
||||
|
||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||
|
||||
into("arm64-v8a") {
|
||||
from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("x86_64") {
|
||||
from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("armeabi-v7a") {
|
||||
from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
doLast {
|
||||
println("Native binaries for Android moved to ./lib/src/main/jniLibs/")
|
||||
}
|
||||
}
|
||||
|
||||
// generate the bindings using the bdk-ffi-bindgen tool located in the bdk-ffi submodule
|
||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||
dependsOn(moveNativeAndroidLibs)
|
||||
|
||||
val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
// The code above worked for uniffi 0.24.3 using the --library flag
|
||||
// The code below works for uniffi 0.23.0
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--config", "uniffi-android.toml", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
doLast {
|
||||
println("Android bindings file successfully created")
|
||||
}
|
||||
}
|
||||
|
||||
// create an aggregate task which will run the required tasks to build the Android libs in order
|
||||
// the task will also appear in the printout of the ./gradlew tasks task with group and description
|
||||
tasks.register("buildAndroidLib") {
|
||||
group = "Bitcoindevkit"
|
||||
description = "Aggregate task to build Android library"
|
||||
|
||||
dependsOn(
|
||||
buildAndroidAarch64Binary,
|
||||
buildAndroidX86_64Binary,
|
||||
buildAndroidArmv7Binary,
|
||||
moveNativeAndroidLibs,
|
||||
generateAndroidBindings
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
18
bdk-android/settings.gradle.kts
Normal file
18
bdk-android/settings.gradle.kts
Normal file
@ -0,0 +1,18 @@
|
||||
rootProject.name = "bdk-android"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "bdk-ffi-bindgen"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.45" # remove after upgrading to next version of uniffi
|
||||
structopt = "0.3"
|
||||
uniffi_bindgen = "0.19.3"
|
||||
camino = "1.0.9"
|
@ -1,137 +0,0 @@
|
||||
use camino::Utf8Path;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Language {
|
||||
Kotlin,
|
||||
Python,
|
||||
Swift,
|
||||
}
|
||||
|
||||
impl fmt::Display for Language {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Language::Kotlin => write!(f, "kotlin"),
|
||||
Language::Swift => write!(f, "swift"),
|
||||
Language::Python => write!(f, "python"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UnsupportedLanguage,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Language {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"kotlin" => Ok(Language::Kotlin),
|
||||
"python" => Ok(Language::Python),
|
||||
"swift" => Ok(Language::Swift),
|
||||
_ => Err(Error::UnsupportedLanguage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_bindings(opt: &Opt) -> anyhow::Result<(), anyhow::Error> {
|
||||
let path: &Utf8Path = Utf8Path::from_path(&opt.udl_file).unwrap();
|
||||
let out_dir: &Utf8Path = Utf8Path::from_path(&opt.out_dir).unwrap();
|
||||
uniffi_bindgen::generate_bindings(
|
||||
path,
|
||||
None,
|
||||
vec![opt.language.to_string().as_str()],
|
||||
Some(out_dir),
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fixup_python_lib_path(
|
||||
out_dir: &Path,
|
||||
lib_name: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
const LOAD_INDIRECT_DEF: &str = "def loadIndirect():";
|
||||
|
||||
let bindings_file = out_dir.join("bdk.py");
|
||||
let mut data = fs::read_to_string(&bindings_file)?;
|
||||
|
||||
let pos = data
|
||||
.find(LOAD_INDIRECT_DEF)
|
||||
.unwrap_or_else(|| panic!("loadIndirect not found in `{}`", bindings_file.display()));
|
||||
let range = pos..pos + LOAD_INDIRECT_DEF.len();
|
||||
|
||||
let replacement = format!(
|
||||
r#"
|
||||
def loadIndirect():
|
||||
import glob
|
||||
return getattr(ctypes.cdll, glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), '{}.*'))[0])
|
||||
|
||||
def _loadIndirectOld():"#,
|
||||
&lib_name.to_str().expect("lib name")
|
||||
);
|
||||
data.replace_range(range, &replacement);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&bindings_file)?;
|
||||
file.write_all(data.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "bdk-ffi-bindgen",
|
||||
about = "A tool to generate bdk-ffi language bindings"
|
||||
)]
|
||||
struct Opt {
|
||||
/// UDL file
|
||||
#[structopt(env = "BDKFFI_BINDGEN_UDL", short, long, default_value("src/bdk.udl"), parse(try_from_str = PathBuf::from_str))]
|
||||
udl_file: PathBuf,
|
||||
|
||||
/// Language to generate bindings for
|
||||
#[structopt(env = "BDKFFI_BINDGEN_LANGUAGE", short, long, possible_values(&["kotlin","swift","python"]), parse(try_from_str = Language::from_str))]
|
||||
language: Language,
|
||||
|
||||
/// Output directory to put generated language bindings
|
||||
#[structopt(env = "BDKFFI_BINDGEN_OUTPUT_DIR", short, long, parse(try_from_str = PathBuf::from_str))]
|
||||
out_dir: PathBuf,
|
||||
|
||||
/// Python fix up lib path
|
||||
#[structopt(env = "BDKFFI_BINDGEN_PYTHON_FIXUP_PATH", short, long, parse(try_from_str = PathBuf::from_str))]
|
||||
python_fixup_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let opt = Opt::from_args();
|
||||
|
||||
println!("Input UDL file is {:?}", opt.udl_file);
|
||||
println!("Chosen language is {}", opt.language);
|
||||
println!("Output directory is {:?}", opt.out_dir);
|
||||
|
||||
generate_bindings(&opt)?;
|
||||
|
||||
if opt.language == Language::Python {
|
||||
if let Some(path) = opt.python_fixup_path {
|
||||
println!("Fixing up python lib path, {:?}", &path);
|
||||
fixup_python_lib_path(&opt.out_dir, &path)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
1460
bdk-ffi/Cargo.lock
generated
Normal file
1460
bdk-ffi/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
bdk-ffi/Cargo.toml
Normal file
47
bdk-ffi/Cargo.toml
Normal file
@ -0,0 +1,47 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "1.0.0-alpha.11"
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
repository = "https://github.com/bitcoindevkit/bdk"
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
name = "bdkffi"
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen"
|
||||
path = "uniffi-bindgen.rs"
|
||||
|
||||
[features]
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.15.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||
# NOTE: This is a temporary workaround to use the electrum-client with the use-rustls-ring feature. It points to a fork
|
||||
# of bdk in which the bdk_electrum library uses the electrum-client with the use-rustls-ring feature.
|
||||
bdk_electrum = { git = "https://github.com/thunderbiscuit/bdk/", package = "bdk_electrum", branch = "feature/electrum-client-ring-ffi-alpha13", default-features = false, features = ["use-rustls-ring"] }
|
||||
# bdk_electrum = { version = "0.15.0" }
|
||||
bdk_sqlite = { version = "0.2.0" }
|
||||
bdk_bitcoind_rpc = { version = "0.12.0" }
|
||||
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
||||
|
||||
uniffi = { version = "=0.28.0" }
|
||||
thiserror = "1.0.58"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "=0.28.0", features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
uniffi = { version = "=0.28.0", features = ["bindgen-tests"] }
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[profile.release-smaller]
|
||||
inherits = "release"
|
||||
opt-level = 'z' # Optimize for size.
|
||||
lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = "abort" # Abort on panic
|
||||
strip = "debuginfo" # Partially strip symbols from binary
|
3
bdk-ffi/build.rs
Normal file
3
bdk-ffi/build.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("./src/bdk.udl").unwrap();
|
||||
}
|
12
bdk-ffi/justfile
Normal file
12
bdk-ffi/justfile
Normal file
@ -0,0 +1,12 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
cargo build
|
||||
|
||||
check:
|
||||
cargo fmt
|
||||
cargo clippy
|
||||
|
||||
test:
|
||||
cargo test --lib
|
728
bdk-ffi/src/bdk.udl
Normal file
728
bdk-ffi/src/bdk.udl
Normal file
@ -0,0 +1,728 @@
|
||||
namespace bdk {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - error module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Error]
|
||||
interface AddressParseError {
|
||||
Base58();
|
||||
Bech32();
|
||||
WitnessVersion(string error_message);
|
||||
WitnessProgram(string error_message);
|
||||
UnknownHrp();
|
||||
LegacyAddressTooLong();
|
||||
InvalidBase58PayloadLength();
|
||||
InvalidLegacyPrefix();
|
||||
NetworkValidation();
|
||||
OtherAddressParseErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface Bip32Error {
|
||||
CannotDeriveFromHardenedKey();
|
||||
Secp256k1(string error_message);
|
||||
InvalidChildNumber(u32 child_number);
|
||||
InvalidChildNumberFormat();
|
||||
InvalidDerivationPathFormat();
|
||||
UnknownVersion(string version);
|
||||
WrongExtendedKeyLength(u32 length);
|
||||
Base58(string error_message);
|
||||
Hex(string error_message);
|
||||
InvalidPublicKeyHexLength(u32 length);
|
||||
UnknownError(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface Bip39Error {
|
||||
BadWordCount(u64 word_count);
|
||||
UnknownWord(u64 index);
|
||||
BadEntropyBitCount(u64 bit_count);
|
||||
InvalidChecksum();
|
||||
AmbiguousLanguages(string languages);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface CalculateFeeError {
|
||||
MissingTxOut(sequence<OutPoint> out_points);
|
||||
NegativeFee(string amount);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface CannotConnectError {
|
||||
Include(u32 height);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface CreateTxError {
|
||||
Descriptor(string error_message);
|
||||
Policy(string error_message);
|
||||
SpendingPolicyRequired(string kind);
|
||||
Version0();
|
||||
Version1Csv();
|
||||
LockTime(string requested, string required);
|
||||
RbfSequence();
|
||||
RbfSequenceCsv(string rbf, string csv);
|
||||
FeeTooLow(string required);
|
||||
FeeRateTooLow(string required);
|
||||
NoUtxosSelected();
|
||||
OutputBelowDustLimit(u64 index);
|
||||
ChangePolicyDescriptor();
|
||||
CoinSelection(string error_message);
|
||||
InsufficientFunds(u64 needed, u64 available);
|
||||
NoRecipients();
|
||||
Psbt(string error_message);
|
||||
MissingKeyOrigin(string key);
|
||||
UnknownUtxo(string outpoint);
|
||||
MissingNonWitnessUtxo(string outpoint);
|
||||
MiniscriptPsbt(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface DescriptorError {
|
||||
InvalidHdKeyPath();
|
||||
InvalidDescriptorChecksum();
|
||||
HardenedDerivationXpub();
|
||||
MultiPath();
|
||||
Key(string error_message);
|
||||
Policy(string error_message);
|
||||
InvalidDescriptorCharacter(string char);
|
||||
Bip32(string error_message);
|
||||
Base58(string error_message);
|
||||
Pk(string error_message);
|
||||
Miniscript(string error_message);
|
||||
Hex(string error_message);
|
||||
ExternalAndInternalAreTheSame();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface DescriptorKeyError {
|
||||
Parse(string error_message);
|
||||
InvalidKeyType();
|
||||
Bip32(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ElectrumError {
|
||||
IOError(string error_message);
|
||||
Json(string error_message);
|
||||
Hex(string error_message);
|
||||
Protocol(string error_message);
|
||||
Bitcoin(string error_message);
|
||||
AlreadySubscribed();
|
||||
NotSubscribed();
|
||||
InvalidResponse(string error_message);
|
||||
Message(string error_message);
|
||||
InvalidDNSNameError(string domain);
|
||||
MissingDomain();
|
||||
AllAttemptsErrored();
|
||||
SharedIOError(string error_message);
|
||||
CouldntLockReader();
|
||||
Mpsc();
|
||||
CouldNotCreateConnection(string error_message);
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface EsploraError {
|
||||
Minreq(string error_message);
|
||||
HttpResponse(u16 status, string error_message);
|
||||
Parsing(string error_message);
|
||||
StatusCode(string error_message);
|
||||
BitcoinEncoding(string error_message);
|
||||
HexToArray(string error_message);
|
||||
HexToBytes(string error_message);
|
||||
TransactionNotFound();
|
||||
HeaderHeightNotFound(u32 height);
|
||||
HeaderHashNotFound();
|
||||
InvalidHttpHeaderName(string name);
|
||||
InvalidHttpHeaderValue(string value);
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ExtractTxError {
|
||||
AbsurdFeeRate(u64 fee_rate);
|
||||
MissingInputValue();
|
||||
SendingTooMuch();
|
||||
OtherExtractTxErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
enum FeeRateError {
|
||||
"ArithmeticOverflow"
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface FromScriptError {
|
||||
UnrecognizedScript();
|
||||
WitnessProgram(string error_message);
|
||||
WitnessVersion(string error_message);
|
||||
OtherFromScriptErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ParseAmountError {
|
||||
OutOfRange();
|
||||
TooPrecise();
|
||||
MissingDigits();
|
||||
InputTooLarge();
|
||||
InvalidCharacter(string error_message);
|
||||
OtherParseAmountErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PersistenceError {
|
||||
Write(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PsbtError {
|
||||
InvalidMagic();
|
||||
MissingUtxo();
|
||||
InvalidSeparator();
|
||||
PsbtUtxoOutOfBounds();
|
||||
InvalidKey(string key);
|
||||
InvalidProprietaryKey();
|
||||
DuplicateKey(string key);
|
||||
UnsignedTxHasScriptSigs();
|
||||
UnsignedTxHasScriptWitnesses();
|
||||
MustHaveUnsignedTx();
|
||||
NoMorePairs();
|
||||
UnexpectedUnsignedTx();
|
||||
NonStandardSighashType(u32 sighash);
|
||||
InvalidHash(string hash);
|
||||
InvalidPreimageHashPair();
|
||||
CombineInconsistentKeySources(string xpub);
|
||||
ConsensusEncoding(string encoding_error);
|
||||
NegativeFee();
|
||||
FeeOverflow();
|
||||
InvalidPublicKey(string error_message);
|
||||
InvalidSecp256k1PublicKey(string secp256k1_error);
|
||||
InvalidXOnlyPublicKey();
|
||||
InvalidEcdsaSignature(string error_message);
|
||||
InvalidTaprootSignature(string error_message);
|
||||
InvalidControlBlock();
|
||||
InvalidLeafVersion();
|
||||
Taproot();
|
||||
TapTree(string error_message);
|
||||
XPubKey();
|
||||
Version(string error_message);
|
||||
PartialDataConsumption();
|
||||
Io(string error_message);
|
||||
OtherPsbtErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PsbtParseError {
|
||||
PsbtEncoding(string error_message);
|
||||
Base64Encoding(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface InspectError {
|
||||
RequestAlreadyConsumed();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface SignerError {
|
||||
MissingKey();
|
||||
InvalidKey();
|
||||
UserCanceled();
|
||||
InputIndexOutOfRange();
|
||||
MissingNonWitnessUtxo();
|
||||
InvalidNonWitnessUtxo();
|
||||
MissingWitnessUtxo();
|
||||
MissingWitnessScript();
|
||||
MissingHdKeypath();
|
||||
NonStandardSighash();
|
||||
InvalidSighash();
|
||||
SighashP2wpkh(string error_message);
|
||||
SighashTaproot(string error_message);
|
||||
TxInputsIndexError(string error_message);
|
||||
MiniscriptPsbt(string error_message);
|
||||
External(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface SqliteError {
|
||||
InvalidNetwork(Network expected, Network given);
|
||||
Sqlite(string rusqlite_error);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface TransactionError {
|
||||
Io();
|
||||
OversizedVectorAllocation();
|
||||
InvalidChecksum(string expected, string actual);
|
||||
NonMinimalVarInt();
|
||||
ParseFailed();
|
||||
UnsupportedSegwitFlag(u8 flag);
|
||||
OtherTransactionErr();
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface TxidParseError {
|
||||
InvalidTxid(string txid);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface WalletCreationError {
|
||||
Descriptor(string error_message);
|
||||
LoadedGenesisDoesNotMatch(string expected, string got);
|
||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||
LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - types module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
dictionary AddressInfo {
|
||||
u32 index;
|
||||
Address address;
|
||||
KeychainKind keychain;
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
Amount immature;
|
||||
|
||||
Amount trusted_pending;
|
||||
|
||||
Amount untrusted_pending;
|
||||
|
||||
Amount confirmed;
|
||||
|
||||
Amount trusted_spendable;
|
||||
|
||||
Amount total;
|
||||
};
|
||||
|
||||
dictionary LocalOutput {
|
||||
OutPoint outpoint;
|
||||
TxOut txout;
|
||||
KeychainKind keychain;
|
||||
boolean is_spent;
|
||||
};
|
||||
|
||||
dictionary TxOut {
|
||||
u64 value;
|
||||
Script script_pubkey;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface ChainPosition {
|
||||
Confirmed(u32 height, u64 timestamp);
|
||||
Unconfirmed(u64 timestamp);
|
||||
};
|
||||
|
||||
dictionary CanonicalTx {
|
||||
Transaction transaction;
|
||||
ChainPosition chain_position;
|
||||
};
|
||||
|
||||
interface FullScanRequest {
|
||||
[Throws=InspectError]
|
||||
FullScanRequest inspect_spks_for_all_keychains(FullScanScriptInspector inspector);
|
||||
};
|
||||
|
||||
interface SyncRequest {
|
||||
[Throws=InspectError]
|
||||
SyncRequest inspect_spks(SyncScriptInspector inspector);
|
||||
};
|
||||
|
||||
[Trait, WithForeign]
|
||||
interface SyncScriptInspector {
|
||||
void inspect(Script script, u64 total);
|
||||
};
|
||||
|
||||
[Trait, WithForeign]
|
||||
interface FullScanScriptInspector {
|
||||
void inspect(KeychainKind keychain, u32 index, Script script);
|
||||
};
|
||||
|
||||
interface ChangeSet {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - wallet module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum ChangeSpendPolicy {
|
||||
"ChangeAllowed",
|
||||
"OnlyChange",
|
||||
"ChangeForbidden"
|
||||
};
|
||||
|
||||
interface Wallet {
|
||||
[Throws=WalletCreationError]
|
||||
constructor(Descriptor descriptor, Descriptor change_descriptor, Network network);
|
||||
|
||||
[Name=new_or_load, Throws=WalletCreationError]
|
||||
constructor(Descriptor descriptor, Descriptor change_descriptor, ChangeSet? change_set, Network network);
|
||||
|
||||
AddressInfo reveal_next_address(KeychainKind keychain);
|
||||
|
||||
Network network();
|
||||
|
||||
Balance balance();
|
||||
|
||||
[Throws=CannotConnectError]
|
||||
void apply_update(Update update);
|
||||
|
||||
boolean is_mine([ByRef] Script script);
|
||||
|
||||
[Throws=SignerError]
|
||||
boolean sign(Psbt psbt);
|
||||
|
||||
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
|
||||
|
||||
sequence<CanonicalTx> transactions();
|
||||
|
||||
[Throws=TxidParseError]
|
||||
CanonicalTx? get_tx(string txid);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
Amount calculate_fee([ByRef] Transaction tx);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||
|
||||
sequence<LocalOutput> list_unspent();
|
||||
|
||||
sequence<LocalOutput> list_output();
|
||||
|
||||
FullScanRequest start_full_scan();
|
||||
|
||||
SyncRequest start_sync_with_revealed_spks();
|
||||
|
||||
ChangeSet? take_staged();
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient([ByRef] Script script, Amount amount);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
TxBuilder add_unspendable(OutPoint unspendable);
|
||||
|
||||
TxBuilder unspendable(sequence<OutPoint> unspendable);
|
||||
|
||||
TxBuilder add_utxo(OutPoint outpoint);
|
||||
|
||||
TxBuilder change_policy(ChangeSpendPolicy change_policy);
|
||||
|
||||
TxBuilder do_not_spend_change();
|
||||
|
||||
TxBuilder only_spend_change();
|
||||
|
||||
TxBuilder manually_selected_only();
|
||||
|
||||
TxBuilder fee_rate([ByRef] FeeRate fee_rate);
|
||||
|
||||
TxBuilder fee_absolute(Amount fee);
|
||||
|
||||
TxBuilder drain_wallet();
|
||||
|
||||
TxBuilder drain_to([ByRef] Script script);
|
||||
|
||||
TxBuilder enable_rbf();
|
||||
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=CreateTxError]
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, FeeRate fee_rate);
|
||||
|
||||
BumpFeeTxBuilder enable_rbf();
|
||||
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=CreateTxError]
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_sqlite crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface SqliteStore {
|
||||
[Throws=SqliteError]
|
||||
constructor(string path);
|
||||
|
||||
[Throws=SqliteError]
|
||||
void write([ByRef] ChangeSet change_set);
|
||||
|
||||
[Throws=SqliteError]
|
||||
ChangeSet? read();
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - descriptor module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
|
||||
[Name=from_string, Throws=Bip39Error]
|
||||
constructor(string mnemonic);
|
||||
|
||||
[Name=from_entropy, Throws=Bip39Error]
|
||||
constructor(sequence<u8> entropy);
|
||||
};
|
||||
|
||||
interface DerivationPath {
|
||||
[Throws=Bip32Error]
|
||||
constructor(string path);
|
||||
};
|
||||
|
||||
interface DescriptorSecretKey {
|
||||
constructor(Network network, [ByRef] Mnemonic mnemonic, string? password);
|
||||
|
||||
[Name=from_string, Throws=DescriptorKeyError]
|
||||
constructor(string secret_key);
|
||||
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorSecretKey derive([ByRef] DerivationPath path);
|
||||
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorSecretKey extend([ByRef] DerivationPath path);
|
||||
|
||||
DescriptorPublicKey as_public();
|
||||
|
||||
sequence<u8> secret_bytes();
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface DescriptorPublicKey {
|
||||
[Name=from_string, Throws=DescriptorKeyError]
|
||||
constructor(string public_key);
|
||||
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorPublicKey derive([ByRef] DerivationPath path);
|
||||
|
||||
[Throws=DescriptorKeyError]
|
||||
DescriptorPublicKey extend([ByRef] DerivationPath path);
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Descriptor {
|
||||
[Throws=DescriptorError]
|
||||
constructor(string descriptor, Network network);
|
||||
|
||||
[Name=new_bip44]
|
||||
constructor([ByRef] DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip44_public]
|
||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip49]
|
||||
constructor([ByRef] DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip49_public]
|
||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip84]
|
||||
constructor([ByRef] DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip84_public]
|
||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip86]
|
||||
constructor([ByRef] DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip86_public]
|
||||
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
string to_string_with_secret();
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_esplora crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface EsploraClient {
|
||||
constructor(string url);
|
||||
|
||||
[Throws=EsploraError]
|
||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 parallel_requests);
|
||||
|
||||
[Throws=EsploraError]
|
||||
Update sync(SyncRequest sync_request, u64 parallel_requests);
|
||||
|
||||
[Throws=EsploraError]
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_electrum crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface ElectrumClient {
|
||||
[Throws=ElectrumError]
|
||||
constructor(string url);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
Update sync(SyncRequest sync_request, u64 batch_size, boolean fetch_prev_txouts);
|
||||
|
||||
[Throws=ElectrumError]
|
||||
string broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk-ffi-defined types
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
Amount amount;
|
||||
};
|
||||
|
||||
dictionary SentAndReceivedValues {
|
||||
Amount sent;
|
||||
Amount received;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_wallet crate - bitcoin re-exports
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface Script {
|
||||
constructor(sequence<u8> raw_output_script);
|
||||
|
||||
sequence<u8> to_bytes();
|
||||
};
|
||||
|
||||
[NonExhaustive]
|
||||
enum Network {
|
||||
"Bitcoin",
|
||||
"Testnet",
|
||||
"Signet",
|
||||
"Regtest",
|
||||
};
|
||||
|
||||
enum WordCount {
|
||||
"Words12",
|
||||
"Words15",
|
||||
"Words18",
|
||||
"Words21",
|
||||
"Words24",
|
||||
};
|
||||
|
||||
[Traits=(Display)]
|
||||
interface Address {
|
||||
[Throws=AddressParseError]
|
||||
constructor(string address, Network network);
|
||||
|
||||
[Name=from_script, Throws=FromScriptError]
|
||||
constructor(Script script, Network network);
|
||||
|
||||
Script script_pubkey();
|
||||
|
||||
string to_qr_uri();
|
||||
|
||||
boolean is_valid_for_network(Network network);
|
||||
};
|
||||
|
||||
interface Transaction {
|
||||
[Throws=TransactionError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string compute_txid();
|
||||
|
||||
u64 total_size();
|
||||
|
||||
u64 vsize();
|
||||
|
||||
boolean is_coinbase();
|
||||
|
||||
boolean is_explicitly_rbf();
|
||||
|
||||
boolean is_lock_time_enabled();
|
||||
|
||||
i32 version();
|
||||
|
||||
sequence<u8> serialize();
|
||||
|
||||
u64 weight();
|
||||
|
||||
sequence<TxIn> input();
|
||||
|
||||
sequence<TxOut> output();
|
||||
|
||||
u32 lock_time();
|
||||
};
|
||||
|
||||
interface Psbt {
|
||||
[Throws=PsbtParseError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
[Throws=ExtractTxError]
|
||||
Transaction extract_tx();
|
||||
|
||||
[Throws=PsbtError]
|
||||
u64 fee();
|
||||
|
||||
[Throws=PsbtError]
|
||||
Psbt combine(Psbt other);
|
||||
|
||||
string json_serialize();
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
interface Amount {
|
||||
[Name=from_sat]
|
||||
constructor(u64 from_sat);
|
||||
|
||||
[Name=from_btc, Throws=ParseAmountError]
|
||||
constructor(f64 from_btc);
|
||||
|
||||
u64 to_sat();
|
||||
|
||||
f64 to_btc();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb, Throws=FeeRateError]
|
||||
constructor(u64 sat_per_vb);
|
||||
|
||||
[Name=from_sat_per_kwu]
|
||||
constructor(u64 sat_per_kwu);
|
||||
|
||||
u64 to_sat_per_vb_ceil();
|
||||
|
||||
u64 to_sat_per_vb_floor();
|
||||
|
||||
u64 to_sat_per_kwu();
|
||||
};
|
||||
|
||||
dictionary TxIn {
|
||||
OutPoint previous_output;
|
||||
Script script_sig;
|
||||
u32 sequence;
|
||||
sequence<sequence<u8>> witness;
|
||||
};
|
679
bdk-ffi/src/bitcoin.rs
Normal file
679
bdk-ffi/src/bitcoin.rs
Normal file
@ -0,0 +1,679 @@
|
||||
use crate::error::{
|
||||
AddressParseError, FeeRateError, FromScriptError, PsbtError, PsbtParseError, TransactionError,
|
||||
};
|
||||
|
||||
use bdk_bitcoind_rpc::bitcoincore_rpc::jsonrpc::serde_json;
|
||||
use bdk_wallet::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk_wallet::bitcoin::amount::ParseAmountError;
|
||||
use bdk_wallet::bitcoin::consensus::encode::serialize;
|
||||
use bdk_wallet::bitcoin::consensus::Decodable;
|
||||
use bdk_wallet::bitcoin::io::Cursor;
|
||||
use bdk_wallet::bitcoin::psbt::ExtractTxError;
|
||||
use bdk_wallet::bitcoin::Address as BdkAddress;
|
||||
use bdk_wallet::bitcoin::Amount as BdkAmount;
|
||||
use bdk_wallet::bitcoin::FeeRate as BdkFeeRate;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk_wallet::bitcoin::TxIn as BdkTxIn;
|
||||
use bdk_wallet::bitcoin::TxOut as BdkTxOut;
|
||||
use bdk_wallet::bitcoin::Txid;
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Amount(pub(crate) BdkAmount);
|
||||
|
||||
impl Amount {
|
||||
pub fn from_sat(sat: u64) -> Self {
|
||||
Amount(BdkAmount::from_sat(sat))
|
||||
}
|
||||
|
||||
pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
|
||||
let bdk_amount = BdkAmount::from_btc(btc).map_err(ParseAmountError::from)?;
|
||||
Ok(Amount(bdk_amount))
|
||||
}
|
||||
|
||||
pub fn to_sat(&self) -> u64 {
|
||||
self.0.to_sat()
|
||||
}
|
||||
|
||||
pub fn to_btc(&self) -> f64 {
|
||||
self.0.to_btc()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Amount> for BdkAmount {
|
||||
fn from(amount: Amount) -> Self {
|
||||
amount.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAmount> for Amount {
|
||||
fn from(amount: BdkAmount) -> Self {
|
||||
Amount(amount)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script(pub(crate) BdkScriptBuf);
|
||||
|
||||
impl Script {
|
||||
pub fn new(raw_output_script: Vec<u8>) -> Self {
|
||||
let script: BdkScriptBuf = raw_output_script.into();
|
||||
Script(script)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkScriptBuf> for Script {
|
||||
fn from(script: BdkScriptBuf) -> Self {
|
||||
Script(script)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Address(BdkAddress<NetworkChecked>);
|
||||
|
||||
impl Address {
|
||||
pub fn new(address: String, network: Network) -> Result<Self, AddressParseError> {
|
||||
let parsed_address = address.parse::<bdk_wallet::bitcoin::Address<NetworkUnchecked>>()?;
|
||||
let network_checked_address = parsed_address.require_network(network)?;
|
||||
|
||||
Ok(Address(network_checked_address))
|
||||
}
|
||||
|
||||
pub fn from_script(script: Arc<Script>, network: Network) -> Result<Self, FromScriptError> {
|
||||
let address = BdkAddress::from_script(&script.0.clone(), network)?;
|
||||
|
||||
Ok(Address(address))
|
||||
}
|
||||
|
||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||
Arc::new(Script(self.0.script_pubkey()))
|
||||
}
|
||||
|
||||
pub fn to_qr_uri(&self) -> String {
|
||||
self.0.to_qr_uri()
|
||||
}
|
||||
|
||||
pub fn is_valid_for_network(&self, network: Network) -> bool {
|
||||
let address_str = self.0.to_string();
|
||||
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
|
||||
unchecked_address.is_valid_for_network(network)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Address {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Address> for BdkAddress {
|
||||
fn from(address: Address) -> Self {
|
||||
address.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAddress> for Address {
|
||||
fn from(address: BdkAddress) -> Self {
|
||||
Address(address)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Transaction(BdkTransaction);
|
||||
|
||||
impl Transaction {
|
||||
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, TransactionError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
Ok(Transaction(tx))
|
||||
}
|
||||
|
||||
pub fn compute_txid(&self) -> String {
|
||||
self.0.compute_txid().to_string()
|
||||
}
|
||||
|
||||
pub fn weight(&self) -> u64 {
|
||||
self.0.weight().to_wu()
|
||||
}
|
||||
|
||||
pub fn total_size(&self) -> u64 {
|
||||
self.0.total_size() as u64
|
||||
}
|
||||
|
||||
pub fn vsize(&self) -> u64 {
|
||||
self.0.vsize() as u64
|
||||
}
|
||||
|
||||
pub fn is_coinbase(&self) -> bool {
|
||||
self.0.is_coinbase()
|
||||
}
|
||||
|
||||
pub fn is_explicitly_rbf(&self) -> bool {
|
||||
self.0.is_explicitly_rbf()
|
||||
}
|
||||
|
||||
pub fn is_lock_time_enabled(&self) -> bool {
|
||||
self.0.is_lock_time_enabled()
|
||||
}
|
||||
|
||||
pub fn version(&self) -> i32 {
|
||||
self.0.version.0
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
serialize(&self.0)
|
||||
}
|
||||
|
||||
pub fn input(&self) -> Vec<TxIn> {
|
||||
self.0.input.iter().map(|tx_in| tx_in.into()).collect()
|
||||
}
|
||||
|
||||
pub fn output(&self) -> Vec<TxOut> {
|
||||
self.0.output.iter().map(|tx_out| tx_out.into()).collect()
|
||||
}
|
||||
|
||||
pub fn lock_time(&self) -> u32 {
|
||||
self.0.lock_time.to_consensus_u32()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkTransaction> for Transaction {
|
||||
fn from(tx: BdkTransaction) -> Self {
|
||||
Transaction(tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BdkTransaction> for Transaction {
|
||||
fn from(tx: &BdkTransaction) -> Self {
|
||||
Transaction(tx.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Transaction> for BdkTransaction {
|
||||
fn from(tx: &Transaction) -> Self {
|
||||
tx.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Psbt(pub(crate) Mutex<BdkPsbt>);
|
||||
|
||||
impl Psbt {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, PsbtParseError> {
|
||||
let psbt: BdkPsbt = BdkPsbt::from_str(&psbt_base64)?;
|
||||
Ok(Psbt(Mutex::new(psbt)))
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> String {
|
||||
let psbt = self.0.lock().unwrap().clone();
|
||||
psbt.to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn extract_tx(&self) -> Result<Arc<Transaction>, ExtractTxError> {
|
||||
let tx: BdkTransaction = self.0.lock().unwrap().clone().extract_tx()?;
|
||||
let transaction: Transaction = tx.into();
|
||||
Ok(Arc::new(transaction))
|
||||
}
|
||||
|
||||
pub(crate) fn fee(&self) -> Result<u64, PsbtError> {
|
||||
self.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fee()
|
||||
.map(|fee| fee.to_sat())
|
||||
.map_err(PsbtError::from)
|
||||
}
|
||||
|
||||
pub(crate) fn combine(&self, other: Arc<Psbt>) -> Result<Arc<Psbt>, PsbtError> {
|
||||
let mut original_psbt = self.0.lock().unwrap().clone();
|
||||
let other_psbt = other.0.lock().unwrap().clone();
|
||||
original_psbt.combine(other_psbt)?;
|
||||
Ok(Arc::new(Psbt(Mutex::new(original_psbt))))
|
||||
}
|
||||
|
||||
pub(crate) fn json_serialize(&self) -> String {
|
||||
let psbt = self.0.lock().unwrap();
|
||||
serde_json::to_string(psbt.deref()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkPsbt> for Psbt {
|
||||
fn from(psbt: BdkPsbt) -> Self {
|
||||
Psbt(Mutex::new(psbt))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OutPoint {
|
||||
pub txid: String,
|
||||
pub vout: u32,
|
||||
}
|
||||
|
||||
impl From<&OutPoint> for BdkOutPoint {
|
||||
fn from(outpoint: &OutPoint) -> Self {
|
||||
BdkOutPoint {
|
||||
txid: Txid::from_str(&outpoint.txid).unwrap(),
|
||||
vout: outpoint.vout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&BdkOutPoint> for OutPoint {
|
||||
fn from(outpoint: &BdkOutPoint) -> Self {
|
||||
OutPoint {
|
||||
txid: outpoint.txid.to_string(),
|
||||
vout: outpoint.vout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxIn {
|
||||
pub previous_output: OutPoint,
|
||||
pub script_sig: Arc<Script>,
|
||||
pub sequence: u32,
|
||||
pub witness: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl From<&BdkTxIn> for TxIn {
|
||||
fn from(tx_in: &BdkTxIn) -> Self {
|
||||
TxIn {
|
||||
previous_output: OutPoint {
|
||||
txid: tx_in.previous_output.txid.to_string(),
|
||||
vout: tx_in.previous_output.vout,
|
||||
},
|
||||
script_sig: Arc::new(Script(tx_in.script_sig.clone())),
|
||||
sequence: tx_in.sequence.0,
|
||||
witness: tx_in.witness.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxOut {
|
||||
pub value: u64,
|
||||
pub script_pubkey: Arc<Script>,
|
||||
}
|
||||
|
||||
impl From<&BdkTxOut> for TxOut {
|
||||
fn from(tx_out: &BdkTxOut) -> Self {
|
||||
TxOut {
|
||||
value: tx_out.value.to_sat(),
|
||||
script_pubkey: Arc::new(Script(tx_out.script_pubkey.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FeeRate(pub(crate) BdkFeeRate);
|
||||
|
||||
impl FeeRate {
|
||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||
let fee_rate: Option<BdkFeeRate> = BdkFeeRate::from_sat_per_vb(sat_per_vb);
|
||||
match fee_rate {
|
||||
Some(fee_rate) => Ok(FeeRate(fee_rate)),
|
||||
None => Err(FeeRateError::ArithmeticOverflow),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self {
|
||||
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_ceil(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_ceil()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_vb_floor(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_floor()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_kwu(&self) -> u64 {
|
||||
self.0.to_sat_per_kwu()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::bitcoin::Address;
|
||||
use crate::bitcoin::Network;
|
||||
|
||||
#[test]
|
||||
fn test_is_valid_for_network() {
|
||||
// ====Docs tests====
|
||||
// https://docs.rs/bitcoin/0.29.2/src/bitcoin/util/address.rs.html#798-802
|
||||
|
||||
let docs_address_testnet_str = "2N83imGV3gPwBzKJQvWJ7cRUY2SpUyU6A5e";
|
||||
let docs_address_testnet =
|
||||
Address::new(docs_address_testnet_str.to_string(), Network::Testnet).unwrap();
|
||||
assert!(
|
||||
docs_address_testnet.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
docs_address_testnet.is_valid_for_network(Network::Signet),
|
||||
"Address should be valid for Signet"
|
||||
);
|
||||
assert!(
|
||||
docs_address_testnet.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
|
||||
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
|
||||
let docs_address_mainnet =
|
||||
Address::new(docs_address_mainnet_str.to_string(), Network::Bitcoin).unwrap();
|
||||
assert!(
|
||||
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should be valid for Bitcoin"
|
||||
);
|
||||
|
||||
// ====Bech32====
|
||||
|
||||
// | Network | Prefix | Address Type |
|
||||
// |-----------------|---------|--------------|
|
||||
// | Bitcoin Mainnet | `bc1` | Bech32 |
|
||||
// | Bitcoin Testnet | `tb1` | Bech32 |
|
||||
// | Bitcoin Signet | `tb1` | Bech32 |
|
||||
// | Bitcoin Regtest | `bcrt1` | Bech32 |
|
||||
|
||||
// Bech32 - Bitcoin
|
||||
// Valid for:
|
||||
// - Bitcoin
|
||||
// Not valid for:
|
||||
// - Testnet
|
||||
// - Signet
|
||||
// - Regtest
|
||||
let bitcoin_mainnet_bech32_address_str = "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj";
|
||||
let bitcoin_mainnet_bech32_address = Address::new(
|
||||
bitcoin_mainnet_bech32_address_str.to_string(),
|
||||
Network::Bitcoin,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should not be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Signet),
|
||||
"Address should not be valid for Signet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should not be valid for Regtest"
|
||||
);
|
||||
|
||||
// Bech32 - Testnet
|
||||
// Valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
// - Regtest
|
||||
let bitcoin_testnet_bech32_address_str =
|
||||
"tb1p4nel7wkc34raczk8c4jwk5cf9d47u2284rxn98rsjrs4w3p2sheqvjmfdh";
|
||||
let bitcoin_testnet_bech32_address = Address::new(
|
||||
bitcoin_testnet_bech32_address_str.to_string(),
|
||||
Network::Testnet,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Signet),
|
||||
"Address should be valid for Signet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should not not be valid for Regtest"
|
||||
);
|
||||
|
||||
// Bech32 - Signet
|
||||
// Valid for:
|
||||
// - Signet
|
||||
// - Testnet
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
// - Regtest
|
||||
let bitcoin_signet_bech32_address_str =
|
||||
"tb1pwzv7fv35yl7ypwj8w7al2t8apd6yf4568cs772qjwper74xqc99sk8x7tk";
|
||||
let bitcoin_signet_bech32_address = Address::new(
|
||||
bitcoin_signet_bech32_address_str.to_string(),
|
||||
Network::Signet,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_signet_bech32_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_signet_bech32_address.is_valid_for_network(Network::Signet),
|
||||
"Address should be valid for Signet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should not not be valid for Regtest"
|
||||
);
|
||||
|
||||
// Bech32 - Regtest
|
||||
// Valid for:
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
// - Testnet
|
||||
// - Signet
|
||||
let bitcoin_regtest_bech32_address_str = "bcrt1q39c0vrwpgfjkhasu5mfke9wnym45nydfwaeems";
|
||||
let bitcoin_regtest_bech32_address = Address::new(
|
||||
bitcoin_regtest_bech32_address_str.to_string(),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should not be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Signet),
|
||||
"Address should not be valid for Signet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_regtest_bech32_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
|
||||
// ====P2PKH====
|
||||
|
||||
// | Network | Prefix for P2PKH | Prefix for P2SH |
|
||||
// |------------------------------------|------------------|-----------------|
|
||||
// | Bitcoin Mainnet | `1` | `3` |
|
||||
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
|
||||
|
||||
// P2PKH - Bitcoin
|
||||
// Valid for:
|
||||
// - Bitcoin
|
||||
// Not valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
let bitcoin_mainnet_p2pkh_address_str = "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH";
|
||||
let bitcoin_mainnet_p2pkh_address = Address::new(
|
||||
bitcoin_mainnet_p2pkh_address_str.to_string(),
|
||||
Network::Bitcoin,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should not be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should not be valid for Regtest"
|
||||
);
|
||||
|
||||
// P2PKH - Testnet
|
||||
// Valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
let bitcoin_testnet_p2pkh_address_str = "mucFNhKMYoBQYUAEsrFVscQ1YaFQPekBpg";
|
||||
let bitcoin_testnet_p2pkh_address = Address::new(
|
||||
bitcoin_testnet_p2pkh_address_str.to_string(),
|
||||
Network::Testnet,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
|
||||
// P2PKH - Regtest
|
||||
// Valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
let bitcoin_regtest_p2pkh_address_str = "msiGFK1PjCk8E6FXeoGkQPTscmcpyBdkgS";
|
||||
let bitcoin_regtest_p2pkh_address = Address::new(
|
||||
bitcoin_regtest_p2pkh_address_str.to_string(),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
|
||||
// ====P2SH====
|
||||
|
||||
// | Network | Prefix for P2PKH | Prefix for P2SH |
|
||||
// |------------------------------------|------------------|-----------------|
|
||||
// | Bitcoin Mainnet | `1` | `3` |
|
||||
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
|
||||
|
||||
// P2SH - Bitcoin
|
||||
// Valid for:
|
||||
// - Bitcoin
|
||||
// Not valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
let bitcoin_mainnet_p2sh_address_str = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy";
|
||||
let bitcoin_mainnet_p2sh_address = Address::new(
|
||||
bitcoin_mainnet_p2sh_address_str.to_string(),
|
||||
Network::Bitcoin,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should not be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should not be valid for Regtest"
|
||||
);
|
||||
|
||||
// P2SH - Testnet
|
||||
// Valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
let bitcoin_testnet_p2sh_address_str = "2NFUBBRcTJbYc1D4HSCbJhKZp6YCV4PQFpQ";
|
||||
let bitcoin_testnet_p2sh_address = Address::new(
|
||||
bitcoin_testnet_p2sh_address_str.to_string(),
|
||||
Network::Testnet,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
|
||||
// P2SH - Regtest
|
||||
// Valid for:
|
||||
// - Testnet
|
||||
// - Regtest
|
||||
// Not valid for:
|
||||
// - Bitcoin
|
||||
let bitcoin_regtest_p2sh_address_str = "2NEb8N5B9jhPUCBchz16BB7bkJk8VCZQjf3";
|
||||
let bitcoin_regtest_p2sh_address = Address::new(
|
||||
bitcoin_regtest_p2sh_address_str.to_string(),
|
||||
Network::Regtest,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
!bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Bitcoin),
|
||||
"Address should not be valid for Bitcoin"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Testnet),
|
||||
"Address should be valid for Testnet"
|
||||
);
|
||||
assert!(
|
||||
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Regtest),
|
||||
"Address should be valid for Regtest"
|
||||
);
|
||||
}
|
||||
}
|
402
bdk-ffi/src/descriptor.rs
Normal file
402
bdk-ffi/src/descriptor.rs
Normal file
@ -0,0 +1,402 @@
|
||||
use crate::error::DescriptorError;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use std::fmt::Display;
|
||||
|
||||
use bdk_wallet::bitcoin::bip32::Fingerprint;
|
||||
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||
use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||
use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||
use bdk_wallet::template::{
|
||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||
DescriptorTemplate,
|
||||
};
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Descriptor {
|
||||
pub extended_descriptor: ExtendedDescriptor,
|
||||
pub key_map: KeyMap,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, DescriptorError> {
|
||||
let secp = Secp256k1::new();
|
||||
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
|
||||
Ok(Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip44(
|
||||
secret_key: &DescriptorSecretKey,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip44(derivable_key, keychain_kind).build(network).unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip44_public(
|
||||
public_key: &DescriptorPublicKey,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip44Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49(
|
||||
secret_key: &DescriptorSecretKey,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip49(derivable_key, keychain_kind).build(network).unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49_public(
|
||||
public_key: &DescriptorPublicKey,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip49Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84(
|
||||
secret_key: &DescriptorSecretKey,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip84(derivable_key, keychain_kind).build(network).unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84_public(
|
||||
public_key: &DescriptorPublicKey,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip84Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip86(
|
||||
secret_key: &DescriptorSecretKey,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip86(derivable_key, keychain_kind).build(network).unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip86_public(
|
||||
public_key: &DescriptorPublicKey,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.0;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip86Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_string_with_secret(&self) -> String {
|
||||
let descriptor = &self.extended_descriptor;
|
||||
let key_map = &self.key_map;
|
||||
descriptor.to_string_with_secret(key_map)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Descriptor {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.extended_descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
fn get_descriptor_secret_key() -> DescriptorSecretKey {
|
||||
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_descriptor_templates() {
|
||||
let master: DescriptorSecretKey = get_descriptor_secret_key();
|
||||
println!("Master: {:?}", master.as_string());
|
||||
// tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h
|
||||
let handmade_public_44 = master
|
||||
.derive(&DerivationPath::new("m/44h/1h/0h".to_string()).unwrap())
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 44: {}", handmade_public_44.as_string());
|
||||
// Public 44: [d1d04177/44'/1'/0']tpubDCoPjomfTqh1e7o1WgGpQtARWtkueXQAepTeNpWiitS3Sdv8RKJ1yvTrGHcwjDXp2SKyMrTEca4LoN7gEUiGCWboyWe2rz99Kf4jK4m2Zmx/*
|
||||
let handmade_public_49 = master
|
||||
.derive(&DerivationPath::new("m/49h/1h/0h".to_string()).unwrap())
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 49: {}", handmade_public_49.as_string());
|
||||
// Public 49: [d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/*
|
||||
let handmade_public_84 = master
|
||||
.derive(&DerivationPath::new("m/84h/1h/0h".to_string()).unwrap())
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 84: {}", handmade_public_84.as_string());
|
||||
// Public 84: [d1d04177/84'/1'/0']tpubDDNxbq17egjFk2edjv8oLnzxk52zny9aAYNv9CMqTzA4mQDiQq818sEkNe9Gzmd4QU8558zftqbfoVBDQorG3E4Wq26tB2JeE4KUoahLkx6/*
|
||||
let handmade_public_86 = master
|
||||
.derive(&DerivationPath::new("m/86h/1h/0h".to_string()).unwrap())
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 86: {}", handmade_public_86.as_string());
|
||||
// Public 86: [d1d04177/86'/1'/0']tpubDCJzjbcGbdEfXMWaL6QmgVmuSfXkrue7m2YNoacWwyc7a2XjXaKojRqNEbo41CFL3PyYmKdhwg2fkGpLX4SQCbQjCGxAkWHJTw9WEeenrJb/*
|
||||
let template_private_44 =
|
||||
Descriptor::new_bip44(&master, KeychainKind::External, Network::Testnet);
|
||||
let template_private_49 =
|
||||
Descriptor::new_bip49(&master, KeychainKind::External, Network::Testnet);
|
||||
let template_private_84 =
|
||||
Descriptor::new_bip84(&master, KeychainKind::External, Network::Testnet);
|
||||
let template_private_86 =
|
||||
Descriptor::new_bip86(&master, KeychainKind::External, Network::Testnet);
|
||||
// the extended public keys are the same when creating them manually as they are with the templates
|
||||
println!("Template 49: {}", template_private_49);
|
||||
println!("Template 44: {}", template_private_44);
|
||||
println!("Template 84: {}", template_private_84);
|
||||
println!("Template 86: {}", template_private_86);
|
||||
let template_public_44 = Descriptor::new_bip44_public(
|
||||
&handmade_public_44,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_49 = Descriptor::new_bip49_public(
|
||||
&handmade_public_49,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_84 = Descriptor::new_bip84_public(
|
||||
&handmade_public_84,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_86 = Descriptor::new_bip86_public(
|
||||
&handmade_public_86,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
println!("Template public 49: {}", template_public_49);
|
||||
println!("Template public 44: {}", template_public_44);
|
||||
println!("Template public 84: {}", template_public_84);
|
||||
println!("Template public 86: {}", template_public_86);
|
||||
// when using a public key, both to_string and as_string_private return the same string
|
||||
assert_eq!(
|
||||
template_public_44.to_string_with_secret(),
|
||||
template_public_44.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_49.to_string_with_secret(),
|
||||
template_public_49.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_84.to_string_with_secret(),
|
||||
template_public_84.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_86.to_string_with_secret(),
|
||||
template_public_86.to_string()
|
||||
);
|
||||
// when using to_string on a private key, we get the same result as when using it on a public key
|
||||
assert_eq!(
|
||||
template_private_44.to_string(),
|
||||
template_public_44.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_49.to_string(),
|
||||
template_public_49.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_84.to_string(),
|
||||
template_public_84.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_86.to_string(),
|
||||
template_public_86.to_string()
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_descriptor_from_string() {
|
||||
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet);
|
||||
let descriptor2 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Bitcoin);
|
||||
// Creating a Descriptor using an extended key that doesn't match the network provided will throw a DescriptorError::Key with inner InvalidNetwork error
|
||||
assert!(descriptor1.is_ok());
|
||||
assert_matches!(descriptor2.unwrap_err(), DescriptorError::Key { .. });
|
||||
}
|
||||
}
|
101
bdk-ffi/src/electrum.rs
Normal file
101
bdk-ffi/src/electrum.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
|
||||
use bdk_electrum::BdkElectrumClient as BdkBdkElectrumClient;
|
||||
use bdk_electrum::{ElectrumFullScanResult, ElectrumSyncResult};
|
||||
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
|
||||
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
// NOTE: We are keeping our naming convention where the alias of the inner type is the Rust type
|
||||
// prefixed with `Bdk`. In this case the inner type is `BdkElectrumClient`, so the alias is
|
||||
// funnily enough named `BdkBdkElectrumClient`.
|
||||
pub struct ElectrumClient(BdkBdkElectrumClient<bdk_electrum::electrum_client::Client>);
|
||||
|
||||
impl ElectrumClient {
|
||||
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
||||
let inner_client: bdk_electrum::electrum_client::Client =
|
||||
bdk_electrum::electrum_client::Client::new(url.as_str())?;
|
||||
let client = BdkBdkElectrumClient::new(inner_client);
|
||||
Ok(Self(client))
|
||||
}
|
||||
|
||||
pub fn full_scan(
|
||||
&self,
|
||||
request: Arc<FullScanRequest>,
|
||||
stop_gap: u64,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkFullScanRequest<KeychainKind> = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
|
||||
request,
|
||||
stop_gap as usize,
|
||||
batch_size as usize,
|
||||
fetch_prev_txouts,
|
||||
)?;
|
||||
let full_scan_result: BdkFullScanResult<KeychainKind> =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: full_scan_result.last_active_indices,
|
||||
graph: full_scan_result.graph_update,
|
||||
chain: Some(full_scan_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn sync(
|
||||
&self,
|
||||
request: Arc<SyncRequest>,
|
||||
batch_size: u64,
|
||||
fetch_prev_txouts: bool,
|
||||
) -> Result<Arc<Update>, ElectrumError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkSyncRequest = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||
|
||||
let electrum_result: ElectrumSyncResult =
|
||||
self.0
|
||||
.sync(request, batch_size as usize, fetch_prev_txouts)?;
|
||||
let sync_result: BdkSyncResult =
|
||||
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: sync_result.graph_update,
|
||||
chain: Some(sync_result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.transaction_broadcast(&bdk_transaction)
|
||||
.map_err(ElectrumError::from)
|
||||
.map(|txid| txid.to_string())
|
||||
}
|
||||
}
|
1960
bdk-ffi/src/error.rs
Normal file
1960
bdk-ffi/src/error.rs
Normal file
File diff suppressed because it is too large
Load Diff
84
bdk-ffi/src/esplora.rs
Normal file
84
bdk-ffi/src/esplora.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::error::EsploraError;
|
||||
use crate::types::{FullScanRequest, SyncRequest};
|
||||
use crate::wallet::Update;
|
||||
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
use bdk_esplora::EsploraExt;
|
||||
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
|
||||
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
|
||||
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct EsploraClient(BlockingClient);
|
||||
|
||||
impl EsploraClient {
|
||||
pub fn new(url: String) -> Self {
|
||||
let client = Builder::new(url.as_str()).build_blocking();
|
||||
Self(client)
|
||||
}
|
||||
|
||||
pub fn full_scan(
|
||||
&self,
|
||||
request: Arc<FullScanRequest>,
|
||||
stop_gap: u64,
|
||||
parallel_requests: u64,
|
||||
) -> Result<Arc<Update>, EsploraError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkFullScanRequest<KeychainKind> = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(EsploraError::RequestAlreadyConsumed)?;
|
||||
|
||||
let result: BdkFullScanResult<KeychainKind> =
|
||||
self.0
|
||||
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: result.last_active_indices,
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn sync(
|
||||
&self,
|
||||
request: Arc<SyncRequest>,
|
||||
parallel_requests: u64,
|
||||
) -> Result<Arc<Update>, EsploraError> {
|
||||
// using option and take is not ideal but the only way to take full ownership of the request
|
||||
let request: BdkSyncRequest = request
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.ok_or(EsploraError::RequestAlreadyConsumed)?;
|
||||
|
||||
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices: BTreeMap::default(),
|
||||
graph: result.graph_update,
|
||||
chain: Some(result.chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.broadcast(&bdk_transaction)
|
||||
.map_err(EsploraError::from)
|
||||
}
|
||||
}
|
357
bdk-ffi/src/keys.rs
Normal file
357
bdk-ffi/src/keys.rs
Normal file
@ -0,0 +1,357 @@
|
||||
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
|
||||
use std::fmt::Display;
|
||||
|
||||
use bdk_wallet::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk_wallet::bitcoin::key::Secp256k1;
|
||||
use bdk_wallet::bitcoin::secp256k1::rand;
|
||||
use bdk_wallet::bitcoin::secp256k1::rand::Rng;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::keys::bip39::WordCount;
|
||||
use bdk_wallet::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||
use bdk_wallet::keys::{
|
||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||
};
|
||||
use bdk_wallet::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||
use bdk_wallet::miniscript::BareCtx;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub(crate) struct Mnemonic(BdkMnemonic);
|
||||
|
||||
impl Mnemonic {
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
// TODO 4: I DON'T KNOW IF THIS IS A DECENT WAY TO GENERATE ENTROPY PLEASE CONFIRM
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut entropy = [0u8; 32];
|
||||
rng.fill(&mut entropy);
|
||||
|
||||
let generated_key: GeneratedKey<_, BareCtx> =
|
||||
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
|
||||
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||
Mnemonic(mnemonic)
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(mnemonic: String) -> Result<Self, Bip39Error> {
|
||||
BdkMnemonic::from_str(&mnemonic)
|
||||
.map(Mnemonic)
|
||||
.map_err(Bip39Error::from)
|
||||
}
|
||||
|
||||
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, Bip39Error> {
|
||||
BdkMnemonic::from_entropy(entropy.as_slice())
|
||||
.map(Mnemonic)
|
||||
.map_err(Bip39Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Mnemonic {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DerivationPath {
|
||||
inner_mutex: Mutex<BdkDerivationPath>,
|
||||
}
|
||||
|
||||
impl DerivationPath {
|
||||
pub(crate) fn new(path: String) -> Result<Self, Bip32Error> {
|
||||
BdkDerivationPath::from_str(&path)
|
||||
.map(|x| DerivationPath {
|
||||
inner_mutex: Mutex::new(x),
|
||||
})
|
||||
.map_err(Bip32Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorSecretKey(pub(crate) BdkDescriptorSecretKey);
|
||||
|
||||
impl DescriptorSecretKey {
|
||||
pub(crate) fn new(network: Network, mnemonic: &Mnemonic, password: Option<String>) -> Self {
|
||||
let mnemonic = mnemonic.0.clone();
|
||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: None,
|
||||
xkey: xkey.into_xprv(network).unwrap(),
|
||||
derivation_path: BdkDerivationPath::master(),
|
||||
wildcard: Wildcard::Unhardened,
|
||||
});
|
||||
Self(descriptor_secret_key)
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(private_key: String) -> Result<Self, DescriptorKeyError> {
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
Ok(Self(descriptor_secret_key))
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_secret_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derived_xprv = descriptor_x_key
|
||||
.xkey
|
||||
.derive_priv(&secp, &path)
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(&secp), path),
|
||||
};
|
||||
let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: Some(key_source),
|
||||
xkey: derived_xprv,
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self(derived_descriptor_secret_key)))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let descriptor_secret_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: descriptor_x_key.origin.clone(),
|
||||
xkey: descriptor_x_key.xkey,
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self(extended_descriptor_secret_key)))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = self.0.to_public(&secp).unwrap();
|
||||
Arc::new(DescriptorPublicKey(descriptor_public_key))
|
||||
}
|
||||
|
||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||
let inner = &self.0;
|
||||
let secret_bytes: Vec<u8> = match inner {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
secret_bytes
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorPublicKey(pub(crate) BdkDescriptorPublicKey);
|
||||
|
||||
impl DescriptorPublicKey {
|
||||
pub(crate) fn from_string(public_key: String) -> Result<Self, DescriptorKeyError> {
|
||||
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
Ok(Self(descriptor_public_key))
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derived_xpub = descriptor_x_key
|
||||
.xkey
|
||||
.derive_pub(&secp, &path)
|
||||
.map_err(DescriptorKeyError::from)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(), path),
|
||||
};
|
||||
let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
origin: Some(key_source),
|
||||
xkey: derived_xpub,
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self(derived_descriptor_public_key)))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
|
||||
let descriptor_public_key = &self.0;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
origin: descriptor_x_key.origin.clone(),
|
||||
xkey: descriptor_x_key.xkey,
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self(extended_descriptor_public_key)))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_inner() -> DescriptorSecretKey {
|
||||
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
|
||||
}
|
||||
|
||||
fn derive_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.derive(&path)
|
||||
}
|
||||
|
||||
fn extend_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.extend(&path)
|
||||
}
|
||||
|
||||
fn derive_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.derive(&path)
|
||||
}
|
||||
|
||||
fn extend_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
|
||||
let path = DerivationPath::new(path.to_string()).unwrap();
|
||||
key.extend(&path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_descriptor_secret_key() {
|
||||
let master_dsk = get_inner();
|
||||
assert_eq!(master_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
|
||||
assert_eq!(master_dsk.as_public().as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_self() {
|
||||
let master_dsk = get_inner();
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177]tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m").unwrap();
|
||||
assert_eq!(derived_dpk.as_string(), "[d1d04177]tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_descriptors_keys() {
|
||||
let master_dsk = get_inner();
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m/0").unwrap();
|
||||
assert_eq!(derived_dpk.as_string(), "[d1d04177/0]tpubD9oaCiP1MPmQdndm7DCD3D3QU34pWd6BbKSRedoZF1UJcNhEk3PJwkALNYkhxeTKL29oGNR7psqvT1KZydCGqUDEKXN6dVQJY2R8ooLPy8m/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_descriptor_keys() {
|
||||
let master_dsk = get_inner();
|
||||
let extended_dsk: &DescriptorSecretKey = &extend_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(extended_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let extended_dpk: &DescriptorPublicKey = &extend_dpk(master_dpk, "m/0").unwrap();
|
||||
assert_eq!(extended_dpk.as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/*");
|
||||
let wif = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
|
||||
let extended_key = DescriptorSecretKey::from_string(wif.to_string()).unwrap();
|
||||
let result = extended_key.derive(&DerivationPath::new("m/0".to_string()).unwrap());
|
||||
dbg!(&result);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_inner() {
|
||||
let key1 = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
|
||||
let key2 = "tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/1/1/*";
|
||||
let private_descriptor_key1 = DescriptorSecretKey::from_string(key1.to_string()).unwrap();
|
||||
let private_descriptor_key2 = DescriptorSecretKey::from_string(key2.to_string()).unwrap();
|
||||
dbg!(private_descriptor_key1);
|
||||
dbg!(private_descriptor_key2);
|
||||
// Should error out because you can't produce a DescriptorSecretKey from an xpub
|
||||
let key0 = "tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi";
|
||||
assert!(DescriptorSecretKey::from_string(key0.to_string()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_and_extend_inner() {
|
||||
let master_dsk = get_inner();
|
||||
// derive DescriptorSecretKey with path "m/0" from master
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
||||
// extend derived_dsk with path "m/0"
|
||||
let extended_dsk: &DescriptorSecretKey = &extend_dsk(derived_dsk, "m/0").unwrap();
|
||||
assert_eq!(extended_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/0/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_hardened_path_using_public() {
|
||||
let master_dpk = get_inner().as_public();
|
||||
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
|
||||
assert!(derived_dpk.is_err());
|
||||
}
|
||||
|
||||
// TODO 7: It appears that the to_hex() method is not available anymore.
|
||||
// Look into the correct way to pull the hex out of the DescriptorSecretKey.
|
||||
// Note: ToHex was removed in bitcoin_hashes 0.12.0
|
||||
// #[test]
|
||||
// fn test_retrieve_master_secret_key() {
|
||||
// let master_dpk = get_inner();
|
||||
// let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||
// assert_eq!(
|
||||
// master_private_key,
|
||||
// "e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||
// )
|
||||
// }
|
||||
}
|
73
bdk-ffi/src/lib.rs
Normal file
73
bdk-ffi/src/lib.rs
Normal file
@ -0,0 +1,73 @@
|
||||
mod bitcoin;
|
||||
mod descriptor;
|
||||
mod electrum;
|
||||
mod error;
|
||||
mod esplora;
|
||||
mod keys;
|
||||
mod store;
|
||||
mod types;
|
||||
mod wallet;
|
||||
|
||||
use crate::bitcoin::Address;
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::FeeRate;
|
||||
use crate::bitcoin::OutPoint;
|
||||
use crate::bitcoin::Psbt;
|
||||
use crate::bitcoin::Script;
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::bitcoin::TxIn;
|
||||
use crate::bitcoin::TxOut;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::electrum::ElectrumClient;
|
||||
use crate::error::AddressParseError;
|
||||
use crate::error::Bip32Error;
|
||||
use crate::error::Bip39Error;
|
||||
use crate::error::CalculateFeeError;
|
||||
use crate::error::CannotConnectError;
|
||||
use crate::error::CreateTxError;
|
||||
use crate::error::DescriptorError;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::error::ElectrumError;
|
||||
use crate::error::EsploraError;
|
||||
use crate::error::ExtractTxError;
|
||||
use crate::error::FeeRateError;
|
||||
use crate::error::FromScriptError;
|
||||
use crate::error::InspectError;
|
||||
use crate::error::ParseAmountError;
|
||||
use crate::error::PersistenceError;
|
||||
use crate::error::PsbtError;
|
||||
use crate::error::PsbtParseError;
|
||||
use crate::error::SignerError;
|
||||
use crate::error::SqliteError;
|
||||
use crate::error::TransactionError;
|
||||
use crate::error::TxidParseError;
|
||||
use crate::error::WalletCreationError;
|
||||
use crate::esplora::EsploraClient;
|
||||
use crate::keys::DerivationPath;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use crate::keys::Mnemonic;
|
||||
use crate::store::SqliteStore;
|
||||
use crate::types::AddressInfo;
|
||||
use crate::types::Balance;
|
||||
use crate::types::CanonicalTx;
|
||||
use crate::types::ChainPosition;
|
||||
use crate::types::ChangeSet;
|
||||
use crate::types::FullScanRequest;
|
||||
use crate::types::FullScanScriptInspector;
|
||||
use crate::types::LocalOutput;
|
||||
use crate::types::ScriptAmount;
|
||||
use crate::types::SyncRequest;
|
||||
use crate::types::SyncScriptInspector;
|
||||
use crate::wallet::BumpFeeTxBuilder;
|
||||
use crate::wallet::SentAndReceivedValues;
|
||||
use crate::wallet::TxBuilder;
|
||||
use crate::wallet::Update;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::keys::bip39::WordCount;
|
||||
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
uniffi::include_scaffolding!("bdk");
|
39
bdk-ffi/src/store.rs
Normal file
39
bdk-ffi/src/store.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::error::SqliteError;
|
||||
use crate::types::ChangeSet;
|
||||
|
||||
use bdk_sqlite::rusqlite::Connection;
|
||||
use bdk_sqlite::{Store as BdkSqliteStore, Store};
|
||||
use bdk_wallet::chain::ConfirmationTimeHeightAnchor;
|
||||
use bdk_wallet::KeychainKind;
|
||||
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
pub struct SqliteStore(Mutex<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>>);
|
||||
|
||||
impl SqliteStore {
|
||||
pub fn new(path: String) -> Result<Self, SqliteError> {
|
||||
let connection = Connection::open(path)?;
|
||||
let db = Store::new(connection)?;
|
||||
Ok(Self(Mutex::new(db)))
|
||||
}
|
||||
|
||||
pub(crate) fn get_store(
|
||||
&self,
|
||||
) -> MutexGuard<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>> {
|
||||
self.0.lock().expect("sqlite store")
|
||||
}
|
||||
|
||||
pub fn write(&self, changeset: &ChangeSet) -> Result<(), SqliteError> {
|
||||
self.get_store()
|
||||
.write(&changeset.0)
|
||||
.map_err(SqliteError::from)
|
||||
}
|
||||
|
||||
pub fn read(&self) -> Result<Option<Arc<ChangeSet>>, SqliteError> {
|
||||
self.get_store()
|
||||
.read()
|
||||
.map_err(SqliteError::from)
|
||||
.map(|optional_bdk_change_set| optional_bdk_change_set.map(ChangeSet::from))
|
||||
.map(|optional_change_set| optional_change_set.map(Arc::new))
|
||||
}
|
||||
}
|
173
bdk-ffi/src/types.rs
Normal file
173
bdk-ffi/src/types.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
|
||||
use crate::InspectError;
|
||||
|
||||
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
|
||||
use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
|
||||
use bdk_wallet::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
|
||||
use bdk_wallet::wallet::AddressInfo as BdkAddressInfo;
|
||||
use bdk_wallet::wallet::Balance as BdkBalance;
|
||||
use bdk_wallet::KeychainKind;
|
||||
use bdk_wallet::LocalOutput as BdkLocalOutput;
|
||||
|
||||
use bdk_electrum::bdk_chain::CombinedChangeSet;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ChainPosition {
|
||||
Confirmed { height: u32, timestamp: u64 },
|
||||
Unconfirmed { timestamp: u64 },
|
||||
}
|
||||
|
||||
pub struct CanonicalTx {
|
||||
pub transaction: Arc<Transaction>,
|
||||
pub chain_position: ChainPosition,
|
||||
}
|
||||
|
||||
impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>> for CanonicalTx {
|
||||
fn from(tx: BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>) -> Self {
|
||||
let chain_position = match tx.chain_position {
|
||||
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
|
||||
height: anchor.confirmation_height,
|
||||
timestamp: anchor.confirmation_time,
|
||||
},
|
||||
BdkChainPosition::Unconfirmed(timestamp) => ChainPosition::Unconfirmed { timestamp },
|
||||
};
|
||||
|
||||
CanonicalTx {
|
||||
transaction: Arc::new(Transaction::from(tx.tx_node.tx.as_ref().clone())),
|
||||
chain_position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScriptAmount {
|
||||
pub script: Arc<Script>,
|
||||
pub amount: Arc<Amount>,
|
||||
}
|
||||
|
||||
pub struct AddressInfo {
|
||||
pub index: u32,
|
||||
pub address: Arc<Address>,
|
||||
pub keychain: KeychainKind,
|
||||
}
|
||||
|
||||
impl From<BdkAddressInfo> for AddressInfo {
|
||||
fn from(address_info: BdkAddressInfo) -> Self {
|
||||
AddressInfo {
|
||||
index: address_info.index,
|
||||
address: Arc::new(address_info.address.into()),
|
||||
keychain: address_info.keychain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Balance {
|
||||
pub immature: Arc<Amount>,
|
||||
pub trusted_pending: Arc<Amount>,
|
||||
pub untrusted_pending: Arc<Amount>,
|
||||
pub confirmed: Arc<Amount>,
|
||||
pub trusted_spendable: Arc<Amount>,
|
||||
pub total: Arc<Amount>,
|
||||
}
|
||||
|
||||
impl From<BdkBalance> for Balance {
|
||||
fn from(bdk_balance: BdkBalance) -> Self {
|
||||
Balance {
|
||||
immature: Arc::new(bdk_balance.immature.into()),
|
||||
trusted_pending: Arc::new(bdk_balance.trusted_pending.into()),
|
||||
untrusted_pending: Arc::new(bdk_balance.untrusted_pending.into()),
|
||||
confirmed: Arc::new(bdk_balance.confirmed.into()),
|
||||
trusted_spendable: Arc::new(bdk_balance.trusted_spendable().into()),
|
||||
total: Arc::new(bdk_balance.total().into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChangeSet(pub(crate) CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>);
|
||||
|
||||
impl From<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> for ChangeSet {
|
||||
fn from(change_set: CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>) -> Self {
|
||||
ChangeSet(change_set)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalOutput {
|
||||
pub outpoint: OutPoint,
|
||||
pub txout: TxOut,
|
||||
pub keychain: KeychainKind,
|
||||
pub is_spent: bool,
|
||||
}
|
||||
|
||||
impl From<BdkLocalOutput> for LocalOutput {
|
||||
fn from(local_utxo: BdkLocalOutput) -> Self {
|
||||
LocalOutput {
|
||||
outpoint: OutPoint {
|
||||
txid: local_utxo.outpoint.txid.to_string(),
|
||||
vout: local_utxo.outpoint.vout,
|
||||
},
|
||||
txout: TxOut {
|
||||
value: local_utxo.txout.value.to_sat(),
|
||||
script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)),
|
||||
},
|
||||
keychain: local_utxo.keychain,
|
||||
is_spent: local_utxo.is_spent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for the FullScanRequest
|
||||
pub trait FullScanScriptInspector: Sync + Send {
|
||||
fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
|
||||
}
|
||||
|
||||
// Callback for the SyncRequest
|
||||
pub trait SyncScriptInspector: Sync + Send {
|
||||
fn inspect(&self, script: Arc<Script>, total: u64);
|
||||
}
|
||||
|
||||
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
|
||||
|
||||
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);
|
||||
|
||||
impl SyncRequest {
|
||||
pub fn inspect_spks(
|
||||
&self,
|
||||
inspector: Arc<dyn SyncScriptInspector>,
|
||||
) -> Result<Arc<Self>, InspectError> {
|
||||
let mut guard = self.0.lock().unwrap();
|
||||
if let Some(sync_request) = guard.take() {
|
||||
let total = sync_request.spks.len() as u64;
|
||||
let sync_request = sync_request.inspect_spks(move |spk| {
|
||||
inspector.inspect(Arc::new(BdkScriptBuf::from(spk).into()), total)
|
||||
});
|
||||
Ok(Arc::new(SyncRequest(Mutex::new(Some(sync_request)))))
|
||||
} else {
|
||||
Err(InspectError::RequestAlreadyConsumed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FullScanRequest {
|
||||
pub fn inspect_spks_for_all_keychains(
|
||||
&self,
|
||||
inspector: Arc<dyn FullScanScriptInspector>,
|
||||
) -> Result<Arc<Self>, InspectError> {
|
||||
let mut guard = self.0.lock().unwrap();
|
||||
if let Some(full_scan_request) = guard.take() {
|
||||
let inspector = Arc::new(inspector);
|
||||
let full_scan_request =
|
||||
full_scan_request.inspect_spks_for_all_keychains(move |k, spk_i, script| {
|
||||
inspector.inspect(k, spk_i, Arc::new(BdkScriptBuf::from(script).into()))
|
||||
});
|
||||
Ok(Arc::new(FullScanRequest(Mutex::new(Some(
|
||||
full_scan_request,
|
||||
)))))
|
||||
} else {
|
||||
Err(InspectError::RequestAlreadyConsumed)
|
||||
}
|
||||
}
|
||||
}
|
726
bdk-ffi/src/wallet.rs
Normal file
726
bdk-ffi/src/wallet.rs
Normal file
@ -0,0 +1,726 @@
|
||||
use crate::bitcoin::Amount;
|
||||
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::error::{
|
||||
CalculateFeeError, CannotConnectError, CreateTxError, SignerError, TxidParseError,
|
||||
WalletCreationError,
|
||||
};
|
||||
use crate::types::{
|
||||
AddressInfo, Balance, CanonicalTx, ChangeSet, FullScanRequest, LocalOutput, ScriptAmount,
|
||||
SyncRequest,
|
||||
};
|
||||
|
||||
use bdk_wallet::bitcoin::amount::Amount as BdkAmount;
|
||||
use bdk_wallet::bitcoin::Network;
|
||||
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
|
||||
use bdk_wallet::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk_wallet::chain::{CombinedChangeSet, ConfirmationTimeHeightAnchor};
|
||||
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk_wallet::wallet::Update as BdkUpdate;
|
||||
use bdk_wallet::Wallet as BdkWallet;
|
||||
use bdk_wallet::{KeychainKind, SignOptions};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
pub struct Wallet {
|
||||
inner_mutex: Mutex<BdkWallet>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
pub fn new(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Arc<Descriptor>,
|
||||
network: Network,
|
||||
) -> Result<Self, WalletCreationError> {
|
||||
let descriptor = descriptor.to_string_with_secret();
|
||||
let change_descriptor = change_descriptor.to_string_with_secret();
|
||||
let wallet: BdkWallet = BdkWallet::new(&descriptor, &change_descriptor, network)?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_or_load(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Arc<Descriptor>,
|
||||
change_set: Option<Arc<ChangeSet>>,
|
||||
network: Network,
|
||||
) -> Result<Self, WalletCreationError> {
|
||||
let descriptor = descriptor.to_string_with_secret();
|
||||
let change_descriptor = change_descriptor.to_string_with_secret();
|
||||
let change_set: Option<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> =
|
||||
change_set.map(|cs| cs.0.clone());
|
||||
let wallet: BdkWallet =
|
||||
BdkWallet::new_or_load(&descriptor, &change_descriptor, change_set, network)?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet> {
|
||||
self.inner_mutex.lock().expect("wallet")
|
||||
}
|
||||
|
||||
pub fn reveal_next_address(&self, keychain_kind: KeychainKind) -> AddressInfo {
|
||||
self.get_wallet().reveal_next_address(keychain_kind).into()
|
||||
}
|
||||
|
||||
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), CannotConnectError> {
|
||||
self.get_wallet()
|
||||
.apply_update(update.0.clone())
|
||||
.map_err(CannotConnectError::from)
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
self.get_wallet().network()
|
||||
}
|
||||
|
||||
pub fn balance(&self) -> Balance {
|
||||
let bdk_balance = self.get_wallet().balance();
|
||||
Balance::from(bdk_balance)
|
||||
}
|
||||
|
||||
pub fn is_mine(&self, script: &Script) -> bool {
|
||||
self.get_wallet().is_mine(&script.0)
|
||||
}
|
||||
|
||||
pub(crate) fn sign(
|
||||
&self,
|
||||
psbt: Arc<Psbt>,
|
||||
// sign_options: Option<SignOptions>,
|
||||
) -> Result<bool, SignerError> {
|
||||
let mut psbt = psbt.0.lock().unwrap();
|
||||
self.get_wallet()
|
||||
.sign(&mut psbt, SignOptions::default())
|
||||
.map_err(SignerError::from)
|
||||
}
|
||||
|
||||
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
|
||||
let (sent, received) = self.get_wallet().sent_and_received(&tx.into());
|
||||
SentAndReceivedValues {
|
||||
sent: Arc::new(sent.into()),
|
||||
received: Arc::new(received.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transactions(&self) -> Vec<CanonicalTx> {
|
||||
self.get_wallet()
|
||||
.transactions()
|
||||
.map(|tx| tx.into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_tx(&self, txid: String) -> Result<Option<CanonicalTx>, TxidParseError> {
|
||||
let txid =
|
||||
Txid::from_str(txid.as_str()).map_err(|_| TxidParseError::InvalidTxid { txid })?;
|
||||
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
|
||||
}
|
||||
|
||||
pub fn calculate_fee(&self, tx: &Transaction) -> Result<Arc<Amount>, CalculateFeeError> {
|
||||
self.get_wallet()
|
||||
.calculate_fee(&tx.into())
|
||||
.map(Amount::from)
|
||||
.map(Arc::new)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<Arc<FeeRate>, CalculateFeeError> {
|
||||
self.get_wallet()
|
||||
.calculate_fee_rate(&tx.into())
|
||||
.map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate)))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub fn list_unspent(&self) -> Vec<LocalOutput> {
|
||||
self.get_wallet().list_unspent().map(|o| o.into()).collect()
|
||||
}
|
||||
|
||||
pub fn list_output(&self) -> Vec<LocalOutput> {
|
||||
self.get_wallet().list_output().map(|o| o.into()).collect()
|
||||
}
|
||||
|
||||
pub fn start_full_scan(&self) -> Arc<FullScanRequest> {
|
||||
let request = self.get_wallet().start_full_scan();
|
||||
Arc::new(FullScanRequest(Mutex::new(Some(request))))
|
||||
}
|
||||
|
||||
pub fn start_sync_with_revealed_spks(&self) -> Arc<SyncRequest> {
|
||||
let request = self.get_wallet().start_sync_with_revealed_spks();
|
||||
Arc::new(SyncRequest(Mutex::new(Some(request))))
|
||||
}
|
||||
|
||||
pub fn take_staged(&self) -> Option<Arc<ChangeSet>> {
|
||||
self.get_wallet()
|
||||
.take_staged()
|
||||
.map(|change_set| Arc::new(change_set.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SentAndReceivedValues {
|
||||
pub sent: Arc<Amount>,
|
||||
pub received: Arc<Amount>,
|
||||
}
|
||||
|
||||
pub struct Update(pub(crate) BdkUpdate);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TxBuilder {
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>,
|
||||
pub(crate) utxos: Vec<OutPoint>,
|
||||
pub(crate) unspendable: HashSet<OutPoint>,
|
||||
pub(crate) change_policy: ChangeSpendPolicy,
|
||||
pub(crate) manually_selected_only: bool,
|
||||
pub(crate) fee_rate: Option<FeeRate>,
|
||||
pub(crate) fee_absolute: Option<Arc<Amount>>,
|
||||
pub(crate) drain_wallet: bool,
|
||||
pub(crate) drain_to: Option<BdkScriptBuf>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
// pub(crate) data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TxBuilder {
|
||||
pub(crate) fn new() -> Self {
|
||||
TxBuilder {
|
||||
recipients: Vec::new(),
|
||||
utxos: Vec::new(),
|
||||
unspendable: HashSet::new(),
|
||||
change_policy: ChangeSpendPolicy::ChangeAllowed,
|
||||
manually_selected_only: false,
|
||||
fee_rate: None,
|
||||
fee_absolute: None,
|
||||
drain_wallet: false,
|
||||
drain_to: None,
|
||||
rbf: None,
|
||||
// data: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_recipient(&self, script: &Script, amount: Arc<Amount>) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, BdkAmount)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount.0)]);
|
||||
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
|
||||
let recipients = recipients
|
||||
.iter()
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount.0)) //;
|
||||
.collect();
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
|
||||
let mut unspendable_hash_set = self.unspendable.clone();
|
||||
unspendable_hash_set.insert(unspendable);
|
||||
Arc::new(TxBuilder {
|
||||
unspendable: unspendable_hash_set,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
unspendable: unspendable.into_iter().collect(),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
|
||||
self.add_utxos(vec![outpoint])
|
||||
}
|
||||
|
||||
pub(crate) fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
|
||||
let mut utxos = self.utxos.to_vec();
|
||||
utxos.append(&mut outpoints);
|
||||
Arc::new(TxBuilder {
|
||||
utxos,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn do_not_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::ChangeForbidden,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn only_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::OnlyChange,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn manually_selected_only(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
manually_selected_only: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fee_rate(&self, fee_rate: &FeeRate) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_rate: Some(fee_rate.clone()),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fee_absolute(&self, fee_amount: Arc<Amount>) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_absolute: Some(fee_amount),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn drain_wallet(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
drain_wallet: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn drain_to(&self, script: &Script) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
drain_to: Some(script.0.clone()),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
rbf: Some(RbfValue::Default),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
rbf: Some(RbfValue::Value(nsequence)),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
|
||||
// TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
for (script, amount) in &self.recipients {
|
||||
tx_builder.add_recipient(script.clone(), *amount);
|
||||
}
|
||||
tx_builder.change_policy(self.change_policy);
|
||||
if !self.utxos.is_empty() {
|
||||
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
|
||||
tx_builder
|
||||
.add_utxos(&bdk_utxos)
|
||||
.map_err(CreateTxError::from)?;
|
||||
}
|
||||
if !self.unspendable.is_empty() {
|
||||
let bdk_unspendable: Vec<BdkOutPoint> =
|
||||
self.unspendable.iter().map(BdkOutPoint::from).collect();
|
||||
tx_builder.unspendable(bdk_unspendable);
|
||||
}
|
||||
if self.manually_selected_only {
|
||||
tx_builder.manually_selected_only();
|
||||
}
|
||||
if let Some(fee_rate) = &self.fee_rate {
|
||||
tx_builder.fee_rate(fee_rate.0);
|
||||
}
|
||||
if let Some(fee_amount) = &self.fee_absolute {
|
||||
tx_builder.fee_absolute(fee_amount.0);
|
||||
}
|
||||
if self.drain_wallet {
|
||||
tx_builder.drain_wallet();
|
||||
}
|
||||
if let Some(script) = &self.drain_to {
|
||||
tx_builder.drain_to(script.clone());
|
||||
}
|
||||
if let Some(rbf) = &self.rbf {
|
||||
match *rbf {
|
||||
RbfValue::Default => {
|
||||
tx_builder.enable_rbf();
|
||||
}
|
||||
RbfValue::Value(nsequence) => {
|
||||
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let psbt = tx_builder.finish().map_err(CreateTxError::from)?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BumpFeeTxBuilder {
|
||||
pub(crate) txid: String,
|
||||
pub(crate) fee_rate: Arc<FeeRate>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
}
|
||||
|
||||
impl BumpFeeTxBuilder {
|
||||
pub(crate) fn new(txid: String, fee_rate: Arc<FeeRate>) -> Self {
|
||||
Self {
|
||||
txid,
|
||||
fee_rate,
|
||||
rbf: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rbf: Some(RbfValue::Default),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rbf: Some(RbfValue::Value(nsequence)),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, CreateTxError> {
|
||||
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo {
|
||||
outpoint: self.txid.clone(),
|
||||
})?;
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_fee_bump(txid).map_err(CreateTxError::from)?;
|
||||
tx_builder.fee_rate(self.fee_rate.0);
|
||||
if let Some(rbf) = &self.rbf {
|
||||
match *rbf {
|
||||
RbfValue::Default => {
|
||||
tx_builder.enable_rbf();
|
||||
}
|
||||
RbfValue::Value(nsequence) => {
|
||||
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
let psbt: BdkPsbt = tx_builder.finish()?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RbfValue {
|
||||
Default,
|
||||
Value(u32),
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use crate::database::DatabaseConfig;
|
||||
// use crate::descriptor::Descriptor;
|
||||
// use crate::keys::{DescriptorSecretKey, Mnemonic};
|
||||
// use crate::wallet::{AddressIndex, TxBuilder, Wallet};
|
||||
// use crate::Script;
|
||||
// use assert_matches::assert_matches;
|
||||
// use bdk::bitcoin::{Address, Network};
|
||||
// // use bdk::wallet::get_funded_wallet;
|
||||
// use bdk::KeychainKind;
|
||||
// use std::str::FromStr;
|
||||
// use std::sync::{Arc, Mutex};
|
||||
//
|
||||
// // #[test]
|
||||
// // fn test_drain_wallet() {
|
||||
// // let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
// // let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||
// // let test_wallet = Wallet {
|
||||
// // inner_mutex: Mutex::new(funded_wallet),
|
||||
// // };
|
||||
// // let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||
// // let drain_to_script = crate::Address::new(drain_to_address)
|
||||
// // .unwrap()
|
||||
// // .script_pubkey();
|
||||
// // let tx_builder = TxBuilder::new()
|
||||
// // .drain_wallet()
|
||||
// // .drain_to(drain_to_script.clone());
|
||||
// // assert!(tx_builder.drain_wallet);
|
||||
// // assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
||||
// //
|
||||
// // let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||
// // let psbt = tx_builder_result.psbt.inner.lock().unwrap().clone();
|
||||
// // let tx_details = tx_builder_result.transaction_details;
|
||||
// //
|
||||
// // // confirm one input with 50,000 sats
|
||||
// // assert_eq!(psbt.inputs.len(), 1);
|
||||
// // let input_value = psbt
|
||||
// // .inputs
|
||||
// // .get(0)
|
||||
// // .cloned()
|
||||
// // .unwrap()
|
||||
// // .non_witness_utxo
|
||||
// // .unwrap()
|
||||
// // .output
|
||||
// // .get(0)
|
||||
// // .unwrap()
|
||||
// // .value;
|
||||
// // assert_eq!(input_value, 50_000_u64);
|
||||
// //
|
||||
// // // confirm one output to correct address with all sats - fee
|
||||
// // assert_eq!(psbt.outputs.len(), 1);
|
||||
// // let output_address = Address::from_script(
|
||||
// // &psbt
|
||||
// // .unsigned_tx
|
||||
// // .output
|
||||
// // .get(0)
|
||||
// // .cloned()
|
||||
// // .unwrap()
|
||||
// // .script_pubkey,
|
||||
// // Network::Testnet,
|
||||
// // )
|
||||
// // .unwrap();
|
||||
// // assert_eq!(
|
||||
// // output_address,
|
||||
// // Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap()
|
||||
// // );
|
||||
// // let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value;
|
||||
// // assert_eq!(output_value, 49_890_u64); // input - fee
|
||||
// //
|
||||
// // assert_eq!(
|
||||
// // tx_details.txid,
|
||||
// // "312f1733badab22dc26b8dcbc83ba5629fb7b493af802e8abe07d865e49629c5"
|
||||
// // );
|
||||
// // assert_eq!(tx_details.received, 0);
|
||||
// // assert_eq!(tx_details.sent, 50000);
|
||||
// // assert!(tx_details.fee.is_some());
|
||||
// // assert_eq!(tx_details.fee.unwrap(), 110);
|
||||
// // assert!(tx_details.confirmation_time.is_none());
|
||||
// // }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_peek_reset_address() {
|
||||
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
|
||||
// let change_descriptor = Descriptor::new(
|
||||
// test_wpkh.to_string().replace("/0/*", "/1/*"),
|
||||
// Network::Regtest,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// Some(Arc::new(change_descriptor)),
|
||||
// Network::Regtest,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 2 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 1 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // new index still 0
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// );
|
||||
//
|
||||
// // new index now 1
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // new index now 2
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
|
||||
// );
|
||||
//
|
||||
// // peek index 1
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 1 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // reset to index 0
|
||||
// // assert_eq!(
|
||||
// // wallet
|
||||
// // .get_address(AddressIndex::Reset { index: 0 })
|
||||
// // .unwrap()
|
||||
// // .address
|
||||
// // .as_string(),
|
||||
// // "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// // );
|
||||
//
|
||||
// // new index 1 again
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_get_address() {
|
||||
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
|
||||
// let change_descriptor = Descriptor::new(
|
||||
// test_wpkh.to_string().replace("/0/*", "/1/*"),
|
||||
// Network::Regtest,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// Some(Arc::new(change_descriptor)),
|
||||
// Network::Regtest,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::LastUnused)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qpmz73cyx00r4a5dea469j40ax6d6kqyd67nnpj"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::LastUnused)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_is_mine() {
|
||||
// // is_mine should return true for addresses generated by the wallet
|
||||
// let mnemonic: Mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||
// let secret_key: DescriptorSecretKey =
|
||||
// DescriptorSecretKey::new(Network::Testnet, Arc::new(mnemonic), None);
|
||||
// let descriptor: Descriptor = Descriptor::new_bip84(
|
||||
// Arc::new(secret_key),
|
||||
// KeychainKind::External,
|
||||
// Network::Testnet,
|
||||
// );
|
||||
// let wallet: Wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// None,
|
||||
// Network::Testnet,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// // let address = wallet.get_address(AddressIndex::New).unwrap();
|
||||
// // let script: Arc<Script> = address.address.script_pubkey();
|
||||
//
|
||||
// // let is_mine_1: bool = wallet.is_mine(script).unwrap();
|
||||
// // assert!(is_mine_1);
|
||||
//
|
||||
// // is_mine returns false when provided a script that is not in the wallet
|
||||
// let other_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let other_descriptor = Descriptor::new(other_wpkh.to_string(), Network::Testnet).unwrap();
|
||||
//
|
||||
// let other_wallet = Wallet::new(
|
||||
// Arc::new(other_descriptor),
|
||||
// None,
|
||||
// Network::Testnet,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let other_address = other_wallet.get_address(AddressIndex::New).unwrap();
|
||||
// let other_script: Arc<Script> = other_address.address.script_pubkey();
|
||||
// let is_mine_2: bool = wallet.is_mine(other_script).unwrap();
|
||||
// assert_matches!(is_mine_2, false);
|
||||
// }
|
||||
// }
|
21
bdk-ffi/tests/README.md
Normal file
21
bdk-ffi/tests/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Integration tests for bdk-ffi
|
||||
|
||||
This contains simple tests to make sure bdk-ffi can be used as a dependency for each of the
|
||||
supported bindings languages.
|
||||
|
||||
To skip integration tests and only run unit tests use `cargo test --lib`.
|
||||
|
||||
To run all tests including integration tests use `CLASSPATH=./tests/jna/jna-5.14.0.jar cargo test`.
|
||||
|
||||
Before running integration tests you must install the following development tools:
|
||||
|
||||
1. [Java](https://openjdk.org/) and [Kotlin](https://kotlinlang.org/),
|
||||
[sdkman](https://sdkman.io/) can help:
|
||||
```shell
|
||||
sdk install java 11.0.16.1-zulu
|
||||
sdk install kotlin 1.7.20`
|
||||
```
|
||||
|
||||
2. [Swift](https://www.swift.org/)
|
||||
|
||||
3. [Python](https://www.python.org/)
|
8
bdk-ffi/tests/bindings/test.kts
Normal file
8
bdk-ffi/tests/bindings/test.kts
Normal file
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* This is a basic test kotlin program that does nothing but confirm that the kotlin bindings compile
|
||||
* and that a program that depends on them will run.
|
||||
*/
|
||||
|
||||
import org.bitcoindevkit.*
|
||||
|
||||
val network = Network.TESTNET
|
10
bdk-ffi/tests/bindings/test.py
Normal file
10
bdk-ffi/tests/bindings/test.py
Normal file
@ -0,0 +1,10 @@
|
||||
import unittest
|
||||
from bdk import *
|
||||
|
||||
class TestBdk(unittest.TestCase):
|
||||
|
||||
def test_some_enum(self):
|
||||
network = Network.TESTNET
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
9
bdk-ffi/tests/bindings/test.swift
Normal file
9
bdk-ffi/tests/bindings/test.swift
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* This is a basic test swift program that does nothing but confirm that the swift bindings compile
|
||||
* and that a program that depends on them will run.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import BitcoinDevKit
|
||||
|
||||
let network = Network.testnet
|
BIN
bdk-ffi/tests/jna/jna-5.14.0.jar
Normal file
BIN
bdk-ffi/tests/jna/jna-5.14.0.jar
Normal file
Binary file not shown.
5
bdk-ffi/tests/test_generated_bindings.rs
Normal file
5
bdk-ffi/tests/test_generated_bindings.rs
Normal file
@ -0,0 +1,5 @@
|
||||
uniffi::build_foreign_language_testcases!(
|
||||
"tests/bindings/test.kts",
|
||||
"tests/bindings/test.swift",
|
||||
"tests/bindings/test.py",
|
||||
);
|
3
bdk-ffi/uniffi-android.toml
Normal file
3
bdk-ffi/uniffi-android.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[bindings.kotlin]
|
||||
android = true
|
||||
android_cleaner = true
|
3
bdk-ffi/uniffi-bindgen.rs
Normal file
3
bdk-ffi/uniffi-bindgen.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
@ -5,8 +5,6 @@ cdylib_name = "bdkffi"
|
||||
[bindings.python]
|
||||
cdylib_name = "bdkffi"
|
||||
|
||||
[bindings.ruby]
|
||||
cdylib_name = "bdkffi"
|
||||
|
||||
[bindings.swift]
|
||||
module_name = "BitcoinDevKit"
|
||||
cdylib_name = "bdkffi"
|
90
bdk-jvm/README.md
Normal file
90
bdk-jvm/README.md
Normal file
@ -0,0 +1,90 @@
|
||||
# bdk-jvm
|
||||
This project builds a .jar package for the JVM platform that provides Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
|
||||
|
||||
## How to Use
|
||||
To use the Kotlin language bindings for [`bdk`] in your JVM project add the following to your gradle dependencies:
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
### Snapshot releases
|
||||
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
|
||||
}
|
||||
```
|
||||
|
||||
## Example Projects
|
||||
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
|
||||
|
||||
## How to build
|
||||
_Note that Kotlin version `1.9.23` or later is required to build the library._
|
||||
1. Install JDK 17. For example, with SDKMAN!:
|
||||
```shell
|
||||
curl -s "https://get.sdkman.io" | bash
|
||||
source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
sdk install java 17.0.2-tem
|
||||
```
|
||||
2. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.77.1
|
||||
```
|
||||
3. Clone this repository.
|
||||
```shell
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
4. If building on macOS install required intel and m1 jvm targets
|
||||
```sh
|
||||
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||
```
|
||||
5. Build kotlin bindings
|
||||
```sh
|
||||
./gradlew buildJvmLib
|
||||
```
|
||||
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-jvm
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
```
|
||||
|
||||
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
|
||||
```properties
|
||||
signing.gnupg.keyName=<YOUR_GNUPG_ID>
|
||||
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
|
||||
```
|
||||
|
||||
and use the `publishToMavenLocal` task without the `localBuild` flag:
|
||||
```shell
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
|
||||
## Known issues
|
||||
## JNA dependency
|
||||
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
|
||||
```shell
|
||||
class file for com.sun.jna.Pointer not found
|
||||
```
|
||||
|
||||
The solution is to add JNA as a dependency like so:
|
||||
```kotlin
|
||||
dependencies {
|
||||
// ...
|
||||
implementation("net.java.dev.jna:jna:5.12.1")
|
||||
}
|
||||
```
|
||||
|
||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
32
bdk-jvm/build.gradle.kts
Normal file
32
bdk-jvm/build.gradle.kts
Normal file
@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm").version("1.9.23").apply(false)
|
||||
id("org.gradle.java-library")
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
id("org.bitcoindevkit.plugins.generate-jvm-bindings").apply(false)
|
||||
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
|
||||
}
|
||||
|
||||
// library version is defined in gradle.properties
|
||||
val libraryVersion: String by project
|
||||
|
||||
// These properties are required here so that the nexus publish-plugin
|
||||
// finds a staging profile with the correct group (group is otherwise set as "")
|
||||
// and knows whether to publish to a SNAPSHOT repository or not
|
||||
// https://github.com/gradle-nexus/publish-plugin#applying-the-plugin
|
||||
group = "org.bitcoindevkit"
|
||||
version = libraryVersion
|
||||
|
||||
nexusPublishing {
|
||||
repositories {
|
||||
create("sonatype") {
|
||||
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
|
||||
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
||||
|
||||
val ossrhUsername: String? by project
|
||||
val ossrhPassword: String? by project
|
||||
username.set(ossrhUsername)
|
||||
password.set(ossrhPassword)
|
||||
}
|
||||
}
|
||||
}
|
4
bdk-jvm/gradle.properties
Normal file
4
bdk-jvm/gradle.properties
Normal file
@ -0,0 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.12-SNAPSHOT
|
BIN
bdk-jvm/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
bdk-jvm/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
bdk-jvm/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
bdk-jvm/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
249
bdk-jvm/gradlew
vendored
Executable file
249
bdk-jvm/gradlew
vendored
Executable file
@ -0,0 +1,249 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
92
bdk-jvm/gradlew.bat
vendored
Normal file
92
bdk-jvm/gradlew.bat
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
23
bdk-jvm/justfile
Normal file
23
bdk-jvm/justfile
Normal file
@ -0,0 +1,23 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build:
|
||||
./gradlew buildJvmLib
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
rm -rf ./build/
|
||||
rm -rf ./lib/build/
|
||||
rm -rf ./plugins/build/
|
||||
|
||||
publish-local:
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
|
||||
test:
|
||||
./gradlew test
|
||||
|
||||
test-offline:
|
||||
./gradlew test -P excludeConnectedTests
|
||||
|
||||
test-specific TEST:
|
||||
./gradlew test --tests {{TEST}}
|
132
bdk-jvm/lib/build.gradle.kts
Normal file
132
bdk-jvm/lib/build.gradle.kts
Normal file
@ -0,0 +1,132 @@
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.*
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
// library version is defined in gradle.properties
|
||||
val libraryVersion: String by project
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
id("org.gradle.java-library")
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-jvm-bindings")
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
// This block ensures that the tests that require access to a blockchain are not
|
||||
// run if the -P excludeConnectedTests flag is passed to gradle.
|
||||
// This ensures our CI runs are not fickle by not requiring access to testnet or signet.
|
||||
// This is a workaround until we have a proper regtest setup for the CI.
|
||||
// Note that the command in the CI is ./gradlew test -P excludeConnectedTests
|
||||
tasks.test {
|
||||
if (project.hasProperty("excludeConnectedTests")) {
|
||||
exclude("**/LiveElectrumClientTest.class")
|
||||
exclude("**/LiveMemoryWalletTest.class")
|
||||
exclude("**/LiveTransactionTest.class")
|
||||
exclude("**/LiveTxBuilderTest.class")
|
||||
exclude("**/LiveWalletTest.class")
|
||||
}
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
val test by getting(JvmTestSuite::class) {
|
||||
useKotlinTest("1.9.23")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
testLogging {
|
||||
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
|
||||
exceptionFormat = FULL
|
||||
showExceptions = true
|
||||
showStackTraces = true
|
||||
showCauses = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
|
||||
implementation("net.java.dev.jna:jna:5.14.0")
|
||||
api("org.slf4j:slf4j-api:1.7.30")
|
||||
|
||||
// testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
|
||||
// testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
// testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
|
||||
testImplementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
testImplementation("ch.qos.logback:logback-core:1.2.3")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("maven") {
|
||||
groupId = "org.bitcoindevkit"
|
||||
artifactId = "bdk-jvm"
|
||||
version = libraryVersion
|
||||
|
||||
from(components["java"])
|
||||
pom {
|
||||
name.set("bdk-jvm")
|
||||
description.set("Bitcoin Dev Kit Kotlin language bindings.")
|
||||
url.set("https://bitcoindevkit.org")
|
||||
licenses {
|
||||
license {
|
||||
name.set("APACHE 2.0")
|
||||
url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE")
|
||||
}
|
||||
license {
|
||||
name.set("MIT")
|
||||
url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("bdkdevelopers")
|
||||
name.set("Bitcoin Dev Kit Developers")
|
||||
email.set("dev@bitcoindevkit.org")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git")
|
||||
developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git")
|
||||
url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
if (project.hasProperty("localBuild")) {
|
||||
isRequired = false
|
||||
}
|
||||
|
||||
val signingKeyId: String? by project
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
||||
sign(publishing.publications)
|
||||
}
|
||||
|
||||
// This task dependency ensures that we build the bindings
|
||||
// binaries before running the tests
|
||||
tasks.withType<KotlinCompile> {
|
||||
dependsOn("buildJvmLib")
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||
|
||||
class LiveElectrumClientTest {
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val electrumClient: ElectrumClient = ElectrumClient(SIGNET_ELECTRUM_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||
println("Received ${sentAndReceived.received.toSat()}")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveMemoryWalletTest {
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent.toSat()}")
|
||||
println("Received ${sentAndReceived.received.toSat()}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptInspector() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
|
||||
val scriptInspector: FullScriptInspector = FullScriptInspector()
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan().inspectSpksForAllKeychains(scriptInspector)
|
||||
val update = esploraClient.fullScan(fullScanRequest, 21uL, 1uL)
|
||||
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FullScriptInspector: FullScanScriptInspector {
|
||||
override fun inspect(keychain: KeychainKind, index: UInt, script: Script){
|
||||
println("Inspecting index $index for keychain $keychain")
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTransactionTests {
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Wallet balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val transaction: Transaction = wallet.transactions().first().transaction
|
||||
println("First transaction:")
|
||||
println("Txid: ${transaction.computeTxid()}")
|
||||
println("Version: ${transaction.version()}")
|
||||
println("Total size: ${transaction.totalSize()}")
|
||||
println("Vsize: ${transaction.vsize()}")
|
||||
println("Weight: ${transaction.weight()}")
|
||||
println("Coinbase transaction: ${transaction.isCoinbase()}")
|
||||
println("Is explicitly RBF: ${transaction.isExplicitlyRbf()}")
|
||||
println("Inputs: ${transaction.input()}")
|
||||
println("Outputs: ${transaction.output()}")
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveTxBuilderTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
}
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun complexTxBuilder() {
|
||||
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
|
||||
val allRecipients: List<ScriptAmount> = listOf(
|
||||
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
|
||||
)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.setRecipients(allRecipients)
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
|
||||
.enableRbf()
|
||||
.finish(wallet)
|
||||
|
||||
wallet.sign(psbt)
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
|
||||
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
|
||||
|
||||
class LiveWalletTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
}
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.computeTxid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
|
||||
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
|
||||
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
|
||||
val fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.balance().total.toSat()}")
|
||||
|
||||
assert(wallet.balance().total.toSat() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
|
||||
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
|
||||
val walletDidSign = wallet.sign(psbt)
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
println("Txid is: ${tx.computeTxid()}")
|
||||
|
||||
val txFee: Amount = wallet.calculateFee(tx)
|
||||
println("Tx fee is: ${txFee}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class OfflineDescriptorTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.toString()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class OfflinePersistenceTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
val dbFileName = "pre_existing_wallet_persistence_test.sqlite"
|
||||
"$currentDirectory/src/test/resources/$dbFileName"
|
||||
}
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.SIGNET
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testPersistence() {
|
||||
val sqliteStore: SqliteStore = SqliteStore(persistenceFilePath)
|
||||
val initialChangeSet: ChangeSet? = sqliteStore.read()
|
||||
requireNotNull(initialChangeSet) { "ChangeSet should not be null after loading a valid database" }
|
||||
|
||||
val wallet: Wallet = Wallet.newOrLoad(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
initialChangeSet,
|
||||
Network.SIGNET,
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
println("Address: $addressInfo")
|
||||
|
||||
assertEquals(
|
||||
expected = 7u,
|
||||
actual = addressInfo.index,
|
||||
)
|
||||
assertEquals(
|
||||
expected = "tb1qan3lldunh37ma6c0afeywgjyjgnyc8uz975zl2",
|
||||
actual = addressInfo.address.toString(),
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import java.io.File
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
class OfflineWalletTest {
|
||||
private val persistenceFilePath = run {
|
||||
val currentDirectory = System.getProperty("user.dir")
|
||||
"$currentDirectory/bdk_persistence.sqlite"
|
||||
}
|
||||
private val descriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
private val changeDescriptor: Descriptor = Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
@AfterTest
|
||||
fun cleanup() {
|
||||
val file = File(persistenceFilePath)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
|
||||
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
|
||||
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
|
||||
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
actual = addressInfo.address.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor,
|
||||
changeDescriptor,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.balance().total.toSat()
|
||||
)
|
||||
}
|
||||
}
|
Binary file not shown.
16
bdk-jvm/plugins/README.md
Normal file
16
bdk-jvm/plugins/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Readme
|
||||
The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by bdk-jvm, and building the language bindings files.
|
||||
|
||||
The plugin is applied to the `build.gradle.kts` file through the `plugins` block:
|
||||
```kotlin
|
||||
plugins {
|
||||
id("org.bitcoindevkit.plugin.generate-jvm-bindings")
|
||||
}
|
||||
```
|
||||
|
||||
The plugin adds a series of tasks which are brought together into an aggregate task called `buildJvmLib` for `bdk-jvm`.
|
||||
|
||||
This aggregate task:
|
||||
1. Builds the native library(ies) using `bdk-ffi`
|
||||
2. Places it in the correct resource directory
|
||||
3. Builds the bindings file
|
13
bdk-jvm/plugins/build.gradle.kts
Normal file
13
bdk-jvm/plugins/build.gradle.kts
Normal file
@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("java-gradle-plugin")
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("uniFfiJvmBindings") {
|
||||
id = "org.bitcoindevkit.plugins.generate-jvm-bindings"
|
||||
implementationClass = "org.bitcoindevkit.plugins.UniFfiJvmPlugin"
|
||||
}
|
||||
}
|
||||
}
|
8
bdk-jvm/plugins/settings.gradle.kts
Normal file
8
bdk-jvm/plugins/settings.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
// include(":plugins")
|
@ -0,0 +1,16 @@
|
||||
package org.bitcoindevkit.plugins
|
||||
|
||||
|
||||
val operatingSystem: OS = when {
|
||||
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
|
||||
System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
|
||||
System.getProperty("os.name").contains("windows", ignoreCase = true) -> OS.WINDOWS
|
||||
else -> OS.OTHER
|
||||
}
|
||||
|
||||
enum class OS {
|
||||
MAC,
|
||||
LINUX,
|
||||
WINDOWS,
|
||||
OTHER,
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
package org.bitcoindevkit.plugins
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Exec
|
||||
import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
// TODO 18: Migrate hard coded strings to constants all in the same location so they're at least easy
|
||||
// to find and reason about.
|
||||
internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = target.run {
|
||||
|
||||
// register a task called buildJvmBinaries which will run something like
|
||||
// cargo build --release --target aarch64-apple-darwin
|
||||
val buildJvmBinaries by tasks.register<DefaultTask>("buildJvmBinaries") {
|
||||
if (operatingSystem == OS.MAC) {
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-apple-darwin")
|
||||
args(cargoArgs)
|
||||
}
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
|
||||
args(cargoArgs)
|
||||
}
|
||||
} else if (operatingSystem == OS.LINUX) {
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
|
||||
args(cargoArgs)
|
||||
}
|
||||
} else if (operatingSystem == OS.WINDOWS) {
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-pc-windows-msvc")
|
||||
args(cargoArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move the native libs build by cargo from target/.../release/
|
||||
// to their place in the bdk-jvm library
|
||||
val moveNativeJvmLibs by tasks.register<DefaultTask>("moveNativeJvmLibs") {
|
||||
|
||||
// dependsOn(buildJvmBinaryX86_64MacOS, buildJvmBinaryAarch64MacOS, buildJvmBinaryLinux)
|
||||
dependsOn(buildJvmBinaries)
|
||||
|
||||
data class CopyMetadata(val targetDir: String, val resDir: String, val ext: String)
|
||||
val libsToCopy: MutableList<CopyMetadata> = mutableListOf()
|
||||
|
||||
if (operatingSystem == OS.MAC) {
|
||||
libsToCopy.add(
|
||||
CopyMetadata(
|
||||
targetDir = "aarch64-apple-darwin",
|
||||
resDir = "darwin-aarch64",
|
||||
ext = "dylib"
|
||||
)
|
||||
)
|
||||
libsToCopy.add(
|
||||
CopyMetadata(
|
||||
targetDir = "x86_64-apple-darwin",
|
||||
resDir = "darwin-x86-64",
|
||||
ext = "dylib"
|
||||
)
|
||||
)
|
||||
} else if (operatingSystem == OS.LINUX) {
|
||||
libsToCopy.add(
|
||||
CopyMetadata(
|
||||
targetDir = "x86_64-unknown-linux-gnu",
|
||||
resDir = "linux-x86-64",
|
||||
ext = "so"
|
||||
)
|
||||
)
|
||||
} else if (operatingSystem == OS.WINDOWS) {
|
||||
libsToCopy.add(
|
||||
CopyMetadata(
|
||||
targetDir = "x86_64-pc-windows-msvc",
|
||||
resDir = "win32-x86-64",
|
||||
ext = "dll"
|
||||
)
|
||||
)
|
||||
}
|
||||
val libName = when (operatingSystem) {
|
||||
OS.WINDOWS -> "bdkffi"
|
||||
else -> "libbdkffi"
|
||||
}
|
||||
|
||||
libsToCopy.forEach {
|
||||
doFirst {
|
||||
copy {
|
||||
with(it) {
|
||||
from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
|
||||
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate the bindings using the bdk-ffi-bindgen tool created in the bdk-ffi submodule
|
||||
val generateJvmBindings by tasks.register<Exec>("generateJvmBindings") {
|
||||
|
||||
dependsOn(moveNativeJvmLibs)
|
||||
|
||||
// TODO 2: Is the Windows name the correct one?
|
||||
// TODO 3: This will not work on mac Intel (x86_64 architecture)
|
||||
val libraryPath = when (operatingSystem) {
|
||||
OS.LINUX -> "./target/x86_64-unknown-linux-gnu/release-smaller/libbdkffi.so"
|
||||
OS.MAC -> "./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib"
|
||||
OS.WINDOWS -> "./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll"
|
||||
else -> throw Exception("Unsupported OS")
|
||||
}
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin/", "--no-format")
|
||||
|
||||
// The code above was for the migration to uniffi 0.24.3 using the --library flag
|
||||
// The code below works with uniffi 0.23.0
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
|
||||
doLast {
|
||||
println("JVM bindings file successfully created")
|
||||
}
|
||||
}
|
||||
|
||||
// we need an aggregate task which will run the 3 required tasks to build the JVM libs in order
|
||||
// the task will also appear in the printout of the ./gradlew tasks task with a group and description
|
||||
tasks.register("buildJvmLib") {
|
||||
group = "Bitcoindevkit"
|
||||
description = "Aggregate task to build JVM library"
|
||||
|
||||
dependsOn(
|
||||
buildJvmBinaries,
|
||||
moveNativeJvmLibs,
|
||||
generateJvmBindings
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
16
bdk-jvm/settings.gradle.kts
Normal file
16
bdk-jvm/settings.gradle.kts
Normal file
@ -0,0 +1,16 @@
|
||||
rootProject.name = "bdk-jvm"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
16
bdk-python/.gitignore
vendored
Normal file
16
bdk-python/.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
.tox/
|
||||
dist/
|
||||
bdkpython.egg-info/
|
||||
__pycache__/
|
||||
libbdkffi.dylib
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
*.swp
|
||||
|
||||
src/bdkpython/bdk.py
|
||||
src/bdkpython/*.so
|
||||
*.whl
|
||||
build/
|
||||
|
||||
testing-setup-py-simple-example.py
|
3
bdk-python/MANIFEST.in
Normal file
3
bdk-python/MANIFEST.in
Normal file
@ -0,0 +1,3 @@
|
||||
include ./src/bdkpython/libbdkffi.dylib
|
||||
include ./src/bdkpython/libbdkffi.so
|
||||
include ./src/bdkpython/bdkffi.dll
|
36
bdk-python/README.md
Normal file
36
bdk-python/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# bdk-python
|
||||
The Python language bindings for the [bitcoindevkit](https://github.com/bitcoindevkit).
|
||||
|
||||
See the [package on PyPI](https://pypi.org/project/bdkpython/).
|
||||
|
||||
## Install from PyPI
|
||||
Install the latest release using
|
||||
```shell
|
||||
pip install bdkpython
|
||||
```
|
||||
|
||||
## Run the tests
|
||||
```shell
|
||||
pip install --requirement requirements.txt
|
||||
bash ./scripts/generate-linux.sh # here you should run the script appropriate for your platform
|
||||
python setup.py bdist_wheel --verbose
|
||||
pip install ./dist/bdkpython-<yourversion>.whl --force-reinstall
|
||||
python -m unittest --verbose
|
||||
```
|
||||
|
||||
## Build the package
|
||||
```shell
|
||||
# Install dependencies
|
||||
pip install --requirement requirements.txt
|
||||
|
||||
# Generate the bindings (use the script appropriate for your platform)
|
||||
bash ./scripts/generate-linux.sh
|
||||
|
||||
# Build the wheel
|
||||
python setup.py --verbose bdist_wheel
|
||||
```
|
||||
|
||||
## Install locally
|
||||
```shell
|
||||
pip install ./dist/bdkpython-<yourversion>.whl
|
||||
```
|
14
bdk-python/justfile
Normal file
14
bdk-python/justfile
Normal file
@ -0,0 +1,14 @@
|
||||
default:
|
||||
just --list
|
||||
|
||||
build-local-mac:
|
||||
bash ./scripts/generate-macos-arm64.sh && python3 setup.py bdist_wheel --verbose
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
rm -rf ./bdkpython.egg-info/
|
||||
rm -rf ./build/
|
||||
rm -rf ./dist/
|
||||
|
||||
test:
|
||||
python3 -m unittest --verbose
|
387
bdk-python/nix/uniffi_0.14.1_cargo_lock.patch
Normal file
387
bdk-python/nix/uniffi_0.14.1_cargo_lock.patch
Normal file
@ -0,0 +1,387 @@
|
||||
--- /dev/null 2021-12-15 11:22:02.342000000 +0100
|
||||
+++ uniffi_bindgen/Cargo.lock 2021-12-15 16:15:16.132084011 +0100
|
||||
@@ -0,0 +1,384 @@
|
||||
+# This file is automatically @generated by Cargo.
|
||||
+# It is not intended for manual editing.
|
||||
+version = 3
|
||||
+
|
||||
+[[package]]
|
||||
+name = "anyhow"
|
||||
+version = "1.0.51"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "arrayvec"
|
||||
+version = "0.5.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama"
|
||||
+version = "0.10.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134"
|
||||
+dependencies = [
|
||||
+ "askama_derive",
|
||||
+ "askama_escape",
|
||||
+ "askama_shared",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_derive"
|
||||
+version = "0.10.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522"
|
||||
+dependencies = [
|
||||
+ "askama_shared",
|
||||
+ "proc-macro2",
|
||||
+ "syn",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_escape"
|
||||
+version = "0.10.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_shared"
|
||||
+version = "0.11.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc"
|
||||
+dependencies = [
|
||||
+ "askama_escape",
|
||||
+ "nom 6.2.1",
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "serde",
|
||||
+ "syn",
|
||||
+ "toml",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "bitflags"
|
||||
+version = "1.3.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "bitvec"
|
||||
+version = "0.19.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
|
||||
+dependencies = [
|
||||
+ "funty",
|
||||
+ "radium",
|
||||
+ "tap",
|
||||
+ "wyz",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "camino"
|
||||
+version = "1.0.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cargo-platform"
|
||||
+version = "0.1.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cargo_metadata"
|
||||
+version = "0.13.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8"
|
||||
+dependencies = [
|
||||
+ "camino",
|
||||
+ "cargo-platform",
|
||||
+ "semver",
|
||||
+ "semver-parser",
|
||||
+ "serde",
|
||||
+ "serde_json",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cfg-if"
|
||||
+version = "1.0.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "clap"
|
||||
+version = "2.34.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
+dependencies = [
|
||||
+ "bitflags",
|
||||
+ "textwrap",
|
||||
+ "unicode-width",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "funty"
|
||||
+version = "1.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "heck"
|
||||
+version = "0.3.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
+dependencies = [
|
||||
+ "unicode-segmentation",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "itoa"
|
||||
+version = "1.0.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "lexical-core"
|
||||
+version = "0.7.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
+dependencies = [
|
||||
+ "arrayvec",
|
||||
+ "bitflags",
|
||||
+ "cfg-if",
|
||||
+ "ryu",
|
||||
+ "static_assertions",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "memchr"
|
||||
+version = "2.3.4"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "nom"
|
||||
+version = "5.1.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
+dependencies = [
|
||||
+ "memchr",
|
||||
+ "version_check",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "nom"
|
||||
+version = "6.2.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
|
||||
+dependencies = [
|
||||
+ "bitvec",
|
||||
+ "funty",
|
||||
+ "lexical-core",
|
||||
+ "memchr",
|
||||
+ "version_check",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "paste"
|
||||
+version = "1.0.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "pest"
|
||||
+version = "2.1.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
+dependencies = [
|
||||
+ "ucd-trie",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "proc-macro2"
|
||||
+version = "1.0.34"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
|
||||
+dependencies = [
|
||||
+ "unicode-xid",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "quote"
|
||||
+version = "1.0.10"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "radium"
|
||||
+version = "0.5.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "ryu"
|
||||
+version = "1.0.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "semver"
|
||||
+version = "0.11.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
+dependencies = [
|
||||
+ "semver-parser",
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "semver-parser"
|
||||
+version = "0.10.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
+dependencies = [
|
||||
+ "pest",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde"
|
||||
+version = "1.0.131"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
|
||||
+dependencies = [
|
||||
+ "serde_derive",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_derive"
|
||||
+version = "1.0.131"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "syn",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_json"
|
||||
+version = "1.0.73"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
|
||||
+dependencies = [
|
||||
+ "itoa",
|
||||
+ "ryu",
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "static_assertions"
|
||||
+version = "1.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "syn"
|
||||
+version = "1.0.82"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "unicode-xid",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "tap"
|
||||
+version = "1.0.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "textwrap"
|
||||
+version = "0.11.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
+dependencies = [
|
||||
+ "unicode-width",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "toml"
|
||||
+version = "0.5.8"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "ucd-trie"
|
||||
+version = "0.1.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-segmentation"
|
||||
+version = "1.8.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-width"
|
||||
+version = "0.1.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-xid"
|
||||
+version = "0.2.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "uniffi_bindgen"
|
||||
+version = "0.14.1"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "askama",
|
||||
+ "cargo_metadata",
|
||||
+ "clap",
|
||||
+ "heck",
|
||||
+ "paste",
|
||||
+ "serde",
|
||||
+ "toml",
|
||||
+ "weedle",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "version_check"
|
||||
+version = "0.9.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "weedle"
|
||||
+version = "0.12.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "610950904727748ca09682e857f0d6d6437f0ca862f32f9229edba8cec8b2635"
|
||||
+dependencies = [
|
||||
+ "nom 5.1.2",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wyz"
|
||||
+version = "0.2.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
387
bdk-python/nix/uniffi_0.15.2_cargo_lock.patch
Normal file
387
bdk-python/nix/uniffi_0.15.2_cargo_lock.patch
Normal file
@ -0,0 +1,387 @@
|
||||
--- /dev/null 2021-12-15 11:22:02.342000000 +0100
|
||||
+++ uniffi_bindgen/Cargo.lock 2021-12-15 15:54:49.278543090 +0100
|
||||
@@ -0,0 +1,384 @@
|
||||
+# This file is automatically @generated by Cargo.
|
||||
+# It is not intended for manual editing.
|
||||
+version = 3
|
||||
+
|
||||
+[[package]]
|
||||
+name = "anyhow"
|
||||
+version = "1.0.51"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "arrayvec"
|
||||
+version = "0.5.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama"
|
||||
+version = "0.10.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "d298738b6e47e1034e560e5afe63aa488fea34e25ec11b855a76f0d7b8e73134"
|
||||
+dependencies = [
|
||||
+ "askama_derive",
|
||||
+ "askama_escape",
|
||||
+ "askama_shared",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_derive"
|
||||
+version = "0.10.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ca2925c4c290382f9d2fa3d1c1b6a63fa1427099721ecca4749b154cc9c25522"
|
||||
+dependencies = [
|
||||
+ "askama_shared",
|
||||
+ "proc-macro2",
|
||||
+ "syn",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_escape"
|
||||
+version = "0.10.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "90c108c1a94380c89d2215d0ac54ce09796823cca0fd91b299cfff3b33e346fb"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "askama_shared"
|
||||
+version = "0.11.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2582b77e0f3c506ec4838a25fa8a5f97b9bed72bb6d3d272ea1c031d8bd373bc"
|
||||
+dependencies = [
|
||||
+ "askama_escape",
|
||||
+ "nom 6.2.1",
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "serde",
|
||||
+ "syn",
|
||||
+ "toml",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "bitflags"
|
||||
+version = "1.3.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "bitvec"
|
||||
+version = "0.19.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
|
||||
+dependencies = [
|
||||
+ "funty",
|
||||
+ "radium",
|
||||
+ "tap",
|
||||
+ "wyz",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "camino"
|
||||
+version = "1.0.5"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cargo-platform"
|
||||
+version = "0.1.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cargo_metadata"
|
||||
+version = "0.13.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8"
|
||||
+dependencies = [
|
||||
+ "camino",
|
||||
+ "cargo-platform",
|
||||
+ "semver",
|
||||
+ "semver-parser",
|
||||
+ "serde",
|
||||
+ "serde_json",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "cfg-if"
|
||||
+version = "1.0.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "clap"
|
||||
+version = "2.34.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
+dependencies = [
|
||||
+ "bitflags",
|
||||
+ "textwrap",
|
||||
+ "unicode-width",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "funty"
|
||||
+version = "1.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "heck"
|
||||
+version = "0.3.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
+dependencies = [
|
||||
+ "unicode-segmentation",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "itoa"
|
||||
+version = "1.0.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "lexical-core"
|
||||
+version = "0.7.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
+dependencies = [
|
||||
+ "arrayvec",
|
||||
+ "bitflags",
|
||||
+ "cfg-if",
|
||||
+ "ryu",
|
||||
+ "static_assertions",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "memchr"
|
||||
+version = "2.3.4"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "nom"
|
||||
+version = "5.1.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
+dependencies = [
|
||||
+ "memchr",
|
||||
+ "version_check",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "nom"
|
||||
+version = "6.2.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
|
||||
+dependencies = [
|
||||
+ "bitvec",
|
||||
+ "funty",
|
||||
+ "lexical-core",
|
||||
+ "memchr",
|
||||
+ "version_check",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "paste"
|
||||
+version = "1.0.6"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "pest"
|
||||
+version = "2.1.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
+dependencies = [
|
||||
+ "ucd-trie",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "proc-macro2"
|
||||
+version = "1.0.34"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
|
||||
+dependencies = [
|
||||
+ "unicode-xid",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "quote"
|
||||
+version = "1.0.10"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "radium"
|
||||
+version = "0.5.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "ryu"
|
||||
+version = "1.0.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "semver"
|
||||
+version = "0.11.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
+dependencies = [
|
||||
+ "semver-parser",
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "semver-parser"
|
||||
+version = "0.10.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
+dependencies = [
|
||||
+ "pest",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde"
|
||||
+version = "1.0.131"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
|
||||
+dependencies = [
|
||||
+ "serde_derive",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_derive"
|
||||
+version = "1.0.131"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "syn",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "serde_json"
|
||||
+version = "1.0.73"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
|
||||
+dependencies = [
|
||||
+ "itoa",
|
||||
+ "ryu",
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "static_assertions"
|
||||
+version = "1.1.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "syn"
|
||||
+version = "1.0.82"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
+dependencies = [
|
||||
+ "proc-macro2",
|
||||
+ "quote",
|
||||
+ "unicode-xid",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "tap"
|
||||
+version = "1.0.1"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "textwrap"
|
||||
+version = "0.11.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
+dependencies = [
|
||||
+ "unicode-width",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "toml"
|
||||
+version = "0.5.8"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
+dependencies = [
|
||||
+ "serde",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "ucd-trie"
|
||||
+version = "0.1.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-segmentation"
|
||||
+version = "1.8.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-width"
|
||||
+version = "0.1.9"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "unicode-xid"
|
||||
+version = "0.2.2"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "uniffi_bindgen"
|
||||
+version = "0.15.2"
|
||||
+dependencies = [
|
||||
+ "anyhow",
|
||||
+ "askama",
|
||||
+ "cargo_metadata",
|
||||
+ "clap",
|
||||
+ "heck",
|
||||
+ "paste",
|
||||
+ "serde",
|
||||
+ "toml",
|
||||
+ "weedle",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "version_check"
|
||||
+version = "0.9.3"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
+
|
||||
+[[package]]
|
||||
+name = "weedle"
|
||||
+version = "0.12.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "610950904727748ca09682e857f0d6d6437f0ca862f32f9229edba8cec8b2635"
|
||||
+dependencies = [
|
||||
+ "nom 5.1.2",
|
||||
+]
|
||||
+
|
||||
+[[package]]
|
||||
+name = "wyz"
|
||||
+version = "0.2.0"
|
||||
+source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
+checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
20
bdk-python/nix/uniffi_bindgen.nix
Normal file
20
bdk-python/nix/uniffi_bindgen.nix
Normal file
@ -0,0 +1,20 @@
|
||||
with import <nixpkgs> {};
|
||||
|
||||
rustPlatform.buildRustPackage rec {
|
||||
pname = "uniffi_bindgen";
|
||||
version = "0.15.2";
|
||||
src = fetchFromGitHub {
|
||||
owner = "mozilla";
|
||||
repo = "uniffi-rs";
|
||||
rev = "6fa9c06a394b4e9b219fa30fc94e353d17f86e11";
|
||||
# rev = "refs/tags/v0.14.1";
|
||||
sha256 = "1chahy1ac1r88drpslln2p1b04cbg79ylpxzyyp92s1z7ldm5ddb"; # 0.15.2
|
||||
# sha256 = "1mff3f3fqqzqx1yv70ff1yzdnvbd90vg2r477mzzcgisg1wfpwi0"; # 0.14.1
|
||||
fetchSubmodules = true;
|
||||
} + "/uniffi_bindgen/";
|
||||
|
||||
doCheck = false;
|
||||
cargoSha256 = "sha256:08gg285fq8i32nf9kd8s0nn0niacd7sg8krv818nx41i18sm2cf3"; # 0.15.2
|
||||
# cargoSha256 = "sha256:01zp3rwlni988h02dqhkhzhwccs7bhwc1alhbf6gbw3av4b0m9cf"; # 0.14.1
|
||||
cargoPatches = [ ./uniffi_0.15.2_cargo_lock.patch ];
|
||||
}
|
7
bdk-python/pyproject.toml
Normal file
7
bdk-python/pyproject.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel", "setuptools-rust"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = [
|
||||
"."
|
||||
]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user