package org.ldk;
import org.bitcoinj.core.*;
-import org.bitcoinj.core.Transaction;
import org.bitcoinj.script.Script;
import org.junit.jupiter.api.Test;
-import org.ldk.batteries.ChannelManagerConstructor;
import org.ldk.batteries.NioPeerHandler;
+import org.ldk.enums.LDKCurrency;
import org.ldk.enums.LDKNetwork;
import org.ldk.impl.bindings;
import org.ldk.structs.*;
@Override
public Sign get_channel_signer(boolean inbound, long channel_value_satoshis) {
Sign underlying_ck = underlying_if.get_channel_signer(inbound, channel_value_satoshis);
- Sign.SignInterface si = new Sign.SignInterface() {
+ // TODO: Expose the underlying signer from a Sign
+ /*BaseSign.BaseSignInterface si = new BaseSign.BaseSignInterface() {
@Override
public byte[] get_per_commitment_point(long idx) {
return underlying_ck.get_per_commitment_point(idx);
public byte[] write() {
return underlying_ck.write();
}
- };
- Sign resp = Sign.new_impl(si, underlying_ck.get_pubkeys());
- must_free_objs.add(new WeakReference<>(si));
- must_free_objs.add(new WeakReference<>(resp));
+ };*/
+ //Sign resp = Sign.new_impl(si, underlying_ck.get_pubkeys());
+ //must_free_objs.add(new WeakReference<>(si));
+ //must_free_objs.add(new WeakReference<>(resp));
must_free_objs.add(new WeakReference<>(underlying_ck));
- return resp;
+ //return resp;
+ return underlying_ck;
}
@Override
public Result_SignDecodeErrorZ read_chan_signer(byte[] reader) {
return underlying_if.read_chan_signer(reader);
}
+
+ @Override
+ public Result_RecoverableSignatureNoneZ sign_invoice(byte[] invoice_preimage) {
+ return underlying_if.sign_invoice(invoice_preimage);
+ }
});
}
synchronized (monitors) {
assert monitors.put(Arrays.toString(funding_txo.get_txid()), monitor) == null;
}
- return new Result_NoneChannelMonitorUpdateErrZ.Result_NoneChannelMonitorUpdateErrZ_OK();
+ return Result_NoneChannelMonitorUpdateErrZ.constructor_ok();
}
public Result_NoneChannelMonitorUpdateErrZ update_channel(OutPoint funding_txo, ChannelMonitorUpdate update) {
Result_NoneMonitorUpdateErrorZ update_res = monitors.get(txid).update_monitor(update, tx_broadcaster, fee_estimator, logger);
assert update_res instanceof Result_NoneMonitorUpdateErrorZ.Result_NoneMonitorUpdateErrorZ_OK;
}
- return new Result_NoneChannelMonitorUpdateErrZ.Result_NoneChannelMonitorUpdateErrZ_OK();
+ return Result_NoneChannelMonitorUpdateErrZ.constructor_ok();
}
@Override
final Logger logger;
final FeeEstimator fee_estimator;
final BroadcasterInterface tx_broadcaster;
+ final KeysManager explicit_keys_manager;
final KeysInterface keys_interface;
final ChainMonitor chain_monitor;
final NetGraphMsgHandler router;
final HashMap<String, ChannelMonitor> monitors; // Wow I forgot just how terrible Java is - we can't put a byte array here.
byte[] node_id;
final LinkedList<byte[]> broadcast_set = new LinkedList<>();
+ GcCheck obj = new GcCheck();
private TwoTuple<OutPoint, byte[]> test_mon_roundtrip(ChannelMonitor mon) {
// Because get_funding_txo() returns an OutPoint in a tuple that is a reference to an OutPoint inside the
assert Arrays.equals(res.a.get_txid(), id.get_txid());
assert res.a.get_index() == id.get_index();
}
- return new Result_NoneChannelMonitorUpdateErrZ.Result_NoneChannelMonitorUpdateErrZ_OK();
+ return Result_NoneChannelMonitorUpdateErrZ.constructor_ok();
}
@Override
assert Arrays.equals(res.a.get_txid(), id.get_txid());
assert res.a.get_index() == id.get_index();
}
- return new Result_NoneChannelMonitorUpdateErrZ.Result_NoneChannelMonitorUpdateErrZ_OK();
+ return Result_NoneChannelMonitorUpdateErrZ.constructor_ok();
}
});
@Override public void register_tx(byte[] txid, byte[] script_pubkey) {
filter_additions.add(Arrays.toString(txid));
}
- @Override public void register_output(OutPoint outpoint, byte[] script_pubkey) {
- filter_additions.add(Arrays.toString(outpoint.get_txid()) + ":" + outpoint.get_index());
+ @Override public Option_C2Tuple_usizeTransactionZZ register_output(WatchedOutput output) {
+ filter_additions.add(Arrays.toString(output.get_outpoint().get_txid()) + ":" + output.get_outpoint().get_index());
+ return Option_C2Tuple_usizeTransactionZZ.constructor_none();
}
});
} else {
KeysManager keys = KeysManager.constructor_new(key_seed, System.currentTimeMillis() / 1000, (int) (System.currentTimeMillis() * 1000));
if (use_km_wrapper) {
this.keys_interface = manual_keysif(keys.as_KeysInterface());
+ this.explicit_keys_manager = null;
} else {
this.keys_interface = keys.as_KeysInterface();
+ this.explicit_keys_manager = keys;
}
this.router = NetGraphMsgHandler.constructor_new(new byte[32], null, logger);
}
}
Peer(byte seed) {
this(null, seed);
- this.chan_manager = ChannelManager.constructor_new(FeeEstimator.new_impl(confirmation_target -> 0), chain_watch, tx_broadcaster, logger, this.keys_interface, UserConfig.constructor_default(), LDKNetwork.LDKNetwork_Bitcoin, new byte[32], 1);
+ this.chan_manager = ChannelManager.constructor_new(FeeEstimator.new_impl(confirmation_target -> 0), chain_watch, tx_broadcaster, logger, this.keys_interface, UserConfig.constructor_default(), LDKNetwork.LDKNetwork_Bitcoin, BestBlock.constructor_new(new byte[32], 0));
this.node_id = chan_manager.get_our_node_id();
this.chan_manager_events = chan_manager.as_EventsProvider();
Object ptr_to;
Peer(Peer orig) {
this(null, orig.seed);
- if (!break_cross_peer_refs) {
- ChannelMonitor[] monitors = new ChannelMonitor[1];
- synchronized (monitors) {
- assert orig.monitors.size() == 1;
+ // TODO: Optionally test ChannelManagerConstructor
+ ChannelMonitor[] monitors = new ChannelMonitor[1];
+ synchronized (monitors) {
+ assert orig.monitors.size() == 1;
+ if (!break_cross_peer_refs) {
monitors[0] = orig.monitors.values().stream().iterator().next();
- }
- byte[] serialized = orig.chan_manager.write();
- Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ read_res =
- UtilMethods.constructor_BlockHashChannelManagerZ_read(serialized, this.keys_interface, this.fee_estimator, this.chain_watch, this.tx_broadcaster, this.logger, UserConfig.constructor_default(), monitors);
- assert read_res instanceof Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK;
- this.chan_manager = ((Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK) read_res).res.b;
- this.chain_watch.watch_channel(monitors[0].get_funding_txo().a, monitors[0]);
- } else {
- final ArrayList<byte[]> channel_monitors = new ArrayList();
- synchronized (monitors) {
- assert orig.monitors.size() == 1;
- channel_monitors.add(orig.monitors.values().stream().iterator().next().write());
- }
- byte[] serialized = orig.chan_manager.write();
- try {
- ChannelManagerConstructor constructed = new ChannelManagerConstructor(serialized, channel_monitors.toArray(new byte[1][]), this.keys_interface, this.fee_estimator, this.chain_watch, this.filter, this.tx_broadcaster, this.logger);
- this.chan_manager = constructed.channel_manager;
- constructed.chain_sync_completed();
- if (use_filter && !use_manual_watch) {
- // With a manual watch we don't actually use the filter object at all.
- assert this.filter_additions.containsAll(orig.filter_additions) &&
- orig.filter_additions.containsAll(this.filter_additions);
- }
- } catch (ChannelManagerConstructor.InvalidSerializedDataException e) {
- assert false;
+ } else {
+ byte[] serialized = orig.monitors.values().stream().iterator().next().write();
+ Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ res =
+ UtilMethods.constructor_BlockHashChannelMonitorZ_read(serialized, this.keys_interface);
+ assert res instanceof Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ.Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ_OK;
+ monitors[0] = ((Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ.Result_C2Tuple_BlockHashChannelMonitorZDecodeErrorZ_OK) res).res.b;
}
}
+ byte[] serialized = orig.chan_manager.write();
+ Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ read_res =
+ UtilMethods.constructor_BlockHashChannelManagerZ_read(serialized, this.keys_interface, this.fee_estimator, this.chain_watch, this.tx_broadcaster, this.logger, UserConfig.constructor_default(), monitors);
+ assert read_res instanceof Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK;
+ this.chan_manager = ((Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ.Result_C2Tuple_BlockHashChannelManagerZDecodeErrorZ_OK) read_res).res.b;
+ this.chain_watch.watch_channel(monitors[0].get_funding_txo().a, monitors[0]);
if (!break_cross_peer_refs && (use_manual_watch || use_km_wrapper)) {
// When we pass monitors[0] into chain_watch.watch_channel we create a reference from the new Peer to a
// field in the old peer, preventing freeing of the original Peer until the new Peer is freed. Thus, we
TwoTuple<Long, byte[]>[] txn;
if (b.hasTransactions()) {
assert b.getTransactions().size() == 1;
- TwoTuple<Long, byte[]> txp = new TwoTuple<>((long) 1, b.getTransactions().get(0).bitcoinSerialize());
+ TwoTuple<Long, byte[]> txp = new TwoTuple<>((long) 0, b.getTransactions().get(0).bitcoinSerialize());
txn = new TwoTuple[]{txp};
} else
txn = new TwoTuple[0];
- chan_manager.block_connected(header, txn, height);
if (chain_monitor != null) {
- chain_monitor.block_connected(header, txn, height);
+ chan_manager.as_Listen().block_connected(b.bitcoinSerialize(), height);
+ chain_monitor.as_Listen().block_connected(b.bitcoinSerialize(), height);
} else {
+ chan_manager.as_Confirm().transactions_confirmed(header, txn, height);
+ chan_manager.as_Confirm().best_block_updated(header, height);
+ // Connect manually if we aren't using a ChainMonitor and are implementing Watch ourselves
synchronized (monitors) {
assert monitors.size() == 1;
for (ChannelMonitor mon : monitors.values()) {
Route get_route(byte[] dest_node, ChannelDetails[] our_chans) {
try (LockedNetworkGraph netgraph = this.router.read_locked_graph()) {
NetworkGraph graph = netgraph.graph();
- long res = bindings.get_route(this.node_id, graph._test_only_get_ptr(), dest_node, new long[]{our_chans[0]._test_only_get_ptr()},
- new long[0], 1000, 42, this.logger._test_only_get_ptr());
+ long res = bindings.get_route(this.node_id, graph._test_only_get_ptr(), dest_node, 0L, new long[]{our_chans[0]._test_only_get_ptr()},
+ new long[0], 1000000, 42, this.logger._test_only_get_ptr());
assert bindings.LDKCResult_RouteLightningErrorZ_result_ok(res);
byte[] serialized_route = bindings.Route_write(bindings.LDKCResult_RouteLightningErrorZ_get_ok(res));
must_free_objs.add(new WeakReference<>(serialized_route));
funding.getInputs().get(0).setWitness(new TransactionWitness(2)); // Make sure we don't complain about lack of witness
funding.getInput(0).getWitness().setPush(0, new byte[]{0x1});
funding.addOutput(Coin.SATOSHI.multiply(10000), new Script(funding_spk));
- peer1.chan_manager.funding_transaction_generated(chan_id, OutPoint.constructor_new(funding.getTxId().getReversedBytes(), (short) 0));
+ Result_NoneAPIErrorZ funding_res = peer1.chan_manager.funding_transaction_generated(chan_id, funding.bitcoinSerialize());
+ assert funding_res instanceof Result_NoneAPIErrorZ.Result_NoneAPIErrorZ_OK;
wait_events_processed(peer1, peer2);
- events = peer1.chan_manager_events.get_and_clear_pending_events();
- assert events.length == 1;
- assert events[0] instanceof Event.FundingBroadcastSafe;
- assert ((Event.FundingBroadcastSafe) events[0]).user_channel_id == 42;
+ assert peer1.broadcast_set.size() == 1;
+ assert Arrays.equals(peer1.broadcast_set.get(0), funding.bitcoinSerialize());
+ peer1.broadcast_set.clear();
Block b = new Block(bitcoinj_net, 2, Sha256Hash.ZERO_HASH, Sha256Hash.ZERO_HASH, 42, 0, 0, Arrays.asList(new Transaction[]{funding}));
peer1.connect_block(b, 1, 0);
wait_events_processed(peer1, peer2);
peer1.chan_manager.list_channels();
- ChannelDetails[] peer1_chans = peer1.chan_manager.list_channels();
- ChannelDetails[] peer2_chans = peer2.chan_manager.list_channels();
+ ChannelDetails[] peer1_chans = peer1.chan_manager.list_usable_channels();
+ ChannelDetails[] peer2_chans = peer2.chan_manager.list_usable_channels();
assert peer1_chans.length == 1;
assert peer2_chans.length == 1;
assert peer1_chans[0].get_channel_value_satoshis() == 10000;
assert peer1_chans[0].get_is_live();
+ Option_u64Z short_chan_id = peer1_chans[0].get_short_channel_id();
+ assert short_chan_id instanceof Option_u64Z.Some;
+ assert ((Option_u64Z.Some)short_chan_id).some == (1L << 40); // 0th output in the 0th transaction in the 1st block
assert Arrays.equals(peer1_chans[0].get_channel_id(), funding.getTxId().getReversedBytes());
assert Arrays.equals(peer2_chans[0].get_channel_id(), funding.getTxId().getReversedBytes());
- byte[] payment_preimage = new byte[32];
- for (int i = 0; i < 32; i++) payment_preimage[i] = (byte) (i ^ 0x0f);
- byte[] payment_hash = Sha256Hash.hash(payment_preimage);
+ Result_InvoiceSignOrCreationErrorZ invoice = UtilMethods.constructor_invoice_from_channelmanager(peer2.chan_manager, peer2.keys_interface, LDKCurrency.LDKCurrency_Bitcoin, Option_u64Z.constructor_none(), "");
+ assert invoice instanceof Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK;
+ System.out.println("Got invoice: " + ((Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK) invoice).res.to_str());
+ Result_InvoiceNoneZ parsed_invoice = Invoice.constructor_from_str(((Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK) invoice).res.to_str());
+ assert parsed_invoice instanceof Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK;
+ assert Arrays.equals(((Result_InvoiceNoneZ.Result_InvoiceNoneZ_OK) parsed_invoice).res.payment_hash(), ((Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK) invoice).res.payment_hash());
+ byte[] payment_hash = ((Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK) invoice).res.payment_hash();
+ byte[] payment_secret = ((Result_InvoiceSignOrCreationErrorZ.Result_InvoiceSignOrCreationErrorZ_OK) invoice).res.payment_secret();
+
Route route = peer1.get_route(peer2.node_id, peer1_chans);
- Result_NonePaymentSendFailureZ payment_res = peer1.chan_manager.send_payment(route, payment_hash, new byte[32]);
+ Result_NonePaymentSendFailureZ payment_res = peer1.chan_manager.send_payment(route, payment_hash, payment_secret);
assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_OK;
wait_events_processed(peer1, peer2);
hop_pubkey[1] = 42;
hops[0][0] = RouteHop.constructor_new(hop_pubkey, NodeFeatures.constructor_known(), 42, ChannelFeatures.constructor_known(), 100, 0);
Route r2 = Route.constructor_new(hops);
- payment_res = peer1.chan_manager.send_payment(r2, payment_hash, new byte[32]);
+ payment_res = peer1.chan_manager.send_payment(r2, payment_hash, payment_secret);
assert payment_res instanceof Result_NonePaymentSendFailureZ.Result_NonePaymentSendFailureZ_Err;
assert peer1.get_monitor_events().length == 0;
WeakReference<Peer> op1 = new WeakReference<Peer>(peer1);
peer1 = new Peer(peer1);
peer2 = new Peer(peer2);
- return new TestState(op1, peer1, peer2, payment_preimage, b.getHash());
+ return new TestState(op1, peer1, peer2, b.getHash());
}
- return new TestState(null, peer1, peer2, payment_preimage, b.getHash());
+ return new TestState(null, peer1, peer2, b.getHash());
}
boolean cross_reload_ref_pollution = false;
private final WeakReference<Peer> ref_block;
private final Peer peer1;
private final Peer peer2;
- private final byte[] payment_preimage;
public Sha256Hash best_blockhash;
- public TestState(WeakReference<Peer> ref_block, Peer peer1, Peer peer2, byte[] payment_preimage, Sha256Hash best_blockhash) {
+ public TestState(WeakReference<Peer> ref_block, Peer peer1, Peer peer2, Sha256Hash best_blockhash) {
this.ref_block = ref_block;
this.peer1 = peer1;
this.peer2 = peer2;
- this.payment_preimage = payment_preimage;
this.best_blockhash = best_blockhash;
}
}
System.runFinalization();
}
connect_peers(state.peer1, state.peer2);
- wait_events_processed(state.peer1, state.peer2);
}
+ wait_events_processed(state.peer1, state.peer2);
Event[] events = state.peer2.chan_manager_events.get_and_clear_pending_events();
assert events.length == 1;
events = state.peer2.chan_manager_events.get_and_clear_pending_events();
assert events.length == 1;
assert events[0] instanceof Event.PaymentReceived;
- state.peer2.chan_manager.claim_funds(state.payment_preimage, new byte[32], ((Event.PaymentReceived) events[0]).amt);
+ byte[] payment_preimage = ((Event.PaymentReceived)events[0]).payment_preimage;
+ assert !Arrays.equals(payment_preimage, new byte[32]);
+ state.peer2.chan_manager.claim_funds(payment_preimage);
wait_events_processed(state.peer1, state.peer2);
events = state.peer1.chan_manager_events.get_and_clear_pending_events();
assert events.length == 1;
assert events[0] instanceof Event.PaymentSent;
- assert Arrays.equals(((Event.PaymentSent) events[0]).payment_preimage, state.payment_preimage);
+ assert Arrays.equals(((Event.PaymentSent) events[0]).payment_preimage, payment_preimage);
wait_events_processed(state.peer1, state.peer2);
ChannelDetails[] peer1_chans = state.peer1.chan_manager.list_channels();
wait_events_processed(state.peer1, state.peer2);
assert state.peer1.broadcast_set.size() == 1;
- assert state.peer2.broadcast_set.size() == 0;
+ assert state.peer2.broadcast_set.size() == 1;
NetworkParameters bitcoinj_net = NetworkParameters.fromID(NetworkParameters.ID_MAINNET);
Transaction tx = new Transaction(bitcoinj_net, state.peer1.broadcast_set.getFirst());
Block b = new Block(bitcoinj_net, 2, state.best_blockhash, Sha256Hash.ZERO_HASH, 42, 0, 0,
Arrays.asList(new Transaction[]{tx}));
- TwoTuple<byte[], TwoTuple<Integer, TxOut>[]>[] watch_outputs = state.peer2.connect_block(b, 1, 1);
+ TwoTuple<byte[], TwoTuple<Integer, TxOut>[]>[] watch_outputs = state.peer2.connect_block(b, 10, 1);
if (watch_outputs != null) { // We only process watch_outputs manually when we use a manually-build Watch impl
assert watch_outputs.length == 1;
assert Arrays.equals(watch_outputs[0].a, tx.getTxId().getReversedBytes());
- assert watch_outputs[0].b.length == 1;
+ assert watch_outputs[0].b.length == 2;
+ assert watch_outputs[0].b[0].a == 0;
+ assert watch_outputs[0].b[1].a == 1;
}
+ for (int i = 11; i < 21; i++) {
+ b = new Block(bitcoinj_net, 2, b.getHash(), Sha256Hash.ZERO_HASH, 42, 0, 0, new ArrayList<>());
+ state.peer2.connect_block(b, i, 0);
+ }
+
+ Event[] broadcastable_event = null;
+ if (state.peer2.chain_monitor != null) {
+ broadcastable_event = state.peer2.chain_monitor.as_EventsProvider().get_and_clear_pending_events();
+ }
// This used to be buggy and double-free, so go ahead and fetch them!
for (ChannelMonitor mon : state.peer2.monitors.values()) {
byte[][] txn = mon.get_latest_holder_commitment_txn(state.peer2.logger);
+ if (state.peer2.chain_monitor == null) {
+ broadcastable_event = mon.get_and_clear_pending_events();
+ }
+ }
+ assert broadcastable_event.length == 1;
+ assert broadcastable_event[0] instanceof Event.SpendableOutputs;
+ if (state.peer2.explicit_keys_manager != null) {
+ Result_TransactionNoneZ tx_res = state.peer2.explicit_keys_manager.spend_spendable_outputs(((Event.SpendableOutputs) broadcastable_event[0]).outputs, new TxOut[0], new byte[] {0x00}, 253);
+ assert tx_res instanceof Result_TransactionNoneZ.Result_TransactionNoneZ_OK;
+ Transaction built_tx = new Transaction(bitcoinj_net, ((Result_TransactionNoneZ.Result_TransactionNoneZ_OK) tx_res).res);
+ assert built_tx.getOutputs().size() == 1;
+ assert Arrays.equals(built_tx.getOutput(0).getScriptBytes(), new byte[]{0x00});
}
}
}
java.util.LinkedList<WeakReference<Object>> must_free_objs = new java.util.LinkedList();
- boolean gc_ran = false;
+ int gc_count = 0;
+ int gc_exp_count = 0;
class GcCheck {
+ GcCheck() { gc_exp_count += 1; }
@Override
protected void finalize() throws Throwable {
- gc_ran = true;
+ gc_count += 1;
super.finalize();
}
}
}
void do_test(boolean nice_close, boolean use_km_wrapper, boolean use_manual_watch, boolean reload_peers, boolean break_cross_peer_refs, boolean nio_peer_handler) throws InterruptedException {
HumanObjectPeerTestInstance instance = do_test_run(nice_close, use_km_wrapper, use_manual_watch, reload_peers, break_cross_peer_refs, nio_peer_handler);
- while (!instance.gc_ran) {
+ while (instance.gc_count != instance.gc_exp_count) {
System.gc();
System.runFinalization();
}