Compare commits
661 Commits
release/0.
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
790d08c4c9 | ||
|
|
ffd5a96ee0 | ||
|
|
fd02fc2a02 | ||
|
|
5c560c6013 | ||
|
|
9d1b2de29b | ||
|
|
3e3c0bf22f | ||
|
|
9b919e5ceb | ||
|
|
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 | ||
|
|
7d95433c28 | ||
|
|
6d53cbeb25 | ||
|
|
1a12f37a2f | ||
|
|
04022787c4 | ||
|
|
46850ed471 | ||
|
|
49da6fcbae | ||
|
|
075510d6e4 | ||
|
|
fa5c67cd05 | ||
|
|
1a20d0a6c2 | ||
|
|
a3ae4635eb | ||
|
|
5f12900c6d | ||
|
|
1a5a628a5d | ||
|
|
d8c8261e44 | ||
|
|
692904df1e | ||
|
|
2ac42b3ae0 | ||
|
|
d50e2a6832 | ||
|
|
8df13adac7 | ||
|
|
d978993d06 | ||
|
|
5862b13dac | ||
|
|
5db9b1bc7b | ||
|
|
fe59f29cbc | ||
|
|
0dfaf3fd35 | ||
|
|
6be22daf84 | ||
|
|
fad5e3cefd | ||
|
|
28a9b302d5 | ||
|
|
8f5ca7f0fc | ||
|
|
c4b1985076 | ||
|
|
d213266c52 | ||
|
|
29f09e7cd8 | ||
|
|
860130f08c | ||
|
|
d8c3ddca16 | ||
|
|
df67ded9f1 | ||
|
|
5f2297ed7c | ||
|
|
de9fde0d9c | ||
|
|
fe045c13f4 | ||
|
|
80ed21e4c9 | ||
|
|
f2efcb6196 | ||
|
|
cc8a17ef86 | ||
|
|
5ffe9ff331 | ||
|
|
9a3d609826 | ||
|
|
bf8fef807d | ||
|
|
e469dcd32c | ||
|
|
0a3347b85a | ||
|
|
b76bdfcb22 | ||
|
|
9b8cc006ba | ||
|
|
f40ab551b6 | ||
|
|
efc475e33f | ||
|
|
cdea6dc0bf | ||
|
|
6beb98ca4c | ||
|
|
04d538ad45 | ||
|
|
c074a92e0c | ||
|
|
c9c85875a5 | ||
|
|
a9e868cb7e | ||
|
|
5622b07047 | ||
|
|
e6223be905 | ||
|
|
ff260edb3c | ||
|
|
15a0795626 | ||
|
|
cecf973777 | ||
|
|
108fcd46ec | ||
|
|
44b724ea9f | ||
|
|
e5cd7cb3a2 | ||
|
|
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
|
||||
26
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**To Reproduce**
|
||||
<!-- Steps or code to reproduce the behavior. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Build environment**
|
||||
- BDK tag/commit: <!-- e.g. v0.13.0, 3a07614 -->
|
||||
- OS+version: <!-- e.g. ubuntu 20.04.01, macOS 12.0.1, windows -->
|
||||
- Rust/Cargo version: <!-- e.g. 1.56.0 -->
|
||||
- Rust/Cargo target: <!-- e.g. x86_64-apple-darwin, x86_64-unknown-linux-gnu, etc. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the problem here. -->
|
||||
83
.github/ISSUE_TEMPLATE/minor_release.md
vendored
Normal file
83
.github/ISSUE_TEMPLATE/minor_release.md
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: Minor Release
|
||||
about: Create a new minor release [for release managers only]
|
||||
title: 'Release MAJOR.MINOR+1.0'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Create a new minor release
|
||||
### _Main Workflow_
|
||||
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.
|
||||
3. - [ ] Update the Android, JVM, Python, and Swift libraries as per the ["**_Sub-Workflows_**" section below](#Sub-Workflows). Open a single PR on master for all of these changes called `Prepare language bindings libraries for 0.X release`
|
||||
18. - [ ] Create a new branch off of `master` called `release/version`
|
||||
19. - [ ] Checkout that branch and open a PR to update the Android, JVM, and Python libraries' versions
|
||||
- [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
||||
- [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
||||
- [ ] Update bdk-python version from `.dev` version to release version
|
||||
20. - [ ] Merge the PR updating all of the languages to their release versions
|
||||
21. - [ ] Create the tag 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) and push it to GitHub.
|
||||
```sh
|
||||
git tag v0.6.0 --sign --edit
|
||||
git push upstream v0.6.0
|
||||
```
|
||||
22. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
|
||||
23. - [ ] Trigger manual releases for all 4 libraries (for Swift, simply add the version number in the text field when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`)
|
||||
24. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
|
||||
25. - [ ] Build and publish API docs for JVM, Android, and Java on the website
|
||||
```bash!
|
||||
./gradlew dokkaHtml # bdk-jvm (Dokka)
|
||||
./gradlew dokkaJavadoc # bdk-jvm (java-style documentation)
|
||||
./gradlew dokkaHtml # bdk-android (Dokka)
|
||||
```
|
||||
26. - [ ] Tweet about the library
|
||||
27. - [ ] Post in the announcement channel
|
||||
|
||||
### _Sub Workflows_
|
||||
#### _Android_
|
||||
4. - [ ] Update the API docs to reflect the changes in the API
|
||||
5. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch
|
||||
6. - [ ] Build the library and run the tests, and adjust if necessary.
|
||||
```sh
|
||||
# start an emulator prior to running the tests
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
./gradlew connectedAndroidTest
|
||||
```
|
||||
7. - [ ] Update the readme if necessary
|
||||
|
||||
#### _JVM_
|
||||
8. - [ ] Update the API docs to reflect the changes in the API
|
||||
9. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch
|
||||
10. - [ ] Build the library and run the tests, and adjust if necessary
|
||||
```sh
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
./gradlew test
|
||||
```
|
||||
11. - [ ] Update the readme if necessary
|
||||
|
||||
#### _Swift_
|
||||
12. - [ ] Run the tests and adjust if necessary
|
||||
```sh
|
||||
./bdk-swift/build-local-swift.sh
|
||||
cd bdk-swift
|
||||
swift test
|
||||
```
|
||||
13. - [ ] Update the readme if necessary
|
||||
|
||||
#### _Python_
|
||||
14. - [ ] Delete the `.tox`, `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
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./generate.sh
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
16. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||
python -m unittest --verbose tests/test_bdk.py
|
||||
```
|
||||
17. - [ ] Update the readme and `setup.py` if necessary
|
||||
84
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
84
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: Patch Release
|
||||
about: Create a new patch release [for release managers only]
|
||||
title: 'Release MAJOR.MINOR.PATCH+1'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Creating a new patch release
|
||||
|
||||
## Bumping 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.
|
||||
|
||||
### Specific Libraries' Workflows
|
||||
#### _Android_
|
||||
3. - [ ] Update the API docs to reflect the changes in the API
|
||||
4. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch.
|
||||
5. - [ ] Build the library and run the tests, and adjust if necessary.
|
||||
```sh
|
||||
# start an emulator prior to running the tests
|
||||
cd ./bdk-android/
|
||||
./gradlew buildAndroidLib
|
||||
./gradlew connectedAndroidTest
|
||||
```
|
||||
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 previous artifacts to make sure you're building the library from scratch
|
||||
9. - [ ] Build the library and run the tests, and adjust if necessary
|
||||
```sh
|
||||
cd ./bdk-jvm/
|
||||
./gradlew buildJvmLib
|
||||
./gradlew test
|
||||
```
|
||||
10. - [ ] Update the readme if necessary
|
||||
#### _Swift_
|
||||
11. - [ ] Run the tests and adjust if necessary
|
||||
```sh
|
||||
./bdk-swift/build-local-swift.sh
|
||||
cd ./bdk-swift/
|
||||
swift test
|
||||
```
|
||||
12. - [ ] Update the readme if necessary
|
||||
#### _Python_
|
||||
13. - [ ] Delete the `.tox`, `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches
|
||||
14. - [ ] Build the library
|
||||
```shell
|
||||
cd ./bdk-python/
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./generate.sh
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
15. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
|
||||
python -m unittest --verbose tests/test_bdk.py
|
||||
```
|
||||
16. - [ ] Update the readme and `setup.py` if necessary
|
||||
|
||||
### Release Workflow
|
||||
17. - [ ] 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`
|
||||
- [ ] Create a new branch off of `master` called `release/version`
|
||||
18. - [ ] Checkout that branch and open a PR to update the Android, JVM, and Python libraries' versions
|
||||
- [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
||||
- [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
||||
- [ ] Update bdk-python version from `.dev` version to release version
|
||||
19. - [ ] Merge the PR updating all of the languages to their release versions
|
||||
20. - [ ] Create the tag 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) and push it to GitHub.
|
||||
```sh
|
||||
git tag v0.6.0 --sign --edit
|
||||
git push upstream v0.6.0
|
||||
```
|
||||
21. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
|
||||
22. - [ ] Trigger manual releases for all 4 libraries (for Swift, simply add the version number in the text field when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`)
|
||||
23. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
|
||||
24. - [ ] Build and publish API docs for JVM, Android, and Java on the website
|
||||
```bash!
|
||||
./gradlew dokkaHtml # bdk-jvm (Dokka)
|
||||
./gradlew dokkaJavadoc # bdk-jvm (java-style documentation)
|
||||
./gradlew dokkaHtml # bdk-android (Dokka)
|
||||
```
|
||||
25. - [ ] Tweet about the library
|
||||
26. - [ ] Post in the announcement channel
|
||||
77
.github/ISSUE_TEMPLATE/summer_project.md
vendored
Normal file
77
.github/ISSUE_TEMPLATE/summer_project.md
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: Summer of Bitcoin Project
|
||||
about: Template to suggest a new https://www.summerofbitcoin.org/ project.
|
||||
title: ''
|
||||
labels: 'summer-of-bitcoin'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
## Overview
|
||||
|
||||
Project ideas are scoped for a university-level student with a basic background in CS and bitcoin
|
||||
fundamentals - achievable over 12-weeks. Below are just a few types of ideas:
|
||||
|
||||
- Low-hanging fruit: Relatively short projects with clear goals; requires basic technical knowledge
|
||||
and minimal familiarity with the codebase.
|
||||
- Core development: These projects derive from the ongoing work from the core of your development
|
||||
team. The list of features and bugs is never-ending, and help is always welcome.
|
||||
- Risky/Exploratory: These projects push the scope boundaries of your development effort. They
|
||||
might require expertise in an area not covered by your current development team. They might take
|
||||
advantage of a new technology. There is a reasonable chance that the project might be less
|
||||
successful, but the potential rewards make it worth the attempt.
|
||||
- Infrastructure/Automation: These projects are the code that your organization uses to get its
|
||||
development work done; for example, projects that improve the automation of releases, regression
|
||||
tests and automated builds. This is a category where a Summer of Bitcoin student can be really
|
||||
helpful, doing work that the development team has been putting off while they focus on core
|
||||
development.
|
||||
- Quality Assurance/Testing: Projects that work on and test your project's software development
|
||||
process. Additionally, projects that involve a thorough test and review of individual PRs.
|
||||
- Fun/Peripheral: These projects might not be related to the current core development focus, but
|
||||
create new innovations and new perspectives for your project.
|
||||
-->
|
||||
|
||||
**Description**
|
||||
<!-- Description: 3-7 sentences describing the project background and tasks to be done. -->
|
||||
|
||||
**Expected Outcomes**
|
||||
<!-- Short bullet list describing what is to be accomplished -->
|
||||
|
||||
**Resources**
|
||||
<!-- 2-3 reading materials for candidate to learn about the repo, project, scope etc -->
|
||||
<!-- Recommended reading such as a developer/contributor guide -->
|
||||
<!-- [Another example a paper citation](https://arxiv.org/pdf/1802.08091.pdf) -->
|
||||
<!-- [Another example an existing issue](https://github.com/opencv/opencv/issues/11013) -->
|
||||
<!-- [An existing related module](https://github.com/opencv/opencv_contrib/tree/master/modules/optflow) -->
|
||||
|
||||
**Skills Required**
|
||||
<!-- 3-4 technical skills that the candidate should know -->
|
||||
<!-- hands on experience with git -->
|
||||
<!-- mastery plus experience coding in C++ -->
|
||||
<!-- basic knowledge in matrix and tensor computations, college course work in cryptography -->
|
||||
<!-- strong mathematical background -->
|
||||
<!-- Bonus - has experience with React Native. Best if you have also worked with OSSFuzz -->
|
||||
|
||||
**Mentor(s)**
|
||||
<!-- names of mentor(s) for this project go here -->
|
||||
|
||||
**Difficulty**
|
||||
<!-- Easy, Medium, Hard -->
|
||||
|
||||
**Competency Test (optional)**
|
||||
<!-- 2-3 technical tasks related to the project idea or repository you’d like a candidate to
|
||||
perform in order to demonstrate competency, good first bugs, warm-up exercises -->
|
||||
<!-- ex. Read the instructions here to get Bitcoin core running on your machine -->
|
||||
<!-- ex. pick an issue labeled as “newcomer” in the repository, and send a merge request to the
|
||||
repository. You can also suggest some other improvement that we did not think of yet, or
|
||||
something that you find interesting or useful -->
|
||||
<!-- ex. fixes for coding style are usually easy to do, and are good issues for first time
|
||||
contributions for those learning how to interact with the project. After you are done with the
|
||||
coding style issue, try making a different contribution. -->
|
||||
<!-- ex. setup a full Debian packaging development environment and learn the basics of Debian
|
||||
packaging. Then identify and package the missing dependencies to package Specter Desktop -->
|
||||
<!-- ex. write a pull parser for CSV files. You'll be judged by the decisions to store the parser
|
||||
state and how flexible it is to wrap this parser in other scenarios. -->
|
||||
<!-- ex. Stretch Goal: Implement some basic metaprogram/app to prove you're very familiar with BDK.
|
||||
Be prepared to make adjustments as we judge your solution. -->
|
||||
34
.github/pull_request_template.md
vendored
Normal file
34
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- Erase any parts of this template not applicable to your Pull Request. -->
|
||||
|
||||
### Description
|
||||
|
||||
<!-- Describe the purpose of this PR, what's being adding and/or fixed -->
|
||||
|
||||
### Notes to the reviewers
|
||||
|
||||
<!-- 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:
|
||||
|
||||
* [ ] I've signed all my commits
|
||||
* [ ] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
|
||||
* [ ] I ran `cargo fmt` and `cargo clippy` before committing
|
||||
|
||||
#### New Features:
|
||||
|
||||
* [ ] I've added tests for the new feature
|
||||
* [ ] I've added docs for the new feature
|
||||
|
||||
#### Bugfixes:
|
||||
|
||||
* [ ] This pull request breaks the existing API
|
||||
* [ ] I've added tests to reproduce the issue which are now passing
|
||||
* [ ] I'm linking the issue being fixed by this PR
|
||||
19
.github/workflows/audit.yml
vendored
Normal file
19
.github/workflows/audit.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Once per week
|
||||
|
||||
jobs:
|
||||
security_audit:
|
||||
name: Security audit
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
79
.github/workflows/cont_integration.yml
vendored
Normal file
79
.github/workflows/cont_integration.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Rust layer CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.63.0 # STABLE
|
||||
clippy: true
|
||||
- version: 1.61.0 # MSRV
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Generate cache key
|
||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set default toolchain
|
||||
run: rustup default ${{ matrix.rust.version }}
|
||||
|
||||
- name: Set profile
|
||||
run: rustup set profile minimal
|
||||
|
||||
- name: Add clippy
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: rustup component add clippy
|
||||
|
||||
- name: Update toolchain
|
||||
run: rustup update
|
||||
|
||||
- name: Build
|
||||
run: cargo build
|
||||
|
||||
- name: Clippy
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
||||
|
||||
- name: Test
|
||||
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
|
||||
|
||||
fmt:
|
||||
name: Rust fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set default toolchain
|
||||
run: rustup default nightly
|
||||
|
||||
- name: Set profile
|
||||
run: rustup set profile minimal
|
||||
|
||||
- name: Add rustfmt
|
||||
run: rustup component add rustfmt
|
||||
|
||||
- name: Update toolchain
|
||||
run: rustup update
|
||||
|
||||
- name: Check fmt
|
||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
||||
60
.github/workflows/publish-android.yaml
vendored
Normal file
60
.github/workflows/publish-android.yaml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Publish bdk-android to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
||||
# We replace the default environment variable ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.2.9519653
|
||||
# with an older version of the NDK (21.4.7075529) using the fix proposed here: https://github.com/actions/runner-images/issues/5930
|
||||
# For information on why this is needed at the moment see issues #242 and #243, and PR #282
|
||||
env:
|
||||
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Install Android NDK 21.4.7075529"
|
||||
run: |
|
||||
ANDROID_ROOT=/usr/local/lib/android
|
||||
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
|
||||
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- 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 Local and 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
|
||||
99
.github/workflows/publish-jvm.yaml
vendored
Normal file
99
.github/workflows/publish-jvm.yaml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: Publish bdk-jvm to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build-jvm-macOS-M1-native-lib:
|
||||
name: "Create M1 and x86_64 JVM native binaries"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- 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@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: Install aarch64 Rust target
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Build bdk-jvm library
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
# build aarch64 + x86_64 native libraries and upload
|
||||
- name: Upload macOS native libraries for reuse in publishing job
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
# name: no name is required because we upload the entire directory
|
||||
# the default name "artifact" will be used
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
|
||||
|
||||
build-jvm-full-library:
|
||||
name: Create full bdk-jvm library
|
||||
needs: [build-jvm-macOS-M1-native-lib]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout publishing branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Update bdk-ffi git submodule
|
||||
run: |
|
||||
git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git
|
||||
git submodule update --init bdk-ffi
|
||||
|
||||
- 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@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: Build bdk-jvm library
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: Download macOS native libraries from previous job
|
||||
uses: actions/download-artifact@v3
|
||||
id: download
|
||||
with:
|
||||
# download the artifact created in the prior job (named "artifact")
|
||||
name: artifact
|
||||
path: ./bdk-jvm/lib/src/main/resources/
|
||||
|
||||
- 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
|
||||
159
.github/workflows/publish-python.yaml
vendored
Normal file
159
.github/workflows/publish-python.yaml
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
name: Build and publish Python wheels
|
||||
on: [workflow_dispatch]
|
||||
|
||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
||||
# tries to load glibc and fails because it requires a more recent version.
|
||||
|
||||
jobs:
|
||||
build-manylinux2014-x86_64-wheel:
|
||||
name: "Build Manylinux 2014 x86_64 wheel"
|
||||
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: # Update this list whenever the docker image is updated (check /opt/python/)
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: "Install requirements"
|
||||
run: ${PYBIN}/pip install -r requirements.txt
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: bash generate.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
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-universal-wheel:
|
||||
name: "Build macOS universal wheel"
|
||||
runs-on: macos-12
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: |
|
||||
python3 --version
|
||||
rustup target add aarch64-apple-darwin
|
||||
pip3 install --user -r requirements.txt
|
||||
bash generate.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
env:
|
||||
ARCHFLAGS: "-arch x86_64 -arch arm64"
|
||||
# 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_12_0_universal2 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-macos-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-windows-wheel:
|
||||
name: "Build Windows wheel"
|
||||
runs-on: windows-2022
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: |
|
||||
python --version
|
||||
pip install --user -r requirements.txt
|
||||
bash generate.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
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-manylinux2014-x86_64-wheel, build-macos-universal-wheel, build-windows-wheel]
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Download artifacts in dist/ directory"
|
||||
uses: actions/download-artifact@v2
|
||||
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/*/
|
||||
65
.github/workflows/test-android.yaml
vendored
Normal file
65
.github/workflows/test-android.yaml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
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
|
||||
# We replace the default environment variable ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.2.9519653
|
||||
# with an older version of the NDK (21.4.7075529) using the fix proposed here: https://github.com/actions/runner-images/issues/5930
|
||||
# For information on why this is needed at the moment see issues #242 and #243, and PR #282
|
||||
env:
|
||||
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Install Android NDK 21.4.7075529"
|
||||
run: |
|
||||
ANDROID_ROOT=/usr/local/lib/android
|
||||
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
|
||||
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- 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
|
||||
|
||||
# There are currently no unit tests for bdk-android 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.
|
||||
# - name: "Run Android unit tests"
|
||||
# run: |
|
||||
# cd bdk-android
|
||||
# ./gradlew test --console=rich
|
||||
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@v2
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: Run JVM tests
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew test --console=rich
|
||||
148
.github/workflows/test-python.yaml
vendored
Normal file
148
.github/workflows/test-python.yaml
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
name: Test Python
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
|
||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
||||
# tries to load glibc and fails because it requires a more recent version.
|
||||
|
||||
jobs:
|
||||
build-manylinux2014-x86_64-wheel:
|
||||
name: "Build and test Manylinux 2014 x86_64 wheels"
|
||||
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:
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: "Install requirements"
|
||||
run: ${PYBIN}/pip install -r requirements.txt
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: bash generate.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python -m unittest tests/test_bdk.py --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-universal-wheel:
|
||||
name: "Build and test macOS wheels"
|
||||
runs-on: macos-12
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: |
|
||||
python3 --version
|
||||
rustup target add aarch64-apple-darwin
|
||||
pip3 install --user -r requirements.txt
|
||||
bash generate.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
env:
|
||||
ARCHFLAGS: "-arch x86_64 -arch arm64"
|
||||
run: python3 setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: pip3 install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
run: python3 -m unittest tests/test_bdk.py --verbose
|
||||
|
||||
build-windows-wheel:
|
||||
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"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py"
|
||||
run: |
|
||||
python --version
|
||||
pip install --user -r requirements.txt
|
||||
bash generate.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
# TODO: On Windows the pip install ./dist/*.whl step fails with the following error:
|
||||
# Run pip install ./dist/*.whl
|
||||
# WARNING: Requirement './dist/*.whl' looks like a filename, but the file does not exist
|
||||
# ERROR: *.whl is not a valid wheel filename.*.whl is not a valid wheel name
|
||||
# So we skip the installing and the tests and simply test that the wheel builds
|
||||
# - name: Install wheel
|
||||
# run: pip install ./dist/*.whl
|
||||
# - name: Run tests
|
||||
# run: python -m unittest tests/test_bdk.py --verbose
|
||||
55
.github/workflows/test-swift.yaml
vendored
Normal file
55
.github/workflows/test-swift.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Test Swift
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-swift/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-swift/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: "Set default Rust version to 1.67.0"
|
||||
run: rustup default 1.67.0
|
||||
|
||||
- name: Install Rust targets
|
||||
run: |
|
||||
rustup install nightly-x86_64-apple-darwin
|
||||
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
|
||||
- name: Run bdk-ffi-bindgen
|
||||
working-directory: bdk-ffi
|
||||
run: cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
|
||||
- name: Build bdk-ffi for x86_64-apple-darwin
|
||||
run: cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
- name: Build bdk-ffi for aarch64-apple-darwin
|
||||
run: cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
- name: Create lipo-ios-sim and lipo-macos
|
||||
run: |
|
||||
mkdir -p target/lipo-macos/release-smaller
|
||||
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
||||
|
||||
- name: Create bdkFFI.xcframework
|
||||
working-directory: bdk-swift
|
||||
run: |
|
||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevkit/bdkFFI.modulemap
|
||||
|
||||
- name: Run Swift tests
|
||||
working-directory: bdk-swift
|
||||
run: swift test
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,7 +1,5 @@
|
||||
target
|
||||
build
|
||||
Cargo.lock
|
||||
/bindings/bdk-kotlin/local.properties
|
||||
.gradle
|
||||
wallet_db
|
||||
bdk_ffi_test
|
||||
@@ -14,3 +12,25 @@ testdb
|
||||
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
|
||||
|
||||
# Python related
|
||||
__pycache__
|
||||
165
CHANGELOG.md
165
CHANGELOG.md
@@ -1,19 +1,164 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
All notable changes to this project prior to release **0.9.0** are documented in this file. Future
|
||||
changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`.
|
||||
Changelog info is also documented 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).
|
||||
|
||||
## [Unreleased]
|
||||
## [v0.27.1]
|
||||
- Update BDK to latest 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 latest 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 latest 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 latest 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 latest 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)`, contructs 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
|
||||
- fixes sqlite-db issue causing wrong balance
|
||||
- adds experimental taproot descriptor and PSBT support
|
||||
- APIs Removed
|
||||
- `Wallet.get_new_address()`, returned String, [#137]
|
||||
- `Wallet.get_last_unused_address()`, returned String [#137]
|
||||
- APIs Added
|
||||
- `Wallet.get_address(AddressIndex)`, returns `AddressInfo` [#137]
|
||||
- APIs Changed
|
||||
- `Wallet.sign(PartiallySignedBitcoinTransaction)` now returns a bool, true if finalized [#161]
|
||||
|
||||
[#137]: https://github.com/bitcoindevkit/bdk-ffi/pull/137
|
||||
[#161]: https://github.com/bitcoindevkit/bdk-ffi/pull/161
|
||||
|
||||
## [v0.6.0]
|
||||
|
||||
- Update BDK to version 0.18.0
|
||||
- Add BumpFeeTxBuilder to bump the fee on an unconfirmed tx created by the Wallet
|
||||
- Change TxBuilder.build() to TxBuilder.finish() to align with bdk function name
|
||||
|
||||
## [v0.5.0]
|
||||
|
||||
- Fix Wallet.broadcast function, now returns a tx id as a hex string
|
||||
- Remove creating a new spending Transaction via the PartiallySignedBitcoinTransaction constructor
|
||||
- Add TxBuilder for creating new spending PartiallySignedBitcoinTransaction
|
||||
@@ -22,17 +167,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Update generate cli tool to generate all binding languages and rename to bdk-ffi-bindgen
|
||||
|
||||
## [v0.4.0]
|
||||
|
||||
- Add dual license MIT and Apache 2.0
|
||||
- Add sqlite database support
|
||||
- Fix memory database configuration enum, remove junk field
|
||||
|
||||
## [v0.3.1]
|
||||
|
||||
- Remove hard coded sync progress value (was always returning 21.0)
|
||||
|
||||
## [v0.3.0]
|
||||
|
||||
- Move bdk-kotlin bindings and ios example to separate repos
|
||||
- Add bin to generate Python bindings
|
||||
- Add `PartiallySignedBitcoinTransaction::deserialize` function as named constructor to decode from a string per [BIP 0174]
|
||||
@@ -43,7 +185,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [v0.2.0]
|
||||
|
||||
[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...HEAD
|
||||
[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
|
||||
[v0.4.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.3.1...v0.4.0
|
||||
|
||||
1534
Cargo.lock
generated
Normal file
1534
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
Cargo.toml
31
Cargo.toml
@@ -1,23 +1,12 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "0.6.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"]
|
||||
members = ["bdk-ffi"]
|
||||
default-members = ["bdk-ffi"]
|
||||
exclude = ["api-docs", "bdk-android", "bdk-jvm", "bdk-python", "bdk-swift"]
|
||||
|
||||
# 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.18", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
||||
|
||||
uniffi_macros = { version = "0.16.0", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "0.16.0", features = ["builtin-bindgen"] }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.16.0", features = ["builtin-bindgen"] }
|
||||
[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 = true # Strip symbols from binary*
|
||||
|
||||
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-----
|
||||
172
README.md
172
README.md
@@ -1,68 +1,148 @@
|
||||
# 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.
|
||||
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 | Repository |
|
||||
| -------- | ------------ | ------------ |
|
||||
| Kotlin | jvm | [bdk-kotlin] |
|
||||
| Kotlin | android | [bdk-kotlin] |
|
||||
| Swift | iOS, macOS | [bdk-swift] |
|
||||
| Python | linux, macOS | [bdk-python] |
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
[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
|
||||
| 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] | |
|
||||
|
||||
## 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
|
||||
2. Update mapping `bdk.udl` file with new `dictionary`
|
||||
|
||||
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`
|
||||
#### For pass by reference values
|
||||
1. Create wrapper rust struct/impl with only fields that are `Sync + Send`
|
||||
2. Update mapping `bdk.udl` file with new `interface`
|
||||
|
||||
## 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/
|
||||
|
||||
6
api-docs/README.md
Normal file
6
api-docs/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# API documentation
|
||||
The Bitcoin Dev Kit language bindings make use of the [uniffi-rs](https://github.com/mozilla/uniffi-rs) library to produce their bindings. While efforts are ongoing to allow inline documentation on the Rust side to be ported to the bindings code, this is not currently possible.
|
||||
|
||||
This directory contains our temporary solution to this problem. A set of files mimicking the bindings libraries in their function signatures, but without any implementation. This allows for documentation build tools to produce API docs similarly to how we would like them to be if they could be inlined.
|
||||
|
||||
You can find the resulting API documentation websites in the ["API Reference" section of the sidebar](https://bitcoindevkit.org/getting-started/) under the "Docs" tab on the bitcoindevkit official website.
|
||||
4
api-docs/kotlin/Module1.md
Normal file
4
api-docs/kotlin/Module1.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Module bdk-android
|
||||
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android.
|
||||
|
||||
# Package org.bitcoindevkit
|
||||
4
api-docs/kotlin/Module2.md
Normal file
4
api-docs/kotlin/Module2.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Module bdk-jvm
|
||||
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM.
|
||||
|
||||
# Package org.bitcoindevkit
|
||||
46
api-docs/kotlin/build.gradle.kts
Normal file
46
api-docs/kotlin/build.gradle.kts
Normal file
@@ -0,0 +1,46 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.7.10"
|
||||
|
||||
// API docs
|
||||
id("org.jetbrains.dokka") version "1.7.10"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
// tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
|
||||
// dokkaSourceSets {
|
||||
// named("main") {
|
||||
// moduleName.set("bdk-android")
|
||||
// moduleVersion.set("0.11.0")
|
||||
// includes.from("Module1.md")
|
||||
// samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
|
||||
dokkaSourceSets {
|
||||
named("main") {
|
||||
moduleName.set("bdk-jvm")
|
||||
moduleVersion.set("0.11.0")
|
||||
includes.from("Module2.md")
|
||||
samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
|
||||
}
|
||||
}
|
||||
}
|
||||
8
api-docs/kotlin/deploy.sh
Normal file
8
api-docs/kotlin/deploy.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
./gradlew dokkaHtml
|
||||
cd build/dokka/html
|
||||
git init .
|
||||
git add .
|
||||
git switch --create gh-pages
|
||||
git commit -m "Deploy"
|
||||
git remote add origin git@github.com:bitcoindevkit/bdk-kotlin.git
|
||||
git push --set-upstream origin gh-pages --force
|
||||
1
api-docs/kotlin/gradle.properties
Normal file
1
api-docs/kotlin/gradle.properties
Normal file
@@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
||||
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
api-docs/kotlin/gradlew
vendored
Executable file
234
api-docs/kotlin/gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/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/master/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
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
|
||||
# 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
|
||||
which java >/dev/null 2>&1 || 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
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
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
|
||||
|
||||
# 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 \
|
||||
"$@"
|
||||
|
||||
# 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" "$@"
|
||||
89
api-docs/kotlin/gradlew.bat
vendored
Normal file
89
api-docs/kotlin/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@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=.
|
||||
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%" == "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%"=="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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
api-docs/kotlin/settings.gradle.kts
Normal file
1
api-docs/kotlin/settings.gradle.kts
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = "BDK Android and BDK JVM API Docs"
|
||||
915
api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
Normal file
915
api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
Normal file
@@ -0,0 +1,915 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
/**
|
||||
* The cryptocurrency to act on.
|
||||
*
|
||||
* @sample org.bitcoindevkit.networkSample
|
||||
*/
|
||||
enum class Network {
|
||||
/** Bitcoin's mainnet. */
|
||||
BITCOIN,
|
||||
|
||||
/** Bitcoin’s testnet. */
|
||||
TESTNET,
|
||||
|
||||
/** Bitcoin’s signet. */
|
||||
SIGNET,
|
||||
|
||||
/** Bitcoin’s regtest. */
|
||||
REGTEST,
|
||||
}
|
||||
|
||||
/**
|
||||
* A derived address and the index it was found at.
|
||||
*
|
||||
* @property index Child index of this address.
|
||||
* @property address Address.
|
||||
* @property keychain Type of keychain.
|
||||
*
|
||||
* @sample org.bitcoindevkit.addressInfoSample
|
||||
*/
|
||||
data class AddressInfo (
|
||||
var index: UInt,
|
||||
var address: Address,
|
||||
var keychain: KeychainKind
|
||||
)
|
||||
|
||||
/**
|
||||
* The address index selection strategy to use to derive an address from the wallet’s external descriptor.
|
||||
*
|
||||
* If you’re unsure which one to use, use `AddressIndex.New`.
|
||||
*
|
||||
* @sample org.bitcoindevkit.addressIndexSample
|
||||
*/
|
||||
sealed class AddressIndex {
|
||||
/** Return a new address after incrementing the current descriptor index. */
|
||||
object New: AddressIndex(),
|
||||
|
||||
/**
|
||||
* Return the address for the current descriptor index if it has not been used in a received transaction.
|
||||
* Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet
|
||||
* has not yet detected an address has been used it could return an already used address.
|
||||
* This function is primarily meant for situations where the caller is untrusted;
|
||||
* for example when deriving donation addresses on-demand for a public web page.
|
||||
*/
|
||||
object LastUnused: AddressIndex(),
|
||||
|
||||
/**
|
||||
* Return the address for a specific descriptor index. Does not change the current descriptor
|
||||
* index used by [AddressIndex.New] and [AddressIndex.LastUsed].
|
||||
* Use with caution, if an index is given that is less than the current descriptor index
|
||||
* then the returned address may have already been used.
|
||||
*/
|
||||
data class Peek(val index: UInt): AddressIndex(),
|
||||
|
||||
/**
|
||||
* Return the address for a specific descriptor index and reset the current descriptor index
|
||||
* used by [AddressIndex.New] and [AddressIndex.LastUsed] to this value.
|
||||
* Use with caution, if an index is given that is less than the current descriptor index
|
||||
* then the returned address and subsequent addresses returned by calls to [AddressIndex.New]
|
||||
* and [AddressIndex.LastUsed] may have already been used. Also if the index is reset to a
|
||||
* value earlier than the [Blockchain] stopGap (default is 20) then a
|
||||
* larger stopGap should be used to monitor for all possibly used addresses.
|
||||
*/
|
||||
data class Reset(val index: UInt): AddressIndex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance differentiated in various categories.
|
||||
*
|
||||
* @property immature All coinbase outputs not yet matured.
|
||||
* @property trustedPending Unconfirmed UTXOs generated by a wallet tx.
|
||||
* @property untrustedPending Unconfirmed UTXOs received from an external wallet.
|
||||
* @property confirmed Confirmed and immediately spendable balance.
|
||||
* @property spendable The sum of trustedPending and confirmed coins.
|
||||
* @property total The whole balance visible to the wallet.
|
||||
*
|
||||
* @sample org.bitcoindevkit.balanceSample
|
||||
*/
|
||||
data class Balance (
|
||||
var immature: ULong,
|
||||
var trustedPending: ULong,
|
||||
var untrustedPending: ULong,
|
||||
var confirmed: ULong,
|
||||
var spendable: ULong,
|
||||
var total: ULong
|
||||
)
|
||||
|
||||
/**
|
||||
* Type that can contain any of the database configurations defined by the library.
|
||||
*
|
||||
* @sample org.bitcoindevkit.memoryDatabaseConfigSample
|
||||
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
|
||||
*/
|
||||
sealed class DatabaseConfig {
|
||||
/** Configuration for an in-memory database. */
|
||||
object Memory : DatabaseConfig()
|
||||
|
||||
/** Configuration for a Sled database. */
|
||||
data class Sled(val config: SledDbConfiguration) : DatabaseConfig()
|
||||
|
||||
/** Configuration for a SQLite database. */
|
||||
data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration type for a SQLite database.
|
||||
*
|
||||
* @property path Main directory of the DB.
|
||||
*
|
||||
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
|
||||
*/
|
||||
data class SqliteDbConfiguration(
|
||||
var path: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration type for a SledDB database.
|
||||
*
|
||||
* @property path Main directory of the DB.
|
||||
* @property treeName Name of the database tree, a separated namespace for the data.
|
||||
*/
|
||||
data class SledDbConfiguration(
|
||||
var path: String,
|
||||
var treeName: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration for an Electrum blockchain.
|
||||
*
|
||||
* @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`.
|
||||
* @property socks5 URL of the socks5 proxy server or a Tor service.
|
||||
* @property retry Request retry count.
|
||||
* @property timeout Request timeout (seconds).
|
||||
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
* @property validateDomain Validate the domain when using SSL.
|
||||
*
|
||||
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
|
||||
*/
|
||||
data class ElectrumConfig(
|
||||
var url: String,
|
||||
var socks5: String?,
|
||||
var retry: UByte,
|
||||
var timeout: UByte?,
|
||||
var stopGap: ULong,
|
||||
var validateDomain: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration for an Esplora blockchain.
|
||||
*
|
||||
* @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`.
|
||||
* @property proxy Optional URL of the proxy to use to make requests to the Esplora server.
|
||||
* @property concurrency Number of parallel requests sent to the esplora service (default: 4).
|
||||
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
* @property timeout Socket timeout.
|
||||
*
|
||||
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
|
||||
*/
|
||||
data class EsploraConfig(
|
||||
var baseUrl: String,
|
||||
var proxy: String?,
|
||||
var concurrency: UByte?,
|
||||
var stopGap: ULong,
|
||||
var timeout: ULong?
|
||||
)
|
||||
|
||||
/**
|
||||
* Authentication mechanism for RPC connection to full node.
|
||||
*/
|
||||
sealed class Auth {
|
||||
/** No authentication */
|
||||
object None: Auth()
|
||||
|
||||
/** Authentication with username and password, usually [Auth.Cookie] should be preferred */
|
||||
data class UserPass(val username: String, val password: String): Auth()
|
||||
|
||||
/** Authentication with a cookie file */
|
||||
data class Cookie(val file: String): Auth()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync parameters for Bitcoin Core RPC.
|
||||
*
|
||||
* In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
|
||||
* `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
|
||||
* how the `importdescriptors` RPC calls are to be made.
|
||||
*
|
||||
* @property startScriptCount The minimum number of scripts to scan for on initial sync.
|
||||
* @property startTime Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
|
||||
* @property forceStartTime Forces every sync to use `start_time` as import timestamp.
|
||||
* @property pollRateSec RPC poll rate (in seconds) to get state updates.
|
||||
*/
|
||||
data class RcpSyncParams(
|
||||
val startScriptCount: ULong,
|
||||
val startTime: Ulong,
|
||||
val forceStartTime: Boolean,
|
||||
val pollRateSec: ULong,
|
||||
)
|
||||
|
||||
/**
|
||||
* RpcBlockchain configuration options
|
||||
*
|
||||
* @property url The bitcoin node url.
|
||||
* @property auth The bicoin node authentication mechanism.
|
||||
* @property network The network we are using (it will be checked the bitcoin node network matches this).
|
||||
* @property walletName The wallet name in the bitcoin node.
|
||||
* @property syncParams Sync parameters.
|
||||
*/
|
||||
data class RpcConfig(
|
||||
val url: String,
|
||||
val auth: Auth,
|
||||
val network: Network,
|
||||
val walletName: String,
|
||||
val syncParams: RcpSyncParams?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Type that can contain any of the blockchain configurations defined by the library.
|
||||
*
|
||||
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
|
||||
*/
|
||||
sealed class BlockchainConfig {
|
||||
/** Electrum client. */
|
||||
data class Electrum(val config: ElectrumConfig) : BlockchainConfig()
|
||||
|
||||
/** Esplora client. */
|
||||
data class Esplora(val config: EsploraConfig) : BlockchainConfig()
|
||||
|
||||
/** Bitcoin Core RPC client. */
|
||||
data class Rpc(val config: RpcConfig) : BlockchainConfig()
|
||||
}
|
||||
|
||||
/**
|
||||
* A wallet transaction.
|
||||
*
|
||||
* @property fee Fee value (sats) if available. The availability of the fee depends on the backend. It’s never None with an Electrum server backend, but it could be None with a Bitcoin RPC node without txindex that receive funds while offline.
|
||||
* @property received Received value (sats) Sum of owned outputs of this transaction.
|
||||
* @property sent Sent value (sats) Sum of owned inputs of this transaction.
|
||||
* @property txid Transaction id.
|
||||
* @property confirmationTime If the transaction is confirmed, [BlockTime] contains height and timestamp of the block containing the transaction. This property is null for unconfirmed transactions.
|
||||
*/
|
||||
data class TransactionDetails (
|
||||
var transaction?: Transaction,
|
||||
var fee: ULong?,
|
||||
var received: ULong,
|
||||
var sent: ULong,
|
||||
var txid: String,
|
||||
var confirmationTime: BlockTime?
|
||||
)
|
||||
|
||||
/**
|
||||
* A blockchain backend.
|
||||
*
|
||||
* @constructor Create the new blockchain client.
|
||||
*
|
||||
* @param config The blockchain configuration required.
|
||||
*
|
||||
* @sample org.bitcoindevkit.blockchainSample
|
||||
*/
|
||||
class Blockchain(
|
||||
config: BlockchainConfig
|
||||
) {
|
||||
/** Broadcast a transaction. */
|
||||
fun broadcast(transaction: Transaction) {}
|
||||
|
||||
/** Estimate the fee rate required to confirm a transaction in a given target of blocks. */
|
||||
fun estimateFee(target: ULong): FeeRate {}
|
||||
|
||||
/** Get the current height of the blockchain. */
|
||||
fun getHeight(): UInt {}
|
||||
|
||||
/** Get the block hash of a given block. */
|
||||
fun getBlockHash(height: UInt): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A bitcoin transaction.
|
||||
*
|
||||
* @constructor Build a new Bitcoin Transaction.
|
||||
*
|
||||
* @param transactionBytes The transaction bytes, bitcoin consensus encoded.
|
||||
*/
|
||||
class Transaction(transactionBytes: List<UByte>) {
|
||||
/** Computes the txid. */
|
||||
fun txid(): String {}
|
||||
|
||||
/**
|
||||
* Returns the "weight" of this transaction, as defined by BIP141.
|
||||
*
|
||||
* For transactions with an empty witness, this is simply the consensus-serialized size times four.
|
||||
* For transactions with a witness, this is the non-witness consensus-serialized size multiplied by three
|
||||
* plus the with-witness consensus-serialized size.
|
||||
*/
|
||||
fun weight(): ULong {}
|
||||
|
||||
/** Returns the regular byte-wise consensus-serialized size of this transaction. */
|
||||
fun size(): ULong {}
|
||||
|
||||
/**
|
||||
* Returns the "virtual size" (vsize) of this transaction.
|
||||
*
|
||||
* Will be ceil(weight / 4.0). Note this implements the virtual size as per BIP141, which is different to
|
||||
* what is implemented in Bitcoin Core. The computation should be the same for any remotely sane transaction.
|
||||
*/
|
||||
fun vsize(): ULong {}
|
||||
|
||||
/** Return the transaction bytes, bitcoin consensus encoded. */
|
||||
fun serialize(): List<UByte> {}
|
||||
|
||||
/** Is this a coin base transaction? */
|
||||
fun isCoinBase(): Boolean {}
|
||||
|
||||
/**
|
||||
* Returns true if the transaction itself opted in to be BIP-125-replaceable (RBF).
|
||||
* This does not cover the case where a transaction becomes replaceable due to ancestors being RBF.
|
||||
*/
|
||||
fun isExplicitlyRbf(): Boolean {}
|
||||
|
||||
/** Returns true if this transactions nLockTime is enabled (BIP-65). */
|
||||
fun isLockTimeEnabled(): Boolean {}
|
||||
|
||||
/** The protocol version, is currently expected to be 1 or 2 (BIP 68). */
|
||||
fun version(): Int {}
|
||||
|
||||
/**
|
||||
* Block height or timestamp. Transaction cannot be included in a block until this height/time.
|
||||
* Relevant BIPs
|
||||
* BIP-65 OP_CHECKLOCKTIMEVERIFY
|
||||
* BIP-113 Median time-past as endpoint for lock-time calculations
|
||||
*/
|
||||
fun lockTime(): UInt {}
|
||||
|
||||
/** List of transaction inputs. */
|
||||
fun input(): List<TxIn> {}
|
||||
|
||||
/** List of transaction outputs. */
|
||||
fun output(): List<TxOut> {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A partially signed bitcoin transaction.
|
||||
*
|
||||
* @constructor Build a new Partially Signed Bitcoin Transaction.
|
||||
*
|
||||
* @param psbtBase64 The PSBT in base64 format.
|
||||
*/
|
||||
class PartiallySignedTransaction(psbtBase64: String) {
|
||||
/** Return the PSBT in string format, using a base64 encoding. */
|
||||
fun serialize(): String {}
|
||||
|
||||
/** Get the txid of the PSBT. */
|
||||
fun txid(): String {}
|
||||
|
||||
/** Extract the transaction. */
|
||||
fun extractTx(): Transaction {}
|
||||
|
||||
/**
|
||||
* Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.
|
||||
* In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||
*/
|
||||
fun combine(other: PartiallySignedTransaction): PartiallySignedTransaction
|
||||
|
||||
/** Serialize the PSBT data structure as a String of JSON. */
|
||||
fun jsonSerialize(): String
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to a transaction output.
|
||||
*
|
||||
* @property txid The referenced transaction’s txid.
|
||||
* @property vout The index of the referenced output in its transaction’s vout.
|
||||
*/
|
||||
data class OutPoint (
|
||||
var txid: String,
|
||||
var vout: UInt
|
||||
)
|
||||
|
||||
/**
|
||||
* A transaction output, which defines new coins to be created from old ones.
|
||||
*
|
||||
* @property value The value of the output, in satoshis.
|
||||
* @property scriptPubkey The script which must be satisfied for the output to be spent.
|
||||
*/
|
||||
data class TxOut (
|
||||
var value: ULong,
|
||||
var scriptPubkey: Script
|
||||
)
|
||||
|
||||
/**
|
||||
* Bitcoin transaction input.
|
||||
*
|
||||
* It contains the location of the previous transaction’s output, that it spends and set of scripts that satisfy its spending conditions.
|
||||
*
|
||||
* @property previousOutput The reference to the previous output that is being used an an input.
|
||||
* @property scriptSig The script which pushes values on the stack which will cause the referenced output’s script to be accepted.
|
||||
* @property sequence The sequence number, which suggests to miners which of two conflicting transactions should be preferred, or 0xFFFFFFFF to ignore this feature. This is generally never used since the miner behaviour cannot be enforced.
|
||||
* @property witness Witness data: an array of byte-arrays. Note that this field is not (de)serialized with the rest of the TxIn in Encodable/Decodable, as it is (de)serialized at the end of the full Transaction. It is (de)serialized with the rest of the TxIn in other (de)serialization routines.
|
||||
*
|
||||
*/
|
||||
data class TxIn (
|
||||
var previousOutput: OutPoint,
|
||||
var scriptSig: Script,
|
||||
var sequence: UInt,
|
||||
var witness: List<List<UByte>>
|
||||
)
|
||||
|
||||
/**
|
||||
* An unspent output owned by a [Wallet].
|
||||
*
|
||||
* @property outpoint Reference to a transaction output.
|
||||
* @property txout Transaction output.
|
||||
* @property keychain Type of keychain.
|
||||
* @property isSpent Whether this UTXO is spent or not.
|
||||
*/
|
||||
data class LocalUtxo (
|
||||
var outpoint: OutPoint,
|
||||
var txout: TxOut,
|
||||
var keychain: KeychainKind,
|
||||
var isSpent: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Types of keychains.
|
||||
*/
|
||||
enum class KeychainKind {
|
||||
/** External. */
|
||||
EXTERNAL,
|
||||
|
||||
/** Internal, usually used for change outputs. */
|
||||
INTERNAL,
|
||||
}
|
||||
|
||||
/**
|
||||
* Block height and timestamp of a block.
|
||||
*
|
||||
* @property height Confirmation block height.
|
||||
* @property timestamp Confirmation block timestamp.
|
||||
*/
|
||||
data class BlockTime (
|
||||
var height: UInt,
|
||||
var timestamp: ULong,
|
||||
)
|
||||
|
||||
/**
|
||||
* A Bitcoin wallet.
|
||||
* The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
|
||||
* 1. Output descriptors from which it can derive addresses.
|
||||
* 2. A Database where it tracks transactions and utxos related to the descriptors.
|
||||
* 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
|
||||
*
|
||||
* @constructor Create a BDK wallet.
|
||||
*
|
||||
* @param descriptor The main (or "external") descriptor.
|
||||
* @param changeDescriptor? The change (or "internal") descriptor.
|
||||
* @param network The network to act on.
|
||||
* @param databaseConfig The database configuration.
|
||||
*
|
||||
* @sample org.bitcoindevkit.walletSample
|
||||
*/
|
||||
class Wallet(
|
||||
descriptor: Descriptor,
|
||||
changeDescriptor: Descriptor?,
|
||||
network: Network,
|
||||
databaseConfig: DatabaseConfig,
|
||||
) {
|
||||
/**
|
||||
* Return a derived address using the external descriptor, see [AddressIndex] for available address index
|
||||
* selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end
|
||||
* with a * character) then the same address will always be returned for any [AddressIndex].
|
||||
*/
|
||||
fun getAddress(addressIndex: AddressIndex): AddressInfo {}
|
||||
|
||||
/**
|
||||
* Return a derived address using the internal (change) descriptor.
|
||||
* If the wallet doesn't have an internal descriptor it will use the external descriptor.
|
||||
* See [AddressIndex] for available address index selection strategies. If none of the keys
|
||||
* in the descriptor are derivable (i.e. does not end with /\*) then the same address will always
|
||||
* be returned for any [AddressIndex].
|
||||
*/
|
||||
fun getInternalAddress(addressIndex: AddressIndex): AddressInfo {}
|
||||
|
||||
/** Return the wallet's balance, across different categories. See [Balance] for the categories. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun getBalance(): Balance {}
|
||||
|
||||
/**
|
||||
* Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
* `SignerOrdering`.
|
||||
*
|
||||
* The `SignOptions` can be used to tweak the behavior of the software signers, and the way
|
||||
* the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
* signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
|
||||
* in this library will.
|
||||
*
|
||||
* @param psbt PSBT to be signed
|
||||
* @param signOptions signing options
|
||||
* @return true if the PSBT was finalized, or false otherwise
|
||||
*/
|
||||
fun sign(psbt: PartiallySignedTransaction, signOptions: SignOptions?): Boolean {}
|
||||
|
||||
/** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun listTransactions(includeRaw: Boolean): List<TransactionDetails> {}
|
||||
|
||||
/** Get the Bitcoin network the wallet is using. */
|
||||
fun network(): Network {}
|
||||
|
||||
/** Sync the internal database with the blockchain. */
|
||||
fun sync(blockchain: Blockchain, progress: Progress?) {}
|
||||
|
||||
/** Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun listUnspent(): List<LocalUtxo> {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that logs at level INFO every update received (if any).
|
||||
*/
|
||||
class Progress {
|
||||
/** Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an optional text message that can be displayed to the user. */
|
||||
fun update(progress: Float, message: String?) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A transaction builder.
|
||||
*
|
||||
* After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction.
|
||||
*
|
||||
* Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
||||
*
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample1
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample2
|
||||
*/
|
||||
class TxBuilder() {
|
||||
/** Add data as an output using OP_RETURN. */
|
||||
fun addData(data: List<UByte>): TxBuilder {}
|
||||
|
||||
/** Add a recipient to the internal list. */
|
||||
fun addRecipient(script: Script, amount: ULong): TxBuilder {}
|
||||
|
||||
/** Set the list of recipients by providing a list of [ScriptAmount]. */
|
||||
fun setRecipients(recipients: List<ScriptAmount>): TxBuilder {}
|
||||
|
||||
/** Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent" utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details. */
|
||||
fun addUnspendable(unspendable: OutPoint): TxBuilder {}
|
||||
|
||||
/** Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent. */
|
||||
fun addUtxo(outpoint: OutPoint): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Add the list of outpoints to the internal list of UTXOs that must be spent. If an error
|
||||
* occurs while adding any of the UTXOs then none of them are added and the error is returned.
|
||||
* These have priority over the "unspendable" utxos, meaning that if a utxo is present both
|
||||
* in the "utxos" and the "unspendable" list, it will be spent.
|
||||
*/
|
||||
fun addUtxos(outpoints: List<OutPoint>): TxBuilder {}
|
||||
|
||||
/** Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
|
||||
fun doNotSpendChange(): TxBuilder {}
|
||||
|
||||
/** Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid. */
|
||||
fun manuallySelectedOnly(): TxBuilder {}
|
||||
|
||||
/** Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
|
||||
fun onlySpendChange(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos
|
||||
* added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details.
|
||||
*/
|
||||
fun unspendable(unspendable: List<OutPoint>): TxBuilder {}
|
||||
|
||||
/** Set a custom fee rate. */
|
||||
fun feeRate(satPerVbyte: Float): TxBuilder {}
|
||||
|
||||
/** Set an absolute fee. */
|
||||
fun feeAbsolute(feeAmount: ULong): TxBuilder {}
|
||||
|
||||
/** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */
|
||||
fun drainWallet(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Sets the address to drain excess coins to. Usually, when there are excess coins they are
|
||||
* sent to a change address generated by the wallet. This option replaces the usual change address
|
||||
* with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the
|
||||
* drain output is not needed (the excess coins are too small) it will not be included in the resulting
|
||||
* transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients
|
||||
* with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any
|
||||
* recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set
|
||||
* [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option,
|
||||
* you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees.
|
||||
*/
|
||||
fun drainTo(script: Script): TxBuilder {}
|
||||
|
||||
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
|
||||
fun enableRbf(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
|
||||
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
|
||||
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
*/
|
||||
fun enableRbfWithSequence(nsequence: UInt): TxBuilder {}
|
||||
|
||||
/** Finish building the transaction. Returns a [TxBuilderResult]. */
|
||||
fun finish(wallet: Wallet): TxBuilderResult {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a software signer.
|
||||
*
|
||||
* Adjust the behavior of our software signers and the way a transaction is finalized.
|
||||
*
|
||||
* @property trustWitnessUtxo Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been provided. Defaults to `false`.
|
||||
* @property assumeHeight Whether the wallet should assume a specific height has been reached when trying to finalize a transaction.
|
||||
* @property allowAllSighashes Whether the signer should use the sighash_type set in the PSBT when signing, no matter what its value is. Defaults to `false`.
|
||||
* @property removePartialSigs Whether to remove partial signatures from the PSBT inputs while finalizing PSBT. Defaults to `true`.
|
||||
* @property tryFinalize Whether to try finalizing the PSBT after the inputs are signed. Defaults to `true`.
|
||||
* @property signWithTapInternalKey Whether we should try to sign a taproot transaction with the taproot internal key or not. This option is ignored if we're signing a non-taproot PSBT. Defaults to `true`.
|
||||
* @property allowGrinding Whether we should grind ECDSA signature to ensure signing with low r or not. Defaults to `true`.
|
||||
*/
|
||||
data class SignOptions (
|
||||
var trustWitnessUtxo: Boolean,
|
||||
var assumeHeight: UInt?,
|
||||
var allowAllSighashes: Boolean,
|
||||
var removePartialSigs: Boolean,
|
||||
var tryFinalize: Boolean,
|
||||
var signWithTapInternalKey: Boolean,
|
||||
var allowGrinding: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* A object holding a ScriptPubKey and an amount.
|
||||
*
|
||||
* @property script The ScriptPubKey.
|
||||
* @property amount The amount.
|
||||
*/
|
||||
data class ScriptAmount (
|
||||
var script: Script,
|
||||
var amount: ULong
|
||||
)
|
||||
|
||||
/**
|
||||
* The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
|
||||
*/
|
||||
class BumpFeeTxBuilder() {
|
||||
/**
|
||||
* Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this scriptPubKey
|
||||
* in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output
|
||||
* to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is
|
||||
* preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error
|
||||
* if scriptPubkey can’t be found among the recipients of the transaction we are bumping.
|
||||
*/
|
||||
fun allowShrinking(address: String): BumpFeeTxBuilder {}
|
||||
|
||||
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
|
||||
fun enableRbf(): BumpFeeTxBuilder {}
|
||||
|
||||
/**
|
||||
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
|
||||
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
|
||||
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
*/
|
||||
fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {}
|
||||
|
||||
/** Finish building the transaction. Returns a [TxBuilderResult]. */
|
||||
fun finish(wallet: Wallet): TxBuilderResult {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A BIP-32 derivation path.
|
||||
*
|
||||
* @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey]
|
||||
* or [DescriptorPublicKey].
|
||||
*/
|
||||
class DerivationPath(path: String) {}
|
||||
|
||||
/**
|
||||
* An extended secret key.
|
||||
*
|
||||
* @param network The network this DescriptorSecretKey is to be used on.
|
||||
* @param mnemonic The mnemonic.
|
||||
* @param password The optional passphrase that can be provided as per BIP-39.
|
||||
*
|
||||
* @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample
|
||||
* @sample org.bitcoindevkit.descriptorSecretKeyExtendSample
|
||||
*/
|
||||
class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String?) {
|
||||
/** Build a DescriptorSecretKey from a String */
|
||||
fun fromString(secretKey: String): DescriptorSecretKey {}
|
||||
|
||||
/** Derive a private descriptor at a given path. */
|
||||
fun derive(path: DerivationPath): DescriptorSecretKey {}
|
||||
|
||||
/** Extend the private descriptor with a custom path. */
|
||||
fun extend(path: DerivationPath): DescriptorSecretKey {}
|
||||
|
||||
/** Return the public version of the descriptor. */
|
||||
fun asPublic(): DescriptorPublicKey {}
|
||||
|
||||
/** Return the raw private key as bytes. */
|
||||
fun secretBytes(): List<UByte>
|
||||
|
||||
/** Return the private descriptor as a string. */
|
||||
fun asString(): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extended public key.
|
||||
*
|
||||
* @param network The network this DescriptorPublicKey is to be used on.
|
||||
* @param mnemonic The mnemonic.
|
||||
* @param password The optional passphrase that can be provided as per BIP-39.
|
||||
*/
|
||||
class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) {
|
||||
/** Build a DescriptorPublicKey from a String */
|
||||
fun fromString(publicKey: String): DescriptorPublicKey {}
|
||||
|
||||
/** Derive a public descriptor at a given path. */
|
||||
fun derive(path: DerivationPath): DescriptorPublicKey
|
||||
|
||||
/** Extend the public descriptor with a custom path. */
|
||||
fun extend(path: DerivationPath): DescriptorPublicKey
|
||||
|
||||
/** Return the public descriptor as a string. */
|
||||
fun asString(): String
|
||||
}
|
||||
|
||||
/**
|
||||
* A output descriptor.
|
||||
*
|
||||
* @param descriptor The descriptor in string format.
|
||||
* @param network The network this descriptor is to be used on.
|
||||
*
|
||||
* @sample org.bitcoindevkit.descriptorTemplates1
|
||||
* @sample org.bitcoindevkit.descriptorTemplates2
|
||||
*/
|
||||
class Descriptor(descriptor: String, network: Network) {
|
||||
/**
|
||||
* BIP44 template. Expands to pkh(key/44'/{0,1}'/0'/{0,1}/\*)
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip44(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP44 public template. Expands to pkh(key/{0,1}/\*)
|
||||
* This assumes that the key used has already been derived with m/44'/0'/0' for Mainnet or m/44'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip44Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP49 template. Expands to sh(wpkh(key/49'/{0,1}'/0'/{0,1}/\*))
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip49(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP49 public template. Expands to sh(wpkh(key/{0,1}/\*))
|
||||
* This assumes that the key used has already been derived with m/49'/0'/0' for Mainnet or m/49'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip49Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP84 template. Expands to wpkh(key/84'/{0,1}'/0'/{0,1}/\*)
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip84(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP84 public template. Expands to wpkh(key/{0,1}/\*)
|
||||
* This assumes that the key used has already been derived with m/84'/0'/0' for Mainnet or m/84'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip84Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/** Return the public version of the output descriptor. */
|
||||
fun asString(): String {}
|
||||
|
||||
/** Return the private version of the output descriptor if available, otherwise return the public version. */
|
||||
fun asStringPrivate(): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum describing entropy length (aka word count) in the mnemonic.
|
||||
*/
|
||||
enum class WordCount {
|
||||
/** 12 words mnemonic (128 bits entropy). */
|
||||
WORDS12,
|
||||
|
||||
/** 15 words mnemonic (160 bits entropy). */
|
||||
WORDS15,
|
||||
|
||||
/** 18 words mnemonic (192 bits entropy). */
|
||||
WORDS18,
|
||||
|
||||
/** 21 words mnemonic (224 bits entropy). */
|
||||
WORDS21,
|
||||
|
||||
/** 24 words mnemonic (256 bits entropy). */
|
||||
WORDS24,
|
||||
}
|
||||
|
||||
/**
|
||||
* The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder].
|
||||
*
|
||||
* @property psbt The PSBT
|
||||
* @property transactionDetails The transaction details.
|
||||
*
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample1
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample2
|
||||
*/
|
||||
data class TxBuilderResult (
|
||||
var psbt: PartiallySignedTransaction,
|
||||
var transactionDetails: TransactionDetails
|
||||
)
|
||||
|
||||
/**
|
||||
* A bitcoin script.
|
||||
*/
|
||||
class Script(rawOutputScript: List<UByte>)
|
||||
|
||||
/**
|
||||
* A bitcoin address.
|
||||
*
|
||||
* @param address The address in string format.
|
||||
*/
|
||||
class Address(address: String) {
|
||||
/** Construct an [`Address`] from an output script. */
|
||||
fun fromScript(script: Script, network: Network): Address {}
|
||||
|
||||
/** Return the Payload */
|
||||
fun payload(): Payload
|
||||
|
||||
/** Return the Network. */
|
||||
fun network(): Network
|
||||
|
||||
/** Return the ScriptPubKey. */
|
||||
fun scriptPubkey(): Script
|
||||
|
||||
/**
|
||||
* Creates a URI string bitcoin:address optimized to be encoded in QR codes.
|
||||
*
|
||||
* If the address is bech32, both the schema and the address become uppercase. If the address is base58, the schema is lowercase and the address is left mixed case.
|
||||
*
|
||||
* Quoting BIP 173 "inside QR codes uppercase SHOULD be used, as those permit the use of alphanumeric mode, which is 45% more compact than the normal byte mode."
|
||||
*/
|
||||
fun toQrUri(): String
|
||||
|
||||
/** Return the address as a string. */
|
||||
fun asString(): String
|
||||
}}
|
||||
|
||||
/**
|
||||
* The method used to produce an address.
|
||||
*/
|
||||
sealed class Payload {
|
||||
/** P2PKH address. */
|
||||
data class PubkeyHash(
|
||||
val pubkeyHash: List<UByte>
|
||||
) : Payload()
|
||||
|
||||
/** P2SH address. */
|
||||
data class ScriptHash(
|
||||
val scriptHash: List<UByte>
|
||||
) : Payload()
|
||||
|
||||
/** Segwit address. */
|
||||
data class WitnessProgram(
|
||||
val version: WitnessVersion,
|
||||
val program: List<UByte>
|
||||
) : Payload()
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of the witness program.
|
||||
*
|
||||
* Helps limit possible versions of the witness according to the specification. If a plain u8 type
|
||||
* was used instead it would mean that the version may be > 16, which would be incorrect.
|
||||
* First byte of scriptPubkey in transaction output for transactions starting with opcodes ranging
|
||||
* from 0 to 16 (inclusive).
|
||||
*/
|
||||
enum class WitnessVersion {
|
||||
V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16
|
||||
}
|
||||
|
||||
/**
|
||||
* Mnemonic phrases are a human-readable version of the private keys. Supported number of words are 12, 15, 18, 21 and 24.
|
||||
*
|
||||
* @constructor Generates Mnemonic with a random entropy.
|
||||
* @param mnemonic The mnemonic as a string of space-separated words.
|
||||
*
|
||||
* @sample org.bitcoindevkit.mnemonicSample
|
||||
*/
|
||||
class Mnemonic(mnemonic: String) {
|
||||
/* Returns Mnemonic as string */
|
||||
fun asString(): String
|
||||
|
||||
/* Parse a Mnemonic from a given string. */
|
||||
fun fromString(): Mnemonic
|
||||
|
||||
/*
|
||||
* Create a new Mnemonic in the specified language from the given entropy. Entropy must be a
|
||||
* multiple of 32 bits (4 bytes) and 128-256 bits in length.
|
||||
*/
|
||||
fun fromEntropy(): Mnemonic
|
||||
}
|
||||
281
api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
Normal file
281
api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
Normal file
@@ -0,0 +1,281 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
fun networkSample() {
|
||||
val wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
}
|
||||
|
||||
fun balanceSample() {
|
||||
object LogProgress : Progress {
|
||||
override fun update(progress: Float, message: String?) {}
|
||||
}
|
||||
|
||||
val memoryDatabaseConfig = DatabaseConfig.Memory
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
200u
|
||||
)
|
||||
)
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, memoryDatabaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress)
|
||||
|
||||
val balance: Balance = wallet.getBalance()
|
||||
println("Total wallet balance is ${balance.total}")
|
||||
}
|
||||
|
||||
fun electrumBlockchainConfigSample() {
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
url = "ssl://electrum.blockstream.info:60002",
|
||||
socks5 = null,
|
||||
retry = 5u,
|
||||
timeout = null,
|
||||
stopGap = 200u
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun esploraBlockchainConfigSample() {
|
||||
val esploraURL: String = "http://10.0.2.2:3002"
|
||||
val esploraConfig: EsploraConfig = EsploraConfig(
|
||||
baseUrl = esploraURL,
|
||||
proxy = null,
|
||||
concurrency = 4u,
|
||||
stopGap = 20UL,
|
||||
timeout = null
|
||||
)
|
||||
val blockchainConfig = BlockchainConfig.Esplora(config = esploraConfig)
|
||||
}
|
||||
|
||||
fun memoryDatabaseConfigSample() {
|
||||
val memoryDatabaseConfig = DatabaseConfig.Memory
|
||||
}
|
||||
|
||||
fun sqliteDatabaseConfigSample() {
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
|
||||
}
|
||||
|
||||
fun addressIndexSample() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
|
||||
fun peekAddress100(): AddressInfo {
|
||||
return wallet.getAddress(AddressIndex.Peek(100u))
|
||||
}
|
||||
}
|
||||
|
||||
fun addressInfoSample() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
|
||||
val newAddress: AddressInfo = wallet.getAddress(AddressIndex.New)
|
||||
|
||||
println("New address at index ${newAddress.index} is ${newAddress.address.asString()}")
|
||||
}
|
||||
|
||||
fun blockchainSample() {
|
||||
val blockchainConfig: BlockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
electrumURL,
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
10u
|
||||
)
|
||||
)
|
||||
|
||||
val blockchain: Blockchain = Blockchain(blockchainConfig)
|
||||
|
||||
val feeRate: FeeRate = blockchain.estimateFee(3u)
|
||||
|
||||
val (psbt, txDetails) = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(feeRate.asSatPerVb())
|
||||
.finish(wallet)
|
||||
|
||||
wallet.sign(psbt)
|
||||
|
||||
blockchain.broadcast(psbt)
|
||||
}
|
||||
|
||||
|
||||
fun txBuilderResultSample1() {
|
||||
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
// TxBuilderResult is a data class, which means you can use destructuring declarations on it to
|
||||
// open it up into its component parts
|
||||
val (psbt, txDetails) = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
println("Txid is ${txDetails.txid}")
|
||||
wallet.sign(psbt)
|
||||
}
|
||||
|
||||
fun txBuilderResultSample2() {
|
||||
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
val txBuilderResult: TxBuilderResult = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
val psbt = txBuilderResult.psbt
|
||||
val txDetails = txBuilderResult.transactionDetails
|
||||
|
||||
println("Txid is ${txDetails.txid}")
|
||||
wallet.sign(psbt)
|
||||
}
|
||||
|
||||
fun descriptorSecretKeyExtendSample() {
|
||||
// The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path.
|
||||
|
||||
// val mnemonic: String = generateMnemonic(WordCount.WORDS12)
|
||||
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
|
||||
// the initial DescriptorSecretKey will always be at the "master" node,
|
||||
// i.e. the derivation path is empty
|
||||
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
|
||||
network = Network.TESTNET,
|
||||
mnemonic = mnemonic,
|
||||
password = ""
|
||||
)
|
||||
println(bip32RootKey.asString())
|
||||
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/*
|
||||
|
||||
// the derive method will also automatically apply the wildcard (*) to your path,
|
||||
// i.e the following will generate the typical testnet BIP84 external wallet path
|
||||
// m/84h/1h/0h/0/*
|
||||
val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0")
|
||||
val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString()
|
||||
println(externalExtendedKey)
|
||||
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/*
|
||||
|
||||
// to create the descriptor you'll need to use this extended key in a descriptor function,
|
||||
// i.e. wpkh(), tr(), etc.
|
||||
val externalDescriptor = "wpkh($externalExtendedKey)"
|
||||
}
|
||||
|
||||
fun descriptorSecretKeyDeriveSample() {
|
||||
// The DescriptorSecretKey.derive() method allows you to derive an extended key for a given
|
||||
// node in the derivation tree (for example to create an xpub for a particular account)
|
||||
|
||||
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
|
||||
network = Network.TESTNET,
|
||||
mnemonic = mnemonic,
|
||||
password = ""
|
||||
)
|
||||
|
||||
val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h")
|
||||
val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0)
|
||||
println(xpubAccount0.asString())
|
||||
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/*
|
||||
|
||||
val internalPath: DerivationPath = DerivationPath("m/0")
|
||||
val externalExtendedKey = xpubAccount0.extend(internalPath).asString()
|
||||
println(externalExtendedKey)
|
||||
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/*
|
||||
|
||||
// to create the descriptor you'll need to use this extended key in a descriptor function,
|
||||
// i.e. wpkh(), tr(), etc.
|
||||
val externalDescriptor = "wpkh($externalExtendedKey)"
|
||||
}
|
||||
|
||||
fun createTransaction() {
|
||||
val wallet = BdkWallet(
|
||||
descriptor = externalDescriptor,
|
||||
changeDescriptor = internalDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = memoryDatabaseConfig,
|
||||
)
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
200u
|
||||
)
|
||||
)
|
||||
|
||||
val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
val (psbt, txDetails) = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
wallet.sign(psbt)
|
||||
blockchain.broadcast(psbt)
|
||||
}
|
||||
|
||||
fun walletSample() {
|
||||
val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)"
|
||||
val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)"
|
||||
val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
|
||||
|
||||
val wallet = Wallet(
|
||||
descriptor = externalDescriptor,
|
||||
changeDescriptor = internalDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = sqliteDatabaseConfig,
|
||||
)
|
||||
}
|
||||
|
||||
fun mnemonicSample() {
|
||||
val mnemonic0: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
|
||||
val mnemonic1: Mnemonic = Mnemonic.fromString("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
|
||||
val entropy: List<UByte> = listOf<UByte>(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)
|
||||
val mnemonic2: Mnemonic = Mnemonic.fromEntropy(entropy)
|
||||
|
||||
println(mnemonic0.asString(), mnemonic1.asString(), mnemonic2.asString())
|
||||
}
|
||||
|
||||
fun descriptorTemplates1() {
|
||||
// Bip84 private descriptor
|
||||
val recoveryPhrase: String = "scene change clap smart together mind wheel knee clip normal trial unusual"
|
||||
val mnemonic = Mnemonic.fromString(recoveryPhrase)
|
||||
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val bip84ExternalDescriptor: Descriptor = Descriptor.newBip84(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
}
|
||||
|
||||
fun descriptorTemplates2() {
|
||||
// Bip49 public descriptor
|
||||
// assume we already have the xpub for m/49'/0'/1' created on an external device that only shared the xpub with the wallet
|
||||
// using the template requires the parent fingerprint to populate correctly the metadata of PSBTs, which the external device would provide
|
||||
// the xpub (tpub for testnet): tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR
|
||||
// the fingerprint: d1d04177
|
||||
val descriptorPublicKey: DescriptorPublicKey = DescriptorPublicKey.fromString("tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR")
|
||||
val bip49PublicDescriptor: Descriptor = Descriptor.newBip49Public(
|
||||
publicKey = descriptorPublicKey,
|
||||
fingerprint = "d1d04177",
|
||||
keychain = KeychainKind.EXTERNAL,
|
||||
network = Network.TESTNET,
|
||||
)
|
||||
println(bip49PublicDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
println(bip49PublicDescriptor.asStringPrivate()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
|
||||
// Creating it starting from the full xprv derived from a mnemonic will give you the same public descriptor
|
||||
val mnemonic = Mnemonic.fromString("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect")
|
||||
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val bip49PrivateDescriptor: Descriptor = Descriptor.newBip49(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
println(bip49PrivateDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
}
|
||||
116
bdk-android/README.md
Normal file
116
bdk-android/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 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>")
|
||||
}
|
||||
```
|
||||
|
||||
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||
```kotlin
|
||||
import org.bitcoindevkit.*
|
||||
|
||||
// ...
|
||||
|
||||
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
|
||||
|
||||
val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
|
||||
)
|
||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
||||
```
|
||||
|
||||
### 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
|
||||
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
|
||||
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
||||
|
||||
### How to build
|
||||
_Note that Kotlin version `1.6.10` 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. If building on macOS install required intel and m1 jvm targets
|
||||
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), for example (NDK major version 21 is required):
|
||||
```shell
|
||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.<NDK_VERSION>
|
||||
```
|
||||
7. Build kotlin bindings
|
||||
```sh
|
||||
# build Android library
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
```
|
||||
8. Start android emulator (must be x86_64) and run tests
|
||||
```sh
|
||||
./gradlew connectedAndroidTest
|
||||
```
|
||||
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-android
|
||||
./gradlew publishToMavenLocal --exclude-task signMavenPublication
|
||||
```
|
||||
|
||||
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 excluding the signing task:
|
||||
```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
|
||||
36
bdk-android/build.gradle.kts
Normal file
36
bdk-android/build.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.1.2")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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=0.28.0
|
||||
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.
5
bdk-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
bdk-android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
bdk-android/gradlew
vendored
Executable file
234
bdk-android/gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/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/master/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
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
|
||||
# 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
|
||||
which java >/dev/null 2>&1 || 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
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
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
|
||||
|
||||
# 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 \
|
||||
"$@"
|
||||
|
||||
# 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" "$@"
|
||||
89
bdk-android/gradlew.bat
vendored
Normal file
89
bdk-android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@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=.
|
||||
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%" == "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%"=="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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
117
bdk-android/lib/build.gradle.kts
Normal file
117
bdk-android/lib/build.gradle.kts
Normal file
@@ -0,0 +1,117 @@
|
||||
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") version "1.6.10"
|
||||
id("maven-publish")
|
||||
id("signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-android-bindings")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 31
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.java.dev.jna:jna:5.8.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.kotlinx:kotlinx-coroutines-core:1.4.1")
|
||||
}
|
||||
|
||||
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("notmandatory")
|
||||
name.set("Steve Myers")
|
||||
email.set("notmandatory@noreply.github.org")
|
||||
}
|
||||
developer {
|
||||
id.set("artfuldev")
|
||||
name.set("Sudarsan Balaji")
|
||||
email.set("sudarsan.balaji@artfuldev.com")
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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,81 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import android.app.Application
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AndroidLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
val context = ApplicationProvider.getApplicationContext<Application>()
|
||||
return context.getDir("bdk-test", MODE_PRIVATE).toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.New).address.asString()
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
}
|
||||
}
|
||||
6
bdk-android/lib/src/main/AndroidManifest.xml
Normal file
6
bdk-android/lib/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.bitcoindevkit">
|
||||
|
||||
<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,14 @@
|
||||
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,166 @@
|
||||
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")
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
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__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
||||
Pair("CC", "aarch64-linux-android21-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)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
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__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
||||
Pair("CC", "x86_64-linux-android21-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)
|
||||
|
||||
// if ANDROID_NDK_ROOT is not set then set it to github actions default
|
||||
if (System.getenv("ANDROID_NDK_ROOT") == null) {
|
||||
environment(
|
||||
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
|
||||
)
|
||||
}
|
||||
|
||||
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__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
|
||||
Pair("CC", "armv7a-linux-androideabi21-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)
|
||||
|
||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||
|
||||
into("arm64-v8a") {
|
||||
from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("x86_64") {
|
||||
from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("armeabi-v7a") {
|
||||
from("${project.projectDir}/../../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)
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
4
bdk-android/settings.gradle.kts
Normal file
4
bdk-android/settings.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
||||
rootProject.name = "bdk-android"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
@@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "bdk-ffi-bindgen"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "=1.0.45" # remove after upgrading to next version of uniffi
|
||||
structopt = "0.3"
|
||||
uniffi_bindgen = "0.16.0"
|
||||
@@ -1,134 +0,0 @@
|
||||
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> {
|
||||
uniffi_bindgen::generate_bindings(
|
||||
&opt.udl_file,
|
||||
None,
|
||||
vec![opt.language.to_string().as_str()],
|
||||
Some(&opt.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(())
|
||||
}
|
||||
30
bdk-ffi/Cargo.toml
Normal file
30
bdk-ffi/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "0.28.0"
|
||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
#crate-type = ["staticlib", "cdylib"]
|
||||
#crate-type = ["cdylib"]
|
||||
name = "bdkffi"
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen"
|
||||
path = "uniffi-bindgen.rs"
|
||||
|
||||
[features]
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "0.28", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
|
||||
uniffi = { version = "0.23.0" }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.23.0", features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
uniffi = { version = "0.23.0", features = ["bindgen-tests"] }
|
||||
assert_matches = "1.5.0"
|
||||
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();
|
||||
}
|
||||
496
bdk-ffi/src/bdk.udl
Normal file
496
bdk-ffi/src/bdk.udl
Normal file
@@ -0,0 +1,496 @@
|
||||
namespace bdk {
|
||||
|
||||
};
|
||||
|
||||
[Error]
|
||||
enum BdkError {
|
||||
"InvalidU32Bytes",
|
||||
"Generic",
|
||||
"MissingCachedScripts",
|
||||
"ScriptDoesntHaveAddressForm",
|
||||
"NoRecipients",
|
||||
"NoUtxosSelected",
|
||||
"OutputBelowDustLimit",
|
||||
"InsufficientFunds",
|
||||
"BnBTotalTriesExceeded",
|
||||
"BnBNoExactMatch",
|
||||
"UnknownUtxo",
|
||||
"TransactionNotFound",
|
||||
"TransactionConfirmed",
|
||||
"IrreplaceableTransaction",
|
||||
"FeeRateTooLow",
|
||||
"FeeTooLow",
|
||||
"FeeRateUnavailable",
|
||||
"MissingKeyOrigin",
|
||||
"Key",
|
||||
"ChecksumMismatch",
|
||||
"SpendingPolicyRequired",
|
||||
"InvalidPolicyPathError",
|
||||
"Signer",
|
||||
"InvalidNetwork",
|
||||
"InvalidProgressValue",
|
||||
"ProgressUpdateError",
|
||||
"InvalidOutpoint",
|
||||
"Descriptor",
|
||||
"Encode",
|
||||
"Miniscript",
|
||||
"MiniscriptPsbt",
|
||||
"Bip32",
|
||||
"Secp256k1",
|
||||
"Json",
|
||||
"Hex",
|
||||
"Psbt",
|
||||
"PsbtParse",
|
||||
"Electrum",
|
||||
"Esplora",
|
||||
"Sled",
|
||||
"Rusqlite",
|
||||
"Rpc",
|
||||
};
|
||||
|
||||
dictionary AddressInfo {
|
||||
u32 index;
|
||||
Address address;
|
||||
KeychainKind keychain;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface AddressIndex {
|
||||
New();
|
||||
LastUnused();
|
||||
Peek(u32 index);
|
||||
Reset(u32 index);
|
||||
};
|
||||
|
||||
enum Network {
|
||||
"Bitcoin",
|
||||
"Testnet",
|
||||
"Signet",
|
||||
"Regtest",
|
||||
};
|
||||
|
||||
dictionary SledDbConfiguration {
|
||||
string path;
|
||||
string tree_name;
|
||||
};
|
||||
|
||||
dictionary SqliteDbConfiguration {
|
||||
string path;
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
u64 immature;
|
||||
u64 trusted_pending;
|
||||
u64 untrusted_pending;
|
||||
u64 confirmed;
|
||||
u64 spendable;
|
||||
u64 total;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface DatabaseConfig {
|
||||
Memory();
|
||||
Sled(SledDbConfiguration config);
|
||||
Sqlite(SqliteDbConfiguration config);
|
||||
};
|
||||
|
||||
dictionary TransactionDetails {
|
||||
Transaction? transaction;
|
||||
u64? fee;
|
||||
u64 received;
|
||||
u64 sent;
|
||||
string txid;
|
||||
BlockTime? confirmation_time;
|
||||
};
|
||||
|
||||
dictionary BlockTime {
|
||||
u32 height;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
enum WordCount {
|
||||
"Words12",
|
||||
"Words15",
|
||||
"Words18",
|
||||
"Words21",
|
||||
"Words24",
|
||||
};
|
||||
|
||||
dictionary ElectrumConfig {
|
||||
string url;
|
||||
string? socks5;
|
||||
u8 retry;
|
||||
u8? timeout;
|
||||
u64 stop_gap;
|
||||
boolean validate_domain;
|
||||
};
|
||||
|
||||
dictionary EsploraConfig {
|
||||
string base_url;
|
||||
string? proxy;
|
||||
u8? concurrency;
|
||||
u64 stop_gap;
|
||||
u64? timeout;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Auth {
|
||||
None();
|
||||
UserPass(string username, string password);
|
||||
Cookie(string file);
|
||||
};
|
||||
|
||||
dictionary RpcSyncParams {
|
||||
u64 start_script_count;
|
||||
u64 start_time;
|
||||
boolean force_start_time;
|
||||
u64 poll_rate_sec;
|
||||
};
|
||||
|
||||
dictionary RpcConfig {
|
||||
string url;
|
||||
Auth auth;
|
||||
Network network;
|
||||
string wallet_name;
|
||||
RpcSyncParams? sync_params;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface BlockchainConfig {
|
||||
Electrum(ElectrumConfig config);
|
||||
Esplora(EsploraConfig config);
|
||||
Rpc(RpcConfig config);
|
||||
};
|
||||
|
||||
interface Blockchain {
|
||||
[Throws=BdkError]
|
||||
constructor(BlockchainConfig config);
|
||||
|
||||
[Throws=BdkError]
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
|
||||
[Throws=BdkError]
|
||||
FeeRate estimate_fee(u64 target);
|
||||
|
||||
[Throws=BdkError]
|
||||
u32 get_height();
|
||||
|
||||
[Throws=BdkError]
|
||||
string get_block_hash(u32 height);
|
||||
};
|
||||
|
||||
callback interface Progress {
|
||||
void update(f32 progress, string? message);
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
dictionary TxIn {
|
||||
OutPoint previous_output;
|
||||
Script script_sig;
|
||||
u32 sequence;
|
||||
sequence<sequence<u8>> witness;
|
||||
};
|
||||
|
||||
dictionary TxOut {
|
||||
u64 value;
|
||||
Script script_pubkey;
|
||||
};
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
dictionary LocalUtxo {
|
||||
OutPoint outpoint;
|
||||
TxOut txout;
|
||||
KeychainKind keychain;
|
||||
boolean is_spent;
|
||||
};
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
u64 amount;
|
||||
};
|
||||
|
||||
interface Wallet {
|
||||
[Throws=BdkError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network, DatabaseConfig database_config);
|
||||
|
||||
[Throws=BdkError]
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
|
||||
[Throws=BdkError]
|
||||
AddressInfo get_internal_address(AddressIndex address_index);
|
||||
|
||||
[Throws=BdkError]
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=BdkError]
|
||||
boolean sign([ByRef] PartiallySignedTransaction psbt, SignOptions? sign_options);
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<TransactionDetails> list_transactions(boolean include_raw);
|
||||
|
||||
Network network();
|
||||
|
||||
[Throws=BdkError]
|
||||
void sync([ByRef] Blockchain blockchain, Progress? progress);
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<LocalUtxo> list_unspent();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb]
|
||||
constructor(float sat_per_vb);
|
||||
|
||||
float as_sat_per_vb();
|
||||
};
|
||||
|
||||
dictionary SignOptions {
|
||||
boolean trust_witness_utxo;
|
||||
u32? assume_height;
|
||||
boolean allow_all_sighashes;
|
||||
boolean remove_partial_sigs;
|
||||
boolean try_finalize;
|
||||
boolean sign_with_tap_internal_key;
|
||||
boolean allow_grinding;
|
||||
};
|
||||
|
||||
interface Transaction {
|
||||
[Throws=BdkError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string txid();
|
||||
|
||||
u64 weight();
|
||||
|
||||
u64 size();
|
||||
|
||||
u64 vsize();
|
||||
|
||||
sequence<u8> serialize();
|
||||
|
||||
boolean is_coin_base();
|
||||
|
||||
boolean is_explicitly_rbf();
|
||||
|
||||
boolean is_lock_time_enabled();
|
||||
|
||||
i32 version();
|
||||
|
||||
u32 lock_time();
|
||||
|
||||
sequence<TxIn> input();
|
||||
|
||||
sequence<TxOut> output();
|
||||
};
|
||||
|
||||
interface PartiallySignedTransaction {
|
||||
[Throws=BdkError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
string txid();
|
||||
|
||||
Transaction extract_tx();
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction combine(PartiallySignedTransaction other);
|
||||
|
||||
u64? fee_amount();
|
||||
|
||||
FeeRate? fee_rate();
|
||||
|
||||
string json_serialize();
|
||||
};
|
||||
|
||||
dictionary TxBuilderResult {
|
||||
PartiallySignedTransaction psbt;
|
||||
TransactionDetails transaction_details;
|
||||
};
|
||||
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient(Script script, u64 amount);
|
||||
|
||||
TxBuilder add_unspendable(OutPoint unspendable);
|
||||
|
||||
TxBuilder add_utxo(OutPoint outpoint);
|
||||
|
||||
TxBuilder add_utxos(sequence<OutPoint> outpoints);
|
||||
|
||||
TxBuilder do_not_spend_change();
|
||||
|
||||
TxBuilder manually_selected_only();
|
||||
|
||||
TxBuilder only_spend_change();
|
||||
|
||||
TxBuilder unspendable(sequence<OutPoint> unspendable);
|
||||
|
||||
TxBuilder fee_rate(float sat_per_vbyte);
|
||||
|
||||
TxBuilder fee_absolute(u64 fee_amount);
|
||||
|
||||
TxBuilder drain_wallet();
|
||||
|
||||
TxBuilder drain_to(Script script);
|
||||
|
||||
TxBuilder enable_rbf();
|
||||
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
TxBuilder add_data(sequence<u8> data);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
[Throws=BdkError]
|
||||
TxBuilderResult finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, float new_fee_rate);
|
||||
|
||||
BumpFeeTxBuilder allow_shrinking(string address);
|
||||
|
||||
BumpFeeTxBuilder enable_rbf();
|
||||
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
|
||||
[Name=from_string, Throws=BdkError]
|
||||
constructor(string mnemonic);
|
||||
|
||||
[Name=from_entropy, Throws=BdkError]
|
||||
constructor(sequence<u8> entropy);
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface DerivationPath {
|
||||
[Throws=BdkError]
|
||||
constructor(string path);
|
||||
};
|
||||
|
||||
interface DescriptorSecretKey {
|
||||
constructor(Network network, Mnemonic mnemonic, string? password);
|
||||
|
||||
[Name=from_string, Throws=BdkError]
|
||||
constructor(string secret_key);
|
||||
|
||||
[Throws=BdkError]
|
||||
DescriptorSecretKey derive(DerivationPath path);
|
||||
|
||||
[Throws=BdkError]
|
||||
DescriptorSecretKey extend(DerivationPath path);
|
||||
|
||||
DescriptorPublicKey as_public();
|
||||
|
||||
sequence<u8> secret_bytes();
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface DescriptorPublicKey {
|
||||
[Name=from_string, Throws=BdkError]
|
||||
constructor(string public_key);
|
||||
|
||||
[Throws=BdkError]
|
||||
DescriptorPublicKey derive(DerivationPath path);
|
||||
|
||||
[Throws=BdkError]
|
||||
DescriptorPublicKey extend(DerivationPath path);
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface Descriptor {
|
||||
[Throws=BdkError]
|
||||
constructor(string descriptor, Network network);
|
||||
|
||||
[Name=new_bip44]
|
||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip44_public]
|
||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip49]
|
||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip49_public]
|
||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip84]
|
||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip84_public]
|
||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
string as_string();
|
||||
|
||||
string as_string_private();
|
||||
};
|
||||
|
||||
interface Address {
|
||||
[Throws=BdkError]
|
||||
constructor(string address);
|
||||
|
||||
[Name=from_script, Throws=BdkError]
|
||||
constructor(Script script, Network network);
|
||||
|
||||
Payload payload();
|
||||
|
||||
Network network();
|
||||
|
||||
Script script_pubkey();
|
||||
|
||||
string to_qr_uri();
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Payload {
|
||||
PubkeyHash(sequence<u8> pubkey_hash);
|
||||
|
||||
ScriptHash(sequence<u8> script_hash);
|
||||
|
||||
WitnessProgram(WitnessVersion version, sequence<u8> program);
|
||||
};
|
||||
|
||||
enum WitnessVersion {
|
||||
"V0",
|
||||
"V1",
|
||||
"V2",
|
||||
"V3",
|
||||
"V4",
|
||||
"V5",
|
||||
"V6",
|
||||
"V7",
|
||||
"V8",
|
||||
"V9",
|
||||
"V10",
|
||||
"V11",
|
||||
"V12",
|
||||
"V13",
|
||||
"V14",
|
||||
"V15",
|
||||
"V16"
|
||||
};
|
||||
|
||||
interface Script {
|
||||
constructor(sequence<u8> raw_output_script);
|
||||
};
|
||||
201
bdk-ffi/src/blockchain.rs
Normal file
201
bdk-ffi/src/blockchain.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
// use crate::BlockchainConfig;
|
||||
use crate::{BdkError, Transaction};
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
|
||||
use bdk::blockchain::rpc::Auth as BdkAuth;
|
||||
use bdk::blockchain::rpc::RpcSyncParams as BdkRpcSyncParams;
|
||||
use bdk::blockchain::Blockchain as BdkBlockchain;
|
||||
use bdk::blockchain::GetBlockHash;
|
||||
use bdk::blockchain::GetHeight;
|
||||
use bdk::blockchain::{
|
||||
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig,
|
||||
rpc::RpcConfig as BdkRpcConfig, ConfigurableBlockchain,
|
||||
};
|
||||
use bdk::FeeRate;
|
||||
use std::convert::{From, TryFrom};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
pub(crate) struct Blockchain {
|
||||
blockchain_mutex: Mutex<AnyBlockchain>,
|
||||
}
|
||||
|
||||
impl Blockchain {
|
||||
pub(crate) fn new(blockchain_config: BlockchainConfig) -> Result<Self, BdkError> {
|
||||
let any_blockchain_config = match blockchain_config {
|
||||
BlockchainConfig::Electrum { config } => {
|
||||
AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
|
||||
retry: config.retry,
|
||||
socks5: config.socks5,
|
||||
timeout: config.timeout,
|
||||
url: config.url,
|
||||
stop_gap: usize::try_from(config.stop_gap).unwrap(),
|
||||
validate_domain: config.validate_domain,
|
||||
})
|
||||
}
|
||||
BlockchainConfig::Esplora { config } => {
|
||||
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
|
||||
base_url: config.base_url,
|
||||
proxy: config.proxy,
|
||||
concurrency: config.concurrency,
|
||||
stop_gap: usize::try_from(config.stop_gap).unwrap(),
|
||||
timeout: config.timeout,
|
||||
})
|
||||
}
|
||||
BlockchainConfig::Rpc { config } => AnyBlockchainConfig::Rpc(BdkRpcConfig {
|
||||
url: config.url,
|
||||
auth: config.auth.into(),
|
||||
network: config.network,
|
||||
wallet_name: config.wallet_name,
|
||||
sync_params: config.sync_params.map(|p| p.into()),
|
||||
}),
|
||||
};
|
||||
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
|
||||
Ok(Self {
|
||||
blockchain_mutex: Mutex::new(blockchain),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_blockchain(&self) -> MutexGuard<AnyBlockchain> {
|
||||
self.blockchain_mutex.lock().expect("blockchain")
|
||||
}
|
||||
|
||||
pub(crate) fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
|
||||
let tx = &transaction.internal;
|
||||
self.get_blockchain().broadcast(tx)
|
||||
}
|
||||
|
||||
pub(crate) fn estimate_fee(&self, target: u64) -> Result<Arc<FeeRate>, BdkError> {
|
||||
let result: Result<FeeRate, bdk::Error> =
|
||||
self.get_blockchain().estimate_fee(target as usize);
|
||||
result.map(Arc::new)
|
||||
}
|
||||
|
||||
pub(crate) fn get_height(&self) -> Result<u32, BdkError> {
|
||||
self.get_blockchain().get_height()
|
||||
}
|
||||
|
||||
pub(crate) fn get_block_hash(&self, height: u32) -> Result<String, BdkError> {
|
||||
self.get_blockchain()
|
||||
.get_block_hash(u64::from(height))
|
||||
.map(|hash| hash.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for an ElectrumBlockchain
|
||||
pub struct ElectrumConfig {
|
||||
/// URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with ssl:// or tcp:// and include a port
|
||||
/// e.g. ssl://electrum.blockstream.info:60002
|
||||
pub url: String,
|
||||
/// URL of the socks5 proxy server or a Tor service
|
||||
pub socks5: Option<String>,
|
||||
/// Request retry count
|
||||
pub retry: u8,
|
||||
/// Request timeout (seconds)
|
||||
pub timeout: Option<u8>,
|
||||
/// Stop searching addresses for transactions after finding an unused gap of this length
|
||||
pub stop_gap: u64,
|
||||
/// Validate the domain when using SSL
|
||||
pub validate_domain: bool,
|
||||
}
|
||||
|
||||
/// Configuration for an EsploraBlockchain
|
||||
pub struct EsploraConfig {
|
||||
/// Base URL of the esplora service
|
||||
/// e.g. https://blockstream.info/api/
|
||||
pub base_url: String,
|
||||
/// Optional URL of the proxy to use to make requests to the Esplora server
|
||||
/// The string should be formatted as: <protocol>://<user>:<password>@host:<port>.
|
||||
/// Note that the format of this value and the supported protocols change slightly between the
|
||||
/// sync version of esplora (using ureq) and the async version (using reqwest). For more
|
||||
/// details check with the documentation of the two crates. Both of them are compiled with
|
||||
/// the socks feature enabled.
|
||||
/// The proxy is ignored when targeting wasm32.
|
||||
pub proxy: Option<String>,
|
||||
/// Number of parallel requests sent to the esplora service (default: 4)
|
||||
pub concurrency: Option<u8>,
|
||||
/// Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
pub stop_gap: u64,
|
||||
/// Socket timeout.
|
||||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum Auth {
|
||||
/// No authentication
|
||||
None,
|
||||
/// Authentication with username and password, usually [Auth::Cookie] should be preferred
|
||||
UserPass {
|
||||
/// Username
|
||||
username: String,
|
||||
/// Password
|
||||
password: String,
|
||||
},
|
||||
/// Authentication with a cookie file
|
||||
Cookie {
|
||||
/// Cookie file
|
||||
file: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Auth> for BdkAuth {
|
||||
fn from(auth: Auth) -> Self {
|
||||
match auth {
|
||||
Auth::None => BdkAuth::None,
|
||||
Auth::UserPass { username, password } => BdkAuth::UserPass { username, password },
|
||||
Auth::Cookie { file } => BdkAuth::Cookie {
|
||||
file: PathBuf::from(file),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync parameters for Bitcoin Core RPC.
|
||||
///
|
||||
/// In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
|
||||
/// `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
|
||||
/// how the `importdescriptors` RPC calls are to be made.
|
||||
pub struct RpcSyncParams {
|
||||
/// The minimum number of scripts to scan for on initial sync.
|
||||
pub start_script_count: u64,
|
||||
/// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
|
||||
pub start_time: u64,
|
||||
/// Forces every sync to use `start_time` as import timestamp.
|
||||
pub force_start_time: bool,
|
||||
/// RPC poll rate (in seconds) to get state updates.
|
||||
pub poll_rate_sec: u64,
|
||||
}
|
||||
|
||||
impl From<RpcSyncParams> for BdkRpcSyncParams {
|
||||
fn from(params: RpcSyncParams) -> Self {
|
||||
BdkRpcSyncParams {
|
||||
start_script_count: params.start_script_count as usize,
|
||||
start_time: params.start_time,
|
||||
force_start_time: params.force_start_time,
|
||||
poll_rate_sec: params.poll_rate_sec,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RpcBlockchain configuration options
|
||||
pub struct RpcConfig {
|
||||
/// The bitcoin node url
|
||||
pub url: String,
|
||||
/// The bitcoin node authentication mechanism
|
||||
pub auth: Auth,
|
||||
/// The network we are using (it will be checked the bitcoin node network matches this)
|
||||
pub network: Network,
|
||||
/// The wallet name in the bitcoin node, consider using [crate::wallet::wallet_name_from_descriptor] for this
|
||||
pub wallet_name: String,
|
||||
/// Sync parameters
|
||||
pub sync_params: Option<RpcSyncParams>,
|
||||
}
|
||||
|
||||
/// Type that can contain any of the blockchain configurations defined by the library.
|
||||
pub enum BlockchainConfig {
|
||||
/// Electrum client
|
||||
Electrum { config: ElectrumConfig },
|
||||
/// Esplora client
|
||||
Esplora { config: EsploraConfig },
|
||||
/// Bitcoin Core RPC client
|
||||
Rpc { config: RpcConfig },
|
||||
}
|
||||
14
bdk-ffi/src/database.rs
Normal file
14
bdk-ffi/src/database.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
|
||||
|
||||
/// Type that can contain any of the database configurations defined by the library
|
||||
/// This allows storing a single configuration that can be loaded into an AnyDatabaseConfig
|
||||
/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
|
||||
/// will find this particularly useful.
|
||||
pub enum DatabaseConfig {
|
||||
/// Memory database has no config
|
||||
Memory,
|
||||
/// Simple key-value embedded database based on sled
|
||||
Sled { config: SledDbConfiguration },
|
||||
/// Sqlite embedded database using rusqlite
|
||||
Sqlite { config: SqliteDbConfiguration },
|
||||
}
|
||||
334
bdk-ffi/src/descriptor.rs
Normal file
334
bdk-ffi/src/descriptor.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
use crate::{BdkError, DescriptorPublicKey, DescriptorSecretKey};
|
||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||
use bdk::bitcoin::util::bip32::Fingerprint;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
||||
use bdk::keys::{
|
||||
DescriptorPublicKey as BdkDescriptorPublicKey, DescriptorSecretKey as BdkDescriptorSecretKey,
|
||||
};
|
||||
use bdk::template::{
|
||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, DescriptorTemplate,
|
||||
};
|
||||
use bdk::KeychainKind;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Descriptor {
|
||||
pub(crate) extended_descriptor: ExtendedDescriptor,
|
||||
pub(crate) key_map: KeyMap,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
|
||||
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: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip44_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
|
||||
|
||||
match derivable_key.deref() {
|
||||
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::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string_private(&self) -> String {
|
||||
let descriptor = &self.extended_descriptor;
|
||||
let key_map = &self.key_map;
|
||||
descriptor.to_string_with_secret(key_map)
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.extended_descriptor.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bdk::descriptor::DescriptorError::Key;
|
||||
use bdk::keys::KeyError::InvalidNetwork;
|
||||
|
||||
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, Arc::new(mnemonic), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_descriptor_templates() {
|
||||
let master: Arc<DescriptorSecretKey> = Arc::new(get_descriptor_secret_key());
|
||||
println!("Master: {:?}", master.as_string());
|
||||
// tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h
|
||||
let handmade_public_44 = master
|
||||
.derive(Arc::new(
|
||||
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(Arc::new(
|
||||
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(Arc::new(
|
||||
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 template_private_44 =
|
||||
Descriptor::new_bip44(master.clone(), KeychainKind::External, Network::Testnet);
|
||||
let template_private_49 =
|
||||
Descriptor::new_bip49(master.clone(), KeychainKind::External, Network::Testnet);
|
||||
let template_private_84 =
|
||||
Descriptor::new_bip84(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.as_string());
|
||||
println!("Template 44: {}", template_private_44.as_string());
|
||||
println!("Template 84: {}", template_private_84.as_string());
|
||||
// for the public versions of the templates these are incorrect, bug report and fix in bitcoindevkit/bdk#817 and bitcoindevkit/bdk#818
|
||||
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,
|
||||
);
|
||||
println!("Template public 49: {}", template_public_49.as_string());
|
||||
println!("Template public 44: {}", template_public_44.as_string());
|
||||
println!("Template public 84: {}", template_public_84.as_string());
|
||||
// when using a public key, both as_string and as_string_private return the same string
|
||||
assert_eq!(
|
||||
template_public_44.as_string_private(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_49.as_string_private(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_84.as_string_private(),
|
||||
template_public_84.as_string()
|
||||
);
|
||||
// when using as_string on a private key, we get the same result as when using it on a public key
|
||||
assert_eq!(
|
||||
template_private_44.as_string(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_49.as_string(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_84.as_string(),
|
||||
template_public_84.as_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 and InvalidNetwork Error
|
||||
assert!(descriptor1.is_ok());
|
||||
assert_matches!(
|
||||
descriptor2.unwrap_err(),
|
||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
fn test_wallet_from_descriptor() {
|
||||
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap();
|
||||
let wallet1 = Wallet::new(
|
||||
Arc::new(Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap()),
|
||||
None,
|
||||
Network::Testnet,
|
||||
DatabaseConfig::Memory
|
||||
);
|
||||
let wallet2 = Wallet::new(
|
||||
Arc::new(descriptor1),
|
||||
None,
|
||||
Network::Bitcoin,
|
||||
DatabaseConfig::Memory,
|
||||
);
|
||||
// Creating a wallet using a Descriptor with an extended key that doesn't match the network provided in the wallet constructor will throw and InvalidNetwork Error
|
||||
assert!(wallet1.is_ok());
|
||||
assert_matches!(
|
||||
wallet2.unwrap_err(),
|
||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
||||
)
|
||||
}
|
||||
}
|
||||
376
bdk-ffi/src/keys.rs
Normal file
376
bdk-ffi/src/keys.rs
Normal file
@@ -0,0 +1,376 @@
|
||||
use crate::BdkError;
|
||||
|
||||
use bdk::bitcoin::secp256k1::Secp256k1;
|
||||
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::DescriptorXKey;
|
||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
|
||||
use bdk::keys::{
|
||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||
};
|
||||
use bdk::miniscript::BareCtx;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Mnemonic phrases are a human-readable version of the private keys.
|
||||
/// Supported number of words are 12, 15, 18, 21 and 24.
|
||||
pub(crate) struct Mnemonic {
|
||||
internal: BdkMnemonic,
|
||||
}
|
||||
|
||||
impl Mnemonic {
|
||||
/// Generates Mnemonic with a random entropy
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
let generated_key: GeneratedKey<_, BareCtx> =
|
||||
BdkMnemonic::generate((word_count, Language::English)).unwrap();
|
||||
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||
Mnemonic { internal: mnemonic }
|
||||
}
|
||||
|
||||
/// Parse a Mnemonic with given string
|
||||
pub(crate) fn from_string(mnemonic: String) -> Result<Self, BdkError> {
|
||||
BdkMnemonic::from_str(&mnemonic)
|
||||
.map(|m| Mnemonic { internal: m })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
/// Create a new Mnemonic in the specified language from the given entropy.
|
||||
/// Entropy must be a multiple of 32 bits (4 bytes) and 128-256 bits in length.
|
||||
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, BdkError> {
|
||||
BdkMnemonic::from_entropy(entropy.as_slice())
|
||||
.map(|m| Mnemonic { internal: m })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
/// Returns Mnemonic as string
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.internal.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DerivationPath {
|
||||
derivation_path_mutex: Mutex<BdkDerivationPath>,
|
||||
}
|
||||
|
||||
impl DerivationPath {
|
||||
pub(crate) fn new(path: String) -> Result<Self, BdkError> {
|
||||
BdkDerivationPath::from_str(&path)
|
||||
.map(|x| DerivationPath {
|
||||
derivation_path_mutex: Mutex::new(x),
|
||||
})
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DescriptorSecretKey {
|
||||
pub(crate) descriptor_secret_key_mutex: Mutex<BdkDescriptorSecretKey>,
|
||||
}
|
||||
|
||||
impl DescriptorSecretKey {
|
||||
pub(crate) fn new(network: Network, mnemonic: Arc<Mnemonic>, password: Option<String>) -> Self {
|
||||
let mnemonic = mnemonic.internal.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: bdk::descriptor::Wildcard::Unhardened,
|
||||
});
|
||||
Self {
|
||||
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(private_key: String) -> Result<Self, BdkError> {
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
Ok(Self {
|
||||
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
|
||||
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key.deref() {
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
|
||||
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 {
|
||||
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
|
||||
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key.deref() {
|
||||
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 {
|
||||
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = self
|
||||
.descriptor_secret_key_mutex
|
||||
.lock()
|
||||
.unwrap()
|
||||
.to_public(&secp)
|
||||
.unwrap();
|
||||
Arc::new(DescriptorPublicKey {
|
||||
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the private key as bytes.
|
||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
|
||||
let secret_bytes: Vec<u8> = match descriptor_secret_key.deref() {
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||
}
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
secret_bytes
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.descriptor_secret_key_mutex.lock().unwrap().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DescriptorPublicKey {
|
||||
pub(crate) descriptor_public_key_mutex: Mutex<BdkDescriptorPublicKey>,
|
||||
}
|
||||
|
||||
impl DescriptorPublicKey {
|
||||
pub(crate) fn from_string(public_key: String) -> Result<Self, BdkError> {
|
||||
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
Ok(Self {
|
||||
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap();
|
||||
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
|
||||
|
||||
match descriptor_public_key.deref() {
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
|
||||
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 {
|
||||
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap();
|
||||
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_public_key.deref() {
|
||||
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 {
|
||||
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.descriptor_public_key_mutex.lock().unwrap().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use crate::BdkError;
|
||||
use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use bdk::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
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, Arc::new(mnemonic), None)
|
||||
}
|
||||
|
||||
fn derive_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.derive(path)
|
||||
}
|
||||
|
||||
fn extend_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.extend(path)
|
||||
}
|
||||
|
||||
fn derive_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.derive(path)
|
||||
}
|
||||
|
||||
fn extend_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.extend(path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_descriptor_secret_key() {
|
||||
let master_dsk = get_descriptor_secret_key();
|
||||
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_descriptor_secret_key();
|
||||
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_descriptor_secret_key();
|
||||
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_descriptor_secret_key();
|
||||
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(Arc::new(DerivationPath::new("m/0".to_string()).unwrap()));
|
||||
dbg!(&result);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_descriptor_secret_key() {
|
||||
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_descriptor_secret_key() {
|
||||
let master_dsk = get_descriptor_secret_key();
|
||||
// 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_descriptor_secret_key().as_public();
|
||||
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
|
||||
assert!(derived_dpk.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retrieve_master_secret_key() {
|
||||
let master_dpk = get_descriptor_secret_key();
|
||||
let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||
assert_eq!(
|
||||
master_private_key,
|
||||
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||
)
|
||||
}
|
||||
}
|
||||
497
bdk-ffi/src/lib.rs
Normal file
497
bdk-ffi/src/lib.rs
Normal file
@@ -0,0 +1,497 @@
|
||||
mod blockchain;
|
||||
mod database;
|
||||
mod descriptor;
|
||||
mod keys;
|
||||
mod psbt;
|
||||
mod wallet;
|
||||
|
||||
use crate::blockchain::{
|
||||
Auth, Blockchain, BlockchainConfig, ElectrumConfig, EsploraConfig, RpcConfig, RpcSyncParams,
|
||||
};
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::keys::DerivationPath;
|
||||
use crate::keys::{DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use crate::psbt::PartiallySignedTransaction;
|
||||
use crate::wallet::SignOptions;
|
||||
use crate::wallet::{BumpFeeTxBuilder, TxBuilder, Wallet};
|
||||
use bdk::bitcoin::blockdata::script::Script as BdkScript;
|
||||
use bdk::bitcoin::blockdata::transaction::TxIn as BdkTxIn;
|
||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::psbt::serialize::Serialize;
|
||||
use bdk::bitcoin::util::address::{Payload as BdkPayload, WitnessVersion};
|
||||
use bdk::bitcoin::{
|
||||
Address as BdkAddress, Network, OutPoint as BdkOutPoint, Transaction as BdkTransaction, Txid,
|
||||
};
|
||||
use bdk::blockchain::Progress as BdkProgress;
|
||||
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::wallet::AddressIndex as BdkAddressIndex;
|
||||
use bdk::wallet::AddressInfo as BdkAddressInfo;
|
||||
use bdk::LocalUtxo as BdkLocalUtxo;
|
||||
use bdk::TransactionDetails as BdkTransactionDetails;
|
||||
use bdk::{Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind};
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
uniffi::include_scaffolding!("bdk");
|
||||
|
||||
/// A output script and an amount of satoshis.
|
||||
pub struct ScriptAmount {
|
||||
pub script: Arc<Script>,
|
||||
pub amount: u64,
|
||||
}
|
||||
|
||||
/// A derived address and the index it was found at.
|
||||
pub struct AddressInfo {
|
||||
/// Child index of this address.
|
||||
pub index: u32,
|
||||
/// Address.
|
||||
pub address: Arc<Address>,
|
||||
/// Type of keychain.
|
||||
pub keychain: KeychainKind,
|
||||
}
|
||||
|
||||
impl From<BdkAddressInfo> for AddressInfo {
|
||||
fn from(address_info: BdkAddressInfo) -> Self {
|
||||
AddressInfo {
|
||||
index: address_info.index,
|
||||
address: Arc::new(Address::from(address_info.address)),
|
||||
keychain: address_info.keychain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The address index selection strategy to use to derived an address from the wallet's external
|
||||
/// descriptor.
|
||||
pub enum AddressIndex {
|
||||
/// Return a new address after incrementing the current descriptor index.
|
||||
New,
|
||||
/// Return the address for the current descriptor index if it has not been used in a received
|
||||
/// transaction. Otherwise return a new address as with AddressIndex::New.
|
||||
/// Use with caution, if the wallet has not yet detected an address has been used it could
|
||||
/// return an already used address. This function is primarily meant for situations where the
|
||||
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
|
||||
/// web page.
|
||||
LastUnused,
|
||||
/// Return the address for a specific descriptor index. Does not change the current descriptor
|
||||
/// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
|
||||
/// Use with caution, if an index is given that is less than the current descriptor index
|
||||
/// then the returned address may have already been used.
|
||||
Peek { index: u32 },
|
||||
/// Return the address for a specific descriptor index and reset the current descriptor index
|
||||
/// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value.
|
||||
/// Use with caution, if an index is given that is less than the current descriptor index
|
||||
/// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
|
||||
/// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a
|
||||
/// value earlier than the [`Blockchain`] stop_gap (default is 20) then a
|
||||
/// larger stop_gap should be used to monitor for all possibly used addresses.
|
||||
Reset { index: u32 },
|
||||
}
|
||||
|
||||
impl From<AddressIndex> for BdkAddressIndex {
|
||||
fn from(address_index: AddressIndex) -> Self {
|
||||
match address_index {
|
||||
AddressIndex::New => BdkAddressIndex::New,
|
||||
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
|
||||
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
|
||||
AddressIndex::Reset { index } => BdkAddressIndex::Reset(index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wallet transaction
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
pub struct TransactionDetails {
|
||||
pub transaction: Option<Arc<Transaction>>,
|
||||
/// Transaction id.
|
||||
pub txid: String,
|
||||
/// Received value (sats)
|
||||
/// Sum of owned outputs of this transaction.
|
||||
pub received: u64,
|
||||
/// Sent value (sats)
|
||||
/// Sum of owned inputs of this transaction.
|
||||
pub sent: u64,
|
||||
/// Fee value (sats) if confirmed.
|
||||
/// The availability of the fee depends on the backend. It's never None with an Electrum
|
||||
/// Server backend, but it could be None with a Bitcoin RPC node without txindex that receive
|
||||
/// funds while offline.
|
||||
pub fee: Option<u64>,
|
||||
/// If the transaction is confirmed, contains height and timestamp of the block containing the
|
||||
/// transaction, unconfirmed transaction contains `None`.
|
||||
pub confirmation_time: Option<BlockTime>,
|
||||
}
|
||||
|
||||
impl From<BdkTransactionDetails> for TransactionDetails {
|
||||
fn from(tx_details: BdkTransactionDetails) -> Self {
|
||||
let optional_tx: Option<Arc<Transaction>> =
|
||||
tx_details.transaction.map(|tx| Arc::new(tx.into()));
|
||||
|
||||
TransactionDetails {
|
||||
transaction: optional_tx,
|
||||
fee: tx_details.fee,
|
||||
txid: tx_details.txid.to_string(),
|
||||
received: tx_details.received,
|
||||
sent: tx_details.sent,
|
||||
confirmation_time: tx_details.confirmation_time,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a transaction output.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OutPoint {
|
||||
/// The referenced transaction's txid.
|
||||
txid: String,
|
||||
/// The index of the referenced output in its transaction's vout.
|
||||
vout: u32,
|
||||
}
|
||||
|
||||
impl From<&OutPoint> for BdkOutPoint {
|
||||
fn from(outpoint: &OutPoint) -> Self {
|
||||
BdkOutPoint {
|
||||
txid: Txid::from_str(&outpoint.txid).unwrap(),
|
||||
vout: outpoint.vout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Balance {
|
||||
// All coinbase outputs not yet matured
|
||||
pub immature: u64,
|
||||
/// Unconfirmed UTXOs generated by a wallet tx
|
||||
pub trusted_pending: u64,
|
||||
/// Unconfirmed UTXOs received from an external wallet
|
||||
pub untrusted_pending: u64,
|
||||
/// Confirmed and immediately spendable balance
|
||||
pub confirmed: u64,
|
||||
/// Get sum of trusted_pending and confirmed coins
|
||||
pub spendable: u64,
|
||||
/// Get the whole balance visible to the wallet
|
||||
pub total: u64,
|
||||
}
|
||||
|
||||
impl From<BdkBalance> for Balance {
|
||||
fn from(bdk_balance: BdkBalance) -> Self {
|
||||
Balance {
|
||||
immature: bdk_balance.immature,
|
||||
trusted_pending: bdk_balance.trusted_pending,
|
||||
untrusted_pending: bdk_balance.untrusted_pending,
|
||||
confirmed: bdk_balance.confirmed,
|
||||
spendable: bdk_balance.get_spendable(),
|
||||
total: bdk_balance.get_total(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A transaction output, which defines new coins to be created from old ones.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TxOut {
|
||||
/// The value of the output, in satoshis.
|
||||
value: u64,
|
||||
/// The address of the output.
|
||||
script_pubkey: Arc<Script>,
|
||||
}
|
||||
|
||||
impl From<&BdkTxOut> for TxOut {
|
||||
fn from(tx_out: &BdkTxOut) -> Self {
|
||||
TxOut {
|
||||
value: tx_out.value,
|
||||
script_pubkey: Arc::new(Script {
|
||||
script: tx_out.script_pubkey.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalUtxo {
|
||||
outpoint: OutPoint,
|
||||
txout: TxOut,
|
||||
keychain: KeychainKind,
|
||||
is_spent: bool,
|
||||
}
|
||||
|
||||
impl From<BdkLocalUtxo> for LocalUtxo {
|
||||
fn from(local_utxo: BdkLocalUtxo) -> Self {
|
||||
LocalUtxo {
|
||||
outpoint: OutPoint {
|
||||
txid: local_utxo.outpoint.txid.to_string(),
|
||||
vout: local_utxo.outpoint.vout,
|
||||
},
|
||||
txout: TxOut {
|
||||
value: local_utxo.txout.value,
|
||||
script_pubkey: Arc::new(Script {
|
||||
script: local_utxo.txout.script_pubkey,
|
||||
}),
|
||||
},
|
||||
keychain: local_utxo.keychain,
|
||||
is_spent: local_utxo.is_spent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that logs at level INFO every update received (if any).
|
||||
pub trait Progress: Send + Sync + 'static {
|
||||
/// Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an
|
||||
/// optional text message that can be displayed to the user.
|
||||
fn update(&self, progress: f32, message: Option<String>);
|
||||
}
|
||||
|
||||
struct ProgressHolder {
|
||||
progress: Box<dyn Progress>,
|
||||
}
|
||||
|
||||
impl BdkProgress for ProgressHolder {
|
||||
fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
|
||||
self.progress.update(progress, message);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ProgressHolder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ProgressHolder").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
script: tx_in.script_sig.clone(),
|
||||
}),
|
||||
sequence: tx_in.sequence.0,
|
||||
witness: tx_in.witness.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin transaction.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Transaction {
|
||||
internal: BdkTransaction,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
Ok(Transaction { internal: tx })
|
||||
}
|
||||
|
||||
fn txid(&self) -> String {
|
||||
self.internal.txid().to_string()
|
||||
}
|
||||
|
||||
fn weight(&self) -> u64 {
|
||||
self.internal.weight() as u64
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
self.internal.size() as u64
|
||||
}
|
||||
|
||||
fn vsize(&self) -> u64 {
|
||||
self.internal.vsize() as u64
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
self.internal.serialize()
|
||||
}
|
||||
|
||||
fn is_coin_base(&self) -> bool {
|
||||
self.internal.is_coin_base()
|
||||
}
|
||||
|
||||
fn is_explicitly_rbf(&self) -> bool {
|
||||
self.internal.is_explicitly_rbf()
|
||||
}
|
||||
|
||||
fn is_lock_time_enabled(&self) -> bool {
|
||||
self.internal.is_lock_time_enabled()
|
||||
}
|
||||
|
||||
fn version(&self) -> i32 {
|
||||
self.internal.version
|
||||
}
|
||||
|
||||
fn lock_time(&self) -> u32 {
|
||||
self.internal.lock_time.0
|
||||
}
|
||||
|
||||
fn input(&self) -> Vec<TxIn> {
|
||||
self.internal.input.iter().map(|x| x.into()).collect()
|
||||
}
|
||||
|
||||
fn output(&self) -> Vec<TxOut> {
|
||||
self.internal.output.iter().map(|x| x.into()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkTransaction> for Transaction {
|
||||
fn from(tx: BdkTransaction) -> Self {
|
||||
Transaction { internal: tx }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin address.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Address {
|
||||
address: BdkAddress,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
fn new(address: String) -> Result<Self, BdkError> {
|
||||
BdkAddress::from_str(address.as_str())
|
||||
.map(|a| Address { address: a })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
/// alternative constructor
|
||||
fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
|
||||
BdkAddress::from_script(&script.script, network)
|
||||
.map(|a| Address { address: a })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
fn payload(&self) -> Payload {
|
||||
match &self.address.payload.clone() {
|
||||
BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
|
||||
pubkey_hash: pubkey_hash.to_vec(),
|
||||
},
|
||||
BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
|
||||
script_hash: script_hash.to_vec(),
|
||||
},
|
||||
BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
|
||||
version: *version,
|
||||
program: program.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn network(&self) -> Network {
|
||||
self.address.network
|
||||
}
|
||||
|
||||
fn script_pubkey(&self) -> Arc<Script> {
|
||||
Arc::new(Script {
|
||||
script: self.address.script_pubkey(),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_qr_uri(&self) -> String {
|
||||
self.address.to_qr_uri()
|
||||
}
|
||||
|
||||
fn as_string(&self) -> String {
|
||||
self.address.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAddress> for Address {
|
||||
fn from(address: BdkAddress) -> Self {
|
||||
Address { address }
|
||||
}
|
||||
}
|
||||
|
||||
/// The method used to produce an address.
|
||||
#[derive(Debug)]
|
||||
pub enum Payload {
|
||||
/// P2PKH address.
|
||||
PubkeyHash { pubkey_hash: Vec<u8> },
|
||||
/// P2SH address.
|
||||
ScriptHash { script_hash: Vec<u8> },
|
||||
/// Segwit address.
|
||||
WitnessProgram {
|
||||
/// The witness program version.
|
||||
version: WitnessVersion,
|
||||
/// The witness program.
|
||||
program: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A Bitcoin script.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script {
|
||||
script: BdkScript,
|
||||
}
|
||||
|
||||
impl Script {
|
||||
fn new(raw_output_script: Vec<u8>) -> Self {
|
||||
let script: BdkScript = BdkScript::from(raw_output_script);
|
||||
Script { script }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkScript> for Script {
|
||||
fn from(bdk_script: BdkScript) -> Self {
|
||||
Script { script: bdk_script }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum RbfValue {
|
||||
Default,
|
||||
Value(u32),
|
||||
}
|
||||
|
||||
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
|
||||
/// transaction details.
|
||||
pub struct TxBuilderResult {
|
||||
pub(crate) psbt: Arc<PartiallySignedTransaction>,
|
||||
pub transaction_details: TransactionDetails,
|
||||
}
|
||||
|
||||
uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Transaction;
|
||||
use crate::Network::Regtest;
|
||||
use crate::{Address, Payload};
|
||||
use assert_matches::assert_matches;
|
||||
use bdk::bitcoin::hashes::hex::FromHex;
|
||||
use bdk::bitcoin::util::address::WitnessVersion;
|
||||
|
||||
// Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
|
||||
#[test]
|
||||
fn test_transaction_serde() {
|
||||
let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
|
||||
let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
|
||||
let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
|
||||
assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
|
||||
}
|
||||
|
||||
// Verify that bdk-ffi Address.payload includes expected WitnessProgram variant, version and program bytes.
|
||||
#[test]
|
||||
fn test_address_witness_program() {
|
||||
let address =
|
||||
Address::new("bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv".to_string()).unwrap();
|
||||
let payload = address.payload();
|
||||
assert_matches!(payload, Payload::WitnessProgram { version, program } => {
|
||||
assert_eq!(version,WitnessVersion::V0);
|
||||
assert_eq!(program, Vec::from_hex("04a6545885dd87b8e147cd327f2e9db362b72346").unwrap());
|
||||
});
|
||||
assert_eq!(address.network(), Regtest);
|
||||
}
|
||||
}
|
||||
119
bdk-ffi/src/psbt.rs
Normal file
119
bdk-ffi/src/psbt.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
use bdk::bitcoincore_rpc::jsonrpc::serde_json;
|
||||
use bdk::psbt::PsbtUtils;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::{BdkError, FeeRate, Transaction};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PartiallySignedTransaction {
|
||||
pub(crate) internal: Mutex<BdkPartiallySignedTransaction>,
|
||||
}
|
||||
|
||||
impl PartiallySignedTransaction {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
||||
let psbt: BdkPartiallySignedTransaction =
|
||||
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
|
||||
Ok(PartiallySignedTransaction {
|
||||
internal: Mutex::new(psbt),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> String {
|
||||
let psbt = self.internal.lock().unwrap().clone();
|
||||
psbt.to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn txid(&self) -> String {
|
||||
let tx = self.internal.lock().unwrap().clone().extract_tx();
|
||||
let txid = tx.txid();
|
||||
txid.to_hex()
|
||||
}
|
||||
|
||||
/// Return the transaction.
|
||||
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
|
||||
let tx = self.internal.lock().unwrap().clone().extract_tx();
|
||||
Arc::new(tx.into())
|
||||
}
|
||||
|
||||
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
||||
///
|
||||
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||
pub(crate) fn combine(
|
||||
&self,
|
||||
other: Arc<PartiallySignedTransaction>,
|
||||
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
let other_psbt = other.internal.lock().unwrap().clone();
|
||||
let mut original_psbt = self.internal.lock().unwrap().clone();
|
||||
|
||||
original_psbt.combine(other_psbt)?;
|
||||
Ok(Arc::new(PartiallySignedTransaction {
|
||||
internal: Mutex::new(original_psbt),
|
||||
}))
|
||||
}
|
||||
|
||||
/// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
|
||||
/// If the PSBT is missing a TxOut for an input returns None.
|
||||
pub(crate) fn fee_amount(&self) -> Option<u64> {
|
||||
self.internal.lock().unwrap().fee_amount()
|
||||
}
|
||||
|
||||
/// The transaction's fee rate. This value will only be accurate if calculated AFTER the
|
||||
/// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
|
||||
/// transaction.
|
||||
/// If the PSBT is missing a TxOut for an input returns None.
|
||||
pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
||||
self.internal.lock().unwrap().fee_rate().map(Arc::new)
|
||||
}
|
||||
|
||||
/// Serialize the PSBT data structure as a String of JSON.
|
||||
pub(crate) fn json_serialize(&self) -> String {
|
||||
let psbt = self.internal.lock().unwrap();
|
||||
serde_json::to_string(psbt.deref()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::wallet::{TxBuilder, Wallet};
|
||||
use bdk::wallet::get_funded_wallet;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[test]
|
||||
fn test_psbt_fee() {
|
||||
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||
let test_wallet = Wallet {
|
||||
wallet_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()
|
||||
.fee_rate(2.0)
|
||||
.drain_wallet()
|
||||
.drain_to(drain_to_script.clone());
|
||||
//dbg!(&tx_builder);
|
||||
assert!(tx_builder.drain_wallet);
|
||||
assert_eq!(tx_builder.drain_to, Some(drain_to_script.script.clone()));
|
||||
|
||||
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||
|
||||
assert!(tx_builder_result.psbt.fee_rate().is_some());
|
||||
assert_eq!(
|
||||
tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
|
||||
2.682927
|
||||
);
|
||||
|
||||
assert!(tx_builder_result.psbt.fee_amount().is_some());
|
||||
assert_eq!(tx_builder_result.psbt.fee_amount().unwrap(), 220);
|
||||
}
|
||||
}
|
||||
805
bdk-ffi/src/wallet.rs
Normal file
805
bdk-ffi/src/wallet.rs
Normal file
@@ -0,0 +1,805 @@
|
||||
use bdk::bitcoin::blockdata::script::Script as BdkScript;
|
||||
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk::database::any::AnyDatabase;
|
||||
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::{
|
||||
FeeRate, LocalUtxo as BdkLocalUtxo, SignOptions as BdkSignOptions,
|
||||
SyncOptions as BdkSyncOptions, Wallet as BdkWallet,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use crate::blockchain::Blockchain;
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::psbt::PartiallySignedTransaction;
|
||||
use crate::{
|
||||
AddressIndex, AddressInfo, Balance, BdkError, LocalUtxo, OutPoint, Progress, ProgressHolder,
|
||||
RbfValue, Script, ScriptAmount, TransactionDetails, TxBuilderResult,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Wallet {
|
||||
pub(crate) wallet_mutex: Mutex<BdkWallet<AnyDatabase>>,
|
||||
}
|
||||
|
||||
/// A Bitcoin wallet.
|
||||
/// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
|
||||
/// 1. Output descriptors from which it can derive addresses.
|
||||
/// 2. A Database where it tracks transactions and utxos related to the descriptors.
|
||||
/// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
|
||||
impl Wallet {
|
||||
pub(crate) fn new(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Option<Arc<Descriptor>>,
|
||||
network: Network,
|
||||
database_config: DatabaseConfig,
|
||||
) -> Result<Self, BdkError> {
|
||||
let any_database_config = match database_config {
|
||||
DatabaseConfig::Memory => AnyDatabaseConfig::Memory(()),
|
||||
DatabaseConfig::Sled { config } => AnyDatabaseConfig::Sled(config),
|
||||
DatabaseConfig::Sqlite { config } => AnyDatabaseConfig::Sqlite(config),
|
||||
};
|
||||
let database = AnyDatabase::from_config(&any_database_config)?;
|
||||
let descriptor: String = descriptor.as_string_private();
|
||||
let change_descriptor: Option<String> = change_descriptor.map(|d| d.as_string_private());
|
||||
|
||||
let wallet_mutex = Mutex::new(BdkWallet::new(
|
||||
&descriptor,
|
||||
change_descriptor.as_ref(),
|
||||
network,
|
||||
database,
|
||||
)?);
|
||||
Ok(Wallet { wallet_mutex })
|
||||
}
|
||||
|
||||
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet<AnyDatabase>> {
|
||||
self.wallet_mutex.lock().expect("wallet")
|
||||
}
|
||||
|
||||
/// Get the Bitcoin network the wallet is using.
|
||||
pub(crate) fn network(&self) -> Network {
|
||||
self.get_wallet().network()
|
||||
}
|
||||
|
||||
/// Sync the internal database with the blockchain.
|
||||
pub(crate) fn sync(
|
||||
&self,
|
||||
blockchain: &Blockchain,
|
||||
progress: Option<Box<dyn Progress>>,
|
||||
) -> Result<(), BdkError> {
|
||||
let bdk_sync_opts = BdkSyncOptions {
|
||||
progress: progress.map(|p| {
|
||||
Box::new(ProgressHolder { progress: p })
|
||||
as Box<(dyn bdk::blockchain::Progress + 'static)>
|
||||
}),
|
||||
};
|
||||
|
||||
let blockchain = blockchain.get_blockchain();
|
||||
self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
|
||||
}
|
||||
|
||||
/// Return a derived address using the external descriptor, see AddressIndex for available address index selection
|
||||
/// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
|
||||
/// then the same address will always be returned for any AddressIndex.
|
||||
pub(crate) fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, BdkError> {
|
||||
self.get_wallet()
|
||||
.get_address(address_index.into())
|
||||
.map(AddressInfo::from)
|
||||
}
|
||||
|
||||
/// Return a derived address using the internal (change) descriptor.
|
||||
///
|
||||
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
|
||||
///
|
||||
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
|
||||
/// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
|
||||
/// be returned for any [`AddressIndex`].
|
||||
pub(crate) fn get_internal_address(
|
||||
&self,
|
||||
address_index: AddressIndex,
|
||||
) -> Result<AddressInfo, BdkError> {
|
||||
self.get_wallet()
|
||||
.get_internal_address(address_index.into())
|
||||
.map(AddressInfo::from)
|
||||
}
|
||||
|
||||
/// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
|
||||
/// on the internal database, which first needs to be Wallet.sync manually.
|
||||
pub(crate) fn get_balance(&self) -> Result<Balance, BdkError> {
|
||||
self.get_wallet().get_balance().map(|b| b.into())
|
||||
}
|
||||
|
||||
/// Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
/// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that
|
||||
/// has the value true if the PSBT was finalized, or false otherwise.
|
||||
///
|
||||
/// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
|
||||
/// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
/// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
|
||||
/// in this library will.
|
||||
pub(crate) fn sign(
|
||||
&self,
|
||||
psbt: &PartiallySignedTransaction,
|
||||
sign_options: Option<SignOptions>,
|
||||
) -> Result<bool, BdkError> {
|
||||
let mut psbt = psbt.internal.lock().unwrap();
|
||||
self.get_wallet().sign(
|
||||
&mut psbt,
|
||||
sign_options.map(SignOptions::into).unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
|
||||
pub(crate) fn list_transactions(
|
||||
&self,
|
||||
include_raw: bool,
|
||||
) -> Result<Vec<TransactionDetails>, BdkError> {
|
||||
let transaction_details = self.get_wallet().list_transactions(include_raw)?;
|
||||
Ok(transaction_details
|
||||
.into_iter()
|
||||
.map(TransactionDetails::from)
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
|
||||
/// which first needs to be Wallet.sync manually.
|
||||
pub(crate) fn list_unspent(&self) -> Result<Vec<LocalUtxo>, BdkError> {
|
||||
let unspents: Vec<BdkLocalUtxo> = self.get_wallet().list_unspent()?;
|
||||
Ok(unspents.into_iter().map(LocalUtxo::from).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for a software signer
|
||||
///
|
||||
/// Adjust the behavior of our software signers and the way a transaction is finalized
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SignOptions {
|
||||
/// Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been
|
||||
/// provided
|
||||
///
|
||||
/// Defaults to `false` to mitigate the "SegWit bug" which should trick the wallet into
|
||||
/// paying a fee larger than expected.
|
||||
///
|
||||
/// Some wallets, especially if relatively old, might not provide the `non_witness_utxo` for
|
||||
/// SegWit transactions in the PSBT they generate: in those cases setting this to `true`
|
||||
/// should correctly produce a signature, at the expense of an increased trust in the creator
|
||||
/// of the PSBT.
|
||||
///
|
||||
/// For more details see: <https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd>
|
||||
pub trust_witness_utxo: bool,
|
||||
|
||||
/// Whether the wallet should assume a specific height has been reached when trying to finalize
|
||||
/// a transaction
|
||||
///
|
||||
/// The wallet will only "use" a timelock to satisfy the spending policy of an input if the
|
||||
/// timelock height has already been reached. This option allows overriding the "current height" to let the
|
||||
/// wallet use timelocks in the future to spend a coin.
|
||||
pub assume_height: Option<u32>,
|
||||
|
||||
/// Whether the signer should use the `sighash_type` set in the PSBT when signing, no matter
|
||||
/// what its value is
|
||||
///
|
||||
/// Defaults to `false` which will only allow signing using `SIGHASH_ALL`.
|
||||
pub allow_all_sighashes: bool,
|
||||
|
||||
/// Whether to remove partial signatures from the PSBT inputs while finalizing PSBT.
|
||||
///
|
||||
/// Defaults to `true` which will remove partial signatures during finalization.
|
||||
pub remove_partial_sigs: bool,
|
||||
|
||||
/// Whether to try finalizing the PSBT after the inputs are signed.
|
||||
///
|
||||
/// Defaults to `true` which will try finalizing PSBT after inputs are signed.
|
||||
pub try_finalize: bool,
|
||||
|
||||
// Specifies which Taproot script-spend leaves we should sign for. This option is
|
||||
// ignored if we're signing a non-taproot PSBT.
|
||||
//
|
||||
// Defaults to All, i.e., the wallet will sign all the leaves it has a key for.
|
||||
// TODO pub tap_leaves_options: TapLeavesOptions,
|
||||
/// Whether we should try to sign a taproot transaction with the taproot internal key
|
||||
/// or not. This option is ignored if we're signing a non-taproot PSBT.
|
||||
///
|
||||
/// Defaults to `true`, i.e., we always try to sign with the taproot internal key.
|
||||
pub sign_with_tap_internal_key: bool,
|
||||
|
||||
/// Whether we should grind ECDSA signature to ensure signing with low r
|
||||
/// or not.
|
||||
/// Defaults to `true`, i.e., we always grind ECDSA signature to sign with low r.
|
||||
pub allow_grinding: bool,
|
||||
}
|
||||
|
||||
impl From<SignOptions> for BdkSignOptions {
|
||||
fn from(sign_options: SignOptions) -> Self {
|
||||
BdkSignOptions {
|
||||
trust_witness_utxo: sign_options.trust_witness_utxo,
|
||||
assume_height: sign_options.assume_height,
|
||||
allow_all_sighashes: sign_options.allow_all_sighashes,
|
||||
remove_partial_sigs: sign_options.remove_partial_sigs,
|
||||
try_finalize: sign_options.try_finalize,
|
||||
tap_leaves_options: Default::default(),
|
||||
sign_with_tap_internal_key: sign_options.sign_with_tap_internal_key,
|
||||
allow_grinding: sign_options.allow_grinding,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A transaction builder.
|
||||
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
|
||||
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TxBuilder {
|
||||
pub(crate) recipients: Vec<(BdkScript, u64)>,
|
||||
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<f32>,
|
||||
pub(crate) fee_absolute: Option<u64>,
|
||||
pub(crate) drain_wallet: bool,
|
||||
pub(crate) drain_to: Option<BdkScript>,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a recipient to the internal list.
|
||||
pub(crate) fn add_recipient(&self, script: Arc<Script>, amount: u64) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScript, u64)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.script.clone(), amount)]);
|
||||
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.script.clone(), script_amount.amount))
|
||||
.collect();
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent"
|
||||
/// utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details.
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
/// Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable"
|
||||
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
|
||||
pub(crate) fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
|
||||
self.add_utxos(vec![outpoint])
|
||||
}
|
||||
|
||||
/// Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding
|
||||
/// any of the UTXOs then none of them are added and the error is returned. These have priority over the "unspendable"
|
||||
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
/// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable.
|
||||
pub(crate) fn do_not_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::ChangeForbidden,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are
|
||||
/// needed to make the transaction valid.
|
||||
pub(crate) fn manually_selected_only(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
manually_selected_only: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See TxBuilder.unspendable.
|
||||
pub(crate) fn only_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::OnlyChange,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos added with
|
||||
/// TxBuilder.addUtxo have priority over these. See the Rust docs of the two linked methods for more details.
|
||||
pub(crate) fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
unspendable: unspendable.into_iter().collect(),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Set a custom fee rate.
|
||||
pub(crate) fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_rate: Some(sat_per_vb),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Set an absolute fee.
|
||||
pub(crate) fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_absolute: Some(fee_amount),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
|
||||
pub(crate) fn drain_wallet(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
drain_wallet: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the address to drain excess coins to. Usually, when there are excess coins they are sent to a change address
|
||||
/// generated by the wallet. This option replaces the usual change address with an arbitrary ScriptPubKey of your choosing.
|
||||
/// Just as with a change output, if the drain output is not needed (the excess coins are too small) it will not be included
|
||||
/// in the resulting transaction. The only difference is that it is valid to use drain_to without setting any ordinary recipients
|
||||
/// with add_recipient (but it is perfectly fine to add recipients as well). If you choose not to set any recipients, you should
|
||||
/// either provide the utxos that the transaction should spend via add_utxos, or set drain_wallet to spend all of them.
|
||||
/// When bumping the fees of a transaction made with this option, you probably want to use BumpFeeTxBuilder.allow_shrinking
|
||||
/// to allow this output to be reduced to pay for the extra fees.
|
||||
pub(crate) fn drain_to(&self, script: Arc<Script>) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
drain_to: Some(script.script.clone()),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
|
||||
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
rbf: Some(RbfValue::Default),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
|
||||
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
|
||||
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
rbf: Some(RbfValue::Value(nsequence)),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Add data as an output using OP_RETURN.
|
||||
pub(crate) fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
data,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<TxBuilderResult, BdkError> {
|
||||
let 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();
|
||||
let utxos: &[BdkOutPoint] = &bdk_utxos;
|
||||
tx_builder.add_utxos(utxos)?;
|
||||
}
|
||||
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(sat_per_vb) = self.fee_rate {
|
||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
|
||||
}
|
||||
if let Some(fee_amount) = self.fee_absolute {
|
||||
tx_builder.fee_absolute(fee_amount);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
if !&self.data.is_empty() {
|
||||
tx_builder.add_data(self.data.as_slice());
|
||||
}
|
||||
|
||||
tx_builder
|
||||
.finish()
|
||||
.map(|(psbt, tx_details)| TxBuilderResult {
|
||||
psbt: Arc::new(PartiallySignedTransaction {
|
||||
internal: Mutex::new(psbt),
|
||||
}),
|
||||
transaction_details: TransactionDetails::from(tx_details),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BumpFeeTxBuilder {
|
||||
pub(crate) txid: String,
|
||||
pub(crate) fee_rate: f32,
|
||||
pub(crate) allow_shrinking: Option<String>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
}
|
||||
|
||||
impl BumpFeeTxBuilder {
|
||||
pub(crate) fn new(txid: String, fee_rate: f32) -> Self {
|
||||
Self {
|
||||
txid,
|
||||
fee_rate,
|
||||
allow_shrinking: None,
|
||||
rbf: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey
|
||||
/// in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to
|
||||
/// shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved
|
||||
/// then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey
|
||||
/// can’t be found among the recipients of the transaction we are bumping.
|
||||
pub(crate) fn allow_shrinking(&self, address: String) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
allow_shrinking: Some(address),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
|
||||
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rbf: Some(RbfValue::Default),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
|
||||
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
|
||||
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
rbf: Some(RbfValue::Value(nsequence)),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||
pub(crate) fn finish(
|
||||
&self,
|
||||
wallet: &Wallet,
|
||||
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
let wallet = wallet.get_wallet();
|
||||
let txid = Txid::from_str(self.txid.as_str())?;
|
||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
|
||||
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||
let address = BdkAddress::from_str(allow_shrinking)
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
let script = address.script_pubkey();
|
||||
tx_builder.allow_shrinking(script)?;
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
tx_builder
|
||||
.finish()
|
||||
.map(|(psbt, _)| PartiallySignedTransaction {
|
||||
internal: Mutex::new(psbt),
|
||||
})
|
||||
.map(Arc::new)
|
||||
}
|
||||
}
|
||||
|
||||
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::database::DatabaseConfig;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::wallet::{AddressIndex, TxBuilder, Wallet};
|
||||
use bdk::bitcoin::{Address, Network};
|
||||
use bdk::wallet::get_funded_wallet;
|
||||
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 {
|
||||
wallet_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.script.clone()));
|
||||
|
||||
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||
let psbt = tx_builder_result.psbt.internal.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"
|
||||
);
|
||||
}
|
||||
}
|
||||
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.8.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 bdk
|
||||
|
||||
let network = Network.testnet
|
||||
BIN
bdk-ffi/tests/jna/jna-5.8.0.jar
Normal file
BIN
bdk-ffi/tests/jna/jna-5.8.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-bindgen.rs
Normal file
3
bdk-ffi/uniffi-bindgen.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
||||
100
bdk-jvm/README.md
Normal file
100
bdk-jvm/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# bdk-android
|
||||
This project builds a .jar package for the JVM 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 JVM project add the following to your gradle dependencies:
|
||||
```kotlin
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||
```kotlin
|
||||
import org.bitcoindevkit.*
|
||||
|
||||
// ...
|
||||
|
||||
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
|
||||
|
||||
val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
|
||||
)
|
||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
||||
```
|
||||
|
||||
### 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.6.10` 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. If building on macOS install required intel and m1 jvm targets
|
||||
```sh
|
||||
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||
```
|
||||
4. Build kotlin bindings
|
||||
```sh
|
||||
# build JVM library
|
||||
./gradlew buildJvmLib
|
||||
```
|
||||
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-jvm
|
||||
./gradlew publishToMavenLocal --exclude-task signMavenPublication
|
||||
```
|
||||
|
||||
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 excluding the signing task:
|
||||
```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
|
||||
27
bdk-jvm/build.gradle.kts
Normal file
27
bdk-jvm/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
||||
plugins {
|
||||
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=0.28.0
|
||||
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.
5
bdk-jvm/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
bdk-jvm/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
bdk-jvm/gradlew
vendored
Executable file
234
bdk-jvm/gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/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/master/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
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
|
||||
# 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
|
||||
which java >/dev/null 2>&1 || 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
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
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
|
||||
|
||||
# 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 \
|
||||
"$@"
|
||||
|
||||
# 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" "$@"
|
||||
89
bdk-jvm/gradlew.bat
vendored
Normal file
89
bdk-jvm/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@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=.
|
||||
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%" == "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%"=="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!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
110
bdk-jvm/lib/build.gradle.kts
Normal file
110
bdk-jvm/lib/build.gradle.kts
Normal file
@@ -0,0 +1,110 @@
|
||||
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") version "1.6.10"
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
id("signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-jvm-bindings")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
|
||||
testLogging {
|
||||
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
|
||||
exceptionFormat = FULL
|
||||
showExceptions = true
|
||||
showCauses = true
|
||||
showStackTraces = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
|
||||
implementation("net.java.dev.jna:jna:5.8.0")
|
||||
api("org.slf4j:slf4j-api:1.7.30")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
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("notmandatory")
|
||||
name.set("Steve Myers")
|
||||
email.set("notmandatory@noreply.github.org")
|
||||
}
|
||||
developer {
|
||||
id.set("artfuldev")
|
||||
name.set("Sudarsan Balaji")
|
||||
email.set("sudarsan.balaji@artfuldev.com")
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
73
bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
Normal file
73
bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Library test, which will execute on linux host.
|
||||
*/
|
||||
class JvmLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
return Files.createTempDirectory("bdk-test").toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.New).address.asString()
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
}
|
||||
}
|
||||
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,14 @@
|
||||
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,116 @@
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
libsToCopy.forEach {
|
||||
doFirst {
|
||||
copy {
|
||||
with(it) {
|
||||
from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/libbdkffi.${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)
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
4
bdk-jvm/settings.gradle.kts
Normal file
4
bdk-jvm/settings.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
||||
rootProject.name = "bdk-jvm"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
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
|
||||
2
bdk-python/MANIFEST.in
Normal file
2
bdk-python/MANIFEST.in
Normal file
@@ -0,0 +1,2 @@
|
||||
include ./src/bdkpython/libbdkffi.dylib
|
||||
include ./src/bdkpython/libbdkffi.so
|
||||
48
bdk-python/README.md
Normal file
48
bdk-python/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 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 ./generate.sh
|
||||
python setup.py bdist_wheel --verbose
|
||||
pip install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
|
||||
python -m unittest --verbose tests/test_bdk.py
|
||||
```
|
||||
|
||||
## Build the package
|
||||
```shell
|
||||
# Install dependencies
|
||||
pip install --requirement requirements.txt
|
||||
|
||||
# Generate the bindings
|
||||
bash generate.sh
|
||||
|
||||
# Build the wheel
|
||||
python setup.py --verbose bdist_wheel
|
||||
```
|
||||
|
||||
## Run tox to build and test locally
|
||||
```shell
|
||||
# install dev requirements
|
||||
pip install --requirement requirements-dev.txt
|
||||
|
||||
# build bindings glue code (located at ./src/bdkpython/bdk.py)
|
||||
source ./generate.sh
|
||||
|
||||
# build and test
|
||||
tox -vv
|
||||
```
|
||||
|
||||
## Install locally
|
||||
```shell
|
||||
pip install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||
```
|
||||
24
bdk-python/generate.sh
Normal file
24
bdk-python/generate.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
OS=$(uname -s)
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Generating native binaries..."
|
||||
cargo build --profile release-smaller
|
||||
case $OS in
|
||||
"Darwin")
|
||||
echo "Copying macOS libbdkffi.dylib..."
|
||||
cp ../target/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
;;
|
||||
"Linux")
|
||||
echo "Copying linux libbdkffi.so..."
|
||||
cp ../target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
|
||||
;;
|
||||
esac
|
||||
cd ../bdk-python/
|
||||
|
||||
echo "All done!"
|
||||
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 = [
|
||||
"."
|
||||
]
|
||||
2
bdk-python/requirements-dev.txt
Normal file
2
bdk-python/requirements-dev.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
pytest==7.1.2
|
||||
tox==3.25.1
|
||||
4
bdk-python/requirements.txt
Normal file
4
bdk-python/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
semantic-version==2.9.0
|
||||
typing_extensions==4.0.1
|
||||
setuptools==67.4.0
|
||||
wheel==0.38.4
|
||||
69
bdk-python/setup.py
Normal file
69
bdk-python/setup.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
LONG_DESCRIPTION = """# bdkpython
|
||||
The Python language bindings for the [Bitcoin Dev Kit](https://github.com/bitcoindevkit).
|
||||
|
||||
## Install the package
|
||||
```shell
|
||||
pip install bdkpython
|
||||
```
|
||||
|
||||
## Simple example
|
||||
```python
|
||||
import bdkpython as bdk
|
||||
|
||||
|
||||
descriptor = bdk.Descriptor("wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)", bdk.Network.TESTNET)
|
||||
db_config = bdk.DatabaseConfig.MEMORY()
|
||||
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||
bdk.ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
None,
|
||||
5,
|
||||
None,
|
||||
100,
|
||||
True,
|
||||
)
|
||||
)
|
||||
blockchain = bdk.Blockchain(blockchain_config)
|
||||
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config,
|
||||
)
|
||||
|
||||
# print new receive address
|
||||
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
|
||||
address = address_info.address
|
||||
index = address_info.index
|
||||
print(f"New BIP84 testnet address: {address} at index {index}")
|
||||
|
||||
|
||||
# print wallet balance
|
||||
wallet.sync(blockchain, None)
|
||||
balance = wallet.get_balance()
|
||||
print(f"Wallet balance is: {balance.total}")
|
||||
"""
|
||||
|
||||
setup(
|
||||
name="bdkpython",
|
||||
version="0.28.3",
|
||||
description="The Python language bindings for the Bitcoin Development Kit",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type="text/markdown",
|
||||
include_package_data = True,
|
||||
zip_safe=False,
|
||||
packages=["bdkpython"],
|
||||
package_dir={"bdkpython": "./src/bdkpython"},
|
||||
url="https://github.com/bitcoindevkit/bdk-ffi",
|
||||
author="Alekos Filini <alekos.filini@gmail.com>, Steve Myers <steve@notmandatory.org>",
|
||||
license="MIT or Apache 2.0",
|
||||
|
||||
# This is required to ensure the library name includes the python version, abi, and platform tags
|
||||
# See issue #350 for more information
|
||||
has_ext_modules=lambda: True,
|
||||
)
|
||||
17
bdk-python/shell.nix
Normal file
17
bdk-python/shell.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
with import <nixpkgs> {};
|
||||
|
||||
mkShell {
|
||||
name = "bdk-python-shell";
|
||||
packages = [ ( import ./nix/uniffi_bindgen.nix ) ];
|
||||
buildInputs = with python37.pkgs; [
|
||||
pip
|
||||
setuptools
|
||||
];
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH=${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH
|
||||
alias pip="PIP_PREFIX='$(pwd)/_build/pip_packages' \pip"
|
||||
export PYTHONPATH="$(pwd)/_build/pip_packages/lib/python3.7/site-packages:$(pwd):$PYTHONPATH"
|
||||
export PATH="$(pwd)/_build/pip_packages/bin:$PATH"
|
||||
unset SOURCE_DATE_EPOCH
|
||||
'';
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user