diff --git a/.github/workflows/kotlin.yml b/.github/workflows/kotlin.yml deleted file mode 100644 index a1711ba49..000000000 --- a/.github/workflows/kotlin.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: CI Checks - Kotlin Tests - -on: [push, pull_request] - -jobs: - check-kotlin: - runs-on: ubuntu-latest - - env: - LDK_NODE_JVM_DIR: bindings/kotlin/ldk-node-jvm - LDK_NODE_ANDROID_DIR: bindings/kotlin/ldk-node-android - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up JDK - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - - name: Set default Rust version to stable - run: rustup default stable - - - name: Show default version of NDK - run: echo $ANDROID_NDK_ROOT - - - name: Run ktlintCheck on ldk-node-jvm - run: | - cd $LDK_NODE_JVM_DIR - ./gradlew ktlintCheck - - - name: Run ktlintCheck on ldk-node-android - run: | - cd $LDK_NODE_ANDROID_DIR - ./gradlew ktlintCheck - - - name: Generate Kotlin JVM - run: ./scripts/uniffi_bindgen_generate_kotlin.sh - - - name: Generate Kotlin Android - run: ./scripts/uniffi_bindgen_generate_kotlin_android.sh - - - name: Start bitcoind and electrs - run: docker compose up -d - - - name: Run ldk-node-jvm tests - run: | - cd $LDK_NODE_JVM_DIR - ./gradlew test -Penv=ci - - - name: Run ldk-node-android tests - run: | - cd $LDK_NODE_ANDROID_DIR - ./gradlew test diff --git a/.github/workflows/publish-android.yml b/.github/workflows/publish-android.yml deleted file mode 100644 index b6b24ac90..000000000 --- a/.github/workflows/publish-android.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Publish ldk-node-android to Maven Central -on: [workflow_dispatch] - -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: "Install Rust Android targets" - run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi - - - name: "Build ldk-node-android library" - run: | - export PATH=$PATH:$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin - ./scripts/uniffi_bindgen_generate_kotlin_android.sh - - - 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 bindings/kotlin/ldk-node-android - ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository diff --git a/.github/workflows/publish-jvm.yml b/.github/workflows/publish-jvm.yml deleted file mode 100644 index e9b0a3594..000000000 --- a/.github/workflows/publish-jvm.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Publish ldk-node-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: Install aarch64 Rust target - run: rustup target add aarch64-apple-darwin - - - name: Build ldk-node-jvm library - run: | - ./scripts/uniffi_bindgen_generate_kotlin.sh - - # 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/ldk-node/ldk-node/bindings/kotlin/ldk-node-jvm/lib/src/main/resources/ - - build-jvm-full-library: - name: "Create full ldk-node-jvm library" - needs: [build-jvm-macOS-M1-native-lib] - 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: "Build ldk-node-jvm library" - run: | - ./scripts/uniffi_bindgen_generate_kotlin.sh - - - 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: ./bindings/kotlin/ldk-node-jvm/lib/src/main/resources/ - - - 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 bindings/kotlin/ldk-node-jvm - ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml deleted file mode 100644 index 21c0139a2..000000000 --- a/.github/workflows/python.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: CI Checks - Python Tests - -on: [push, pull_request] - -jobs: - check-python: - runs-on: ubuntu-latest - - env: - LDK_NODE_PYTHON_DIR: bindings/python - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Generate Python bindings - run: ./scripts/uniffi_bindgen_generate_python.sh - - - name: Start bitcoind and electrs - run: docker compose up -d - - - name: Install testing prerequisites - run: | - pip3 install requests - - - name: Run Python unit tests - env: - BITCOIN_CLI_BIN: "docker exec ldk-node-bitcoin-1 bitcoin-cli" - BITCOIND_RPC_USER: "user" - BITCOIND_RPC_PASSWORD: "pass" - ESPLORA_ENDPOINT: "http://127.0.0.1:3002" - run: | - cd $LDK_NODE_PYTHON_DIR - python3 -m unittest discover -s src/ldk_node diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 14d87d2eb..4b77edca9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,8 +17,6 @@ jobs: ] toolchain: [ stable, - beta, - 1.63.0, # Our MSRV ] include: - toolchain: stable @@ -29,8 +27,6 @@ jobs: platform: macos-latest - toolchain: stable platform: windows-latest - - toolchain: 1.63.0 - msrv: true runs-on: ${{ matrix.platform }} steps: - name: Checkout source code @@ -65,10 +61,6 @@ jobs: - name: Build with UniFFI support on Rust ${{ matrix.toolchain }} if: matrix.build-uniffi run: cargo build --features uniffi --verbose --color always - - name: Build documentation on Rust ${{ matrix.toolchain }} - run: | - cargo doc --release --verbose --color always - cargo doc --document-private-items --verbose --color always - name: Check release build on Rust ${{ matrix.toolchain }} run: cargo check --release --verbose --color always - name: Check release build with UniFFI support on Rust ${{ matrix.toolchain }} diff --git a/.gitignore b/.gitignore index de30b070c..9d3368337 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ swift.swiftdoc /bindings/swift/LDKNodeFFI.xcframework /bindings/kotlin/ldk-node-android/lib/src/main/jniLibs /bindings/kotlin/ldk-node-android/lib/src/main/kotlin/org/lightningdevkit/ldknode/ldk_node.kt + +/ffi/ diff --git a/Cargo.toml b/Cargo.toml index 31286b8f9..1d6bf9c1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,6 +99,8 @@ uniffi = { version = "0.25.3", features = ["build"], optional = true } [profile.release] panic = "abort" +lto = true +rpath = true [profile.dev] panic = "abort" diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index e353b4054..1cdde4032 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -72,7 +72,7 @@ interface LDKNode { [Throws=NodeError] PaymentHash send_payment_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat); [Throws=NodeError] - PaymentHash send_spontaneous_payment(u64 amount_msat, PublicKey node_id); + PaymentHash send_spontaneous_payment(u64 amount_msat, PublicKey node_id, sequence custom_tlvs); [Throws=NodeError] void send_payment_probes([ByRef]Bolt11Invoice invoice); [Throws=NodeError] @@ -131,6 +131,7 @@ enum NodeError { "InvalidInvoice", "InvalidChannelId", "InvalidNetwork", + "InvalidCustomTlv", "DuplicatePayment", "InsufficientFunds", "LiquiditySourceUnavailable", @@ -316,6 +317,11 @@ enum LogLevel { "Error", }; +dictionary TlvEntry { + u64 type; + sequence value; +}; + [Custom] typedef string Txid; diff --git a/scripts/uniffi_bindgen_generate_go.sh b/scripts/uniffi_bindgen_generate_go.sh new file mode 100755 index 000000000..9cf563a9e --- /dev/null +++ b/scripts/uniffi_bindgen_generate_go.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +if ! command -v cross &> /dev/null; then + echo "cross-rs is required to build bindings. Install it by running:" + echo " cargo install cross --git https://github.com/cross-rs/cross" + exit 1 +fi + +uniffi-bindgen-go bindings/ldk_node.udl -o ffi/golang -c ./uniffi.toml + +build_lib() { + local TOOL=$1 + local TARGET=$2 + local OUTPUT_FILE=$3 + + $TOOL build --release --target $TARGET --features uniffi || exit 1 + mkdir -p "ffi/golang/ldk_node/$TARGET" || exit 1 + cp "target/$TARGET/release/$OUTPUT_FILE" "ffi/golang/ldk_node/$TARGET/" || exit 1 +} + +is_target_installed() { + local TARGET=$1 + rustup target list --installed | grep -q $TARGET +} + +# If we're running on macOS, build the macOS library using the host compiler. +# Cross compilation is not supported (needs more complex setup). +if [[ "$OSTYPE" == "darwin"* ]]; then + build_lib "cargo" "aarch64-apple-darwin" "libldk_node.dylib" +fi + +build_lib "cross" "x86_64-unknown-linux-gnu" "libldk_node.so" +build_lib "cross" "x86_64-pc-windows-gnu" "ldk_node.dll" diff --git a/src/error.rs b/src/error.rs index 0182b3092..730e95460 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,6 +61,8 @@ pub enum Error { InvalidChannelId, /// The given network is invalid. InvalidNetwork, + /// The custom TLVs are invalid. + InvalidCustomTlv, /// A payment with the given hash has already been initiated. DuplicatePayment, /// The available funds are insufficient to complete the given operation. @@ -107,6 +109,7 @@ impl fmt::Display for Error { Self::InvalidInvoice => write!(f, "The given invoice is invalid."), Self::InvalidChannelId => write!(f, "The given channel ID is invalid."), Self::InvalidNetwork => write!(f, "The given network is invalid."), + Self::InvalidCustomTlv => write!(f, "The given custom TLVs are invalid."), Self::DuplicatePayment => { write!(f, "A payment with the given hash has already been initiated.") }, diff --git a/src/lib.rs b/src/lib.rs index 87b6ecb42..e3f9ccc31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,7 +134,7 @@ use types::{ Broadcaster, ChainMonitor, ChannelManager, FeeEstimator, KeysManager, NetworkGraph, PeerManager, Router, Scorer, Sweeper, Wallet, }; -pub use types::{ChannelDetails, PeerDetails, UserChannelId}; +pub use types::{ChannelDetails, PeerDetails, TlvEntry, UserChannelId}; use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger}; @@ -1234,7 +1234,7 @@ impl Node { /// Send a spontaneous, aka. "keysend", payment pub fn send_spontaneous_payment( - &self, amount_msat: u64, node_id: PublicKey, + &self, amount_msat: u64, node_id: PublicKey, custom_tlvs: Vec, ) -> Result { let rt_lock = self.runtime.read().unwrap(); if rt_lock.is_none() { @@ -1257,7 +1257,12 @@ impl Node { PaymentParameters::from_node_id(node_id, self.config.default_cltv_expiry_delta), amount_msat, ); - let recipient_fields = RecipientOnionFields::spontaneous_empty(); + let recipient_fields = RecipientOnionFields::spontaneous_empty() + .with_custom_tlvs(custom_tlvs.into_iter().map(|tlv| (tlv.r#type, tlv.value)).collect()) + .map_err(|_| { + log_error!(self.logger, "Payment error: invalid custom TLVs."); + Error::InvalidCustomTlv + })?; match self.channel_manager.send_spontaneous_payment_with_retry( Some(payment_preimage), diff --git a/src/types.rs b/src/types.rs index 6269b3ddf..96b83c11e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -456,3 +456,12 @@ impl Default for ChannelConfig { LdkChannelConfig::default().into() } } + +/// Custom TLV entry. +pub struct TlvEntry { + /// Type number. + pub r#type: u64, + + /// Serialized value. + pub value: Vec, +} diff --git a/tests/common.rs b/tests/common.rs index 815056b82..8b910ce56 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,7 +3,7 @@ use ldk_node::io::sqlite_store::SqliteStore; use ldk_node::{ - Builder, Config, Event, LogLevel, Node, NodeError, PaymentDirection, PaymentStatus, + Builder, Config, Event, LogLevel, Node, NodeError, PaymentDirection, PaymentStatus, TlvEntry, }; use lightning::ln::msgs::SocketAddress; @@ -489,8 +489,11 @@ pub(crate) fn do_channel_full_cycle( // Test spontaneous/keysend payments println!("\nA send_spontaneous_payment"); let keysend_amount_msat = 2500_000; - let keysend_payment_hash = - node_a.send_spontaneous_payment(keysend_amount_msat, node_b.node_id()).unwrap(); + let tlv1 = TlvEntry { r#type: 131073, value: vec![0x00, 0x11, 0x22, 0x33] }; + let tlv2 = TlvEntry { r#type: 131075, value: vec![0xaa, 0xbb] }; + let keysend_payment_hash = node_a + .send_spontaneous_payment(keysend_amount_msat, node_b.node_id(), vec![tlv1, tlv2]) + .unwrap(); expect_event!(node_a, PaymentSuccessful); let received_keysend_amount = match node_b.wait_next_event() { ref e @ Event::PaymentReceived { amount_msat, .. } => {