Skip to content

Commit

Permalink
Merge pull request #12 from TheBlueMatt/2021-03-fresh-bindings
Browse files Browse the repository at this point in the history
Update bindings to latest upstream + Add CI
  • Loading branch information
TheBlueMatt authored Mar 22, 2021
2 parents bbc02b4 + 0064602 commit f8c25e5
Show file tree
Hide file tree
Showing 40 changed files with 1,031 additions and 397 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Continuous Integration Checks

on: [push, pull_request]

jobs:
check_bindings:
runs-on: ubuntu-latest
# Ubuntu's version of rustc uses its own LLVM instead of being a real native package.
# This leaves us with an incompatible LLVM version when linking. Instead, use a real OS.
container: debian:bullseye
env:
TOOLCHAIN: stable
steps:
- name: Install native Rust toolchain, Valgrind, and build utilitis
run: |
apt-get update
apt-get -y dist-upgrade
apt-get -y install cargo libstd-rust-dev-wasm32 valgrind lld git g++ clang openjdk-11-jdk maven
- name: Checkout source code
uses: actions/checkout@v2
- name: Install cbindgen
run: cargo install --force cbindgen
- name: Checkout Rust-Lightning and LDK-C-Bindings git
run: |
git clone https://github.com/rust-bitcoin/rust-lightning
cd rust-lightning
git remote add matt https://git.bitcoin.ninja/rust-lightning
git fetch matt
git merge matt/2021-03-java-bindings-base
cd ..
git clone https://github.com/lightningdevkit/ldk-c-bindings
- name: Rebuild C bindings, and check the sample app builds + links
run: cd ldk-c-bindings && ./genbindings.sh ../rust-lightning && cd ..
- name: Build Java/TS Debug Bindings
run: ./genbindings.sh ./ldk-c-bindings/ "-I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/" true false
- name: Run Java Tests against Debug Bindings
run: |
rm liblightningjni.so
ln -s liblightningjni_debug.so liblightningjni.so
export LD_LIBRARY_PATH=.
export LD_PRELOAD=/usr/lib/llvm-11/lib/clang/11.0.1/lib/linux/libclang_rt.asan-x86_64.so
export ASAN_OPTIONS=detect_leaks=0
mvn test
git checkout liblightningjni.so
- name: Build Java/TS Release Bindings
run: ./genbindings.sh ./ldk-c-bindings/ "-I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/" false false
- name: Check latest headers are in git
run: |
git checkout liblightningjni_debug.so
git checkout liblightningjni_release.so
git diff --exit-code
2 changes: 1 addition & 1 deletion genbindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ usage() {
echo "android should either be true or false"
exit 1
}
[ "$1" = "" -o "$2" = "" ] && usage
[ "$1" = "" ] && usage
[ "$3" != "true" -a "$3" != "false" ] && usage
[ "$4" != "true" -a "$4" != "false" ] && usage

Expand Down
8 changes: 4 additions & 4 deletions java_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,10 +391,10 @@ def c_fn_name_define_pfx(self, fn_name, has_args):

def init_str(self):
res = ""
for ty in self.c_array_class_caches:
for ty in sorted(self.c_array_class_caches):
res = res + "static jclass " + ty + "_clz = NULL;\n"
res = res + "JNIEXPORT void Java_org_ldk_impl_bindings_init_1class_1cache(JNIEnv * env, jclass clz) {\n"
for ty in self.c_array_class_caches:
for ty in sorted(self.c_array_class_caches):
res = res + "\t" + ty + "_clz = (*env)->FindClass(env, \"" + ty.replace("arr_of_", "[") + "\");\n"
res = res + "\tCHECK(" + ty + "_clz != NULL);\n"
res = res + "\t" + ty + "_clz = (*env)->NewGlobalRef(env, " + ty + "_clz);\n"
Expand Down Expand Up @@ -638,7 +638,7 @@ def native_c_map_trait(self, struct_name, field_vars, field_fns, trait_doc_comme
for idx, fn_line in enumerate(field_fns):
if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
assert fn_line.ret_ty_info.ty_info.get_full_rust_ty()[1] == ""
out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_jcall("
out_c = out_c + fn_line.ret_ty_info.ty_info.get_full_rust_ty()[0] + " " + fn_line.fn_name + "_" + struct_name + "_jcall("
if fn_line.self_is_const:
out_c = out_c + "const void* this_arg"
else:
Expand Down Expand Up @@ -714,7 +714,7 @@ def native_c_map_trait(self, struct_name, field_vars, field_fns, trait_doc_comme
out_c = out_c + "\t\t.this_arg = (void*) calls,\n"
for fn_line in field_fns:
if fn_line.fn_name != "free" and fn_line.fn_name != "clone":
out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_jcall,\n"
out_c = out_c + "\t\t." + fn_line.fn_name + " = " + fn_line.fn_name + "_" + struct_name + "_jcall,\n"
elif fn_line.fn_name == "free":
out_c = out_c + "\t\t.free = " + struct_name + "_JCalls_free,\n"
else:
Expand Down
Binary file modified liblightningjni_debug.so
Binary file not shown.
Binary file modified liblightningjni_release.so
Binary file not shown.
65 changes: 50 additions & 15 deletions src/main/java/org/ldk/batteries/NioPeerHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ldk.batteries;

import org.ldk.impl.bindings;
import org.ldk.structs.*;

import java.io.IOException;
Expand All @@ -10,6 +11,7 @@
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Callable;

/**
* A NioPeerHandler maps LDK's PeerHandler to Java's NIO I/O interface. It spawns a single background thread which
Expand All @@ -26,6 +28,31 @@ private static class Peer {
SelectionKey key;
}

// Android's java.nio implementation has a big lock inside the selector, preventing any concurrent access to it.
// This appears to largely defeat the entire purpose of java.nio, but we work around it here by explicitly checking
// for an Android environment and passing any selector access on any thread other than our internal one through
// do_selector_action, which wakes up the selector before accessing it.
private static boolean IS_ANDROID;
static {
IS_ANDROID = System.getProperty("java.vendor").toLowerCase().contains("android");
}
private boolean wakeup_selector = false;
private interface SelectorCall {
void meth() throws IOException;
}
private void do_selector_action(SelectorCall meth) throws IOException {
if (IS_ANDROID) {
wakeup_selector = true;
this.selector.wakeup();
synchronized (this.selector) {
meth.meth();
wakeup_selector = false;
}
} else {
meth.meth();
}
}

private Peer setup_socket(SocketChannel chan) throws IOException {
chan.configureBlocking(false);
// Lightning tends to send a number of small messages back and forth between peers quickly, which Nagle is
Expand All @@ -41,15 +68,13 @@ private Peer setup_socket(SocketChannel chan) throws IOException {
SocketDescriptor descriptor = SocketDescriptor.new_impl(new SocketDescriptor.SocketDescriptorInterface() {
@Override
public long send_data(byte[] data, boolean resume_read) {
if (resume_read) {
peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_READ);
selector.wakeup();
}
try {
if (resume_read) {
do_selector_action(() -> peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_READ));
}
long written = chan.write(ByteBuffer.wrap(data));
if (written != data.length) {
peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_WRITE);
selector.wakeup();
do_selector_action(() -> peer.key.interestOps(peer.key.interestOps() | SelectionKey.OP_WRITE));
}
return written;
} catch (IOException e) {
Expand All @@ -61,9 +86,10 @@ public long send_data(byte[] data, boolean resume_read) {
@Override
public void disconnect_socket() {
try {
peer.key.cancel();
peer.key.channel().close();
selector.wakeup();
do_selector_action(() -> {
peer.key.cancel();
peer.key.channel().close();
});
} catch (IOException ignored) { }
synchronized (peer) {
while (peer.block_disconnect_socket) {
Expand All @@ -82,7 +108,7 @@ public void disconnect_socket() {

PeerManager peer_manager;
Thread io_thread;
Selector selector;
final Selector selector;
long socket_id;
volatile boolean shutdown = false;

Expand All @@ -101,7 +127,18 @@ public NioPeerHandler(PeerManager manager) throws IOException {
long lastTimerTick = System.currentTimeMillis();
while (true) {
try {
this.selector.select(1000);
if (IS_ANDROID) {
while (true) {
synchronized (this.selector) {
if (!wakeup_selector) {
this.selector.select(1000);
break;
}
}
}
} else {
this.selector.select(1000);
}
} catch (IOException ignored) {
System.err.println("java.nio threw an unexpected IOException. Stopping PeerHandler thread!");
return;
Expand Down Expand Up @@ -210,8 +247,7 @@ public void connect(byte[] their_node_id, SocketAddress remote, int timeout_ms)
if (chan.write(ByteBuffer.wrap(initial_bytes)) != initial_bytes.length) {
throw new IOException("We assume TCP socket buffer is at least a single packet in length");
}
peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer);
this.selector.wakeup();
do_selector_action(() -> peer.key = chan.register(this.selector, SelectionKey.OP_READ, peer));
} else {
throw new IOException("LDK rejected outbound connection. This likely shouldn't ever happen.");
}
Expand All @@ -228,8 +264,7 @@ public void bind_listener(SocketAddress socket_address) throws IOException {
ServerSocketChannel listen_channel = ServerSocketChannel.open();
listen_channel.bind(socket_address);
listen_channel.configureBlocking(false);
listen_channel.register(this.selector, SelectionKey.OP_ACCEPT);
this.selector.wakeup();
do_selector_action(() -> listen_channel.register(this.selector, SelectionKey.OP_ACCEPT));
}

/**
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/ldk/enums/LDKAccessError.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

/**
* An error when accessing the chain via [`Access`].
*
* [`Access`]: trait.Access.html
*/
public enum LDKAccessError {
LDKAccessError_UnknownChain,
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/org/ldk/impl/bindings.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public final static class SendShortIdsQuery extends LDKMessageSendEvent {
public long msg;
SendShortIdsQuery(byte[] node_id, long msg) { this.node_id = node_id; this.msg = msg; }
}
public final static class SendReplyChannelRange extends LDKMessageSendEvent {
public byte[] node_id;
public long msg;
SendReplyChannelRange(byte[] node_id, long msg) { this.node_id = node_id; this.msg = msg; }
}
static native void init();
}
static { LDKMessageSendEvent.init(); }
Expand Down Expand Up @@ -758,6 +763,7 @@ public interface LDKChannelMessageHandler {
void peer_disconnected(byte[] their_node_id, boolean no_connection_possible);
void peer_connected(byte[] their_node_id, long msg);
void handle_channel_reestablish(byte[] their_node_id, long msg);
void handle_channel_update(byte[] their_node_id, long msg);
void handle_error(byte[] their_node_id, long msg);
}
public static native long LDKChannelMessageHandler_new(LDKChannelMessageHandler impl, LDKMessageSendEventsProvider MessageSendEventsProvider);
Expand Down Expand Up @@ -797,6 +803,8 @@ public interface LDKChannelMessageHandler {
public static native void ChannelMessageHandler_peer_connected(long this_arg, byte[] their_node_id, long msg);
// void ChannelMessageHandler_handle_channel_reestablish LDKChannelMessageHandler *NONNULL_PTR this_arg, struct LDKPublicKey their_node_id, const struct LDKChannelReestablish *NONNULL_PTR msg
public static native void ChannelMessageHandler_handle_channel_reestablish(long this_arg, byte[] their_node_id, long msg);
// void ChannelMessageHandler_handle_channel_update LDKChannelMessageHandler *NONNULL_PTR this_arg, struct LDKPublicKey their_node_id, const struct LDKChannelUpdate *NONNULL_PTR msg
public static native void ChannelMessageHandler_handle_channel_update(long this_arg, byte[] their_node_id, long msg);
// void ChannelMessageHandler_handle_error LDKChannelMessageHandler *NONNULL_PTR this_arg, struct LDKPublicKey their_node_id, const struct LDKErrorMessage *NONNULL_PTR msg
public static native void ChannelMessageHandler_handle_error(long this_arg, byte[] their_node_id, long msg);
public interface LDKRoutingMessageHandler {
Expand Down Expand Up @@ -1688,6 +1696,10 @@ public interface LDKSocketDescriptor {
public static native int ChannelConfig_get_fee_proportional_millionths(long this_ptr);
// void ChannelConfig_set_fee_proportional_millionths(struct LDKChannelConfig *NONNULL_PTR this_ptr, uint32_t val);
public static native void ChannelConfig_set_fee_proportional_millionths(long this_ptr, int val);
// uint16_t ChannelConfig_get_cltv_expiry_delta(const struct LDKChannelConfig *NONNULL_PTR this_ptr);
public static native short ChannelConfig_get_cltv_expiry_delta(long this_ptr);
// void ChannelConfig_set_cltv_expiry_delta(struct LDKChannelConfig *NONNULL_PTR this_ptr, uint16_t val);
public static native void ChannelConfig_set_cltv_expiry_delta(long this_ptr, short val);
// bool ChannelConfig_get_announced_channel(const struct LDKChannelConfig *NONNULL_PTR this_ptr);
public static native boolean ChannelConfig_get_announced_channel(long this_ptr);
// void ChannelConfig_set_announced_channel(struct LDKChannelConfig *NONNULL_PTR this_ptr, bool val);
Expand All @@ -1696,8 +1708,8 @@ public interface LDKSocketDescriptor {
public static native boolean ChannelConfig_get_commit_upfront_shutdown_pubkey(long this_ptr);
// void ChannelConfig_set_commit_upfront_shutdown_pubkey(struct LDKChannelConfig *NONNULL_PTR this_ptr, bool val);
public static native void ChannelConfig_set_commit_upfront_shutdown_pubkey(long this_ptr, boolean val);
// MUST_USE_RES struct LDKChannelConfig ChannelConfig_new(uint32_t fee_proportional_millionths_arg, bool announced_channel_arg, bool commit_upfront_shutdown_pubkey_arg);
public static native long ChannelConfig_new(int fee_proportional_millionths_arg, boolean announced_channel_arg, boolean commit_upfront_shutdown_pubkey_arg);
// MUST_USE_RES struct LDKChannelConfig ChannelConfig_new(uint32_t fee_proportional_millionths_arg, uint16_t cltv_expiry_delta_arg, bool announced_channel_arg, bool commit_upfront_shutdown_pubkey_arg);
public static native long ChannelConfig_new(int fee_proportional_millionths_arg, short cltv_expiry_delta_arg, boolean announced_channel_arg, boolean commit_upfront_shutdown_pubkey_arg);
// struct LDKChannelConfig ChannelConfig_clone(const struct LDKChannelConfig *NONNULL_PTR orig);
public static native long ChannelConfig_clone(long orig);
// MUST_USE_RES struct LDKChannelConfig ChannelConfig_default(void);
Expand Down Expand Up @@ -2940,6 +2952,8 @@ public interface LDKSocketDescriptor {
public static native long ReplyShortChannelIdsEnd_read(byte[] ser);
// struct LDKCVec_u8Z ReplyShortChannelIdsEnd_write(const struct LDKReplyShortChannelIdsEnd *NONNULL_PTR obj);
public static native byte[] ReplyShortChannelIdsEnd_write(long obj);
// MUST_USE_RES uint32_t QueryChannelRange_end_blocknum(const struct LDKQueryChannelRange *NONNULL_PTR this_arg);
public static native int QueryChannelRange_end_blocknum(long this_arg);
// struct LDKCResult_QueryChannelRangeDecodeErrorZ QueryChannelRange_read(struct LDKu8slice ser);
public static native long QueryChannelRange_read(byte[] ser);
// struct LDKCVec_u8Z QueryChannelRange_write(const struct LDKQueryChannelRange *NONNULL_PTR obj);
Expand Down
13 changes: 2 additions & 11 deletions src/main/java/org/ldk/structs/ChainMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
* or used independently to monitor channels remotely. See the [module-level documentation] for
* details.
*
* [`chain::Watch`]: ../trait.Watch.html
* [`ChannelManager`]: ../../ln/channelmanager/struct.ChannelManager.html
* [module-level documentation]: index.html
* [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
* [module-level documentation]: crate::chain::chainmonitor
*/
@SuppressWarnings("unchecked") // We correctly assign various generic arrays
public class ChainMonitor extends CommonBase {
Expand All @@ -37,10 +36,6 @@ protected void finalize() throws Throwable {
* calls must not exclude any transactions matching the new outputs nor any in-block
* descendants of such transactions. It is not necessary to re-fetch the block to obtain
* updated `txdata`.
*
* [`ChannelMonitor::block_connected`]: ../channelmonitor/struct.ChannelMonitor.html#method.block_connected
* [`chain::Watch::release_pending_monitor_events`]: ../trait.Watch.html#tymethod.release_pending_monitor_events
* [`chain::Filter`]: ../trait.Filter.html
*/
public void block_connected(byte[] header, TwoTuple<Long, byte[]>[] txdata, int height) {
bindings.ChainMonitor_block_connected(this.ptr, header, Arrays.stream(txdata).mapToLong(txdata_conv_24 -> bindings.C2Tuple_usizeTransactionZ_new(txdata_conv_24.a, txdata_conv_24.b)).toArray(), height);
Expand All @@ -51,8 +46,6 @@ public void block_connected(byte[] header, TwoTuple<Long, byte[]>[] txdata, int
* Dispatches to per-channel monitors, which are responsible for updating their on-chain view
* of a channel based on the disconnected block. See [`ChannelMonitor::block_disconnected`] for
* details.
*
* [`ChannelMonitor::block_disconnected`]: ../channelmonitor/struct.ChannelMonitor.html#method.block_disconnected
*/
public void block_disconnected(byte[] header, int disconnected_height) {
bindings.ChainMonitor_block_disconnected(this.ptr, header, disconnected_height);
Expand All @@ -66,8 +59,6 @@ public void block_disconnected(byte[] header, int disconnected_height) {
* pre-filter blocks or only fetch blocks matching a compact filter. Otherwise, clients may
* always need to fetch full blocks absent another means for determining which blocks contain
* transactions relevant to the watched channels.
*
* [`chain::Filter`]: ../trait.Filter.html
*/
public static ChainMonitor constructor_new(Filter chain_source, BroadcasterInterface broadcaster, Logger logger, FeeEstimator feeest, Persist persister) {
long ret = bindings.ChainMonitor_new(chain_source == null ? 0 : chain_source.ptr, broadcaster == null ? 0 : broadcaster.ptr, logger == null ? 0 : logger.ptr, feeest == null ? 0 : feeest.ptr, persister == null ? 0 : persister.ptr);
Expand Down
Loading

0 comments on commit f8c25e5

Please sign in to comment.