From c11e17b5e2db148c595e42b5919260be3eb935f4 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Sat, 25 Sep 2021 21:17:40 -0700 Subject: [PATCH] Upgrade bdk dependency to 0.11 --- .gitignore | 3 +- Cargo.toml | 2 +- README.md | 12 ++ bdk-kotlin/gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 +- bdk-kotlin/gradlew | 185 ++++++++++++++++++ bdk-kotlin/gradlew.bat | 89 +++++++++ .../kotlin/org/bitcoindevkit/bdk/LibJna.kt | 57 ++++-- .../bdk/wallet/BlockchainConfig.kt | 5 +- .../org/bitcoindevkit/bdk/wallet/Wallet.kt | 9 + .../kotlin/org/bitcoindevkit/bdk/LibTest.kt | 35 ++-- build.sh | 162 +++++++++------ cc/bdk_ffi.h | 35 +++- cc/bdk_ffi_test.c | 59 +++--- src/error.rs | 11 +- src/wallet/blockchain.rs | 2 + src/wallet/mod.rs | 10 +- src/wallet/transaction.rs | 43 +++- test.sh | 64 +++++- 19 files changed, 625 insertions(+), 163 deletions(-) create mode 100755 bdk-kotlin/gradlew create mode 100644 bdk-kotlin/gradlew.bat diff --git a/.gitignore b/.gitignore index d295440..17d844a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Cargo.lock .gradle wallet_db bdk_ffi_test -local.properties \ No newline at end of file +local.properties +*.log \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c8afbd2..cb9787c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" crate-type = ["cdylib"] [dependencies] -bdk = { version = "^0.7", features = ["all-keys"] } +bdk = { version = "^0.11", features = ["all-keys"] } safer-ffi = { version = "0.0.6", features = ["proc_macros"]} [features] diff --git a/README.md b/README.md index 6d3f227..2935e1c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ +Setup build environment +1. Add Android rust targets + +```sh +rustup target add x86_64-apple-darwin x86_64-unknown-linux-gnu x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android +``` + +2. Set ANDROID_NDK_HOME + +```sh +export ANDROID_NDK_HOME=/home//Android/Sdk/ndk/ +``` Adding new structs and functions diff --git a/bdk-kotlin/gradle/wrapper/gradle-wrapper.jar b/bdk-kotlin/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 6656 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 Yte!%o?r4GTrVtxD61Ym!|5fq-1K$0e!T1w z1SC8j)_dObefzK9b=~*c&wBRW>;B{VGKiBofK!FMN5oJBE0V;;!kWUz!jc1W?5KdY zyZ3mCBHprpchz-9{ASiJJh&&h1|4rdw6wxD2+9= z#6#}Uq8&^1F3wgvGFoNDo?bIeEQXpcuAR0-+w$JWoK-@yUal1M&~W_O)r+Rx;{@hWH5n^oQWR36GMYBDDZyPK4L@WVjRrF+XlSzi4X4!_!U%Uujl6LHQ#|l(sUU%{ zefYd8jnVYP91K}Qn-OmmSLYFK1h~_}RPS~>+Xdz%dpvpJ{ll!IKX=JN99qowqslbO zV3DmqPZ}6>KB!9>jEObpi$u5oGPfO3O5!o3N2Mn`ozpje<}1I1H)m2rJDcB7AwXc6 z6j)tnPiql7#)r+b+p9?MVahp&=qJ^$oG+a^C*);FoJ!+V*^W+|2Olx5{*&$bXth)U zejc7mU6cBp?^Rj|dd{GL-0eHRTBi6_yJ&GLP5kIncv^z{?=0AVy^5{S8_n=rtua!J zFGY=A(yV^ZhB}1J_y(F`3QTu+zkHlw;1GiFeP&pw0N1k%NShHlO(4W+(!wy5phcg4 zA-|}(lE_1@@e6y`veg;v7m;q%(PFG&K3#}eRhJioXUU0jg_8{kn$;KVwf;zpL2X_( zC*_R#5*PaBaY73(x*oZ}oE#HPLJQRQ7brNK=v!lsu==lSG1(&q>F)`adBT~d*lMS| z%!%7(p~<7kWNmpZ5-N31*e=8`kih|g5lVrI%2wnLF-2D+G4k6@FrYsJ_80AJ}KMRi>) z-kIeHp{maorNWkF81v0FKgB==_6blyaF$5GaW)B!i4v*jNk6r)vU6?G$0pV8(Y+UK z5lgRVt%;N_gWp)^osv=h+^07UY6+$4^#t=M3>0i0`{`aEkFLL#a)93uXhYO+aKTtu zckg2T9S&GKNtZmdAS^8PzvDva-%-K&g9eqPXQ4$dM^inr@6Zl z{!Cq&C_+V;g*{>!0cZP}?ogDb$#ZS=n@NHE{>k@84lOkl&$Bt2NF)W%GClViJq14_ zQIfa^q+0aq){}CO8j%g%R9|;G0uJuND*HO$2i&U_uW_a5xJ33~(Vy?;%6_(2_Cuq1 zLhThN@xH7-BaNtkKTn^taQHrs$<<)euc6z(dhps>SM;^Wx=7;O&IfNVJq3wk4<1VS z-`*7W4DR_i^W4=dRh>AXi~J$K>`UqP>CKVVH&+T(ODhRJZO7DScU$F7D)di-%^8?O z6)Ux`zdrVOe1GNkPo0FgrrxSu1AGQkJe@pqu}8LkBDm+V!N_1l}`tjLW8${rgDLv3m@E*#zappt-Mm zSC<$o+6UO~w0C=(0$&*y**@nKe_Q{|eAuD!(0YL0_a{z%+sdfSyP={Nyd$re6Rzbp zvsgTY7~VflX0^Vf7qqomYZ_$ryrFVV2$sFyzw2r%Q8*uYDA+)iQdfKms_5(>!s#!( z!P5S(N0i9CKQKaqg(U%Gk#V3*?)lO6dLv`8KB~F<-%VhbtL8Rl>mEz+PN=qx&t*|= zQHV=qG)YKlPk4iCyWIUGjC?kpeA>hIBK*A?B0)rB=RqAal#D%1C9yVQwBcz${#Jb5 zR{TRmMrOrJsLc&6x9qDo@FJ^=do_Y?3oU0G^nV5_EU&+DS+VA7Tp{^TAF>yZbyM3c zf*1CqHY9T|aL_lyY7c)i!_MtGPA!sdy3|mrsKVj1mi&>dms@-ozSa}OZ?2I*tAndg z@S7er$t^d^-;!wLQbG60nWd@1pQVD7tw-G_B#OscoYyremiZ_hj8*sXqQdchuD^!R zpXGuSj5psk+jR>3rWu3^`17>j&*^9^rWbszP=Mf@5KIEj%b=z98v=Ymp%$FYt>%Ld zm8})EDbNOJu9n)gwhz_RS``#Ag)fr)3<*?(!9O~mTQWeh;8c;0@o=iBLQNqx3d_2#W7S9#FXzr6VXfs>4 z;QXw}-STvK9_-7H=uqgal2{GkbjVLN+=D5ddd)4^WvX;(NYA*X*(JxTdiUzqVJopd zQg#~psX4o<)cF>r=rxP`(Xsf<+HG-pf&7aFPL8z|-&B*P?Vmsu5d>Nlg^2$WRY!S@#`g2{81;(1w#o5HsvN}5pFZi});>|VK^kL{Zkx~wgn ztlZp;HW`H8(GdRfIwc~?#N6}o#h158ohI*GIsK%56I_9sf2k_K@4vD!l{(dX9E7PJ;w>$|Y;-VBJSO4@){07bo-89^LZ9g<<%;dOl zyIq{s8`8Ltp*GDwu(l_Z$6sA2nam$BM$Q~6TpZg)w2TtW?G5whV(lRwaf$6EU86is zBP9Rs&vS_~sk?Nn_b}^HkM8LiO@>J}=g(T4hLmvH@5Jj#2aHa~K)lD9VB0k>$V2BP zgh;(=y9Op(KQ=H5vj+%qs>?s4tYN~-Q|fyQePA)s?HrF~;l!+@t8VMzqUpqMLudFT z)=o~s!MM4XkgbetIsODwtQ=FF$IcIp&!pjh6Q6{tL+l*7GQ%8Wsg(tC#qU3oW$~n) zL=>XIxI}Hi7HS0F_mmi+(c%1HDuKiWm>|6Xa}nW7ei55ggru9)xjBvC#JcEIN*#cp zv*ACvr=HTC?dX9NNo9Yhulu_gX5Z~}QQ2&QZ&C77{(>Y3_ z6j5Z1Uc5FtPEpS_31HsgmSLHZijGb_p$WlRJ1p^_1!ZLP8kr6OtCEK7Qh267o$H>e zf<4cNGQRk{g5h$XfvTFQ@`qm@iju83-~}ebAYpZryARHVR$AEt3229U{y@Fp4 z-8FBBtGG&(hTyUdx5ZOfiz`c=<0F%+w|Fl=rWk{K7>70k04SN?RU(^mrKSeKDqA!K^Hsv8C?#ioj4@WUL zC*?{hTai6q0%_oBTqDHygp_Kl;({sAScYQIwMDM1U>{x0ww zve?_}E;DG?+|zsUrsph5X_G7l#Y~vqkq3@NNDabbw7|`eJBmn`Qrlr%?`va=mm$Mc{+FBbQbogAZ6{MuzT|P%QZZotd21eb1hfj|;GYAX&>bx#D5EB+=XMj2XJkpnyMUykaVo) zj3ZLqEl1&)Rturc8m@+uUuD^vaNaSxGwP4dq0-OSb~62lPv8E_K4usLvG{Qg zdR%z8dd2H!{JaT|X_bfm{##*W$YM;_J8Y8&Z)*ImOAf4+| zEyi)qK%Ld1bHuqD+}-WiCnjszDeC-%8g+8JRpG1bOc!xUGB?@?6f~FTrI%U#5R~YF z%t5(S2Q>?0`(XNHa8xKdTEZ~Z4SJOheit#ldfdg63}#W6j8kO;SjQD`vftxS+#x1B zYu|5szEvkyz|}|B3x|DNlyi$;+n+cW$Hu+?)=X1!sa%{H-^;oBO9XACZJ}wkQ!sTa zQ#J3h|HX{{&WwIG3h7d6aWktuJaO)ie6&=KJBoX@w(rBWfin`*a6OmCC5M0HzL(gv zY<*e4hmW>SWVhxk-`UGOAbD%Hk+uu<^7zJ_ytVXamfqCd0$g+W08>?QAB}Cv{b}eM z@X}ILg+uT%>-6`A25p@uhS3%;u>ccSq}8|H_^o&`nBT5S0y z;2H0I^(4MO*S+(4l$gULc4KSeKvidto5Nl0P|%9CqQ*ikY!w_GUlo}sb9HYB=L^oFpJ zfTQskXW!LFVnUo4(OHPDaZSf3zB|3{RGu1>ueE$(+dr?tT zp!SGlqDU8vu{5xLWSvj+j$arHglg54#Lx&TvuO3LIIU>hF9Uoj&=-b*Q?uYr`#V?xz?2 zhirZrv^eA{k%{hFh%9LYVXEYWd5#PuUd1QqaqB*J!CMXEM>fEB$@#1>mtB`Bfil}t zhhTIObqh5HRvT+4q_Do$Q*Jika?qV=Np-DtPkU z(KoXyWLfPwr@UY1)hBAvR3nCBZgd|CevTG?H~HqDF}dzy%2sd2`f{^CBbTk*^K~RO zN~O0+2EjAJlywF%SjgYz810l&G5AqzI<=Ber{912^PpSPRJl3dm8W@dKHL}7_@k3)Y!SXYkyxQy>Q4I2o zr`ev7fLF$1t96h|sH<-#*YzGD-b^3$_!#wsh(Yw;)b@udLz9mm`mFYh z1Zz24KIQJ(*_-E0(3&1InqG;U?wF)GYd>DFo(em`#|UaaYmkA9;GTX7b?0@C@QkTVpGD#mf$dQoRNV=n{^Zi_W*ps;3?^$s`0;ER7;==~OmQ~9 zS5P=FjxE5%|;xq6h4@!_h?@|aK&FYI2IT(OHXv2%1 zWEo-v!L7x^YT(xLVHlpJttcwaF@1Y;-S*q3CRa!g7xdzl|Jan>2#dI0`LKl!T1GMk zRKe4|bQO&ET}Z^Aiym*HII>cSxIzl|F~JEUGxz;+DB=8fxXhnBI4R12q6ews$lA`Jfi}r@A@-)6TOAUMNYFYJ zZ-Zd?lxFTyjN3mXnL!%#>Z%$0gJ4*9g;e;@zSmQ{eGGDaRRNM3s@6!;hYuVc=c+3B z=qzNNS~n^EsJU4aOGE|mdy={C^lPKEfPL-IJAsTpQsDgZ@~s+eHZYmp9yb=YW_4r?lqQaYZQ`nau){W`LY#P)>i zq^wHEuOYs#FlPZeMuT@Etb@~A6feCebq`miJE3w+gAL%bVF_s*5e*@)?xmKSo%I3? zLELHVdWia$}~s6 zr!^LfxSSB4Td&9iTXrzQpl5ZDo#SdmNr;23QsPHQ!x!UT9xtb!Ycz^JF8x)%cFOXK z^EXw%dRz_VD}7?RU^4{)1+xFO=z!EI8IUa3U*rag=1BpHX$Xi<__kSbS{y_xa*MJv z_`thq0Z^sPzjAk48ssDQj}!$N8Q$XC84(bU$t_Bm69Jf+C!h_}ep zwzpQj9sRA94<{x3{~z&ix-DwX;RAzka)4-#6ZHJqKh|SVuO|>Yrv+m30+!|sK<-|E z=)5E->#y<_1V|T1f%Af!ZYqXg}`O zI$qKOWdnclF`%_Z`WGOe{`A`l-#a?s=Q1a#@BOWmExH2;Wl`OB!B-%lq3nO{4=WO& z#k_x|N&(qzm*6S{G*|GCegF2N2ulC+(58z2DG~yUs}i8zvRf&$CJCaexJ6Xu!`qz( z)*v8*kAE#D0KCo*s{8^Rbg=`*E2MzeIt0|x55%n-gO&yX#$l=3W7-_~&(G8j1E(XB hw}tl`5K!1C(72%nnjQrp<7@!WCh47rWB+@R{{wClNUHz< diff --git a/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties b/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties index 382d831..0f80bbf 100644 --- a/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Jun 10 21:43:37 PDT 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/bdk-kotlin/gradlew b/bdk-kotlin/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/bdk-kotlin/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$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 "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/bdk-kotlin/gradlew.bat b/bdk-kotlin/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/bdk-kotlin/gradlew.bat @@ -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 diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt index 3fd4cbe..19d2e43 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/LibJna.kt @@ -61,12 +61,14 @@ interface LibJna : Library { // char const * url, // char const * socks5, // int16_t retry, - // int16_t timeout); + // int16_t timeout, + // size_t stop_gap); fun new_electrum_config( url: String, socks5: String?, retry: Short, - timeout: Short + timeout: Short, + stop_gap: Long, ): BlockchainConfig_t // void free_blockchain_config ( @@ -108,11 +110,13 @@ interface LibJna : Library { // FfiResult_OpaqueWallet_ptr_t new_wallet_result ( // char const * descriptor, // char const * change_descriptor, + // char const * network, // BlockchainConfig_t const * blockchain_config, // DatabaseConfig_t const * database_config); fun new_wallet_result( descriptor: String, changeDescriptor: String?, + network: String, blockchainConfig: BlockchainConfig_t, databaseConfig: DatabaseConfig_t, ): FfiResult_OpaqueWallet_ptr_t.ByValue @@ -288,21 +292,33 @@ interface LibJna : Library { // void free_database_config ( // DatabaseConfig_t * database_config); fun free_database_config(database_config: DatabaseConfig_t) - + // typedef struct { - // - // char * txid; - // + // uint32_t height; // uint64_t timestamp; - // + // } ConfirmationTime_t; + open class ConfirmationTime_t : Structure() { + + class ByValue : ConfirmationTime_t(), Structure.ByValue + class ByReference : ConfirmationTime_t(), Structure.ByReference + + @JvmField + var height: Int? = null + + @JvmField + var timestamp: Long? = null + + override fun getFieldOrder() = listOf("height", "timestamp") + } + + // typedef struct { + // char * txid; // uint64_t received; - // // uint64_t sent; - // - // uint64_t fees; - // - // int32_t height; - // + // int64_t fee; + // bool is_confirmed; + // ConfirmationTime_t confirmation_time; + // bool verified; // } TransactionDetails_t; open class TransactionDetails_t : Structure() { @@ -312,9 +328,6 @@ interface LibJna : Library { @JvmField var txid: String? = null - @JvmField - var timestamp: Long? = null - @JvmField var received: Long? = null @@ -322,12 +335,18 @@ interface LibJna : Library { var sent: Long? = null @JvmField - var fees: Long? = null + var fee: Long? = null + + @JvmField + var is_confirmed: Boolean? = null @JvmField - var height: Int? = null + var confirmation_time: ConfirmationTime_t? = null + + @JvmField + var verified: Boolean? = null - override fun getFieldOrder() = listOf("txid", "timestamp", "received", "sent", "fees", "height") + override fun getFieldOrder() = listOf("txid", "received", "sent", "fee", "is_confirmed", "confirmation_time", "verified") } // typedef struct { diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/BlockchainConfig.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/BlockchainConfig.kt index 592f299..5a7f5db 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/BlockchainConfig.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/BlockchainConfig.kt @@ -17,9 +17,10 @@ class ElectrumConfig( url: String, socks5: String?, retry: Short, - timeout: Short + timeout: Short, + stopGap: Long, ) : BlockchainConfig() { private val log: Logger = LoggerFactory.getLogger(ElectrumConfig::class.java) - override val blockchainConfigT = libJna.new_electrum_config(url, socks5, retry, timeout) + override val blockchainConfigT = libJna.new_electrum_config(url, socks5, retry, timeout, stopGap) } \ No newline at end of file diff --git a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt index 9cd3346..57db810 100644 --- a/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt +++ b/bdk-kotlin/jvm/src/main/kotlin/org/bitcoindevkit/bdk/wallet/Wallet.kt @@ -10,9 +10,17 @@ import org.bitcoindevkit.bdk.types.VoidResult import org.slf4j.Logger import org.slf4j.LoggerFactory +enum class Network { + Bitcoin, + Testnet, + Signet, + Regtest, +} + class Wallet constructor( descriptor: String, changeDescriptor: String?, + network: Network, blockchainConfig: BlockchainConfig, databaseConfig: DatabaseConfig, ) : LibBase() { @@ -23,6 +31,7 @@ class Wallet constructor( libJna.new_wallet_result( descriptor, changeDescriptor, + network.toString().lowercase(), blockchainConfig.blockchainConfigT, databaseConfig.databaseConfigT ) diff --git a/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt b/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt index 739e348..24d9b20 100644 --- a/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt +++ b/bdk-kotlin/test-fixtures/src/main/kotlin/org/bitcoindevkit/bdk/LibTest.kt @@ -1,5 +1,6 @@ package org.bitcoindevkit.bdk +import org.bitcoindevkit.bdk.wallet.Network import org.bitcoindevkit.bdk.wallet.Wallet import org.junit.Assert.* import org.junit.Test @@ -19,8 +20,9 @@ abstract class LibTest : LibBase() { "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)" val change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)" - - val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30) + val network = Network.Testnet + + val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30, 100) val databaseConfig = MemoryConfig() abstract fun getTestDataDir(): String @@ -32,7 +34,7 @@ abstract class LibTest : LibBase() { @Test fun walletResultError() { val jnaException = assertThrows(FfiException::class.java) { - Wallet("bad", "bad", blockchainConfig, databaseConfig) + Wallet("bad", "bad", network, blockchainConfig, databaseConfig) } assertEquals(jnaException.err, FfiError.Descriptor) } @@ -58,18 +60,18 @@ abstract class LibTest : LibBase() { @Test fun walletSync() { - val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30) + val blockchainConfig = ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5, 30, 100) val testDataDir = getTestDataDir() // log.debug("testDataDir = $testDataDir") val databaseConfig = SledConfig(testDataDir, "steve-test") - val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) wallet.sync() cleanupTestDataDir() } @Test fun walletNewAddress() { - val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) val address = wallet.getAddress() assertNotNull(address) // log.debug("address created from kotlin: $address") @@ -78,7 +80,7 @@ abstract class LibTest : LibBase() { @Test fun walletUnspent() { - val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) wallet.sync() val unspent = wallet.listUnspent() assertTrue(unspent.isNotEmpty()) @@ -99,7 +101,7 @@ abstract class LibTest : LibBase() { @Test fun walletBalance() { - val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) wallet.sync() val balance = wallet.balance() //log.debug("balance from kotlin: $balance") @@ -108,7 +110,7 @@ abstract class LibTest : LibBase() { @Test fun walletTxDetails() { - val wallet = Wallet(desc, change, blockchainConfig, databaseConfig) + val wallet = Wallet(desc, change, network, blockchainConfig, databaseConfig) wallet.sync() val txDetails = wallet.listTransactionDetails() assertTrue(txDetails.isNotEmpty()) @@ -116,15 +118,18 @@ abstract class LibTest : LibBase() { txDetails.iterator().forEach { //log.debug("txDetails.txid: ${it.txid}") assertNotNull(it.txid) - //log.debug("txDetails.timestamp: ${it.timestamp}") - assertTrue(it.timestamp!! > 0) //log.debug("txDetails.received: ${it.received}") //log.debug("txDetails.sent: ${it.sent}") assertTrue(it.received!! > 0 || it.sent!! > 0) - //log.debug("txDetails.fees: ${it.fees}") - assertTrue(it.fees!! > 0) - //log.debug("txDetails.fees: ${it.height}") - assertTrue(it.height!! >= -1) + //log.debug("txDetails.fee: ${it.fee}") + assertTrue(it.fee!! > 0) + //log.debug("txDetails.is_confirmed: ${it.is_confirmed}") + assertTrue(it.is_confirmed!!) + assertNotNull(it.confirmation_time!!) + //log.debug("txDetails.confirmation_time.timestamp: ${it.confirmation_time!!.timestamp}") + assertTrue(it.confirmation_time!!.timestamp!! > 0) + //log.debug("txDetails.confirmation_time.height: ${it.confirmation_time!!.height}") + assertTrue(it.confirmation_time!!.height!! > 0) } } } diff --git a/build.sh b/build.sh index bf0fc42..67ca036 100755 --- a/build.sh +++ b/build.sh @@ -1,64 +1,114 @@ #!/usr/bin/env bash -set -eo pipefail -o xtrace +set -eo pipefail -# rust -cargo fmt -cargo build -cargo test --features c-headers -- generate_headers +# functions -# cc -export LD_LIBRARY_PATH=`pwd`/target/debug -cc cc/bdk_ffi_test.c -o cc/bdk_ffi_test -L target/debug -l bdk_ffi -l pthread -l dl -l m +## help +help() +{ + # Display Help + echo "Build bdk-ffi and related libraries." + echo + echo "Syntax: build [-a|h|k]" + echo "options:" + echo "-a Android aar." + echo "-h Print this Help." + echo "-k JVM jar." + echo +} + +## rust +build_rust() { + echo "Build Rust library and C headers" + cargo fmt + cargo build + cargo test --features c-headers -- generate_headers +} + +## cc +build_cc() { + echo "Build C test library" + export LD_LIBRARY_PATH=`pwd`/target/debug + cc cc/bdk_ffi_test.c -o cc/bdk_ffi_test -L target/debug -l bdk_ffi -l pthread -l dl -l m +} + +## copy to bdk-kotlin +copy_lib_kotlin() { + echo -n "Copy " + case $OS in + "Darwin") + echo -n "darwin " + mkdir -p bdk-kotlin/jvm/src/main/resources/darwin-x86-64 + cp target/debug/libbdk_ffi.dylib bdk-kotlin/jvm/src/main/resources/darwin-x86-64 + ;; + "Linux") + echo -n "linux " + mkdir -p bdk-kotlin/jvm/src/main/resources/linux-x86-64 + cp target/debug/libbdk_ffi.so bdk-kotlin/jvm/src/main/resources/linux-x86-64 + ;; + esac + echo "libs to kotlin sub-project" +} + +## bdk-kotlin jar +build_kotlin() { + (cd bdk-kotlin && ./gradlew :jvm:build && ./gradlew :jvm:publishToMavenLocal) +} + +## rust android +build_android() { + # If ANDROID_NDK_HOME is not set then set it to github actions default + [ -z "$ANDROID_NDK_HOME" ] && export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle + + # Update this line accordingly if you are not building *from* darwin-x86_64 or linux-x86_64 + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/`uname | tr '[:upper:]' '[:lower:]'`-x86_64/bin + + # Required for 'ring' dependency to cross-compile to Android platform, must be at least 21 + export CFLAGS="-D__ANDROID_API__=21" + + # IMPORTANT: make sure every target is not a substring of a different one. We check for them with grep later on + BUILD_TARGETS="${BUILD_TARGETS:-aarch64,armv7,x86_64,i686}" + + mkdir -p bdk-kotlin/android/src/main/jniLibs/ bdk-kotlin/android/src/main/jniLibs/arm64-v8a bdk-kotlin/android/src/main/jniLibs/x86_64 bdk-kotlin/android/src/main/jniLibs/armeabi-v7a bdk-kotlin/android/src/main/jniLibs/x86 + + if echo $BUILD_TARGETS | grep "aarch64"; then + CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android21-clang" CC="aarch64-linux-android21-clang" cargo build --target=aarch64-linux-android + cp target/aarch64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/arm64-v8a + fi + if echo $BUILD_TARGETS | grep "x86_64"; then + CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="x86_64-linux-android21-clang" CC="x86_64-linux-android21-clang" cargo build --target=x86_64-linux-android + cp target/x86_64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86_64 + fi + if echo $BUILD_TARGETS | grep "armv7"; then + CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="armv7a-linux-androideabi21-clang" CC="armv7a-linux-androideabi21-clang" cargo build --target=armv7-linux-androideabi + cp target/armv7-linux-androideabi/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/armeabi-v7a + fi + if echo $BUILD_TARGETS | grep "i686"; then + CARGO_TARGET_I686_LINUX_ANDROID_LINKER="i686-linux-android21-clang" CC="i686-linux-android21-clang" cargo build --target=i686-linux-android + cp target/i686-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86 + fi + + # bdk-kotlin aar + (cd bdk-kotlin && ./gradlew :android:build && ./gradlew :android:publishToMavenLocal) +} -# bdk-kotlin jar OS=$(uname) -case $OS in - "Darwin") - echo "Darwin build system" - mkdir -p bdk-kotlin/jvm/src/main/resources/darwin-x86-64 - cp target/debug/libbdk_ffi.dylib bdk-kotlin/jvm/src/main/resources/darwin-x86-64 - ;; - "Linux") - echo "Linux build system" - mkdir -p bdk-kotlin/jvm/src/main/resources/linux-x86-64 - cp target/debug/libbdk_ffi.so bdk-kotlin/jvm/src/main/resources/linux-x86-64 - ;; -esac -(cd bdk-kotlin && gradle :jvm:build && gradle :jvm:publishToMavenLocal) +if [ $1 = "-h" ] +then + help +else + build_rust + build_cc + copy_lib_kotlin -# rust android - -# If ANDROID_NDK_HOME is not set then set it to github actions default -[ -z "$ANDROID_NDK_HOME" ] && export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle - -# Update this line accordingly if you are not building *from* darwin-x86_64 or linux-x86_64 -export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/`uname | tr '[:upper:]' '[:lower:]'`-x86_64/bin - -# Required for 'ring' dependency to cross-compile to Android platform, must be at least 21 -export CFLAGS="-D__ANDROID_API__=21" - -# IMPORTANT: make sure every target is not a substring of a different one. We check for them with grep later on -BUILD_TARGETS="${BUILD_TARGETS:-aarch64,armv7,x86_64,i686}" - -mkdir -p bdk-kotlin/android/src/main/jniLibs/ bdk-kotlin/android/src/main/jniLibs/arm64-v8a bdk-kotlin/android/src/main/jniLibs/x86_64 bdk-kotlin/android/src/main/jniLibs/armeabi-v7a bdk-kotlin/android/src/main/jniLibs/x86 - -if echo $BUILD_TARGETS | grep "aarch64"; then - CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android21-clang" CC="aarch64-linux-android21-clang" cargo build --target=aarch64-linux-android - cp target/aarch64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/arm64-v8a + while [ -n "$1" ]; do # while loop starts + case "$1" in + -k) build_kotlin ;; + -a) build_android ;; + -h) help ;; + *) echo "Option $1 not recognized" ;; + esac + shift + done fi -if echo $BUILD_TARGETS | grep "x86_64"; then - CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="x86_64-linux-android21-clang" CC="x86_64-linux-android21-clang" cargo build --target=x86_64-linux-android - cp target/x86_64-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86_64 -fi -if echo $BUILD_TARGETS | grep "armv7"; then - CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="armv7a-linux-androideabi21-clang" CC="armv7a-linux-androideabi21-clang" cargo build --target=armv7-linux-androideabi - cp target/armv7-linux-androideabi/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/armeabi-v7a -fi -if echo $BUILD_TARGETS | grep "i686"; then - CARGO_TARGET_I686_LINUX_ANDROID_LINKER="i686-linux-android21-clang" CC="i686-linux-android21-clang" cargo build --target=i686-linux-android - cp target/i686-linux-android/debug/libbdk_ffi.so bdk-kotlin/android/src/main/jniLibs/x86 -fi - -# bdk-kotlin aar -(cd bdk-kotlin && gradle :android:build && gradle :android:publishToMavenLocal) diff --git a/cc/bdk_ffi.h b/cc/bdk_ffi.h index 850e75a..4170429 100644 --- a/cc/bdk_ffi.h +++ b/cc/bdk_ffi.h @@ -40,10 +40,6 @@ typedef uint16_t FfiError_t; enum /** . */ FFI_ERROR_SCRIPT_DOESNT_HAVE_ADDRESS_FORM, /** . */ - FFI_ERROR_SINGLE_RECIPIENT_MULTIPLE_OUTPUTS, - /** . */ - FFI_ERROR_SINGLE_RECIPIENT_NO_INPUTS, - /** . */ FFI_ERROR_NO_RECIPIENTS, /** . */ FFI_ERROR_NO_UTXOS_SELECTED, @@ -68,6 +64,8 @@ typedef uint16_t FfiError_t; enum /** . */ FFI_ERROR_FEE_TOO_LOW, /** . */ + FFI_ERROR_FEE_RATE_UNAVAILABLE, + /** . */ FFI_ERROR_MISSING_KEY_ORIGIN, /** . */ FFI_ERROR_KEY, @@ -80,6 +78,8 @@ typedef uint16_t FfiError_t; enum /** . */ FFI_ERROR_SIGNER, /** . */ + FFI_ERROR_INVALID_NETWORK, + /** . */ FFI_ERROR_INVALID_PROGRESS_VALUE, /** . */ FFI_ERROR_PROGRESS_UPDATE_ERROR, @@ -104,6 +104,8 @@ typedef uint16_t FfiError_t; enum /** . */ FFI_ERROR_PSBT, /** . */ + FFI_ERROR_PSBT_PARSE, + /** . */ FFI_ERROR_ELECTRUM, /** . */ FFI_ERROR_SLED, @@ -124,6 +126,7 @@ typedef struct { FfiResult_OpaqueWallet_ptr_t new_wallet_result ( char const * descriptor, char const * change_descriptor, + char const * network, BlockchainConfig_t const * blockchain_config, DatabaseConfig_t const * database_config); @@ -214,19 +217,32 @@ typedef struct { FfiResult_uint64_t balance ( OpaqueWallet_t const * opaque_wallet); + +#include + +typedef struct { + + uint32_t height; + + uint64_t timestamp; + +} ConfirmationTime_t; + typedef struct { char * txid; - uint64_t timestamp; - uint64_t received; uint64_t sent; - uint64_t fees; + int64_t fee; - int32_t height; + bool is_confirmed; + + ConfirmationTime_t confirmation_time; + + bool verified; } TransactionDetails_t; @@ -261,7 +277,8 @@ BlockchainConfig_t * new_electrum_config ( char const * url, char const * socks5, int16_t retry, - int16_t timeout); + int16_t timeout, + size_t stop_gap); void free_blockchain_config ( BlockchainConfig_t * blockchain_config); diff --git a/cc/bdk_ffi_test.c b/cc/bdk_ffi_test.c index 5f95924..cc10627 100644 --- a/cc/bdk_ffi_test.c +++ b/cc/bdk_ffi_test.c @@ -6,14 +6,21 @@ int main (int argc, char const * const argv[]) { + + // shared consts + char const *desc = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/0/*)"; + char const *change = "wpkh([bf988dd3/84'/1'/0']tpubDD7bHVspyCSvvU8qEycydF664NAX6EAPjJ77j9E614GU2zVdXgnZZo6JJjKbDT6fUn8owMN6TCP9rZMznsNEhJbpkEwp6fAyyoSqy3DH2Qj/1/*)"; + char const *net = "testnet"; + char const *blocks = "ssl://electrum.blockstream.info:60002"; + // test new wallet error { - BlockchainConfig_t *bc_config = new_electrum_config("ssl://electrum.blockstream.info:60002", NULL, 5, 30); + BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); //DatabaseConfig_t *db_config = new_sled_config("/home/steve/.bdk", "test_wallet"); DatabaseConfig_t *db_config = new_memory_config(); // new wallet with bad descriptor - FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result("bad","bad",bc_config,db_config); + FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result("bad","bad",net,bc_config,db_config); assert(wallet_result.err == FFI_ERROR_DESCRIPTOR); assert(wallet_result.ok == NULL); @@ -25,14 +32,11 @@ int main (int argc, char const * const argv[]) // test new wallet { - char const *desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; - char const *change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; - - BlockchainConfig_t *bc_config = new_electrum_config("ssl://electrum.blockstream.info:60002", NULL, 5, 30); + BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); DatabaseConfig_t *db_config = new_memory_config(); // new wallet - FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config); + FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); // printf("wallet_result.err = %d\n", wallet_result.err)); assert(wallet_result.err == FFI_ERROR_NONE); assert(wallet_result.ok != NULL); @@ -51,15 +55,15 @@ int main (int argc, char const * const argv[]) FfiResult_char_ptr_t address1_result = new_address(wallet); assert(address1_result.ok != NULL); assert(address1_result.err == FFI_ERROR_NONE); - // printf("address1 = %s\n", *address1_result.ok); - assert( 0 == strcmp(address1_result.ok,"tb1qgkhp034fyxeta00h0nne9tzfm0vsxq4prduzxp")); + //printf("address1 = %s\n", address1_result.ok); + assert( 0 == strcmp(address1_result.ok,"tb1qh4ajvhz9nd76tqddnl99l89hx4dat33hrjauzw")); free_string_result(address1_result); FfiResult_char_ptr_t address2_result = new_address(wallet); assert(address2_result.ok != NULL); assert(address2_result.err == FFI_ERROR_NONE); - // printf("address2 = %s\n", *address2_result.ok); - assert( 0 == strcmp(address2_result.ok,"tb1qd6u9q327sru2ljvwzdtfrdg36sapax7udz97wf")); + //printf("address2 = %s\n", address2_result.ok); + assert( 0 == strcmp(address2_result.ok,"tb1qr7pu0pech43hcjrc4pzxcen0qkslj7xk7s5w3m")); free_string_result(address2_result); // free_wallet @@ -73,15 +77,12 @@ int main (int argc, char const * const argv[]) } // test get unspent utxos - { - char const *desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; - char const *change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; - - BlockchainConfig_t *bc_config = new_electrum_config("ssl://electrum.blockstream.info:60002", NULL, 5, 30); + { + BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); DatabaseConfig_t *db_config = new_memory_config(); // new wallet - FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config); + FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); assert(wallet_result.err == FFI_ERROR_NONE); assert(wallet_result.ok != NULL); @@ -97,7 +98,7 @@ int main (int argc, char const * const argv[]) // list unspent FfiResult_Vec_LocalUtxo_t unspent_result = list_unspent(wallet); - assert(unspent_result.ok.len == 7); + assert(unspent_result.ok.len == 1); assert(unspent_result.err == FFI_ERROR_NONE); LocalUtxo_t * unspent_ptr = unspent_result.ok.ptr; @@ -119,15 +120,12 @@ int main (int argc, char const * const argv[]) } // test balance - { - char const *desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; - char const *change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; - - BlockchainConfig_t *bc_config = new_electrum_config("ssl://electrum.blockstream.info:60002", NULL, 5, 30); + { + BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); DatabaseConfig_t *db_config = new_memory_config(); // new wallet - FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config); + FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); assert(wallet_result.err == FFI_ERROR_NONE); assert(wallet_result.ok != NULL); @@ -155,14 +153,11 @@ int main (int argc, char const * const argv[]) // test get transaction details { - char const *desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"; - char const *change = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"; - - BlockchainConfig_t *bc_config = new_electrum_config("ssl://electrum.blockstream.info:60002", NULL, 5, 30); + BlockchainConfig_t *bc_config = new_electrum_config(blocks, NULL, 5, 30, 100); DatabaseConfig_t *db_config = new_memory_config(); // new wallet - FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config); + FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,net,bc_config,db_config); assert(wallet_result.err == FFI_ERROR_NONE); assert(wallet_result.ok != NULL); @@ -186,14 +181,14 @@ int main (int argc, char const * const argv[]) //printf("%d: txid: %s\n", i, txdetails_ptr[i].txid); assert(txdetails_ptr[i].txid != NULL); //printf("%d: timestamp: %ld\n", i, txdetails_ptr[i].timestamp); - assert(txdetails_ptr[i].timestamp > 0); + assert(txdetails_ptr[i].is_confirmed); //printf("%d: received: %ld\n", i, txdetails_ptr[i].received); //printf("%d: sent: %ld\n", i, txdetails_ptr[i].sent); assert(txdetails_ptr[i].received > 0 || txdetails_ptr[i].sent > 0); //printf("%d: fees: %ld\n", i, txdetails_ptr[i].fees); - assert(txdetails_ptr[i].fees > 0); + assert(txdetails_ptr[i].fee > 0); //printf("%d: height: %d\n", i, txdetails_ptr[i].height); - assert(txdetails_ptr[i].height >= -1); + assert(txdetails_ptr[i].confirmation_time.height > 0); } free_vectxdetails_result(txdetails_result); diff --git a/src/error.rs b/src/error.rs index f27c6dd..b0631b3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,8 +9,6 @@ pub enum FfiError { InvalidU32Bytes, Generic, ScriptDoesntHaveAddressForm, - SingleRecipientMultipleOutputs, - SingleRecipientNoInputs, NoRecipients, NoUtxosSelected, OutputBelowDustLimit, @@ -23,12 +21,14 @@ pub enum FfiError { IrreplaceableTransaction, FeeRateTooLow, FeeTooLow, + FeeRateUnavailable, MissingKeyOrigin, Key, ChecksumMismatch, SpendingPolicyRequired, InvalidPolicyPathError, Signer, + InvalidNetwork, InvalidProgressValue, ProgressUpdateError, InvalidOutpoint, @@ -41,6 +41,7 @@ pub enum FfiError { Json, Hex, Psbt, + PsbtParse, Electrum, // Esplora, // CompactFilters, @@ -53,8 +54,6 @@ impl From<&bdk::Error> for FfiError { Error::InvalidU32Bytes(_) => FfiError::InvalidU32Bytes, Error::Generic(_) => FfiError::Generic, Error::ScriptDoesntHaveAddressForm => FfiError::ScriptDoesntHaveAddressForm, - Error::SingleRecipientMultipleOutputs => FfiError::SingleRecipientMultipleOutputs, - Error::SingleRecipientNoInputs => FfiError::SingleRecipientNoInputs, Error::NoRecipients => FfiError::NoRecipients, Error::NoUtxosSelected => FfiError::NoUtxosSelected, Error::OutputBelowDustLimit(_) => FfiError::OutputBelowDustLimit, @@ -67,12 +66,14 @@ impl From<&bdk::Error> for FfiError { Error::IrreplaceableTransaction => FfiError::IrreplaceableTransaction, Error::FeeRateTooLow { .. } => FfiError::FeeRateTooLow, Error::FeeTooLow { .. } => FfiError::FeeTooLow, + Error::FeeRateUnavailable => FfiError::FeeRateUnavailable, Error::MissingKeyOrigin(_) => FfiError::MissingKeyOrigin, Error::Key(_) => FfiError::Key, Error::ChecksumMismatch => FfiError::ChecksumMismatch, Error::SpendingPolicyRequired(_) => FfiError::SpendingPolicyRequired, Error::InvalidPolicyPathError(_) => FfiError::InvalidPolicyPathError, Error::Signer(_) => FfiError::Signer, + Error::InvalidNetwork { .. } => FfiError::InvalidNetwork, Error::InvalidProgressValue(_) => FfiError::InvalidProgressValue, Error::ProgressUpdateError => FfiError::ProgressUpdateError, Error::InvalidOutpoint(_) => FfiError::InvalidOutpoint, @@ -85,9 +86,11 @@ impl From<&bdk::Error> for FfiError { Error::Json(_) => FfiError::Json, Error::Hex(_) => FfiError::Hex, Error::Psbt(_) => FfiError::Psbt, + Error::PsbtParse(_) => FfiError::PsbtParse, Error::Electrum(_) => FfiError::Electrum, // Error::Esplora(_) => JniError::Esplora, // Error::CompactFilters(_) => JniError::CompactFilters, + // Error::Rpc(_) => JniError::Rpc, Error::Sled(_) => FfiError::Sled, } } diff --git a/src/wallet/blockchain.rs b/src/wallet/blockchain.rs index b3ecd8b..50742f8 100644 --- a/src/wallet/blockchain.rs +++ b/src/wallet/blockchain.rs @@ -16,6 +16,7 @@ fn new_electrum_config( socks5: Option, retry: i16, timeout: i16, + stop_gap: usize, ) -> Box { let url = url.to_string(); let socks5 = socks5.map(|s| s.to_string()); @@ -27,6 +28,7 @@ fn new_electrum_config( socks5, retry, timeout, + stop_gap, }); Box::new(BlockchainConfig { raw: electrum_config, diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 90327eb..4637efc 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -2,7 +2,6 @@ use std::convert::TryFrom; use std::ffi::CString; use ::safer_ffi::prelude::*; -use bdk::bitcoin::network::constants::Network::Testnet; use bdk::blockchain::{log_progress, AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain}; use bdk::database::{AnyDatabase, AnyDatabaseConfig, ConfigurableDatabase}; use bdk::wallet::AddressIndex::New; @@ -16,6 +15,8 @@ use database::DatabaseConfig; use crate::error::FfiError; use crate::types::{FfiResult, FfiResultVoid}; use crate::wallet::transaction::{LocalUtxo, TransactionDetails}; +use bdk::bitcoin::Network; +use std::str::FromStr; mod blockchain; mod database; @@ -33,14 +34,16 @@ pub struct OpaqueWallet { fn new_wallet_result( descriptor: char_p_ref, change_descriptor: Option, + network: char_p_ref, blockchain_config: &BlockchainConfig, database_config: &DatabaseConfig, ) -> FfiResult>> { let descriptor = descriptor.to_string(); let change_descriptor = change_descriptor.map(|s| s.to_string()); + let net = Network::from_str(network.to_str()).expect("Network name"); let bc_config = &blockchain_config.raw; let db_config = &database_config.raw; - let wallet_result = new_wallet(descriptor, change_descriptor, bc_config, db_config); + let wallet_result = new_wallet(descriptor, change_descriptor, net, bc_config, db_config); match wallet_result { Ok(w) => FfiResult { @@ -57,11 +60,10 @@ fn new_wallet_result( fn new_wallet( descriptor: String, change_descriptor: Option, + network: Network, blockchain_config: &AnyBlockchainConfig, database_config: &AnyDatabaseConfig, ) -> Result, Error> { - let network = Testnet; - let client = AnyBlockchain::from_config(blockchain_config)?; let database = AnyDatabase::from_config(database_config)?; diff --git a/src/wallet/transaction.rs b/src/wallet/transaction.rs index 378d4cf..5c2194c 100644 --- a/src/wallet/transaction.rs +++ b/src/wallet/transaction.rs @@ -13,27 +13,52 @@ pub struct TransactionDetails { // pub transaction: Option, /// Transaction id pub txid: char_p_boxed, - /// Timestamp - pub timestamp: u64, /// Received value (sats) pub received: u64, /// Sent value (sats) pub sent: u64, - /// Fee value (sats) - pub fees: u64, - /// Confirmed in block height, `None` means unconfirmed - pub height: i32, + /// Fee value (sats) if known, -1 if unknown, based on backend + pub fee: i64, + /// true if confirmed + pub is_confirmed: bool, + /// Confirmed in block height + pub confirmation_time: ConfirmationTime, + /// Whether the tx has been verified against the consensus rules + pub verified: bool, +} + +#[derive_ReprC] +#[repr(C)] +#[derive(Debug, Clone)] +pub struct ConfirmationTime { + /// confirmation block height, 0 if is_confirmed is false + pub height: u32, + /// confirmation block timestamp, 0 if is_confirmed is false + pub timestamp: u64, } impl From<&bdk::TransactionDetails> for TransactionDetails { fn from(op: &bdk::TransactionDetails) -> Self { + let fee = op.fee.map(|f| i64::try_from(f).unwrap()).unwrap_or(-1); + let confirmation_time = op + .confirmation_time + .as_ref() + .map(|c| ConfirmationTime { + height: c.height, + timestamp: c.timestamp, + }) + .unwrap_or(ConfirmationTime { + height: 0, + timestamp: 0, + }); TransactionDetails { txid: char_p_boxed::try_from(op.txid.to_string()).unwrap(), - timestamp: op.timestamp, received: op.received, sent: op.sent, - fees: op.fees, - height: op.height.map(|h| h as i32).unwrap_or(-1), + fee, + is_confirmed: op.confirmation_time.is_some(), + confirmation_time, + verified: op.verified, } } } diff --git a/test.sh b/test.sh index c5a23fc..45b497e 100755 --- a/test.sh +++ b/test.sh @@ -1,14 +1,62 @@ #!/usr/bin/env bash -set -eo pipefail -o xtrace +set -eo pipefail + +# functions + +## help +help() +{ + # Display Help + echo "Test bdk-ffi and related libraries." + echo + echo "Syntax: build [-a|h|k|v]" + echo "options:" + echo "-a Android aar tests." + echo "-h Print this Help." + echo "-k JVM jar tests." + echo "-v Valgrind tests." + echo +} # rust -cargo test --features c-headers -- generate_headers +c_headers() { + cargo test --features c-headers -- generate_headers +} # cc -export LD_LIBRARY_PATH=`pwd`/target/debug -#valgrind --leak-check=full --show-leak-kinds=all cc/bdk_ffi_test -cc/bdk_ffi_test +test_c() { + export LD_LIBRARY_PATH=`pwd`/target/debug + cc/bdk_ffi_test +} -# bdk-kotlin -(cd bdk-kotlin && gradle test) -(cd bdk-kotlin && gradle :android:connectedDebugAndroidTest) +test_valgrind() { + valgrind --leak-check=full --show-leak-kinds=all cc/bdk_ffi_test +} + +test_kotlin() { + (cd bdk-kotlin && ./gradlew test) +} + +test_android() { + (cd bdk-kotlin && ./gradlew :android:connectedDebugAndroidTest) +} + +if [ $1 = "-h" ] +then + help +else + c_headers + test_c + + # optional tests + while [ -n "$1" ]; do # while loop starts + case "$1" in + -a) test_android ;; + -h) help ;; + -k) test_kotlin ;; + -v) test_valgrind ;; + *) echo "Option $1 not recognized" ;; + esac + shift + done +fi \ No newline at end of file