diff --git a/.dir-locals.el b/.dir-locals.el index 1ca27d4ff271..518359236abd 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -4,4 +4,10 @@ (c-basic-offset . 8) (tab-width . 8) )) -) + + (c++-mode . ((c-file-style . "linux") + (indent-tabs-mode . t) + (show-trailing-whitespace . t) + (c-basic-offset . 8) + (tab-width . 8) + ))) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh deleted file mode 100755 index 1037cc97d8aa..000000000000 --- a/.github/scripts/build.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -set -e -echo "Running in $(pwd)" -export ARCH=${ARCH:-64} -export BOLTDIR=bolts -export CC=${COMPILER:-gcc} -export COMPAT=${COMPAT:-1} -export TEST_CHECK_DBSTMTS=${TEST_CHECK_DBSTMTS:-0} -export DEVELOPER=${DEVELOPER:-1} -export EXPERIMENTAL_FEATURES=${EXPERIMENTAL_FEATURES:-0} -export PATH=$CWD/dependencies/bin:"$HOME"/.local/bin:"$PATH" -export PYTEST_OPTS="--maxfail=5 --suppress-no-test-exit-code ${PYTEST_OPTS}" -export PYTEST_PAR=${PYTEST_PAR:-10} -export PYTEST_SENTRY_ALWAYS_REPORT=1 -export SLOW_MACHINE=1 -export TEST_CMD=${TEST_CMD:-"make -j $PYTEST_PAR pytest"} -export TEST_DB_PROVIDER=${TEST_DB_PROVIDER:-"sqlite3"} -export TEST_NETWORK=${NETWORK:-"regtest"} -export TIMEOUT=900 -export VALGRIND=${VALGRIND:-0} -export FUZZING=${FUZZING:-0} -export LIGHTNINGD_POSTGRES_NO_VACUUM=1 - -# Fail if any commands fail. -set -e - -pip3 install --user poetry -poetry config virtualenvs.create false --local -poetry install - -git clone https://github.com/lightning/bolts.git ../${BOLTDIR} -git submodule update --init --recursive - -./configure CC="$CC" -cat config.vars - -cat << EOF > pytest.ini -[pytest] -addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 -markers = - slow_test: marks tests as slow (deselect with '-m "not slow_test"') -EOF - -if [ "$TARGET_HOST" == "arm-linux-gnueabihf" ] || [ "$TARGET_HOST" == "aarch64-linux-gnu" ] -then - export QEMU_LD_PREFIX=/usr/"$TARGET_HOST"/ - export MAKE_HOST="$TARGET_HOST" - export BUILD=x86_64-pc-linux-gnu - export AR="$TARGET_HOST"-ar - export AS="$TARGET_HOST"-as - export CC="$TARGET_HOST"-gcc - export CXX="$TARGET_HOST"-g++ - export LD="$TARGET_HOST"-ld - export STRIP="$TARGET_HOST"-strip - export CONFIGURATION_WRAPPER=qemu-"${TARGET_HOST%%-*}"-static - - wget -q https://zlib.net/fossils/zlib-1.2.13.tar.gz - tar xf zlib-1.2.13.tar.gz - cd zlib-1.2.13 || exit 1 - ./configure --prefix="$QEMU_LD_PREFIX" - make - sudo make install - cd .. || exit 1 - rm zlib-1.2.13.tar.gz && rm -rf zlib-1.2.13 - - wget -q https://www.sqlite.org/2018/sqlite-src-3260000.zip - unzip -q sqlite-src-3260000.zip - cd sqlite-src-3260000 || exit 1 - automake --add-missing --force-missing --copy || true - ./configure --disable-tcl \ - --enable-static \ - --disable-readline \ - --disable-threadsafe \ - --disable-load-extension \ - --host="$TARGET_HOST" \ - --prefix="$QEMU_LD_PREFIX" - make - sudo make install - cd .. || exit 1 - rm sqlite-src-3260000.zip - rm -rf sqlite-src-3260000 - - wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz - tar xf gmp-6.1.2.tar.xz - cd gmp-6.1.2 || exit 1 - ./configure --disable-assembly --prefix="$QEMU_LD_PREFIX" --host="$TARGET_HOST" - make - sudo make install - cd .. - rm gmp-6.1.2.tar.xz - rm -rf gmp-6.1.2 - - ./configure CC="$TARGET_HOST-gcc" --enable-static - - make -s -j32 CC="$TARGET_HOST-gcc" -else - eatmydata make -j32 - # shellcheck disable=SC2086 - eatmydata $TEST_CMD -fi diff --git a/.github/scripts/setup.sh b/.github/scripts/setup.sh index 4a7ebd5bc4b0..f517cfd63dfa 100755 --- a/.github/scripts/setup.sh +++ b/.github/scripts/setup.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e export DEBIAN_FRONTEND=noninteractive -export BITCOIN_VERSION=24.0.1 +export BITCOIN_VERSION=25.0 export ELEMENTS_VERSION=22.0.2 export RUST_VERSION=stable @@ -15,6 +15,7 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ build-essential \ clang \ cppcheck \ + curl \ docbook-xml \ eatmydata \ gcc-aarch64-linux-gnu \ @@ -24,7 +25,6 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ git \ libc6-dev-arm64-cross \ libc6-dev-armhf-cross \ - libgmp-dev \ libpq-dev \ libprotobuf-c-dev \ libsqlite3-dev \ @@ -74,12 +74,30 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ # We also need a relatively recent protobuf-compiler, at least 3.12.0, # in order to support the experimental `optional` flag. -PROTOC_VERSION=3.15.8 + +# BUT WAIT! Gentoo wants this to match the version from the Python protobuf, +# which comes from the same tree. Makes sense! + +# And +# grpcio-tools-1.54.0` requires `protobuf = ">=4.21.6,<5.0dev"` + +# Now, protoc changed to date-based releases, BUT Python protobuf +# didn't, so Python protobuf 4.21.12 (in Ubuntu 23.04) corresponds to +# protoc 21.12 (which, FYI, is packaged in Ubuntu as version 3.21.12). + +# So we're going to nail these versions as 21.12, which is what recent +# Ubuntu has, and hopefully everyone else can get. And this means that +# When CI checks that no files have changed under regeneration, you won't +# get a fail just because the dev's protoc is a different version. + +# Honorable mention go to Matt Whitlock for spelunking this horror with me! + +PROTOC_VERSION=21.12 PB_REL="https://github.com/protocolbuffers/protobuf/releases" curl -LO $PB_REL/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip -sudo unzip protoc-3.15.8-linux-x86_64.zip -d /usr/local/ +sudo unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local/ sudo chmod a+x /usr/local/bin/protoc export PROTOC=/usr/local/bin/protoc export PATH=$PATH:/usr/local/bin env -ls -lha /usr/local/bin \ No newline at end of file +ls -lha /usr/local/bin diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 96cd61dad310..e2db5b4dfb66 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -11,18 +11,12 @@ jobs: runs-on: macos-10.15 name: Build and test on FreeBSD timeout-minutes: 120 - env: - DEVELOPER: 1 - VALGRIND: 0 - EXPERIMENTAL_FEATURES: 0 - COMPAT: 1 steps: - uses: actions/checkout@v2 - name: Test in FreeBSD id: test uses: vmactions/freebsd-vm@v0.1.5 with: - envs: 'DEVELOPER VALGRIND EXPERIMENTAL_FEATURES COMPAT' usesh: true prepare: | pkg install -y \ @@ -36,7 +30,6 @@ jobs: autoconf \ automake \ libtool \ - gmp \ bash \ gettext \ sqlite3 \ @@ -46,12 +39,12 @@ jobs: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2021-08-3z1 cd /tmp/ || exit 1 - wget https://bitcoincore.org/bin/bitcoin-core-24.0.1/bitcoin-24.0.1-x86_64-linux-gnu.tar.gz - tar -xf bitcoin-24.0.1-x86_64-linux-gnu.tar.bz2 - sudo mv bitcoin-24.0.1/bin/* /usr/local/bin + wget https://bitcoincore.org/bin/bitcoin-core-25.0/bitcoin-25.0-x86_64-linux-gnu.tar.gz + tar -xf bitcoin-25.0-x86_64-linux-gnu.tar.bz2 + sudo mv bitcoin-25.0/bin/* /usr/local/bin rm -rf \ - bitcoin-24.0.1-x86_64-linux-gnu.tar.gz \ - bitcoin-24.0.1 + bitcoin-25.0-x86_64-linux-gnu.tar.gz \ + bitcoin-25.0 run: | PATH=/root/.local/bin:$PATH @@ -75,7 +68,8 @@ jobs: git config --global --add safe.directory `pwd` git submodule update --init --recursive - ./configure CC="$CC" + ./configure CC="$CC" --enable-developer --disable-valgrind + cat config.vars cat << EOF > pytest.ini diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c22fb8f0304d..47ccd1906937 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,19 +22,27 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 30 env: - RUST: 1 - COMPAT: 1 BOLTDIR: bolts strategy: fail-fast: true steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Rebase + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git fetch origin ${{ github.base_ref }} + git rebase origin/${{ github.base_ref }} - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | @@ -46,233 +54,232 @@ jobs: # We're going to check BOLT quotes, so get the latest version git clone https://github.com/lightning/bolts.git ../${BOLTDIR} - name: Configure - run: ./configure + run: ./configure --enable-debugbuild --enable-rust - name: Check source - run: make -j 4 check-source + run: make -j 4 check-source BASE_REF="origin/${{ github.base_ref }}" - name: Check Generated Files have been updated run: make -j 4 check-gen-updated - name: Check docs run: make -j 4 check-doc - check-units: - # The unit test checks are not in the critical path (not dependent - # on the integration tests), so run them with `valgrind` - name: Run unit tests + compile: + name: Compile CLN ${{ matrix.cfg }} runs-on: ubuntu-22.04 timeout-minutes: 30 - env: - COMPAT: 1 - VALGRIND: 1 - BOLTDIR: bolts needs: - prebuild + strategy: + fail-fast: true + matrix: + include: + - CFG: gcc-dev1 + DEVELOPER: 1 + VALGRIND: 1 + COMPILER: gcc + - CFG: gcc-dev0 + DEVELOPER: 0 + VALGRIND: 1 + COMPILER: gcc + # While we're at it let's try to compile with clang + - CFG: clang-dev1 + DEVELOPER: 1 + VALGRIND: 1 + COMPILER: clang + - CFG: clang-sanitizers + DEVELOPER: 1 + COMPILER: clang + ASAN: 1 + UBSAN: 1 + VALGRIND: 0 steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | bash -x .github/scripts/setup.sh - sudo apt-get install -y -qq lowdown - pip install -U pip wheel poetry - # Export and then use pip to install into the current env - poetry export -o /tmp/requirements.txt --without-hashes --with dev - pip install -r /tmp/requirements.txt - # We're going to check BOLT quotes, so get the latest version - git clone https://github.com/lightning/bolts.git ../${BOLTDIR} - name: Build + env: + DEVELOPER: ${{ matrix.DEVELOPER }} + COMPILER: ${{ matrix.COMPILER }} + ASAN: ${{ matrix.ASAN }} + UBSAN: ${{ matrix.UBSAN }} + VALGRIND: ${{ matrix.VALGRIND }} + COMPAT: 1 + CFG: ${{ matrix.CFG }} run: | - ./configure - make -j $(nproc) check-units installcheck + set -e + pip3 install --user pip wheel poetry + poetry export -o requirements.txt --with dev --without-hashes + python3 -m pip install -r requirements.txt + ./configure --enable-debugbuild CC="$COMPILER" - check-fuzz: - name: Run fuzz regression tests + make -j $(nproc) testpack.tar.bz2 + + # Rename now so we don't clash + mv testpack.tar.bz2 cln-${CFG}.tar.bz2 + - name: Check rust packages + run: cargo test --all + - uses: actions/upload-artifact@v3 + with: + name: cln-${{ matrix.CFG }}.tar.bz2 + path: cln-${{ matrix.CFG }}.tar.bz2 + + check-units: + # The unit test checks are not in the critical path (not dependent + # on the integration tests), so run them with `valgrind` + name: Run unit tests runs-on: ubuntu-22.04 + timeout-minutes: 30 env: - COMPAT: 1 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - ASAN: 1 - UBSAN: 1 - VALGRIND: 0 + BOLTDIR: bolts needs: - - prebuild + - compile + strategy: + fail-fast: true + matrix: + include: + - CFG: gcc-dev1 + VALGRIND: 1 + - CFG: clang-sanitizers + VALGRIND: 0 steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | bash -x .github/scripts/setup.sh + sudo apt-get install -y -qq lowdown pip install -U pip wheel poetry # Export and then use pip to install into the current env poetry export -o /tmp/requirements.txt --without-hashes --with dev pip install -r /tmp/requirements.txt + # We're going to check BOLT quotes, so get the latest version + git clone https://github.com/lightning/bolts.git ../${BOLTDIR} - - name: Build + - name: Download build + uses: actions/download-artifact@v3 + with: + name: cln-${{ matrix.CFG }}.tar.bz2 + + - name: Check run: | - ./configure --enable-fuzzing CC=clang - make -j $(nproc) check-fuzz + tar -xaf cln-${{ matrix.CFG }}.tar.bz2 + make -j $(nproc) check-units installcheck VALGRIND=${{ matrix.VALGRIND }} - compile: - name: Compile CLN ${{ matrix.cfg }} + check-fuzz: + name: Run fuzz regression tests runs-on: ubuntu-22.04 - timeout-minutes: 30 - env: - COMPAT: 1 needs: - prebuild - strategy: - fail-fast: true - matrix: - include: - - CFG: gcc-dev1-exp1 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - COMPILER: gcc - - CFG: gcc-dev1-exp0 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 0 - COMPILER: gcc - - CFG: gcc-dev0-exp1 - DEVELOPER: 0 - EXPERIMENTAL_FEATURES: 1 - COMPILER: gcc - - CFG: gcc-dev0-exp0 - DEVELOPER: 0 - EXPERIMENTAL_FEATURES: 0 - COMPILER: gcc - # While we're at it let's try to compile with clang - - CFG: clang-dev1-exp1 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - COMPILER: clang steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | bash -x .github/scripts/setup.sh + pip install -U pip wheel poetry + # Export and then use pip to install into the current env + poetry export -o /tmp/requirements.txt --without-hashes --with dev + pip install -r /tmp/requirements.txt - name: Build - env: - VALGRIND: ${{ matrix.VALGRIND }} - DEVELOPER: ${{ matrix.DEVELOPER }} - EXPERIMENTAL_FEATURES: ${{ matrix.EXPERIMENTAL_FEATURES }} - COMPILER: ${{ matrix.COMPILER }} - COMPAT: 1 - CFG: ${{ matrix.CFG }} run: | - set -e - pip3 install --user pip wheel poetry - poetry export -o requirements.txt --with dev --without-hashes - python3 -m pip install -r requirements.txt - ./configure CC="$COMPILER" - - make -j $(nproc) testpack.tar.bz2 - - # Rename now so we don't clash - mv testpack.tar.bz2 cln-${CFG}.tar.bz2 - - name: Check rust packages - run: cargo test --all - - uses: actions/upload-artifact@v2.2.4 - with: - name: cln-${{ matrix.CFG }}.tar.bz2 - path: cln-${{ matrix.CFG }}.tar.bz2 + ./configure --enable-debugbuild --enable-fuzzing --enable-developer --disable-valgrind CC=clang + make -j $(nproc) check-fuzz integration: name: Test CLN ${{ matrix.name }} runs-on: ubuntu-22.04 timeout-minutes: 120 env: - COMPAT: 1 - BITCOIN_VERSION: 24.0.1 + BITCOIN_VERSION: "25.0" ELEMENTS_VERSION: 22.0.2 RUST_PROFILE: release # Has to match the one in the compile step + PYTEST_OPTS: --timeout=1200 needs: - compile strategy: fail-fast: true matrix: include: - - NAME: gcc-dev1-exp1 - CFG: gcc-dev1-exp1 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - TEST_DB_PROVIDER: sqlite3 - COMPILER: gcc - TEST_NETWORK: regtest - - NAME: gcc-dev1-exp0 - CFG: gcc-dev1-exp0 + - NAME: gcc-dev1 + CFG: gcc-dev1 DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 0 TEST_DB_PROVIDER: sqlite3 COMPILER: gcc TEST_NETWORK: regtest - - NAME: gcc-dev0-exp1 - CFG: gcc-dev0-exp1 + - NAME: gcc-dev0 + CFG: gcc-dev0 DEVELOPER: 0 - EXPERIMENTAL_FEATURES: 1 - TEST_DB_PROVIDER: sqlite3 - COMPILER: gcc - TEST_NETWORK: regtest - - NAME: gcc-dev0-exp0 - CFG: gcc-dev0-exp0 - DEVELOPER: 0 - EXPERIMENTAL_FEATURES: 0 TEST_DB_PROVIDER: sqlite3 COMPILER: gcc TEST_NETWORK: regtest # While we're at it let's try to compile with clang - - NAME: clang-dev1-exp1 - CFG: clang-dev1-exp1 + - NAME: clang-dev1 + CFG: clang-dev1 DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 TEST_DB_PROVIDER: sqlite3 COMPILER: clang TEST_NETWORK: regtest # And of course we want to test postgres too - NAME: postgres - CFG: gcc-dev1-exp1 + CFG: gcc-dev1 DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 COMPILER: gcc TEST_DB_PROVIDER: postgres TEST_NETWORK: regtest # And don't forget about elements (like cdecker did when # reworking the CI...) - NAME: liquid - CFG: gcc-dev1-exp1 + CFG: gcc-dev1 DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 COMPILER: gcc TEST_NETWORK: liquid-regtest TEST_DB_PROVIDER: sqlite3 + # And dual funding! + - NAME: dual-fund + CFG: gcc-dev1 + TEST_DB_PROVIDER: sqlite3 + COMPILER: gcc + TEST_NETWORK: regtest + DEVELOPER: 1 + EXPERIMENTAL_DUAL_FUND: 1 + # And splicing! + - NAME: splicing + CFG: gcc-dev1 + TEST_DB_PROVIDER: sqlite3 + COMPILER: gcc + TEST_NETWORK: regtest + DEVELOPER: 1 + EXPERIMENTAL_DUAL_FUND: 1 + EXPERIMENTAL_SPLICING: 1 steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | @@ -289,10 +296,10 @@ jobs: - name: Test env: - VALGRIND: ${{ matrix.VALGRIND }} DEVELOPER: ${{ matrix.DEVELOPER }} - EXPERIMENTAL_FEATURES: ${{ matrix.EXPERIMENTAL_FEATURES }} COMPILER: ${{ matrix.COMPILER }} + EXPERIMENTAL_DUAL_FUND: ${{ matrix.EXPERIMENTAL_DUAL_FUND }} + EXPERIMENTAL_SPLICING: ${{ matrix.EXPERIMENTAL_SPLICING }} COMPAT: 1 CFG: ${{ matrix.CFG }} SLOW_MACHINE: 1 @@ -300,24 +307,21 @@ jobs: TEST_DEBUG: 1 TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }} TEST_NETWORK: ${{ matrix.TEST_NETWORK }} + LIGHTNINGD_POSTGRES_NO_VACUUM: 1 run: | tar -xaf cln-${CFG}.tar.bz2 - poetry run pytest tests/ -vvv -n ${PYTEST_PAR} ${PYTEST_OPTS} + VALGRIND=0 poetry run pytest tests/ -vvv -n ${PYTEST_PAR} ${PYTEST_OPTS} integration-valgrind: name: Valgrind Test CLN ${{ matrix.name }} runs-on: ubuntu-22.04 timeout-minutes: 120 env: - COMPAT: 1 - BITCOIN_VERSION: 24.0.1 + BITCOIN_VERSION: "25.0" ELEMENTS_VERSION: 22.0.2 RUST_PROFILE: release # Has to match the one in the compile step - VALGRIND: 1 - CFG: gcc-dev1-exp1 - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - PYTEST_OPTS: --test-group-random-seed=42 + CFG: gcc-dev1 + PYTEST_OPTS: --test-group-random-seed=42 --timeout=1800 needs: - compile strategy: @@ -348,10 +352,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.8 - name: Install dependencies run: | @@ -365,20 +369,87 @@ jobs: - name: Download build uses: actions/download-artifact@v3 with: - name: cln-gcc-dev1-exp1.tar.bz2 + name: cln-gcc-dev1.tar.bz2 - name: Unpack build - run: tar -xvjf cln-gcc-dev1-exp1.tar.bz2 + run: tar -xvjf cln-gcc-dev1.tar.bz2 - name: Test env: - COMPAT: 1 SLOW_MACHINE: 1 TEST_DEBUG: 1 run: | + VALGRIND=1 poetry run pytest tests/ -vvv -n 3 ${PYTEST_OPTS} ${{ matrix.PYTEST_OPTS }} - sed -i 's/VALGRIND=0/VALGRIND=1/g' config.vars - poetry run pytest tests/ -vvv -n 3 ${PYTEST_OPTS} ${{ matrix.PYTEST_OPTS }} + integration-sanitizers: + name: Sanitizers Test CLN + runs-on: ubuntu-22.04 + timeout-minutes: 120 + env: + BITCOIN_VERSION: "25.0" + ELEMENTS_VERSION: 22.0.2 + RUST_PROFILE: release + SLOW_MACHINE: 1 + TEST_DEBUG: 1 + PYTEST_OPTS: --test-group-random-seed=42 --timeout=1800 + needs: + - compile + strategy: + fail-fast: true + matrix: + include: + - NAME: ASan/UBSan (01/10) + PYTEST_OPTS: --test-group=1 --test-group-count=10 + - NAME: ASan/UBSan (02/10) + PYTEST_OPTS: --test-group=2 --test-group-count=10 -n 1 + - NAME: ASan/UBSan (03/10) + PYTEST_OPTS: --test-group=3 --test-group-count=10 + - NAME: ASan/UBSan (04/10) + PYTEST_OPTS: --test-group=4 --test-group-count=10 + - NAME: ASan/UBSan (05/10) + PYTEST_OPTS: --test-group=5 --test-group-count=10 + - NAME: ASan/UBSan (06/10) + PYTEST_OPTS: --test-group=6 --test-group-count=10 + - NAME: ASan/UBSan (07/10) + PYTEST_OPTS: --test-group=7 --test-group-count=10 + - NAME: ASan/UBSan (08/10) + PYTEST_OPTS: --test-group=8 --test-group-count=10 + - NAME: ASan/UBSan (09/10) + PYTEST_OPTS: --test-group=9 --test-group-count=10 + - NAME: ASan/UBSan (10/10) + PYTEST_OPTS: --test-group=10 --test-group-count=10 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + bash -x .github/scripts/setup.sh + set -e + pip3 install --user pip wheel poetry + poetry export -o requirements.txt --with dev --without-hashes + python3 -m pip install -r requirements.txt + poetry install + + - name: Install bitcoind + run: .github/scripts/install-bitcoind.sh + + - name: Download build + uses: actions/download-artifact@v3 + with: + name: cln-clang-sanitizers.tar.bz2 + + - name: Unpack build + run: tar -xvjf cln-clang-sanitizers.tar.bz2 + + - name: Test + run: | + poetry run pytest tests/ -vvv -n 2 ${PYTEST_OPTS} ${{ matrix.PYTEST_OPTS }} gather: # A dummy task that depends on the full matrix of tests, and @@ -389,6 +460,8 @@ jobs: needs: - integration - check-units + - integration-valgrind + - integration-sanitizers steps: - name: Complete run: | diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index a7c96d830d57..280052d32886 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -7,11 +7,6 @@ jobs: name: Smoke Test macOS runs-on: macos-latest timeout-minutes: 120 - env: - DEVELOPER: 1 - VALGRIND: 0 - EXPERIMENTAL_FEATURES: 1 - COMPAT: 0 strategy: fail-fast: true steps: @@ -22,8 +17,8 @@ jobs: run: | export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.10/bin:$PATH" - export BITCOIN_VERSION=24.0.1 - brew install wget autoconf automake libtool python@3.10 gmp gnu-sed gettext libsodium + export BITCOIN_VERSION=25.0 + brew install wget autoconf automake libtool python@3.10 gnu-sed gettext libsodium ( cd /tmp/ @@ -40,11 +35,6 @@ jobs: - name: Build env: - VALGRIND: ${{ matrix.VALGRIND }} - DEVELOPER: ${{ matrix.DEVELOPER }} - EXPERIMENTAL_FEATURES: ${{ matrix.EXPERIMENTAL_FEATURES }} - COMPILER: ${{ matrix.COMPILER }} - COMPAT: ${{ matrix.COMPAT }} PYTEST_PAR: ${{ matrix.PYTEST_PAR }} PYTEST_OPTS: ${{ matrix.PYTEST_OPTS }} NO_PYTHON: ${{ matrix.NO_PYTHON }} @@ -65,5 +55,5 @@ jobs: slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF - python3.10 -m poetry run ./configure + python3.10 -m poetry run ./configure --enable-developer --disable-valgrind --disable-compat python3.10 -m poetry run make diff --git a/.github/workflows/prototest.yaml b/.github/workflows/prototest.yaml index 93da5bd7ab1f..c9940f66f30a 100644 --- a/.github/workflows/prototest.yaml +++ b/.github/workflows/prototest.yaml @@ -29,13 +29,12 @@ jobs: -e TARGET_HOST=${{ matrix.TARGET_HOST }} \ -e VALGRIND=${{ matrix.valgrind }} \ -e DEVELOPER=1 \ - -e EXPERIMENTAL_FEATURES=1 \ - -e COMPAT=0 \ -e PYTEST_PAR=2 \ -e PYTEST_OPTS="--timeout=300" \ -e TEST_CMD="make check-protos" \ -e TEST_GROUP=1 \ -e TEST_GROUP_COUNT=1 \ + -e TIMEOUT=120 \ cln-ci-ubuntu - name: Upload Unit Test Results if: always() diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 115a4fc28e33..db3fdb1d3799 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -25,6 +25,8 @@ jobs: WORKDIR: contrib/pyln-testing - PACKAGE: pyln-proto WORKDIR: contrib/pyln-proto + - PACKAGE: pyln-grpc-proto + WORKDIR: contrib/pyln-grpc-proto # Bolt packages are handled differently #- PACKAGE: pyn-bolt1 # WORKDIR: contrib/pyln-spec/bolt1/ @@ -40,10 +42,10 @@ jobs: # Need to fetch entire history in order to locate the version tag fetch-depth: 0 - - name: Set up Python 3.7 + - name: Set up Python 3.8 uses: actions/setup-python@v1 with: - python-version: 3.7 + python-version: 3.8 - name: Install pypa/build and poetry run: >- diff --git a/.github/workflows/rdme-docs-sync.yml b/.github/workflows/rdme-docs-sync.yml new file mode 100644 index 000000000000..e29809b1c568 --- /dev/null +++ b/.github/workflows/rdme-docs-sync.yml @@ -0,0 +1,50 @@ +name: ReadMe Sync + +on: + push: + branches: + - 'master' + paths: + - 'doc/**' + +jobs: + rdme-docs-sync: + runs-on: ubuntu-latest + steps: + - name: Check out repo 📚 + uses: actions/checkout@v3 + + - name: Sync doc/getting-started/ 🚀 + uses: readmeio/rdme@v8 + env: + README_API_KEY: ${{ secrets.README_API_KEY }} + with: + rdme: docs doc/getting-started --key=${{ env.README_API_KEY }} --version=1 + + - name: Sync doc/beginners-guide/ 🚀 + uses: readmeio/rdme@v8 + env: + README_API_KEY: ${{ secrets.README_API_KEY }} + with: + rdme: docs doc/beginners-guide --key=${{ env.README_API_KEY }} --version=1 + + - name: Sync doc/node-operators-guide/ 🚀 + uses: readmeio/rdme@v8 + env: + README_API_KEY: ${{ secrets.README_API_KEY }} + with: + rdme: docs doc/node-operators-guide --key=${{ env.README_API_KEY }} --version=1 + + - name: Sync doc/developers-guide/ 🚀 + uses: readmeio/rdme@v8 + env: + README_API_KEY: ${{ secrets.README_API_KEY }} + with: + rdme: docs doc/developers-guide --key=${{ env.README_API_KEY }} --version=1 + + - name: Sync doc/contributing-to-core-lightning/ 🚀 + uses: readmeio/rdme@v8 + env: + README_API_KEY: ${{ secrets.README_API_KEY }} + with: + rdme: docs doc/contribute-to-core-lightning --key=${{ env.README_API_KEY }} --version=1 diff --git a/.gitignore b/.gitignore index 14aeba09131c..8d2e96ec68db 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ focal/ jammy/ release/ .vscode/ + +# Ignore release verification Sha256Sums +SHA256SUMS-* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 52d90feb6b59..6e01161c764e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,7 @@ build: curl -s -H "Authorization: token $GITHUB_STATUS_TOKEN" -X POST --data '{"state": "pending", "description": "Gitlab-CI is building the commit", "context": "gitlab-ci"}' - https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_BUILD_REF || true + https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_COMMIT_SHA || true script: - make - make -j 12 check @@ -27,7 +27,7 @@ update-status-fail: when: on_failure script: - >- - curl -s -H "Authorization: token $GITHUB_STATUS_TOKEN" -X POST --data '{"state": "failure", "description": "Gitlab-CI build failed, please contact @cdecker for details about build #$CI_BUILD_ID.", "context": "gitlab-ci"}' https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_BUILD_REF || true + curl -s -H "Authorization: token $GITHUB_STATUS_TOKEN" -X POST --data '{"state": "failure", "description": "Gitlab-CI build failed, please contact @cdecker for details about build #$CI_JOB_ID.", "context": "gitlab-ci"}' https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_COMMIT_SHA || true update-status-success: image: tutum/curl @@ -35,4 +35,4 @@ update-status-success: when: on_success script: - >- - curl -s -H "Authorization: token $GITHUB_STATUS_TOKEN" -X POST --data '{"state": "success", "description": "Gitlab-CI build succeeded.", "context": "gitlab-ci"}' https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_BUILD_REF || true + curl -s -H "Authorization: token $GITHUB_STATUS_TOKEN" -X POST --data '{"state": "success", "description": "Gitlab-CI build succeeded.", "context": "gitlab-ci"}' https://api.github.com/repos/ElementsProject/lightning/statuses/$CI_COMMIT_SHA || true diff --git a/.msggen.json b/.msggen.json index 677fa967a318..e53b7cdf404a 100644 --- a/.msggen.json +++ b/.msggen.json @@ -63,7 +63,8 @@ "ipv6": 2, "local socket": 0, "torv2": 3, - "torv3": 4 + "torv3": 4, + "websocket": 5 }, "GetrouteRouteStyle": { "tlv": 0 @@ -101,6 +102,36 @@ "spent": 2, "unconfirmed": 0 }, + "ListhtlcsHtlcsDirection": { + "in": 1, + "out": 0 + }, + "ListhtlcsHtlcsState": { + "RCVD_ADD_ACK_COMMIT": 3, + "RCVD_ADD_ACK_REVOCATION": 14, + "RCVD_ADD_COMMIT": 11, + "RCVD_ADD_HTLC": 10, + "RCVD_ADD_REVOCATION": 2, + "RCVD_REMOVE_ACK_COMMIT": 18, + "RCVD_REMOVE_ACK_REVOCATION": 9, + "RCVD_REMOVE_COMMIT": 6, + "RCVD_REMOVE_HTLC": 5, + "RCVD_REMOVE_REVOCATION": 17, + "SENT_ADD_ACK_COMMIT": 13, + "SENT_ADD_ACK_REVOCATION": 4, + "SENT_ADD_COMMIT": 1, + "SENT_ADD_HTLC": 0, + "SENT_ADD_REVOCATION": 12, + "SENT_REMOVE_ACK_COMMIT": 8, + "SENT_REMOVE_ACK_REVOCATION": 19, + "SENT_REMOVE_COMMIT": 16, + "SENT_REMOVE_HTLC": 15, + "SENT_REMOVE_REVOCATION": 7 + }, + "ListinvoicesIndex": { + "created": 0, + "updated": 1 + }, "ListinvoicesInvoicesStatus": { "expired": 2, "paid": 1, @@ -151,6 +182,7 @@ "ListpeerchannelsChannelsState": { "AWAITING_UNILATERAL": 6, "CHANNELD_AWAITING_LOCKIN": 1, + "CHANNELD_AWAITING_SPLICE": 11, "CHANNELD_NORMAL": 2, "CHANNELD_SHUTTING_DOWN": 3, "CLOSINGD_COMPLETE": 5, @@ -272,7 +304,8 @@ "NewaddrAddresstype": { "all": 2, "bech32": 0, - "p2sh-segwit": 1 + "p2sh-segwit": 1, + "p2tr": 3 }, "PayStatus": { "complete": 0, @@ -362,6 +395,7 @@ "CreateInvoice.amount_received_msat": 10, "CreateInvoice.bolt11": 2, "CreateInvoice.bolt12": 3, + "CreateInvoice.created_index": 16, "CreateInvoice.description": 7, "CreateInvoice.expires_at": 8, "CreateInvoice.invreq_payer_note": 15, @@ -568,6 +602,7 @@ "DelInvoice.amount_msat": 4, "DelInvoice.bolt11": 2, "DelInvoice.bolt12": 3, + "DelInvoice.created_index": 12, "DelInvoice.description": 5, "DelInvoice.expires_at": 8, "DelInvoice.invreq_payer_note": 11, @@ -575,7 +610,8 @@ "DelInvoice.local_offer_id": 9, "DelInvoice.payer_note": 10, "DelInvoice.payment_hash": 6, - "DelInvoice.status": 7 + "DelInvoice.status": 7, + "DelInvoice.updated_index": 13 }, "DisconnectRequest": { "Disconnect.force": 2, @@ -586,6 +622,7 @@ "Feerates.onchain_fee_estimates.htlc_timeout_satoshis": 4, "Feerates.onchain_fee_estimates.mutual_close_satoshis": 2, "Feerates.onchain_fee_estimates.opening_channel_satoshis": 1, + "Feerates.onchain_fee_estimates.unilateral_close_nonanchor_satoshis": 6, "Feerates.onchain_fee_estimates.unilateral_close_satoshis": 3 }, "FeeratesPerkb": { @@ -598,6 +635,7 @@ "Feerates.perkb.mutual_close": 4, "Feerates.perkb.opening": 3, "Feerates.perkb.penalty": 8, + "Feerates.perkb.unilateral_anchor_close": 11, "Feerates.perkb.unilateral_close": 5 }, "FeeratesPerkbEstimates": { @@ -615,6 +653,7 @@ "Feerates.perkw.mutual_close": 4, "Feerates.perkw.opening": 3, "Feerates.perkw.penalty": 8, + "Feerates.perkw.unilateral_anchor_close": 11, "Feerates.perkw.unilateral_close": 5 }, "FeeratesPerkwEstimates": { @@ -660,6 +699,8 @@ "FundPsbt.locktime": 6, "FundPsbt.min_witness_weight": 7, "FundPsbt.minconf": 4, + "FundPsbt.nonwrapped": 9, + "FundPsbt.opening_anchor_channel": 10, "FundPsbt.reserve": 5, "FundPsbt.satoshi": 1, "FundPsbt.startweight": 3 @@ -753,6 +794,7 @@ }, "InvoiceResponse": { "Invoice.bolt11": 1, + "Invoice.created_index": 10, "Invoice.expires_at": 4, "Invoice.payment_hash": 2, "Invoice.payment_secret": 3, @@ -910,11 +952,27 @@ "ListFunds.channels[]": 2, "ListFunds.outputs[]": 1 }, + "ListhtlcsHtlcs": { + "ListHtlcs.htlcs[].amount_msat": 4, + "ListHtlcs.htlcs[].direction": 5, + "ListHtlcs.htlcs[].expiry": 3, + "ListHtlcs.htlcs[].id": 2, + "ListHtlcs.htlcs[].payment_hash": 6, + "ListHtlcs.htlcs[].short_channel_id": 1, + "ListHtlcs.htlcs[].state": 7 + }, + "ListhtlcsRequest": { + "ListHtlcs.id": 1 + }, + "ListhtlcsResponse": { + "ListHtlcs.htlcs[]": 1 + }, "ListinvoicesInvoices": { "ListInvoices.invoices[].amount_msat": 6, "ListInvoices.invoices[].amount_received_msat": 12, "ListInvoices.invoices[].bolt11": 7, "ListInvoices.invoices[].bolt12": 8, + "ListInvoices.invoices[].created_index": 16, "ListInvoices.invoices[].description": 2, "ListInvoices.invoices[].expires_at": 5, "ListInvoices.invoices[].invreq_payer_note": 15, @@ -925,13 +983,17 @@ "ListInvoices.invoices[].payer_note": 10, "ListInvoices.invoices[].payment_hash": 3, "ListInvoices.invoices[].payment_preimage": 14, - "ListInvoices.invoices[].status": 4 + "ListInvoices.invoices[].status": 4, + "ListInvoices.invoices[].updated_index": 17 }, "ListinvoicesRequest": { + "ListInvoices.index": 5, "ListInvoices.invstring": 2, "ListInvoices.label": 1, + "ListInvoices.limit": 7, "ListInvoices.offer_id": 4, - "ListInvoices.payment_hash": 3 + "ListInvoices.payment_hash": 3, + "ListInvoices.start": 6 }, "ListinvoicesResponse": { "ListInvoices.invoices[]": 1 @@ -995,6 +1057,7 @@ "ListPeerChannels.channels[].funding_outnum": 11, "ListPeerChannels.channels[].funding_txid": 10, "ListPeerChannels.channels[].htlcs[]": 52, + "ListPeerChannels.channels[].ignore_fee_limits": 54, "ListPeerChannels.channels[].in_fulfilled_msat": 47, "ListPeerChannels.channels[].in_offered_msat": 45, "ListPeerChannels.channels[].in_payments_fulfilled": 46, @@ -1069,6 +1132,7 @@ "ListPeerChannels.channels[].inflight[].funding_txid": 1, "ListPeerChannels.channels[].inflight[].our_funding_msat": 5, "ListPeerChannels.channels[].inflight[].scratch_txid": 6, + "ListPeerChannels.channels[].inflight[].splice_amount": 7, "ListPeerChannels.channels[].inflight[].total_funding_msat": 4 }, "ListpeerchannelsChannelsState_changes": { @@ -1179,6 +1243,7 @@ "ListPeers.peers[].channels[].inflight[].funding_txid": 1, "ListPeers.peers[].channels[].inflight[].our_funding_msat": 5, "ListPeers.peers[].channels[].inflight[].scratch_txid": 6, + "ListPeers.peers[].channels[].inflight[].splice_amount": 7, "ListPeers.peers[].channels[].inflight[].total_funding_msat": 4 }, "ListpeersPeersLog": { @@ -1257,7 +1322,8 @@ }, "NewaddrResponse": { "NewAddr.bech32": 1, - "NewAddr.p2sh-segwit": 2 + "NewAddr.p2sh-segwit": 2, + "NewAddr.p2tr": 3 }, "PayRequest": { "Pay.amount_msat": 13, @@ -1294,6 +1360,14 @@ "PingResponse": { "Ping.totlen": 1 }, + "PreapproveinvoiceRequest": { + "PreApproveInvoice.bolt11": 1 + }, + "PreapprovekeysendRequest": { + "PreApproveKeysend.amount_msat": 3, + "PreApproveKeysend.destination": 1, + "PreApproveKeysend.payment_hash": 2 + }, "SendcustommsgRequest": { "SendCustomMsg.msg": 2, "SendCustomMsg.node_id": 1 @@ -1385,6 +1459,7 @@ "SetChannel.channels[].channel_id": 2, "SetChannel.channels[].fee_base_msat": 4, "SetChannel.channels[].fee_proportional_millionths": 5, + "SetChannel.channels[].ignore_fee_limits": 10, "SetChannel.channels[].maximum_htlc_out_msat": 8, "SetChannel.channels[].minimum_htlc_out_msat": 6, "SetChannel.channels[].peer_id": 1, @@ -1398,7 +1473,8 @@ "SetChannel.feeppm": 3, "SetChannel.htlcmax": 5, "SetChannel.htlcmin": 4, - "SetChannel.id": 1 + "SetChannel.id": 1, + "SetChannel.ignorefeelimits": 7 }, "SetchannelResponse": { "SetChannel.channels[]": 1 @@ -1456,6 +1532,7 @@ "UtxoPsbt.feerate": 2, "UtxoPsbt.locktime": 6, "UtxoPsbt.min_witness_weight": 7, + "UtxoPsbt.opening_anchor_channel": 10, "UtxoPsbt.reserve": 5, "UtxoPsbt.reservedok": 8, "UtxoPsbt.satoshi": 1, @@ -1486,6 +1563,7 @@ "WaitAnyInvoice.amount_received_msat": 10, "WaitAnyInvoice.bolt11": 7, "WaitAnyInvoice.bolt12": 8, + "WaitAnyInvoice.created_index": 13, "WaitAnyInvoice.description": 2, "WaitAnyInvoice.expires_at": 5, "WaitAnyInvoice.label": 1, @@ -1493,7 +1571,8 @@ "WaitAnyInvoice.pay_index": 9, "WaitAnyInvoice.payment_hash": 3, "WaitAnyInvoice.payment_preimage": 12, - "WaitAnyInvoice.status": 4 + "WaitAnyInvoice.status": 4, + "WaitAnyInvoice.updated_index": 14 }, "WaitinvoiceRequest": { "WaitInvoice.label": 1 @@ -1503,6 +1582,7 @@ "WaitInvoice.amount_received_msat": 10, "WaitInvoice.bolt11": 7, "WaitInvoice.bolt12": 8, + "WaitInvoice.created_index": 13, "WaitInvoice.description": 2, "WaitInvoice.expires_at": 5, "WaitInvoice.label": 1, @@ -1510,7 +1590,8 @@ "WaitInvoice.pay_index": 9, "WaitInvoice.payment_hash": 3, "WaitInvoice.payment_preimage": 12, - "WaitInvoice.status": 4 + "WaitInvoice.status": 4, + "WaitInvoice.updated_index": 14 }, "WaitsendpayRequest": { "WaitSendPay.groupid": 4, @@ -1700,6 +1781,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "CreateInvoice.created_index": { + "added": "v23.08", + "deprecated": false + }, "CreateInvoice.description": { "added": "pre-v0.10.1", "deprecated": false @@ -2456,6 +2541,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "DelInvoice.created_index": { + "added": "v23.08", + "deprecated": false + }, "DelInvoice.desconly": { "added": "pre-v0.10.1", "deprecated": false @@ -2488,6 +2577,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "DelInvoice.updated_index": { + "added": "v23.08", + "deprecated": false + }, "Disconnect": { "added": "pre-v0.10.1", "deprecated": null @@ -2524,6 +2617,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "Feerates.onchain_fee_estimates.unilateral_close_nonanchor_satoshis": { + "added": "v23.08", + "deprecated": false + }, "Feerates.onchain_fee_estimates.unilateral_close_satoshis": { "added": "pre-v0.10.1", "deprecated": false @@ -2580,6 +2677,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "Feerates.perkb.unilateral_anchor_close": { + "added": "v23.08", + "deprecated": false + }, "Feerates.perkb.unilateral_close": { "added": "pre-v0.10.1", "deprecated": false @@ -2636,6 +2737,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "Feerates.perkw.unilateral_anchor_close": { + "added": "v23.08", + "deprecated": false + }, "Feerates.perkw.unilateral_close": { "added": "pre-v0.10.1", "deprecated": false @@ -2756,6 +2861,14 @@ "added": "pre-v0.10.1", "deprecated": false }, + "FundPsbt.nonwrapped": { + "added": "v23.02", + "deprecated": false + }, + "FundPsbt.opening_anchor_channel": { + "added": "v23.08", + "deprecated": false + }, "FundPsbt.psbt": { "added": "pre-v0.10.1", "deprecated": false @@ -2865,7 +2978,7 @@ "deprecated": null }, "Getinfo.address[]": { - "added": "pre-v0.10.1", + "added": "v23.02", "deprecated": false }, "Getinfo.address[].address": { @@ -2992,6 +3105,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "Invoice.created_index": { + "added": "v23.08", + "deprecated": false + }, "Invoice.deschashonly": { "added": "pre-v0.10.1", "deprecated": false @@ -3504,10 +3621,54 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListHtlcs": { + "added": "v23.02", + "deprecated": null + }, + "ListHtlcs.htlcs[]": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].amount_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].direction": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].expiry": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].id": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].payment_hash": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].short_channel_id": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.htlcs[].state": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListHtlcs.id": { + "added": "pre-v0.10.1", + "deprecated": false + }, "ListInvoices": { "added": "pre-v0.10.1", "deprecated": null }, + "ListInvoices.index": { + "added": "v23.08", + "deprecated": false + }, "ListInvoices.invoices[]": { "added": "pre-v0.10.1", "deprecated": false @@ -3528,6 +3689,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListInvoices.invoices[].created_index": { + "added": "v23.08", + "deprecated": false + }, "ListInvoices.invoices[].description": { "added": "pre-v0.10.1", "deprecated": false @@ -3568,6 +3733,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListInvoices.invoices[].updated_index": { + "added": "v23.08", + "deprecated": false + }, "ListInvoices.invstring": { "added": "pre-v0.10.1", "deprecated": false @@ -3576,6 +3745,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListInvoices.limit": { + "added": "v23.08", + "deprecated": false + }, "ListInvoices.offer_id": { "added": "pre-v0.10.1", "deprecated": false @@ -3584,6 +3757,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListInvoices.start": { + "added": "v23.08", + "deprecated": false + }, "ListNodes": { "added": "pre-v0.10.1", "deprecated": null @@ -3636,6 +3813,14 @@ "added": "pre-v0.10.1", "deprecated": null }, + "ListPays.amount_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListPays.amount_sent_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, "ListPays.bolt11": { "added": "pre-v0.10.1", "deprecated": false @@ -3648,6 +3833,14 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListPays.pays[].amount_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, + "ListPays.pays[].amount_sent_msat": { + "added": "pre-v0.10.1", + "deprecated": false + }, "ListPays.pays[].bolt11": { "added": "pre-v0.10.1", "deprecated": false @@ -3844,6 +4037,10 @@ "added": "v23.02", "deprecated": false }, + "ListPeerChannels.channels[].ignore_fee_limits": { + "added": "v23.08", + "deprecated": false + }, "ListPeerChannels.channels[].in_fulfilled_msat": { "added": "v23.02", "deprecated": false @@ -3884,6 +4081,10 @@ "added": "v23.02", "deprecated": false }, + "ListPeerChannels.channels[].inflight[].splice_amount": { + "added": "v23.08", + "deprecated": false + }, "ListPeerChannels.channels[].inflight[].total_funding_msat": { "added": "v23.02", "deprecated": false @@ -4228,6 +4429,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "ListPeers.peers[].channels[].inflight[].splice_amount": { + "added": "v23.08", + "deprecated": false + }, "ListPeers.peers[].channels[].inflight[].total_funding_msat": { "added": "pre-v0.10.1", "deprecated": false @@ -4608,6 +4813,10 @@ "added": "pre-v0.10.1", "deprecated": "v23.02" }, + "NewAddr.p2tr": { + "added": "v23.08", + "deprecated": false + }, "Pay": { "added": "pre-v0.10.1", "deprecated": null @@ -4712,6 +4921,30 @@ "added": "pre-v0.10.1", "deprecated": false }, + "PreApproveInvoice": { + "added": "v23.02", + "deprecated": null + }, + "PreApproveInvoice.bolt11": { + "added": "v23.02", + "deprecated": false + }, + "PreApproveKeysend": { + "added": "v23.02", + "deprecated": null + }, + "PreApproveKeysend.amount_msat": { + "added": "v23.02", + "deprecated": false + }, + "PreApproveKeysend.destination": { + "added": "v23.02", + "deprecated": false + }, + "PreApproveKeysend.payment_hash": { + "added": "v23.02", + "deprecated": false + }, "SendCustomMsg": { "added": "v0.10.1", "deprecated": null @@ -4948,6 +5181,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "SetChannel.channels[].ignore_fee_limits": { + "added": "v23.08", + "deprecated": false + }, "SetChannel.channels[].maximum_htlc_out_msat": { "added": "pre-v0.10.1", "deprecated": false @@ -4996,6 +5233,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "SetChannel.ignorefeelimits": { + "added": "v23.08", + "deprecated": false + }, "SignInvoice": { "added": "v23.02", "deprecated": null @@ -5144,6 +5385,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "UtxoPsbt.opening_anchor_channel": { + "added": "v23.08", + "deprecated": false + }, "UtxoPsbt.psbt": { "added": "pre-v0.10.1", "deprecated": false @@ -5212,6 +5457,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "WaitAnyInvoice.created_index": { + "added": "v23.08", + "deprecated": false + }, "WaitAnyInvoice.description": { "added": "pre-v0.10.1", "deprecated": false @@ -5252,6 +5501,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "WaitAnyInvoice.updated_index": { + "added": "v23.08", + "deprecated": false + }, "WaitInvoice": { "added": "pre-v0.10.1", "deprecated": null @@ -5272,6 +5525,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "WaitInvoice.created_index": { + "added": "v23.08", + "deprecated": false + }, "WaitInvoice.description": { "added": "pre-v0.10.1", "deprecated": false @@ -5304,6 +5561,10 @@ "added": "pre-v0.10.1", "deprecated": false }, + "WaitInvoice.updated_index": { + "added": "v23.08", + "deprecated": false + }, "WaitSendPay": { "added": "pre-v0.10.1", "deprecated": null diff --git a/CHANGELOG.md b/CHANGELOG.md index e16e8b8c048f..601626aad866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,211 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [23.05.1] - 2023-06-05: "Austin Texas Agreement(ATXA) II" + +Bugfix release for bad issues found since 23.05 which can't wait for 23.08. + +### Fixed + + - Fixed crash (memory corruption!) in `listtransactions` ([#6304]) + - Don't crash on gossip store deletion fail ([#6297]) + - Fix incompatibility with LND which prevented us opening private channels ([#6304]) + +### EXPERIMENTAL + + - Fixed crash in dual-funding. ([#6273]) + +[#6273]: https://github.com/ElementsProject/lightning/pull/6273 +[#6304]: https://github.com/ElementsProject/lightning/pull/6304 +[#6297]: https://github.com/ElementsProject/lightning/pull/6297 +[#6304]: https://github.com/ElementsProject/lightning/pull/6304 + +## [v23.08rc1] - 2023-08-02: "Satoshi's Successor" + +This release named by Matt Morehouse. + +### Added + + - Plugins: `renepay`: an experimental pay plugin implementing Pickhardt payments (`renepay` and `renepaystatus`). ([#6376]) + - Plugins: `clnrest`: a lightweight python rest API service. ([#6389]) + - JSON-RPC: `wait`: new generic command to wait for events. ([#6127]) + - JSON-RPC: `setchannel` adds a new `ignorefeelimits` parameter to allow peer to set arbitrary commitment transaction fees on a per-channel basis. ([#6398]) + - Config: A new opentracing system with minimal performance impact for performance tracing in productive systems: see doc/developers-guide/tracing-cln-performance.md ([#5492]) + - Plugins: `pay` will now pay your own invoices if you try. ([#6399]) + - JSON-RPC: `checkrune`: check rune validity for authorization; `createrune` to create/modify rune; `showrunes` to list existing runes; `blacklistrune` to revoke permission of rune ([#6403]) + - Protocol: When we send our own gossip when a peer connects, also send any incoming channel_updates. ([#6412]) + - Config: `log-level` can be specified on a per-logfile basis. ([#6406]) + - Config: `--recover` can restore a node from a codex32 secret ([#6302]) + - Tools: `hsmtool` `getcodexsecret` to extract node secret as codex32 secret ([#6466]) + - JSON-RPC: newaddr: p2tr option to create taproot addresses. ([#6035]) + - JSON-RPC: new command `setconfig` allows a limited number of configuration settings to be changed without restart. ([#6303]) + - JSON-RPC: `listconfigs` now has `configs` subobject with more information about each config option. ([#6243]) + - Config: `--regtest` option as alias for `--network=regtest` ([#6243]) + - Config: `accept-htlc-tlv-type` (replaces awkward-to-use `accept-htlc-tlv-types`) ([#6243]) + - Config: `bind=ws:...` to explicitly listen on a websocket. ([#6173]) + - Config: `bind` can now take `dns:` prefix to advertize DNS records. ([#6173]) + - Plugins: `sendpay` now allows self-payment of invoices, by specifying an empty route. ([#6399]) + - Plugins: plugins can subscribe to all notifications using "*". ([#6347]) + - Plugins: Pass the current known block height down to the getchaininfo call. ([#6181]) + - JSON-RPC: `listinvoices` has `limit` parameter for listing control. ([#6127]) + - JSON-RPC: `listinvoices` has `index` and `start` parameters for listing control. ([#6127]) + - JSON-RPC: `listpeerchannels` has a new field `ignore_fee_limits` ([#6398]) + - JSON-RPC: `shutdown` notification contains `shutdown` object (notification consistency) ([#6347]) + - JSON-RPC: `block_added` notification wraps fields in `block_added` object (notification consistency) ([#6388]) + - JSON-RPC: `connect` and `disconnect` notifications now wrap `id` field in a `connect`/`disconnect` object (consistency with other notifications) ([#6388]) + - JSON-RPC: `fundpsbt` and `utxopsbt` new parameter `opening_anchor_channel` so lightningd knowns it needs emergency reserve for anchors. ([#6334]) + - Config: `min-emergency-msat` setting for (currently experimental!) anchor channels, to keep funds in reserve for forced closes. ([#6334]) + - JSON-RPC: `feerates` has new fields `unilateral_anchor_close` to show the feerate used for anchor channels (currently experimental), and `unilateral_close_nonanchor_satoshis`. ([#6334]) + + +### Changed + + - Tools: Reckless can now install directly from local sources. ([#6393]) + - Protocol: We allow update_fail_malformed_htlc with invalid error codes (LND?) ([#6425]) + - pyln-testing: The grpc dependencies are now optional. ([#6417]) + - Protocol: commando commands now allow a missing params field, instead of requiring an empty field. ([#6405]) + - Wallet: we now use taproot change addresses. ([#6035]) + - Plugins: `autoclean` configuration variables now settable with `setconfig`. ([#6303]) + - JSON-RPC: `fundchannel` and `multifundchannel` will refuse to spend funds below `min-emergency-msat` if we have any anchor channels (or are opening one). ([#6334]) + - JSON-RPC: `withdraw` will refuse to spend funds below `min-emergency-msat` if we have any anchor channels (and `all` will be reduced appropriately). ([#6334]) + - JSON-RPC: `fundpsbt` and `utxopsbt` will refuse to spend funds below `min-emergency-msat` if we have any anchor channels. ([#6334]) + - JSON-RPC: `feerates` `unilateral_close_satoshis` now assumes anchor channels if enabled (currently experimental). ([#6334]) + + +### Deprecated + +Note: You should always set `allow-deprecated-apis=false` to test for changes. + + - JSON-RPC: `commando-rune`, `commando-listrunes` and `commando-blacklist` (use `createrune`, `showrunes` and `blacklistrune` ([#6403]) + - JSON-RPC: `connect`, `disconnect` and `block_added` notification fields outside the same-named object (use .connect/.disconnect/.block_added sub-objects) ([#6388]) + - `pay` has *undeprecated* paying a description-hash invoice without providing the description. ([#6337]) + - JSON-RPC: `listconfigs` direct fields, use `configs` sub-object and `set`, `value_bool`, `value_str`, `value_int`, or `value_msat` fields. ([#6243]) + - Config: boolean plugin options set to `1` or `0` (use `true` and `false` like non-plugin options). ([#6243]) + - Config: `accept-htlc-tlv-types` (use `accept-htlc-tlv-type` multiple times) ([#6243]) + - Config: `experimental-websocket-port`: use `--bind=ws::`. ([#6173]) + - Config: bind-addr=xxx.onion and addr=xxx.onion, use announce=xxx.onion (which was always equivalent). ([#6173]) + - Config: addr=/socketpath, use listen=/socketpath (which was always equivalent). ([#6173]) + - Config: `announce-addr-dns`; use `--bind-addr=dns:ADDR` for finer control. ([#6173]) + + +### Removed + + - Plugins: `commando` no longer allows datastore ['commando', 'secret'] to override master secret (re-issue runes if you were using that!). ([#6431]) + - Plugins: pay: `pay` no longer splits based on common size, as it was causing issues in various scenarios. ([#6400]) + - Build: Support for python v<=3.7 & Ubuntu bionic ([#6414]) + + +### Fixed + + - Protocol: Fix incompatibility with LND which prevented us opening private channels ([#6304]) + - Protocol: We no longer gossip about recently-closed channels (Eclair gets upset with this). ([#6413]) + - Protocol: We will close incoming HTLCs early if the outgoing HTLC is stuck onchain long enough, to avoid cascating failure. ([#6378]) + - JSON-RPC: `close` returns a `tx` field with witness data populated (i.e. signed). ([#6468]) + - Protocol: When we send our own gossip when a peer connects, send our node_announcement too (regression in v23.05) ([#6412]) + - Protocol: `dualopend`: Fix behavior for tx-aborts. No longer hangs, appropriately continues re-init of RBF requests without reconnection msg exchange. ([#6461]) + - Protocol: Node announcements are refreshed more reliably. ([#6454]) + - Build: Small fix for Mac OS building ([#6253]) + - msggen: `listpays` now includes the missing `amount_msat` and `amount_sent_msat` fields ([#6441]) + - Protocol: Adding a >0 version witness program to a fallback address now is *just* the witness program, as per bolt11 spec ([#6435]) + - JSON-RPC: `sendonion` and `sendpay` will now consider amounts involved when using picking one channel for a peer ([#6428]) + - Plugins: pay: We now track spendable amounts when routing on both the local alias as well as the short channel ID ([#6428]) + - Config: `log-level` filters now apply correctly to messages from `connectd`. ([#6406]) + - Lightnind: don't infinite loop on 32 bit platforms if only invoices are expiring after 2038. ([#6361]) + - JSON-RPC: `pay` and `decodepay` with description now correctly handle JSON escapes (e.g " inside description) ([#6337]) + - Plugins: `commando` runes can now compare integer parameters using '<' and '>' as expected. ([#6295]) + - Plugins: reloaded plugins get passed any vars from configuration files. ([#6243]) + - JSON-RPC: `listconfigs` `rpc-file-mode` no longer has gratuitous quotes (e.g. "0600" not "\"0600\""). ([#6243]) + - JSON-RPC: `listconfigs` `htlc-minimum-msat`, `htlc-maximum-msat` and `max-dust-htlc-exposure-msat` fields are now numbers, not strings. ([#6243]) + + +### EXPERIMENTAL + + - Build: all experimental features are now runtime-enabled; no more `./configure --enable-experimental-features` ([#6209]) + - Protocol: `experimental-splicing` to enable splicing & resizing of active channels. ([#6253]) + - protocol: `experimental-anchors` to support zero-fee-htlc anchors (`option_anchors_zero_fee_htlc_tx`). ([#6334]) + - Protocol: Removed support for advertizing websocket addresses in gossip. ([#6173]) + - Crash: Fixed crash in dual-funding. ([#6273]) + - Config: `experimental-upgrade-protocol` enables simple channel upgrades. ([#6209]) + - Config: `experimental-quiesce` enables quiescence, for testing. ([#6209]) + + +[#5492]: https://github.com/ElementsProject/lightning/pull/5492 +[#6035]: https://github.com/ElementsProject/lightning/pull/6035 +[#6127]: https://github.com/ElementsProject/lightning/pull/6127 +[#6173]: https://github.com/ElementsProject/lightning/pull/6173 +[#6181]: https://github.com/ElementsProject/lightning/pull/6181 +[#6209]: https://github.com/ElementsProject/lightning/pull/6209 +[#6243]: https://github.com/ElementsProject/lightning/pull/6243 +[#6253]: https://github.com/ElementsProject/lightning/pull/6253 +[#6273]: https://github.com/ElementsProject/lightning/pull/6273 +[#6295]: https://github.com/ElementsProject/lightning/pull/6295 +[#6302]: https://github.com/ElementsProject/lightning/pull/6302 +[#6303]: https://github.com/ElementsProject/lightning/pull/6303 +[#6304]: https://github.com/ElementsProject/lightning/pull/6304 +[#6310]: https://github.com/ElementsProject/lightning/pull/6310 +[#6334]: https://github.com/ElementsProject/lightning/pull/6334 +[#6337]: https://github.com/ElementsProject/lightning/pull/6337 +[#6347]: https://github.com/ElementsProject/lightning/pull/6347 +[#6361]: https://github.com/ElementsProject/lightning/pull/6361 +[#6376]: https://github.com/ElementsProject/lightning/pull/6376 +[#6378]: https://github.com/ElementsProject/lightning/pull/6378 +[#6388]: https://github.com/ElementsProject/lightning/pull/6388 +[#6389]: https://github.com/ElementsProject/lightning/pull/6389 +[#6393]: https://github.com/ElementsProject/lightning/pull/6393 +[#6398]: https://github.com/ElementsProject/lightning/pull/6398 +[#6399]: https://github.com/ElementsProject/lightning/pull/6399 +[#6400]: https://github.com/ElementsProject/lightning/pull/6400 +[#6403]: https://github.com/ElementsProject/lightning/pull/6403 +[#6405]: https://github.com/ElementsProject/lightning/pull/6405 +[#6406]: https://github.com/ElementsProject/lightning/pull/6406 +[#6412]: https://github.com/ElementsProject/lightning/pull/6412 +[#6413]: https://github.com/ElementsProject/lightning/pull/6413 +[#6414]: https://github.com/ElementsProject/lightning/pull/6414 +[#6417]: https://github.com/ElementsProject/lightning/pull/6417 +[#6425]: https://github.com/ElementsProject/lightning/pull/6425 +[#6428]: https://github.com/ElementsProject/lightning/pull/6428 +[#6431]: https://github.com/ElementsProject/lightning/pull/6431 +[#6435]: https://github.com/ElementsProject/lightning/pull/6435 +[#6441]: https://github.com/ElementsProject/lightning/pull/6441 +[#6454]: https://github.com/ElementsProject/lightning/pull/6454 +[#6461]: https://github.com/ElementsProject/lightning/pull/6461 +[#6466]: https://github.com/ElementsProject/lightning/pull/6466 +[#6468]: https://github.com/ElementsProject/lightning/pull/6468 +[v23.08rc1]: https://github.com/ElementsProject/lightning/releases/tag/v23.08rc1 + + +## [23.05.2] - 2023-06-21: "Austin Texas Agreement(ATXA) III" + +Bugfix release for bad issues found since 23.05.1 which can't wait for 23.08. + +### Fixed + + - JSON-RPC: `pay` and `decodepay` with description now correctly handle JSON escapes (e.g " inside description) + - JSON-RPC: `pay` has *undeprecated* paying a description-hash invoice without providing the description (since it didn't work reliably until now!) + - GRPC: `listpeers` sometimes could fail on unknown HTLC states. + +### EXPERIMENTAL + - Fixed compilation error when `--enable-experimental-features` configured. + + +## [23.05.1] - 2023-06-05: "Austin Texas Agreement(ATXA) II" + +Bugfix release for bad issues found since 23.05 which can't wait for 23.08. + +### Fixed + + - Fixed crash (memory corruption!) in `listtransactions` ([#6304]) + - Don't crash on gossip store deletion fail ([#6297]) + - Fix incompatibility with LND which prevented us opening private channels ([#6304]) + +### EXPERIMENTAL + + - Fixed crash in dual-funding. ([#6273]) + +[#6273]: https://github.com/ElementsProject/lightning/pull/6273 +[#6304]: https://github.com/ElementsProject/lightning/pull/6304 +[#6297]: https://github.com/ElementsProject/lightning/pull/6297 +[#6304]: https://github.com/ElementsProject/lightning/pull/6304 ## [23.05] - 2023-05-10: "Austin Texas Agreement(ATXA)" diff --git a/Cargo.lock b/Cargo.lock index ec0df74cf62a..c69dda74bb6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,24 +4,24 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "asn1-rs" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -41,7 +41,7 @@ checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] @@ -53,39 +53,40 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -96,9 +97,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.1" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08b108ad2665fa3f6e6a517c3d80ec3e77d224c47d605167aefaa5d7ef97fa48" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" dependencies = [ "async-trait", "axum-core", @@ -118,16 +119,15 @@ dependencies = [ "serde", "sync_wrapper", "tower", - "tower-http", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b8558f5a0581152dc94dcd289132a1d377494bdeafcd41869b3258e3e2ad92" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -146,6 +146,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "bech32" version = "0.9.1" @@ -181,21 +187,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -205,7 +211,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cln-grpc" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "bitcoin", @@ -220,7 +226,7 @@ dependencies = [ [[package]] name = "cln-grpc-plugin" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "cln-grpc", @@ -235,7 +241,7 @@ dependencies = [ [[package]] name = "cln-plugin" -version = "0.1.2" +version = "0.1.4" dependencies = [ "anyhow", "bytes", @@ -252,7 +258,7 @@ dependencies = [ [[package]] name = "cln-rpc" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "bitcoin", @@ -269,15 +275,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ "asn1-rs", "displaydoc", @@ -289,20 +295,20 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "env_logger" @@ -319,13 +325,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -340,9 +346,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -361,9 +367,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -376,9 +382,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -386,15 +392,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -403,38 +409,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -450,9 +456,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -461,9 +467,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -486,9 +492,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -499,6 +505,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -507,9 +519,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -527,12 +539,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - [[package]] name = "httparse" version = "1.8.0" @@ -553,9 +559,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -589,9 +595,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -608,24 +614,25 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.3" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ + "hermit-abi 0.3.1", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -639,15 +646,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] @@ -660,24 +667,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "matchit" @@ -693,9 +697,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -705,14 +709,13 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -723,9 +726,9 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -767,7 +770,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -782,17 +785,17 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64", + "base64 0.13.1", ] [[package]] @@ -803,9 +806,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", "indexmap", @@ -813,22 +816,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -851,28 +854,28 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.1.22" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8992a85d8e93a28bdf76137db888d3874e3b230dee5ed8bebac4c9f7617773" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", - "syn", + "syn 1.0.109", ] [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.5" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01db6702aa05baa3f57dec92b8eeeeb4cb19e894e73996b32a4093289e54592" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -880,9 +883,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.5" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -895,39 +898,38 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 1.0.109", "tempfile", "which", ] [[package]] name = "prost-derive" -version = "0.11.5" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8842bad1a5419bca14eac663ba798f6bc19c413c2fdceb5f3ba3b0932d96720" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "prost-types" -version = "0.11.5" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -977,18 +979,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.7.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick", "memchr", @@ -997,18 +999,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "ring" @@ -1036,23 +1029,23 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.5" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -1062,24 +1055,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "sct" @@ -1093,9 +1086,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9512ffd81e3a3503ed401f79c33168b9148c75038956039166cd750eaa037c3" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes", "secp256k1-sys", @@ -1113,29 +1106,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.151" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1144,18 +1137,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1169,9 +1162,20 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -1180,9 +1184,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "synstructure" @@ -1192,58 +1196,57 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "unicode-xid", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "time" -version = "0.3.17" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" dependencies = [ "itoa", "serde", @@ -1253,35 +1256,34 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] [[package]] name = "tokio" -version = "1.24.2" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1296,13 +1298,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -1318,9 +1320,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -1329,9 +1331,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1350,7 +1352,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.13.1", "bytes", "futures-core", "futures-util", @@ -1385,7 +1387,7 @@ dependencies = [ "proc-macro2", "prost-build", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1408,25 +1410,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-http" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.2" @@ -1446,7 +1429,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1454,20 +1436,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1484,15 +1466,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-xid" @@ -1524,9 +1506,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1534,24 +1516,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1559,28 +1541,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -1598,9 +1580,9 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -1640,60 +1622,135 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "x509-parser" @@ -1702,7 +1759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ "asn1-rs", - "base64", + "base64 0.13.1", "data-encoding", "der-parser", "lazy_static", @@ -1716,9 +1773,9 @@ dependencies = [ [[package]] name = "yasna" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "time", ] diff --git a/Dockerfile b/Dockerfile index 4cd3c6ae2cec..426143a70363 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,9 +13,9 @@ RUN set -ex \ WORKDIR /opt -RUN wget -qO /opt/tini "https://github.com/krallin/tini/releases/download/v0.18.0/tini" \ - && echo "12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855 /opt/tini" | sha256sum -c - \ - && chmod +x /opt/tini +RUN wget -qO /tini "https://github.com/krallin/tini/releases/download/v0.18.0/tini" \ + && echo "12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855 /tini" | sha256sum -c - \ + && chmod +x /tini ARG BITCOIN_VERSION=22.0 ENV BITCOIN_TARBALL bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz @@ -60,13 +60,18 @@ RUN apt-get update -qq && \ libpq-dev \ libtool \ libffi-dev \ + pkg-config \ + libssl-dev \ protobuf-compiler \ - python3 \ + python3.9 \ python3-dev \ python3-mako \ python3-pip \ python3-venv \ python3-setuptools \ + libev-dev \ + libevent-dev \ + qemu-user-static \ wget RUN wget -q https://zlib.net/fossils/zlib-1.2.13.tar.gz \ @@ -86,6 +91,7 @@ RUN apt-get install -y --no-install-recommends unzip tclsh \ && make \ && make install && cd .. && rm sqlite-src-3290000.zip && rm -rf sqlite-src-3290000 +USER root RUN wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz \ && tar xvf gmp-6.1.2.tar.xz \ && cd gmp-6.1.2 \ @@ -100,30 +106,39 @@ RUN rustup toolchain install stable --component rustfmt --allow-downgrade WORKDIR /opt/lightningd COPY . /tmp/lightning + RUN git clone --recursive /tmp/lightning . && \ git checkout $(git --work-tree=/tmp/lightning --git-dir=/tmp/lightning/.git rev-parse HEAD) ARG DEVELOPER=1 ENV PYTHON_VERSION=3 -RUN curl -sSL https://install.python-poetry.org | python3 - \ - && pip3 install -U pip \ - && pip3 install -U wheel \ - && /root/.local/bin/poetry install +RUN curl -sSL https://install.python-poetry.org | python3 - + +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1 + +RUN pip3 install --upgrade pip setuptools wheel +RUN pip3 wheel cryptography +RUN pip3 install grpcio-tools + +RUN /root/.local/bin/poetry install RUN ./configure --prefix=/tmp/lightning_install --enable-static && \ make DEVELOPER=${DEVELOPER} && \ /root/.local/bin/poetry run make install -FROM debian:bullseye-slim as final +# RUN pip3 install -r plugins/clnrest/requirements.txt +RUN pip3 install flask gunicorn json5 flask_restx flask-socketio gevent gevent-websocket +RUN pip3 install ./contrib/pyln-client -COPY --from=downloader /opt/tini /usr/bin/tini +FROM debian:bullseye-slim as final RUN apt-get update && \ apt-get install -y --no-install-recommends \ socat \ inotify-tools \ - python3 \ + python3.9 \ python3-pip \ + qemu-user-static \ libpq5 && \ rm -rf /var/lib/apt/lists/* @@ -135,9 +150,12 @@ ENV LIGHTNINGD_NETWORK=bitcoin RUN mkdir $LIGHTNINGD_DATA && \ touch $LIGHTNINGD_DATA/config VOLUME [ "/root/.lightning" ] + COPY --from=builder /tmp/lightning_install/ /usr/local/ +COPY --from=builder /usr/local/lib/python3.9/dist-packages/ /usr/local/lib/python3.9/dist-packages/ COPY --from=downloader /opt/bitcoin/bin /usr/bin COPY --from=downloader /opt/litecoin/bin /usr/bin +COPY --from=downloader "/tini" /usr/bin/tini COPY tools/docker-entrypoint.sh entrypoint.sh EXPOSE 9735 9835 diff --git a/Makefile b/Makefile index a5da85b84d87..6bca70c677ee 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ VG=VALGRIND=1 valgrind -q --error-exitcode=7 VG_TEST_ARGS = --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all endif -ifeq ($(DEVELOPER),1) +ifeq ($(DEBUGBUILD),1) DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1 -DCCAN_JSON_OUT_DEBUG=1 else DEV_CFLAGS= @@ -53,6 +53,10 @@ ifeq ($(COVERAGE),1) COVFLAGS = --coverage endif +ifeq ($(CLANG_COVERAGE),1) +COVFLAGS+=-fprofile-instr-generate -fcoverage-mapping +endif + ifeq ($(PIE),1) PIE_CFLAGS=-fPIE -fPIC PIE_LDFLAGS=-pie @@ -68,9 +72,9 @@ PYTEST_OPTS := -v -p no:logging $(PYTEST_OPTS) MY_CHECK_PYTHONPATH=$${PYTHONPATH}$${PYTHONPATH:+:}$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(shell pwd)/external/lnprototest:$(shell pwd)/contrib/pyln-spec/bolt1:$(shell pwd)/contrib/pyln-spec/bolt2:$(shell pwd)/contrib/pyln-spec/bolt4:$(shell pwd)/contrib/pyln-spec/bolt7 # Collect generated python files to be excluded from lint checks PYTHON_GENERATED= \ - contrib/pyln-testing/pyln/testing/primitives_pb2.py \ - contrib/pyln-testing/pyln/testing/node_pb2_grpc.py \ - contrib/pyln-testing/pyln/testing/node_pb2.py \ + contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py \ + contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py \ + contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py \ contrib/pyln-testing/pyln/testing/grpc2py.py # Options to pass to cppcheck. Mostly used to exclude files that are @@ -177,6 +181,7 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/json_out/json_out.h \ $(CCANDIR)/ccan/likely/likely.h \ $(CCANDIR)/ccan/list/list.h \ + $(CCANDIR)/ccan/lqueue/lqueue.h \ $(CCANDIR)/ccan/mem/mem.h \ $(CCANDIR)/ccan/membuf/membuf.h \ $(CCANDIR)/ccan/noerr/noerr.h \ @@ -236,6 +241,9 @@ DEFAULT_TARGETS := ifeq ("$(OS)-$(ARCH)", "Darwin-arm64") CPATH := /opt/homebrew/include LIBRARY_PATH := /opt/homebrew/lib +LDFLAGS := -L/opt/homebrew/opt/sqlite/lib +CPPFLAGS := -I/opt/homebrew/opt/sqlite/include +PKG_CONFIG_PATH=/opt/homebrew/opt/sqlite/lib/pkgconfig else CPATH := /usr/local/include LIBRARY_PATH := /usr/local/lib @@ -258,11 +266,11 @@ LDFLAGS += $(PIE_LDFLAGS) $(CSANFLAGS) $(COPTFLAGS) ifeq ($(STATIC),1) # For MacOS, Jacob Rapoport changed this to: -# -L/usr/local/lib -Wl,-lgmp -lsqlite3 -lz -Wl,-lm -lpthread -ldl $(COVFLAGS) +# -L/usr/local/lib -lsqlite3 -lz -Wl,-lm -lpthread -ldl $(COVFLAGS) # But that doesn't static link. -LDLIBS = -L$(CPATH) -Wl,-dn -lgmp $(SQLITE3_LDLIBS) -lz -Wl,-dy -lm -lpthread -ldl $(COVFLAGS) +LDLIBS = -L$(CPATH) -Wl,-dn $(SQLITE3_LDLIBS) -lz -Wl,-dy -lm -lpthread -ldl $(COVFLAGS) else -LDLIBS = -L$(CPATH) -lm -lgmp $(SQLITE3_LDLIBS) -lz $(COVFLAGS) +LDLIBS = -L$(CPATH) -lm $(SQLITE3_LDLIBS) -lz $(COVFLAGS) endif # If we have the postgres client library we need to link against it as well @@ -270,7 +278,7 @@ ifeq ($(HAVE_POSTGRES),1) LDLIBS += $(POSTGRES_LDLIBS) endif -default: show-flags all-programs all-test-programs doc-all default-targets +default: show-flags gen all-programs all-test-programs doc-all default-targets $(PYTHON_GENERATED) ifneq ($(SUPPRESS_GENERATION),1) FORCE = FORCE @@ -291,14 +299,6 @@ config.vars: %.o: %.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) -# '_exp' inserted before _wiregen.[ch] to demark experimental -# spec-derived headers, which are *not* committed into git. -ifeq ($(EXPERIMENTAL_FEATURES),1) -EXP := _exp -else -EXP := -endif - # tools/update-mocks.sh does nasty recursive make, must not do this! ifeq ($(SUPPRESS_GENERATION),1) SHA256STAMP_CHANGED = false @@ -367,20 +367,27 @@ ifneq ($(RUST),0) include cln-rpc/Makefile include cln-grpc/Makefile -GRPC_GEN = contrib/pyln-testing/pyln/testing/node_pb2.py \ - contrib/pyln-testing/pyln/testing/node_pb2_grpc.py \ - contrib/pyln-testing/pyln/testing/primitives_pb2.py +$(MSGGEN_GENALL)&: doc/schemas/*.request.json doc/schemas/*.schema.json + PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py -ALL_TEST_GEN += $(GRPC_GEN) +# The compiler assumes that the proto files are in the same +# directory structure as the generated files will be. Since we +# don't do that we need to path the files up. +GRPC_DIR = contrib/pyln-grpc-proto/pyln +GRPC_PATH = $(GRPC_DIR)/grpc -$(GRPC_GEN): cln-grpc/proto/node.proto cln-grpc/proto/primitives.proto - python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=contrib/pyln-testing/pyln/testing/ --grpc_python_out=contrib/pyln-testing/pyln/testing/ --experimental_allow_proto3_optional - python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=contrib/pyln-testing/pyln/testing/ --experimental_allow_proto3_optional - # The compiler assumes that the proto files are in the same - # directory structure as the generated files will be. Since we - # don't do that we need to path the files up. - find contrib/pyln-testing/pyln/testing/ -type f -name "*.py" -print0 | xargs -0 sed -i 's/^import \(.*\)_pb2 as .*__pb2/from . import \1_pb2 as \1__pb2/g' +GRPC_GEN = \ + $(GRPC_PATH)/node_pb2.py \ + $(GRPC_PATH)/node_pb2_grpc.py \ + $(GRPC_PATH)/primitives_pb2.py +ALL_TEST_GEN += $(GRPC_GEN) + +$(GRPC_GEN)&: cln-grpc/proto/node.proto cln-grpc/proto/primitives.proto + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=$(GRPC_PATH)/ --grpc_python_out=$(GRPC_PATH)/ --experimental_allow_proto3_optional + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=$(GRPC_PATH)/ --experimental_allow_proto3_optional + find $(GRPC_DIR)/ -type f -name "*.py" -print0 | xargs -0 sed -i'.bak' -e 's/^import \(.*\)_pb2 as .*__pb2/from pyln.grpc import \1_pb2 as \1__pb2/g' + find $(GRPC_DIR)/ -type f -name "*.py.bak" -delete endif # We make pretty much everything depend on these. @@ -420,7 +427,7 @@ mkdocs.yml: $(MANPAGES:=.md) # Don't delete these intermediaries. -.PRECIOUS: $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) +.PRECIOUS: $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) $(PYTHON_GENERATED) # Every single object file. ALL_OBJS := $(ALL_C_SOURCES:.c=.o) @@ -516,9 +523,6 @@ check-whitespace/%: % check-whitespace: check-whitespace/Makefile check-whitespace/tools/check-bolt.c $(ALL_NONGEN_SRCFILES:%=check-whitespace/%) -check-markdown: - @tools/check-markdown.sh - check-spelling: @tools/check-spelling.sh @@ -570,7 +574,10 @@ check-amount-access: @! (git grep -nE "(->|\.)(milli)?satoshis" -- "*.c" "*.h" ":(exclude)common/amount.*" ":(exclude)*/test/*" | grep -v '/* Raw:') @! git grep -nE "\\(struct amount_(m)?sat\\)" -- "*.c" "*.h" ":(exclude)common/amount.*" ":(exclude)*/test/*" -check-source: check-makefile check-source-bolt check-whitespace check-markdown check-spelling check-python check-includes check-cppcheck check-shellcheck check-setup_locale check-tmpctx check-discouraged-functions check-amount-access +# For those without working cppcheck +check-source-no-cppcheck: check-makefile check-source-bolt check-whitespace check-spelling check-python check-includes check-shellcheck check-setup_locale check-tmpctx check-discouraged-functions check-amount-access + +check-source: check-source-no-cppcheck check-cppcheck full-check: check check-source @@ -594,6 +601,8 @@ CHECK_GEN_ALL = \ .msggen.json \ doc/index.rst +gen: $(CHECK_GEN_ALL) + check-gen-updated: $(CHECK_GEN_ALL) @echo "Checking for generated files being changed by make" git diff --exit-code HEAD @@ -664,7 +673,7 @@ $(ALL_FUZZ_TARGETS): # Everything depends on the CCAN headers, and Makefile -$(CCAN_OBJS) $(CDUMP_OBJS): $(CCAN_HEADERS) Makefile +$(CCAN_OBJS) $(CDUMP_OBJS): $(CCAN_HEADERS) Makefile ccan_compat.h # Except for CCAN, we treat everything else as dependent on external/ bitcoin/ common/ wire/ and all generated headers, and Makefile $(ALL_OBJS): $(BITCOIN_HEADERS) $(COMMON_HEADERS) $(CCAN_HEADERS) $(WIRE_HEADERS) $(ALL_GEN_HEADERS) $(EXTERNAL_HEADERS) Makefile @@ -690,6 +699,7 @@ default-targets: $(DEFAULT_TARGETS) distclean: clean $(RM) ccan/config.h config.vars + $(RM) $(PYTHON_GENERATED) maintainer-clean: distclean @echo 'This command is intended for maintainers to use; it' @@ -727,11 +737,11 @@ pyln-release-%: cd contrib/pyln-$* && $(MAKE) prod-release # These must both be enabled for update-mocks -ifeq ($(DEVELOPER)$(EXPERIMENTAL_FEATURES),11) +ifeq ($(DEVELOPER),1) update-mocks: $(ALL_TEST_PROGRAMS:%=update-mocks/%.c) else update-mocks: - @echo Need DEVELOPER=1 and EXPERIMENTAL_FEATURES=1 to regenerate mocks >&2; exit 1 + @echo Need DEVELOPER=1 to regenerate mocks >&2; exit 1 endif $(ALL_TEST_PROGRAMS:%=update-mocks/%.c): $(ALL_GEN_HEADERS) $(EXTERNAL_LIBS) libccan.a ccan/ccan/cdump/tools/cdump-enumstr config.vars @@ -784,17 +794,18 @@ installdirs: # $(PLUGINS) is defined in plugins/Makefile. -install-program: installdirs $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) +install-program: installdirs $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) $(PY_PLUGINS) @$(NORMAL_INSTALL) $(INSTALL_PROGRAM) $(BIN_PROGRAMS) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) $(PKGLIBEXEC_PROGRAMS) $(DESTDIR)$(pkglibexecdir) [ -z "$(PLUGINS)" ] || $(INSTALL_PROGRAM) $(PLUGINS) $(DESTDIR)$(plugindir) + for PY in $(PY_PLUGINS); do DIR=`dirname $$PY`; DST=$(DESTDIR)$(plugindir)/`basename $$DIR`; $(INSTALL_PROGRAM) -d $$DIR; cp -a $$DIR $$DST ; done MAN1PAGES = $(filter %.1,$(MANPAGES)) MAN5PAGES = $(filter %.5,$(MANPAGES)) MAN7PAGES = $(filter %.7,$(MANPAGES)) MAN8PAGES = $(filter %.8,$(MANPAGES)) -DOC_DATA = README.md doc/INSTALL.md doc/HACKING.md LICENSE +DOC_DATA = README.md LICENSE install-data: installdirs $(MAN1PAGES) $(MAN5PAGES) $(MAN7PAGES) $(MAN8PAGES) $(DOC_DATA) @$(NORMAL_INSTALL) @@ -823,7 +834,7 @@ TESTBINS = \ # version of `lightningd` leading to bogus results. We bundle up all # built artefacts here, and will unpack them on the tester (overlaying # on top of the checked out repo as if we had just built it in place). -testpack.tar.bz2: $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) $(MAN1PAGES) $(MAN5PAGES) $(MAN7PAGES) $(MAN8PAGES) $(DOC_DATA) config.vars $(TESTBINS) $(DEVTOOLS) +testpack.tar.bz2: $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) $(PY_PLUGINS) $(MAN1PAGES) $(MAN5PAGES) $(MAN7PAGES) $(MAN8PAGES) $(DOC_DATA) config.vars $(TESTBINS) $(DEVTOOLS) tar -caf $@ $^ uninstall: @@ -836,6 +847,10 @@ uninstall: $(ECHO) rm -f $(DESTDIR)$(plugindir)/`basename $$f`; \ rm -f $(DESTDIR)$(plugindir)/`basename $$f`; \ done + @for f in $(PY_PLUGINS); do \ + $(ECHO) rm -rf $(DESTDIR)$(plugindir)/$$(basename $$(dirname $$f)); \ + rm -rf $(DESTDIR)$(plugindir)/$$(basename $$(dirname $$f)); \ + done @for f in $(PKGLIBEXEC_PROGRAMS); do \ $(ECHO) rm -f $(DESTDIR)$(pkglibexecdir)/`basename $$f`; \ rm -f $(DESTDIR)$(pkglibexecdir)/`basename $$f`; \ diff --git a/README.md b/README.md index f6015c3dc97d..088f9514b2eb 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Core Lightning (previously c-lightning) is a lightweight, highly customizable an * [Pruning](#pruning) * [HD wallet encryption](#hd-wallet-encryption) * [Developers](#developers) -* [Documentation](https://lightning.readthedocs.io/) +* [Documentation](https://docs.corelightning.org/docs) ## Project Status @@ -39,20 +39,9 @@ Pruning (`prune=n` option in `bitcoin.conf`) is partially supported, see [here]( There are 4 supported installation options: - - Installation from the [Ubuntu PPA][ppa]. - Installation of a pre-compiled binary from the [release page][releases] on GitHub. - Using one of the [provided docker images][dockerhub] on the Docker Hub. - - Compiling the source code yourself as described in the [installation documentation](doc/INSTALL.md). - -For the impatient here's the gist of it for Ubuntu: - -```bash -sudo apt-get install -y software-properties-common -sudo add-apt-repository -u ppa:lightningnetwork/ppa -sudo apt-get install lightningd snapd -sudo snap install bitcoin-core -sudo ln -s /snap/bitcoin-core/current/bin/bitcoin{d,-cli} /usr/local/bin/ -``` + - Compiling the source code yourself as described in the [installation documentation](doc/getting-started/getting-started/installation.md). ### Starting `lightningd` @@ -91,7 +80,7 @@ You can start `lightningd` with the following command: lightningd --network=bitcoin --log-level=debug ``` -This creates a `.lightning/` subdirectory in your home directory: see `man -l doc/lightningd.8` (or https://lightning.readthedocs.io/) for more runtime options. +This creates a `.lightning/` subdirectory in your home directory: see `man -l doc/lightningd.8` (or https://docs.corelightning.org/docs) for more runtime options. ### Using The JSON-RPC Interface @@ -200,6 +189,8 @@ Command line options will always override the values in the configuration file. To use a configuration file, create a file named `config` within your top-level lightning directory or network subdirectory (eg. `~/.lightning/config` or `~/.lightning/bitcoin/config`). See `man -l doc/lightningd-config.5`. +A sample configuration file is available at `contrib/config-example`. + ## Further information ### Pruning @@ -219,7 +210,7 @@ If you encrypt your `hsm_secret`, you will have to pass the `--encrypted-hsm` st ### Developers -Developers wishing to contribute should start with the developer guide [here](doc/HACKING.md). +Developers wishing to contribute should start with the developer guide [here](doc/contribute-to-core-lightning/coding-style-guidelines.md). You should also configure with `--enable-developer` to get additional checks and options. [blockstream-store-blog]: https://blockstream.com/2018/01/16/en-lightning-charge/ @@ -234,8 +225,7 @@ You should also configure with `--enable-developer` to get additional checks and [ml2]: https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev [discord]: https://discord.gg/mE9s4rc5un [telegram]: https://t.me/lightningd -[docs]: https://lightning.readthedocs.org -[ppa]: https://launchpad.net/~lightningnetwork/+archive/ubuntu/ppa +[docs]: https://docs.corelightning.org/docs [releases]: https://github.com/ElementsProject/lightning/releases [dockerhub]: https://hub.docker.com/r/elementsproject/lightningd/ [jsonrpcspec]: https://www.jsonrpc.org/specification diff --git a/SECURITY.md b/SECURITY.md index a56ee308c423..27717d863963 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ We have a 3 month release cycle, and the last two versions are supported. ## Reporting a Vulnerability To report security issues send an email to rusty@rustcorp.com.au, or -security@bockstream.com (not for support). +security@blockstream.com (not for support). ## Signatures For Releases diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index 643a120cf24b..d526596118f8 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -17,17 +18,16 @@ static void psbt_destroy(struct wally_psbt *psbt) wally_psbt_free(psbt); } -static struct wally_psbt *init_psbt(const tal_t *ctx, size_t num_inputs, size_t num_outputs) +struct wally_psbt *create_psbt(const tal_t *ctx, size_t num_inputs, size_t num_outputs, u32 locktime) { - int wally_err; + const u32 init_flags = is_elements(chainparams) ? WALLY_PSBT_INIT_PSET : 0; struct wally_psbt *psbt; + int wally_err; tal_wally_start(); - if (is_elements(chainparams)) - wally_err = wally_psbt_init_alloc(2, num_inputs, num_outputs, 0, WALLY_PSBT_INIT_PSET, &psbt); - else - wally_err = wally_psbt_init_alloc(2, num_inputs, num_outputs, 0, 0, &psbt); + wally_err = wally_psbt_init_alloc(2, num_inputs, num_outputs, 0, init_flags, &psbt); assert(wally_err == WALLY_OK); + wally_psbt_set_fallback_locktime(psbt, locktime); /* By default we are modifying them internally; allow it */ wally_psbt_set_tx_modifiable_flags(psbt, WALLY_PSBT_TXMOD_INPUTS | WALLY_PSBT_TXMOD_OUTPUTS); tal_add_destructor(psbt, psbt_destroy); @@ -36,17 +36,6 @@ static struct wally_psbt *init_psbt(const tal_t *ctx, size_t num_inputs, size_t return psbt; } -/* FIXME extremely thin wrapper; remove? */ -struct wally_psbt *create_psbt(const tal_t *ctx, size_t num_inputs, size_t num_outputs, u32 locktime) -{ - struct wally_psbt *psbt; - - psbt = init_psbt(ctx, num_inputs, num_outputs); - wally_psbt_set_fallback_locktime(psbt, locktime); - - return psbt; -} - struct wally_psbt *clone_psbt(const tal_t *ctx, struct wally_psbt *psbt) { struct wally_psbt *clone; @@ -66,9 +55,8 @@ struct wally_psbt *new_psbt(const tal_t *ctx, const struct wally_tx *wtx) tal_wally_start(); - /* locktime set in create_psbt for now */ + /* locktime and modifiable flags are set in create_psbt */ wally_psbt_set_tx_version(psbt, wtx->version); - wally_psbt_set_tx_modifiable_flags(psbt, WALLY_PSBT_TXMOD_INPUTS | WALLY_PSBT_TXMOD_OUTPUTS); for (size_t i = 0; i < wtx->num_inputs; i++) { wally_err = wally_psbt_add_tx_input_at(psbt, i, 0, &wtx->inputs[i]); @@ -107,7 +95,7 @@ bool psbt_is_finalized(const struct wally_psbt *psbt) } struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt, - struct wally_tx_input *input, + const struct wally_tx_input *input, size_t insert_at) { const u32 flags = WALLY_PSBT_FLAG_NON_FINAL; /* Skip script/witness */ @@ -200,13 +188,9 @@ struct wally_psbt_output *psbt_append_output(struct wally_psbt *psbt, const u8 *script, struct amount_sat amount) { - struct wally_psbt_output *out; - struct wally_tx_output *tx_out = wally_tx_output(NULL, script, amount); - - out = psbt_add_output(psbt, tx_out, psbt->num_outputs); - wally_tx_output_free(tx_out); - return out; + return psbt_insert_output(psbt, script, amount, psbt->num_outputs); } + struct wally_psbt_output *psbt_insert_output(struct wally_psbt *psbt, const u8 *script, struct amount_sat amount, @@ -228,7 +212,7 @@ void psbt_rm_output(struct wally_psbt *psbt, } void psbt_input_add_pubkey(struct wally_psbt *psbt, size_t in, - const struct pubkey *pubkey) + const struct pubkey *pubkey, bool is_taproot) { int wally_err; u32 empty_path[1] = {0}; @@ -250,11 +234,20 @@ void psbt_input_add_pubkey(struct wally_psbt *psbt, size_t in, pubkey_to_der(pk_der, pubkey); tal_wally_start(); - wally_err = wally_psbt_input_keypath_add(&psbt->inputs[in], - pk_der, sizeof(pk_der), - fingerprint, sizeof(fingerprint), - empty_path, ARRAY_SIZE(empty_path)); - assert(wally_err == WALLY_OK); + if (is_taproot) { + wally_err = wally_psbt_input_taproot_keypath_add(&psbt->inputs[in], + pk_der + 1, 32, + NULL /* tapleaf_hashes */, 0 /* tapleaf_hashes_len */, + fingerprint, sizeof(fingerprint), + empty_path, ARRAY_SIZE(empty_path)); + assert(wally_err == WALLY_OK); + } else { + wally_err = wally_psbt_input_keypath_add(&psbt->inputs[in], + pk_der, sizeof(pk_der), + fingerprint, sizeof(fingerprint), + empty_path, ARRAY_SIZE(empty_path)); + assert(wally_err == WALLY_OK); + } tal_wally_end(psbt); } @@ -329,6 +322,15 @@ void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in, assert(wally_err == WALLY_OK); } +void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in, + struct bitcoin_outpoint outpoint) +{ + assert(in < psbt->num_inputs); + psbt->inputs[in].index = outpoint.n; + memcpy(psbt->inputs[in].txhash, &outpoint.txid, + sizeof(struct bitcoin_txid)); +} + void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript) { int wally_err; @@ -453,6 +455,28 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, return val; } +size_t psbt_input_get_weight(const struct wally_psbt *psbt, + size_t in) +{ + size_t weight; + const struct wally_map_item *redeem_script; + + redeem_script = wally_map_get_integer(&psbt->inputs[in].psbt_fields, /* PSBT_IN_REDEEM_SCRIPT */ 0x04); + + /* txid + txout + sequence */ + weight = (32 + 4 + 4) * 4; + if (redeem_script) { + weight += + (redeem_script->value_len + + varint_size(redeem_script->value_len)) * 4; + } else { + /* zero scriptSig length */ + weight += varint_size(0) * 4; + } + + return weight; +} + struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, size_t out) { @@ -463,6 +487,13 @@ struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, return amount_asset_to_sat(&asset); } +size_t psbt_output_get_weight(const struct wally_psbt *psbt, + size_t outnum) +{ + return (8 /* amount*/ + varint_size(psbt->outputs[outnum].script_len) + + psbt->outputs[outnum].script_len) * 4; +} + static void add(u8 **key, const void *mem, size_t len) { size_t oldlen = tal_count(*key); @@ -475,12 +506,12 @@ static void add_type(u8 **key, const u8 num) add(key, &num, 1); } -static void add_varint(u8 **key, size_t val) +void add_varint(u8 **arr, size_t val) { u8 vt[VARINT_MAX_LEN]; size_t vtlen; vtlen = varint_put(vt, val); - add(key, vt, vtlen); + tal_expand(arr, vt, vtlen); } #define LIGHTNING_PROPRIETARY_PREFIX "lightning" @@ -603,7 +634,7 @@ bool psbt_finalize(struct wally_psbt *psbt) tal_wally_start(); /* Wally doesn't know how to finalize P2WSH; this happens with - * option_anchor_outputs, and finalizing is trivial. */ + * option_anchor_outputs, and finalizing those two cases is trivial. */ /* FIXME: miniscript! miniscript! miniscript! */ for (size_t i = 0; i < psbt->num_inputs; i++) { struct wally_psbt_input *input = &psbt->inputs[i]; @@ -611,10 +642,16 @@ bool psbt_finalize(struct wally_psbt *psbt) const struct wally_map_item *iws; iws = wally_map_get_integer(&input->psbt_fields, /* PSBT_IN_WITNESS_SCRIPT */ 0x05); - if (!iws || !is_to_remote_anchored_witness_script(iws->value, - iws->value_len)) + if (!iws) continue; + if (!is_to_remote_anchored_witness_script(iws->value, + iws->value_len) + && !is_anchor_witness_script(iws->value, + iws->value_len)) { + continue; + } + if (input->signatures.num_items != 1) continue; @@ -632,6 +669,19 @@ bool psbt_finalize(struct wally_psbt *psbt) * * */ + /* BOLT #3: + * #### `to_local_anchor` and `to_remote_anchor` Output (option_anchors) + *... + * OP_CHECKSIG OP_IFDUP + * OP_NOTIF + * OP_16 OP_CHECKSEQUENCEVERIFY + * OP_ENDIF + *... + * Spending of the output requires the following witness: + * + */ + + /* i.e. in both cases, this is the same thing */ wally_tx_witness_stack_init_alloc(2, &stack); wally_tx_witness_stack_add(stack, input->signatures.items[0].value, @@ -640,6 +690,7 @@ bool psbt_finalize(struct wally_psbt *psbt) iws->value, iws->value_len); wally_psbt_input_set_final_witness(input, stack); + wally_tx_witness_stack_free(stack); } ok = (wally_psbt_finalize(psbt, 0 /* flags */) == WALLY_OK); @@ -720,6 +771,12 @@ const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt, return bytes; } +bool validate_psbt(const struct wally_psbt *psbt) +{ + size_t len; + return wally_psbt_get_length(psbt, 0, &len) == WALLY_OK; +} + struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, size_t byte_len) { diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index 18b0351dae26..dc22b3bbe926 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -17,6 +17,11 @@ struct bitcoin_signature; struct bitcoin_txid; struct pubkey; + +/* Utility we need for psbt stuffs; + * add the varint onto the given array */ +void add_varint(u8 **arr, size_t val); + /** * create_psbt - Create a new psbt object * @@ -103,7 +108,7 @@ struct wally_tx *psbt_final_tx(const tal_t *ctx, const struct wally_psbt *psbt); u8 *psbt_make_key(const tal_t *ctx, u8 key_subtype, const u8 *key_data); struct wally_psbt_input *psbt_add_input(struct wally_psbt *psbt, - struct wally_tx_input *input, + const struct wally_tx_input *input, size_t insert_at); /* One stop shop for adding an input + metadata to a PSBT */ @@ -122,6 +127,9 @@ void psbt_input_set_wit_utxo(struct wally_psbt *psbt, size_t in, void psbt_input_set_utxo(struct wally_psbt *psbt, size_t in, const struct wally_tx *prev_tx); +void psbt_input_set_outpoint(struct wally_psbt *psbt, size_t in, + struct bitcoin_outpoint outpoint); + /* psbt_elements_input_set_asset - Set the asset/value fields for an * Elements PSBT (PSET, technically */ void psbt_elements_input_set_asset(struct wally_psbt *psbt, size_t in, @@ -153,7 +161,7 @@ void psbt_rm_output(struct wally_psbt *psbt, size_t remove_at); void psbt_input_add_pubkey(struct wally_psbt *psbt, size_t in, - const struct pubkey *pubkey); + const struct pubkey *pubkey, bool is_taproot); WARN_UNUSED_RESULT bool psbt_input_set_signature(struct wally_psbt *psbt, size_t in, const struct pubkey *pubkey, @@ -206,6 +214,10 @@ void psbt_output_set_unknown(const tal_t *ctx, struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, size_t in); +/* psbt_input_get_weight - Calculate the tx weight for input index `in` */ +size_t psbt_input_get_weight(const struct wally_psbt *psbt, + size_t in); + /* psbt_output_get_amount - Returns the value of this output * * @psbt - psbt @@ -214,6 +226,10 @@ struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, struct amount_sat psbt_output_get_amount(const struct wally_psbt *psbt, size_t out); +/* psbt_output_get_weight - Calculate the tx weight for output index `outnum` */ +size_t psbt_output_get_weight(const struct wally_psbt *psbt, + size_t outnum); + /* psbt_compute_fee - Returns value of fee for PSBT * * @psbt -psbt @@ -261,6 +277,7 @@ struct wally_psbt *psbt_from_b64(const tal_t *ctx, char *psbt_to_b64(const tal_t *ctx, const struct wally_psbt *psbt); const u8 *psbt_get_bytes(const tal_t *ctx, const struct wally_psbt *psbt, size_t *bytes_written); +bool validate_psbt(const struct wally_psbt *psbt); struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, size_t byte_len); void towire_wally_psbt(u8 **pptr, const struct wally_psbt *psbt); diff --git a/bitcoin/script.c b/bitcoin/script.c index a127ce96c004..54c53d82e5ee 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -310,6 +310,69 @@ u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, return script; } +u8 *scriptpubkey_raw_p2tr(const tal_t *ctx, const struct pubkey *output_pubkey) +{ + int ok; + secp256k1_xonly_pubkey x_key; + unsigned char x_key_bytes[32]; + u8 *script = tal_arr(ctx, u8, 0); + + add_op(&script, OP_1); + + ok = secp256k1_xonly_pubkey_from_pubkey(secp256k1_ctx, + &x_key, + /* pk_parity */ NULL, + &(output_pubkey->pubkey)); + assert(ok); + + ok = secp256k1_xonly_pubkey_serialize(secp256k1_ctx, + x_key_bytes, + &x_key); + assert(ok); + + script_push_bytes(&script, x_key_bytes, sizeof(x_key_bytes)); + assert(tal_count(script) == BITCOIN_SCRIPTPUBKEY_P2TR_LEN); + return script; +} + +u8 *scriptpubkey_raw_p2tr_derkey(const tal_t *ctx, const u8 output_der[33]) +{ + struct pubkey tr_key; + if (!pubkey_from_der(output_der, 33, &tr_key)) { + abort(); + } + return scriptpubkey_raw_p2tr(ctx, &tr_key); +} + +u8 *scriptpubkey_p2tr(const tal_t *ctx, const struct pubkey *inner_pubkey) +{ + unsigned char key_bytes[33]; + unsigned char tweaked_key_bytes[33]; + size_t out_len = sizeof(key_bytes); + u8 *script = tal_arr(ctx, u8, 0); + + add_op(&script, OP_1); + + secp256k1_ec_pubkey_serialize(secp256k1_ctx, key_bytes, &out_len, &inner_pubkey->pubkey, SECP256K1_EC_COMPRESSED); + /* Only commit to inner pubkey in tweak */ + if (wally_ec_public_key_bip341_tweak(key_bytes, 33, /* merkle_root*/ NULL, 0, 0 /* flags */, tweaked_key_bytes, sizeof(tweaked_key_bytes)) != WALLY_OK) + abort(); + + /* Cut off the first byte from the serialized compressed key */ + script_push_bytes(&script, tweaked_key_bytes + 1, sizeof(tweaked_key_bytes) - 1); + assert(tal_count(script) == BITCOIN_SCRIPTPUBKEY_P2TR_LEN); + return script; +} + +u8 *scriptpubkey_p2tr_derkey(const tal_t *ctx, const u8 inner_der[33]) +{ + struct pubkey tr_key; + if (!pubkey_from_der(inner_der, 33, &tr_key)) { + abort(); + } + return scriptpubkey_p2tr(ctx, &tr_key); +} + /* BOLT #3: * * #### `to_remote` Output @@ -481,10 +544,33 @@ bool is_p2wpkh(const u8 *script, struct bitcoin_address *addr) return true; } +bool is_p2tr(const u8 *script, u8 xonly_pubkey[32]) +{ + size_t script_len = tal_count(script); + + if (script_len != BITCOIN_SCRIPTPUBKEY_P2TR_LEN) + return false; + if (script[0] != OP_1) + return false; + /* x-only pubkey */ + if (script[1] != OP_PUSHBYTES(32)) + return false; + if (xonly_pubkey) + memcpy(xonly_pubkey, script+2, 32); + return true; +} + bool is_known_scripttype(const u8 *script) { return is_p2wpkh(script, NULL) || is_p2wsh(script, NULL) - || is_p2sh(script, NULL) || is_p2pkh(script, NULL); + || is_p2sh(script, NULL) || is_p2pkh(script, NULL) + || is_p2tr(script, NULL); +} + +bool is_known_segwit_scripttype(const u8 *script) +{ + return is_p2wpkh(script, NULL) || is_p2wsh(script, NULL) + || is_p2tr(script, NULL); } u8 **bitcoin_witness_sig_and_element(const tal_t *ctx, @@ -606,7 +692,8 @@ u8 *bitcoin_wscript_htlc_offer_ripemd160(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct ripemd160 *payment_ripemd, const struct pubkey *revocationkey, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { u8 *script = tal_arr(ctx, u8, 0); struct ripemd160 ripemd; @@ -638,7 +725,7 @@ u8 *bitcoin_wscript_htlc_offer_ripemd160(const tal_t *ctx, add_op(&script, OP_EQUALVERIFY); add_op(&script, OP_CHECKSIG); add_op(&script, OP_ENDIF); - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { add_number(&script, 1); add_op(&script, OP_CHECKSEQUENCEVERIFY); add_op(&script, OP_DROP); @@ -653,7 +740,8 @@ u8 *bitcoin_wscript_htlc_offer(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct sha256 *payment_hash, const struct pubkey *revocationkey, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct ripemd160 ripemd; @@ -661,7 +749,8 @@ u8 *bitcoin_wscript_htlc_offer(const tal_t *ctx, return bitcoin_wscript_htlc_offer_ripemd160(ctx, localhtlckey, remotehtlckey, &ripemd, revocationkey, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } /* BOLT #3: @@ -717,7 +806,8 @@ u8 *bitcoin_wscript_htlc_receive_ripemd(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct ripemd160 *payment_ripemd, const struct pubkey *revocationkey, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { u8 *script = tal_arr(ctx, u8, 0); struct ripemd160 ripemd; @@ -752,7 +842,7 @@ u8 *bitcoin_wscript_htlc_receive_ripemd(const tal_t *ctx, add_op(&script, OP_DROP); add_op(&script, OP_CHECKSIG); add_op(&script, OP_ENDIF); - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { add_number(&script, 1); add_op(&script, OP_CHECKSEQUENCEVERIFY); add_op(&script, OP_DROP); @@ -768,7 +858,8 @@ u8 *bitcoin_wscript_htlc_receive(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct sha256 *payment_hash, const struct pubkey *revocationkey, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct ripemd160 ripemd; @@ -776,7 +867,8 @@ u8 *bitcoin_wscript_htlc_receive(const tal_t *ctx, return bitcoin_wscript_htlc_receive_ripemd(ctx, htlc_abstimeout, localhtlckey, remotehtlckey, &ripemd, revocationkey, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } /* BOLT #3: @@ -874,9 +966,31 @@ u8 *bitcoin_wscript_anchor(const tal_t *ctx, add_op(&script, OP_CHECKSEQUENCEVERIFY); add_op(&script, OP_ENDIF); + assert(is_anchor_witness_script(script, tal_bytelen(script))); return script; } +bool is_anchor_witness_script(const u8 *script, size_t script_len) +{ + if (script_len != 34 + 1 + 1 + 1 + 1 + 1 + 1) + return false; + if (script[0] != OP_PUSHBYTES(33)) + return false; + if (script[34] != OP_CHECKSIG) + return false; + if (script[35] != OP_IFDUP) + return false; + if (script[36] != OP_NOTIF) + return false; + if (script[37] != 0x50 + 16) + return false; + if (script[38] != OP_CHECKSEQUENCEVERIFY) + return false; + if (script[39] != OP_ENDIF) + return false; + return true; +} + bool scripteq(const u8 *s1, const u8 *s2) { memcheck(s1, tal_count(s1)); diff --git a/bitcoin/script.h b/bitcoin/script.h index a00f12cc2425..660c83ad68a5 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -63,6 +63,19 @@ u8 *scriptpubkey_p2wpkh_derkey(const tal_t *ctx, const u8 der[33]); u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, const u8 *wprog, size_t wprog_size); +/* Create an output script for a "raw"(perhaps already tweaked) taproot output pubkey */ +u8 *scriptpubkey_raw_p2tr(const tal_t *ctx, const struct pubkey *output_pubkey); + +/* Same as above, but compressed key is DER-encoded. */ +u8 *scriptpubkey_raw_p2tr_derkey(const tal_t *ctx, const u8 output_der[33]); + +/* Create an output script for an internal taproot pubkey. Results in different script than + * scriptpubkey_raw_p2tr! TODO support merkle root tweaking */ +u8 *scriptpubkey_p2tr(const tal_t *ctx, const struct pubkey *inner_pubkey); + +/* Same as above, but compressed key is DER-encoded. TODO support merkle root tweaking */ +u8 *scriptpubkey_p2tr_derkey(const tal_t *ctx, const u8 inner_der[33]); + /* To-remotekey with csv max(lease_expiry - blockheight, 1) delay. */ u8 *bitcoin_wscript_to_remote_anchored(const tal_t *ctx, const struct pubkey *remote_key, @@ -98,7 +111,8 @@ u8 *bitcoin_wscript_htlc_offer(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct sha256 *payment_hash, const struct pubkey *revocationkey, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); u8 **bitcoin_witness_htlc_timeout_tx(const tal_t *ctx, const struct bitcoin_signature *localsig, const struct bitcoin_signature *remotesig, @@ -109,7 +123,8 @@ u8 *bitcoin_wscript_htlc_receive(const tal_t *ctx, const struct pubkey *remotekey, const struct sha256 *payment_hash, const struct pubkey *revocationkey, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); u8 **bitcoin_witness_htlc_success_tx(const tal_t *ctx, const struct bitcoin_signature *localsig, const struct bitcoin_signature *remotesig, @@ -122,14 +137,16 @@ u8 *bitcoin_wscript_htlc_offer_ripemd160(const tal_t *ctx, const struct pubkey *remotehtlckey, const struct ripemd160 *payment_ripemd, const struct pubkey *revocationkey, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); u8 *bitcoin_wscript_htlc_receive_ripemd(const tal_t *ctx, const struct abs_locktime *htlc_abstimeout, const struct pubkey *localkey, const struct pubkey *remotekey, const struct ripemd160 *payment_ripemd, const struct pubkey *revocationkey, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* BOLT #3 HTLC-success/HTLC-timeout output */ u8 *bitcoin_wscript_htlc_tx(const tal_t *ctx, @@ -153,12 +170,21 @@ bool is_p2wsh(const u8 *script, struct sha256 *addr); /* Is this (version 0) pay to witness pubkey hash? (extract addr if not NULL) */ bool is_p2wpkh(const u8 *script, struct bitcoin_address *addr); -/* Is this one of the four above script types? */ +/* Is this a taproot output? (extract xonly_pubkey bytes if not NULL) */ +bool is_p2tr(const u8 *script, u8 xonly_pubkey[32]); + +/* Is this one of the above script types? */ bool is_known_scripttype(const u8 *script); +/* Is this a witness script type? */ +bool is_known_segwit_scripttype(const u8 *script); + /* Is this a to-remote witness script (used for option_anchor_outputs)? */ bool is_to_remote_anchored_witness_script(const u8 *script, size_t script_len); +/* Is this an anchor witness script? */ +bool is_anchor_witness_script(const u8 *script, size_t script_len); + /* Are these two scripts equal? */ bool scripteq(const u8 *s1, const u8 *s2); @@ -177,4 +203,7 @@ void script_push_bytes(u8 **scriptp, const void *mem, size_t len); /* OP_0 + PUSH(32-byte-hash) */ #define BITCOIN_SCRIPTPUBKEY_P2WSH_LEN (1 + 1 + 32) +/* OP_1 + PUSH(32-byte-key) */ +#define BITCOIN_SCRIPTPUBKEY_P2TR_LEN (1 + 1 + 32) + #endif /* LIGHTNING_BITCOIN_SCRIPT_H */ diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 0495e5af12c2..bb05350a6c7c 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -69,6 +69,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); } diff --git a/bitcoin/test/run-psbt-from-tx.c b/bitcoin/test/run-psbt-from-tx.c index 9fe77ea4f992..8809636efb41 100644 --- a/bitcoin/test/run-psbt-from-tx.c +++ b/bitcoin/test/run-psbt-from-tx.c @@ -50,6 +50,9 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) void fromwire_sha256_double(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256_double *sha256d UNNEEDED) { fprintf(stderr, "fromwire_sha256_double called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); } diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index d99c838a496c..d2b0a0c2cdb2 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -70,6 +70,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for is_anchor_witness_script */ +bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) +{ fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } /* Generated stub for is_to_remote_anchored_witness_script */ bool is_to_remote_anchored_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_to_remote_anchored_witness_script called!\n"); abort(); } diff --git a/bitcoin/tx.c b/bitcoin/tx.c index c48d255faee8..db179fb3371a 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -299,6 +299,7 @@ void bitcoin_tx_output_set_amount(struct bitcoin_tx *tx, int outnum, } else { output->satoshi = satoshis; } + wally_psbt_output_set_amount(&tx->psbt->outputs[outnum], satoshis); } const u8 *wally_tx_output_get_script(const tal_t *ctx, @@ -965,22 +966,28 @@ size_t bitcoin_tx_2of2_input_witness_weight(void) ); } -struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw, - size_t total_weight) +struct amount_sat change_fee(u32 feerate_perkw, size_t total_weight) { size_t outweight; - struct amount_sat change_fee; + struct amount_sat fee; /* Must be able to pay for its own additional weight */ - outweight = bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN); + outweight = bitcoin_tx_output_weight(chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN); /* Rounding can cause off by one errors, so we do this */ - if (!amount_sat_sub(&change_fee, + if (!amount_sat_sub(&fee, amount_tx_fee(feerate_perkw, outweight + total_weight), amount_tx_fee(feerate_perkw, total_weight))) - return AMOUNT_SAT(0); + abort(); + return fee; +} + +struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw, + size_t total_weight) +{ + struct amount_sat fee = change_fee(feerate_perkw, total_weight); - if (!amount_sat_sub(&excess, excess, change_fee)) + if (!amount_sat_sub(&excess, excess, fee)) return AMOUNT_SAT(0); /* Must be non-dust */ diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 8bb62c50e1fb..74454a1da252 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -320,16 +320,26 @@ size_t bitcoin_tx_simple_input_weight(bool p2sh); size_t bitcoin_tx_2of2_input_witness_weight(void); /** - * change_amount - Is it worth making a P2WPKH change output at this feerate? + * change_fee - what's the cost to add a change output to this tx? + * @feerate_perkw: feerate. + * @total_weight: current weight of tx. + * + * We pass in the total_weight of the tx (up until this point) so as + * to avoid any off-by-one errors with rounding the change fee (down) + */ +struct amount_sat change_fee(u32 feerate_perkw, size_t total_weight); + +/** + * change_amount - Is it worth making a change output at this feerate? * @excess: input amount we have above the tx fee and other outputs. * @feerate_perkw: feerate. + * @total_weight: current weight of tx. + * + * Change script is P2TR for Bitcoin, P2WPKH for Elements * * If it's not worth (or possible) to make change, returns AMOUNT_SAT(0). * Otherwise returns the amount of the change output to add (@excess minus - * the additional fee for the change output itself). - * - * We pass in the total_weight of the tx (up until this point) so as - * to avoid any off-by-one errors with rounding the change fee (down) + * the change_fee()). */ struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw, size_t total_weight); diff --git a/ccan/README b/ccan/README index 24ee7eb0cc28..ba6234ee1d10 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2565-g3942778b +CCAN version: init-2577-g1ae4c432 diff --git a/ccan/ccan/base64/base64.c b/ccan/ccan/base64/base64.c index b2326293a992..89e0d38b4621 100644 --- a/ccan/ccan/base64/base64.c +++ b/ccan/ccan/base64/base64.c @@ -31,7 +31,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps, int8_t ret; ret = maps->decode_map[(unsigned char)b64letter]; - if (ret == (char)0xff) { + if (ret == (int8_t)'\xff') { errno = EDOM; return -1; } @@ -41,7 +41,7 @@ static int8_t sixbit_from_b64(const base64_maps_t *maps, bool base64_char_in_alphabet(const base64_maps_t *maps, const char b64char) { - return (maps->decode_map[(const unsigned char)b64char] != (char)0xff); + return (maps->decode_map[(const unsigned char)b64char] != (signed char)'\xff'); } void base64_init_maps(base64_maps_t *dest, const char src[64]) diff --git a/ccan/ccan/lqueue/LICENSE b/ccan/ccan/lqueue/LICENSE new file mode 120000 index 000000000000..2354d12945d3 --- /dev/null +++ b/ccan/ccan/lqueue/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/lqueue/_info b/ccan/ccan/lqueue/_info new file mode 100644 index 000000000000..d6b6232dac39 --- /dev/null +++ b/ccan/ccan/lqueue/_info @@ -0,0 +1,57 @@ +#include "config.h" +#include +#include + +/** + * lqueue - Simple, singly-linked-list queue implementation + * + * This code provides a simple implementation of the Queue abstract + * data type in terms of a singly linked (circular) list. + * + * License: BSD-MIT + * Author: David Gibson + * + * Example: + * #include + * + * struct arg { + * const char *arg; + * struct lqueue_link ql; + * }; + * + * int main(int argc, char *argv[]) + * { + * int i; + * struct arg *a; + * LQUEUE(struct arg, ql) argq = LQUEUE_INIT; + * + * for (i = 0; i < argc; i++) { + * a = malloc(sizeof(*a)); + * a->arg = argv[i]; + * lqueue_enqueue(&argq, a); + * } + * + * printf("Command line arguments in order:\n"); + * + * while (!lqueue_empty(&argq)) { + * a = lqueue_dequeue(&argq); + * printf("Argument: %s\n", a->arg); + * free(a); + * } + * + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/tcon\n"); + return 0; + } + + return 1; +} diff --git a/ccan/ccan/lqueue/lqueue.h b/ccan/ccan/lqueue/lqueue.h new file mode 100644 index 000000000000..cff51bb89103 --- /dev/null +++ b/ccan/ccan/lqueue/lqueue.h @@ -0,0 +1,238 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#ifndef CCAN_LQUEUE_H +#define CCAN_LQUEUE_H + +#include +#include +#include + +#include + +/** + * struct lqueue_link - a queue link + * @next: next entry, or front of queue, if this is the back + * + * This is used as a link within a queue entry. + * + * Example: + * struct waiter { + * char *name; + * struct lqueue_link ql; + * }; + */ +struct lqueue_link { + struct lqueue_link *next; +}; + +/** + * struct lqueue_ - a queue (internal type) + * @b: the back of the queue (NULL if empty) + */ +struct lqueue_ { + struct lqueue_link *back; +}; + +/** + * LQUEUE - declare a queue + * @type: the type of elements in the queue + * @link: the field containing the lqueue_link in @type + * + * The LQUEUE macro declares an lqueue. It can be prepended by + * "static" to define a static lqueue. The queue begins in undefined + * state, you must either initialize with LQUEUE_INIT, or call + * lqueue_init() before using it. + * + * See also: + * lqueue_init() + * + * Example: + * struct element { + * int value; + * struct lqueue_link link; + * }; + * LQUEUE(struct element, link) my_queue; + */ +#define LQUEUE(etype, link) \ + TCON_WRAP(struct lqueue_, \ + TCON_CONTAINER(canary, etype, link)) + +/** + * LQUEUE_INIT - initializer for an empty queue + * + * The LQUEUE_INIT macro returns a suitable initializer for a queue + * defined with LQUEUE. + * + * Example: + * struct element { + * int value; + * struct lqueue_link link; + * }; + * LQUEUE(struct element, link) my_queue = LQUEUE_INIT; + * + * assert(lqueue_empty(&my_queue)); + */ +#define LQUEUE_INIT \ + TCON_WRAP_INIT({ NULL, }) + +/** + * lqueue_entry - convert an lqueue_link back into the structure containing it. + * @q: the queue + * @l: the lqueue_link + * + * Example: + * struct waiter { + * char *name; + * struct lqueue_link ql; + * } w; + * LQUEUE(struct waiter, ql) my_queue; + * assert(lqueue_entry(&my_queue, &w.ql) == &w); + */ +#define lqueue_entry(q_, l_) tcon_container_of((q_), canary, (l_)) + +/** + * lqueue_init_from_back - initialize a queue with a specific back element + * @s: the lqueue to initialize + * @e: pointer to the back element of the new queue + * + * USE WITH CAUTION: This is for handling unusual cases where you have + * a pointer to an element in a previously constructed queue but can't + * conveniently pass around a normal struct lqueue. Usually you + * should use lqueue_init(). + * + * Example: + * struct element { + * int value; + * struct lqueue_link link; + * } el; + * LQUEUE(struct element, link) queue1; + * LQUEUE(struct element, link) queue2; + * + * lqueue_enqueue(&queue1, &el); + * + * lqueue_init_from_back(&queue2, lqueue_back(&queue1)); + */ +#define lqueue_init_from_back(q_, e_) \ + (lqueue_init_(tcon_unwrap(q_), tcon_member_of((q_), canary, (e_)))) + +/** + * lqueue_init - initialize a queue + * @h: the lqueue to set to an empty queue + * + * Example: + * struct element { + * int value; + * struct lqueue_link link; + * }; + * LQUEUE(struct element, link) *qp = malloc(sizeof(*qp)); + * lqueue_init(qp); + */ +#define lqueue_init(q_) \ + (lqueue_init_(tcon_unwrap(q_), NULL)) +static inline void lqueue_init_(struct lqueue_ *q, struct lqueue_link *back) +{ + q->back = back; +} + +/** + * lqueue_empty - is a queue empty? + * @q: the queue + * + * If the queue is empty, returns true. + */ +#define lqueue_empty(q_) \ + lqueue_empty_(tcon_unwrap(q_)) +static inline bool lqueue_empty_(const struct lqueue_ *q) +{ + return (q->back == NULL); +} + +/** + * lqueue_front - get front entry in a queue + * @q: the queue + * + * If the queue is empty, returns NULL. + * + * Example: + * struct element *f; + * + * f = lqueue_front(qp); + * assert(lqueue_dequeue(qp) == f); + */ +#define lqueue_front(q_) \ + lqueue_entry((q_), lqueue_front_(tcon_unwrap(q_))) +static inline struct lqueue_link *lqueue_front_(const struct lqueue_ *q) +{ + if (!q->back) + return NULL; + else + return q->back->next; +} + +/** + * lqueue_back - get back entry in a queue + * @q: the queue + * + * If the queue is empty, returns NULL. + * + * Example: + * struct element b; + * + * lqueue_enqueue(qp, &b); + * assert(lqueue_back(qp) == &b); + */ +#define lqueue_back(q_) \ + lqueue_entry((q_), lqueue_back_(tcon_unwrap(q_))) +static inline struct lqueue_link *lqueue_back_(const struct lqueue_ *q) +{ + return q->back; +} + +/** + * lqueue_enqueue - add an entry to the back of a queue + * @q: the queue to add the node to + * @e: the item to enqueue + * + * The lqueue_link does not need to be initialized; it will be overwritten. + */ +#define lqueue_enqueue(q_, e_) \ + lqueue_enqueue_(tcon_unwrap(q_), tcon_member_of((q_), canary, (e_))) +static inline void lqueue_enqueue_(struct lqueue_ *q, struct lqueue_link *e) +{ + if (lqueue_empty_(q)) { + /* New entry will be both front and back of queue */ + e->next = e; + q->back = e; + } else { + e->next = lqueue_front_(q); + q->back->next = e; + q->back = e; + } +} + +/** + * lqueue_dequeue - remove and return the entry from the front of the queue + * @q: the queue + * + * Note that this leaves the returned entry's link in an undefined + * state; it can be added to another queue, but not deleted again. + */ +#define lqueue_dequeue(q_) \ + lqueue_entry((q_), lqueue_dequeue_(tcon_unwrap(q_))) +static inline struct lqueue_link *lqueue_dequeue_(struct lqueue_ *q) +{ + struct lqueue_link *front; + + if (lqueue_empty_(q)) + return NULL; + + front = lqueue_front_(q); + if (front == lqueue_back_(q)) { + assert(front->next == front); + q->back = NULL; + } else { + q->back->next = front->next; + } + return front; +} + +#endif /* CCAN_LQUEUE_H */ diff --git a/ccan/ccan/lqueue/test/run.c b/ccan/ccan/lqueue/test/run.c new file mode 100644 index 000000000000..6791d2518890 --- /dev/null +++ b/ccan/ccan/lqueue/test/run.c @@ -0,0 +1,69 @@ +#include "config.h" + +#include +#include + +struct waiter { + const char *name; + struct lqueue_link ql; +}; + +int main(void) +{ + LQUEUE(struct waiter, ql) q = LQUEUE_INIT; + struct waiter a = { "Alice" }; + struct waiter b = { "Bob" }; + struct waiter c = { "Carol" }; + struct waiter *waiter; + + /* This is how many tests you plan to run */ + plan_tests(25); + + ok1(lqueue_empty(&q)); + ok1(lqueue_front(&q) == NULL); + ok1(lqueue_back(&q) == NULL); + + lqueue_enqueue(&q, &a); + + ok1(!lqueue_empty(&q)); + ok1(lqueue_front(&q) == &a); + ok1(lqueue_back(&q) == &a); + + lqueue_enqueue(&q, &b); + + ok1(!lqueue_empty(&q)); + ok1(lqueue_front(&q) == &a); + ok1(lqueue_back(&q) == &b); + + lqueue_enqueue(&q, &c); + + ok1(!lqueue_empty(&q)); + ok1(lqueue_front(&q) == &a); + ok1(lqueue_back(&q) == &c); + + waiter = lqueue_dequeue(&q); + ok1(waiter == &a); + + ok1(!lqueue_empty(&q)); + ok1(lqueue_front(&q) == &b); + ok1(lqueue_back(&q) == &c); + + waiter = lqueue_dequeue(&q); + ok1(waiter == &b); + + ok1(!lqueue_empty(&q)); + ok1(lqueue_front(&q) == &c); + ok1(lqueue_back(&q) == &c); + + waiter = lqueue_dequeue(&q); + ok1(waiter == &c); + + ok1(lqueue_empty(&q)); + ok1(lqueue_front(&q) == NULL); + ok1(lqueue_back(&q) == NULL); + + ok1(lqueue_dequeue(&q) == NULL); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/opt/helpers.c b/ccan/ccan/opt/helpers.c index 118e543602e0..df7ee6bb1f20 100644 --- a/ccan/ccan/opt/helpers.c +++ b/ccan/ccan/opt/helpers.c @@ -138,10 +138,11 @@ char *opt_set_floatval(const char *arg, float *f) return NULL; } -void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f) +bool opt_show_floatval(char *buf, size_t len, const float *f) { double d = *f; - opt_show_doubleval(buf, &d); + opt_show_doubleval(buf, len, &d); + return true; } char *opt_set_doubleval(const char *arg, double *d) @@ -160,9 +161,10 @@ char *opt_set_doubleval(const char *arg, double *d) return NULL; } -void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d) +bool opt_show_doubleval(char *buf, size_t len, const double *d) { - snprintf(buf, OPT_SHOW_LEN, "%f", *d); + snprintf(buf, len, "%f", *d); + return true; } char *opt_inc_intval(int *i) @@ -196,52 +198,60 @@ char *opt_usage_and_exit(const char *extra) exit(0); } -void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b) +bool opt_show_bool(char *buf, size_t len, const bool *b) { - strncpy(buf, *b ? "true" : "false", OPT_SHOW_LEN); + strncpy(buf, *b ? "true" : "false", len); + return true; } -void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b) +bool opt_show_invbool(char *buf, size_t len, const bool *b) { - strncpy(buf, *b ? "false" : "true", OPT_SHOW_LEN); + strncpy(buf, *b ? "false" : "true", len); + return true; } -void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p) +bool opt_show_charp(char *buf, size_t len, char *const *p) { - if (*p){ - size_t len = strlen(*p); + if (*p) { + size_t plen = strlen(*p); + if (len < 2) + return false; buf[0] = '"'; - if (len > OPT_SHOW_LEN - 2) - len = OPT_SHOW_LEN - 2; - strncpy(buf+1, *p, len); - buf[1+len] = '"'; - if (len < OPT_SHOW_LEN - 2) - buf[2+len] = '\0'; - } - else { - strncpy(buf, "(nil)", OPT_SHOW_LEN); + if (plen > len - 2) + plen = len - 2; + strncpy(buf+1, *p, plen); + buf[1+plen] = '"'; + if (plen < len - 2) + buf[2+plen] = '\0'; + return true; + } else { + return false; } } /* Show an integer value, various forms. */ -void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i) +bool opt_show_intval(char *buf, size_t len, const int *i) { - snprintf(buf, OPT_SHOW_LEN, "%i", *i); + snprintf(buf, len, "%i", *i); + return true; } -void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui) +bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui) { - snprintf(buf, OPT_SHOW_LEN, "%u", *ui); + snprintf(buf, len, "%u", *ui); + return true; } -void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l) +bool opt_show_longval(char *buf, size_t len, const long *l) { - snprintf(buf, OPT_SHOW_LEN, "%li", *l); + snprintf(buf, len, "%li", *l); + return true; } -void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul) +bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul) { - snprintf(buf, OPT_SHOW_LEN, "%lu", *ul); + snprintf(buf, len, "%lu", *ul); + return true; } /* a helper function that multiplies out an argument's kMGTPE suffix in the @@ -447,14 +457,14 @@ char * opt_set_uintval_si(const char *arg, unsigned int *u) are separate but essentially identical functions for signed and unsigned values, so that unsigned values greater than LLONG_MAX get suffixes. */ -static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll, - const long long base) +static void show_llong_with_suffix(char *buf, size_t len, long long ll, + const long long base) { const char *suffixes = "kMGTPE"; int i; if (ll == 0){ /*zero is special because everything divides it (you'd get "0E")*/ - snprintf(buf, OPT_SHOW_LEN, "0"); + snprintf(buf, len, "0"); return; } for (i = 0; i < strlen(suffixes); i++){ @@ -464,19 +474,20 @@ static void show_llong_with_suffix(char buf[OPT_SHOW_LEN], long long ll, ll = tmp; } if (i == 0) - snprintf(buf, OPT_SHOW_LEN, "%"PRId64, (int64_t)ll); + snprintf(buf, len, "%"PRId64, (int64_t)ll); else - snprintf(buf, OPT_SHOW_LEN, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]); + snprintf(buf, len, "%"PRId64"%c", (int64_t)ll, suffixes[i - 1]); } -static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long ull, +static void show_ullong_with_suffix(char *buf, size_t len, + unsigned long long ull, const unsigned base) { const char *suffixes = "kMGTPE"; int i; if (ull == 0){ /*zero is special because everything divides it (you'd get "0E")*/ - snprintf(buf, OPT_SHOW_LEN, "0"); + snprintf(buf, len, "0"); return; } for (i = 0; i < strlen(suffixes); i++){ @@ -486,72 +497,84 @@ static void show_ullong_with_suffix(char buf[OPT_SHOW_LEN], unsigned long long u ull = tmp; } if (i == 0) - snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, (uint64_t)ull); + snprintf(buf, len, "%"PRIu64, (uint64_t)ull); else - snprintf(buf, OPT_SHOW_LEN, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]); + snprintf(buf, len, "%"PRIu64"%c", (uint64_t)ull, suffixes[i - 1]); } /* _bi, signed */ -void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x) +bool opt_show_intval_bi(char *buf, size_t len, const int *x) { - show_llong_with_suffix(buf, *x, 1024); + show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x) +bool opt_show_longval_bi(char *buf, size_t len, const long *x) { - show_llong_with_suffix(buf, *x, 1024); + show_llong_with_suffix(buf, len, *x, 1024); + return true; } -void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x) +bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x) { - show_llong_with_suffix(buf, *x, 1024); + show_llong_with_suffix(buf, len, *x, 1024); + return true; } /* _bi, unsigned */ -void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x) +bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x) +bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } -void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x) +bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1024); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1024); + return true; } /* _si, signed */ -void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x) +bool opt_show_intval_si(char *buf, size_t len, const int *x) { - show_llong_with_suffix(buf, (long long) *x, 1000); + show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x) +bool opt_show_longval_si(char *buf, size_t len, const long *x) { - show_llong_with_suffix(buf, (long long) *x, 1000); + show_llong_with_suffix(buf, len, (long long) *x, 1000); + return true; } -void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x) +bool opt_show_longlongval_si(char *buf, size_t len, const long long *x) { - show_llong_with_suffix(buf, *x, 1000); + show_llong_with_suffix(buf, len, *x, 1000); + return true; } /* _si, unsigned */ -void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x) +bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x) +bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } -void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x) +bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x) { - show_ullong_with_suffix(buf, (unsigned long long) *x, 1000); + show_ullong_with_suffix(buf, len, (unsigned long long) *x, 1000); + return true; } diff --git a/ccan/ccan/opt/opt.c b/ccan/ccan/opt/opt.c index d376a598da93..9149374cb001 100644 --- a/ccan/ccan/opt/opt.c +++ b/ccan/ccan/opt/opt.c @@ -34,7 +34,7 @@ static const char *next_name(const char *names, unsigned *len) static const char *first_opt(unsigned *i, unsigned *len) { for (*i = 0; *i < opt_count; (*i)++) { - if (opt_table[*i].type == OPT_SUBTABLE) + if (opt_table[*i].type & OPT_SUBTABLE) continue; return first_name(opt_table[*i].names, len); } @@ -44,7 +44,7 @@ static const char *first_opt(unsigned *i, unsigned *len) static const char *next_opt(const char *p, unsigned *i, unsigned *len) { for (; *i < opt_count; (*i)++) { - if (opt_table[*i].type == OPT_SUBTABLE) + if (opt_table[*i].type & OPT_SUBTABLE) continue; if (!p) return first_name(opt_table[*i].names, len); @@ -114,10 +114,11 @@ static void check_opt(const struct opt_table *entry) { const char *p; unsigned len; + enum opt_type type = entry->type & (OPT_USER_MIN-1); - if (entry->type != OPT_HASARG && entry->type != OPT_NOARG - && entry->type != (OPT_EARLY|OPT_HASARG) - && entry->type != (OPT_EARLY|OPT_NOARG)) + if (type != OPT_HASARG && type != OPT_NOARG + && type != (OPT_EARLY|OPT_HASARG) + && type != (OPT_EARLY|OPT_NOARG)) failmsg("Option %s: unknown entry type %u", entry->names, entry->type); @@ -161,7 +162,7 @@ static void add_opt(const struct opt_table *entry) void _opt_register(const char *names, enum opt_type type, char *(*cb)(void *arg), char *(*cb_arg)(const char *optarg, void *arg), - void (*show)(char buf[OPT_SHOW_LEN], const void *arg), + bool (*show)(char *buf, size_t len, const void *arg), const void *arg, const char *desc) { struct opt_table opt; @@ -181,7 +182,7 @@ bool opt_unregister(const char *names) int found = -1, i; for (i = 0; i < opt_count; i++) { - if (opt_table[i].type == OPT_SUBTABLE) + if (opt_table[i].type & OPT_SUBTABLE) continue; if (strcmp(opt_table[i].names, names) == 0) found = i; @@ -203,7 +204,7 @@ void opt_register_table(const struct opt_table entry[], const char *desc) add_opt(&heading); } for (i = 0; entry[i].type != OPT_END; i++) { - if (entry[i].type == OPT_SUBTABLE) + if (entry[i].type & OPT_SUBTABLE) opt_register_table(subtable_of(&entry[i]), entry[i].desc); else { diff --git a/ccan/ccan/opt/opt.h b/ccan/ccan/opt/opt.h index 6f4b9dda8c85..e0331be26423 100644 --- a/ccan/ccan/opt/opt.h +++ b/ccan/ccan/opt/opt.h @@ -47,10 +47,11 @@ struct opt_table; * where "type" is the type of the @arg argument. The first argument to the * @cb is the argument found on the commandline. * - * Similarly, if @show is not NULL, it should be of type "void *show(char *, - * const type *)". It should write up to OPT_SHOW_LEN bytes into the first - * argument; unless it uses the entire OPT_SHOW_LEN bytes it should - * nul-terminate that buffer. + * Similarly, if @show is not NULL, it should be of type "bool show(char *, + * size_t len, const type *)". If there is no default, it should return false, + * otherwise it should write up to len bytes into the first argument and + * return true; unless it uses the entire len bytes it should nul-terminate that + * buffer. * * Any number of equivalent short or long options can be listed in @names, * separated by '|'. Short options are a single hyphen followed by a single @@ -429,40 +430,38 @@ void opt_usage_exit_fail(const char *msg, ...) NORETURN; */ extern const char opt_hidden[]; -/* Maximum length of arg to show in opt_usage */ -#define OPT_SHOW_LEN 80 - /* Standard helpers. You can write your own: */ /* Sets the @b to true. */ char *opt_set_bool(bool *b); /* Sets @b based on arg: (yes/no/true/false). */ char *opt_set_bool_arg(const char *arg, bool *b); -void opt_show_bool(char buf[OPT_SHOW_LEN], const bool *b); +bool opt_show_bool(char *buf, size_t len, const bool *b); /* The inverse */ char *opt_set_invbool(bool *b); -void opt_show_invbool(char buf[OPT_SHOW_LEN], const bool *b); +bool opt_show_invbool(char *buf, size_t len, const bool *b); /* Sets @b based on !arg: (yes/no/true/false). */ char *opt_set_invbool_arg(const char *arg, bool *b); /* Set a char *. */ char *opt_set_charp(const char *arg, char **p); -void opt_show_charp(char buf[OPT_SHOW_LEN], char *const *p); +/* If *p is NULL, this returns false (i.e. doesn't show a default) */ +bool opt_show_charp(char *buf, size_t len, char *const *p); /* Set an integer value, various forms. Sets to 1 on arg == NULL. */ char *opt_set_intval(const char *arg, int *i); -void opt_show_intval(char buf[OPT_SHOW_LEN], const int *i); +bool opt_show_intval(char *buf, size_t len, const int *i); char *opt_set_uintval(const char *arg, unsigned int *ui); -void opt_show_uintval(char buf[OPT_SHOW_LEN], const unsigned int *ui); +bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui); char *opt_set_longval(const char *arg, long *l); -void opt_show_longval(char buf[OPT_SHOW_LEN], const long *l); +bool opt_show_longval(char *buf, size_t len, const long *l); char *opt_set_ulongval(const char *arg, unsigned long *ul); -void opt_show_ulongval(char buf[OPT_SHOW_LEN], const unsigned long *ul); +bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul); /* Set an floating point value, various forms. */ char *opt_set_floatval(const char *arg, float *f); -void opt_show_floatval(char buf[OPT_SHOW_LEN], const float *f); +bool opt_show_floatval(char *buf, size_t len, const float *f); char *opt_set_doubleval(const char *arg, double *d); -void opt_show_doubleval(char buf[OPT_SHOW_LEN], const double *d); +bool opt_show_doubleval(char *buf, size_t len, const double *d); /* the following setting functions accept k, M, G, T, P, or E suffixes, which multiplies the numeric value by the corresponding power of 1000 or 1024 @@ -482,19 +481,19 @@ char *opt_set_ulonglongval_bi(const char *arg, unsigned long long *ll); char *opt_set_ulonglongval_si(const char *arg, unsigned long long *ll); -void opt_show_intval_bi(char buf[OPT_SHOW_LEN], const int *x); -void opt_show_longval_bi(char buf[OPT_SHOW_LEN], const long *x); -void opt_show_longlongval_bi(char buf[OPT_SHOW_LEN], const long long *x); -void opt_show_uintval_bi(char buf[OPT_SHOW_LEN], const unsigned int *x); -void opt_show_ulongval_bi(char buf[OPT_SHOW_LEN], const unsigned long *x); -void opt_show_ulonglongval_bi(char buf[OPT_SHOW_LEN], const unsigned long long *x); +bool opt_show_intval_bi(char *buf, size_t len, const int *x); +bool opt_show_longval_bi(char *buf, size_t len, const long *x); +bool opt_show_longlongval_bi(char *buf, size_t len, const long long *x); +bool opt_show_uintval_bi(char *buf, size_t len, const unsigned int *x); +bool opt_show_ulongval_bi(char *buf, size_t len, const unsigned long *x); +bool opt_show_ulonglongval_bi(char *buf, size_t len, const unsigned long long *x); -void opt_show_intval_si(char buf[OPT_SHOW_LEN], const int *x); -void opt_show_longval_si(char buf[OPT_SHOW_LEN], const long *x); -void opt_show_longlongval_si(char buf[OPT_SHOW_LEN], const long long *x); -void opt_show_uintval_si(char buf[OPT_SHOW_LEN], const unsigned int *x); -void opt_show_ulongval_si(char buf[OPT_SHOW_LEN], const unsigned long *x); -void opt_show_ulonglongval_si(char buf[OPT_SHOW_LEN], const unsigned long long *x); +bool opt_show_intval_si(char *buf, size_t len, const int *x); +bool opt_show_longval_si(char *buf, size_t len, const long *x); +bool opt_show_longlongval_si(char *buf, size_t len, const long long *x); +bool opt_show_uintval_si(char *buf, size_t len, const unsigned int *x); +bool opt_show_ulongval_si(char *buf, size_t len, const unsigned long *x); +bool opt_show_ulonglongval_si(char *buf, size_t len, const unsigned long long *x); @@ -509,6 +508,30 @@ char *opt_version_and_exit(const char *version); /* Display usage string to stdout, exit(0). */ char *opt_usage_and_exit(const char *extra); +/** + * opt_find_long: low-level access to the parser + * @arg: string of form 'arg' or 'arg=val'. + * @optarg: set to `val` of present in arg, otherwise NULL. Can be NULL. + * + * Returns NULL if option is unknown. Sets *@optarg to NULL if + * there's no '='. + */ +struct opt_table *opt_find_long(const char *arg, const char **optarg); + +/** + * opt_find_short: low-level access to the parser + * @arg: character representing short option + * + * Returns NULL if option is unknown. + */ +struct opt_table *opt_find_short(char arg); + +/* opt_type bits reserved for users to play with (ignored!). + * You can set bits in type e.g. (1<type & OPT_NOARG) { if (optarg) return parse_err(errlog, argv[0], o, len, "doesn't allow an argument"); - if ((opt_table[i].type & OPT_EARLY) == is_early) - problem = opt_table[i].cb(opt_table[i].u.arg); + if ((ot->type & OPT_EARLY) == is_early) + problem = ot->cb(ot->u.arg); } else { if (!optarg) { /* Swallow any short options as optarg, eg -afile */ @@ -117,9 +158,8 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset, if (!optarg) return parse_err(errlog, argv[0], o, len, "requires an argument"); - if ((opt_table[i].type & OPT_EARLY) == is_early) - problem = opt_table[i].cb_arg(optarg, - opt_table[i].u.arg); + if ((ot->type & OPT_EARLY) == is_early) + problem = ot->cb_arg(optarg, ot->u.arg); } if (problem) { diff --git a/ccan/ccan/opt/test/run-add_desc.c b/ccan/ccan/opt/test/run-add_desc.c index b559c7f74768..03e6986dd191 100644 --- a/ccan/ccan/opt/test/run-add_desc.c +++ b/ccan/ccan/opt/test/run-add_desc.c @@ -4,15 +4,24 @@ #include #include -static void show_10(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED) +static bool show_10(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', 10); buf[10] = '\0'; + return true; } -static void show_max(char buf[OPT_SHOW_LEN], const void *arg UNNEEDED) +static bool show_10_false(char *buf, size_t len, const void *arg UNNEEDED) +{ + memset(buf, 'X', 10); + buf[10] = '\0'; + return false; +} + +static bool show_max(char *buf, size_t len, const void *arg UNNEEDED) { memset(buf, 'X', OPT_SHOW_LEN); + return true; } /* Test add_desc helper. */ @@ -22,7 +31,7 @@ int main(void) char *ret; size_t len, max; - plan_tests(30); + plan_tests(32); opt.show = NULL; opt.names = "01234"; @@ -113,6 +122,14 @@ int main(void) " (default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...)\n") == 0); free(ret); len = max = 0; + /* With show function which fails doesn't print. */ + opt.show = show_10_false; + ret = add_desc(NULL, &len, &max, 7, 41, &opt); + ok1(len < max); + ret[len] = '\0'; + ok1(strcmp(ret, "01234 0123456789 0\n") == 0); + free(ret); len = max = 0; + /* With added " ". Fits, just. */ opt.show = NULL; opt.type = OPT_HASARG; diff --git a/ccan/ccan/opt/test/run-correct-reporting.c b/ccan/ccan/opt/test/run-correct-reporting.c index 8534f291ac3e..0c4f6c869397 100644 --- a/ccan/ccan/opt/test/run-correct-reporting.c +++ b/ccan/ccan/opt/test/run-correct-reporting.c @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { - plan_tests(12); + plan_tests(14); /* --aaa without args. */ opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", ""); @@ -42,6 +42,10 @@ int main(int argc, char *argv[]) free(err_output); err_output = NULL; + opt_register_noarg("-d", test_noarg, NULL, ""); + ok1(!parse_args(&argc, &argv, "-dc", NULL)); + ok1(strstr(err_output, ": -c: requires an argument")); + /* parse_args allocates argv */ free(argv); return exit_status(); diff --git a/ccan/ccan/opt/test/run-helpers.c b/ccan/ccan/opt/test/run-helpers.c index 0a08a85f7aa3..9aa41fe8db62 100644 --- a/ccan/ccan/opt/test/run-helpers.c +++ b/ccan/ccan/opt/test/run-helpers.c @@ -476,26 +476,26 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -77; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-77") == 0); i = 0; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 77; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -1234 * k; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-1234k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * M; - opt_show_intval_bi(buf, &i); + opt_show_intval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -506,27 +506,27 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -77; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 77; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -1 * k; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-1k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * M; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 0; - opt_show_longval_bi(buf, &i); + opt_show_longval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -537,23 +537,23 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -7777; - opt_show_longlongval_bi(buf, &i); + opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 7777; - opt_show_longlongval_bi(buf, &i); + opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -10240000 * k; - opt_show_longlongval_bi(buf, &i); + opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-10000M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 5 * P; - opt_show_longlongval_bi(buf, &i); + opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "5P") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * P; - opt_show_longlongval_bi(buf, &i); + opt_show_longlongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1E") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -564,19 +564,19 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 77; - opt_show_uintval_bi(buf, &i); + opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1234 * k; - opt_show_uintval_bi(buf, &i); + opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1234k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_uintval_bi(buf, &i); + opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * M; - opt_show_uintval_bi(buf, &i); + opt_show_uintval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -587,23 +587,23 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 77; - opt_show_ulongval_bi(buf, &i); + opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = k; - opt_show_ulongval_bi(buf, &i); + opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_ulongval_bi(buf, &i); + opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * M; - opt_show_ulongval_bi(buf, &i); + opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 0; - opt_show_ulongval_bi(buf, &i); + opt_show_ulongval_bi(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -614,19 +614,19 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 7777; - opt_show_ulonglongval_bi(buf, (unsigned long long *)&i); + opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 10240000 * k; - opt_show_ulonglongval_bi(buf, (unsigned long long *)&i); + opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "10000M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 5 * P; - opt_show_ulonglongval_bi(buf, (unsigned long long *)&i); + opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "5P") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * P; - opt_show_ulonglongval_bi(buf, (unsigned long long *)&i); + opt_show_ulonglongval_bi(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "1E") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -860,26 +860,26 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -77; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-77") == 0); i = 0; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 77; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -1234 * k; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-1234k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1000 * M; - opt_show_intval_si(buf, &i); + opt_show_intval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -890,27 +890,27 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -77; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 77; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -1 * k; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-1k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1000 * M; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 0; - opt_show_longval_si(buf, &i); + opt_show_longval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -921,23 +921,23 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = -7777; - opt_show_longlongval_si(buf, &i); + opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 7777; - opt_show_longlongval_si(buf, &i); + opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = -10240000 * k; - opt_show_longlongval_si(buf, &i); + opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-10240M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 5 * P; - opt_show_longlongval_si(buf, &i); + opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "5P") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 2000 * P; - opt_show_longlongval_si(buf, &i); + opt_show_longlongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "2E") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -948,19 +948,19 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 77; - opt_show_uintval_si(buf, &i); + opt_show_uintval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1234 * k; - opt_show_uintval_si(buf, &i); + opt_show_uintval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1234k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_uintval_si(buf, &i); + opt_show_uintval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1000 * M; - opt_show_uintval_si(buf, &i); + opt_show_uintval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1G") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -971,23 +971,23 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 77; - opt_show_ulongval_si(buf, &i); + opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = k; - opt_show_ulongval_si(buf, &i); + opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1k") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 500 * M; - opt_show_ulongval_si(buf, &i); + opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "500M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1024 * M; - opt_show_ulongval_si(buf, &i); + opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "1024M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 0; - opt_show_ulongval_si(buf, &i); + opt_show_ulongval_si(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "0") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -998,19 +998,19 @@ int main(int argc, char *argv[]) char buf[OPT_SHOW_LEN+2] = { 0 }; buf[OPT_SHOW_LEN] = '!'; i = 7777; - opt_show_ulonglongval_si(buf, (unsigned long long *)&i); + opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "7777") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 10240000 * k; - opt_show_ulonglongval_si(buf, (unsigned long long *)&i); + opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "10240M") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 5 * P; - opt_show_ulonglongval_si(buf, (unsigned long long *)&i); + opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "5P") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 1000 * P; - opt_show_ulonglongval_si(buf, (unsigned long long *)&i); + opt_show_ulonglongval_si(buf, OPT_SHOW_LEN, (unsigned long long *)&i); ok1(strcmp(buf, "1E") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1090,12 +1090,12 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; b = true; - opt_show_bool(buf, &b); + opt_show_bool(buf, OPT_SHOW_LEN, &b); ok1(strcmp(buf, "true") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); b = false; - opt_show_bool(buf, &b); + opt_show_bool(buf, OPT_SHOW_LEN, &b); ok1(strcmp(buf, "false") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1107,12 +1107,12 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; b = true; - opt_show_invbool(buf, &b); + opt_show_invbool(buf, OPT_SHOW_LEN, &b); ok1(strcmp(buf, "false") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); b = false; - opt_show_invbool(buf, &b); + opt_show_invbool(buf, OPT_SHOW_LEN, &b); ok1(strcmp(buf, "true") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1126,14 +1126,14 @@ int main(int argc, char *argv[]) /* Short test. */ p = str; strcpy(p, "short"); - opt_show_charp(buf, &p); + opt_show_charp(buf, OPT_SHOW_LEN, &p); ok1(strcmp(buf, "\"short\"") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); /* Truncate test. */ memset(p, 'x', OPT_SHOW_LEN*2); p[OPT_SHOW_LEN*2-1] = '\0'; - opt_show_charp(buf, &p); + opt_show_charp(buf, OPT_SHOW_LEN, &p); ok1(buf[0] == '"'); ok1(buf[OPT_SHOW_LEN-1] == '"'); ok1(buf[OPT_SHOW_LEN] == '!'); @@ -1147,12 +1147,12 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; i = -77; - opt_show_intval(buf, &i); + opt_show_intval(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "-77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); i = 77; - opt_show_intval(buf, &i); + opt_show_intval(buf, OPT_SHOW_LEN, &i); ok1(strcmp(buf, "77") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1164,7 +1164,7 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; ui = 4294967295U; - opt_show_uintval(buf, &ui); + opt_show_uintval(buf, OPT_SHOW_LEN, &ui); ok1(strcmp(buf, "4294967295") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1176,7 +1176,7 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; l = 1234567890L; - opt_show_longval(buf, &l); + opt_show_longval(buf, OPT_SHOW_LEN, &l); ok1(strcmp(buf, "1234567890") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1188,7 +1188,7 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; ul = 4294967295UL; - opt_show_ulongval(buf, &ul); + opt_show_ulongval(buf, OPT_SHOW_LEN, &ul); ok1(strcmp(buf, "4294967295") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1200,12 +1200,12 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; f = -77.5; - opt_show_floatval(buf, &f); + opt_show_floatval(buf, OPT_SHOW_LEN, &f); ok1(strcmp(buf, "-77.500000") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); f = 77.5; - opt_show_floatval(buf, &f); + opt_show_floatval(buf, OPT_SHOW_LEN, &f); ok1(strcmp(buf, "77.500000") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } @@ -1217,12 +1217,12 @@ int main(int argc, char *argv[]) buf[OPT_SHOW_LEN] = '!'; d = -77; - opt_show_doubleval(buf, &d); + opt_show_doubleval(buf, OPT_SHOW_LEN, &d); ok1(strcmp(buf, "-77.000000") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); d = 77; - opt_show_doubleval(buf, &d); + opt_show_doubleval(buf, OPT_SHOW_LEN, &d); ok1(strcmp(buf, "77.000000") == 0); ok1(buf[OPT_SHOW_LEN] == '!'); } diff --git a/ccan/ccan/opt/test/run-userbits.c b/ccan/ccan/opt/test/run-userbits.c new file mode 100644 index 000000000000..7f102f08ec0b --- /dev/null +++ b/ccan/ccan/opt/test/run-userbits.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +int main(int argc, char *argv[]) +{ + const char *myname = argv[0]; + + plan_tests(28); + + opt_register_noarg("-a", test_noarg, NULL, "All"); + opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll"); + opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "AAAAAAll"); + + ok1(strcmp(opt_table[0].names, "-a") == 0); + ok1(opt_table[0].type == OPT_NOARG); + ok1(strcmp(opt_table[1].names, "--aaa") == 0); + ok1(opt_table[1].type == OPT_NOARG); + ok1(strcmp(opt_table[2].names, "-b|--bbb") == 0); + ok1(opt_table[2].type == OPT_HASARG); + + opt_table[0].type |= (1 << OPT_USER_START); + opt_table[1].type |= ((1 << OPT_USER_END)-1) - ((1 << OPT_USER_START)-1); + opt_table[2].type |= (1 << OPT_USER_END); + + /* Should all work fine! */ + ok1(parse_args(&argc, &argv, "-a", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(test_cb_called == 1); + + ok1(parse_args(&argc, &argv, "--aaa", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(test_cb_called == 2); + + /* This one needs an arg. */ + ok1(parse_args(&argc, &argv, "-b", NULL) == false); + ok1(test_cb_called == 2); + ok1(parse_args(&argc, &argv, "-b", "bbb", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(argv[1] == NULL); + ok1(test_cb_called == 3); + + ok1(parse_args(&argc, &argv, "--bbb", "bbb", NULL)); + ok1(argc == 1); + ok1(argv[0] == myname); + ok1(argv[1] == NULL); + ok1(test_cb_called == 4); + + /* parse_args allocates argv */ + free(argv); + return exit_status(); +} diff --git a/ccan/ccan/opt/test/utils.c b/ccan/ccan/opt/test/utils.c index 2ff04884ebdc..61199fb4676d 100644 --- a/ccan/ccan/opt/test/utils.c +++ b/ccan/ccan/opt/test/utils.c @@ -21,9 +21,10 @@ char *test_arg(const char *optarg, const char *arg) return NULL; } -void show_arg(char buf[OPT_SHOW_LEN], const char *arg) +bool show_arg(char *buf, size_t len, const char *arg) { - strncpy(buf, arg, OPT_SHOW_LEN); + strncpy(buf, arg, len); + return true; } char *err_output = NULL; diff --git a/ccan/ccan/opt/test/utils.h b/ccan/ccan/opt/test/utils.h index 12cf0b753e99..3ada62d117c0 100644 --- a/ccan/ccan/opt/test/utils.h +++ b/ccan/ccan/opt/test/utils.h @@ -13,7 +13,7 @@ void reset_options(void); extern unsigned int test_cb_called; char *test_noarg(void *arg); char *test_arg(const char *optarg, const char *arg); -void show_arg(char buf[OPT_SHOW_LEN], const char *arg); +bool show_arg(char *buf, size_t len, const char *arg); extern struct opt_table short_table[]; extern struct opt_table long_table[]; diff --git a/ccan/ccan/opt/usage.c b/ccan/ccan/opt/usage.c index 8ee4ebd03ad5..568e4661d809 100644 --- a/ccan/ccan/opt/usage.c +++ b/ccan/ccan/opt/usage.c @@ -20,6 +20,9 @@ const char opt_hidden[1]; #define MIN_DESC_WIDTH 40 #define MIN_TOTAL_WIDTH 50 +/* Maximum length of arg to show in opt_usage */ +#define OPT_SHOW_LEN 80 + static unsigned int get_columns(void) { int ws_col = 0; @@ -148,20 +151,20 @@ static char *add_desc(char *base, size_t *len, size_t *max, if (opt->show) { char buf[OPT_SHOW_LEN + sizeof("...")]; strcpy(buf + OPT_SHOW_LEN, "..."); - opt->show(buf, opt->u.arg); + if (opt->show(buf, OPT_SHOW_LEN, opt->u.arg)) { + /* If it doesn't fit on this line, indent. */ + if (off + strlen(" (default: ") + strlen(buf) + strlen(")") + > width) { + base = add_indent(base, len, max, indent); + } else { + /* Remove \n. */ + (*len)--; + } - /* If it doesn't fit on this line, indent. */ - if (off + strlen(" (default: ") + strlen(buf) + strlen(")") - > width) { - base = add_indent(base, len, max, indent); - } else { - /* Remove \n. */ - (*len)--; + base = add_str(base, len, max, " (default: "); + base = add_str(base, len, max, buf); + base = add_str(base, len, max, ")\n"); } - - base = add_str(base, len, max, " (default: "); - base = add_str(base, len, max, buf); - base = add_str(base, len, max, ")\n"); } return base; } @@ -182,10 +185,10 @@ char *opt_usage(const char *argv0, const char *extra) size_t l; if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) + if (opt_table[i].type & OPT_SUBTABLE) continue; l = strlen(opt_table[i].names); - if (opt_table[i].type == OPT_HASARG + if ((opt_table[i].type & OPT_HASARG) && !strchr(opt_table[i].names, ' ') && !strchr(opt_table[i].names, '=')) l += strlen(" "); @@ -221,7 +224,7 @@ char *opt_usage(const char *argv0, const char *extra) for (i = 0; i < opt_count; i++) { if (opt_table[i].desc == opt_hidden) continue; - if (opt_table[i].type == OPT_SUBTABLE) { + if (opt_table[i].type & OPT_SUBTABLE) { ret = add_str(ret, &len, &max, opt_table[i].desc); ret = add_str(ret, &len, &max, ":\n"); continue; diff --git a/ccan/ccan/rune/coding.c b/ccan/ccan/rune/coding.c index f4d110283245..495d37c34e31 100644 --- a/ccan/ccan/rune/coding.c +++ b/ccan/ccan/rune/coding.c @@ -206,7 +206,7 @@ bool rune_condition_is_valid(enum rune_condition cond) size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen) { for (size_t i = 0; i < alternstrlen; i++) { - if (cispunct(alternstr[i])) + if (cispunct(alternstr[i]) && alternstr[i] != '_') return i; } return alternstrlen; diff --git a/ccan/ccan/rune/test/run.c b/ccan/ccan/rune/test/run.c index e532655284db..d90b701c609c 100644 --- a/ccan/ccan/rune/test/run.c +++ b/ccan/ccan/rune/test/run.c @@ -43,7 +43,7 @@ int main(void) assert(vecs); lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY); - plan_tests(343); + plan_tests(355); for (size_t i = 0; lines[i]; i++) { struct rune *rune1, *rune2; diff --git a/ccan/ccan/rune/test/test_vectors.csv b/ccan/ccan/rune/test/test_vectors.csv index a8411693c605..880ea3c69f3c 100644 --- a/ccan/ccan/rune/test/test_vectors.csv +++ b/ccan/ccan/rune/test/test_vectors.csv @@ -94,6 +94,12 @@ PASS,f1= PASS,f1=/ PASS,f1=11 PASS +VALID,f_with_underscores equals v1,ee979e1f2c376d69923aab0e8e001111963af038bdce394ffd7ecdc9e7020a6e:f_with_underscores=v1,7peeHyw3bWmSOqsOjgAREZY68Di9zjlP_X7NyecCCm5mX3dpdGhfdW5kZXJzY29yZXM9djE= +PASS,f_with_underscores=v1 +FAIL,f_with_underscores=v +FAIL,f_with_underscores=v1a +FAIL +FAIL,f2=f_with_underscores VALID,f1=1 or f2=3,85c3643dc102f0a0d6f20eeb8c294092151688fae41ef7c8ec7272ab23918376:f1=1|f2=3,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM= PASS,f1=1 PASS,f1=1,f2=2 @@ -144,7 +150,6 @@ MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87 MALFORMED,Bad condition [,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1[11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVsxMQ== MALFORMED,Bad condition \,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1\11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVwxMQ== MALFORMED,Bad condition ],76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1]11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV0xMQ== -MALFORMED,Bad condition _,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1_11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV8xMQ== MALFORMED,Bad condition `,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1`11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMWAxMQ== MALFORMED,Bad condition |,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1|11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMXwxMQ== BAD DERIVATION,Incremented sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0e:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw5mMSMxMQ== diff --git a/ccan/ccan/tcon/tcon.h b/ccan/ccan/tcon/tcon.h index e0f84b383976..df3aac88b785 100644 --- a/ccan/ccan/tcon/tcon.h +++ b/ccan/ccan/tcon/tcon.h @@ -147,8 +147,7 @@ * It evaluates to @x so you can chain it. */ #define tcon_check_ptr(x, canary, expr) \ - (sizeof(&(x)->_tcon[0].canary == (expr)) ? (x) : (x)) - + (sizeof((expr) ? (expr) : &(x)->_tcon[0].canary) ? (x) : (x)) /** * tcon_type - the type within a container (or void *) diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 722a6f692883..7d8f6b095de4 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -471,7 +471,7 @@ static const struct test base_tests[] = { "#include \n" "#include \n" "static int worked = 0;\n" - "static char stack[1024];\n" + "static char stack[8192];\n" "static ucontext_t a, b;\n" "static void fn(void *p, void *q) {\n" " void *cp = &worked;\n" diff --git a/ccan_compat.h b/ccan_compat.h index 565327eafdc5..8400d76d1987 100644 --- a/ccan_compat.h +++ b/ccan_compat.h @@ -2,7 +2,8 @@ #define LIGHTNING_CCAN_COMPAT_H /* Magical file included from config.h (ie. everywhere) which renames - * sha256 routines so they don't clash with libwally-core's internal ones */ + * sha256 and ripemd160 routines so they don't clash with libwally-core's + * internal ones */ /* So, for obvious reasons, this is an exception to the usual rule that we #include "config.h" @@ -21,4 +22,19 @@ #define sha256_be16(ctx, v) ccan_sha256_be16(ctx, v) #define sha256_be32(ctx, v) ccan_sha256_be32(ctx, v) #define sha256_be64(ctx, v) ccan_sha256_be64(ctx, v) + +#define ripemd160(sha, p, size) ccan_ripemd160(sha, p, size) +#define ripemd160_init(ctx) ccan_ripemd160_init(ctx) +#define ripemd160_update(ctx, p, size) ccan_ripemd160_update(ctx, p, size) +#define ripemd160_done(ripemd160, res) ccan_ripemd160_done(ripemd160, res) +#define ripemd160_u8(ctx, v) ccan_ripemd160_u8(ctx, v) +#define ripemd160_u16(ctx, v) ccan_ripemd160_u16(ctx, v) +#define ripemd160_u32(ctx, v) ccan_ripemd160_u32(ctx, v) +#define ripemd160_u64(ctx, v) ccan_ripemd160_u64(ctx, v) +#define ripemd160_le16(ctx, v) ccan_ripemd160_le16(ctx, v) +#define ripemd160_le32(ctx, v) ccan_ripemd160_le32(ctx, v) +#define ripemd160_le64(ctx, v) ccan_ripemd160_le64(ctx, v) +#define ripemd160_be16(ctx, v) ccan_ripemd160_be16(ctx, v) +#define ripemd160_be32(ctx, v) ccan_ripemd160_be32(ctx, v) +#define ripemd160_be64(ctx, v) ccan_ripemd160_be64(ctx, v) #endif /* LIGHTNING_CCAN_COMPAT_H */ diff --git a/channeld/Makefile b/channeld/Makefile index 5e98db2f0147..894929b3ed01 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -13,6 +13,8 @@ CHANNELD_HEADERS := \ CHANNELD_SRC := channeld/channeld.c \ channeld/commit_tx.c \ channeld/full_channel.c \ + channeld/splice.c \ + channeld/inflight.c \ channeld/channeld_wiregen.c \ channeld/watchtower.c @@ -25,8 +27,13 @@ ALL_C_HEADERS += $(CHANNELD_HEADERS) ALL_PROGRAMS += lightningd/lightning_channeld # Here's what lightningd depends on -LIGHTNINGD_CONTROL_HEADERS += channeld/channeld_wiregen.h -LIGHTNINGD_CONTROL_OBJS += channeld/channeld_wiregen.o +LIGHTNINGD_CONTROL_HEADERS += \ + channeld/channeld_wiregen.h \ + channeld/inflight.h +LIGHTNINGD_CONTROL_OBJS += \ + channeld/channeld_wiregen.o \ + channeld/inflight.o + # Common source we use. CHANNELD_COMMON_OBJS := \ @@ -53,6 +60,7 @@ CHANNELD_COMMON_OBJS := \ common/status_wiregen.o \ common/gossip_store.o \ common/hmac.o \ + common/interactivetx.o \ common/htlc_state.o \ common/htlc_trim.o \ common/htlc_tx.o \ @@ -73,6 +81,7 @@ CHANNELD_COMMON_OBJS := \ common/ping.o \ common/psbt_keypath.o \ common/psbt_open.o \ + common/psbt_internal.o \ common/private_channel_announcement.o \ common/pseudorand.o \ common/read_peer_msg.o \ diff --git a/channeld/channeld.c b/channeld/channeld.c index a1bd75c11113..a9cf6329946c 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -11,6 +11,7 @@ * limits, unlikely as that is. */ #include "config.h" +#include #include #include #include @@ -18,10 +19,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -31,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -42,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +57,10 @@ #define MASTER_FD STDIN_FILENO #define HSM_FD 4 +#define VALID_STFU_MESSAGE(msg) \ + ((msg) == WIRE_SPLICE || \ + (msg) == WIRE_SPLICE_ACK) + struct peer { struct per_peer_state *pps; bool channel_ready[NUM_SIDES]; @@ -112,6 +123,7 @@ struct peer { secp256k1_ecdsa_signature announcement_node_sigs[NUM_SIDES]; secp256k1_ecdsa_signature announcement_bitcoin_sigs[NUM_SIDES]; bool have_sigs[NUM_SIDES]; + bool send_duplicate_announce_sigs; /* Which direction of the channel do we control? */ u16 channel_direction; @@ -139,16 +151,23 @@ struct peer { /* If master told us to send wrong_funding */ struct bitcoin_outpoint *shutdown_wrong_funding; -#if EXPERIMENTAL_FEATURES - /* Do we want quiescence? */ - bool stfu; + /* Do we want quiescence? + * Note: This flag is needed seperately from `stfu_sent` so we can + * detect the entering "stfu" mode. */ + bool want_stfu; /* Which side is considered the initiator? */ enum side stfu_initiator; /* Has stfu been sent by each side? */ bool stfu_sent[NUM_SIDES]; + /* After STFU mode is enabled, wait for a signle message flag */ + bool stfu_wait_single_msg; /* Updates master asked, which we've deferred while quiescing */ struct msg_queue *update_queue; -#endif + /* Callback for when when stfu is negotiated successfully */ + void (*on_stfu_success)(struct peer*); + + struct splice_state *splice_state; + struct splice *splice; #if DEVELOPER /* If set, don't fire commit counter when this hits 0 */ @@ -196,6 +215,9 @@ struct peer { /* Most recent channel_update message. */ u8 *channel_update; + + /* --experimental-upgrade-protocol */ + bool experimental_upgrade; }; static u8 *create_channel_announcement(const tal_t *ctx, struct peer *peer); @@ -227,13 +249,32 @@ const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) return msg; } -#if EXPERIMENTAL_FEATURES +static bool is_stfu_active(const struct peer *peer) +{ + return peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]; +} + +static void end_stfu_mode(struct peer *peer) +{ + peer->want_stfu = false; + peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; + peer->stfu_wait_single_msg = false; + peer->on_stfu_success = NULL; + + status_debug("Left STFU mode."); +} + static void maybe_send_stfu(struct peer *peer) { - if (!peer->stfu) + if (!peer->want_stfu) return; - if (!peer->stfu_sent[LOCAL] && !pending_updates(peer->channel, LOCAL, false)) { + if (pending_updates(peer->channel, LOCAL, false)) { + status_info("Pending updates prevent us from STFU mode at this" + " time."); + } + else if (!peer->stfu_sent[LOCAL]) { + status_debug("Sending peer that we want to STFU."); u8 *msg = towire_stfu(NULL, &peer->channel_id, peer->stfu_initiator == LOCAL); peer_write(peer->pps, take(msg)); @@ -241,9 +282,21 @@ static void maybe_send_stfu(struct peer *peer) } if (peer->stfu_sent[LOCAL] && peer->stfu_sent[REMOTE]) { + /* Prevent STFU mode being inadvertantly activated twice during + * splice. This occurs because the commit -> revoke_and_ack + * cycle calls into `maybe_send_stfu`. The `want_stfu` flag is + * to prevent triggering the entering of stfu events twice. */ + peer->want_stfu = false; status_unusual("STFU complete: we are quiescent"); wire_sync_write(MASTER_FD, towire_channeld_dev_quiesce_reply(tmpctx)); + + peer->stfu_wait_single_msg = true; + status_unusual("STFU complete: setting stfu_wait_single_msg = true"); + if (peer->on_stfu_success) { + peer->on_stfu_success(peer); + peer->on_stfu_success = NULL; + } } } @@ -252,6 +305,12 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) struct channel_id channel_id; u8 remote_initiated; + if (!feature_negotiated(peer->our_features, + peer->their_features, + OPT_QUIESCE)) + peer_failed_warn(peer->pps, &peer->channel_id, + "stfu not supported"); + if (!fromwire_stfu(stfu, &channel_id, &remote_initiated)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad stfu %s", tal_hex(peer, stfu)); @@ -270,13 +329,15 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) peer_failed_warn(peer->pps, &peer->channel_id, "STFU but you still have updates pending?"); - if (!peer->stfu) { - peer->stfu = true; + if (!peer->want_stfu) { + peer->want_stfu = true; if (!remote_initiated) peer_failed_warn(peer->pps, &peer->channel_id, "Unsolicited STFU but you said" " you didn't initiate?"); peer->stfu_initiator = REMOTE; + + status_debug("STFU initiator was remote."); } else { /* BOLT-quiescent #2: * @@ -285,8 +346,13 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) * arbitrarily considered to be the channel funder (the sender * of `open_channel`). */ - if (remote_initiated) + if (remote_initiated) { + status_debug("Dual STFU intiation tiebreaker. Setting initiator to %s", + peer->channel->opener == LOCAL ? "LOCAL" : "REMOTE"); peer->stfu_initiator = peer->channel->opener; + } else { + status_debug("STFU initiator local."); + } } /* BOLT-quiescent #2: @@ -305,7 +371,7 @@ static void handle_stfu(struct peer *peer, const u8 *stfu) /* Returns true if we queued this for later handling (steals if true) */ static bool handle_master_request_later(struct peer *peer, const u8 *msg) { - if (peer->stfu) { + if (is_stfu_active(peer)) { msg_enqueue(peer->update_queue, take(msg)); return true; } @@ -341,22 +407,14 @@ static void set_channel_type(struct channel *channel, const u8 *type) wire_sync_write(MASTER_FD, take(towire_channeld_upgraded(NULL, channel->type))); } -#else /* !EXPERIMENTAL_FEATURES */ -static bool handle_master_request_later(struct peer *peer, const u8 *msg) -{ - return false; -} - -static void maybe_send_stfu(struct peer *peer) -{ -} -#endif /* Tell gossipd to create channel_update (then it goes into * gossip_store, then streams out to peers, or sends it directly if * it's a private channel) */ static void send_channel_update(struct peer *peer, int disable_flag) { + status_debug("send_channel_update %d", disable_flag); + u8 *msg; assert(disable_flag == 0 || disable_flag == ROUTING_FLAGS_DISABLED); @@ -533,15 +591,22 @@ static void announce_channel(struct peer *peer) send_channel_update(peer, 0); } -static void channel_announcement_negotiate(struct peer *peer) +/* Returns true if an announcement was sent */ +static bool channel_announcement_negotiate(struct peer *peer) { + bool sent_announcement = false; + /* Don't do any announcement work if we're shutting down */ if (peer->shutdown_sent[LOCAL]) - return; + return false; /* Can't do anything until funding is locked. */ if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) - return; + return false; + + /* Don't announce channel if we're in stfu mode */ + if (peer->want_stfu || is_stfu_active(peer)) + return false; if (!peer->channel_local_active) { peer->channel_local_active = true; @@ -567,7 +632,7 @@ static void channel_announcement_negotiate(struct peer *peer) * - MUST NOT send the `announcement_signatures` message. */ if (!(peer->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)) - return; + return false; /* BOLT #7: * @@ -582,6 +647,7 @@ static void channel_announcement_negotiate(struct peer *peer) send_announcement_signatures(peer); peer->have_sigs[LOCAL] = true; billboard_update(peer); + sent_announcement = true; } /* If we've completed the signature exchange, we can send a real @@ -601,12 +667,131 @@ static void channel_announcement_negotiate(struct peer *peer) time_from_sec(GOSSIP_ANNOUNCE_DELAY(peer->dev_fast_gossip)), announce_channel, peer)); } + + return sent_announcement; +} + +/* Call this method when splice_locked status are changed. If both sides have + * splice_locked'ed than this function consumes the `splice_locked_ready` values + * and considers the channel funding to be switched to the splice tx. */ +static void check_mutual_splice_locked(struct peer *peer) +{ + u8 *msg; + const char *error; + struct inflight *inflight; + + /* If both sides haven't `splice_locked` we're not ready */ + if (!peer->splice_state->locked_ready[LOCAL] + || !peer->splice_state->locked_ready[REMOTE]) + return; + + if (short_channel_id_eq(&peer->short_channel_ids[LOCAL], + &peer->splice_state->short_channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Duplicate splice_locked events detected"); + + peer->splice_state->await_commitment_succcess = true; + + /* This splice_locked event is used, so reset the flags to false */ + peer->splice_state->locked_ready[LOCAL] = false; + peer->splice_state->locked_ready[REMOTE] = false; + + peer->have_sigs[LOCAL] = false; + peer->have_sigs[REMOTE] = false; + peer->send_duplicate_announce_sigs = true; + + peer->splice_state->last_short_channel_id = peer->short_channel_ids[LOCAL]; + peer->short_channel_ids[LOCAL] = peer->splice_state->short_channel_id; + peer->short_channel_ids[REMOTE] = peer->splice_state->short_channel_id; + + peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL] = 0; + peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE] = 0; + peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL] = 0; + peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE] = 0; + + status_debug("mutual splice_locked, scid LOCAL & REMOTE updated to: %s", + type_to_string(tmpctx, struct short_channel_id, + &peer->splice_state->short_channel_id)); + + inflight = NULL; + for (size_t i = 0; i < tal_count(peer->splice_state->inflights); i++) + if (bitcoin_txid_eq(&peer->splice_state->inflights[i]->outpoint.txid, + &peer->splice_state->locked_txid)) + inflight = peer->splice_state->inflights[i]; + + if (!inflight) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to find inflight txid amoung %zu" + " inflights. new funding txid: %s", + tal_count(peer->splice_state->inflights), + type_to_string(tmpctx, struct bitcoin_txid, + &peer->splice_state->locked_txid)); + + status_debug("mutual splice_locked, updating change from: %s", + type_to_string(tmpctx, struct channel, peer->channel)); + + error = channel_update_funding(peer->channel, &inflight->outpoint, + inflight->amnt, + inflight->splice_amnt); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice lock unable to update funding. %s", + error); + + status_debug("mutual splice_locked, channel updated to: %s", + type_to_string(tmpctx, struct channel, peer->channel)); + + msg = towire_channeld_got_splice_locked(NULL, inflight->amnt, + inflight->splice_amnt, + &inflight->outpoint.txid); + wire_sync_write(MASTER_FD, take(msg)); + + channel_announcement_negotiate(peer); + billboard_update(peer); + send_channel_update(peer, 0); + + /* valgrind complains last_tx is leaked so we explicitly free it + * DTODO: investigate this more. */ + for (size_t i = 0; i < tal_count(peer->splice_state->inflights); i++) { + inflight = peer->splice_state->inflights[i]; + tal_free(inflight->last_tx); + tal_free(inflight); + } + + peer->splice_state->inflights = tal_free(peer->splice_state->inflights); + peer->splice_state->count = 0; + peer->splice_state->revoked_count = 0; + peer->splice_state->committed_count = 0; +} + +/* Our peer told us they saw our splice confirm on chain with `splice_locked`. + * If we see it to we jump into tansitioning to post-splice, otherwise we mark + * a flag and wait until we see it on chain too. */ +static void handle_peer_splice_locked(struct peer *peer, const u8 *msg) +{ + struct channel_id chanid; + + if (!fromwire_splice_locked(msg, &chanid)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice_locked %s", tal_hex(msg, msg)); + + if (!channel_id_eq(&chanid, &peer->channel_id)) + peer_failed_err(peer->pps, &chanid, + "Wrong splice lock channel id in %s " + "(expected %s)", + tal_hex(tmpctx, msg), + type_to_string(msg, struct channel_id, + &peer->channel_id)); + + peer->splice_state->locked_ready[REMOTE] = true; + check_mutual_splice_locked(peer); } static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) { struct channel_id chanid; struct tlv_channel_ready_tlvs *tlvs; + /* BOLT #2: * * A node: @@ -649,21 +834,55 @@ static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) channel_announcement_negotiate(peer); billboard_update(peer); + peer->send_duplicate_announce_sigs = true; +} + +/* Checks that key is valid, and signed this hash + * + * FIXME: move this inside common/utils.h */ +static bool check_signed_hash_nodeid(const struct sha256_double *hash, + const secp256k1_ecdsa_signature *signature, + const struct node_id *id) +{ + struct pubkey key; + + return pubkey_from_node_id(&key, id) + && check_signed_hash(hash, signature, &key); } static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg) { + const u8 *cannounce; struct channel_id chanid; + struct sha256_double hash; + struct short_channel_id remote_scid; if (!fromwire_announcement_signatures(msg, &chanid, - &peer->short_channel_ids[REMOTE], + &remote_scid, &peer->announcement_node_sigs[REMOTE], &peer->announcement_bitcoin_sigs[REMOTE])) peer_failed_warn(peer->pps, &peer->channel_id, "Bad announcement_signatures %s", tal_hex(msg, msg)); + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * Once a node has received and sent `splice_locked`: + * - Until sending OR receiving of `revoke_and_ack` + * - MUST ignore `announcement_signatures` messages where + * `short_channel_id` matches the pre-splice short channel id. */ + if (peer->splice_state->await_commitment_succcess + && !short_channel_id_eq(&remote_scid, + &peer->short_channel_ids[LOCAL])) + status_info("Ignoring stale announcement_signatures: expected" + " %s, got %s", + type_to_string(tmpctx, struct short_channel_id, + &peer->short_channel_ids[REMOTE]), + type_to_string(tmpctx, struct short_channel_id, + &peer->short_channel_ids[LOCAL])); + + peer->short_channel_ids[REMOTE] = remote_scid; + /* Make sure we agree on the channel ids */ if (!channel_id_eq(&chanid, &peer->channel_id)) { peer_failed_err(peer->pps, &chanid, @@ -673,10 +892,53 @@ static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg type_to_string(tmpctx, struct channel_id, &chanid)); } + /* BOLT 7: + * - if the node_signature OR the bitcoin_signature is NOT correct: + * - MAY send a warning and close the connection, or send an error and fail the channel. + * + * In our case, we send an error and stop the open channel procedure. This approach is + * considered overly strict since the peer can recover from it. However, this step is + * optional. If the peer sends it, we assume that the signature must be correct.*/ + cannounce = create_channel_announcement(tmpctx, peer); + + /* 2 byte msg type + 256 byte signatures */ + int offset = 258; + sha256_double(&hash, cannounce + offset, + tal_count(cannounce) - offset); + + if (!check_signed_hash_nodeid(&hash, &peer->announcement_node_sigs[REMOTE], &peer->node_ids[REMOTE])) { + peer_failed_warn(peer->pps, &chanid, + "Bad node_signature %s hash %s" + " on announcement_signatures %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + &peer->announcement_node_sigs[REMOTE]), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, cannounce)); + } + if (!check_signed_hash(&hash, &peer->announcement_bitcoin_sigs[REMOTE], &peer->channel->funding_pubkey[REMOTE])) { + peer_failed_warn(peer->pps, &chanid, + "Bad bitcoin_signature %s hash %s" + " on announcement_signatures %s", + type_to_string(tmpctx, + secp256k1_ecdsa_signature, + &peer->announcement_bitcoin_sigs[REMOTE]), + type_to_string(tmpctx, + struct sha256_double, + &hash), + tal_hex(tmpctx, cannounce)); + } peer->have_sigs[REMOTE] = true; billboard_update(peer); - channel_announcement_negotiate(peer); + if (!channel_announcement_negotiate(peer) + && peer->send_duplicate_announce_sigs + && peer->have_sigs[LOCAL]) { + peer->send_duplicate_announce_sigs = false; + send_announcement_signatures(peer); + } } static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) @@ -689,20 +951,20 @@ static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) u8 onion_routing_packet[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)]; enum channel_add_err add_err; struct htlc *htlc; - struct tlv_update_add_tlvs *tlvs; + struct tlv_update_add_htlc_tlvs *tlvs; if (!fromwire_update_add_htlc(msg, msg, &channel_id, &id, &amount, &payment_hash, &cltv_expiry, onion_routing_packet, &tlvs) /* This is an *even* field: don't send if we didn't understand */ - || (tlvs->blinding && !feature_offered(peer->our_features->bits[INIT_FEATURE], - OPT_ROUTE_BLINDING))) { + || (tlvs->blinding_point && !feature_offered(peer->our_features->bits[INIT_FEATURE], + OPT_ROUTE_BLINDING))) { peer_failed_warn(peer->pps, &peer->channel_id, "Bad peer_add_htlc %s", tal_hex(msg, msg)); } add_err = channel_add_htlc(peer->channel, REMOTE, id, amount, cltv_expiry, &payment_hash, - onion_routing_packet, tlvs->blinding, &htlc, NULL, + onion_routing_packet, tlvs->blinding_point, &htlc, NULL, /* We don't immediately fail incoming htlcs, * instead we wait and fail them after * they've been committed */ @@ -918,6 +1180,9 @@ static void maybe_send_shutdown(struct peer *peer) if (!peer->send_shutdown) return; + /* DTODO: Ensure 'shutdown' rules around splice are followed once those + * rules get settled on spec */ + /* Send a disable channel_update so others don't try to route * over us */ send_channel_update(peer, ROUTING_FLAGS_DISABLED); @@ -974,7 +1239,7 @@ static u8 *master_wait_sync_reply(const tal_t *ctx, reply = wire_sync_read(ctx, MASTER_FD); if (!reply) - status_failed(STATUS_FAIL_INTERNAL_ERROR, + status_failed(STATUS_FAIL_MASTER_IO, "Could not set sync read from master: %s", strerror(errno)); type = fromwire_peektype(reply); @@ -1012,7 +1277,8 @@ static struct simple_htlc **collect_htlcs(const tal_t *ctx, const struct htlc ** return htlcs; } -/* Returns HTLC sigs, sets commit_sig */ +/* Returns HTLC sigs, sets commit_sig. Also used for making commitsigs for each + * splice awaiting on-chain confirmation. */ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, const struct peer *peer, struct bitcoin_tx **txs, @@ -1074,9 +1340,8 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, wscript = bitcoin_tx_output_get_witscript(tmpctx, txs[0], txs[i+1]->wtx->inputs[0].index); msg = towire_hsmd_sign_remote_htlc_tx(NULL, txs[i + 1], wscript, - &peer->remote_per_commit, - channel_has(peer->channel, - OPT_ANCHOR_OUTPUTS)); + &peer->remote_per_commit, + channel_has_anchors(peer->channel)); msg = hsm_req(tmpctx, take(msg)); if (!fromwire_hsmd_sign_tx_reply(msg, &htlc_sigs[i])) @@ -1144,11 +1409,10 @@ static bool want_fee_update(const struct peer *peer, u32 *target) if (peer->channel->opener != LOCAL) return false; -#if EXPERIMENTAL_FEATURES /* No fee update while quiescing! */ - if (peer->stfu) + if (peer->want_stfu || is_stfu_active(peer)) return false; -#endif + current = channel_feerate(peer->channel, REMOTE); /* max is *approximate*: only take it into account if we're @@ -1184,11 +1448,10 @@ static bool want_blockheight_update(const struct peer *peer, u32 *height) if (peer->channel->lease_expiry == 0) return false; -#if EXPERIMENTAL_FEATURES /* No fee update while quiescing! */ - if (peer->stfu) + if (peer->want_stfu || is_stfu_active(peer)) return false; -#endif + /* What's the current blockheight */ last = get_blockheight(peer->channel->blockheight_states, peer->channel->opener, LOCAL); @@ -1208,19 +1471,97 @@ static bool want_blockheight_update(const struct peer *peer, u32 *height) return true; } -static void send_commit(struct peer *peer) +static u8 *send_commit_part(struct peer *peer, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc **changed_htlcs, + bool notify_master, + s64 splice_amnt, + s64 remote_splice_amnt) { u8 *msg; - const struct htlc **changed_htlcs; struct bitcoin_signature commit_sig, *htlc_sigs; struct bitcoin_tx **txs; const u8 *funding_wscript; const struct htlc **htlc_map; struct wally_tx_output *direct_outputs[NUM_SIDES]; struct penalty_base *pbase; + + status_debug("send_commit_part(splice: %d, remote_splice: %d)", + (int)splice_amnt, (int)remote_splice_amnt); + + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); + cs_tlv->splice_info = tal(cs_tlv, struct channel_id); + derive_channel_id(cs_tlv->splice_info, funding); + + txs = channel_splice_txs(tmpctx, funding, funding_sats, &htlc_map, + direct_outputs, &funding_wscript, + peer->channel, &peer->remote_per_commit, + peer->next_index[REMOTE], REMOTE, + splice_amnt, remote_splice_amnt); + htlc_sigs = + calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, + peer->next_index[REMOTE], &commit_sig); + + if (direct_outputs[LOCAL] != NULL) { + pbase = penalty_base_new(tmpctx, peer->next_index[REMOTE], + txs[0], direct_outputs[LOCAL]); + + /* Add the penalty_base to our in-memory list as well, so we + * can find it again later. */ + tal_arr_expand(&peer->pbases, tal_steal(peer, pbase)); + } else + pbase = NULL; + +#if DEVELOPER + if (peer->dev_disable_commit) { + (*peer->dev_disable_commit)--; + if (*peer->dev_disable_commit == 0) + status_unusual("dev-disable-commit-after: disabling"); + } +#endif + + if (notify_master) { + status_debug("Telling master we're about to commit..."); + /* Tell master to save this next commit to database, then wait. + */ + msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], + pbase, + peer->channel->fee_states, + peer->channel->blockheight_states, + changed_htlcs, + &commit_sig, + htlc_sigs); + /* Message is empty; receiving it is the point. */ + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SENDING_COMMITSIG_REPLY); + + status_debug("Sending commit_sig with %zu htlc sigs", + tal_count(htlc_sigs)); + } + + msg = towire_commitment_signed(NULL, &peer->channel_id, + &commit_sig.s, + raw_sigs(tmpctx, htlc_sigs), + cs_tlv); + return msg; +} + +/* unlike amount.h, we expect negative values for a - b. */ +static s64 sats_diff(struct amount_sat a, struct amount_sat b) +{ + return (s64)a.satoshis - (s64)b.satoshis; /* Raw: splicing numbers can wrap! */ +} + +static void send_commit(struct peer *peer) +{ + const struct htlc **changed_htlcs; u32 our_blockheight; u32 feerate_target; - + u8 **msgs = tal_arr(tmpctx, u8*, 1); + u8 *msg; #if DEVELOPER if (peer->dev_disable_commit && !*peer->dev_disable_commit) { peer->commit_timer = NULL; @@ -1314,7 +1655,10 @@ static void send_commit(struct peer *peer) * any updates. */ changed_htlcs = tal_arr(tmpctx, const struct htlc *, 0); - if (!channel_sending_commit(peer->channel, &changed_htlcs)) { + + if (peer->splice_state->committed_count == peer->splice_state->count + && !channel_sending_commit(peer->channel, &changed_htlcs)) { + status_debug("Can't send commit: nothing to send," " feechange %s (%s)" " blockheight %s (%s)", @@ -1330,54 +1674,40 @@ static void send_commit(struct peer *peer) return; } - txs = channel_txs(tmpctx, &htlc_map, direct_outputs, - &funding_wscript, peer->channel, &peer->remote_per_commit, - peer->next_index[REMOTE], REMOTE); - - htlc_sigs = - calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, - peer->next_index[REMOTE], &commit_sig); - - if (direct_outputs[LOCAL] != NULL) { - pbase = penalty_base_new(tmpctx, peer->next_index[REMOTE], - txs[0], direct_outputs[LOCAL]); - - /* Add the penalty_base to our in-memory list as well, so we - * can find it again later. */ - tal_arr_expand(&peer->pbases, tal_steal(peer, pbase)); - } else - pbase = NULL; + msgs[0] = send_commit_part(peer, &peer->channel->funding, + peer->channel->funding_sats, changed_htlcs, + true, 0, 0); -#if DEVELOPER - if (peer->dev_disable_commit) { - (*peer->dev_disable_commit)--; - if (*peer->dev_disable_commit == 0) - status_unusual("dev-disable-commit-after: disabling"); + /* Loop over current inflights + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * + * A sending node: + *... + * - MUST first send a `commitment_signed` for the active channel then immediately + * send a `commitment_signed` for each splice awaiting confirmation, in increasing + * feerate order. + */ + for (u32 i = 0; i < tal_count(peer->splice_state->inflights); i++) { + s64 funding_diff = sats_diff(peer->splice_state->inflights[i]->amnt, + peer->channel->funding_sats); + s64 remote_splice_amnt = funding_diff + - peer->splice_state->inflights[i]->splice_amnt; + + tal_arr_expand(&msgs, + send_commit_part(peer, + &peer->splice_state->inflights[i]->outpoint, + peer->splice_state->inflights[i]->amnt, + changed_htlcs, false, + peer->splice_state->inflights[i]->splice_amnt, + remote_splice_amnt)); } -#endif - - status_debug("Telling master we're about to commit..."); - /* Tell master to save this next commit to database, then wait. */ - msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], - pbase, - peer->channel->fee_states, - peer->channel->blockheight_states, - changed_htlcs, - &commit_sig, - htlc_sigs); - /* Message is empty; receiving it is the point. */ - master_wait_sync_reply(tmpctx, peer, take(msg), - WIRE_CHANNELD_SENDING_COMMITSIG_REPLY); - - status_debug("Sending commit_sig with %zu htlc sigs", - tal_count(htlc_sigs)); peer->next_index[REMOTE]++; - msg = towire_commitment_signed(NULL, &peer->channel_id, - &commit_sig.s, - raw_sigs(tmpctx, htlc_sigs)); - peer_write(peer->pps, take(msg)); + for(u32 i = 0; i < tal_count(msgs); i++) + peer_write(peer->pps, take(msgs[i])); + + peer->splice_state->committed_count = peer->splice_state->count; maybe_send_shutdown(peer); @@ -1504,7 +1834,8 @@ static void send_revocation(struct peer *peer, const struct htlc **changed_htlcs, const struct bitcoin_tx *committx, const struct secret *old_secret, - const struct pubkey *next_point) + const struct pubkey *next_point, + const struct commitsig **splice_commitsigs) { struct changed_htlc *changed; struct fulfilled_htlc *fulfilled; @@ -1550,42 +1881,102 @@ static void send_revocation(struct peer *peer, fulfilled, failed, changed, - committx); + committx, + splice_commitsigs); master_wait_sync_reply(tmpctx, peer, take(msg_for_master), WIRE_CHANNELD_GOT_COMMITSIG_REPLY); + peer->splice_state->await_commitment_succcess = false; + /* Now we can finally send revoke_and_ack to peer */ peer_write(peer->pps, take(msg)); } -static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) +/* Calling `handle_peer_commit_sig` with a `commit_index` of 0 and + * `changed_htlcs` of NULL will process the message, then read & process coming + * consecutive commitment messages equal to the number of inflight splices. + * + * Returns the last commitsig received. When splicing this is the + * newest splice commit sig. */ +static struct commitsig *handle_peer_commit_sig(struct peer *peer, + const u8 *msg, + u32 commit_index, + const struct htlc **changed_htlcs, + s64 splice_amnt, + s64 remote_splice_amnt) { + struct commitsig *result; struct channel_id channel_id; struct bitcoin_signature commit_sig; secp256k1_ecdsa_signature *raw_sigs; struct bitcoin_signature *htlc_sigs; struct pubkey remote_htlckey; struct bitcoin_tx **txs; - const struct htlc **htlc_map, **changed_htlcs; + const struct htlc **htlc_map; const u8 *funding_wscript; size_t i; struct simple_htlc **htlcs; const u8 * msg2; + u8 *splice_msg; + int type; + struct bitcoin_outpoint outpoint; + struct amount_sat funding_sats; + struct channel_id active_id; + const struct commitsig **commitsigs; - changed_htlcs = tal_arr(msg, const struct htlc *, 0); - if (!channel_rcvd_commit(peer->channel, &changed_htlcs)) { - /* BOLT #2: - * - * A sending node: - * - MUST NOT send a `commitment_signed` message that does not - * include any updates. - */ - status_debug("Oh hi LND! Empty commitment at #%"PRIu64, - peer->next_index[LOCAL]); - if (peer->last_empty_commitment == peer->next_index[LOCAL] - 1) - peer_failed_warn(peer->pps, &peer->channel_id, - "commit_sig with no changes (again!)"); - peer->last_empty_commitment = peer->next_index[LOCAL]; + status_debug("handle_peer_commit_sig(splice: %d, remote_splice: %d)", + (int)splice_amnt, (int)remote_splice_amnt); + + struct tlv_commitment_signed_tlvs *cs_tlv + = tlv_commitment_signed_tlvs_new(tmpctx); + if (!fromwire_commitment_signed(tmpctx, msg, + &channel_id, &commit_sig.s, &raw_sigs, + &cs_tlv)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad commit_sig %s", tal_hex(msg, msg)); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * Once a node has received and sent `splice_locked`: + * - Until sending OR receiving of `revoke_and_ack` + * ... + * - MUST ignore `commitment_signed` messages where `splice_channel_id` + * does not match the `channel_id` of the confirmed splice. */ + derive_channel_id(&active_id, &peer->channel->funding); + if (peer->splice_state->await_commitment_succcess + && !tal_count(peer->splice_state->inflights) && cs_tlv && cs_tlv->splice_info) { + if (!channel_id_eq(&active_id, cs_tlv->splice_info)) { + status_info("Ignoring stale commit_sig for channel_id" + " %s, as %s is locked in now.", + type_to_string(tmpctx, struct channel_id, + cs_tlv->splice_info), + type_to_string(tmpctx, struct channel_id, + &active_id)); + return NULL; + } + } + + /* In a race we can get here with a commitsig with too many splices + * attached. In that case we ignore the main commit msg for the old + * funding tx, and for the splice candidates that didnt win. But we must + * listen to the one that is for the winning splice candidate */ + + if (!changed_htlcs) { + changed_htlcs = tal_arr(msg, const struct htlc *, 0); + if (!channel_rcvd_commit(peer->channel, &changed_htlcs) + && peer->splice_state->count == peer->splice_state->revoked_count) { + /* BOLT #2: + * + * A sending node: + * - MUST NOT send a `commitment_signed` message that does not + * include any updates. + */ + status_debug("Oh hi LND! Empty commitment at #%"PRIu64, + peer->next_index[LOCAL]); + if (peer->last_empty_commitment == peer->next_index[LOCAL] - 1) + peer_failed_warn(peer->pps, &peer->channel_id, + "commit_sig with no changes (again!)"); + peer->last_empty_commitment = peer->next_index[LOCAL]; + } } /* We were supposed to check this was affordable as we go. */ @@ -1598,19 +1989,25 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) LOCAL))); } - if (!fromwire_commitment_signed(tmpctx, msg, - &channel_id, &commit_sig.s, &raw_sigs)) - peer_failed_warn(peer->pps, &peer->channel_id, - "Bad commit_sig %s", tal_hex(msg, msg)); /* SIGHASH_ALL is implied. */ commit_sig.sighash_type = SIGHASH_ALL; htlc_sigs = unraw_sigs(tmpctx, raw_sigs, - channel_has(peer->channel, OPT_ANCHOR_OUTPUTS)); + channel_has_anchors(peer->channel)); + + if (commit_index) { + outpoint = peer->splice_state->inflights[commit_index - 1]->outpoint; + funding_sats = peer->splice_state->inflights[commit_index - 1]->amnt; + } + else { + outpoint = peer->channel->funding; + funding_sats = peer->channel->funding_sats; + } - txs = - channel_txs(tmpctx, &htlc_map, NULL, - &funding_wscript, peer->channel, &peer->next_local_per_commit, - peer->next_index[LOCAL], LOCAL); + txs = channel_splice_txs(tmpctx, &outpoint, funding_sats, &htlc_map, + NULL, &funding_wscript, peer->channel, + &peer->next_local_per_commit, + peer->next_index[LOCAL], LOCAL, splice_amnt, + remote_splice_amnt); /* Set the commit_sig on the commitment tx psbt */ if (!psbt_input_set_signature(txs[0]->psbt, 0, @@ -1642,7 +2039,10 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) &peer->channel->funding_pubkey[REMOTE], &commit_sig)) { dump_htlcs(peer->channel, "receiving commit_sig"); peer_failed_warn(peer->pps, &peer->channel_id, - "Bad commit_sig signature %"PRIu64" %s for tx %s wscript %s key %s feerate %u", + "Bad commit_sig signature %"PRIu64" %s for tx" + " %s wscript %s key %s feerate %u. Cur funding" + " %s, splice_info: %s, race_await_commit: %s," + " inflight splice count: %zu", peer->next_index[LOCAL], type_to_string(msg, struct bitcoin_signature, &commit_sig), @@ -1651,7 +2051,15 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) type_to_string(msg, struct pubkey, &peer->channel->funding_pubkey [REMOTE]), - channel_feerate(peer->channel, LOCAL)); + channel_feerate(peer->channel, LOCAL), + type_to_string(tmpctx, struct channel_id, + &active_id), + type_to_string(tmpctx, struct channel_id, + (cs_tlv ? cs_tlv->splice_info + : NULL)), + peer->splice_state->await_commitment_succcess ? "yes" + : "no", + tal_count(peer->splice_state->inflights)); } /* BOLT #2: @@ -1695,6 +2103,19 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) status_debug("Received commit_sig with %zu htlc sigs", tal_count(htlc_sigs)); + /* First pass some common error scenarios for nicer log outputs */ + if (peer->splice_state->count) { + if (!cs_tlv) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad commitment_signed mesage" + " without a splice commit sig" + " section during a splice."); + if (tal_count(peer->splice_state->inflights) != peer->splice_state->count) + peer_failed_warn(peer->pps, &peer->channel_id, + "Internal splice inflight counting " + "error"); + } + /* Validate the counterparty's signatures, returns prior per_commitment_secret. */ htlcs = collect_htlcs(NULL, htlc_map); msg2 = towire_hsmd_validate_commitment_tx(NULL, @@ -1713,16 +2134,62 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) "Reading validate_commitment_tx reply: %s", tal_hex(tmpctx, msg2)); + result = tal(tmpctx, struct commitsig); + result->tx = clone_bitcoin_tx(result, txs[0]); + result->commit_signature = commit_sig; + result->htlc_signatures = htlc_sigs; + + /* Only the parent call continues from here. + * Return for all child calls. */ + if(commit_index) + return result; + + commitsigs = tal_arr(NULL, const struct commitsig*, 0); + /* We expect multiple consequtive commit_sig messages if we have + * inflight splices. Since consequtive is requred, we recurse for + * each expected message, blocking until all are received. */ + for (i = 0; i < tal_count(peer->splice_state->inflights); i++) { + s64 funding_diff = sats_diff(peer->splice_state->inflights[i]->amnt, + peer->channel->funding_sats); + s64 sub_splice_amnt = peer->splice_state->inflights[i]->splice_amnt; + + splice_msg = peer_read(tmpctx, peer->pps); + /* Check type for cleaner failure message */ + type = fromwire_peektype(msg); + if (type != WIRE_COMMITMENT_SIGNED) + peer_failed_err(peer->pps, &peer->channel_id, + "Expected splice related " + "WIRE_COMMITMENT_SIGNED but got %s", + peer_wire_name(type)); + + /* We purposely just store the last commit msg */ + result = handle_peer_commit_sig(peer, splice_msg, i + 1, + changed_htlcs, sub_splice_amnt, + funding_diff - sub_splice_amnt); + tal_arr_expand(&commitsigs, result); + tal_steal(commitsigs, result); + } + + peer->splice_state->revoked_count = peer->splice_state->count; + send_revocation(peer, &commit_sig, htlc_sigs, changed_htlcs, txs[0], - old_secret, &next_point); + old_secret, &next_point, commitsigs); + + tal_steal(tmpctx, result); + tal_free(commitsigs); - /* We may now be quiescent on our side. */ + /* STFU can't be activated during pending updates. + * With updates finish let's handle a potentially queued stfu request. + */ maybe_send_stfu(peer); /* This might have synced the feerates: if so, we may want to * update */ if (want_fee_update(peer, NULL)) start_commit_timer(peer); + + /* We return the last commit commit msg */ + return result; } /* Pops the penalty base for the given commitnum from our internal list. There @@ -1770,6 +2237,7 @@ static u8 *got_revoke_msg(struct peer *peer, u64 revoke_num, pbase = penalty_base_by_commitnum(tmpctx, peer, revoke_num); if (pbase) { + /* DTODO we need penalty tx's per splice candidate */ ptx = penalty_tx_create( NULL, peer->channel, peer->feerate_penalty, peer->final_index, peer->final_ext_key, @@ -1866,7 +2334,11 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) type_to_string(tmpctx, struct pubkey, &peer->old_remote_per_commit)); - /* We may now be quiescent on our side. */ + peer->splice_state->await_commitment_succcess = false; + + /* STFU can't be activated during pending updates. + * With updates finish let's handle a potentially queued stfu request. + */ maybe_send_stfu(peer); start_commit_timer(peer); @@ -1972,9 +2444,11 @@ static void handle_peer_fail_malformed_htlc(struct peer *peer, const u8 *msg) * `error` and fail the channel. */ if (!(failure_code & BADONION)) { - peer_failed_warn(peer->pps, &peer->channel_id, - "Bad update_fail_malformed_htlc failure code %u", - failure_code); + /* But LND (at least, Bitrefill to Blockstream Store) sends this? */ + status_unusual("Bad update_fail_malformed_htlc failure code %u", + failure_code); + /* We require this internally. */ + failure_code |= BADONION; } e = channel_fail_htlc(peer->channel, LOCAL, id, &htlc); @@ -2006,6 +2480,9 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) struct tlv_shutdown_tlvs *tlvs; struct bitcoin_outpoint *wrong_funding; + /* DTODO: Ensure `shutdown` follows new splice related rules once + * completed in the spec */ + /* Disable the channel. */ send_channel_update(peer, ROUTING_FLAGS_DISABLED); @@ -2098,20 +2575,23 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) static void handle_unexpected_tx_sigs(struct peer *peer, const u8 *msg) { - const struct witness_stack **ws; + const struct witness **witnesses; struct channel_id cid; struct bitcoin_txid txid; + struct tlv_txsigs_tlvs *txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx); + /* In a rare case, a v2 peer may re-send a tx_sigs message. * This happens when they've/we've exchanged channel_ready, * but they did not receive our channel_ready. */ if (!fromwire_tx_signatures(tmpctx, msg, &cid, &txid, - cast_const3(struct witness_stack ***, &ws))) + cast_const3(struct witness ***, &witnesses), + &txsig_tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad tx_signatures %s", tal_hex(msg, msg)); - status_info("Unexpected `tx_signatures` from peer. %s", + status_info("Unexpected `tx_signatures` from peer-> %s", peer->tx_sigs_allowed ? "Allowing." : "Failing."); if (!peer->tx_sigs_allowed) @@ -2128,29 +2608,17 @@ static void handle_unexpected_reestablish(struct peer *peer, const u8 *msg) u64 next_revocation_number; struct secret your_last_per_commitment_secret; struct pubkey my_current_per_commitment_point; -#if EXPERIMENTAL_FEATURES struct tlv_channel_reestablish_tlvs *tlvs; -#endif - - if (!fromwire_channel_reestablish -#if EXPERIMENTAL_FEATURES - (tmpctx, msg, &channel_id, - &next_commitment_number, - &next_revocation_number, - &your_last_per_commitment_secret, - &my_current_per_commitment_point, - &tlvs) -#else - (msg, &channel_id, - &next_commitment_number, - &next_revocation_number, - &your_last_per_commitment_secret, - &my_current_per_commitment_point) -#endif - ) + if (!fromwire_channel_reestablish(tmpctx, msg, &channel_id, + &next_commitment_number, + &next_revocation_number, + &your_last_per_commitment_secret, + &my_current_per_commitment_point, + &tlvs)) { peer_failed_warn(peer->pps, &peer->channel_id, "Bad channel_reestablish %s", tal_hex(peer, msg)); + } /* Is it the same as the peer channel ID? */ if (channel_id_eq(&channel_id, &peer->channel_id)) { @@ -2189,28 +2657,1439 @@ static void handle_unexpected_reestablish(struct peer *peer, const u8 *msg) &channel_id)); } -static void peer_in(struct peer *peer, const u8 *msg) +static bool is_initiators_serial(const struct wally_map *unknowns) { - enum peer_wire type = fromwire_peektype(msg); + /* BOLT-f15b6b0feeffc2acd1a8466537810bbb3f824f9f #2: + * The sending node: ... + * - if is the *initiator*: + * - MUST send even `serial_id`s + * - if is the *non-initiator*: + * - MUST send odd `serial_id`s + */ + u64 serial_id; + if (!psbt_get_serial_id(unknowns, &serial_id)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "PSBTs must have serial_ids set"); - if (handle_peer_error(peer->pps, &peer->channel_id, msg)) - return; + return serial_id % 2 == TX_INITIATOR; +} - /* Must get channel_ready before almost anything. */ - if (!peer->channel_ready[REMOTE]) { - if (type != WIRE_CHANNEL_READY - && type != WIRE_SHUTDOWN - /* We expect these for v2 !! */ - && type != WIRE_TX_SIGNATURES - /* lnd sends these early; it's harmless. */ - && type != WIRE_UPDATE_FEE - && type != WIRE_ANNOUNCEMENT_SIGNATURES) { - peer_failed_warn(peer->pps, &peer->channel_id, - "%s (%u) before funding locked", - peer_wire_name(type), type); - } +static void add_amount_to_side(struct peer *peer, + struct amount_msat amounts[NUM_TX_ROLES], + const struct amount_sat amount, + const struct wally_map *unknowns) +{ + enum tx_role role; + + if (amount_sat_zero(amount)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add 0 sat fields to calculation"); + + if(is_initiators_serial(unknowns)) + role = TX_INITIATOR; + else + role = TX_ACCEPTER; + + if (!amount_msat_add_sat(&amounts[role], amounts[role], amount)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add field amount %s to rolling" + " total %s", + fmt_amount_sat(tmpctx, amount), + fmt_amount_msat(tmpctx, amounts[role])); +} + +static bool do_i_sign_first(struct peer *peer, struct wally_psbt *psbt, + enum tx_role our_role) +{ + struct amount_msat in[NUM_TX_ROLES]; + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - MAY send `tx_signatures` first. */ + if (peer->splice->force_sign_first) + return true; + + in[TX_INITIATOR] = AMOUNT_MSAT(0); + in[TX_ACCEPTER] = AMOUNT_MSAT(0); + + for (size_t i = 0; i < psbt->num_inputs; i++) + add_amount_to_side(peer, in, psbt_input_get_amount(psbt, i), + &psbt->inputs[i].unknowns); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - If recipient's sum(tx_add_input.amount) < peer's + * sum(tx_add_input.amount); or if recipient's + * sum(tx_add_input.amount) == peer's sum(tx_add_input.amount) and + * recipient is the `initiator` of the splice: + * - SHOULD send `tx_signatures` first for the splice transaction. */ + if (amount_msat_less(in[TX_ACCEPTER], in[TX_INITIATOR])) + return our_role == TX_ACCEPTER; + + if (amount_msat_less(in[TX_INITIATOR], in[TX_ACCEPTER])) + return our_role == TX_INITIATOR; + + return our_role == TX_INITIATOR; +} + +static struct wally_psbt *next_splice_step(const tal_t *ctx, + struct interactivetx_context *ictx) +{ + /* DTODO: add plugin wrapper for accepter side of splice to add to the + * negotiated splice. */ + if (ictx->our_role == TX_ACCEPTER) + return NULL; + + return ictx->desired_psbt; +} + +static const u8 *peer_expect_msg(const tal_t *ctx, + struct peer *peer, + enum peer_wire expect_type, + enum peer_wire second_allowed_type) +{ + u8 *msg; + enum peer_wire type; + + msg = peer_read(ctx, peer->pps); + type = fromwire_peektype(msg); + if (type != expect_type && type != second_allowed_type) + peer_failed_warn(peer->pps, &peer->channel_id, + "Got incorrect message from peer: %s" + " (should be %s) [%s]", + peer_wire_name(type), + peer_wire_name(expect_type), + sanitize_error(tmpctx, msg, &peer->channel_id)); + + return msg; +} + +/* The question of "who signs splice commitments first" is the same order as the + * splice `tx_signature`s are. This function handles sending & receiving the + * required commitments as part of the splicing process. */ +static struct commitsig *interactive_send_commitments(struct peer *peer, + struct wally_psbt *psbt, + enum tx_role our_role) +{ + struct commitsig *result; + const u8 *msg, *commit_msg; + + commit_msg = NULL; + + if (do_i_sign_first(peer, psbt, our_role)) { + + status_debug("Splice %s: we commit first", + our_role == TX_INITIATOR ? "initiator" : "accepter"); + + send_commit(peer); + + /* If both sides commit simultaneously, that's fine. */ + msg = peer_expect_msg(tmpctx, peer, WIRE_REVOKE_AND_ACK, + WIRE_COMMITMENT_SIGNED); + + /* If commitments happened simultaneously, we'll get a + * commitment signed message, followed by revoke and ack. + */ + if (fromwire_peektype(msg) == WIRE_COMMITMENT_SIGNED) { + commit_msg = msg; + result = handle_peer_commit_sig(peer, commit_msg, 0, + NULL, 0, 0); + + msg = peer_expect_msg(tmpctx, peer, + WIRE_REVOKE_AND_ACK, 0); + } + + handle_peer_revoke_and_ack(peer, msg); + } + + if (!commit_msg) { + commit_msg = peer_expect_msg(tmpctx, peer, + WIRE_COMMITMENT_SIGNED, 0); + result = handle_peer_commit_sig(peer, commit_msg, 0, + NULL, 0, 0); + } + + if (!do_i_sign_first(peer, psbt, our_role)) { + + status_debug("Splice %s: we commit second", + our_role == TX_INITIATOR ? "initiator" : "accepter"); + + send_commit(peer); + + msg = peer_expect_msg(tmpctx, peer, + WIRE_REVOKE_AND_ACK, 0); + + handle_peer_revoke_and_ack(peer, msg); + } + + return result; +} + +static struct wally_psbt_output *find_channel_output(struct peer *peer, + struct wally_psbt *psbt, + u32 *chan_output_index) +{ + const u8 *wit_script; + u8 *scriptpubkey; + + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + scriptpubkey = scriptpubkey_p2wsh(psbt, wit_script); + + for (size_t i = 0; i < psbt->num_outputs; i++) { + if (memeq(psbt->outputs[i].script, + psbt->outputs[i].script_len, + scriptpubkey, + tal_bytelen(scriptpubkey))) { + if (chan_output_index) + *chan_output_index = i; + return &psbt->outputs[i]; + } + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to find channel output"); + return NULL; +} + +static size_t calc_weight(enum tx_role role, const struct wally_psbt *psbt) +{ + size_t weight = 0; + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * ... + * - MUST pay for the common fields. + */ + if (role == TX_INITIATOR) + weight += bitcoin_tx_core_weight(psbt->num_inputs, + psbt->num_outputs); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * Each node: + * - MUST pay for their own added inputs and outputs. + */ + for (size_t i = 0; i < psbt->num_inputs; i++) + if (is_initiators_serial(&psbt->inputs[i].unknowns)) { + if (role == TX_INITIATOR) + weight += psbt_input_get_weight(psbt, i); + } + else + if (role != TX_INITIATOR) + weight += psbt_input_get_weight(psbt, i); + + for (size_t i = 0; i < psbt->num_outputs; i++) + if (is_initiators_serial(&psbt->inputs[i].unknowns)) { + if (role == TX_INITIATOR) + weight += psbt_output_get_weight(psbt, i); + } + else + if (role != TX_INITIATOR) + weight += psbt_output_get_weight(psbt, i); + + return weight; +} + +/* Returns the total channel funding output amount if all checks pass. + * Otherwise, exits via peer_failed_warn. DTODO: Change to `tx_abort`. */ +static struct amount_sat check_balances(struct peer *peer, + enum tx_role our_role, + const struct wally_psbt *psbt, + int chan_output_index, + int chan_input_index) +{ + struct amount_sat min_initiator_fee, min_accepter_fee, + max_initiator_fee, max_accepter_fee, + funding_amount_res; + struct amount_msat funding_amount, + initiator_fee, accepter_fee; + struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES]; + bool opener = our_role == TX_INITIATOR; + u8 *msg; + + in[TX_INITIATOR] = peer->channel->view->owed[opener ? LOCAL : REMOTE]; + in[TX_ACCEPTER] = peer->channel->view->owed[opener ? REMOTE : LOCAL]; + + for (size_t i = 0; i < psbt->num_inputs; i++) + if (i != chan_input_index) + add_amount_to_side(peer, in, + psbt_input_get_amount(psbt, i), + &psbt->inputs[i].unknowns); + + /* The outgoing channel funds start as current funds, will be modified + * by the splice amount later on */ + out[TX_INITIATOR] = peer->channel->view->owed[opener ? LOCAL : REMOTE]; + out[TX_ACCEPTER] = peer->channel->view->owed[opener ? REMOTE : LOCAL]; + + for (size_t i = 0; i < psbt->num_outputs; i++) + if (i != chan_output_index) + add_amount_to_side(peer, out, + psbt_output_get_amount(psbt, i), + &psbt->outputs[i].unknowns); + + /* Calculate total channel output amount */ + if (!amount_msat_add(&funding_amount, + peer->channel->view->owed[LOCAL], + peer->channel->view->owed[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to calculate starting channel amount"); + + /* Tasks: + * Add up total funding_amount + * Check in[TX_INITIATOR] - out[TX_INITIATOR] > opener_relative + * - refactor as in[TX_INITIATOR] > opener_relative + out[TX_INITIATOR] + * - remainder is the fee contribution + * Check in[TX_ACCEPTER] - out[TX_ACCEPTER] > accepter_relative + * - refactor as in[TX_INITIATOR] > opener_relative + out[TX_INITIATOR] + * - remainder is the fee contribution + * + * Check if fee rate is too low anywhere + * Check if fee rate is too high locally + * + * While we're, here, adjust the output counts by splice amount. + */ + + if(peer->splice->opener_relative > 0) { + if (!amount_msat_add_sat(&funding_amount, funding_amount, + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add opener funding"); + if (!amount_msat_add_sat(&out[TX_INITIATOR], out[TX_INITIATOR], + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add opener funding to out amnt."); + } else { + if (!amount_msat_sub_sat(&funding_amount, funding_amount, + amount_sat((u64)-peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub opener funding"); + if (!amount_msat_sub_sat(&out[TX_INITIATOR], out[TX_INITIATOR], + amount_sat((u64)peer->splice->opener_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub opener funding from out amnt."); + } + + if(peer->splice->accepter_relative > 0) { + if (!amount_msat_add_sat(&funding_amount, funding_amount, + amount_sat((u64)peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add accepter funding"); + if (!amount_msat_add_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER], + amount_sat((u64)peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to add accepter funding to out amnt."); + } else { + if (!amount_msat_sub_sat(&funding_amount, funding_amount, + amount_sat((u64)-peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to subtract accepter funding"); + if (!amount_msat_sub_sat(&out[TX_ACCEPTER], out[TX_ACCEPTER], + amount_sat((u64)-peer->splice->accepter_relative))) + peer_failed_warn(peer->pps, &peer->channel_id, + "Unable to sub accepter funding from out amnt."); + } + + if (amount_msat_less(in[TX_INITIATOR], out[TX_INITIATOR])) { + msg = towire_channeld_splice_funding_error(NULL, in[TX_INITIATOR], + out[TX_INITIATOR], + true); + wire_sync_write(MASTER_FD, take(msg)); + peer_failed_warn(peer->pps, &peer->channel_id, + "Initiator funding is less than commited" + " amount. Initiator contributing %s but they" + " committed to %s.", + fmt_amount_msat(tmpctx, in[TX_INITIATOR]), + fmt_amount_msat(tmpctx, out[TX_INITIATOR])); + } + + if (!amount_msat_sub(&initiator_fee, in[TX_INITIATOR], out[TX_INITIATOR])) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "amount_sat_less / amount_sat_sub mismtach"); + + if (amount_msat_less(in[TX_ACCEPTER], out[TX_ACCEPTER])) { + msg = towire_channeld_splice_funding_error(NULL, in[TX_INITIATOR], + out[TX_INITIATOR], + true); + wire_sync_write(MASTER_FD, take(msg)); + peer_failed_warn(peer->pps, &peer->channel_id, + "Accepter funding is less than commited" + " amount. Accepter contributing %s but they" + " committed to %s.", + fmt_amount_msat(tmpctx, in[TX_INITIATOR]), + fmt_amount_msat(tmpctx, out[TX_INITIATOR])); + } + + if (!amount_msat_sub(&accepter_fee, in[TX_ACCEPTER], out[TX_ACCEPTER])) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "amount_sat_less / amount_sat_sub mismtach"); + + min_initiator_fee = amount_tx_fee(peer->splice->feerate_per_kw, + calc_weight(TX_INITIATOR, psbt)); + min_accepter_fee = amount_tx_fee(peer->splice->feerate_per_kw, + calc_weight(TX_ACCEPTER, psbt)); + + /* As a safeguard max feerate is checked (only) locally, if it's + * particularly high we fail and tell the user but allow them to + * override with `splice_force_feerate` */ + max_accepter_fee = amount_tx_fee(peer->feerate_max, + calc_weight(TX_ACCEPTER, psbt)); + max_initiator_fee = amount_tx_fee(peer->feerate_max, + calc_weight(TX_INITIATOR, psbt)); + + /* Check initiator fee */ + if (amount_msat_less_sat(initiator_fee, min_initiator_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, + false); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ + peer_failed_warn(peer->pps, &peer->channel_id, + "%s fee (%s) was too low, must be at least %s", + opener ? "Our" : "Your", + type_to_string(tmpctx, struct amount_msat, + &initiator_fee), + type_to_string(tmpctx, struct amount_sat, + &min_initiator_fee)); + } + if (!peer->splice->force_feerate && opener + && amount_msat_greater_sat(initiator_fee, max_initiator_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, initiator_fee, + true); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ + peer_failed_warn(peer->pps, &peer->channel_id, + "Our own fee (%s) was too high, max without" + " forcing is %s.", + type_to_string(tmpctx, struct amount_msat, + &initiator_fee), + type_to_string(tmpctx, struct amount_sat, + &max_initiator_fee)); + } + /* Check accepter fee */ + if (amount_msat_less_sat(accepter_fee, min_accepter_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, + false); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort`? */ + peer_failed_warn(peer->pps, &peer->channel_id, + "%s fee (%s) was too low, must be at least %s", + opener ? "Your" : "Our", + type_to_string(tmpctx, struct amount_msat, + &accepter_fee), + type_to_string(tmpctx, struct amount_sat, + &min_accepter_fee)); + } + if (!peer->splice->force_feerate && !opener + && amount_msat_greater_sat(accepter_fee, max_accepter_fee)) { + msg = towire_channeld_splice_feerate_error(NULL, accepter_fee, + true); + wire_sync_write(MASTER_FD, take(msg)); + /* DTODO: Swap `peer_failed_warn` out for `tx_abort` */ + peer_failed_warn(peer->pps, &peer->channel_id, + "Our own fee (%s) was too high, max without" + " forcing is %s.", + type_to_string(tmpctx, struct amount_msat, + &accepter_fee), + type_to_string(tmpctx, struct amount_sat, + &max_accepter_fee)); + } + + /* BOLT-??? #2: + * - if either side has added an output other than the new channel + * funding output: + * - MUST fail the negotiation if the balance for that side is less + * than 1% of the total channel capacity. */ + /* DTODO: Spec out reserve requirements for splices!! Lets gooo */ + /* DTODO: If we were at or over the reserve at start of splice, + * then we must ensure the reserve is preserved through splice. + * It should only to 1% of the old balance + * 1: The channel is growing + * --- your balnce was underneath reserve req + * Valid: YES + * 2: The node's balance is shrinking + * --- and it shrinks below the reserve + * Valid: NO + * + * The reserve requirement should only matter if someone is withdrawing + * from. + * + * Node A Node B + * 1000 sat <-> 1000 sat + * reserve: 20sat + * + * Node B desires withdraw 990 sats + * Can I? + * New reserve req = 1010 * 0.01 = 10 (round down from 10.1) + * */ + + if (!amount_msat_to_sat(&funding_amount_res, funding_amount)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "splice error: msat of boths sides should always" + " add up to a full sat."); + + return funding_amount_res; +} + +static u32 find_channel_funding_input(struct wally_psbt *psbt, + struct bitcoin_outpoint *funding) +{ + for (size_t i = 0; i < psbt->num_inputs; i++) { + struct bitcoin_outpoint psbt_outpoint; + wally_psbt_input_get_outpoint(&psbt->inputs[i], &psbt_outpoint); + + if (!bitcoin_outpoint_eq(&psbt_outpoint, funding)) + continue; + + if (funding->n == psbt->inputs[i].index) + return i; + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to find splice funding tx"); + + return UINT_MAX; +} + +static void update_view_from_inflights(struct peer *peer) +{ + struct inflight **inflights = peer->splice_state->inflights; + + for (size_t i = 0; i < tal_count(inflights); i++) { + s64 splice_amnt = inflights[i]->amnt.satoshis; /* Raw: splicing */ + s64 funding_diff = sats_diff(inflights[i]->amnt, peer->channel->funding_sats); + s64 remote_splice_amnt = funding_diff - inflights[i]->splice_amnt; + + if (splice_amnt < peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL]) + peer->channel->view[LOCAL].lowest_splice_amnt[LOCAL] = splice_amnt; + + if (splice_amnt < peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE]) + peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL] = splice_amnt; + + if (remote_splice_amnt < peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE]) + peer->channel->view[LOCAL].lowest_splice_amnt[REMOTE] = remote_splice_amnt; + + if (remote_splice_amnt < peer->channel->view[REMOTE].lowest_splice_amnt[LOCAL]) + peer->channel->view[REMOTE].lowest_splice_amnt[REMOTE] = remote_splice_amnt; + } +} + +/* Called to finish an ongoing splice OR one restart from chanenl_reestablish */ +static void resume_splice_negotiation(struct peer *peer, + struct inflight *inflight, + bool skip_commitments, + enum tx_role our_role) +{ + const u8 *wit_script; + struct channel_id cid; + enum peer_wire type; + struct wally_psbt *current_psbt = inflight->psbt; + struct commitsig *their_commit; + struct witness **inws; + const struct witness **outws; + u8 der[73]; + size_t der_len; + struct bitcoin_signature splice_sig; + struct bitcoin_tx *bitcoin_tx; + u32 splice_funding_index; + const u8 *msg, *sigmsg; + u32 chan_output_index; + struct bitcoin_signature their_sig; + struct pubkey *their_pubkey; + struct bitcoin_tx *final_tx; + u8 **wit_stack; + struct tlv_txsigs_tlvs *txsig_tlvs, *their_txsigs_tlvs; + + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + find_channel_output(peer, current_psbt, &chan_output_index); + + splice_funding_index = find_channel_funding_input(current_psbt, + &peer->channel->funding); + + if (!skip_commitments) { + their_commit = interactive_send_commitments(peer, current_psbt, + our_role); + + inflight->last_tx = tal_steal(inflight, their_commit->tx); + inflight->last_sig = their_commit->commit_signature; + + msg = towire_channeld_update_inflight(NULL, current_psbt, + their_commit->tx, + &their_commit->commit_signature); + wire_sync_write(MASTER_FD, take(msg)); + } + + /* DTODO Validate splice tx takes none of our funds in either: + * 1) channel balance + * 2) other side sneakily adding other outputs we own + */ + + /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: + * Both nodes: + * - MUST sign the transaction using SIGHASH_ALL */ + splice_sig.sighash_type = SIGHASH_ALL; + + bitcoin_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); + + status_info("Splice signing tx: %s", + tal_hex(tmpctx, linearize_tx(tmpctx, bitcoin_tx))); + + msg = towire_hsmd_sign_splice_tx(tmpctx, bitcoin_tx, + &peer->channel->funding_pubkey[REMOTE], + splice_funding_index); + + msg = hsm_req(tmpctx, take(msg)); + if (!fromwire_hsmd_sign_tx_reply(msg, &splice_sig)) + status_failed(STATUS_FAIL_HSM_IO, + "Reading sign_splice_tx reply: %s", + tal_hex(tmpctx, msg)); + + /* Set the splice_sig on the splice funding tx psbt */ + if (!psbt_input_set_signature(current_psbt, splice_funding_index, + &peer->channel->funding_pubkey[LOCAL], + &splice_sig)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to set signature internally " + "funding_index: %d " + "my pubkey: %s " + "my signature: %s " + "psbt: %s", + splice_funding_index, + type_to_string(tmpctx, struct pubkey, &peer->channel->funding_pubkey[LOCAL]), + type_to_string(tmpctx, struct bitcoin_signature, &splice_sig), + type_to_string(tmpctx, struct wally_psbt, current_psbt)); + + + + txsig_tlvs = tlv_txsigs_tlvs_new(tmpctx); + der_len = signature_to_der(der, &splice_sig); + txsig_tlvs->funding_outpoint_sig = tal_dup_arr(tmpctx, u8, der, + der_len, 0); + + outws = psbt_to_witnesses(tmpctx, current_psbt, + our_role, splice_funding_index); + sigmsg = towire_tx_signatures(tmpctx, &peer->channel_id, + &inflight->outpoint.txid, outws, + txsig_tlvs); + + if (do_i_sign_first(peer, current_psbt, our_role)) { + status_debug("Splice: we sign first"); + msg = towire_channeld_update_inflight(NULL, current_psbt, + NULL, NULL); + wire_sync_write(MASTER_FD, take(msg)); + peer_write(peer->pps, sigmsg); + } + + msg = peer_read(tmpctx, peer->pps); + + type = fromwire_peektype(msg); + + if (handle_peer_error(peer->pps, &peer->channel_id, msg)) + return; + + if (type != WIRE_TX_SIGNATURES) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing got incorrect message from peer: %s " + "(should be WIRE_TX_SIGNATURES)", + peer_wire_name(type)); + + their_txsigs_tlvs = tlv_txsigs_tlvs_new(tmpctx); + if (!fromwire_tx_signatures(tmpctx, msg, &cid, &inflight->outpoint.txid, + cast_const3(struct witness ***, &inws), + &their_txsigs_tlvs)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing bad tx_signatures %s", + tal_hex(msg, msg)); + + /* BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * - Upon receipt of `tx_signatures` for the splice transaction: + * - MUST consider splice negotiation complete. + * - MUST consider the connection no longer quiescent. + */ + end_stfu_mode(peer); + + /* BOLT-a8b9f495cac28124c69cc5ee429f9ef2bacb9921 #2: + * Both nodes: + * - MUST sign the transaction using SIGHASH_ALL */ + their_sig.sighash_type = SIGHASH_ALL; + + if (!signature_from_der(their_txsigs_tlvs->funding_outpoint_sig, + tal_count(their_txsigs_tlvs->funding_outpoint_sig), + &their_sig)) { + + peer_failed_warn(peer->pps, &peer->channel_id, + "Splicing bad tx_signatures %s", + tal_hex(msg, msg)); + } + + their_pubkey = &peer->channel->funding_pubkey[REMOTE]; + + /* Set the commit_sig on the commitment tx psbt */ + if (!psbt_input_set_signature(current_psbt, + splice_funding_index, + their_pubkey, + &their_sig)) { + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Unable to set signature internally " + "funding_index: %d " + "pubkey: %s " + "signature: %s " + "psbt: %s", + splice_funding_index, + type_to_string(tmpctx, struct pubkey, their_pubkey), + type_to_string(tmpctx, struct bitcoin_signature, &their_sig), + type_to_string(tmpctx, struct wally_psbt, current_psbt)); + } + + psbt_input_set_witscript(current_psbt, + splice_funding_index, + wit_script); + + if (tal_count(inws) > current_psbt->num_inputs) + peer_failed_warn(peer->pps, &peer->channel_id, + "%lu too many witness elements received", + tal_count(inws) - current_psbt->num_inputs); + + /* We put the PSBT + sigs all together */ + for (size_t j = 0, i = 0; i < current_psbt->num_inputs; i++) { + struct wally_psbt_input *in = + ¤t_psbt->inputs[i]; + u64 in_serial; + + if (!psbt_get_serial_id(&in->unknowns, &in_serial)) { + status_broken("PSBT input %zu missing serial_id %s", + i, type_to_string(tmpctx, + struct wally_psbt, + current_psbt)); + return; + } + if (in_serial % 2 == our_role) + continue; + + if (i == splice_funding_index) + continue; + + if (j == tal_count(inws)) + peer_failed_warn(peer->pps, + &peer->channel_id, + "Mismatch witness stack count %s", + tal_hex(msg, msg)); + + psbt_finalize_input(current_psbt, in, inws[j++]); + } + + final_tx = bitcoin_tx_with_psbt(tmpctx, current_psbt); + + wit_stack = bitcoin_witness_2of2(current_psbt, &splice_sig, &their_sig, + &peer->channel->funding_pubkey[LOCAL], + their_pubkey); + + bitcoin_tx_input_set_witness(final_tx, splice_funding_index, wit_stack); + + /* We let core validate our peer's signatures are correct. */ + + msg = towire_channeld_update_inflight(NULL, current_psbt, NULL, + NULL); + wire_sync_write(MASTER_FD, take(msg)); + + if (!do_i_sign_first(peer, current_psbt, our_role)) { + status_debug("Splice: we sign second"); + peer_write(peer->pps, sigmsg); + } + + peer->splice = tal_free(peer->splice); + + msg = towire_channeld_splice_confirmed_signed(tmpctx, final_tx, chan_output_index); + wire_sync_write(MASTER_FD, take(msg)); + + send_channel_update(peer, 0); +} + +static struct inflight *inflights_new(struct peer *peer) +{ + struct inflight *inf = tal(peer->splice_state->inflights, struct inflight); + int i = tal_count(peer->splice_state->inflights); + + if (i) + tal_resize(&peer->splice_state->inflights, i + 1); + else + peer->splice_state->inflights = tal_arr(peer->splice_state, + struct inflight *, 1); + + peer->splice_state->inflights[i] = inf; + return inf; +} + +/* ACCEPTER side of the splice. Here we handle all the accepter's steps for the + * splice. Since the channel must be in STFU mode we block the daemon here until + * the splice is finished or aborted. */ +static void splice_accepter(struct peer *peer, const u8 *inmsg) +{ + const u8 *msg; + struct interactivetx_context *ictx; + u32 splice_funding_index; + struct bitcoin_blkid genesis_blockhash; + struct channel_id channel_id; + struct amount_sat both_amount; + u32 funding_feerate_perkw; + u32 locktime; + struct pubkey splice_remote_pubkey; + char *error; + struct inflight *new_inflight; + struct wally_psbt_output *new_chan_output; + struct bitcoin_outpoint outpoint; + + /* Can't start a splice with another splice still active */ + assert(!peer->splice); + peer->splice = splice_new(peer); + + ictx = new_interactivetx_context(tmpctx, TX_ACCEPTER, + peer->pps, peer->channel_id); + + if (!fromwire_splice(inmsg, + &channel_id, + &genesis_blockhash, + &peer->splice->opener_relative, + &funding_feerate_perkw, + &locktime, + &splice_remote_pubkey)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad wire_splice %s", tal_hex(tmpctx, inmsg)); + + peer->splice_state->await_commitment_succcess = false; + + if (!is_stfu_active(peer)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Must be in STFU mode before intiating splice"); + + if (!bitcoin_blkid_eq(&genesis_blockhash, &chainparams->genesis_blockhash)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice blockhash"); + + if (!channel_id_eq(&channel_id, &peer->channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice internal error: mismatched channelid"); + + if (!pubkey_eq(&splice_remote_pubkey, + &peer->channel->funding_pubkey[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice doesnt support changing pubkeys"); + + if (funding_feerate_perkw < peer->feerate_min) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice feerate_perkw is too low"); + + /* TODO: Add plugin hook for user to adjust accepter amount */ + peer->splice->accepter_relative = 0; + + msg = towire_splice_ack(NULL, + &peer->channel_id, + &chainparams->genesis_blockhash, + peer->splice->accepter_relative, + &peer->channel->funding_pubkey[LOCAL]); + + peer->splice->mode = true; + + peer_write(peer->pps, take(msg)); + + /* Now we wait for the other side to go first. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The receiver of `splice_ack`: + * - MUST begin splice negotiation. + */ + + ictx->next_update_fn = next_splice_step; + ictx->desired_psbt = NULL; + ictx->pause_when_complete = false; + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_err(peer->pps, &peer->channel_id, + "Interactive splicing error: %s", error); + + assert(ictx->pause_when_complete == false); + peer->splice->sent_tx_complete = true; + + /* DTODO validate locktime */ + ictx->current_psbt->fallback_locktime = locktime; + + splice_funding_index = find_channel_funding_input(ictx->current_psbt, + &peer->channel->funding); + + new_chan_output = find_channel_output(peer, ictx->current_psbt, + &outpoint.n); + + both_amount = check_balances(peer, TX_ACCEPTER, ictx->current_psbt, + outpoint.n, splice_funding_index); + new_chan_output->amount = both_amount.satoshis; /* Raw: type conv */ + + psbt_elements_normalize_fees(ictx->current_psbt); + + psbt_txid(tmpctx, ictx->current_psbt, &outpoint.txid, NULL); + + psbt_finalize(ictx->current_psbt); + + status_debug("Splice accepter adding inflight: %s", psbt_to_b64(tmpctx, ictx->current_psbt)); + + msg = towire_channeld_add_inflight(NULL, + &outpoint.txid, + outpoint.n, + funding_feerate_perkw, + both_amount, + peer->splice->accepter_relative, + ictx->current_psbt, + false); + + master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_GOT_INFLIGHT); + + new_inflight = inflights_new(peer); + + psbt_txid(new_inflight, ictx->current_psbt, + &new_inflight->outpoint.txid, NULL); + new_inflight->outpoint = outpoint; + new_inflight->amnt = both_amount; + new_inflight->psbt = tal_steal(new_inflight, ictx->current_psbt); + new_inflight->splice_amnt = peer->splice->accepter_relative; + new_inflight->last_tx = NULL; + new_inflight->i_am_initiator = false; + + update_view_from_inflights(peer); + + peer->splice_state->count++; + + resume_splice_negotiation(peer, new_inflight, false, TX_ACCEPTER); +} + +static struct bitcoin_tx *bitcoin_tx_from_txid(struct peer *peer, + struct bitcoin_txid txid) +{ + u8 *msg; + struct bitcoin_tx *tx = NULL; + + msg = towire_channeld_splice_lookup_tx(NULL, &txid); + + msg = master_wait_sync_reply(tmpctx, peer, take(msg), + WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT); + + if (!fromwire_channeld_splice_lookup_tx_result(tmpctx, msg, &tx)) + master_badmsg(WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT, msg); + + return tx; +} + +/* splice_initiator runs when splice_ack is received by the other side. It + * handles the initial splice creation while callbacks will handle later + * stages. */ +static void splice_initiator(struct peer *peer, const u8 *inmsg) +{ + struct bitcoin_blkid genesis_blockhash; + struct channel_id channel_id; + struct pubkey splice_remote_pubkey; + size_t input_index; + const u8 *wit_script; + u8 *outmsg; + struct interactivetx_context *ictx; + struct bitcoin_tx *prev_tx; + u32 sequence = 0; + u8 *scriptPubkey; + char *error; + + status_debug("splice_initiator peer->stfu_wait_single_msg: %d", + (int)peer->stfu_wait_single_msg); + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + if (!fromwire_splice_ack(inmsg, + &channel_id, + &genesis_blockhash, + &peer->splice->accepter_relative, + &splice_remote_pubkey)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad wire_splice_ack %s", tal_hex(tmpctx, inmsg)); + + if (!bitcoin_blkid_eq(&genesis_blockhash, &chainparams->genesis_blockhash)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad splice[ACK] blockhash"); + + if (!channel_id_eq(&channel_id, &peer->channel_id)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice[ACK] internal error: mismatched channelid"); + + if (!pubkey_eq(&splice_remote_pubkey, &peer->channel->funding_pubkey[REMOTE])) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice[ACK] doesnt support changing pubkeys"); + + peer->splice->received_tx_complete = false; + peer->splice->sent_tx_complete = false; + peer->splice_state->locked_ready[LOCAL] = false; + peer->splice_state->locked_ready[REMOTE] = false; + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = true; + ictx->desired_psbt = peer->splice->current_psbt; + + /* We go first as the receiver of the ack. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The receiver of `splice_ack`: + * - MUST begin splice negotiation. + */ + BUILD_ASSERT(NUM_SIDES == 2); + wit_script = bitcoin_redeem_2of2(tmpctx, + &peer->channel->funding_pubkey[LOCAL], + &peer->channel->funding_pubkey[REMOTE]); + + input_index = ictx->desired_psbt->num_inputs; + + /* First we spend the existing channel outpoint + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * - MUST `tx_add_input` an input which spends the current funding + * transaction output. + */ + psbt_append_input(ictx->desired_psbt, &peer->channel->funding, sequence, + NULL, wit_script, NULL); + + status_debug("just added funding w/ outpoint index set to %d, value in psbt input 1 is: %d, and value in psbt input 2 is: %d", + (int)peer->channel->funding.n, + (int)ictx->desired_psbt->inputs[0].index, + (int)ictx->desired_psbt->inputs[1].index); + + /* Segwit requires us to store the value of the outpoint being spent, + * so let's do that */ + scriptPubkey = scriptpubkey_p2wsh(ictx->desired_psbt, wit_script); + psbt_input_set_wit_utxo(ictx->desired_psbt, input_index, + scriptPubkey, peer->channel->funding_sats); + + /* We must loading the funding tx as our previous utxo */ + prev_tx = bitcoin_tx_from_txid(peer, peer->channel->funding.txid); + psbt_input_set_utxo(ictx->desired_psbt, input_index, prev_tx->wtx); + + /* PSBT v2 requires this */ + psbt_input_set_outpoint(ictx->desired_psbt, input_index, + peer->channel->funding); + + /* Next we add the new channel outpoint, with a 0 amount for now. It + * will be filled in later. + * + * BOLT-0d8b701614b09c6ee4172b04da2203e73deec7e2 #2: + * The initiator: + * ... + * - MUST `tx_add_output` a zero-value output which pays to the two + * funding keys using the higher of the two `generation` fields. + */ + psbt_append_output(ictx->desired_psbt, + scriptpubkey_p2wsh(ictx->desired_psbt, wit_script), + amount_sat(0)); + + psbt_add_serials(ictx->desired_psbt, ictx->our_role); + + error = process_interactivetx_updates(tmpctx, + ictx, + &peer->splice->received_tx_complete); + + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Interactive splicing_ack error: %s", error); + + peer->splice->tx_add_input_count = ictx->tx_add_input_count; + peer->splice->tx_add_output_count = ictx->tx_add_output_count; + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + + peer->splice->mode = true; + + /* Return the current PSBT to the channel_control to give to user. + */ + outmsg = towire_channeld_splice_confirmed_init(NULL, + ictx->current_psbt); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +/* This occurs when the user has marked they are done making changes to the + * PSBT. Now we continually send `tx_complete` and intake our peer's changes + * inside `process_interactivetx_updates`. Once they are onboard indicated + * with their sending of `tx_complete` we clean up the final PSBT and return + * to the user for their final signing steps. */ +static void splice_initiator_user_finalized(struct peer *peer) +{ + u8 *outmsg; + struct interactivetx_context *ictx; + char *error; + u32 chan_output_index, splice_funding_index; + struct wally_psbt_output *new_chan_output; + struct inflight *new_inflight; + struct bitcoin_txid current_psbt_txid; + struct amount_sat both_amount; + struct commitsig *their_commit; + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = false; + ictx->desired_psbt = ictx->current_psbt = peer->splice->current_psbt; + ictx->tx_add_input_count = peer->splice->tx_add_input_count; + ictx->tx_add_output_count = peer->splice->tx_add_output_count; + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice finalize error: %s", error); + + /* With pause_when_complete fase, this assert should never fail */ + assert(peer->splice->received_tx_complete); + peer->splice->sent_tx_complete = true; + + psbt_sort_by_serial_id(ictx->current_psbt); + + new_chan_output = find_channel_output(peer, ictx->current_psbt, + &chan_output_index); + + splice_funding_index = find_channel_funding_input(ictx->current_psbt, + &peer->channel->funding); + + both_amount = check_balances(peer, TX_INITIATOR, ictx->current_psbt, + chan_output_index, splice_funding_index); + new_chan_output->amount = both_amount.satoshis; /* Raw: type conv */ + + psbt_elements_normalize_fees(ictx->current_psbt); + + status_debug("Splice adding inflight: %s", + psbt_to_b64(tmpctx, ictx->current_psbt)); + + psbt_txid(tmpctx, ictx->current_psbt, ¤t_psbt_txid, NULL); + + outmsg = towire_channeld_add_inflight(tmpctx, + ¤t_psbt_txid, + chan_output_index, + peer->splice->feerate_per_kw, + amount_sat(new_chan_output->amount), + peer->splice->opener_relative, + ictx->current_psbt, + true); + + master_wait_sync_reply(tmpctx, peer, take(outmsg), + WIRE_CHANNELD_GOT_INFLIGHT); + + new_inflight = inflights_new(peer); + + psbt_txid(tmpctx, ictx->current_psbt, &new_inflight->outpoint.txid, NULL); + new_inflight->outpoint.n = chan_output_index; + new_inflight->psbt = tal_steal(new_inflight, ictx->current_psbt); + new_inflight->amnt = amount_sat(new_chan_output->amount); + new_inflight->splice_amnt = peer->splice->opener_relative; + new_inflight->last_tx = NULL; + new_inflight->i_am_initiator = true; + + update_view_from_inflights(peer); + + peer->splice_state->count++; + + their_commit = interactive_send_commitments(peer, ictx->current_psbt, + TX_INITIATOR); + + new_inflight->last_tx = tal_steal(peer, their_commit->tx); + new_inflight->last_sig = their_commit->commit_signature; + + outmsg = towire_channeld_update_inflight(NULL, ictx->current_psbt, + their_commit->tx, + &their_commit->commit_signature); + wire_sync_write(MASTER_FD, take(outmsg)); + + status_debug("user_finalized peer->stfu_wait_single_msg: %d", (int)peer->stfu_wait_single_msg); + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + outmsg = towire_channeld_splice_confirmed_update(NULL, + ictx->current_psbt, + true); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +/* During a splice the user may call splice_update mulitple times adding + * new details to the active PSBT. Each user call enters here: */ +static void splice_initiator_user_update(struct peer *peer, const u8 *inmsg) +{ + u8 *outmsg, *msg; + struct interactivetx_context *ictx; + char *error; + + if (!peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't accept a" + " splice PSBT update" + " because this channel" + " hasn't begun a" + " splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + ictx = new_interactivetx_context(tmpctx, TX_INITIATOR, + peer->pps, peer->channel_id); + + if (!fromwire_channeld_splice_update(ictx, inmsg, &ictx->desired_psbt)) + master_badmsg(WIRE_CHANNELD_SPLICE_UPDATE, inmsg); + + if (!peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't update a" + " splice when not in" + " splice mode."); + wire_sync_write(MASTER_FD, take(msg)); + return; + + } + + ictx->next_update_fn = next_splice_step; + ictx->pause_when_complete = true; + + /* Should already have a current_psbt from a previously initiated one */ + assert(peer->splice->current_psbt); + ictx->current_psbt = peer->splice->current_psbt; + ictx->tx_add_input_count = peer->splice->tx_add_input_count; + ictx->tx_add_output_count = peer->splice->tx_add_output_count; + + /* User may not have setup serial numbers on their modifeid PSBT, so we + * ensure that for them here */ + psbt_add_serials(ictx->desired_psbt, ictx->our_role); + + status_debug("splice_update start with, current psbt version: %d," + " desired: %d.", ictx->current_psbt->version, + ictx->desired_psbt->version); + + /* If there no are no changes, we consider the splice 'user finalized' */ + if (!interactivetx_has_changes(ictx, ictx->desired_psbt)) { + splice_initiator_user_finalized(peer); + return; + } + + error = process_interactivetx_updates(tmpctx, ictx, + &peer->splice->received_tx_complete); + if (error) + peer_failed_warn(peer->pps, &peer->channel_id, + "Splice update error: %s", error); + + peer->splice->tx_add_input_count = ictx->tx_add_input_count; + peer->splice->tx_add_output_count = ictx->tx_add_output_count; + + if (peer->splice->current_psbt != ictx->current_psbt) + tal_free(peer->splice->current_psbt); + peer->splice->current_psbt = tal_steal(peer->splice, ictx->current_psbt); + + /* Peer may have modified our PSBT so we return it to the user here */ + outmsg = towire_channeld_splice_confirmed_update(NULL, + ictx->current_psbt, + false); + wire_sync_write(MASTER_FD, take(outmsg)); +} + +static struct inflight *last_inflight(struct peer *peer) +{ + size_t count = tal_count(peer->splice_state->inflights); + + if (count) + return peer->splice_state->inflights[count - 1]; + + return NULL; +} + +/* This occurs when the user has signed the final version of the PSBT. At this + * point we do a commitment transaciton round with our peer via + * `interactive_send_commitments`. + * + * Then we finalize the PSBT some more and sign away our funding output, + * place that signature in the PSBT, and pass our signature to the peer and get + * theirs back. */ +static void splice_initiator_user_signed(struct peer *peer, const u8 *inmsg) +{ + struct wally_psbt *signed_psbt; + struct bitcoin_txid current_psbt_txid, signed_psbt_txid; + struct inflight *inflight; + const u8 *msg; + + if (!peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't accept a" + " signed splice PSBT" + " because this channel" + " hasn't begun a" + " splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + if (!fromwire_channeld_splice_signed(tmpctx, inmsg, &signed_psbt, + &peer->splice->force_sign_first)) + master_badmsg(WIRE_CHANNELD_SPLICE_SIGNED, inmsg); + + if (!peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when not in" + " splice mode."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (!peer->splice->received_tx_complete) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when we" + " haven't received" + " tx_complete yet."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (!peer->splice->sent_tx_complete) { + msg = towire_channeld_splice_state_error(NULL, "Can't sign a" + " splice when we" + " haven't sent" + " tx_complete yet."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + psbt_txid(tmpctx, peer->splice->current_psbt, ¤t_psbt_txid, NULL); + psbt_txid(tmpctx, signed_psbt, &signed_psbt_txid, NULL); + + if (!bitcoin_txid_eq(&signed_psbt_txid, ¤t_psbt_txid)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Signed PSBT txid %s does not match" + " current_psbt_txid %s", + type_to_string(tmpctx, struct bitcoin_txid, + &signed_psbt_txid), + type_to_string(tmpctx, struct bitcoin_txid, + ¤t_psbt_txid)); + + peer->splice->current_psbt = tal_free(peer->splice->current_psbt); + + inflight = last_inflight(peer); + inflight->psbt = tal_steal(inflight, signed_psbt); + + resume_splice_negotiation(peer, inflight, true, TX_INITIATOR); +} + +/* This occurs once our 'stfu' transition was successful. */ +static void handle_splice_stfu_success(struct peer *peer) +{ + u8 *msg = towire_splice(tmpctx, + &peer->channel_id, + &chainparams->genesis_blockhash, + peer->splice->opener_relative, + peer->splice->feerate_per_kw, + peer->splice->current_psbt->fallback_locktime, + &peer->channel->funding_pubkey[LOCAL]); + peer->splice_state->await_commitment_succcess = false; + peer_write(peer->pps, take(msg)); +} + +/* User has begun a splice with `splice_init` command. Here we request entry + * into STFU mode, when we get it, send `splice` to our peer-> + * Later the peer will send `splice_ack` and the code that starts the actual + * splice happens at that point in `splice_initiator()`. */ +static void handle_splice_init(struct peer *peer, const u8 *inmsg) +{ + u8 *msg; + + /* Can't start a splice with another splice still active */ + if (peer->splice) { + msg = towire_channeld_splice_state_error(NULL, "Can't start two" + " splices on the same" + " channel at once."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + peer->splice = splice_new(peer); + + if (!fromwire_channeld_splice_init(peer, inmsg, &peer->splice->current_psbt, + &peer->splice->opener_relative, + &peer->splice->feerate_per_kw, + &peer->splice->force_feerate)) + master_badmsg(WIRE_CHANNELD_SPLICE_INIT, inmsg); + + if (peer->want_stfu) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while waiting" + " for STFU."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (is_stfu_active(peer)) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while" + " currently in STFU"); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (peer->splice->mode) { + msg = towire_channeld_splice_state_error(NULL, "Can't begin a" + " splice while already" + " doing a splice."); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + if (peer->splice->feerate_per_kw < peer->feerate_min) { + msg = towire_channeld_splice_state_error(NULL, tal_fmt(tmpctx, + "Feerate %u is too" + " low. Lower than" + " channel feerate_min" + " %u", + peer->splice->feerate_per_kw, + peer->feerate_min)); + wire_sync_write(MASTER_FD, take(msg)); + return; + } + + status_debug("Getting handle_splice_init psbt version %d", peer->splice->current_psbt->version); + + peer->on_stfu_success = handle_splice_stfu_success; + + /* First things first we must STFU the channel */ + peer->stfu_initiator = LOCAL; + peer->want_stfu = true; + maybe_send_stfu(peer); +} + +static void peer_in(struct peer *peer, const u8 *msg) +{ + enum peer_wire type = fromwire_peektype(msg); + + if (handle_peer_error(peer->pps, &peer->channel_id, msg)) + return; + + /* Must get channel_ready before almost anything. */ + if (!peer->channel_ready[REMOTE]) { + if (type != WIRE_CHANNEL_READY + && type != WIRE_SHUTDOWN + /* We expect these for v2 !! */ + && type != WIRE_TX_SIGNATURES + /* lnd sends these early; it's harmless. */ + && type != WIRE_UPDATE_FEE + && type != WIRE_ANNOUNCEMENT_SIGNATURES) { + peer_failed_warn(peer->pps, &peer->channel_id, + "%s (%u) before funding locked", + peer_wire_name(type), type); + } } + /* For cleaner errors, we check message is valid during STFU mode */ + if (peer->stfu_wait_single_msg) + if (!VALID_STFU_MESSAGE(type)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Got invalid message during STFU " + "mode: %s", + peer_wire_name(type)); + + peer->stfu_wait_single_msg = false; + switch (type) { case WIRE_CHANNEL_READY: handle_peer_channel_ready(peer, msg); @@ -2222,7 +4101,7 @@ static void peer_in(struct peer *peer, const u8 *msg) handle_peer_add_htlc(peer, msg); return; case WIRE_COMMITMENT_SIGNED: - handle_peer_commit_sig(peer, msg); + handle_peer_commit_sig(peer, msg, 0, NULL, 0, 0); return; case WIRE_UPDATE_FEE: handle_peer_feechange(peer, msg); @@ -2245,12 +4124,18 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_SHUTDOWN: handle_peer_shutdown(peer, msg); return; - -#if EXPERIMENTAL_FEATURES case WIRE_STFU: handle_stfu(peer, msg); return; -#endif + case WIRE_SPLICE: + splice_accepter(peer, msg); + return; + case WIRE_SPLICE_ACK: + splice_initiator(peer, msg); + return; + case WIRE_SPLICE_LOCKED: + handle_peer_splice_locked(peer, msg); + return; case WIRE_INIT: case WIRE_OPEN_CHANNEL: case WIRE_ACCEPT_CHANNEL: @@ -2416,11 +4301,11 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) last[i].id); if (h->state == SENT_ADD_COMMIT) { - struct tlv_update_add_tlvs *tlvs; + struct tlv_update_add_htlc_tlvs *tlvs; if (h->blinding) { - tlvs = tlv_update_add_tlvs_new(tmpctx); - tlvs->blinding = tal_dup(tlvs, struct pubkey, - h->blinding); + tlvs = tlv_update_add_htlc_tlvs_new(tmpctx); + tlvs->blinding_point = tal_dup(tlvs, struct pubkey, + h->blinding); } else tlvs = NULL; msg = towire_update_add_htlc(NULL, &peer->channel_id, @@ -2453,9 +4338,11 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) htlc_sigs = calc_commitsigs(tmpctx, peer, txs, funding_wscript, htlc_map, peer->next_index[REMOTE]-1, &commit_sig); + msg = towire_commitment_signed(NULL, &peer->channel_id, &commit_sig.s, - raw_sigs(tmpctx, htlc_sigs)); + raw_sigs(tmpctx, htlc_sigs), + NULL); peer_write(peer->pps, take(msg)); /* If we have already received the revocation for the previous, the @@ -2663,7 +4550,6 @@ static bool capture_premature_msg(const u8 ***shit_lnd_says, const u8 *msg) return true; } -#if EXPERIMENTAL_FEATURES /* Unwrap a channel_type into a raw byte array for the wire: can be NULL */ static u8 *to_bytearr(const tal_t *ctx, const struct channel_type *channel_type TAKES) @@ -2683,23 +4569,6 @@ static u8 *to_bytearr(const tal_t *ctx, return ret; } -/* This is the no-tlvs version, where we can't handle old tlvs */ -static bool fromwire_channel_reestablish_notlvs(const void *p, struct channel_id *channel_id, u64 *next_commitment_number, u64 *next_revocation_number, struct secret *your_last_per_commitment_secret, struct pubkey *my_current_per_commitment_point) -{ - const u8 *cursor = p; - size_t plen = tal_count(p); - - if (fromwire_u16(&cursor, &plen) != WIRE_CHANNEL_REESTABLISH) - return false; - fromwire_channel_id(&cursor, &plen, channel_id); - *next_commitment_number = fromwire_u64(&cursor, &plen); - *next_revocation_number = fromwire_u64(&cursor, &plen); - fromwire_secret(&cursor, &plen, your_last_per_commitment_secret); - fromwire_pubkey(&cursor, &plen, my_current_per_commitment_point); - return cursor != NULL; -} -#endif - static void peer_reconnect(struct peer *peer, const struct secret *last_remote_per_commit_secret, bool reestablish_only) @@ -2716,9 +4585,11 @@ static void peer_reconnect(struct peer *peer, struct secret last_local_per_commitment_secret; bool dataloss_protect, check_extra_fields; const u8 **premature_msgs = tal_arr(peer, const u8 *, 0); -#if EXPERIMENTAL_FEATURES + struct inflight *inflight; + bool next_matches_current, next_matches_inflight; + struct bitcoin_txid *local_next_funding, *remote_next_funding; + struct tlv_channel_reestablish_tlvs *send_tlvs, *recv_tlvs; -#endif dataloss_protect = feature_negotiated(peer->our_features, peer->their_features, @@ -2733,53 +4604,51 @@ static void peer_reconnect(struct peer *peer, get_per_commitment_point(peer->next_index[LOCAL] - 1, &my_current_per_commitment_point, NULL); -#if EXPERIMENTAL_FEATURES - /* Subtle: we free tmpctx below as we loop, so tal off peer */ - send_tlvs = tlv_channel_reestablish_tlvs_new(peer); + if (peer->experimental_upgrade) { + /* Subtle: we free tmpctx below as we loop, so tal off peer */ + send_tlvs = tlv_channel_reestablish_tlvs_new(peer); - /* FIXME: v0.10.1 would send a different tlv set, due to older spec. - * That did *not* offer OPT_QUIESCE, so in that case don't send tlvs. */ - if (!feature_negotiated(peer->our_features, - peer->their_features, - OPT_QUIESCE)) - goto skip_tlvs; + inflight = last_inflight(peer); - /* BOLT-upgrade_protocol #2: - * A node sending `channel_reestablish`, if it supports upgrading channels: - * - MUST set `next_to_send` the commitment number of the next - * `commitment_signed` it expects to send. - */ - send_tlvs->next_to_send = tal_dup(send_tlvs, u64, &peer->next_index[REMOTE]); + /* If inflight with no sigs on it, send next_funding */ + if (inflight && !inflight->last_tx) + send_tlvs->next_funding = &inflight->outpoint.txid; - /* BOLT-upgrade_protocol #2: - * - if it initiated the channel: - * - MUST set `desired_type` to the channel_type it wants for the - * channel. - */ - if (peer->channel->opener == LOCAL) - send_tlvs->desired_channel_type = - to_bytearr(send_tlvs, - take(channel_desired_type(NULL, - peer->channel))); - else { /* BOLT-upgrade_protocol #2: - * - otherwise: - * - MUST set `current_type` to the current channel_type of the - * channel. - * - MUST set `upgradable` to the channel types it could change - * to. - * - MAY not set `upgradable` if it would be empty. + * A node sending `channel_reestablish`, if it supports upgrading channels: + * - MUST set `next_to_send` the commitment number of the next + * `commitment_signed` it expects to send. */ - send_tlvs->current_channel_type - = to_bytearr(send_tlvs, peer->channel->type); - send_tlvs->upgradable_channel_type - = to_bytearr(send_tlvs, - take(channel_upgradable_type(NULL, - peer->channel))); - } + send_tlvs->next_to_send = tal_dup(send_tlvs, u64, &peer->next_index[REMOTE]); -skip_tlvs: -#endif + /* BOLT-upgrade_protocol #2: + * - if it initiated the channel: + * - MUST set `desired_type` to the channel_type it wants for the + * channel. + */ + if (peer->channel->opener == LOCAL) { + send_tlvs->desired_channel_type = + to_bytearr(send_tlvs, + take(channel_desired_type(NULL, + peer->channel))); + } else { + /* BOLT-upgrade_protocol #2: + * - otherwise: + * - MUST set `current_type` to the current channel_type of the + * channel. + * - MUST set `upgradable` to the channel types it could change + * to. + * - MAY not set `upgradable` if it would be empty. + */ + send_tlvs->current_channel_type + = to_bytearr(send_tlvs, peer->channel->type); + send_tlvs->upgradable_channel_type + = to_bytearr(send_tlvs, + take(channel_upgradable_type(NULL, + peer->channel))); + } + } else + send_tlvs = NULL; /* BOLT #2: * @@ -2818,22 +4687,15 @@ static void peer_reconnect(struct peer *peer, peer->revocations_received, last_remote_per_commit_secret, /* Can send any (valid) point here */ - &peer->remote_per_commit -#if EXPERIMENTAL_FEATURES - , send_tlvs -#endif - ); + &peer->remote_per_commit, send_tlvs); } else { msg = towire_channel_reestablish (NULL, &peer->channel_id, peer->next_index[LOCAL], peer->revocations_received, last_remote_per_commit_secret, - &my_current_per_commitment_point -#if EXPERIMENTAL_FEATURES - , send_tlvs -#endif - ); + &my_current_per_commitment_point, + send_tlvs); } peer_write(peer->pps, take(msg)); @@ -2857,53 +4719,22 @@ static void peer_reconnect(struct peer *peer, } while (handle_peer_error(peer->pps, &peer->channel_id, msg) || capture_premature_msg(&premature_msgs, msg)); -#if EXPERIMENTAL_FEATURES /* Initialize here in case we don't read it below! */ recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); - /* FIXME: v0.10.1 would send a different tlv set, due to older spec. - * That did *not* offer OPT_QUIESCE, so in that case ignore tlvs. */ - if (!feature_negotiated(peer->our_features, - peer->their_features, - OPT_QUIESCE)) { - if (!fromwire_channel_reestablish_notlvs(msg, - &channel_id, - &next_commitment_number, - &next_revocation_number, - &last_local_per_commitment_secret, - &remote_current_per_commitment_point)) - peer_failed_warn(peer->pps, - &peer->channel_id, - "bad reestablish msg: %s %s", - peer_wire_name(fromwire_peektype(msg)), - tal_hex(msg, msg)); - } else if (!fromwire_channel_reestablish(tmpctx, msg, - &channel_id, - &next_commitment_number, - &next_revocation_number, - &last_local_per_commitment_secret, - &remote_current_per_commitment_point, - &recv_tlvs)) { - peer_failed_warn(peer->pps, - &peer->channel_id, - "bad reestablish msg: %s %s", - peer_wire_name(fromwire_peektype(msg)), - tal_hex(msg, msg)); - } -#else /* !EXPERIMENTAL_FEATURES */ - if (!fromwire_channel_reestablish(msg, - &channel_id, - &next_commitment_number, - &next_revocation_number, - &last_local_per_commitment_secret, - &remote_current_per_commitment_point)) { + if (!fromwire_channel_reestablish(tmpctx, msg, + &channel_id, + &next_commitment_number, + &next_revocation_number, + &last_local_per_commitment_secret, + &remote_current_per_commitment_point, + &recv_tlvs)) { peer_failed_warn(peer->pps, &peer->channel_id, "bad reestablish msg: %s %s", peer_wire_name(fromwire_peektype(msg)), tal_hex(msg, msg)); } -#endif if (!channel_id_eq(&channel_id, &peer->channel_id)) { peer_failed_err(peer->pps, @@ -3081,7 +4912,10 @@ static void peer_reconnect(struct peer *peer, /* (If we had sent `closing_signed`, we'd be in closingd). */ maybe_send_shutdown(peer); -#if EXPERIMENTAL_FEATURES + /* If we didn't send (i.e. don't support!) ignore theirs */ + if (!send_tlvs) + recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); + if (recv_tlvs->desired_channel_type) status_debug("They sent desired_channel_type [%s]", fmt_featurebits(tmpctx, @@ -3159,9 +4993,6 @@ static void peer_reconnect(struct peer *peer, if (type) set_channel_type(peer->channel, type); } - tal_free(send_tlvs); - -#endif /* EXPERIMENTAL_FEATURES */ /* Now stop, we've been polite long enough. */ if (reestablish_only) { @@ -3176,6 +5007,57 @@ static void peer_reconnect(struct peer *peer, "Channel is already closed"); } + local_next_funding = (send_tlvs ? send_tlvs->next_funding : NULL); + remote_next_funding = (recv_tlvs ? recv_tlvs->next_funding : NULL); + + if (local_next_funding || remote_next_funding) { + if (remote_next_funding) { + next_matches_current = bitcoin_txid_eq(remote_next_funding, + &peer->channel->funding.txid); + if (inflight) + next_matches_inflight = bitcoin_txid_eq(remote_next_funding, + &inflight->outpoint.txid); + } + if (remote_next_funding && !next_matches_current + && !next_matches_inflight) { + peer_failed_err(peer->pps, + &peer->channel_id, + "Unrecognized next_funding txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding)); + } else if (inflight && !next_matches_inflight) { + /* DTODO: tx_abort */ + peer_failed_warn(peer->pps, &peer->channel_id, + "next_funding txid %s doesnt match" + " our inflight txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + &inflight->outpoint.txid), + type_to_string(tmpctx, + struct bitcoin_txid, + &peer->channel->funding.txid)); + } else if (!inflight && !next_matches_current) { + /* DTODO: tx_abort */ + peer_failed_warn(peer->pps, &peer->channel_id, + "next_funding txid %s doesnt match" + " our confirmed funding txid %s", + type_to_string(tmpctx, + struct bitcoin_txid, + remote_next_funding), + type_to_string(tmpctx, + struct bitcoin_txid, + &peer->channel->funding.txid)); + } + else { + resume_splice_negotiation(peer, inflight, false, + inflight->i_am_initiator + ? TX_INITIATOR + : TX_ACCEPTER); + } + } + tal_free(send_tlvs); + /* Corner case: we didn't send shutdown before because update_add_htlc * pending, but now they're cleared by restart, and we're actually * complete. In that case, their `shutdown` will trigger us. */ @@ -3216,12 +5098,16 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) struct short_channel_id *scid, *alias_local; struct tlv_channel_ready_tlvs *tlvs; struct pubkey point; + bool splicing; + struct bitcoin_txid txid; if (!fromwire_channeld_funding_depth(tmpctx, msg, &scid, &alias_local, - &depth)) + &depth, + &splicing, + &txid)) master_badmsg(WIRE_CHANNELD_FUNDING_DEPTH, msg); /* Too late, we're shutting down! */ @@ -3230,18 +5116,32 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) if (depth < peer->channel->minimum_depth) { peer->depth_togo = peer->channel->minimum_depth - depth; - - } else { + } + else { peer->depth_togo = 0; - /* If we know an actual short_channel_id prefer to use - * that, otherwise fill in the alias. From channeld's - * point of view switching from zeroconf to an actual - * funding scid is just a reorg. */ - if (scid) - peer->short_channel_ids[LOCAL] = *scid; - else if (alias_local) - peer->short_channel_ids[LOCAL] = *alias_local; + /* For splicing we only update the short channel id on mutual + * splice lock */ + if (splicing) { + peer->splice_state->short_channel_id = *scid; + status_debug("Current channel id is %s, " + "splice_short_channel_id now set to %s", + type_to_string(tmpctx, + struct short_channel_id, + &peer->short_channel_ids[LOCAL]), + type_to_string(tmpctx, + struct short_channel_id, + &peer->splice_state->short_channel_id)); + } else { + /* If we know an actual short_channel_id prefer to use + * that, otherwise fill in the alias. From channeld's + * point of view switching from zeroconf to an actual + * funding scid is just a reorg. */ + if (scid) + peer->short_channel_ids[LOCAL] = *scid; + else if (alias_local) + peer->short_channel_ids[LOCAL] = *alias_local; + } if (!peer->channel_ready[LOCAL]) { status_debug("channel_ready: sending commit index" @@ -3263,6 +5163,18 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->channel_ready[LOCAL] = true; } + else if(splicing && !peer->splice_state->locked_ready[LOCAL]) { + assert(scid); + + msg = towire_splice_locked(NULL, &peer->channel_id); + + peer->splice_state->locked_txid = txid; + + peer_write(peer->pps, take(msg)); + + peer->splice_state->locked_ready[LOCAL] = true; + check_mutual_splice_locked(peer); + } peer->announce_depth_reached = (depth >= ANNOUNCE_MIN_DEPTH); @@ -3294,7 +5206,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) const char *failstr; struct amount_sat htlc_fee; struct pubkey *blinding; - struct tlv_update_add_tlvs *tlvs; + struct tlv_update_add_htlc_tlvs *tlvs; if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) status_failed(STATUS_FAIL_MASTER_IO, @@ -3306,8 +5218,8 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) master_badmsg(WIRE_CHANNELD_OFFER_HTLC, inmsg); if (blinding) { - tlvs = tlv_update_add_tlvs_new(tmpctx); - tlvs->blinding = tal_dup(tlvs, struct pubkey, blinding); + tlvs = tlv_update_add_htlc_tlvs_new(tmpctx); + tlvs->blinding_point = tal_dup(tlvs, struct pubkey, blinding); } else tlvs = NULL; @@ -3636,21 +5548,19 @@ static void handle_dev_memleak(struct peer *peer, const u8 *msg) found_leak))); } -#if EXPERIMENTAL_FEATURES static void handle_dev_quiesce(struct peer *peer, const u8 *msg) { if (!fromwire_channeld_dev_quiesce(msg)) master_badmsg(WIRE_CHANNELD_DEV_QUIESCE, msg); /* Don't do this twice. */ - if (peer->stfu) + if (peer->want_stfu) status_failed(STATUS_FAIL_MASTER_IO, "dev_quiesce already"); - peer->stfu = true; + peer->want_stfu = true; peer->stfu_initiator = LOCAL; maybe_send_stfu(peer); } -#endif /* EXPERIMENTAL_FEATURES */ #endif /* DEVELOPER */ static void req_in(struct peer *peer, const u8 *msg) @@ -3700,6 +5610,23 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_CHANNEL_UPDATE: handle_channel_update(peer, msg); return; + case WIRE_CHANNELD_SPLICE_INIT: + handle_splice_init(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_UPDATE: + splice_initiator_user_update(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_SIGNED: + splice_initiator_user_signed(peer, msg); + return; + case WIRE_CHANNELD_SPLICE_CONFIRMED_INIT: + case WIRE_CHANNELD_SPLICE_CONFIRMED_SIGNED: + case WIRE_CHANNELD_SPLICE_CONFIRMED_UPDATE: + case WIRE_CHANNELD_SPLICE_LOOKUP_TX: + case WIRE_CHANNELD_SPLICE_LOOKUP_TX_RESULT: + case WIRE_CHANNELD_SPLICE_FEERATE_ERROR: + case WIRE_CHANNELD_SPLICE_FUNDING_ERROR: + break; #if DEVELOPER case WIRE_CHANNELD_DEV_REENABLE_COMMIT: handle_dev_reenable_commit(peer); @@ -3708,10 +5635,8 @@ static void req_in(struct peer *peer, const u8 *msg) handle_dev_memleak(peer, msg); return; case WIRE_CHANNELD_DEV_QUIESCE: -#if EXPERIMENTAL_FEATURES handle_dev_quiesce(peer, msg); return; -#endif /* EXPERIMENTAL_FEATURES */ #else case WIRE_CHANNELD_DEV_REENABLE_COMMIT: case WIRE_CHANNELD_DEV_MEMLEAK: @@ -3726,6 +5651,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_GOT_COMMITSIG_REPLY: case WIRE_CHANNELD_GOT_REVOKE_REPLY: case WIRE_CHANNELD_GOT_CHANNEL_READY: + case WIRE_CHANNELD_GOT_SPLICE_LOCKED: case WIRE_CHANNELD_GOT_ANNOUNCEMENT: case WIRE_CHANNELD_GOT_SHUTDOWN: case WIRE_CHANNELD_SHUTDOWN_COMPLETE: @@ -3739,6 +5665,10 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: + case WIRE_CHANNELD_ADD_INFLIGHT: + case WIRE_CHANNELD_UPDATE_INFLIGHT: + case WIRE_CHANNELD_GOT_INFLIGHT: + case WIRE_CHANNELD_SPLICE_STATE_ERROR: break; } master_badmsg(-1, msg); @@ -3836,21 +5766,29 @@ static void init_channel(struct peer *peer) &dev_disable_commit, &pbases, &reestablish_only, - &peer->channel_update)) { + &peer->channel_update, + &peer->experimental_upgrade, + &peer->splice_state->inflights)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } peer->final_index = tal_dup(peer, u32, &final_index); peer->final_ext_key = tal_dup(peer, struct ext_key, &final_ext_key); + peer->splice_state->committed_count = tal_count(peer->splice_state->inflights); + peer->splice_state->revoked_count = tal_count(peer->splice_state->inflights); + peer->splice_state->count = tal_count(peer->splice_state->inflights); #if DEVELOPER peer->dev_disable_commit = dev_disable_commit; peer->dev_fast_gossip = dev_fast_gossip; #endif - status_debug("option_static_remotekey = %u, option_anchor_outputs = %u", + status_debug("option_static_remotekey = %u," + " option_anchor_outputs = %u" + " option_anchors_zero_fee_htlc_tx = %u", channel_type_has(channel_type, OPT_STATIC_REMOTEKEY), - channel_type_has(channel_type, OPT_ANCHOR_OUTPUTS)); + channel_type_has(channel_type, OPT_ANCHOR_OUTPUTS), + channel_type_has(channel_type, OPT_ANCHORS_ZERO_FEE_HTLC_TX)); /* Keeping an array of pointers is better since it allows us to avoid * extra allocations later. */ @@ -3929,6 +5867,8 @@ static void init_channel(struct peer *peer) /* We don't need these any more, so free them. */ tal_free(htlcs); + update_view_from_inflights(peer); + peer->channel_direction = node_id_idx(&peer->node_ids[LOCAL], &peer->node_ids[REMOTE]); @@ -3980,11 +5920,14 @@ int main(int argc, char *argv[]) peer->shutdown_wrong_funding = NULL; peer->last_update_timestamp = 0; peer->last_empty_commitment = 0; -#if EXPERIMENTAL_FEATURES - peer->stfu = false; + peer->send_duplicate_announce_sigs = false; + peer->want_stfu = false; peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; + peer->stfu_wait_single_msg = false; + peer->on_stfu_success = NULL; peer->update_queue = msg_queue_new(peer, false); -#endif + peer->splice_state = splice_state_new(peer); + peer->splice = NULL; /* We send these to HSM to get real signatures; don't have valgrind * complain. */ @@ -4046,6 +5989,11 @@ int main(int argc, char *argv[]) tptr = &timeout; } + /* If we're in STFU mode and aren't waiting for a STFU mode + * specific message, don't read from the peer. */ + if (is_stfu_active(peer) && !peer->stfu_wait_single_msg) + FD_CLR(peer->pps->peer_fd, &rfds); + if (select(nfds, &rfds, NULL, NULL, tptr) < 0) { /* Signals OK, eg. SIGUSR1 */ if (errno == EINTR) diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index f4b60fe8b75f..5174c18e0f95 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -82,6 +83,9 @@ msgdata,channeld_init,pbases,penalty_base,num_penalty_bases msgdata,channeld_init,reestablish_only,bool, msgdata,channeld_init,channel_update_len,u16, msgdata,channeld_init,channel_update,u8,channel_update_len +msgdata,channeld_init,experimental_upgrade,bool, +msgdata,channeld_init,num_inflights,u16, +msgdata,channeld_init,inflights,inflight,num_inflights # master->channeld funding hit new depth(funding locked if >= lock depth) # alias != NULL if zeroconf and short_channel_id == NULL @@ -90,6 +94,8 @@ msgtype,channeld_funding_depth,1002 msgdata,channeld_funding_depth,short_channel_id,?short_channel_id, msgdata,channeld_funding_depth,alias_local,?short_channel_id, msgdata,channeld_funding_depth,depth,u32, +msgdata,channeld_funding_depth,splicing,bool, +msgdata,channeld_funding_depth,txid,bitcoin_txid, # Tell channel to offer this htlc msgtype,channeld_offer_htlc,1004 @@ -121,6 +127,12 @@ msgtype,channeld_got_channel_ready,1019 msgdata,channeld_got_channel_ready,next_per_commit_point,pubkey, msgdata,channeld_got_channel_ready,alias,?short_channel_id, +# When we receive funding_locked. +msgtype,channeld_got_splice_locked,1119 +msgdata,channeld_got_splice_locked,funding_sats,amount_sat, +msgdata,channeld_got_splice_locked,splice_amnt,s64, +msgdata,channeld_got_splice_locked,locked_txid,bitcoin_txid, + #include # When we send a commitment_signed message, tell master. @@ -159,6 +171,14 @@ msgdata,channeld_got_commitsig,failed,failed_htlc,num_failed msgdata,channeld_got_commitsig,num_changed,u16, msgdata,channeld_got_commitsig,changed,changed_htlc,num_changed msgdata,channeld_got_commitsig,tx,bitcoin_tx, +# Inflight splice commitments +msgdata,channeld_got_commitsig,num_inflight_commitsigs,u16, +msgdata,channeld_got_commitsig,inflight_commitsigs,commitsig,num_inflight_commitsigs +subtype,commitsig +subtypedata,commitsig,tx,bitcoin_tx, +subtypedata,commitsig,commit_signature,bitcoin_signature, +subtypedata,commitsig,num_htlcs,u16, +subtypedata,commitsig,htlc_signatures,bitcoin_signature,num_htlcs # Wait for reply, to make sure it's on disk before we send revocation. msgtype,channeld_got_commitsig_reply,1121 @@ -181,6 +201,79 @@ msgdata,channeld_got_revoke,penalty_tx,?bitcoin_tx, msgtype,channeld_got_revoke_reply,1122 #include + +# master->channeld: hello, I'd like to start a channel splice open +msgtype,channeld_splice_init,7204 +msgdata,channeld_splice_init,psbt,wally_psbt, +msgdata,channeld_splice_init,relative_amount,s64, +msgdata,channeld_splice_init,feerate_per_kw,u32, +msgdata,channeld_splice_init,force_feerate,bool, + +# channeld->master: hello, I started a channel splice open +msgtype,channeld_splice_confirmed_init,7205 +msgdata,channeld_splice_confirmed_init,psbt,wally_psbt, + +# master->channeld: Update an active splice +msgtype,channeld_splice_update,7206 +msgdata,channeld_splice_update,psbt,wally_psbt, + +# channeld->master: Splice update complete +msgtype,channeld_splice_confirmed_update,7207 +msgdata,channeld_splice_confirmed_update,psbt,wally_psbt, +msgdata,channeld_splice_confirmed_update,commitments_secured,bool, + +# channeld->master: Lookup a transaction +msgtype,channeld_splice_lookup_tx,7208 +msgdata,channeld_splice_lookup_tx,txid,bitcoin_txid, + +# master->channeld: Retrieved transaction +msgtype,channeld_splice_lookup_tx_result,7209 +msgdata,channeld_splice_lookup_tx_result,tx,bitcoin_tx, + +# master->channeld: User has signed psbt and it's ready to complete +msgtype,channeld_splice_signed,7212 +msgdata,channeld_splice_signed,psbt,wally_psbt, +msgdata,channeld_splice_signed,force_sign_first,bool, + +# channeld->master: Signed psbt is completed +msgtype,channeld_splice_confirmed_signed,7213 +msgdata,channeld_splice_confirmed_signed,tx,bitcoin_tx, +msgdata,channeld_splice_confirmed_signed,output_index,u32, + +# channeld->master: A feerate error has occured +msgtype,channeld_splice_feerate_error,7215 +msgdata,channeld_splice_feerate_error,fee,amount_msat, +msgdata,channeld_splice_feerate_error,too_high,bool, + +# channeld->master: Add an inflight to the DB +msgtype,channeld_add_inflight,7216 +msgdata,channeld_add_inflight,tx_id,bitcoin_txid, +msgdata,channeld_add_inflight,tx_outnum,u32, +msgdata,channeld_add_inflight,feerate,u32, +msgdata,channeld_add_inflight,satoshis,amount_sat, +msgdata,channeld_add_inflight,splice_amount,s64, +msgdata,channeld_add_inflight,psbt,wally_psbt, +msgdata,channeld_add_inflight,i_am_initiator,bool, + +# master->channeld: Inflight saved successfully +msgtype,channeld_got_inflight,7217 + +# channeld->master: Update inflight with sigs +msgtype,channeld_update_inflight,7219 +msgdata,channeld_update_inflight,psbt,wally_psbt, +msgdata,channeld_update_inflight,last_tx,?bitcoin_tx, +msgdata,channeld_update_inflight,last_sig,?bitcoin_signature, + +# channeld->master: A funding error has occured +msgtype,channeld_splice_funding_error,7220 +msgdata,channeld_splice_funding_error,funding,amount_msat, +msgdata,channeld_splice_funding_error,req_funding,amount_msat, +msgdata,channeld_splice_funding_error,opener_error,bool, + +# channeld->master: A splice state error has occured +msgtype,channeld_splice_state_error,7221 +msgdata,channeld_splice_state_error,state_error,wirestring, + # Tell peer to shut down channel. msgtype,channeld_send_shutdown,1023 msgdata,channeld_send_shutdown,final_index,?u32, diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 30f946de7cdc..48ce7427e18b 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -14,24 +14,29 @@ static bool trim(const struct htlc *htlc, u32 feerate_per_kw, struct amount_sat dust_limit, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side) { return htlc_is_trimmed(htlc_owner(htlc), htlc->amount, feerate_per_kw, dust_limit, side, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } size_t commit_tx_num_untrimmed(const struct htlc **htlcs, u32 feerate_per_kw, struct amount_sat dust_limit, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side) { size_t i, n; for (i = n = 0; i < tal_count(htlcs); i++) n += !trim(htlcs[i], feerate_per_kw, dust_limit, - option_anchor_outputs, side); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, + side); return n; } @@ -40,14 +45,17 @@ bool commit_tx_amount_trimmed(const struct htlc **htlcs, u32 feerate_per_kw, struct amount_sat dust_limit, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side, struct amount_msat *amt) { for (size_t i = 0; i < tal_count(htlcs); i++) { if (trim(htlcs[i], feerate_per_kw, dust_limit, - option_anchor_outputs, side)) + option_anchor_outputs, option_anchors_zero_fee_htlc_tx, + side)) { if (!amount_msat_add(amt, *amt, htlcs[i]->amount)) return false; + } } return true; } @@ -55,7 +63,8 @@ bool commit_tx_amount_trimmed(const struct htlc **htlcs, static void add_offered_htlc_out(struct bitcoin_tx *tx, size_t n, const struct htlc *htlc, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct ripemd160 ripemd; u8 *wscript, *p2wsh; @@ -63,7 +72,8 @@ static void add_offered_htlc_out(struct bitcoin_tx *tx, size_t n, ripemd160(&ripemd, htlc->rhash.u.u8, sizeof(htlc->rhash.u.u8)); wscript = htlc_offered_wscript(tx, &ripemd, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); p2wsh = scriptpubkey_p2wsh(tx, wscript); bitcoin_tx_add_output(tx, p2wsh, wscript, amount); SUPERVERBOSE("# HTLC #%" PRIu64 " offered amount %"PRIu64" wscript %s\n", htlc->id, @@ -75,7 +85,8 @@ static void add_offered_htlc_out(struct bitcoin_tx *tx, size_t n, static void add_received_htlc_out(struct bitcoin_tx *tx, size_t n, const struct htlc *htlc, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct ripemd160 ripemd; u8 *wscript, *p2wsh; @@ -83,7 +94,8 @@ static void add_received_htlc_out(struct bitcoin_tx *tx, size_t n, ripemd160(&ripemd, htlc->rhash.u.u8, sizeof(htlc->rhash.u.u8)); wscript = htlc_received_wscript(tx, &ripemd, &htlc->expiry, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); p2wsh = scriptpubkey_p2wsh(tx, wscript); amount = amount_msat_to_sat_round_down(htlc->amount); @@ -115,6 +127,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, struct wally_tx_output *direct_outputs[NUM_SIDES], u64 obscured_commitment_number, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side) { struct amount_sat base_fee; @@ -146,6 +159,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, feerate_per_kw, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, side); /* BOLT #3: @@ -154,7 +168,8 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * fee](#fee-calculation). */ base_fee = commit_tx_base_fee(feerate_per_kw, untrimmed, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); SUPERVERBOSE("# base commitment transaction fee = %"PRIu64" for %zu untrimmed\n", base_fee.satoshis /* Raw: spec uses raw numbers */, untrimmed); @@ -165,7 +180,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && !amount_sat_add(&base_fee, base_fee, AMOUNT_SAT(660))) /* Can't overflow: feerate is u32. */ abort(); @@ -183,7 +198,9 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, bool ok = true; for (size_t i = 0; i < tal_count(htlcs); i++) { if (!trim(htlcs[i], feerate_per_kw, dust_limit, - option_anchor_outputs, side)) + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, + side)) ok &= amount_sat_add(&out, out, amount_msat_to_sat_round_down(htlcs[i]->amount)); } if (amount_msat_greater_eq_sat(self_pay, dust_limit)) @@ -219,10 +236,12 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, if (htlc_owner(htlcs[i]) != side) continue; if (trim(htlcs[i], feerate_per_kw, dust_limit, - option_anchor_outputs, side)) + option_anchor_outputs, option_anchors_zero_fee_htlc_tx, + side)) continue; add_offered_htlc_out(tx, n, htlcs[i], keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); (*htlcmap)[n] = htlcs[i]; cltvs[n] = abs_locktime_to_blocks(&htlcs[i]->expiry); n++; @@ -237,10 +256,12 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, if (htlc_owner(htlcs[i]) == side) continue; if (trim(htlcs[i], feerate_per_kw, dust_limit, - option_anchor_outputs, side)) + option_anchor_outputs, option_anchors_zero_fee_htlc_tx, + side)) continue; add_received_htlc_out(tx, n, htlcs[i], keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); (*htlcmap)[n] = htlcs[i]; cltvs[n] = abs_locktime_to_blocks(&htlcs[i]->expiry); n++; @@ -303,7 +324,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, *... * Otherwise, this output is a simple P2WPKH to `remotepubkey`. */ - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { redeem = bitcoin_wscript_to_remote_anchored(tmpctx, &keyset->other_payment_key, (!side) == lessor ? @@ -351,7 +372,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * * if `to_remote` exists or there are untrimmed HTLCs, add a * [`to_remote_anchor` output] */ - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { if (to_local || untrimmed != 0) { tx_add_anchor_output(tx, local_funding_key); (*htlcmap)[n] = NULL; diff --git a/channeld/commit_tx.h b/channeld/commit_tx.h index fc2db029de40..23eacdf54a72 100644 --- a/channeld/commit_tx.h +++ b/channeld/commit_tx.h @@ -14,6 +14,7 @@ struct keyset; * @option_anchor_outputs: does option_anchor_outputs apply to this channel? * @side: from which side's point of view * @option_anchor_outputs: does option_anchor_outputs apply to this channel? + * @option_anchors_zero_fee_htlc_tx: does option_anchors_zero_fee_htlc_tx apply to this channel? * * We need @side because HTLC fees are different for offered and * received HTLCs. @@ -21,6 +22,7 @@ struct keyset; size_t commit_tx_num_untrimmed(const struct htlc **htlcs, u32 feerate_per_kw, struct amount_sat dust_limit, + bool option_anchors_zero_fee_htlc_tx, bool option_anchor_outputs, enum side side); @@ -42,6 +44,7 @@ bool commit_tx_amount_trimmed(const struct htlc **htlcs, u32 feerate_per_kw, struct amount_sat dust_limit, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side, struct amount_msat *amt); /** @@ -86,6 +89,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, struct wally_tx_output *direct_outputs[NUM_SIDES], u64 obscured_commitment_number, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, enum side side); #endif /* LIGHTNING_CHANNELD_COMMIT_TX_H */ diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 02ba567f6c04..888cf86f9f2c 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -243,8 +243,14 @@ static void add_htlcs(struct bitcoin_tx ***txs, enum side side) { struct bitcoin_outpoint outpoint; - u32 feerate_per_kw = channel_feerate(channel, side); + u32 htlc_feerate_per_kw; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); + + if (channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX)) + htlc_feerate_per_kw = 0; + else + htlc_feerate_per_kw = channel_feerate(channel, side); /* Get txid of commitment transaction */ bitcoin_txid((*txs)[0], &outpoint.txid); @@ -261,27 +267,31 @@ static void add_htlcs(struct bitcoin_tx ***txs, if (htlc_owner(htlc) == side) { ripemd160(&ripemd, htlc->rhash.u.u8, sizeof(htlc->rhash.u.u8)); wscript = htlc_offered_wscript(tmpctx, &ripemd, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); tx = htlc_timeout_tx(*txs, chainparams, &outpoint, wscript, htlc->amount, htlc->expiry.locktime, channel->config[!side].to_self_delay, - feerate_per_kw, + htlc_feerate_per_kw, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } else { ripemd160(&ripemd, htlc->rhash.u.u8, sizeof(htlc->rhash.u.u8)); wscript = htlc_received_wscript(tmpctx, &ripemd, &htlc->expiry, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); tx = htlc_success_tx(*txs, chainparams, &outpoint, wscript, htlc->amount, channel->config[!side].to_self_delay, - feerate_per_kw, + htlc_feerate_per_kw, keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } /* Append to array. */ @@ -298,10 +308,30 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, const struct pubkey *per_commitment_point, u64 commitment_number, enum side side) +{ + return channel_splice_txs(ctx, &channel->funding, channel->funding_sats, + htlcmap, direct_outputs, funding_wscript, + channel, per_commitment_point, + commitment_number, side, 0, 0); +} + +struct bitcoin_tx **channel_splice_txs(const tal_t *ctx, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc ***htlcmap, + struct wally_tx_output *direct_outputs[NUM_SIDES], + const u8 **funding_wscript, + const struct channel *channel, + const struct pubkey *per_commitment_point, + u64 commitment_number, + enum side side, + s64 splice_amnt, + s64 remote_splice_amnt) { struct bitcoin_tx **txs; const struct htlc **committed; struct keyset keyset; + struct amount_msat side_pay, other_side_pay; if (!derive_keyset(per_commitment_point, &channel->basepoints[side], @@ -319,10 +349,25 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, &channel->funding_pubkey[side], &channel->funding_pubkey[!side]); + side_pay = channel->view[side].owed[side]; + other_side_pay = channel->view[side].owed[!side]; + + if (side == LOCAL) { + if (!amount_msat_add_sat_s64(&side_pay, side_pay, splice_amnt)) + return NULL; + if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, remote_splice_amnt)) + return NULL; + } else if (side == REMOTE) { + if (!amount_msat_add_sat_s64(&side_pay, side_pay, remote_splice_amnt)) + return NULL; + if (!amount_msat_add_sat_s64(&other_side_pay, other_side_pay, splice_amnt)) + return NULL; + } + txs = tal_arr(ctx, struct bitcoin_tx *, 1); txs[0] = commit_tx( - ctx, &channel->funding, - channel->funding_sats, + txs, funding, + funding_sats, &channel->funding_pubkey[side], &channel->funding_pubkey[!side], channel->opener, @@ -330,17 +375,18 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, channel->lease_expiry, channel_blockheight(channel, side), &keyset, channel_feerate(channel, side), - channel->config[side].dust_limit, channel->view[side].owed[side], - channel->view[side].owed[!side], committed, htlcmap, direct_outputs, + channel->config[side].dust_limit, side_pay, + other_side_pay, committed, htlcmap, direct_outputs, commitment_number ^ channel->commitment_number_obscurer, channel_has(channel, OPT_ANCHOR_OUTPUTS), + channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX), side); /* Set the remote/local pubkeys on the commitment tx psbt */ psbt_input_add_pubkey(txs[0]->psbt, 0, - &channel->funding_pubkey[side]); + &channel->funding_pubkey[side], false /* is_taproot */); psbt_input_add_pubkey(txs[0]->psbt, 0, - &channel->funding_pubkey[!side]); + &channel->funding_pubkey[!side], false /* is_taproot */); add_htlcs(&txs, *htlcmap, channel, &keyset, side); @@ -360,8 +406,22 @@ static bool get_room_above_reserve(const struct channel *channel, /* Reserve is set by the *other* side */ struct amount_sat reserve = channel->config[!side].channel_reserve; struct balance balance; + struct amount_msat owed = view->owed[side]; + + /* `lowest_splice_amnt` will always be negative or 0 */ + if (amount_msat_less_eq_sat(owed, amount_sat(-view->lowest_splice_amnt[side]))) { + status_debug("Relative splice balance invalid"); + return false; + } + + /* `lowest_splice_amnt` is a relative amount */ + if (!amount_msat_sub_sat(&owed, owed, + amount_sat(-view->lowest_splice_amnt[side]))) { + status_debug("Owed amount should not wrap around from splice"); + return false; + } - to_balance(&balance, view->owed[side]); + to_balance(&balance, owed); for (size_t i = 0; i < tal_count(removing); i++) balance_remove_htlc(&balance, removing[i], side); @@ -392,17 +452,24 @@ static bool get_room_above_reserve(const struct channel *channel, static size_t num_untrimmed_htlcs(enum side side, struct amount_sat dust_limit, u32 feerate, - bool option_static_remotekey, + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, const struct htlc **committed, const struct htlc **adding, const struct htlc **removing) { return commit_tx_num_untrimmed(committed, feerate, dust_limit, - option_static_remotekey, side) + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, + side) + commit_tx_num_untrimmed(adding, feerate, dust_limit, - option_static_remotekey, side) + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, + side) - commit_tx_num_untrimmed(removing, feerate, dust_limit, - option_static_remotekey, side); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, + side); } static struct amount_sat fee_for_htlcs(const struct channel *channel, @@ -415,12 +482,16 @@ static struct amount_sat fee_for_htlcs(const struct channel *channel, struct amount_sat dust_limit = channel->config[side].dust_limit; size_t untrimmed; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); untrimmed = num_untrimmed_htlcs(side, dust_limit, feerate, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, committed, adding, removing); - return commit_tx_base_fee(feerate, untrimmed, option_anchor_outputs); + return commit_tx_base_fee(feerate, untrimmed, + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } static bool htlc_dust(const struct channel *channel, @@ -433,21 +504,25 @@ static bool htlc_dust(const struct channel *channel, { struct amount_sat dust_limit = channel->config[side].dust_limit; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); struct amount_msat trim_rmvd = AMOUNT_MSAT(0); if (!commit_tx_amount_trimmed(committed, feerate, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, side, trim_total)) return false; if (!commit_tx_amount_trimmed(adding, feerate, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, side, trim_total)) return false; if (!commit_tx_amount_trimmed(removing, feerate, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, side, &trim_rmvd)) return false; @@ -489,6 +564,7 @@ static bool local_opener_has_fee_headroom(const struct channel *channel, size_t untrimmed; struct amount_sat fee; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); assert(channel->opener == LOCAL); @@ -497,12 +573,14 @@ static bool local_opener_has_fee_headroom(const struct channel *channel, untrimmed = num_untrimmed_htlcs(LOCAL, channel->config[LOCAL].dust_limit, feerate, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, committed, adding, removing); /* Now, how much would it cost us if feerate increases 100% and we added * another HTLC? */ fee = commit_tx_base_fee(2 * feerate, untrimmed + 1, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); if (amount_msat_greater_eq_sat(remainder, fee)) return true; @@ -535,6 +613,7 @@ static enum channel_add_err add_htlc(struct channel *channel, const struct channel_view *view; size_t htlc_count; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); u32 feerate, feerate_ceil; htlc = tal(tmpctx, struct htlc); @@ -704,7 +783,7 @@ static enum channel_add_err add_htlc(struct channel *channel, * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && channel->opener == sender && !amount_msat_sub_sat(&remainder, remainder, AMOUNT_SAT(660))) return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; @@ -738,7 +817,7 @@ static enum channel_add_err add_htlc(struct channel *channel, &remainder)) return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && channel->opener != sender && !amount_msat_sub_sat(&remainder, remainder, AMOUNT_SAT(660))) return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; @@ -1167,6 +1246,7 @@ u32 approx_max_feerate(const struct channel *channel) struct amount_sat avail; const struct htlc **committed, **adding, **removing; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); gather_htlcs(tmpctx, channel, !channel->opener, &committed, &removing, &adding); @@ -1174,7 +1254,7 @@ u32 approx_max_feerate(const struct channel *channel) /* Assume none are trimmed; this gives lower bound on feerate. */ num = tal_count(committed) + tal_count(adding) - tal_count(removing); - weight = commit_tx_base_weight(num, option_anchor_outputs); + weight = commit_tx_base_weight(num, option_anchor_outputs, option_anchors_zero_fee_htlc_tx); /* Available is their view */ avail = amount_msat_to_sat_round_down(channel->view[!channel->opener].owed[channel->opener]); @@ -1185,7 +1265,7 @@ u32 approx_max_feerate(const struct channel *channel) * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && !amount_sat_sub(&avail, avail, AMOUNT_SAT(660))) { avail = AMOUNT_SAT(0); } else { @@ -1236,22 +1316,27 @@ bool can_opener_afford_feerate(const struct channel *channel, u32 feerate_per_kw size_t untrimmed; const struct htlc **committed, **adding, **removing; bool option_anchor_outputs = channel_has(channel, OPT_ANCHOR_OUTPUTS); + bool option_anchors_zero_fee_htlc_tx = channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX); gather_htlcs(tmpctx, channel, !channel->opener, &committed, &removing, &adding); untrimmed = commit_tx_num_untrimmed(committed, feerate_per_kw, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, !channel->opener) + commit_tx_num_untrimmed(adding, feerate_per_kw, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, !channel->opener) - commit_tx_num_untrimmed(removing, feerate_per_kw, dust_limit, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, !channel->opener); fee = commit_tx_base_fee(feerate_per_kw, untrimmed, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); /* BOLT #3: * If `option_anchors` applies to the commitment @@ -1259,7 +1344,7 @@ bool can_opener_afford_feerate(const struct channel *channel, u32 feerate_per_kw * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && !amount_sat_add(&fee, fee, AMOUNT_SAT(660))) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Cannot add 660 sats to %s for anchor", diff --git a/channeld/full_channel.h b/channeld/full_channel.h index 6f674a75f2c0..c0fbe6b7d4c8 100644 --- a/channeld/full_channel.h +++ b/channeld/full_channel.h @@ -76,6 +76,22 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, u64 commitment_number, enum side side); +/* Version of `channel_txs` that lets you specify a custom funding outpoint + * and funding_sats. + */ +struct bitcoin_tx **channel_splice_txs(const tal_t *ctx, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + const struct htlc ***htlcmap, + struct wally_tx_output *direct_outputs[NUM_SIDES], + const u8 **funding_wscript, + const struct channel *channel, + const struct pubkey *per_commitment_point, + u64 commitment_number, + enum side side, + s64 splice_amnt, + s64 remote_splice_amnt); + /** * actual_feerate: what is the actual feerate for the local side. * @channel: The channel state diff --git a/channeld/inflight.c b/channeld/inflight.c new file mode 100644 index 000000000000..77f0fa996381 --- /dev/null +++ b/channeld/inflight.c @@ -0,0 +1,42 @@ +#include "config.h" +#include +#include +#include +#include + +struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max) +{ + struct inflight *inflight = tal(ctx, struct inflight); + + fromwire_bitcoin_outpoint(cursor, max, &inflight->outpoint); + inflight->amnt = fromwire_amount_sat(cursor, max); + inflight->psbt = fromwire_wally_psbt(inflight, cursor, max); + inflight->splice_amnt = fromwire_s64(cursor, max); + inflight->last_tx = fromwire_bitcoin_tx(inflight, cursor, max); + fromwire_bitcoin_signature(cursor, max, &inflight->last_sig); + inflight->i_am_initiator = fromwire_bool(cursor, max); + + return inflight; +} + +void towire_inflight(u8 **pptr, const struct inflight *inflight) +{ + towire_bitcoin_outpoint(pptr, &inflight->outpoint); + towire_amount_sat(pptr, inflight->amnt); + towire_wally_psbt(pptr, inflight->psbt); + towire_s64(pptr, inflight->splice_amnt); + towire_bitcoin_tx(pptr, inflight->last_tx); + towire_bitcoin_signature(pptr, &inflight->last_sig); + towire_bool(pptr, inflight->i_am_initiator); +} + +void copy_inflight(struct inflight *dest, struct inflight *src) +{ + dest->outpoint = src->outpoint; + dest->amnt = src->amnt; + dest->psbt = src->psbt ? clone_psbt(dest, src->psbt): NULL; + dest->splice_amnt = src->splice_amnt; + dest->last_tx = src->last_tx ? clone_bitcoin_tx(dest, src->last_tx) : NULL; + dest->last_sig = src->last_sig; + dest->i_am_initiator = src->i_am_initiator; +} diff --git a/channeld/inflight.h b/channeld/inflight.h new file mode 100644 index 000000000000..c95acf9af215 --- /dev/null +++ b/channeld/inflight.h @@ -0,0 +1,24 @@ +#ifndef LIGHTNING_CHANNELD_INFLIGHT_H +#define LIGHTNING_CHANNELD_INFLIGHT_H + +#include "config.h" +#include +#include + +struct inflight { + struct bitcoin_outpoint outpoint; + struct amount_sat amnt; + struct wally_psbt *psbt; + s64 splice_amnt; + struct bitcoin_tx *last_tx; + /* last_sig is assumed valid if last_tx is set */ + struct bitcoin_signature last_sig; + bool i_am_initiator; +}; + +struct inflight *fromwire_inflight(const tal_t *ctx, const u8 **cursor, size_t *max); +void towire_inflight(u8 **pptr, const struct inflight *inflight); + +void copy_inflight(struct inflight *dest, struct inflight *src); + +#endif /* LIGHTNING_CHANNELD_INFLIGHT_H */ diff --git a/channeld/splice.c b/channeld/splice.c new file mode 100644 index 000000000000..e3019f97737d --- /dev/null +++ b/channeld/splice.c @@ -0,0 +1,37 @@ +#include "config.h" +#include +#include + +struct splice_state *splice_state_new(const tal_t *ctx) +{ + struct splice_state *splice_state = tal(ctx, struct splice_state); + + splice_state->committed_count = 0; + splice_state->revoked_count = 0; + splice_state->count = 0; + splice_state->locked_ready[LOCAL] = false; + splice_state->locked_ready[REMOTE] = false; + splice_state->await_commitment_succcess = false; + splice_state->inflights = NULL; + + return splice_state; +} + +struct splice *splice_new(const tal_t *ctx) +{ + struct splice *splice = tal(ctx, struct splice); + + splice->opener_relative = 0; + splice->accepter_relative = 0; + splice->feerate_per_kw = 0; + splice->force_feerate = false; + splice->force_sign_first = false; + splice->mode = false; + splice->tx_add_input_count = 0; + splice->tx_add_output_count = 0; + splice->current_psbt = NULL; + splice->received_tx_complete = false; + splice->sent_tx_complete = false; + + return splice; +} diff --git a/channeld/splice.h b/channeld/splice.h new file mode 100644 index 000000000000..8a9fc6552870 --- /dev/null +++ b/channeld/splice.h @@ -0,0 +1,63 @@ +#ifndef LIGHTNING_CHANNELD_SPLICE_H +#define LIGHTNING_CHANNELD_SPLICE_H + +#include "config.h" +#include +#include +#include +#include + +/* The channel's general splice state for tracking splice candidates */ +struct splice_state { + /* The active inflights */ + struct inflight **inflights; + /* The pending short channel id for a splice. Set when mutual lock. */ + struct short_channel_id short_channel_id; + /* Set to old short channel id when mutual lock occurs. */ + struct short_channel_id last_short_channel_id; + /* Tally of which sides are locked, or not */ + bool locked_ready[NUM_SIDES]; + /* Set to true when commitment cycle completes successfully */ + bool await_commitment_succcess; + /* The txid of which splice inflight was confirmed */ + struct bitcoin_txid locked_txid; + /* The number of splices that have been signed & committed */ + u32 committed_count; + /* the number of splices that have been revoke_and_ack'ed */ + u32 revoked_count; + /* The number of splices that are active (awaiting confirmation) */ + u32 count; +}; + +/* Sets `splice_state` items to default values */ +struct splice_state *splice_state_new(const tal_t *ctx); + +/* An active splice negotiation. Born when splice beings and dies when a splice + * negotation has finished */ +struct splice { + /* The opener side's relative balance change */ + s64 opener_relative; + /* The accepter side's relative balance change */ + s64 accepter_relative; + /* The feerate for the splice (on set for the initiator) */ + u32 feerate_per_kw; + /* If the feerate is higher than max, don't abort the splice */ + bool force_feerate; + /* Make our side sign first */ + bool force_sign_first; + /* After `splice` and `splice_ack` occur, we are in splice mode */ + bool mode; + /* Track how many of each tx collab msg we receive */ + u16 tx_add_input_count, tx_add_output_count; + /* Current negoitated psbt */ + struct wally_psbt *current_psbt; + /* If, in the last splice_update, was tx_complete was received */ + bool received_tx_complete; + /* If, in the last splice_update, we sent tx_complete */ + bool sent_tx_complete; +}; + +/* Sets `splice` items to default values */ +struct splice *splice_new(const tal_t *ctx); + +#endif /* LIGHTNING_CHANNELD_SPLICE_H */ diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index d1eb5721202a..68983d220dc6 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -245,7 +245,8 @@ static void report_htlcs(const struct bitcoin_tx *tx, const struct pubkey *remote_htlckey, const struct pubkey *remote_revocation_key, u32 feerate_per_kw, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { size_t i, n; struct bitcoin_outpoint outpoint; @@ -291,14 +292,17 @@ static void report_htlcs(const struct bitcoin_tx *tx, remote_htlckey, &htlc->rhash, remote_revocation_key, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); htlc_tx[i] = htlc_timeout_tx(htlc_tx, tx->chainparams, &outpoint, wscript[i], htlc->amount, htlc->expiry.locktime, to_self_delay, feerate_per_kw, - &keyset, option_anchor_outputs); + &keyset, + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } else { wscript[i] = bitcoin_wscript_htlc_receive(tmpctx, &htlc->expiry, @@ -306,14 +310,16 @@ static void report_htlcs(const struct bitcoin_tx *tx, remote_htlckey, &htlc->rhash, remote_revocation_key, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); htlc_tx[i] = htlc_success_tx(htlc_tx, tx->chainparams, &outpoint, wscript[i], htlc->amount, to_self_delay, feerate_per_kw, &keyset, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } sign_tx_input(htlc_tx[i], 0, NULL, @@ -344,7 +350,8 @@ static void report_htlcs(const struct bitcoin_tx *tx, remote_revocation_key, &localhtlcsig, &remotehtlcsig[i], - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } else { htlc_success_tx_add_witness(htlc_tx[i], &htlc->expiry, @@ -354,7 +361,8 @@ static void report_htlcs(const struct bitcoin_tx *tx, &remotehtlcsig[i], htlc->r, remote_revocation_key, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } printf("htlc_%s_tx (htlc #%"PRIu64"): %s\n", htlc_owner(htlc) == LOCAL ? "timeout" : "success", @@ -380,6 +388,7 @@ static void report(struct bitcoin_tx *tx, const struct pubkey *remote_revocation_key, u32 feerate_per_kw, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, const struct htlc **htlc_map, size_t total_htlcs) { @@ -425,7 +434,8 @@ static void report(struct bitcoin_tx *tx, remotekey, remote_htlckey, remote_revocation_key, feerate_per_kw, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } #ifdef DEBUG @@ -530,6 +540,7 @@ int main(int argc, const char *argv[]) struct amount_msat to_local, to_remote; const struct htlc **htlcs, **htlc_map, **htlc_map2, **inv_htlcs; bool option_anchor_outputs = false; + bool option_anchors_zero_fee_htlc_tx = false; bool option_static_remotekey = false; /* Allow us to check static-remotekey BOLT 3 vectors, too */ @@ -814,6 +825,7 @@ int main(int argc, const char *argv[]) to_remote, NULL, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); print_superverbose = false; tx2 = commit_tx(tmpctx, @@ -830,6 +842,7 @@ int main(int argc, const char *argv[]) to_remote, NULL, &htlc_map2, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, REMOTE); tx_must_be_eq(tx, tx2); report(tx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -845,6 +858,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, 0); @@ -880,6 +894,7 @@ int main(int argc, const char *argv[]) to_remote, htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); print_superverbose = false; tx2 = commit_tx(tmpctx, @@ -897,6 +912,7 @@ int main(int argc, const char *argv[]) inv_htlcs, &htlc_map2, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, REMOTE); tx_must_be_eq(tx, tx2); report(tx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -912,6 +928,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, tal_count(htlcs)); @@ -935,6 +952,7 @@ int main(int argc, const char *argv[]) htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); /* This is what it would look like for peer generating it! */ tx2 = commit_tx(tmpctx, @@ -952,6 +970,7 @@ int main(int argc, const char *argv[]) inv_htlcs, &htlc_map2, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, REMOTE); tx_must_be_eq(newtx, tx2); #ifdef DEBUG @@ -995,6 +1014,7 @@ int main(int argc, const char *argv[]) htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); report(tx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -1010,6 +1030,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw-1, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, tal_count(htlcs)); @@ -1045,6 +1066,7 @@ int main(int argc, const char *argv[]) htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); report(newtx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -1060,6 +1082,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, tal_count(htlcs)); @@ -1074,7 +1097,8 @@ int main(int argc, const char *argv[]) for (;;) { struct amount_sat base_fee = commit_tx_base_fee(feerate_per_kw, 0, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); /* BOLT #3: * If `option_anchors` applies to the commitment @@ -1082,7 +1106,7 @@ int main(int argc, const char *argv[]) * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && !amount_sat_add(&base_fee, base_fee, AMOUNT_SAT(660))) abort(); @@ -1121,6 +1145,7 @@ int main(int argc, const char *argv[]) htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); report(tx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -1136,6 +1161,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, tal_count(htlcs)); break; @@ -1176,6 +1202,7 @@ int main(int argc, const char *argv[]) to_remote, htlcs, &htlc_map, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, LOCAL); print_superverbose = false; tx2 = commit_tx(tmpctx, @@ -1193,6 +1220,7 @@ int main(int argc, const char *argv[]) inv_htlcs, &htlc_map2, NULL, commitment_number ^ cn_obscurer, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, REMOTE); tx_must_be_eq(tx, tx2); report(tx, wscript, &x_remote_funding_privkey, &remote_funding_pubkey, @@ -1208,6 +1236,7 @@ int main(int argc, const char *argv[]) &remote_revocation_key, feerate_per_kw, option_anchor_outputs, + option_anchors_zero_fee_htlc_tx, htlc_map, tal_count(htlcs)); common_shutdown(); diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 395eb43b4e8f..3c42a3a9d12c 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -362,6 +362,7 @@ int main(int argc, const char *argv[]) const struct htlc **htlc_map, **htlcs; const u8 *funding_wscript, *funding_wscript_alt; bool option_anchor_outputs = false; + bool option_anchors_zero_fee_htlc_tx = false; u32 blockheight = 0; size_t i; @@ -534,7 +535,7 @@ int main(int argc, const char *argv[]) to_local, to_remote, NULL, &htlc_map, NULL, 0x2bb038521914 ^ 42, - option_anchor_outputs, LOCAL); + option_anchor_outputs, option_anchors_zero_fee_htlc_tx, LOCAL); txs = channel_txs(tmpctx, &htlc_map, NULL, &funding_wscript_alt, @@ -662,7 +663,7 @@ int main(int argc, const char *argv[]) &keyset, feerate_per_kw[LOCAL], local_config->dust_limit, to_local, to_remote, htlcs, &htlc_map, NULL, 0x2bb038521914 ^ 42, - option_anchor_outputs, LOCAL); + option_anchor_outputs, option_anchors_zero_fee_htlc_tx, LOCAL); txs = channel_txs(tmpctx, &htlc_map, NULL, &funding_wscript, lchannel, &local_per_commitment_point, 42, diff --git a/channeld/watchtower.c b/channeld/watchtower.c index 00f34d30fd66..ed038cf22cc1 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -81,7 +81,7 @@ penalty_tx_create(const tal_t *ctx, bitcoin_tx_add_output(tx, final_scriptpubkey, NULL, to_them_sats); assert((final_index == NULL) == (final_ext_key == NULL)); if (final_index) - psbt_add_keypath_to_last_output(tx, *final_index, final_ext_key); + psbt_add_keypath_to_last_output(tx, *final_index, final_ext_key, is_p2tr(final_scriptpubkey, NULL)); /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); diff --git a/cli/Makefile b/cli/Makefile index 5afdb39cae1e..82dea907ea61 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -8,6 +8,7 @@ ALL_PROGRAMS += cli/lightning-cli LIGHTNING_CLI_COMMON_OBJS := \ bitcoin/chainparams.o \ common/configdir.o \ + common/configvar.o \ common/json_parse_simple.o \ common/status_levels.o \ common/utils.o \ diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index dfc2ff00f622..392ad88fc7a8 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -594,12 +594,13 @@ static char *opt_set_level(const char *arg, enum log_level *level) return NULL; } -static void opt_show_level(char buf[OPT_SHOW_LEN], const enum log_level *level) +static bool opt_show_level(char *buf, size_t len, const enum log_level *level) { if (*level == LOG_LEVEL_MAX + 1) - strncpy(buf, "none", OPT_SHOW_LEN-1); + strncpy(buf, "none", len); else - strncpy(buf, log_level_name(*level), OPT_SHOW_LEN-1); + strncpy(buf, log_level_name(*level), len); + return true; } /* The standard opt_log_stderr_exit exits with status 1 */ @@ -649,7 +650,7 @@ int main(int argc, char *argv[]) jsmntok_t *toks; const jsmntok_t *result, *error, *id; const tal_t *ctx = tal(NULL, char); - char *config_filename, *lightning_dir, *net_dir, *rpc_filename; + char *net_dir, *rpc_filename; jsmn_parser parser; int parserr; enum format format = DEFAULT_FORMAT; @@ -666,9 +667,8 @@ int main(int argc, char *argv[]) setup_option_allocators(); - initial_config_opts(ctx, argc, argv, - &config_filename, &lightning_dir, &net_dir, - &rpc_filename); + opt_exitcode = ERROR_USAGE; + minimal_config_opts(ctx, argc, argv, &net_dir, &rpc_filename); opt_register_noarg("--help|-h", opt_usage_and_exit, " [...]", "Show this message. Use the command help (without hyphens -- \"lightning-cli help\") to get a list of all RPC commands"); @@ -694,8 +694,6 @@ int main(int argc, char *argv[]) NULL, &commando, "Send this as a commando command to nodeid:rune"); - opt_register_version(); - opt_early_parse(argc, argv, opt_log_stderr_exit_usage); opt_parse(&argc, argv, opt_log_stderr_exit_usage); diff --git a/cli/test/Makefile b/cli/test/Makefile index 331018413d81..d06cfe2474c6 100644 --- a/cli/test/Makefile +++ b/cli/test/Makefile @@ -10,6 +10,7 @@ ALL_TEST_PROGRAMS += $(CLI_TEST_PROGRAMS) CLI_TEST_COMMON_OBJS := \ common/autodata.o \ common/configdir.o \ + common/configvar.o \ common/daemon_conn.o \ common/htlc_state.o \ common/json_parse_simple.o \ @@ -18,6 +19,7 @@ CLI_TEST_COMMON_OBJS := \ common/msg_queue.o \ common/setup.o \ common/utils.o \ + common/version.o \ common/type_to_string.o \ common/permute_tx.o diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index 880a790ffa9b..9e5f0bed2b95 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for version_and_exit */ -char *version_and_exit(const void *unused UNNEEDED) -{ fprintf(stderr, "version_and_exit called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int test_socket(int domain UNUSED, int type UNUSED, int protocol UNUSED) diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 2e2c0647c82c..0e0dc5494294 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -106,9 +107,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for version_and_exit */ -char *version_and_exit(const void *unused UNNEEDED) -{ fprintf(stderr, "version_and_exit called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int test_socket(int domain UNUSED, int type UNUSED, int protocol UNUSED) diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 06783b26fda0..5811b0fbb2b4 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -109,9 +110,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for version_and_exit */ -char *version_and_exit(const void *unused UNNEEDED) -{ fprintf(stderr, "version_and_exit called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int test_socket(int domain UNUSED, int type UNUSED, int protocol UNUSED) diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index b95246138089..08e49525121d 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-grpc" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "MIT" description = "The Core Lightning API as grpc primitives. Provides the bindings used to expose the API over the network." @@ -8,10 +8,14 @@ homepage = "https://github.com/ElementsProject/lightning/tree/master/cln-grpc" repository = "https://github.com/ElementsProject/lightning" documentation = "https://docs.rs/cln-grpc" +[features] +default = [] +server = ["cln-rpc"] + [dependencies] anyhow = "1.0" log = "0.4" -cln-rpc = { path="../cln-rpc/", version = "^0.1" } +cln-rpc = { path="../cln-rpc/", version = "^0.1", optional = true } tonic = { version = "0.8", features = ["tls", "transport"] } prost = "0.11" hex = "0.4.3" diff --git a/cln-grpc/Makefile b/cln-grpc/Makefile index 993286be7fbb..214579c230ed 100644 --- a/cln-grpc/Makefile +++ b/cln-grpc/Makefile @@ -8,7 +8,6 @@ CLN_GRPC_GENALL = cln-grpc/proto/node.proto \ DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL) -$(CLN_GRPC_GENALL): $(JSON_SCHEMA) - PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py +MSGGEN_GENALL += $(CLN_GRPC_GENALL) contrib/pyln-testing/pyln/testing/grpc2py.py cln-grpc-all: ${CLN_GRPC_GENALL} ${CLN_GRPC_EXAMPLES} diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 7c9def50fa7d..e16dde3c3528 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -55,12 +55,15 @@ service Node { rpc GetRoute(GetrouteRequest) returns (GetrouteResponse) {} rpc ListForwards(ListforwardsRequest) returns (ListforwardsResponse) {} rpc ListPays(ListpaysRequest) returns (ListpaysResponse) {} + rpc ListHtlcs(ListhtlcsRequest) returns (ListhtlcsResponse) {} rpc Ping(PingRequest) returns (PingResponse) {} rpc SendCustomMsg(SendcustommsgRequest) returns (SendcustommsgResponse) {} rpc SetChannel(SetchannelRequest) returns (SetchannelResponse) {} rpc SignInvoice(SigninvoiceRequest) returns (SigninvoiceResponse) {} rpc SignMessage(SignmessageRequest) returns (SignmessageResponse) {} rpc Stop(StopRequest) returns (StopResponse) {} + rpc PreApproveKeysend(PreapprovekeysendRequest) returns (PreapprovekeysendResponse) {} + rpc PreApproveInvoice(PreapproveinvoiceRequest) returns (PreapproveinvoiceResponse) {} } message GetinfoRequest { @@ -68,7 +71,7 @@ message GetinfoRequest { message GetinfoResponse { bytes id = 1; - string alias = 2; + optional string alias = 2; bytes color = 3; uint32 num_peers = 4; uint32 num_pending_channels = 5; @@ -101,7 +104,6 @@ message GetinfoAddress { IPV6 = 2; TORV2 = 3; TORV3 = 4; - WEBSOCKET = 5; } GetinfoAddressType item_type = 1; uint32 port = 2; @@ -112,6 +114,7 @@ message GetinfoBinding { // Getinfo.binding[].type enum GetinfoBindingType { LOCAL_SOCKET = 0; + WEBSOCKET = 5; IPV4 = 1; IPV6 = 2; TORV2 = 3; @@ -240,6 +243,7 @@ message ListpeersPeersChannelsInflight { string feerate = 3; Amount total_funding_msat = 4; Amount our_funding_msat = 5; + optional sint64 splice_amount = 7; bytes scratch_txid = 6; } @@ -488,6 +492,7 @@ message CreateinvoiceResponse { CreateinvoiceStatus status = 6; string description = 7; uint64 expires_at = 8; + optional uint64 created_index = 16; optional uint64 pay_index = 9; optional Amount amount_received_msat = 10; optional uint64 paid_at = 11; @@ -580,6 +585,8 @@ message DelinvoiceResponse { optional Amount amount_msat = 4; optional string description = 5; bytes payment_hash = 6; + optional uint64 created_index = 12; + optional uint64 updated_index = 13; DelinvoiceStatus status = 7; uint64 expires_at = 8; optional bytes local_offer_id = 9; @@ -602,6 +609,7 @@ message InvoiceResponse { bytes payment_hash = 2; bytes payment_secret = 3; uint64 expires_at = 4; + optional uint64 created_index = 10; optional string warning_capacity = 5; optional string warning_offline = 6; optional string warning_deadends = 7; @@ -625,10 +633,18 @@ message ListdatastoreDatastore { } message ListinvoicesRequest { + // ListInvoices.index + enum ListinvoicesIndex { + CREATED = 0; + UPDATED = 1; + } optional string label = 1; optional string invstring = 2; optional bytes payment_hash = 3; optional string offer_id = 4; + optional ListinvoicesIndex index = 5; + optional uint64 start = 6; + optional uint32 limit = 7; } message ListinvoicesResponse { @@ -652,6 +668,8 @@ message ListinvoicesInvoices { optional string bolt12 = 8; optional bytes local_offer_id = 9; optional string invreq_payer_note = 15; + optional uint64 created_index = 16; + optional uint64 updated_index = 17; optional uint64 pay_index = 11; optional Amount amount_received_msat = 12; optional uint64 paid_at = 13; @@ -758,47 +776,15 @@ message ListtransactionsTransactions { } message ListtransactionsTransactionsInputs { - // ListTransactions.transactions[].inputs[].type - enum ListtransactionsTransactionsInputsType { - THEIRS = 0; - DEPOSIT = 1; - WITHDRAW = 2; - CHANNEL_FUNDING = 3; - CHANNEL_MUTUAL_CLOSE = 4; - CHANNEL_UNILATERAL_CLOSE = 5; - CHANNEL_SWEEP = 6; - CHANNEL_HTLC_SUCCESS = 7; - CHANNEL_HTLC_TIMEOUT = 8; - CHANNEL_PENALTY = 9; - CHANNEL_UNILATERAL_CHEAT = 10; - } bytes txid = 1; uint32 index = 2; uint32 sequence = 3; - optional ListtransactionsTransactionsInputsType item_type = 4; - optional string channel = 5; } message ListtransactionsTransactionsOutputs { - // ListTransactions.transactions[].outputs[].type - enum ListtransactionsTransactionsOutputsType { - THEIRS = 0; - DEPOSIT = 1; - WITHDRAW = 2; - CHANNEL_FUNDING = 3; - CHANNEL_MUTUAL_CLOSE = 4; - CHANNEL_UNILATERAL_CLOSE = 5; - CHANNEL_SWEEP = 6; - CHANNEL_HTLC_SUCCESS = 7; - CHANNEL_HTLC_TIMEOUT = 8; - CHANNEL_PENALTY = 9; - CHANNEL_UNILATERAL_CHEAT = 10; - } uint32 index = 1; Amount amount_msat = 6; bytes scriptPubKey = 3; - optional ListtransactionsTransactionsOutputsType item_type = 4; - optional string channel = 5; } message PayRequest { @@ -859,7 +845,6 @@ message ListnodesNodesAddresses { IPV6 = 2; TORV2 = 3; TORV3 = 4; - WEBSOCKET = 5; } ListnodesNodesAddressesType item_type = 1; uint32 port = 2; @@ -885,6 +870,8 @@ message WaitanyinvoiceResponse { optional Amount amount_msat = 6; optional string bolt11 = 7; optional string bolt12 = 8; + optional uint64 created_index = 13; + optional uint64 updated_index = 14; optional uint64 pay_index = 9; optional Amount amount_received_msat = 10; optional uint64 paid_at = 11; @@ -909,6 +896,8 @@ message WaitinvoiceResponse { optional Amount amount_msat = 6; optional string bolt11 = 7; optional string bolt12 = 8; + optional uint64 created_index = 13; + optional uint64 updated_index = 14; optional uint64 pay_index = 9; optional Amount amount_received_msat = 10; optional uint64 paid_at = 11; @@ -947,12 +936,14 @@ message NewaddrRequest { // NewAddr.addresstype enum NewaddrAddresstype { BECH32 = 0; + P2TR = 3; ALL = 2; } optional NewaddrAddresstype addresstype = 1; } message NewaddrResponse { + optional string p2tr = 3; optional string bech32 = 1; optional string p2sh_segwit = 2; } @@ -1008,6 +999,8 @@ message FundpsbtRequest { optional uint32 locktime = 6; optional uint32 min_witness_weight = 7; optional bool excess_as_change = 8; + optional bool nonwrapped = 9; + optional bool opening_anchor_channel = 10; } message FundpsbtResponse { @@ -1056,6 +1049,7 @@ message UtxopsbtRequest { optional uint32 locktime = 6; optional uint32 min_witness_weight = 7; optional bool excess_as_change = 9; + optional bool opening_anchor_channel = 10; } message UtxopsbtResponse { @@ -1129,11 +1123,13 @@ message ListpeerchannelsChannels { ONCHAIN = 8; DUALOPEND_OPEN_INIT = 9; DUALOPEND_AWAITING_LOCKIN = 10; + CHANNELD_AWAITING_SPLICE = 11; } optional bytes peer_id = 1; optional bool peer_connected = 2; optional ListpeerchannelsChannelsState state = 3; optional bytes scratch_txid = 4; + optional bool ignore_fee_limits = 54; optional ListpeerchannelsChannelsFeerate feerate = 6; optional string owner = 7; optional string short_channel_id = 8; @@ -1192,6 +1188,7 @@ message ListpeerchannelsChannelsInflight { optional uint32 funding_outnum = 2; optional string feerate = 3; optional Amount total_funding_msat = 4; + optional sint64 splice_amount = 7; optional Amount our_funding_msat = 5; optional bytes scratch_txid = 6; } @@ -1466,6 +1463,7 @@ message FeeratesPerkb { optional uint32 opening = 3; optional uint32 mutual_close = 4; optional uint32 unilateral_close = 5; + optional uint32 unilateral_anchor_close = 11; optional uint32 delayed_to_us = 6; optional uint32 htlc_resolution = 7; optional uint32 penalty = 8; @@ -1485,6 +1483,7 @@ message FeeratesPerkw { optional uint32 opening = 3; optional uint32 mutual_close = 4; optional uint32 unilateral_close = 5; + optional uint32 unilateral_anchor_close = 11; optional uint32 delayed_to_us = 6; optional uint32 htlc_resolution = 7; optional uint32 penalty = 8; @@ -1500,6 +1499,7 @@ message FeeratesOnchain_fee_estimates { uint64 opening_channel_satoshis = 1; uint64 mutual_close_satoshis = 2; uint64 unilateral_close_satoshis = 3; + optional uint64 unilateral_close_nonanchor_satoshis = 6; uint64 htlc_timeout_satoshis = 4; uint64 htlc_success_satoshis = 5; } @@ -1630,11 +1630,36 @@ message ListpaysPays { optional string bolt11 = 6; optional string description = 11; optional string bolt12 = 7; + optional Amount amount_msat = 8; + optional Amount amount_sent_msat = 9; optional bytes preimage = 13; optional uint64 number_of_parts = 14; optional bytes erroronion = 10; } +message ListhtlcsRequest { + optional string id = 1; +} + +message ListhtlcsResponse { + repeated ListhtlcsHtlcs htlcs = 1; +} + +message ListhtlcsHtlcs { + // ListHtlcs.htlcs[].direction + enum ListhtlcsHtlcsDirection { + OUT = 0; + IN = 1; + } + string short_channel_id = 1; + uint64 id = 2; + uint32 expiry = 3; + Amount amount_msat = 4; + ListhtlcsHtlcsDirection direction = 5; + bytes payment_hash = 6; + HtlcState state = 7; +} + message PingRequest { bytes id = 1; optional uint32 len = 2; @@ -1661,6 +1686,7 @@ message SetchannelRequest { optional Amount htlcmin = 4; optional Amount htlcmax = 5; optional uint32 enforcedelay = 6; + optional bool ignorefeelimits = 7; } message SetchannelResponse { @@ -1673,6 +1699,7 @@ message SetchannelChannels { optional string short_channel_id = 3; Amount fee_base_msat = 4; uint32 fee_proportional_millionths = 5; + optional bool ignore_fee_limits = 10; Amount minimum_htlc_out_msat = 6; optional string warning_htlcmin_too_low = 7; Amount maximum_htlc_out_msat = 8; @@ -1702,3 +1729,19 @@ message StopRequest { message StopResponse { } + +message PreapprovekeysendRequest { + optional bytes destination = 1; + optional bytes payment_hash = 2; + optional Amount amount_msat = 3; +} + +message PreapprovekeysendResponse { +} + +message PreapproveinvoiceRequest { + optional string bolt11 = 1; +} + +message PreapproveinvoiceResponse { +} diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index e60999a13ce3..96411b282015 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -39,17 +39,26 @@ enum ChannelState { } enum HtlcState { - SentAddHtlc = 0; - SentAddCommit = 1; - RcvdAddRevocation = 2; - RcvdAddAckCommit = 3; - SentAddAckRevocation = 4; - RcvdAddAckRevocation = 5; - RcvdRemoveHtlc = 6; - RcvdRemoveCommit = 7; - SentRemoveRevocation = 8; - SentRemoveAckCommit = 9; - RcvdRemoveAckRevocation = 10; + SentAddHtlc = 0; + SentAddCommit = 1; + RcvdAddRevocation = 2; + RcvdAddAckCommit = 3; + SentAddAckRevocation = 4; + RcvdAddAckRevocation = 5; + RcvdRemoveHtlc = 6; + RcvdRemoveCommit = 7; + SentRemoveRevocation = 8; + SentRemoveAckCommit = 9; + RcvdRemoveAckRevocation = 10; + RcvdAddHtlc = 11; + RcvdAddCommit = 12; + SentAddRevocation = 13; + SentAddAckCommit = 14; + SentRemoveHtlc = 15; + SentRemoveCommit = 16; + RcvdRemoveRevocation = 17; + RcvdRemoveAckCommit = 18; + SentRemoveAckRevocation = 19; } message ChannelStateChangeCause {} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index dbd1b54bf535..bb36da54f32d 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -12,7 +12,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; use cln_rpc::primitives::PublicKey; -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetinfoOurFeatures { fn from(c: responses::GetinfoOur_features) -> Self { Self { @@ -24,7 +24,7 @@ impl From for pb::GetinfoOurFeatures { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetinfoAddress { fn from(c: responses::GetinfoAddress) -> Self { Self { @@ -35,7 +35,7 @@ impl From for pb::GetinfoAddress { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetinfoBinding { fn from(c: responses::GetinfoBinding) -> Self { Self { @@ -47,12 +47,12 @@ impl From for pb::GetinfoBinding { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetinfoResponse { fn from(c: responses::GetinfoResponse) -> Self { Self { id: c.id.serialize().to_vec(), // Rule #2 for type pubkey - alias: c.alias, // Rule #2 for type string + alias: c.alias, // Rule #2 for type string? color: hex::decode(&c.color).unwrap(), // Rule #2 for type hex num_peers: c.num_peers, // Rule #2 for type u32 num_pending_channels: c.num_pending_channels, // Rule #2 for type u32 @@ -65,7 +65,7 @@ impl From for pb::GetinfoResponse { network: c.network, // Rule #2 for type string fees_collected_msat: Some(c.fees_collected_msat.into()), // Rule #2 for type msat // Field: Getinfo.address[] - address: c.address.into_iter().map(|i| i.into()).collect(), // Rule #3 for type GetinfoAddress + address: c.address.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 // Field: Getinfo.binding[] binding: c.binding.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 warning_bitcoind_sync: c.warning_bitcoind_sync, // Rule #2 for type string? @@ -74,7 +74,7 @@ impl From for pb::GetinfoResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersLog { fn from(c: responses::ListpeersPeersLog) -> Self { Self { @@ -89,7 +89,7 @@ impl From for pb::ListpeersPeersLog { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannelsFeerate { fn from(c: responses::ListpeersPeersChannelsFeerate) -> Self { Self { @@ -99,7 +99,7 @@ impl From for pb::ListpeersPeersChanne } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannelsInflight { fn from(c: responses::ListpeersPeersChannelsInflight) -> Self { Self { @@ -108,12 +108,13 @@ impl From for pb::ListpeersPeersChann feerate: c.feerate, // Rule #2 for type string total_funding_msat: Some(c.total_funding_msat.into()), // Rule #2 for type msat our_funding_msat: Some(c.our_funding_msat.into()), // Rule #2 for type msat + splice_amount: c.splice_amount, // Rule #2 for type integer? scratch_txid: hex::decode(&c.scratch_txid).unwrap(), // Rule #2 for type txid } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannelsFunding { fn from(c: responses::ListpeersPeersChannelsFunding) -> Self { Self { @@ -126,7 +127,7 @@ impl From for pb::ListpeersPeersChanne } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannelsAlias { fn from(c: responses::ListpeersPeersChannelsAlias) -> Self { Self { @@ -136,7 +137,7 @@ impl From for pb::ListpeersPeersChannels } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannelsHtlcs { fn from(c: responses::ListpeersPeersChannelsHtlcs) -> Self { Self { @@ -152,7 +153,7 @@ impl From for pb::ListpeersPeersChannels } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersPeersChannels { fn from(c: responses::ListpeersPeersChannels) -> Self { Self { @@ -232,7 +233,7 @@ impl From for pb::ListpeersPeers { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersResponse { fn from(c: responses::ListpeersResponse) -> Self { Self { @@ -242,7 +243,7 @@ impl From for pb::ListpeersResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListfundsOutputs { fn from(c: responses::ListfundsOutputs) -> Self { Self { @@ -259,7 +260,7 @@ impl From for pb::ListfundsOutputs { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListfundsChannels { fn from(c: responses::ListfundsChannels) -> Self { Self { @@ -276,7 +277,7 @@ impl From for pb::ListfundsChannels { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListfundsResponse { fn from(c: responses::ListfundsResponse) -> Self { Self { @@ -288,7 +289,7 @@ impl From for pb::ListfundsResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendpayResponse { fn from(c: responses::SendpayResponse) -> Self { Self { @@ -311,7 +312,7 @@ impl From for pb::SendpayResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListchannelsChannels { fn from(c: responses::ListchannelsChannels) -> Self { Self { @@ -335,7 +336,7 @@ impl From for pb::ListchannelsChannels { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListchannelsResponse { fn from(c: responses::ListchannelsResponse) -> Self { Self { @@ -345,7 +346,7 @@ impl From for pb::ListchannelsResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::AddgossipResponse { fn from(c: responses::AddgossipResponse) -> Self { Self { @@ -353,7 +354,7 @@ impl From for pb::AddgossipResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::AutocleaninvoiceResponse { fn from(c: responses::AutocleaninvoiceResponse) -> Self { Self { @@ -364,7 +365,7 @@ impl From for pb::AutocleaninvoiceResponse } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CheckmessageResponse { fn from(c: responses::CheckmessageResponse) -> Self { Self { @@ -374,7 +375,7 @@ impl From for pb::CheckmessageResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CloseResponse { fn from(c: responses::CloseResponse) -> Self { Self { @@ -385,7 +386,7 @@ impl From for pb::CloseResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ConnectAddress { fn from(c: responses::ConnectAddress) -> Self { Self { @@ -397,7 +398,7 @@ impl From for pb::ConnectAddress { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ConnectResponse { fn from(c: responses::ConnectResponse) -> Self { Self { @@ -409,7 +410,7 @@ impl From for pb::ConnectResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CreateinvoiceResponse { fn from(c: responses::CreateinvoiceResponse) -> Self { Self { @@ -421,6 +422,7 @@ impl From for pb::CreateinvoiceResponse { status: c.status as i32, description: c.description, // Rule #2 for type string expires_at: c.expires_at, // Rule #2 for type u64 + created_index: c.created_index, // Rule #2 for type u64? pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at, // Rule #2 for type u64? @@ -431,7 +433,7 @@ impl From for pb::CreateinvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DatastoreResponse { fn from(c: responses::DatastoreResponse) -> Self { Self { @@ -444,7 +446,7 @@ impl From for pb::DatastoreResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CreateonionResponse { fn from(c: responses::CreateonionResponse) -> Self { Self { @@ -455,7 +457,7 @@ impl From for pb::CreateonionResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DeldatastoreResponse { fn from(c: responses::DeldatastoreResponse) -> Self { Self { @@ -468,7 +470,7 @@ impl From for pb::DeldatastoreResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DelexpiredinvoiceResponse { fn from(c: responses::DelexpiredinvoiceResponse) -> Self { Self { @@ -476,7 +478,7 @@ impl From for pb::DelexpiredinvoiceRespons } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DelinvoiceResponse { fn from(c: responses::DelinvoiceResponse) -> Self { Self { @@ -486,6 +488,8 @@ impl From for pb::DelinvoiceResponse { amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? description: c.description, // Rule #2 for type string? payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + created_index: c.created_index, // Rule #2 for type u64? + updated_index: c.updated_index, // Rule #2 for type u64? status: c.status as i32, expires_at: c.expires_at, // Rule #2 for type u64 local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? @@ -494,7 +498,7 @@ impl From for pb::DelinvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::InvoiceResponse { fn from(c: responses::InvoiceResponse) -> Self { Self { @@ -502,6 +506,7 @@ impl From for pb::InvoiceResponse { payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash payment_secret: c.payment_secret.to_vec(), // Rule #2 for type secret expires_at: c.expires_at, // Rule #2 for type u64 + created_index: c.created_index, // Rule #2 for type u64? warning_capacity: c.warning_capacity, // Rule #2 for type string? warning_offline: c.warning_offline, // Rule #2 for type string? warning_deadends: c.warning_deadends, // Rule #2 for type string? @@ -511,7 +516,7 @@ impl From for pb::InvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListdatastoreDatastore { fn from(c: responses::ListdatastoreDatastore) -> Self { Self { @@ -524,7 +529,7 @@ impl From for pb::ListdatastoreDatastore { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListdatastoreResponse { fn from(c: responses::ListdatastoreResponse) -> Self { Self { @@ -534,7 +539,7 @@ impl From for pb::ListdatastoreResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListinvoicesInvoices { fn from(c: responses::ListinvoicesInvoices) -> Self { Self { @@ -548,6 +553,8 @@ impl From for pb::ListinvoicesInvoices { bolt12: c.bolt12, // Rule #2 for type string? local_offer_id: c.local_offer_id.map(|v| v.to_vec()), // Rule #2 for type hash? invreq_payer_note: c.invreq_payer_note, // Rule #2 for type string? + created_index: c.created_index, // Rule #2 for type u64? + updated_index: c.updated_index, // Rule #2 for type u64? pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at, // Rule #2 for type u64? @@ -556,7 +563,7 @@ impl From for pb::ListinvoicesInvoices { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListinvoicesResponse { fn from(c: responses::ListinvoicesResponse) -> Self { Self { @@ -566,7 +573,7 @@ impl From for pb::ListinvoicesResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendonionResponse { fn from(c: responses::SendonionResponse) -> Self { Self { @@ -587,7 +594,7 @@ impl From for pb::SendonionResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListsendpaysPayments { fn from(c: responses::ListsendpaysPayments) -> Self { Self { @@ -610,7 +617,7 @@ impl From for pb::ListsendpaysPayments { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListsendpaysResponse { fn from(c: responses::ListsendpaysResponse) -> Self { Self { @@ -620,33 +627,29 @@ impl From for pb::ListsendpaysResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListtransactionsTransactionsInputs { fn from(c: responses::ListtransactionsTransactionsInputs) -> Self { Self { txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid index: c.index, // Rule #2 for type u32 sequence: c.sequence, // Rule #2 for type u32 - item_type: c.item_type.map(|v| v as i32), - channel: c.channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListtransactionsTransactionsOutputs { fn from(c: responses::ListtransactionsTransactionsOutputs) -> Self { Self { index: c.index, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat script_pub_key: hex::decode(&c.script_pub_key).unwrap(), // Rule #2 for type hex - item_type: c.item_type.map(|v| v as i32), - channel: c.channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListtransactionsTransactions { fn from(c: responses::ListtransactionsTransactions) -> Self { Self { @@ -664,7 +667,7 @@ impl From for pb::ListtransactionsTrans } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListtransactionsResponse { fn from(c: responses::ListtransactionsResponse) -> Self { Self { @@ -674,7 +677,7 @@ impl From for pb::ListtransactionsResponse } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::PayResponse { fn from(c: responses::PayResponse) -> Self { Self { @@ -691,7 +694,7 @@ impl From for pb::PayResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListnodesNodesAddresses { fn from(c: responses::ListnodesNodesAddresses) -> Self { Self { @@ -702,7 +705,7 @@ impl From for pb::ListnodesNodesAddresses { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListnodesNodes { fn from(c: responses::ListnodesNodes) -> Self { Self { @@ -717,7 +720,7 @@ impl From for pb::ListnodesNodes { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListnodesResponse { fn from(c: responses::ListnodesResponse) -> Self { Self { @@ -727,7 +730,7 @@ impl From for pb::ListnodesResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitanyinvoiceResponse { fn from(c: responses::WaitanyinvoiceResponse) -> Self { Self { @@ -739,6 +742,8 @@ impl From for pb::WaitanyinvoiceResponse { amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? bolt11: c.bolt11, // Rule #2 for type string? bolt12: c.bolt12, // Rule #2 for type string? + created_index: c.created_index, // Rule #2 for type u64? + updated_index: c.updated_index, // Rule #2 for type u64? pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at, // Rule #2 for type u64? @@ -747,7 +752,7 @@ impl From for pb::WaitanyinvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitinvoiceResponse { fn from(c: responses::WaitinvoiceResponse) -> Self { Self { @@ -759,6 +764,8 @@ impl From for pb::WaitinvoiceResponse { amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? bolt11: c.bolt11, // Rule #2 for type string? bolt12: c.bolt12, // Rule #2 for type string? + created_index: c.created_index, // Rule #2 for type u64? + updated_index: c.updated_index, // Rule #2 for type u64? pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at, // Rule #2 for type u64? @@ -767,7 +774,7 @@ impl From for pb::WaitinvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitsendpayResponse { fn from(c: responses::WaitsendpayResponse) -> Self { Self { @@ -793,6 +800,7 @@ impl From for pb::WaitsendpayResponse { impl From for pb::NewaddrResponse { fn from(c: responses::NewaddrResponse) -> Self { Self { + p2tr: c.p2tr, // Rule #2 for type string? bech32: c.bech32, // Rule #2 for type string? #[allow(deprecated)] p2sh_segwit: c.p2sh_segwit, // Rule #2 for type string? @@ -800,7 +808,7 @@ impl From for pb::NewaddrResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WithdrawResponse { fn from(c: responses::WithdrawResponse) -> Self { Self { @@ -811,7 +819,7 @@ impl From for pb::WithdrawResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::KeysendResponse { fn from(c: responses::KeysendResponse) -> Self { Self { @@ -828,7 +836,7 @@ impl From for pb::KeysendResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FundpsbtReservations { fn from(c: responses::FundpsbtReservations) -> Self { Self { @@ -841,7 +849,7 @@ impl From for pb::FundpsbtReservations { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FundpsbtResponse { fn from(c: responses::FundpsbtResponse) -> Self { Self { @@ -856,7 +864,7 @@ impl From for pb::FundpsbtResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendpsbtResponse { fn from(c: responses::SendpsbtResponse) -> Self { Self { @@ -866,7 +874,7 @@ impl From for pb::SendpsbtResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SignpsbtResponse { fn from(c: responses::SignpsbtResponse) -> Self { Self { @@ -875,7 +883,7 @@ impl From for pb::SignpsbtResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::UtxopsbtReservations { fn from(c: responses::UtxopsbtReservations) -> Self { Self { @@ -888,7 +896,7 @@ impl From for pb::UtxopsbtReservations { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::UtxopsbtResponse { fn from(c: responses::UtxopsbtResponse) -> Self { Self { @@ -903,7 +911,7 @@ impl From for pb::UtxopsbtResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxdiscardResponse { fn from(c: responses::TxdiscardResponse) -> Self { Self { @@ -913,7 +921,7 @@ impl From for pb::TxdiscardResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxprepareResponse { fn from(c: responses::TxprepareResponse) -> Self { Self { @@ -924,7 +932,7 @@ impl From for pb::TxprepareResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxsendResponse { fn from(c: responses::TxsendResponse) -> Self { Self { @@ -935,7 +943,7 @@ impl From for pb::TxsendResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannelsFeerate { fn from(c: responses::ListpeerchannelsChannelsFeerate) -> Self { Self { @@ -945,7 +953,7 @@ impl From for pb::ListpeerchannelsCh } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannelsInflight { fn from(c: responses::ListpeerchannelsChannelsInflight) -> Self { Self { @@ -953,13 +961,14 @@ impl From for pb::ListpeerchannelsC funding_outnum: c.funding_outnum, // Rule #2 for type u32? feerate: c.feerate, // Rule #2 for type string? total_funding_msat: c.total_funding_msat.map(|f| f.into()), // Rule #2 for type msat? + splice_amount: c.splice_amount, // Rule #2 for type integer? our_funding_msat: c.our_funding_msat.map(|f| f.into()), // Rule #2 for type msat? scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannelsFunding { fn from(c: responses::ListpeerchannelsChannelsFunding) -> Self { Self { @@ -972,7 +981,7 @@ impl From for pb::ListpeerchannelsCh } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannelsAlias { fn from(c: responses::ListpeerchannelsChannelsAlias) -> Self { Self { @@ -982,7 +991,7 @@ impl From for pb::ListpeerchannelsChan } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannelsHtlcs { fn from(c: responses::ListpeerchannelsChannelsHtlcs) -> Self { Self { @@ -998,7 +1007,7 @@ impl From for pb::ListpeerchannelsChan } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsChannels { fn from(c: responses::ListpeerchannelsChannels) -> Self { Self { @@ -1006,6 +1015,7 @@ impl From for pb::ListpeerchannelsChannels peer_connected: c.peer_connected, // Rule #2 for type boolean? state: c.state.map(|v| v as i32), scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? + ignore_fee_limits: c.ignore_fee_limits, // Rule #2 for type boolean? feerate: c.feerate.map(|v| v.into()), owner: c.owner, // Rule #2 for type string? short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id? @@ -1059,7 +1069,7 @@ impl From for pb::ListpeerchannelsChannels } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsResponse { fn from(c: responses::ListpeerchannelsResponse) -> Self { Self { @@ -1069,7 +1079,7 @@ impl From for pb::ListpeerchannelsResponse } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListclosedchannelsClosedchannelsAlias { fn from(c: responses::ListclosedchannelsClosedchannelsAlias) -> Self { Self { @@ -1079,7 +1089,7 @@ impl From for pb::Listclosedch } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListclosedchannelsClosedchannels { fn from(c: responses::ListclosedchannelsClosedchannels) -> Self { Self { @@ -1110,7 +1120,7 @@ impl From for pb::Listclosedchannel } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListclosedchannelsResponse { fn from(c: responses::ListclosedchannelsResponse) -> Self { Self { @@ -1120,7 +1130,7 @@ impl From for pb::ListclosedchannelsRespo } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodepayFallbacks { fn from(c: responses::DecodepayFallbacks) -> Self { Self { @@ -1131,7 +1141,7 @@ impl From for pb::DecodepayFallbacks { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodepayExtra { fn from(c: responses::DecodepayExtra) -> Self { Self { @@ -1141,7 +1151,7 @@ impl From for pb::DecodepayExtra { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodepayResponse { fn from(c: responses::DecodepayResponse) -> Self { Self { @@ -1166,7 +1176,7 @@ impl From for pb::DecodepayResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeOfferPaths { fn from(c: responses::DecodeOffer_paths) -> Self { Self { @@ -1176,7 +1186,7 @@ impl From for pb::DecodeOfferPaths { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeInvoiceFallbacks { fn from(c: responses::DecodeInvoice_fallbacks) -> Self { Self { @@ -1187,7 +1197,7 @@ impl From for pb::DecodeInvoiceFallbacks { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeFallbacks { fn from(c: responses::DecodeFallbacks) -> Self { Self { @@ -1196,7 +1206,7 @@ impl From for pb::DecodeFallbacks { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeExtra { fn from(c: responses::DecodeExtra) -> Self { Self { @@ -1206,7 +1216,7 @@ impl From for pb::DecodeExtra { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeRestrictions { fn from(c: responses::DecodeRestrictions) -> Self { Self { @@ -1217,7 +1227,7 @@ impl From for pb::DecodeRestrictions { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeResponse { fn from(c: responses::DecodeResponse) -> Self { Self { @@ -1300,7 +1310,7 @@ impl From for pb::DecodeResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DisconnectResponse { fn from(c: responses::DisconnectResponse) -> Self { Self { @@ -1308,7 +1318,7 @@ impl From for pb::DisconnectResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FeeratesPerkbEstimates { fn from(c: responses::FeeratesPerkbEstimates) -> Self { Self { @@ -1331,6 +1341,7 @@ impl From for pb::FeeratesPerkb { opening: c.opening, // Rule #2 for type u32? mutual_close: c.mutual_close, // Rule #2 for type u32? unilateral_close: c.unilateral_close, // Rule #2 for type u32? + unilateral_anchor_close: c.unilateral_anchor_close, // Rule #2 for type u32? #[allow(deprecated)] delayed_to_us: c.delayed_to_us, // Rule #2 for type u32? #[allow(deprecated)] @@ -1340,7 +1351,7 @@ impl From for pb::FeeratesPerkb { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FeeratesPerkwEstimates { fn from(c: responses::FeeratesPerkwEstimates) -> Self { Self { @@ -1363,6 +1374,7 @@ impl From for pb::FeeratesPerkw { opening: c.opening, // Rule #2 for type u32? mutual_close: c.mutual_close, // Rule #2 for type u32? unilateral_close: c.unilateral_close, // Rule #2 for type u32? + unilateral_anchor_close: c.unilateral_anchor_close, // Rule #2 for type u32? #[allow(deprecated)] delayed_to_us: c.delayed_to_us, // Rule #2 for type u32? #[allow(deprecated)] @@ -1372,20 +1384,21 @@ impl From for pb::FeeratesPerkw { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FeeratesOnchainFeeEstimates { fn from(c: responses::FeeratesOnchain_fee_estimates) -> Self { Self { opening_channel_satoshis: c.opening_channel_satoshis, // Rule #2 for type u64 mutual_close_satoshis: c.mutual_close_satoshis, // Rule #2 for type u64 unilateral_close_satoshis: c.unilateral_close_satoshis, // Rule #2 for type u64 + unilateral_close_nonanchor_satoshis: c.unilateral_close_nonanchor_satoshis, // Rule #2 for type u64? htlc_timeout_satoshis: c.htlc_timeout_satoshis, // Rule #2 for type u64 htlc_success_satoshis: c.htlc_success_satoshis, // Rule #2 for type u64 } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FeeratesResponse { fn from(c: responses::FeeratesResponse) -> Self { Self { @@ -1397,7 +1410,7 @@ impl From for pb::FeeratesResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FundchannelResponse { fn from(c: responses::FundchannelResponse) -> Self { Self { @@ -1411,7 +1424,7 @@ impl From for pb::FundchannelResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetrouteRoute { fn from(c: responses::GetrouteRoute) -> Self { Self { @@ -1425,7 +1438,7 @@ impl From for pb::GetrouteRoute { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetrouteResponse { fn from(c: responses::GetrouteResponse) -> Self { Self { @@ -1435,7 +1448,7 @@ impl From for pb::GetrouteResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListforwardsForwards { fn from(c: responses::ListforwardsForwards) -> Self { Self { @@ -1453,7 +1466,7 @@ impl From for pb::ListforwardsForwards { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListforwardsResponse { fn from(c: responses::ListforwardsResponse) -> Self { Self { @@ -1463,7 +1476,7 @@ impl From for pb::ListforwardsResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpaysPays { fn from(c: responses::ListpaysPays) -> Self { Self { @@ -1476,6 +1489,8 @@ impl From for pb::ListpaysPays { bolt11: c.bolt11, // Rule #2 for type string? description: c.description, // Rule #2 for type string? bolt12: c.bolt12, // Rule #2 for type string? + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + amount_sent_msat: c.amount_sent_msat.map(|f| f.into()), // Rule #2 for type msat? preimage: c.preimage.map(|v| v.to_vec()), // Rule #2 for type secret? number_of_parts: c.number_of_parts, // Rule #2 for type u64? erroronion: c.erroronion.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? @@ -1483,7 +1498,7 @@ impl From for pb::ListpaysPays { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpaysResponse { fn from(c: responses::ListpaysResponse) -> Self { Self { @@ -1493,7 +1508,32 @@ impl From for pb::ListpaysResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] +impl From for pb::ListhtlcsHtlcs { + fn from(c: responses::ListhtlcsHtlcs) -> Self { + Self { + short_channel_id: c.short_channel_id.to_string(), // Rule #2 for type short_channel_id + id: c.id, // Rule #2 for type u64 + expiry: c.expiry, // Rule #2 for type u32 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + direction: c.direction as i32, + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + state: c.state as i32, + } + } +} + +#[allow(unused_variables)] +impl From for pb::ListhtlcsResponse { + fn from(c: responses::ListhtlcsResponse) -> Self { + Self { + // Field: ListHtlcs.htlcs[] + htlcs: c.htlcs.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListhtlcsHtlcs + } + } +} + +#[allow(unused_variables)] impl From for pb::PingResponse { fn from(c: responses::PingResponse) -> Self { Self { @@ -1502,7 +1542,7 @@ impl From for pb::PingResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendcustommsgResponse { fn from(c: responses::SendcustommsgResponse) -> Self { Self { @@ -1511,7 +1551,7 @@ impl From for pb::SendcustommsgResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SetchannelChannels { fn from(c: responses::SetchannelChannels) -> Self { Self { @@ -1520,6 +1560,7 @@ impl From for pb::SetchannelChannels { short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id? fee_base_msat: Some(c.fee_base_msat.into()), // Rule #2 for type msat fee_proportional_millionths: c.fee_proportional_millionths, // Rule #2 for type u32 + ignore_fee_limits: c.ignore_fee_limits, // Rule #2 for type boolean? minimum_htlc_out_msat: Some(c.minimum_htlc_out_msat.into()), // Rule #2 for type msat warning_htlcmin_too_low: c.warning_htlcmin_too_low, // Rule #2 for type string? maximum_htlc_out_msat: Some(c.maximum_htlc_out_msat.into()), // Rule #2 for type msat @@ -1528,7 +1569,7 @@ impl From for pb::SetchannelChannels { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SetchannelResponse { fn from(c: responses::SetchannelResponse) -> Self { Self { @@ -1538,7 +1579,7 @@ impl From for pb::SetchannelResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SigninvoiceResponse { fn from(c: responses::SigninvoiceResponse) -> Self { Self { @@ -1547,7 +1588,7 @@ impl From for pb::SigninvoiceResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SignmessageResponse { fn from(c: responses::SignmessageResponse) -> Self { Self { @@ -1558,7 +1599,7 @@ impl From for pb::SignmessageResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::StopResponse { fn from(c: responses::StopResponse) -> Self { Self { @@ -1566,7 +1607,23 @@ impl From for pb::StopResponse { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] +impl From for pb::PreapprovekeysendResponse { + fn from(c: responses::PreapprovekeysendResponse) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From for pb::PreapproveinvoiceResponse { + fn from(c: responses::PreapproveinvoiceResponse) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] impl From for pb::GetinfoRequest { fn from(c: requests::GetinfoRequest) -> Self { Self { @@ -1574,7 +1631,7 @@ impl From for pb::GetinfoRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeersRequest { fn from(c: requests::ListpeersRequest) -> Self { Self { @@ -1584,7 +1641,7 @@ impl From for pb::ListpeersRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListfundsRequest { fn from(c: requests::ListfundsRequest) -> Self { Self { @@ -1593,7 +1650,7 @@ impl From for pb::ListfundsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendpayRoute { fn from(c: requests::SendpayRoute) -> Self { Self { @@ -1605,7 +1662,7 @@ impl From for pb::SendpayRoute { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendpayRequest { fn from(c: requests::SendpayRequest) -> Self { Self { @@ -1623,7 +1680,7 @@ impl From for pb::SendpayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListchannelsRequest { fn from(c: requests::ListchannelsRequest) -> Self { Self { @@ -1634,7 +1691,7 @@ impl From for pb::ListchannelsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::AddgossipRequest { fn from(c: requests::AddgossipRequest) -> Self { Self { @@ -1643,7 +1700,7 @@ impl From for pb::AddgossipRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::AutocleaninvoiceRequest { fn from(c: requests::AutocleaninvoiceRequest) -> Self { Self { @@ -1653,7 +1710,7 @@ impl From for pb::AutocleaninvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CheckmessageRequest { fn from(c: requests::CheckmessageRequest) -> Self { Self { @@ -1664,7 +1721,7 @@ impl From for pb::CheckmessageRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CloseRequest { fn from(c: requests::CloseRequest) -> Self { Self { @@ -1680,7 +1737,7 @@ impl From for pb::CloseRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ConnectRequest { fn from(c: requests::ConnectRequest) -> Self { Self { @@ -1691,7 +1748,7 @@ impl From for pb::ConnectRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CreateinvoiceRequest { fn from(c: requests::CreateinvoiceRequest) -> Self { Self { @@ -1702,7 +1759,7 @@ impl From for pb::CreateinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DatastoreRequest { fn from(c: requests::DatastoreRequest) -> Self { Self { @@ -1716,7 +1773,7 @@ impl From for pb::DatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CreateonionHops { fn from(c: requests::CreateonionHops) -> Self { Self { @@ -1726,7 +1783,7 @@ impl From for pb::CreateonionHops { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::CreateonionRequest { fn from(c: requests::CreateonionRequest) -> Self { Self { @@ -1739,7 +1796,7 @@ impl From for pb::CreateonionRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DeldatastoreRequest { fn from(c: requests::DeldatastoreRequest) -> Self { Self { @@ -1750,7 +1807,7 @@ impl From for pb::DeldatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DelexpiredinvoiceRequest { fn from(c: requests::DelexpiredinvoiceRequest) -> Self { Self { @@ -1759,7 +1816,7 @@ impl From for pb::DelexpiredinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DelinvoiceRequest { fn from(c: requests::DelinvoiceRequest) -> Self { Self { @@ -1770,7 +1827,7 @@ impl From for pb::DelinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::InvoiceRequest { fn from(c: requests::InvoiceRequest) -> Self { Self { @@ -1787,7 +1844,7 @@ impl From for pb::InvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListdatastoreRequest { fn from(c: requests::ListdatastoreRequest) -> Self { Self { @@ -1797,7 +1854,7 @@ impl From for pb::ListdatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListinvoicesRequest { fn from(c: requests::ListinvoicesRequest) -> Self { Self { @@ -1805,11 +1862,14 @@ impl From for pb::ListinvoicesRequest { invstring: c.invstring, // Rule #2 for type string? payment_hash: c.payment_hash.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? offer_id: c.offer_id, // Rule #2 for type string? + index: c.index.map(|v| v as i32), + start: c.start, // Rule #2 for type u64? + limit: c.limit, // Rule #2 for type u32? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendonionFirstHop { fn from(c: requests::SendonionFirst_hop) -> Self { Self { @@ -1820,7 +1880,7 @@ impl From for pb::SendonionFirstHop { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendonionRequest { fn from(c: requests::SendonionRequest) -> Self { Self { @@ -1840,7 +1900,7 @@ impl From for pb::SendonionRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListsendpaysRequest { fn from(c: requests::ListsendpaysRequest) -> Self { Self { @@ -1851,7 +1911,7 @@ impl From for pb::ListsendpaysRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListtransactionsRequest { fn from(c: requests::ListtransactionsRequest) -> Self { Self { @@ -1859,7 +1919,7 @@ impl From for pb::ListtransactionsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::PayRequest { fn from(c: requests::PayRequest) -> Self { Self { @@ -1880,7 +1940,7 @@ impl From for pb::PayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListnodesRequest { fn from(c: requests::ListnodesRequest) -> Self { Self { @@ -1889,7 +1949,7 @@ impl From for pb::ListnodesRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitanyinvoiceRequest { fn from(c: requests::WaitanyinvoiceRequest) -> Self { Self { @@ -1899,7 +1959,7 @@ impl From for pb::WaitanyinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitinvoiceRequest { fn from(c: requests::WaitinvoiceRequest) -> Self { Self { @@ -1908,7 +1968,7 @@ impl From for pb::WaitinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WaitsendpayRequest { fn from(c: requests::WaitsendpayRequest) -> Self { Self { @@ -1920,7 +1980,7 @@ impl From for pb::WaitsendpayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::NewaddrRequest { fn from(c: requests::NewaddrRequest) -> Self { Self { @@ -1929,7 +1989,7 @@ impl From for pb::NewaddrRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::WithdrawRequest { fn from(c: requests::WithdrawRequest) -> Self { Self { @@ -1943,7 +2003,7 @@ impl From for pb::WithdrawRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::KeysendRequest { fn from(c: requests::KeysendRequest) -> Self { Self { @@ -1960,7 +2020,7 @@ impl From for pb::KeysendRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FundpsbtRequest { fn from(c: requests::FundpsbtRequest) -> Self { Self { @@ -1972,11 +2032,13 @@ impl From for pb::FundpsbtRequest { locktime: c.locktime, // Rule #2 for type u32? min_witness_weight: c.min_witness_weight, // Rule #2 for type u32? excess_as_change: c.excess_as_change, // Rule #2 for type boolean? + nonwrapped: c.nonwrapped, // Rule #2 for type boolean? + opening_anchor_channel: c.opening_anchor_channel, // Rule #2 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendpsbtRequest { fn from(c: requests::SendpsbtRequest) -> Self { Self { @@ -1986,7 +2048,7 @@ impl From for pb::SendpsbtRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SignpsbtRequest { fn from(c: requests::SignpsbtRequest) -> Self { Self { @@ -1997,7 +2059,7 @@ impl From for pb::SignpsbtRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::UtxopsbtRequest { fn from(c: requests::UtxopsbtRequest) -> Self { Self { @@ -2011,11 +2073,12 @@ impl From for pb::UtxopsbtRequest { locktime: c.locktime, // Rule #2 for type u32? min_witness_weight: c.min_witness_weight, // Rule #2 for type u32? excess_as_change: c.excess_as_change, // Rule #2 for type boolean? + opening_anchor_channel: c.opening_anchor_channel, // Rule #2 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxdiscardRequest { fn from(c: requests::TxdiscardRequest) -> Self { Self { @@ -2024,7 +2087,7 @@ impl From for pb::TxdiscardRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxprepareRequest { fn from(c: requests::TxprepareRequest) -> Self { Self { @@ -2038,7 +2101,7 @@ impl From for pb::TxprepareRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::TxsendRequest { fn from(c: requests::TxsendRequest) -> Self { Self { @@ -2047,7 +2110,7 @@ impl From for pb::TxsendRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpeerchannelsRequest { fn from(c: requests::ListpeerchannelsRequest) -> Self { Self { @@ -2056,7 +2119,7 @@ impl From for pb::ListpeerchannelsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListclosedchannelsRequest { fn from(c: requests::ListclosedchannelsRequest) -> Self { Self { @@ -2065,7 +2128,7 @@ impl From for pb::ListclosedchannelsRequest } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodepayRequest { fn from(c: requests::DecodepayRequest) -> Self { Self { @@ -2075,7 +2138,7 @@ impl From for pb::DecodepayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DecodeRequest { fn from(c: requests::DecodeRequest) -> Self { Self { @@ -2084,7 +2147,7 @@ impl From for pb::DecodeRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::DisconnectRequest { fn from(c: requests::DisconnectRequest) -> Self { Self { @@ -2094,7 +2157,7 @@ impl From for pb::DisconnectRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FeeratesRequest { fn from(c: requests::FeeratesRequest) -> Self { Self { @@ -2103,7 +2166,7 @@ impl From for pb::FeeratesRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::FundchannelRequest { fn from(c: requests::FundchannelRequest) -> Self { Self { @@ -2124,7 +2187,7 @@ impl From for pb::FundchannelRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::GetrouteRequest { fn from(c: requests::GetrouteRequest) -> Self { Self { @@ -2141,7 +2204,7 @@ impl From for pb::GetrouteRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListforwardsRequest { fn from(c: requests::ListforwardsRequest) -> Self { Self { @@ -2152,7 +2215,7 @@ impl From for pb::ListforwardsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::ListpaysRequest { fn from(c: requests::ListpaysRequest) -> Self { Self { @@ -2163,7 +2226,16 @@ impl From for pb::ListpaysRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] +impl From for pb::ListhtlcsRequest { + fn from(c: requests::ListhtlcsRequest) -> Self { + Self { + id: c.id, // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] impl From for pb::PingRequest { fn from(c: requests::PingRequest) -> Self { Self { @@ -2174,7 +2246,7 @@ impl From for pb::PingRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SendcustommsgRequest { fn from(c: requests::SendcustommsgRequest) -> Self { Self { @@ -2184,7 +2256,7 @@ impl From for pb::SendcustommsgRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SetchannelRequest { fn from(c: requests::SetchannelRequest) -> Self { Self { @@ -2194,11 +2266,12 @@ impl From for pb::SetchannelRequest { htlcmin: c.htlcmin.map(|f| f.into()), // Rule #2 for type msat? htlcmax: c.htlcmax.map(|f| f.into()), // Rule #2 for type msat? enforcedelay: c.enforcedelay, // Rule #2 for type u32? + ignorefeelimits: c.ignorefeelimits, // Rule #2 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SigninvoiceRequest { fn from(c: requests::SigninvoiceRequest) -> Self { Self { @@ -2207,7 +2280,7 @@ impl From for pb::SigninvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::SignmessageRequest { fn from(c: requests::SignmessageRequest) -> Self { Self { @@ -2216,7 +2289,7 @@ impl From for pb::SignmessageRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for pb::StopRequest { fn from(c: requests::StopRequest) -> Self { Self { @@ -2224,8 +2297,28 @@ impl From for pb::StopRequest { } } +#[allow(unused_variables)] +impl From for pb::PreapprovekeysendRequest { + fn from(c: requests::PreapprovekeysendRequest) -> Self { + Self { + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? + payment_hash: c.payment_hash.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + } + } +} + +#[allow(unused_variables)] +impl From for pb::PreapproveinvoiceRequest { + fn from(c: requests::PreapproveinvoiceRequest) -> Self { + Self { + bolt11: c.bolt11, // Rule #2 for type string? + } + } +} -#[allow(unused_variables,deprecated)] + +#[allow(unused_variables)] impl From for requests::GetinfoRequest { fn from(c: pb::GetinfoRequest) -> Self { Self { @@ -2233,7 +2326,7 @@ impl From for requests::GetinfoRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListpeersRequest { fn from(c: pb::ListpeersRequest) -> Self { Self { @@ -2243,7 +2336,7 @@ impl From for requests::ListpeersRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListfundsRequest { fn from(c: pb::ListfundsRequest) -> Self { Self { @@ -2252,7 +2345,7 @@ impl From for requests::ListfundsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendpayRoute { fn from(c: pb::SendpayRoute) -> Self { Self { @@ -2264,7 +2357,7 @@ impl From for requests::SendpayRoute { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendpayRequest { fn from(c: pb::SendpayRequest) -> Self { Self { @@ -2281,7 +2374,7 @@ impl From for requests::SendpayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListchannelsRequest { fn from(c: pb::ListchannelsRequest) -> Self { Self { @@ -2292,7 +2385,7 @@ impl From for requests::ListchannelsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::AddgossipRequest { fn from(c: pb::AddgossipRequest) -> Self { Self { @@ -2301,7 +2394,7 @@ impl From for requests::AddgossipRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::AutocleaninvoiceRequest { fn from(c: pb::AutocleaninvoiceRequest) -> Self { Self { @@ -2311,7 +2404,7 @@ impl From for requests::AutocleaninvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::CheckmessageRequest { fn from(c: pb::CheckmessageRequest) -> Self { Self { @@ -2322,7 +2415,7 @@ impl From for requests::CheckmessageRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::CloseRequest { fn from(c: pb::CloseRequest) -> Self { Self { @@ -2337,7 +2430,7 @@ impl From for requests::CloseRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ConnectRequest { fn from(c: pb::ConnectRequest) -> Self { Self { @@ -2348,7 +2441,7 @@ impl From for requests::ConnectRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::CreateinvoiceRequest { fn from(c: pb::CreateinvoiceRequest) -> Self { Self { @@ -2359,7 +2452,7 @@ impl From for requests::CreateinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DatastoreRequest { fn from(c: pb::DatastoreRequest) -> Self { Self { @@ -2372,7 +2465,7 @@ impl From for requests::DatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::CreateonionHops { fn from(c: pb::CreateonionHops) -> Self { Self { @@ -2382,7 +2475,7 @@ impl From for requests::CreateonionHops { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::CreateonionRequest { fn from(c: pb::CreateonionRequest) -> Self { Self { @@ -2394,7 +2487,7 @@ impl From for requests::CreateonionRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DeldatastoreRequest { fn from(c: pb::DeldatastoreRequest) -> Self { Self { @@ -2404,7 +2497,7 @@ impl From for requests::DeldatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DelexpiredinvoiceRequest { fn from(c: pb::DelexpiredinvoiceRequest) -> Self { Self { @@ -2413,7 +2506,7 @@ impl From for requests::DelexpiredinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DelinvoiceRequest { fn from(c: pb::DelinvoiceRequest) -> Self { Self { @@ -2424,7 +2517,7 @@ impl From for requests::DelinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::InvoiceRequest { fn from(c: pb::InvoiceRequest) -> Self { Self { @@ -2440,7 +2533,7 @@ impl From for requests::InvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListdatastoreRequest { fn from(c: pb::ListdatastoreRequest) -> Self { Self { @@ -2449,7 +2542,7 @@ impl From for requests::ListdatastoreRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListinvoicesRequest { fn from(c: pb::ListinvoicesRequest) -> Self { Self { @@ -2457,11 +2550,14 @@ impl From for requests::ListinvoicesRequest { invstring: c.invstring, // Rule #1 for type string? payment_hash: c.payment_hash.map(|v| hex::encode(v)), // Rule #1 for type hex? offer_id: c.offer_id, // Rule #1 for type string? + index: c.index.map(|v| v.try_into().unwrap()), + start: c.start, // Rule #1 for type u64? + limit: c.limit, // Rule #1 for type u32? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendonionFirst_hop { fn from(c: pb::SendonionFirstHop) -> Self { Self { @@ -2472,7 +2568,7 @@ impl From for requests::SendonionFirst_hop { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendonionRequest { fn from(c: pb::SendonionRequest) -> Self { Self { @@ -2491,7 +2587,7 @@ impl From for requests::SendonionRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListsendpaysRequest { fn from(c: pb::ListsendpaysRequest) -> Self { Self { @@ -2502,7 +2598,7 @@ impl From for requests::ListsendpaysRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListtransactionsRequest { fn from(c: pb::ListtransactionsRequest) -> Self { Self { @@ -2510,7 +2606,7 @@ impl From for requests::ListtransactionsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::PayRequest { fn from(c: pb::PayRequest) -> Self { Self { @@ -2530,7 +2626,7 @@ impl From for requests::PayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListnodesRequest { fn from(c: pb::ListnodesRequest) -> Self { Self { @@ -2539,7 +2635,7 @@ impl From for requests::ListnodesRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::WaitanyinvoiceRequest { fn from(c: pb::WaitanyinvoiceRequest) -> Self { Self { @@ -2549,7 +2645,7 @@ impl From for requests::WaitanyinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::WaitinvoiceRequest { fn from(c: pb::WaitinvoiceRequest) -> Self { Self { @@ -2558,7 +2654,7 @@ impl From for requests::WaitinvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::WaitsendpayRequest { fn from(c: pb::WaitsendpayRequest) -> Self { Self { @@ -2570,7 +2666,7 @@ impl From for requests::WaitsendpayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::NewaddrRequest { fn from(c: pb::NewaddrRequest) -> Self { Self { @@ -2579,7 +2675,7 @@ impl From for requests::NewaddrRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::WithdrawRequest { fn from(c: pb::WithdrawRequest) -> Self { Self { @@ -2592,7 +2688,7 @@ impl From for requests::WithdrawRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::KeysendRequest { fn from(c: pb::KeysendRequest) -> Self { Self { @@ -2609,7 +2705,7 @@ impl From for requests::KeysendRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::FundpsbtRequest { fn from(c: pb::FundpsbtRequest) -> Self { Self { @@ -2621,11 +2717,13 @@ impl From for requests::FundpsbtRequest { locktime: c.locktime, // Rule #1 for type u32? min_witness_weight: c.min_witness_weight, // Rule #1 for type u32? excess_as_change: c.excess_as_change, // Rule #1 for type boolean? + nonwrapped: c.nonwrapped, // Rule #1 for type boolean? + opening_anchor_channel: c.opening_anchor_channel, // Rule #1 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendpsbtRequest { fn from(c: pb::SendpsbtRequest) -> Self { Self { @@ -2635,7 +2733,7 @@ impl From for requests::SendpsbtRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SignpsbtRequest { fn from(c: pb::SignpsbtRequest) -> Self { Self { @@ -2645,7 +2743,7 @@ impl From for requests::SignpsbtRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::UtxopsbtRequest { fn from(c: pb::UtxopsbtRequest) -> Self { Self { @@ -2658,11 +2756,12 @@ impl From for requests::UtxopsbtRequest { locktime: c.locktime, // Rule #1 for type u32? min_witness_weight: c.min_witness_weight, // Rule #1 for type u32? excess_as_change: c.excess_as_change, // Rule #1 for type boolean? + opening_anchor_channel: c.opening_anchor_channel, // Rule #1 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::TxdiscardRequest { fn from(c: pb::TxdiscardRequest) -> Self { Self { @@ -2671,7 +2770,7 @@ impl From for requests::TxdiscardRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::TxprepareRequest { fn from(c: pb::TxprepareRequest) -> Self { Self { @@ -2683,7 +2782,7 @@ impl From for requests::TxprepareRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::TxsendRequest { fn from(c: pb::TxsendRequest) -> Self { Self { @@ -2692,7 +2791,7 @@ impl From for requests::TxsendRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListpeerchannelsRequest { fn from(c: pb::ListpeerchannelsRequest) -> Self { Self { @@ -2701,7 +2800,7 @@ impl From for requests::ListpeerchannelsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListclosedchannelsRequest { fn from(c: pb::ListclosedchannelsRequest) -> Self { Self { @@ -2710,7 +2809,7 @@ impl From for requests::ListclosedchannelsRequest } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DecodepayRequest { fn from(c: pb::DecodepayRequest) -> Self { Self { @@ -2720,7 +2819,7 @@ impl From for requests::DecodepayRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DecodeRequest { fn from(c: pb::DecodeRequest) -> Self { Self { @@ -2729,7 +2828,7 @@ impl From for requests::DecodeRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::DisconnectRequest { fn from(c: pb::DisconnectRequest) -> Self { Self { @@ -2739,7 +2838,7 @@ impl From for requests::DisconnectRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::FeeratesRequest { fn from(c: pb::FeeratesRequest) -> Self { Self { @@ -2748,7 +2847,7 @@ impl From for requests::FeeratesRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::FundchannelRequest { fn from(c: pb::FundchannelRequest) -> Self { Self { @@ -2768,7 +2867,7 @@ impl From for requests::FundchannelRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::GetrouteRequest { fn from(c: pb::GetrouteRequest) -> Self { Self { @@ -2784,7 +2883,7 @@ impl From for requests::GetrouteRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListforwardsRequest { fn from(c: pb::ListforwardsRequest) -> Self { Self { @@ -2795,7 +2894,7 @@ impl From for requests::ListforwardsRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::ListpaysRequest { fn from(c: pb::ListpaysRequest) -> Self { Self { @@ -2806,7 +2905,16 @@ impl From for requests::ListpaysRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] +impl From for requests::ListhtlcsRequest { + fn from(c: pb::ListhtlcsRequest) -> Self { + Self { + id: c.id, // Rule #1 for type string? + } + } +} + +#[allow(unused_variables)] impl From for requests::PingRequest { fn from(c: pb::PingRequest) -> Self { Self { @@ -2817,7 +2925,7 @@ impl From for requests::PingRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SendcustommsgRequest { fn from(c: pb::SendcustommsgRequest) -> Self { Self { @@ -2827,7 +2935,7 @@ impl From for requests::SendcustommsgRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SetchannelRequest { fn from(c: pb::SetchannelRequest) -> Self { Self { @@ -2837,11 +2945,12 @@ impl From for requests::SetchannelRequest { htlcmin: c.htlcmin.map(|a| a.into()), // Rule #1 for type msat? htlcmax: c.htlcmax.map(|a| a.into()), // Rule #1 for type msat? enforcedelay: c.enforcedelay, // Rule #1 for type u32? + ignorefeelimits: c.ignorefeelimits, // Rule #1 for type boolean? } } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SigninvoiceRequest { fn from(c: pb::SigninvoiceRequest) -> Self { Self { @@ -2850,7 +2959,7 @@ impl From for requests::SigninvoiceRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::SignmessageRequest { fn from(c: pb::SignmessageRequest) -> Self { Self { @@ -2859,7 +2968,7 @@ impl From for requests::SignmessageRequest { } } -#[allow(unused_variables,deprecated)] +#[allow(unused_variables)] impl From for requests::StopRequest { fn from(c: pb::StopRequest) -> Self { Self { @@ -2867,3 +2976,23 @@ impl From for requests::StopRequest { } } +#[allow(unused_variables)] +impl From for requests::PreapprovekeysendRequest { + fn from(c: pb::PreapprovekeysendRequest) -> Self { + Self { + destination: c.destination.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? + payment_hash: c.payment_hash.map(|v| hex::encode(v)), // Rule #1 for type hex? + amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? + } + } +} + +#[allow(unused_variables)] +impl From for requests::PreapproveinvoiceRequest { + fn from(c: pb::PreapproveinvoiceRequest) -> Self { + Self { + bolt11: c.bolt11, // Rule #1 for type string? + } + } +} + diff --git a/cln-grpc/src/lib.rs b/cln-grpc/src/lib.rs index f9e2fec5423d..30a14cc5561c 100644 --- a/cln-grpc/src/lib.rs +++ b/cln-grpc/src/lib.rs @@ -1,10 +1,14 @@ // Huge json!() macros require lots of recursion #![recursion_limit = "1024"] +#[cfg(feature = "server")] mod convert; pub mod pb; + +#[cfg(feature = "server")] mod server; +#[cfg(feature = "server")] pub use crate::server::Server; #[cfg(test)] diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index adf738b89786..ec86425c6e74 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,446 +1,455 @@ tonic::include_proto!("cln"); -use bitcoin::hashes::Hash; -use std::str::FromStr; -use cln_rpc::primitives::{ - Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, - Feerate as JFeerate, Outpoint as JOutpoint, OutputDesc as JOutputDesc, -}; +#[cfg(feature = "server")] +pub use crate::pb::convert::*; -impl From for Amount { - fn from(a: JAmount) -> Self { - Amount { msat: a.msat() } +#[cfg(feature = "server")] +mod convert { + use super::*; + use bitcoin::hashes::Hash; + use std::str::FromStr; + + use cln_rpc::primitives::{ + Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, + Feerate as JFeerate, Outpoint as JOutpoint, OutputDesc as JOutputDesc, + }; + + impl From for Amount { + fn from(a: JAmount) -> Self { + Amount { msat: a.msat() } + } } -} -impl From for JAmount { - fn from(a: Amount) -> Self { - JAmount::from_msat(a.msat) + impl From for JAmount { + fn from(a: Amount) -> Self { + JAmount::from_msat(a.msat) + } } -} -impl From for Outpoint { - fn from(a: JOutpoint) -> Self { - Outpoint { - txid: a.txid.to_vec(), - outnum: a.outnum, + impl From for Outpoint { + fn from(a: JOutpoint) -> Self { + Outpoint { + txid: a.txid.to_vec(), + outnum: a.outnum, + } } } -} -impl From for JOutpoint { - fn from(a: Outpoint) -> Self { - JOutpoint { - txid: bitcoin::hashes::sha256::Hash::from_slice(&a.txid).unwrap(), - outnum: a.outnum, + impl From for JOutpoint { + fn from(a: Outpoint) -> Self { + JOutpoint { + txid: bitcoin::hashes::sha256::Hash::from_slice(&a.txid).unwrap(), + outnum: a.outnum, + } } } -} -impl From for cln_rpc::primitives::Feerate { - fn from(f: Feerate) -> cln_rpc::primitives::Feerate { - use feerate::Style; - match f.style.unwrap() { - Style::Slow(_) => JFeerate::Slow, - Style::Normal(_) => JFeerate::Normal, - Style::Urgent(_) => JFeerate::Urgent, - Style::Perkw(i) => JFeerate::PerKw(i), - Style::Perkb(i) => JFeerate::PerKb(i), + impl From for cln_rpc::primitives::Feerate { + fn from(f: Feerate) -> cln_rpc::primitives::Feerate { + use feerate::Style; + match f.style.unwrap() { + Style::Slow(_) => JFeerate::Slow, + Style::Normal(_) => JFeerate::Normal, + Style::Urgent(_) => JFeerate::Urgent, + Style::Perkw(i) => JFeerate::PerKw(i), + Style::Perkb(i) => JFeerate::PerKb(i), + } } } -} -impl From for Feerate { - fn from(f: cln_rpc::primitives::Feerate) -> Feerate { - use feerate::Style; - let style = Some(match f { - JFeerate::Slow => Style::Slow(true), - JFeerate::Normal => Style::Normal(true), - JFeerate::Urgent => Style::Urgent(true), - JFeerate::PerKb(i) => Style::Perkb(i), - JFeerate::PerKw(i) => Style::Perkw(i), - }); - Self { style } + impl From for Feerate { + fn from(f: cln_rpc::primitives::Feerate) -> Feerate { + use feerate::Style; + let style = Some(match f { + JFeerate::Slow => Style::Slow(true), + JFeerate::Normal => Style::Normal(true), + JFeerate::Urgent => Style::Urgent(true), + JFeerate::PerKb(i) => Style::Perkb(i), + JFeerate::PerKw(i) => Style::Perkw(i), + }); + Self { style } + } } -} -impl From for JOutputDesc { - fn from(od: OutputDesc) -> JOutputDesc { - JOutputDesc { - address: od.address, - amount: od.amount.unwrap().into(), + impl From for JOutputDesc { + fn from(od: OutputDesc) -> JOutputDesc { + JOutputDesc { + address: od.address, + amount: od.amount.unwrap().into(), + } } } -} -impl From for OutputDesc { - fn from(od: JOutputDesc) -> Self { - Self { - address: od.address, - amount: Some(od.amount.into()), + impl From for OutputDesc { + fn from(od: JOutputDesc) -> Self { + Self { + address: od.address, + amount: Some(od.amount.into()), + } } } -} -impl From for AmountOrAll { - fn from(a: JAmountOrAll) -> Self { - match a { - JAmountOrAll::Amount(a) => AmountOrAll { - value: Some(amount_or_all::Value::Amount(a.into())), - }, - JAmountOrAll::All => AmountOrAll { - value: Some(amount_or_all::Value::All(true)), - }, + impl From for AmountOrAll { + fn from(a: JAmountOrAll) -> Self { + match a { + JAmountOrAll::Amount(a) => AmountOrAll { + value: Some(amount_or_all::Value::Amount(a.into())), + }, + JAmountOrAll::All => AmountOrAll { + value: Some(amount_or_all::Value::All(true)), + }, + } } } -} -impl From for JAmountOrAll { - fn from(a: AmountOrAll) -> Self { - match a.value { - Some(amount_or_all::Value::Amount(a)) => JAmountOrAll::Amount(a.into()), - Some(amount_or_all::Value::All(_)) => JAmountOrAll::All, - None => panic!("AmountOrAll is neither amount nor all: {:?}", a), + impl From for JAmountOrAll { + fn from(a: AmountOrAll) -> Self { + match a.value { + Some(amount_or_all::Value::Amount(a)) => JAmountOrAll::Amount(a.into()), + Some(amount_or_all::Value::All(_)) => JAmountOrAll::All, + None => panic!("AmountOrAll is neither amount nor all: {:?}", a), + } } } -} -impl From for AmountOrAny { - fn from(a: JAmountOrAny) -> Self { - match a { - JAmountOrAny::Amount(a) => AmountOrAny { - value: Some(amount_or_any::Value::Amount(a.into())), - }, - JAmountOrAny::Any => AmountOrAny { - value: Some(amount_or_any::Value::Any(true)), - }, + impl From for AmountOrAny { + fn from(a: JAmountOrAny) -> Self { + match a { + JAmountOrAny::Amount(a) => AmountOrAny { + value: Some(amount_or_any::Value::Amount(a.into())), + }, + JAmountOrAny::Any => AmountOrAny { + value: Some(amount_or_any::Value::Any(true)), + }, + } } } -} -impl From for JAmountOrAny { - fn from(a: AmountOrAny) -> Self { - match a.value { - Some(amount_or_any::Value::Amount(a)) => JAmountOrAny::Amount(a.into()), - Some(amount_or_any::Value::Any(_)) => JAmountOrAny::Any, - None => panic!("AmountOrAll is neither amount nor any: {:?}", a), + impl From for JAmountOrAny { + fn from(a: AmountOrAny) -> Self { + match a.value { + Some(amount_or_any::Value::Amount(a)) => JAmountOrAny::Amount(a.into()), + Some(amount_or_any::Value::Any(_)) => JAmountOrAny::Any, + None => panic!("AmountOrAll is neither amount nor any: {:?}", a), + } } } -} -impl From for cln_rpc::primitives::Routehop { - fn from(c: RouteHop) -> Self { - Self { - id: cln_rpc::primitives::PublicKey::from_slice(&c.id).unwrap(), - scid: cln_rpc::primitives::ShortChannelId::from_str(&c.short_channel_id).unwrap(), - feebase: c.feebase.unwrap().into(), - feeprop: c.feeprop, - expirydelta: c.expirydelta as u16, + impl From for cln_rpc::primitives::Routehop { + fn from(c: RouteHop) -> Self { + Self { + id: cln_rpc::primitives::PublicKey::from_slice(&c.id).unwrap(), + scid: cln_rpc::primitives::ShortChannelId::from_str(&c.short_channel_id).unwrap(), + feebase: c.feebase.unwrap().into(), + feeprop: c.feeprop, + expirydelta: c.expirydelta as u16, + } } } -} -impl From for cln_rpc::primitives::Routehint { - fn from(c: Routehint) -> Self { - Self { - hops: c.hops.into_iter().map(|h| h.into()).collect(), + impl From for cln_rpc::primitives::Routehint { + fn from(c: Routehint) -> Self { + Self { + hops: c.hops.into_iter().map(|h| h.into()).collect(), + } } } -} -impl From for cln_rpc::primitives::RoutehintList { - fn from(c: RoutehintList) -> Self { - Self { - hints: c.hints.into_iter().map(|h| h.into()).collect(), + impl From for cln_rpc::primitives::RoutehintList { + fn from(c: RoutehintList) -> Self { + Self { + hints: c.hints.into_iter().map(|h| h.into()).collect(), + } } } -} -impl From for RouteHop { - fn from(c: cln_rpc::primitives::Routehop) -> Self { - Self { - id: c.id.serialize().to_vec(), - feebase: Some(c.feebase.into()), - feeprop: c.feeprop, - expirydelta: c.expirydelta as u32, - short_channel_id: c.scid.to_string(), + impl From for RouteHop { + fn from(c: cln_rpc::primitives::Routehop) -> Self { + Self { + id: c.id.serialize().to_vec(), + feebase: Some(c.feebase.into()), + feeprop: c.feeprop, + expirydelta: c.expirydelta as u32, + short_channel_id: c.scid.to_string(), + } } } -} -impl From for Routehint { - fn from(c: cln_rpc::primitives::Routehint) -> Self { - Self { - hops: c.hops.into_iter().map(|h| h.into()).collect(), + impl From for Routehint { + fn from(c: cln_rpc::primitives::Routehint) -> Self { + Self { + hops: c.hops.into_iter().map(|h| h.into()).collect(), + } } } -} -impl From for RoutehintList { - fn from(c: cln_rpc::primitives::RoutehintList) -> Self { - Self { - hints: c.hints.into_iter().map(|e| e.into()).collect(), + impl From for RoutehintList { + fn from(c: cln_rpc::primitives::RoutehintList) -> Self { + Self { + hints: c.hints.into_iter().map(|e| e.into()).collect(), + } } } -} -impl From for cln_rpc::primitives::TlvStream { - fn from(s: TlvStream) -> Self { - Self { - entries: s.entries.into_iter().map(|e| e.into()).collect(), + impl From for cln_rpc::primitives::TlvStream { + fn from(s: TlvStream) -> Self { + Self { + entries: s.entries.into_iter().map(|e| e.into()).collect(), + } } } -} -impl From for cln_rpc::primitives::TlvEntry { - fn from(e: TlvEntry) -> Self { - Self { - typ: e.r#type, - value: e.value, + impl From for cln_rpc::primitives::TlvEntry { + fn from(e: TlvEntry) -> Self { + Self { + typ: e.r#type, + value: e.value, + } } } -} -impl From for TlvStream { - fn from(s: cln_rpc::primitives::TlvStream) -> Self { - Self { - entries: s.entries.into_iter().map(|e| e.into()).collect(), + impl From for TlvStream { + fn from(s: cln_rpc::primitives::TlvStream) -> Self { + Self { + entries: s.entries.into_iter().map(|e| e.into()).collect(), + } } } -} -impl From for TlvEntry { - fn from(e: cln_rpc::primitives::TlvEntry) -> Self { - Self { - r#type: e.typ, - value: e.value, + impl From for TlvEntry { + fn from(e: cln_rpc::primitives::TlvEntry) -> Self { + Self { + r#type: e.typ, + value: e.value, + } } } -} -#[cfg(test)] -mod test { - use super::*; - use serde_json::json; + #[cfg(test)] + mod test { + use super::*; + use serde_json::json; - #[test] - fn test_listpeers() { - let j: serde_json::Value = json!({ - "peers": [ - { - "id": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", - "connected": true, - "netaddr": [ - "127.0.0.1:39152" - ], - "features": "8808226aa2", - "num_channels": 0, - "channels": [ + #[test] + fn test_listpeers() { + let j: serde_json::Value = json!({ + "peers": [ { - "state": "CHANNELD_NORMAL", - "scratch_txid": "fd4659658d235c20c81f96f7bc867c17abbfd20fcdd46c27eaad74ea52eaee90", - "last_tx_fee_msat": "14257000msat", - "feerate": { - "perkw": 11000, - "perkb": 44000 - }, - "owner": "channeld", - "short_channel_id": "103x2x1", - "direction": 0, - "channel_id": "44b77a6d66ca54f0c365c84b13a95fbde462415a0549228baa25ee1bb1dfef66", - "funding_txid": "67efdfb11bee25aa8b2249055a4162e4bd5fa9134bc865c3f054ca666d7ab744", - "funding_outnum": 1, - "close_to_addr": "bcrt1q9tc6q49l6wrrtp8ul45rj92hsleehwwxty32zu", - "close_to": "00142af1a054bfd3863584fcfd6839155787f39bb9c6", - "private": false, - "opener": "remote", - "features": [ - "option_static_remotekey", - "option_anchor_outputs" - ], - "funding": { - "local_msat": "0msat", - "remote_msat": "1000000000msat", - "pushed_msat": "0msat", - "local_funds_msat": "0msat", - "remote_funds_msat": "0msat" - }, - "msatoshi_to_us": 0, - "to_us_msat": "0msat", - "msatoshi_to_us_min": 0, - "min_to_us_msat": "0msat", - "msatoshi_to_us_max": 0, - "max_to_us_msat": "0msat", - "msatoshi_total": 1000000000, - "total_msat": "1000000000msat", - "fee_base_msat": "1msat", - "fee_proportional_millionths": 10, - "dust_limit_satoshis": 546, - "dust_limit_msat": "546000msat", - "max_total_htlc_in_msat": "18446744073709551615msat", - "their_channel_reserve_satoshis": 10000, - "their_reserve_msat": "10000000msat", - "our_channel_reserve_satoshis": 10000, - "our_reserve_msat": "10000000msat", - "spendable_msatoshi": 0, - "spendable_msat": "0msat", - "receivable_msatoshi": 853257998, - "receivable_msat": "853257998msat", - "htlc_minimum_msat": 0, - "minimum_htlc_in_msat": "0msat", - "their_to_self_delay": 5, - "our_to_self_delay": 5, - "max_accepted_htlcs": 483, - "state_changes": [ - { - "timestamp": "2022-03-25T13:57:33.322Z", - "old_state": "CHANNELD_AWAITING_LOCKIN", - "new_state": "CHANNELD_NORMAL", - "cause": "remote", - "message": "Lockin complete" - } - ], - "status": [ - "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + "id": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", + "connected": true, + "netaddr": [ + "127.0.0.1:39152" ], - "in_payments_offered": 1, - "in_msatoshi_offered": 100002002, - "in_offered_msat": "100002002msat", - "in_payments_fulfilled": 0, - "in_msatoshi_fulfilled": 0, - "in_fulfilled_msat": "0msat", - "out_payments_offered": 0, - "out_msatoshi_offered": 0, - "out_offered_msat": "0msat", - "out_payments_fulfilled": 0, - "out_msatoshi_fulfilled": 0, - "out_fulfilled_msat": "0msat", - "htlcs": [ + "features": "8808226aa2", + "num_channels": 0, + "channels": [ { - "direction": "in", - "id": 0, - "msatoshi": 100002002, - "amount_msat": "100002002msat", - "expiry": 131, - "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", - "state": "RCVD_ADD_ACK_REVOCATION" + "state": "CHANNELD_NORMAL", + "scratch_txid": "fd4659658d235c20c81f96f7bc867c17abbfd20fcdd46c27eaad74ea52eaee90", + "last_tx_fee_msat": "14257000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x2x1", + "direction": 0, + "channel_id": "44b77a6d66ca54f0c365c84b13a95fbde462415a0549228baa25ee1bb1dfef66", + "funding_txid": "67efdfb11bee25aa8b2249055a4162e4bd5fa9134bc865c3f054ca666d7ab744", + "funding_outnum": 1, + "close_to_addr": "bcrt1q9tc6q49l6wrrtp8ul45rj92hsleehwwxty32zu", + "close_to": "00142af1a054bfd3863584fcfd6839155787f39bb9c6", + "private": false, + "opener": "remote", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "0msat", + "remote_msat": "1000000000msat", + "pushed_msat": "0msat", + "local_funds_msat": "0msat", + "remote_funds_msat": "0msat" + }, + "msatoshi_to_us": 0, + "to_us_msat": "0msat", + "msatoshi_to_us_min": 0, + "min_to_us_msat": "0msat", + "msatoshi_to_us_max": 0, + "max_to_us_msat": "0msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 0, + "spendable_msat": "0msat", + "receivable_msatoshi": 853257998, + "receivable_msat": "853257998msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.322Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "remote", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 1, + "in_msatoshi_offered": 100002002, + "in_offered_msat": "100002002msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 0, + "out_msatoshi_offered": 0, + "out_offered_msat": "0msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "in", + "id": 0, + "msatoshi": 100002002, + "amount_msat": "100002002msat", + "expiry": 131, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "RCVD_ADD_ACK_REVOCATION" + } + ] } ] - } - ] - }, - { - "id": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "connected": true, - "netaddr": [ - "127.0.0.1:38321" - ], - "features": "8808226aa2", - "num_channels": 0, - "channels": [ + }, { - "state": "CHANNELD_NORMAL", - "scratch_txid": "30530d3f522862773100b7600d8ea8921a5ee84df17a2317326f9aa2c4829326", - "last_tx_fee_msat": "16149000msat", - "feerate": { - "perkw": 11000, - "perkb": 44000 - }, - "owner": "channeld", - "short_channel_id": "103x1x0", - "direction": 0, - "channel_id": "006a2044fc72fa5c4a54c9fddbf208970a7b3b4fd2aaa70a96abba757c01769e", - "funding_txid": "9e76017c75baab960aa7aad24f3b7b0a9708f2dbfdc9544a5cfa72fc44206a00", - "funding_outnum": 0, - "close_to_addr": "bcrt1qhfmyce4ujce2pyugew2435tlwft6p6w4s3py6d", - "close_to": "0014ba764c66bc9632a09388cb9558d17f7257a0e9d5", - "private": false, - "opener": "local", - "features": [ - "option_static_remotekey", - "option_anchor_outputs" + "id": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + "connected": true, + "netaddr": [ + "127.0.0.1:38321" ], - "funding": { - "local_msat": "1000000000msat", - "remote_msat": "0msat", - "pushed_msat": "0msat", - "local_funds_msat": "0msat", - "remote_funds_msat": "0msat" - }, - "msatoshi_to_us": 1000000000, - "to_us_msat": "1000000000msat", - "msatoshi_to_us_min": 1000000000, - "min_to_us_msat": "1000000000msat", - "msatoshi_to_us_max": 1000000000, - "max_to_us_msat": "1000000000msat", - "msatoshi_total": 1000000000, - "total_msat": "1000000000msat", - "fee_base_msat": "1msat", - "fee_proportional_millionths": 10, - "dust_limit_satoshis": 546, - "dust_limit_msat": "546000msat", - "max_total_htlc_in_msat": "18446744073709551615msat", - "their_channel_reserve_satoshis": 10000, - "their_reserve_msat": "10000000msat", - "our_channel_reserve_satoshis": 10000, - "our_reserve_msat": "10000000msat", - "spendable_msatoshi": 749473998, - "spendable_msat": "749473998msat", - "receivable_msatoshi": 0, - "receivable_msat": "0msat", - "htlc_minimum_msat": 0, - "minimum_htlc_in_msat": "0msat", - "their_to_self_delay": 5, - "our_to_self_delay": 5, - "max_accepted_htlcs": 483, - "state_changes": [ + "features": "8808226aa2", + "num_channels": 0, + "channels": [ { - "timestamp": "2022-03-25T13:57:33.325Z", - "old_state": "CHANNELD_AWAITING_LOCKIN", - "new_state": "CHANNELD_NORMAL", - "cause": "user", - "message": "Lockin complete" - } - ], - "status": [ - "CHANNELD_NORMAL:Funding transaction locked. Channel announced." - ], - "in_payments_offered": 0, - "in_msatoshi_offered": 0, - "in_offered_msat": "0msat", - "in_payments_fulfilled": 0, - "in_msatoshi_fulfilled": 0, - "in_fulfilled_msat": "0msat", - "out_payments_offered": 2, - "out_msatoshi_offered": 200002002, - "out_offered_msat": "200002002msat", - "out_payments_fulfilled": 0, - "out_msatoshi_fulfilled": 0, - "out_fulfilled_msat": "0msat", - "htlcs": [ - { - "direction": "out", - "id": 1, - "msatoshi": 100001001, - "amount_msat": "100001001msat", - "expiry": 125, - "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", - "state": "SENT_ADD_ACK_REVOCATION" - }, - { - "direction": "out", - "id": 0, - "msatoshi": 100001001, - "amount_msat": "100001001msat", - "expiry": 124, - "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", - "state": "SENT_ADD_ACK_REVOCATION" + "state": "CHANNELD_NORMAL", + "scratch_txid": "30530d3f522862773100b7600d8ea8921a5ee84df17a2317326f9aa2c4829326", + "last_tx_fee_msat": "16149000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x1x0", + "direction": 0, + "channel_id": "006a2044fc72fa5c4a54c9fddbf208970a7b3b4fd2aaa70a96abba757c01769e", + "funding_txid": "9e76017c75baab960aa7aad24f3b7b0a9708f2dbfdc9544a5cfa72fc44206a00", + "funding_outnum": 0, + "close_to_addr": "bcrt1qhfmyce4ujce2pyugew2435tlwft6p6w4s3py6d", + "close_to": "0014ba764c66bc9632a09388cb9558d17f7257a0e9d5", + "private": false, + "opener": "local", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "1000000000msat", + "remote_msat": "0msat", + "pushed_msat": "0msat", + "local_funds_msat": "0msat", + "remote_funds_msat": "0msat" + }, + "msatoshi_to_us": 1000000000, + "to_us_msat": "1000000000msat", + "msatoshi_to_us_min": 1000000000, + "min_to_us_msat": "1000000000msat", + "msatoshi_to_us_max": 1000000000, + "max_to_us_msat": "1000000000msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 749473998, + "spendable_msat": "749473998msat", + "receivable_msatoshi": 0, + "receivable_msat": "0msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.325Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "user", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 0, + "in_msatoshi_offered": 0, + "in_offered_msat": "0msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 2, + "out_msatoshi_offered": 200002002, + "out_offered_msat": "200002002msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "out", + "id": 1, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 125, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + }, + { + "direction": "out", + "id": 0, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 124, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + } + ] } ] } ] - } - ] - }); - let u: cln_rpc::model::ListpeersResponse = serde_json::from_value(j).unwrap(); - let _g: ListpeersResponse = u.into(); + }); + let u: cln_rpc::model::responses::ListpeersResponse = + serde_json::from_value(j).unwrap(); + let _g: ListpeersResponse = u.into(); + } } } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 1a961d081729..963d71c839f9 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1530,6 +1530,38 @@ async fn list_pays( } +async fn list_htlcs( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListhtlcsRequest = req.into(); + debug!("Client asked for list_htlcs"); + trace!("list_htlcs request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListHtlcs(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListHtlcs: {:?}", e)))?; + match result { + Response::ListHtlcs(r) => { + trace!("list_htlcs response: {:?}", r); + Ok(tonic::Response::new(r.into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListHtlcs", + r + ) + )), + } + +} + async fn ping( &self, request: tonic::Request, @@ -1722,4 +1754,68 @@ async fn stop( } +async fn pre_approve_keysend( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::PreapprovekeysendRequest = req.into(); + debug!("Client asked for pre_approve_keysend"); + trace!("pre_approve_keysend request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::PreApproveKeysend(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method PreApproveKeysend: {:?}", e)))?; + match result { + Response::PreApproveKeysend(r) => { + trace!("pre_approve_keysend response: {:?}", r); + Ok(tonic::Response::new(r.into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call PreApproveKeysend", + r + ) + )), + } + +} + +async fn pre_approve_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::PreapproveinvoiceRequest = req.into(); + debug!("Client asked for pre_approve_invoice"); + trace!("pre_approve_invoice request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::PreApproveInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method PreApproveInvoice: {:?}", e)))?; + match result { + Response::PreApproveInvoice(r) => { + trace!("pre_approve_invoice response: {:?}", r); + Ok(tonic::Response::new(r.into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call PreApproveInvoice", + r + ) + )), + } + +} + } diff --git a/cln-grpc/src/test.rs b/cln-grpc/src/test.rs index 33ee185c296b..2b393a2b66f1 100644 --- a/cln-grpc/src/test.rs +++ b/cln-grpc/src/test.rs @@ -220,8 +220,9 @@ fn test_listpeers() { } ] }); - let u: cln_rpc::model::ListpeersResponse = serde_json::from_value(j.clone()).unwrap(); - let _l: ListpeersResponse = u.into(); + let u: cln_rpc::model::responses::ListpeersResponse = + serde_json::from_value(j.clone()).unwrap(); + let _l: cln_rpc::model::responses::ListpeersResponse = u.into(); //let u2: cln_rpc::model::ListpeersResponse = l.into(); //let j2 = serde_json::to_value(u2).unwrap(); println!("{}", j); @@ -246,7 +247,7 @@ fn test_getinfo() { "network": "regtest", "fees_collected_msat": "0msat", "lightning-dir": "/tmp/ltests-20irp76f/test_pay_variants_1/lightning-1/regtest", "our_features": {"init": "8808226aa2", "node": "80008808226aa2", "channel": "", "invoice": "024200"}}); - let u: cln_rpc::model::GetinfoResponse = serde_json::from_value(j.clone()).unwrap(); + let u: cln_rpc::model::responses::GetinfoResponse = serde_json::from_value(j.clone()).unwrap(); let _g: GetinfoResponse = u.into(); //let u2: cln_rpc::model::GetinfoResponse = g.into(); //let j2 = serde_json::to_value(u2).unwrap(); @@ -294,7 +295,7 @@ fn test_keysend() { extratlvs: None, }; - let u: cln_rpc::model::KeysendRequest = g.into(); + let u: cln_rpc::model::requests::KeysendRequest = g.into(); let _ser = serde_json::to_string(&u); let j = r#"{ @@ -309,12 +310,12 @@ fn test_keysend() { "payment_preimage": "e56c22b9ed85560b021e1577daad5742502d25c0c2f636b817f5c0c7580a66a8", "status": "complete" }"#; - let u: cln_rpc::model::KeysendResponse = serde_json::from_str(j).unwrap(); + let u: cln_rpc::model::responses::KeysendResponse = serde_json::from_str(j).unwrap(); let g: KeysendResponse = u.clone().into(); println!("{:?}", g); let v: serde_json::Value = serde_json::to_value(u.clone()).unwrap(); - let g: cln_rpc::model::KeysendResponse = u.into(); + let g: cln_rpc::model::responses::KeysendResponse = u.into(); let v2 = serde_json::to_value(g).unwrap(); assert_eq!(v, v2); } diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index b1b3865d2291..2462816f5c55 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-rpc" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "MIT" description = "An async RPC client for Core Lightning." diff --git a/cln-rpc/Makefile b/cln-rpc/Makefile index eff4fdbd41fc..3582eeb79b0a 100644 --- a/cln-rpc/Makefile +++ b/cln-rpc/Makefile @@ -4,11 +4,9 @@ cln-rpc-wrongdir: CLN_RPC_EXAMPLES := target/${RUST_PROFILE}/examples/cln-rpc-getinfo CLN_RPC_GENALL = cln-rpc/src/model.rs CLN_RPC_SOURCES = $(shell find cln-rpc -name *.rs) ${CLN_RPC_GENALL} -JSON_SCHEMAS = $(wildcard doc/schemas/*.request.json doc/schemas/*.schema.json) DEFAULT_TARGETS += $(CLN_RPC_EXAMPLES) $(CLN_RPC_GENALL) -$(CLN_RPC_GENALL): $(JSON_SCHEMAS) - PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py +MSGGEN_GENALL += $(CLN_RPC_GENALL) target/${RUST_PROFILE}/examples/cln-rpc-getinfo: $(shell find cln-rpc -name *.rs) cargo build ${CARGO_OPTS} --example cln-rpc-getinfo diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs index b9887d8e7253..35e32c2cf1d2 100644 --- a/cln-rpc/examples/getinfo.rs +++ b/cln-rpc/examples/getinfo.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, Context}; -use cln_rpc::{model::GetinfoRequest, ClnRpc, Request}; +use cln_rpc::{model::requests::GetinfoRequest, ClnRpc, Request}; use std::env::args; use std::path::Path; use tokio; diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index 51f2b53cae07..84e94abda9c0 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -159,7 +159,7 @@ mod test { let mut read = FramedRead::new(uds2, JsonCodec::default()); tokio::task::spawn(async move { - let _: GetinfoResponse = cln.call_typed(req).await.unwrap(); + let _: responses::GetinfoResponse = cln.call_typed(req).await.unwrap(); }); let read_req = dbg!(read.next().await.unwrap().unwrap()); diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 2e4a466ceebe..58096b20741f 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -9,8 +9,6 @@ //! this file was generated from use serde::{Deserialize, Serialize}; -pub use requests::*; -pub use responses::*; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "method", content = "params")] @@ -63,12 +61,15 @@ pub enum Request { GetRoute(requests::GetrouteRequest), ListForwards(requests::ListforwardsRequest), ListPays(requests::ListpaysRequest), + ListHtlcs(requests::ListhtlcsRequest), Ping(requests::PingRequest), SendCustomMsg(requests::SendcustommsgRequest), SetChannel(requests::SetchannelRequest), SignInvoice(requests::SigninvoiceRequest), SignMessage(requests::SignmessageRequest), Stop(requests::StopRequest), + PreApproveKeysend(requests::PreapprovekeysendRequest), + PreApproveInvoice(requests::PreapproveinvoiceRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -122,12 +123,15 @@ pub enum Response { GetRoute(responses::GetrouteResponse), ListForwards(responses::ListforwardsResponse), ListPays(responses::ListpaysResponse), + ListHtlcs(responses::ListhtlcsResponse), Ping(responses::PingResponse), SendCustomMsg(responses::SendcustommsgResponse), SetChannel(responses::SetchannelResponse), SignInvoice(responses::SigninvoiceResponse), SignMessage(responses::SignmessageResponse), Stop(responses::StopResponse), + PreApproveKeysend(responses::PreapprovekeysendResponse), + PreApproveInvoice(responses::PreapproveinvoiceResponse), } @@ -365,7 +369,7 @@ pub mod requests { type Response = super::responses::CreateinvoiceResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum DatastoreMode { #[serde(rename = "must-create")] MUST_CREATE, @@ -392,6 +396,19 @@ pub mod requests { } } } + + impl ToString for DatastoreMode { + fn to_string(&self) -> String { + match self { + DatastoreMode::MUST_CREATE => "MUST_CREATE", + DatastoreMode::MUST_REPLACE => "MUST_REPLACE", + DatastoreMode::CREATE_OR_REPLACE => "CREATE_OR_REPLACE", + DatastoreMode::MUST_APPEND => "MUST_APPEND", + DatastoreMode::CREATE_OR_APPEND => "CREATE_OR_APPEND", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DatastoreRequest { pub key: Vec, @@ -474,7 +491,7 @@ pub mod requests { type Response = super::responses::DelexpiredinvoiceResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum DelinvoiceStatus { #[serde(rename = "paid")] PAID, @@ -495,6 +512,17 @@ pub mod requests { } } } + + impl ToString for DelinvoiceStatus { + fn to_string(&self) -> String { + match self { + DelinvoiceStatus::PAID => "PAID", + DelinvoiceStatus::EXPIRED => "EXPIRED", + DelinvoiceStatus::UNPAID => "UNPAID", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelinvoiceRequest { pub label: String, @@ -557,6 +585,34 @@ pub mod requests { type Response = super::responses::ListdatastoreResponse; } + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] + pub enum ListinvoicesIndex { + #[serde(rename = "created")] + CREATED, + #[serde(rename = "updated")] + UPDATED, + } + + impl TryFrom for ListinvoicesIndex { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListinvoicesIndex::CREATED), + 1 => Ok(ListinvoicesIndex::UPDATED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListinvoicesIndex", o)), + } + } + } + + impl ToString for ListinvoicesIndex { + fn to_string(&self) -> String { + match self { + ListinvoicesIndex::CREATED => "CREATED", + ListinvoicesIndex::UPDATED => "UPDATED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListinvoicesRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -567,6 +623,12 @@ pub mod requests { pub payment_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] pub offer_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub start: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, } impl From for Request { @@ -619,7 +681,7 @@ pub mod requests { type Response = super::responses::SendonionResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListsendpaysStatus { #[serde(rename = "pending")] PENDING, @@ -640,6 +702,17 @@ pub mod requests { } } } + + impl ToString for ListsendpaysStatus { + fn to_string(&self) -> String { + match self { + ListsendpaysStatus::PENDING => "PENDING", + ListsendpaysStatus::COMPLETE => "COMPLETE", + ListsendpaysStatus::FAILED => "FAILED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListsendpaysRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -781,10 +854,12 @@ pub mod requests { type Response = super::responses::WaitsendpayResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum NewaddrAddresstype { #[serde(rename = "bech32")] BECH32, + #[serde(rename = "p2tr")] + P2TR, #[serde(rename = "all")] ALL, } @@ -794,11 +869,23 @@ pub mod requests { fn try_from(c: i32) -> Result { match c { 0 => Ok(NewaddrAddresstype::BECH32), - 1 => Ok(NewaddrAddresstype::ALL), + 1 => Ok(NewaddrAddresstype::P2TR), + 2 => Ok(NewaddrAddresstype::ALL), o => Err(anyhow::anyhow!("Unknown variant {} for enum NewaddrAddresstype", o)), } } } + + impl ToString for NewaddrAddresstype { + fn to_string(&self) -> String { + match self { + NewaddrAddresstype::BECH32 => "BECH32", + NewaddrAddresstype::P2TR => "P2TR", + NewaddrAddresstype::ALL => "ALL", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewaddrRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -883,6 +970,10 @@ pub mod requests { pub min_witness_weight: Option, #[serde(skip_serializing_if = "Option::is_none")] pub excess_as_change: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub nonwrapped: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub opening_anchor_channel: Option, } impl From for Request { @@ -945,6 +1036,8 @@ pub mod requests { pub min_witness_weight: Option, #[serde(skip_serializing_if = "Option::is_none")] pub excess_as_change: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub opening_anchor_channel: Option, } impl From for Request { @@ -1089,7 +1182,7 @@ pub mod requests { type Response = super::responses::DisconnectResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum FeeratesStyle { #[serde(rename = "perkb")] PERKB, @@ -1107,6 +1200,16 @@ pub mod requests { } } } + + impl ToString for FeeratesStyle { + fn to_string(&self) -> String { + match self { + FeeratesStyle::PERKB => "PERKB", + FeeratesStyle::PERKW => "PERKW", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FeeratesRequest { // Path `Feerates.style` @@ -1186,7 +1289,7 @@ pub mod requests { type Response = super::responses::GetrouteResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListforwardsStatus { #[serde(rename = "offered")] OFFERED, @@ -1210,6 +1313,18 @@ pub mod requests { } } } + + impl ToString for ListforwardsStatus { + fn to_string(&self) -> String { + match self { + ListforwardsStatus::OFFERED => "OFFERED", + ListforwardsStatus::SETTLED => "SETTLED", + ListforwardsStatus::LOCAL_FAILED => "LOCAL_FAILED", + ListforwardsStatus::FAILED => "FAILED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListforwardsRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -1230,7 +1345,7 @@ pub mod requests { type Response = super::responses::ListforwardsResponse; } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpaysStatus { #[serde(rename = "pending")] PENDING, @@ -1251,6 +1366,17 @@ pub mod requests { } } } + + impl ToString for ListpaysStatus { + fn to_string(&self) -> String { + match self { + ListpaysStatus::PENDING => "PENDING", + ListpaysStatus::COMPLETE => "COMPLETE", + ListpaysStatus::FAILED => "FAILED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpaysRequest { #[serde(skip_serializing_if = "Option::is_none")] @@ -1271,6 +1397,22 @@ pub mod requests { type Response = super::responses::ListpaysResponse; } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListhtlcsRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + } + + impl From for Request { + fn from(r: ListhtlcsRequest) -> Self { + Request::ListHtlcs(r) + } + } + + impl IntoRequest for ListhtlcsRequest { + type Response = super::responses::ListhtlcsResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PingRequest { pub id: PublicKey, @@ -1319,6 +1461,8 @@ pub mod requests { pub htlcmax: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enforcedelay: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ignorefeelimits: Option, } impl From for Request { @@ -1375,6 +1519,42 @@ pub mod requests { type Response = super::responses::StopResponse; } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PreapprovekeysendRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub payment_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + } + + impl From for Request { + fn from(r: PreapprovekeysendRequest) -> Self { + Request::PreApproveKeysend(r) + } + } + + impl IntoRequest for PreapprovekeysendRequest { + type Response = super::responses::PreapprovekeysendResponse; + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PreapproveinvoiceRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + } + + impl From for Request { + fn from(r: PreapproveinvoiceRequest) -> Self { + Request::PreApproveInvoice(r) + } + } + + impl IntoRequest for PreapproveinvoiceRequest { + type Response = super::responses::PreapproveinvoiceResponse; + } + } @@ -1393,8 +1573,8 @@ pub mod responses { pub invoice: String, } - /// Type of connection - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + /// Type of connection (until 23.08, `websocket` was also allowed) + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum GetinfoAddressType { #[serde(rename = "dns")] DNS, @@ -1406,8 +1586,6 @@ pub mod responses { TORV2, #[serde(rename = "torv3")] TORV3, - #[serde(rename = "websocket")] - WEBSOCKET, } impl TryFrom for GetinfoAddressType { @@ -1419,11 +1597,23 @@ pub mod responses { 2 => Ok(GetinfoAddressType::IPV6), 3 => Ok(GetinfoAddressType::TORV2), 4 => Ok(GetinfoAddressType::TORV3), - 5 => Ok(GetinfoAddressType::WEBSOCKET), o => Err(anyhow::anyhow!("Unknown variant {} for enum GetinfoAddressType", o)), } } } + + impl ToString for GetinfoAddressType { + fn to_string(&self) -> String { + match self { + GetinfoAddressType::DNS => "DNS", + GetinfoAddressType::IPV4 => "IPV4", + GetinfoAddressType::IPV6 => "IPV6", + GetinfoAddressType::TORV2 => "TORV2", + GetinfoAddressType::TORV3 => "TORV3", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoAddress { // Path `Getinfo.address[].type` @@ -1435,10 +1625,12 @@ pub mod responses { } /// Type of connection - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum GetinfoBindingType { #[serde(rename = "local socket")] LOCAL_SOCKET, + #[serde(rename = "websocket")] + WEBSOCKET, #[serde(rename = "ipv4")] IPV4, #[serde(rename = "ipv6")] @@ -1454,14 +1646,29 @@ pub mod responses { fn try_from(c: i32) -> Result { match c { 0 => Ok(GetinfoBindingType::LOCAL_SOCKET), - 1 => Ok(GetinfoBindingType::IPV4), - 2 => Ok(GetinfoBindingType::IPV6), - 3 => Ok(GetinfoBindingType::TORV2), - 4 => Ok(GetinfoBindingType::TORV3), + 1 => Ok(GetinfoBindingType::WEBSOCKET), + 2 => Ok(GetinfoBindingType::IPV4), + 3 => Ok(GetinfoBindingType::IPV6), + 4 => Ok(GetinfoBindingType::TORV2), + 5 => Ok(GetinfoBindingType::TORV3), o => Err(anyhow::anyhow!("Unknown variant {} for enum GetinfoBindingType", o)), } } } + + impl ToString for GetinfoBindingType { + fn to_string(&self) -> String { + match self { + GetinfoBindingType::LOCAL_SOCKET => "LOCAL_SOCKET", + GetinfoBindingType::WEBSOCKET => "WEBSOCKET", + GetinfoBindingType::IPV4 => "IPV4", + GetinfoBindingType::IPV6 => "IPV6", + GetinfoBindingType::TORV2 => "TORV2", + GetinfoBindingType::TORV3 => "TORV3", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoBinding { // Path `Getinfo.binding[].type` @@ -1478,7 +1685,8 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoResponse { pub id: PublicKey, - pub alias: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub alias: Option, pub color: String, pub num_peers: u32, pub num_pending_channels: u32, @@ -1492,7 +1700,8 @@ pub mod responses { pub blockheight: u32, pub network: String, pub fees_collected_msat: Amount, - pub address: Vec, + #[serde(skip_serializing_if = "crate::is_none_or_empty")] + pub address: Option>, #[serde(skip_serializing_if = "crate::is_none_or_empty")] pub binding: Option>, #[serde(skip_serializing_if = "Option::is_none")] @@ -1512,7 +1721,7 @@ pub mod responses { } } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpeersPeersLogType { #[serde(rename = "SKIPPED")] SKIPPED, @@ -1545,6 +1754,21 @@ pub mod responses { } } } + + impl ToString for ListpeersPeersLogType { + fn to_string(&self) -> String { + match self { + ListpeersPeersLogType::SKIPPED => "SKIPPED", + ListpeersPeersLogType::BROKEN => "BROKEN", + ListpeersPeersLogType::UNUSUAL => "UNUSUAL", + ListpeersPeersLogType::INFO => "INFO", + ListpeersPeersLogType::DEBUG => "DEBUG", + ListpeersPeersLogType::IO_IN => "IO_IN", + ListpeersPeersLogType::IO_OUT => "IO_OUT", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersLog { // Path `ListPeers.peers[].log[].type` @@ -1565,7 +1789,7 @@ pub mod responses { } /// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpeersPeersChannelsState { #[serde(rename = "OPENINGD")] OPENINGD, @@ -1610,6 +1834,25 @@ pub mod responses { } } } + + impl ToString for ListpeersPeersChannelsState { + fn to_string(&self) -> String { + match self { + ListpeersPeersChannelsState::OPENINGD => "OPENINGD", + ListpeersPeersChannelsState::CHANNELD_AWAITING_LOCKIN => "CHANNELD_AWAITING_LOCKIN", + ListpeersPeersChannelsState::CHANNELD_NORMAL => "CHANNELD_NORMAL", + ListpeersPeersChannelsState::CHANNELD_SHUTTING_DOWN => "CHANNELD_SHUTTING_DOWN", + ListpeersPeersChannelsState::CLOSINGD_SIGEXCHANGE => "CLOSINGD_SIGEXCHANGE", + ListpeersPeersChannelsState::CLOSINGD_COMPLETE => "CLOSINGD_COMPLETE", + ListpeersPeersChannelsState::AWAITING_UNILATERAL => "AWAITING_UNILATERAL", + ListpeersPeersChannelsState::FUNDING_SPEND_SEEN => "FUNDING_SPEND_SEEN", + ListpeersPeersChannelsState::ONCHAIN => "ONCHAIN", + ListpeersPeersChannelsState::DUALOPEND_OPEN_INIT => "DUALOPEND_OPEN_INIT", + ListpeersPeersChannelsState::DUALOPEND_AWAITING_LOCKIN => "DUALOPEND_AWAITING_LOCKIN", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersChannelsFeerate { pub perkw: u32, @@ -1623,6 +1866,8 @@ pub mod responses { pub feerate: String, pub total_funding_msat: Amount, pub our_funding_msat: Amount, + #[serde(skip_serializing_if = "Option::is_none")] + pub splice_amount: Option, pub scratch_txid: String, } @@ -1647,7 +1892,7 @@ pub mod responses { } /// Whether it came from peer, or is going to peer - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpeersPeersChannelsHtlcsDirection { #[serde(rename = "in")] IN, @@ -1665,6 +1910,16 @@ pub mod responses { } } } + + impl ToString for ListpeersPeersChannelsHtlcsDirection { + fn to_string(&self) -> String { + match self { + ListpeersPeersChannelsHtlcsDirection::IN => "IN", + ListpeersPeersChannelsHtlcsDirection::OUT => "OUT", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersChannelsHtlcs { // Path `ListPeers.peers[].channels[].htlcs[].direction` @@ -1817,7 +2072,7 @@ pub mod responses { } } - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListfundsOutputsStatus { #[serde(rename = "unconfirmed")] UNCONFIRMED, @@ -1841,6 +2096,18 @@ pub mod responses { } } } + + impl ToString for ListfundsOutputsStatus { + fn to_string(&self) -> String { + match self { + ListfundsOutputsStatus::UNCONFIRMED => "UNCONFIRMED", + ListfundsOutputsStatus::CONFIRMED => "CONFIRMED", + ListfundsOutputsStatus::SPENT => "SPENT", + ListfundsOutputsStatus::IMMATURE => "IMMATURE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsOutputs { pub txid: String, @@ -1892,7 +2159,7 @@ pub mod responses { } /// status of the payment (could be complete if already sent previously) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum SendpayStatus { #[serde(rename = "pending")] PENDING, @@ -1910,6 +2177,16 @@ pub mod responses { } } } + + impl ToString for SendpayStatus { + fn to_string(&self) -> String { + match self { + SendpayStatus::PENDING => "PENDING", + SendpayStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpayResponse { pub id: u64, @@ -2041,7 +2318,7 @@ pub mod responses { } /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum CloseType { #[serde(rename = "mutual")] MUTUAL, @@ -2062,6 +2339,17 @@ pub mod responses { } } } + + impl ToString for CloseType { + fn to_string(&self) -> String { + match self { + CloseType::MUTUAL => "MUTUAL", + CloseType::UNILATERAL => "UNILATERAL", + CloseType::UNOPENED => "UNOPENED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CloseResponse { // Path `Close.type` @@ -2085,7 +2373,7 @@ pub mod responses { } /// Whether they initiated connection or we did - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ConnectDirection { #[serde(rename = "in")] IN, @@ -2103,8 +2391,18 @@ pub mod responses { } } } + + impl ToString for ConnectDirection { + fn to_string(&self) -> String { + match self { + ConnectDirection::IN => "IN", + ConnectDirection::OUT => "OUT", + }.to_string() + } + } + /// Type of connection (*torv2*/*torv3* only if **direction** is *out*) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ConnectAddressType { #[serde(rename = "local socket")] LOCAL_SOCKET, @@ -2131,6 +2429,19 @@ pub mod responses { } } } + + impl ToString for ConnectAddressType { + fn to_string(&self) -> String { + match self { + ConnectAddressType::LOCAL_SOCKET => "LOCAL_SOCKET", + ConnectAddressType::IPV4 => "IPV4", + ConnectAddressType::IPV6 => "IPV6", + ConnectAddressType::TORV2 => "TORV2", + ConnectAddressType::TORV3 => "TORV3", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ConnectAddress { // Path `Connect.address.type` @@ -2165,7 +2476,7 @@ pub mod responses { } /// Whether it has been paid, or can no longer be paid - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum CreateinvoiceStatus { #[serde(rename = "paid")] PAID, @@ -2186,6 +2497,17 @@ pub mod responses { } } } + + impl ToString for CreateinvoiceStatus { + fn to_string(&self) -> String { + match self { + CreateinvoiceStatus::PAID => "PAID", + CreateinvoiceStatus::EXPIRED => "EXPIRED", + CreateinvoiceStatus::UNPAID => "UNPAID", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateinvoiceResponse { pub label: String, @@ -2201,6 +2523,8 @@ pub mod responses { pub description: String, pub expires_at: u64, #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub pay_index: Option, #[serde(skip_serializing_if = "Option::is_none")] pub amount_received_msat: Option, @@ -2302,7 +2626,7 @@ pub mod responses { } /// State of invoice - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum DelinvoiceStatus { #[serde(rename = "paid")] PAID, @@ -2323,6 +2647,17 @@ pub mod responses { } } } + + impl ToString for DelinvoiceStatus { + fn to_string(&self) -> String { + match self { + DelinvoiceStatus::PAID => "PAID", + DelinvoiceStatus::EXPIRED => "EXPIRED", + DelinvoiceStatus::UNPAID => "UNPAID", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelinvoiceResponse { pub label: String, @@ -2335,6 +2670,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, pub payment_hash: Sha256, + #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_index: Option, // Path `DelInvoice.status` pub status: DelinvoiceStatus, pub expires_at: u64, @@ -2362,6 +2701,8 @@ pub mod responses { pub payment_secret: Secret, pub expires_at: u64, #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub warning_capacity: Option, #[serde(skip_serializing_if = "Option::is_none")] pub warning_offline: Option, @@ -2412,7 +2753,7 @@ pub mod responses { } /// Whether it's paid, unpaid or unpayable - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListinvoicesInvoicesStatus { #[serde(rename = "unpaid")] UNPAID, @@ -2433,6 +2774,17 @@ pub mod responses { } } } + + impl ToString for ListinvoicesInvoicesStatus { + fn to_string(&self) -> String { + match self { + ListinvoicesInvoicesStatus::UNPAID => "UNPAID", + ListinvoicesInvoicesStatus::PAID => "PAID", + ListinvoicesInvoicesStatus::EXPIRED => "EXPIRED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListinvoicesInvoices { pub label: String, @@ -2453,6 +2805,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub invreq_payer_note: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub pay_index: Option, #[serde(skip_serializing_if = "Option::is_none")] pub amount_received_msat: Option, @@ -2479,7 +2835,7 @@ pub mod responses { } /// status of the payment (could be complete if already sent previously) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum SendonionStatus { #[serde(rename = "pending")] PENDING, @@ -2497,6 +2853,16 @@ pub mod responses { } } } + + impl ToString for SendonionStatus { + fn to_string(&self) -> String { + match self { + SendonionStatus::PENDING => "PENDING", + SendonionStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendonionResponse { pub id: u64, @@ -2535,7 +2901,7 @@ pub mod responses { } /// status of the payment - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListsendpaysPaymentsStatus { #[serde(rename = "pending")] PENDING, @@ -2556,6 +2922,17 @@ pub mod responses { } } } + + impl ToString for ListsendpaysPaymentsStatus { + fn to_string(&self) -> String { + match self { + ListsendpaysPaymentsStatus::PENDING => "PENDING", + ListsendpaysPaymentsStatus::FAILED => "FAILED", + ListsendpaysPaymentsStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListsendpaysPayments { pub id: u64, @@ -2601,119 +2978,19 @@ pub mod responses { } } - /// the purpose of this input (*EXPERIMENTAL_FEATURES* only) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - pub enum ListtransactionsTransactionsInputsType { - #[serde(rename = "theirs")] - THEIRS, - #[serde(rename = "deposit")] - DEPOSIT, - #[serde(rename = "withdraw")] - WITHDRAW, - #[serde(rename = "channel_funding")] - CHANNEL_FUNDING, - #[serde(rename = "channel_mutual_close")] - CHANNEL_MUTUAL_CLOSE, - #[serde(rename = "channel_unilateral_close")] - CHANNEL_UNILATERAL_CLOSE, - #[serde(rename = "channel_sweep")] - CHANNEL_SWEEP, - #[serde(rename = "channel_htlc_success")] - CHANNEL_HTLC_SUCCESS, - #[serde(rename = "channel_htlc_timeout")] - CHANNEL_HTLC_TIMEOUT, - #[serde(rename = "channel_penalty")] - CHANNEL_PENALTY, - #[serde(rename = "channel_unilateral_cheat")] - CHANNEL_UNILATERAL_CHEAT, - } - - impl TryFrom for ListtransactionsTransactionsInputsType { - type Error = anyhow::Error; - fn try_from(c: i32) -> Result { - match c { - 0 => Ok(ListtransactionsTransactionsInputsType::THEIRS), - 1 => Ok(ListtransactionsTransactionsInputsType::DEPOSIT), - 2 => Ok(ListtransactionsTransactionsInputsType::WITHDRAW), - 3 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_FUNDING), - 4 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_MUTUAL_CLOSE), - 5 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_UNILATERAL_CLOSE), - 6 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_SWEEP), - 7 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_HTLC_SUCCESS), - 8 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_HTLC_TIMEOUT), - 9 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_PENALTY), - 10 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_UNILATERAL_CHEAT), - o => Err(anyhow::anyhow!("Unknown variant {} for enum ListtransactionsTransactionsInputsType", o)), - } - } - } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListtransactionsTransactionsInputs { pub txid: String, pub index: u32, pub sequence: u32, - #[serde(skip_serializing_if = "Option::is_none")] - pub item_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub channel: Option, - } - - /// the purpose of this output (*EXPERIMENTAL_FEATURES* only) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - pub enum ListtransactionsTransactionsOutputsType { - #[serde(rename = "theirs")] - THEIRS, - #[serde(rename = "deposit")] - DEPOSIT, - #[serde(rename = "withdraw")] - WITHDRAW, - #[serde(rename = "channel_funding")] - CHANNEL_FUNDING, - #[serde(rename = "channel_mutual_close")] - CHANNEL_MUTUAL_CLOSE, - #[serde(rename = "channel_unilateral_close")] - CHANNEL_UNILATERAL_CLOSE, - #[serde(rename = "channel_sweep")] - CHANNEL_SWEEP, - #[serde(rename = "channel_htlc_success")] - CHANNEL_HTLC_SUCCESS, - #[serde(rename = "channel_htlc_timeout")] - CHANNEL_HTLC_TIMEOUT, - #[serde(rename = "channel_penalty")] - CHANNEL_PENALTY, - #[serde(rename = "channel_unilateral_cheat")] - CHANNEL_UNILATERAL_CHEAT, - } - - impl TryFrom for ListtransactionsTransactionsOutputsType { - type Error = anyhow::Error; - fn try_from(c: i32) -> Result { - match c { - 0 => Ok(ListtransactionsTransactionsOutputsType::THEIRS), - 1 => Ok(ListtransactionsTransactionsOutputsType::DEPOSIT), - 2 => Ok(ListtransactionsTransactionsOutputsType::WITHDRAW), - 3 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_FUNDING), - 4 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_MUTUAL_CLOSE), - 5 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_UNILATERAL_CLOSE), - 6 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_SWEEP), - 7 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_HTLC_SUCCESS), - 8 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_HTLC_TIMEOUT), - 9 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_PENALTY), - 10 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_UNILATERAL_CHEAT), - o => Err(anyhow::anyhow!("Unknown variant {} for enum ListtransactionsTransactionsOutputsType", o)), - } - } } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListtransactionsTransactionsOutputs { pub index: u32, pub amount_msat: Amount, #[serde(rename = "scriptPubKey")] pub script_pub_key: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub item_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub channel: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2745,7 +3022,7 @@ pub mod responses { } /// status of payment - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum PayStatus { #[serde(rename = "complete")] COMPLETE, @@ -2766,6 +3043,17 @@ pub mod responses { } } } + + impl ToString for PayStatus { + fn to_string(&self) -> String { + match self { + PayStatus::COMPLETE => "COMPLETE", + PayStatus::PENDING => "PENDING", + PayStatus::FAILED => "FAILED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PayResponse { pub payment_preimage: Secret, @@ -2793,8 +3081,8 @@ pub mod responses { } } - /// Type of connection - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + /// Type of connection (until 23.08, `websocket` was also allowed) + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListnodesNodesAddressesType { #[serde(rename = "dns")] DNS, @@ -2806,8 +3094,6 @@ pub mod responses { TORV2, #[serde(rename = "torv3")] TORV3, - #[serde(rename = "websocket")] - WEBSOCKET, } impl TryFrom for ListnodesNodesAddressesType { @@ -2819,11 +3105,23 @@ pub mod responses { 2 => Ok(ListnodesNodesAddressesType::IPV6), 3 => Ok(ListnodesNodesAddressesType::TORV2), 4 => Ok(ListnodesNodesAddressesType::TORV3), - 5 => Ok(ListnodesNodesAddressesType::WEBSOCKET), o => Err(anyhow::anyhow!("Unknown variant {} for enum ListnodesNodesAddressesType", o)), } } } + + impl ToString for ListnodesNodesAddressesType { + fn to_string(&self) -> String { + match self { + ListnodesNodesAddressesType::DNS => "DNS", + ListnodesNodesAddressesType::IPV4 => "IPV4", + ListnodesNodesAddressesType::IPV6 => "IPV6", + ListnodesNodesAddressesType::TORV2 => "TORV2", + ListnodesNodesAddressesType::TORV3 => "TORV3", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesNodesAddresses { // Path `ListNodes.nodes[].addresses[].type` @@ -2866,7 +3164,7 @@ pub mod responses { } /// Whether it's paid or expired - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum WaitanyinvoiceStatus { #[serde(rename = "paid")] PAID, @@ -2884,6 +3182,16 @@ pub mod responses { } } } + + impl ToString for WaitanyinvoiceStatus { + fn to_string(&self) -> String { + match self { + WaitanyinvoiceStatus::PAID => "PAID", + WaitanyinvoiceStatus::EXPIRED => "EXPIRED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitanyinvoiceResponse { pub label: String, @@ -2899,6 +3207,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub pay_index: Option, #[serde(skip_serializing_if = "Option::is_none")] pub amount_received_msat: Option, @@ -2920,7 +3232,7 @@ pub mod responses { } /// Whether it's paid or expired - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum WaitinvoiceStatus { #[serde(rename = "paid")] PAID, @@ -2938,6 +3250,16 @@ pub mod responses { } } } + + impl ToString for WaitinvoiceStatus { + fn to_string(&self) -> String { + match self { + WaitinvoiceStatus::PAID => "PAID", + WaitinvoiceStatus::EXPIRED => "EXPIRED", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitinvoiceResponse { pub label: String, @@ -2953,6 +3275,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub created_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub updated_index: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub pay_index: Option, #[serde(skip_serializing_if = "Option::is_none")] pub amount_received_msat: Option, @@ -2974,7 +3300,7 @@ pub mod responses { } /// status of the payment - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum WaitsendpayStatus { #[serde(rename = "complete")] COMPLETE, @@ -2989,6 +3315,15 @@ pub mod responses { } } } + + impl ToString for WaitsendpayStatus { + fn to_string(&self) -> String { + match self { + WaitsendpayStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitsendpayResponse { pub id: u64, @@ -3030,6 +3365,8 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewaddrResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub p2tr: Option, #[serde(skip_serializing_if = "Option::is_none")] pub bech32: Option, #[deprecated] @@ -3068,7 +3405,7 @@ pub mod responses { } /// status of payment - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum KeysendStatus { #[serde(rename = "complete")] COMPLETE, @@ -3083,6 +3420,15 @@ pub mod responses { } } } + + impl ToString for KeysendStatus { + fn to_string(&self) -> String { + match self { + KeysendStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct KeysendResponse { pub payment_preimage: Secret, @@ -3261,7 +3607,7 @@ pub mod responses { } /// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpeerchannelsChannelsState { #[serde(rename = "OPENINGD")] OPENINGD, @@ -3285,6 +3631,8 @@ pub mod responses { DUALOPEND_OPEN_INIT, #[serde(rename = "DUALOPEND_AWAITING_LOCKIN")] DUALOPEND_AWAITING_LOCKIN, + #[serde(rename = "CHANNELD_AWAITING_SPLICE")] + CHANNELD_AWAITING_SPLICE, } impl TryFrom for ListpeerchannelsChannelsState { @@ -3302,10 +3650,31 @@ pub mod responses { 8 => Ok(ListpeerchannelsChannelsState::ONCHAIN), 9 => Ok(ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT), 10 => Ok(ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN), + 11 => Ok(ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE), o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeerchannelsChannelsState", o)), } } } + + impl ToString for ListpeerchannelsChannelsState { + fn to_string(&self) -> String { + match self { + ListpeerchannelsChannelsState::OPENINGD => "OPENINGD", + ListpeerchannelsChannelsState::CHANNELD_AWAITING_LOCKIN => "CHANNELD_AWAITING_LOCKIN", + ListpeerchannelsChannelsState::CHANNELD_NORMAL => "CHANNELD_NORMAL", + ListpeerchannelsChannelsState::CHANNELD_SHUTTING_DOWN => "CHANNELD_SHUTTING_DOWN", + ListpeerchannelsChannelsState::CLOSINGD_SIGEXCHANGE => "CLOSINGD_SIGEXCHANGE", + ListpeerchannelsChannelsState::CLOSINGD_COMPLETE => "CLOSINGD_COMPLETE", + ListpeerchannelsChannelsState::AWAITING_UNILATERAL => "AWAITING_UNILATERAL", + ListpeerchannelsChannelsState::FUNDING_SPEND_SEEN => "FUNDING_SPEND_SEEN", + ListpeerchannelsChannelsState::ONCHAIN => "ONCHAIN", + ListpeerchannelsChannelsState::DUALOPEND_OPEN_INIT => "DUALOPEND_OPEN_INIT", + ListpeerchannelsChannelsState::DUALOPEND_AWAITING_LOCKIN => "DUALOPEND_AWAITING_LOCKIN", + ListpeerchannelsChannelsState::CHANNELD_AWAITING_SPLICE => "CHANNELD_AWAITING_SPLICE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeerchannelsChannelsFeerate { #[serde(skip_serializing_if = "Option::is_none")] @@ -3325,6 +3694,8 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub total_funding_msat: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub splice_amount: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub our_funding_msat: Option, #[serde(skip_serializing_if = "Option::is_none")] pub scratch_txid: Option, @@ -3353,7 +3724,7 @@ pub mod responses { } /// Whether it came from peer, or is going to peer - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpeerchannelsChannelsHtlcsDirection { #[serde(rename = "in")] IN, @@ -3371,6 +3742,16 @@ pub mod responses { } } } + + impl ToString for ListpeerchannelsChannelsHtlcsDirection { + fn to_string(&self) -> String { + match self { + ListpeerchannelsChannelsHtlcsDirection::IN => "IN", + ListpeerchannelsChannelsHtlcsDirection::OUT => "OUT", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeerchannelsChannelsHtlcs { #[serde(skip_serializing_if = "Option::is_none")] @@ -3402,6 +3783,8 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub scratch_txid: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub ignore_fee_limits: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub feerate: Option, #[serde(skip_serializing_if = "Option::is_none")] pub owner: Option, @@ -3521,7 +3904,7 @@ pub mod responses { } /// What caused the channel to close - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListclosedchannelsClosedchannelsClose_cause { #[serde(rename = "unknown")] UNKNOWN, @@ -3551,6 +3934,20 @@ pub mod responses { } } } + + impl ToString for ListclosedchannelsClosedchannelsClose_cause { + fn to_string(&self) -> String { + match self { + ListclosedchannelsClosedchannelsClose_cause::UNKNOWN => "UNKNOWN", + ListclosedchannelsClosedchannelsClose_cause::LOCAL => "LOCAL", + ListclosedchannelsClosedchannelsClose_cause::USER => "USER", + ListclosedchannelsClosedchannelsClose_cause::REMOTE => "REMOTE", + ListclosedchannelsClosedchannelsClose_cause::PROTOCOL => "PROTOCOL", + ListclosedchannelsClosedchannelsClose_cause::ONCHAIN => "ONCHAIN", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListclosedchannelsClosedchannels { #[serde(skip_serializing_if = "Option::is_none")] @@ -3606,7 +4003,7 @@ pub mod responses { } /// the address type (if known) - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum DecodepayFallbacksType { #[serde(rename = "P2PKH")] P2PKH, @@ -3630,6 +4027,18 @@ pub mod responses { } } } + + impl ToString for DecodepayFallbacksType { + fn to_string(&self) -> String { + match self { + DecodepayFallbacksType::P2PKH => "P2PKH", + DecodepayFallbacksType::P2SH => "P2SH", + DecodepayFallbacksType::P2WPKH => "P2WPKH", + DecodepayFallbacksType::P2WSH => "P2WSH", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DecodepayFallbacks { // Path `DecodePay.fallbacks[].type` @@ -3685,7 +4094,7 @@ pub mod responses { } /// what kind of object it decoded to - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum DecodeType { #[serde(rename = "bolt12 offer")] BOLT12_OFFER, @@ -3712,6 +4121,19 @@ pub mod responses { } } } + + impl ToString for DecodeType { + fn to_string(&self) -> String { + match self { + DecodeType::BOLT12_OFFER => "BOLT12_OFFER", + DecodeType::BOLT12_INVOICE => "BOLT12_INVOICE", + DecodeType::BOLT12_INVOICE_REQUEST => "BOLT12_INVOICE_REQUEST", + DecodeType::BOLT11_INVOICE => "BOLT11_INVOICE", + DecodeType::RUNE => "RUNE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DecodeOffer_paths { pub first_node_id: PublicKey, @@ -3936,6 +4358,8 @@ pub mod responses { pub mutual_close: Option, #[serde(skip_serializing_if = "Option::is_none")] pub unilateral_close: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub unilateral_anchor_close: Option, #[deprecated] #[serde(skip_serializing_if = "Option::is_none")] pub delayed_to_us: Option, @@ -3970,6 +4394,8 @@ pub mod responses { pub mutual_close: Option, #[serde(skip_serializing_if = "Option::is_none")] pub unilateral_close: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub unilateral_anchor_close: Option, #[deprecated] #[serde(skip_serializing_if = "Option::is_none")] pub delayed_to_us: Option, @@ -3985,6 +4411,8 @@ pub mod responses { pub opening_channel_satoshis: u64, pub mutual_close_satoshis: u64, pub unilateral_close_satoshis: u64, + #[serde(skip_serializing_if = "Option::is_none")] + pub unilateral_close_nonanchor_satoshis: Option, pub htlc_timeout_satoshis: u64, pub htlc_success_satoshis: u64, } @@ -4036,7 +4464,7 @@ pub mod responses { } /// The features understood by the destination node - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum GetrouteRouteStyle { #[serde(rename = "tlv")] TLV, @@ -4051,6 +4479,15 @@ pub mod responses { } } } + + impl ToString for GetrouteRouteStyle { + fn to_string(&self) -> String { + match self { + GetrouteRouteStyle::TLV => "TLV", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetrouteRoute { pub id: PublicKey, @@ -4079,7 +4516,7 @@ pub mod responses { } /// still ongoing, completed, failed locally, or failed after forwarding - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListforwardsForwardsStatus { #[serde(rename = "offered")] OFFERED, @@ -4103,8 +4540,20 @@ pub mod responses { } } } + + impl ToString for ListforwardsForwardsStatus { + fn to_string(&self) -> String { + match self { + ListforwardsForwardsStatus::OFFERED => "OFFERED", + ListforwardsForwardsStatus::SETTLED => "SETTLED", + ListforwardsForwardsStatus::LOCAL_FAILED => "LOCAL_FAILED", + ListforwardsForwardsStatus::FAILED => "FAILED", + }.to_string() + } + } + /// Either a legacy onion format or a modern tlv format - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListforwardsForwardsStyle { #[serde(rename = "legacy")] LEGACY, @@ -4122,6 +4571,16 @@ pub mod responses { } } } + + impl ToString for ListforwardsForwardsStyle { + fn to_string(&self) -> String { + match self { + ListforwardsForwardsStyle::LEGACY => "LEGACY", + ListforwardsForwardsStyle::TLV => "TLV", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListforwardsForwards { pub in_channel: ShortChannelId, @@ -4160,7 +4619,7 @@ pub mod responses { } /// status of the payment - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub enum ListpaysPaysStatus { #[serde(rename = "pending")] PENDING, @@ -4181,6 +4640,17 @@ pub mod responses { } } } + + impl ToString for ListpaysPaysStatus { + fn to_string(&self) -> String { + match self { + ListpaysPaysStatus::PENDING => "PENDING", + ListpaysPaysStatus::FAILED => "FAILED", + ListpaysPaysStatus::COMPLETE => "COMPLETE", + }.to_string() + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpaysPays { pub payment_hash: Sha256, @@ -4200,6 +4670,10 @@ pub mod responses { #[serde(skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub amount_sent_msat: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub preimage: Option, #[serde(skip_serializing_if = "Option::is_none")] pub number_of_parts: Option, @@ -4223,6 +4697,64 @@ pub mod responses { } } + /// out if we offered this to the peer, in if they offered it + #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] + pub enum ListhtlcsHtlcsDirection { + #[serde(rename = "out")] + OUT, + #[serde(rename = "in")] + IN, + } + + impl TryFrom for ListhtlcsHtlcsDirection { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListhtlcsHtlcsDirection::OUT), + 1 => Ok(ListhtlcsHtlcsDirection::IN), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListhtlcsHtlcsDirection", o)), + } + } + } + + impl ToString for ListhtlcsHtlcsDirection { + fn to_string(&self) -> String { + match self { + ListhtlcsHtlcsDirection::OUT => "OUT", + ListhtlcsHtlcsDirection::IN => "IN", + }.to_string() + } + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListhtlcsHtlcs { + pub short_channel_id: ShortChannelId, + pub id: u64, + pub expiry: u32, + pub amount_msat: Amount, + // Path `ListHtlcs.htlcs[].direction` + pub direction: ListhtlcsHtlcsDirection, + pub payment_hash: Sha256, + // Path `ListHtlcs.htlcs[].state` + pub state: HtlcState, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListhtlcsResponse { + pub htlcs: Vec, + } + + impl TryFrom for ListhtlcsResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListHtlcs(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PingResponse { pub totlen: u16, @@ -4263,6 +4795,8 @@ pub mod responses { pub short_channel_id: Option, pub fee_base_msat: Amount, pub fee_proportional_millionths: u32, + #[serde(skip_serializing_if = "Option::is_none")] + pub ignore_fee_limits: Option, pub minimum_htlc_out_msat: Amount, #[serde(skip_serializing_if = "Option::is_none")] pub warning_htlcmin_too_low: Option, @@ -4336,5 +4870,35 @@ pub mod responses { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PreapprovekeysendResponse { + } + + impl TryFrom for PreapprovekeysendResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::PreApproveKeysend(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PreapproveinvoiceResponse { + } + + impl TryFrom for PreapproveinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::PreApproveInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 064c56208721..a55affe958de 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -10,7 +10,7 @@ use std::string::ToString; pub use bitcoin::hashes::sha256::Hash as Sha256; pub use bitcoin::secp256k1::PublicKey; -#[derive(Copy, Clone, Serialize, Deserialize, Debug)] +#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] #[allow(non_camel_case_types)] pub enum ChannelState { OPENINGD = 0, @@ -40,6 +40,15 @@ pub enum HtlcState { SENT_REMOVE_REVOCATION = 8, SENT_REMOVE_ACK_COMMIT = 9, RCVD_REMOVE_ACK_REVOCATION = 10, + RCVD_ADD_HTLC = 11, + RCVD_ADD_COMMIT = 12, + SENT_ADD_REVOCATION = 13, + SENT_ADD_ACK_COMMIT = 14, + SENT_REMOVE_HTLC = 15, + SENT_REMOVE_COMMIT = 16, + RCVD_REMOVE_REVOCATION = 17, + RCVD_REMOVE_ACK_COMMIT = 18, + SENT_REMOVE_ACK_REVOCATION = 19, } #[derive(Copy, Clone, Serialize, Deserialize, Debug)] @@ -319,6 +328,16 @@ impl From for HtlcState { 8 => HtlcState::SENT_REMOVE_REVOCATION, 9 => HtlcState::SENT_REMOVE_ACK_COMMIT, 10 => HtlcState::RCVD_REMOVE_ACK_REVOCATION, + 11 => HtlcState::RCVD_ADD_HTLC, + 12 => HtlcState::RCVD_ADD_COMMIT, + 13 => HtlcState::SENT_ADD_REVOCATION, + 14 => HtlcState::SENT_ADD_ACK_COMMIT, + 15 => HtlcState::SENT_REMOVE_HTLC, + 16 => HtlcState::SENT_REMOVE_COMMIT, + 17 => HtlcState::RCVD_REMOVE_REVOCATION, + 18 => HtlcState::RCVD_REMOVE_ACK_COMMIT, + 19 => HtlcState::SENT_REMOVE_ACK_REVOCATION, + n => panic!("Unmapped HtlcState variant: {}", n), } } diff --git a/common/Makefile b/common/Makefile index 8b63eb3ca118..3e6a5e167bc0 100644 --- a/common/Makefile +++ b/common/Makefile @@ -21,8 +21,10 @@ COMMON_SRC_NOGEN := \ common/channel_id.c \ common/channel_type.c \ common/close_tx.c \ + common/codex32.c \ common/coin_mvt.c \ common/configdir.c \ + common/configvar.c \ common/cryptomsg.c \ common/daemon.c \ common/daemon_conn.c \ @@ -87,6 +89,7 @@ COMMON_SRC_NOGEN := \ common/status_wire.c \ common/subdaemon.c \ common/timeout.c \ + common/trace.c \ common/tx_roles.c \ common/type_to_string.c \ common/utils.c \ diff --git a/common/addr.c b/common/addr.c index 97b19b896fa8..4fa783714d25 100644 --- a/common/addr.c +++ b/common/addr.c @@ -13,6 +13,7 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx, size_t scriptLen = tal_bytelen(scriptPubkey); struct bitcoin_address pkh; struct ripemd160 sh; + int witver; if (is_p2pkh(scriptPubkey, &pkh)) return bitcoin_to_base58(ctx, chainparams, &pkh); @@ -21,7 +22,14 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx, return p2sh_to_base58(ctx, chainparams, &sh); out = tal_arr(ctx, char, 73 + strlen(chainparams->onchain_hrp)); - if (!segwit_addr_encode(out, chainparams->onchain_hrp, 0, + if (is_p2tr(scriptPubkey, NULL)) + witver = 1; + else if (is_p2wpkh(scriptPubkey, NULL) || is_p2wsh(scriptPubkey, NULL)) + witver = 0; + else { + return tal_free(out); + } + if (!segwit_addr_encode(out, chainparams->onchain_hrp, witver, scriptPubkey + 2, scriptLen - 2)) return tal_free(out); diff --git a/common/addr.h b/common/addr.h index bc58ae0d27af..897fbc986599 100644 --- a/common/addr.h +++ b/common/addr.h @@ -3,7 +3,7 @@ #include "config.h" #include -/* Given a scriptPubkey, return an encoded address */ +/* Given a scriptPubkey, return an encoded address for p2pkh/p2w{pkh,sh}/p2tr */ char *encode_scriptpubkey_to_addr(const tal_t *ctx, const struct chainparams *chainparams, const u8 *scriptPubkey); diff --git a/common/amount.c b/common/amount.c index ca48d86e0af9..975ac4414ca6 100644 --- a/common/amount.c +++ b/common/amount.c @@ -37,6 +37,14 @@ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat) return sat; } +struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat) +{ + struct amount_msat res; + + res.millisatoshis = msat.millisatoshis % MSAT_PER_SAT; + return res; +} + /* Different formatting by amounts: btc, sat and msat */ const char *fmt_amount_msat_btc(const tal_t *ctx, struct amount_msat msat, @@ -360,6 +368,27 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val, return true; } +WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val, + struct amount_msat a, + s64 b) +{ + if (b < 0) + return amount_msat_sub_sat(val, a, amount_sat(-b)); + else + return amount_msat_add_sat(val, a, amount_sat(b)); +} + + +WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val, + struct amount_sat a, + s64 b) +{ + if (b < 0) + return amount_sat_sub(val, a, amount_sat(-b)); + else + return amount_sat_add(val, a, amount_sat(b)); +} + bool amount_sat_eq(struct amount_sat a, struct amount_sat b) { return a.satoshis == b.satoshis; diff --git a/common/amount.h b/common/amount.h index de3dd5a3a764..a73be9dd8a0f 100644 --- a/common/amount.h +++ b/common/amount.h @@ -60,6 +60,9 @@ WARN_UNUSED_RESULT bool amount_msat_to_sat(struct amount_sat *sat, /* You can always truncate millisatoshis->satoshis. */ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat); +/* The msats truncated by `amount_msat_to_sat_round_down` */ +struct amount_msat amount_msat_to_sat_remainder(struct amount_msat msat); + /* Simple operations: val = a + b, val = a - b. */ WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val, struct amount_msat a, @@ -89,6 +92,14 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val, struct amount_sat sat, double scale); +WARN_UNUSED_RESULT bool amount_msat_add_sat_s64(struct amount_msat *val, + struct amount_msat a, + s64 b); + +WARN_UNUSED_RESULT bool amount_sat_add_sat_s64(struct amount_sat *val, + struct amount_sat a, + s64 b); + struct amount_msat amount_msat_div(struct amount_msat msat, u64 div); struct amount_sat amount_sat_div(struct amount_sat sat, u64 div); diff --git a/common/bech32.c b/common/bech32.c index 7e949f6c3400..ccf79d5b159c 100644 --- a/common/bech32.c +++ b/common/bech32.c @@ -55,7 +55,7 @@ const int8_t bech32_charset_rev[128] = { 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 }; -int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, size_t max_input_len, bech32_encoding enc) { +int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, size_t max_output_len, bech32_encoding enc) { uint32_t chk = 1; size_t i = 0; while (hrp[i] != 0) { @@ -68,7 +68,7 @@ int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t dat chk = bech32_polymod_step(chk) ^ (ch >> 5); ++i; } - if (i + 7 + data_len > max_input_len) return 0; + if (i + 7 + data_len > max_output_len) return 0; chk = bech32_polymod_step(chk); while (*hrp != 0) { chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); diff --git a/common/bech32.h b/common/bech32.h index 614ad321e3df..794510be2755 100644 --- a/common/bech32.h +++ b/common/bech32.h @@ -83,7 +83,7 @@ typedef enum { * In: hrp : Pointer to the null-terminated human readable part. * data : Pointer to an array of 5-bit values. * data_len: Length of the data array. - * max_input_len: Maximum valid length of input (90 for segwit usage). + * max_output_len: Maximum valid length of output (90 for segwit usage). * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). * Returns 1 if successful. */ @@ -92,7 +92,7 @@ int bech32_encode( const char *hrp, const uint8_t *data, size_t data_len, - size_t max_input_len, + size_t max_output_len, bech32_encoding enc ); diff --git a/common/bolt11.c b/common/bolt11.c index ce8532971947..616eab84ac41 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -411,6 +412,17 @@ static const char *decode_f(struct bolt11 *b11, "f: witness v0 bad length %zu", tal_count(f)); } + if (version == 1 && tal_count(f) != 32) { + return tal_fmt(b11, + "f: witness v1 bad length %zu", + tal_count(f)); + } + if (tal_count(f) > 40) { + return tal_fmt(b11, + "f: witness v%"PRIu64" bad length %zu", + version, + tal_count(f)); + } fallback = scriptpubkey_witness_raw(b11, version, f, tal_count(f)); } else { @@ -678,6 +690,24 @@ static bool bech32_decode_alloc(const tal_t *ctx, return true; } +static bool has_lightning_prefix(const char *invstring) +{ + /* BOLT #11: + * + * If a URI scheme is desired, the current recommendation is to either + * use 'lightning:' as a prefix before the BOLT-11 encoding */ + return (strstarts(invstring, "lightning:") || + strstarts(invstring, "LIGHTNING:")); +} + +const char *to_canonical_invstr(const tal_t *ctx, + const char *invstring) +{ + if (has_lightning_prefix(invstring)) + invstring += strlen("lightning:"); + return str_lowering(ctx, invstring); +} + /* Extracts signature but does not check it. */ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, const struct feature_set *our_features, @@ -701,14 +731,7 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, memset(have_field, 0, sizeof(have_field)); b11->routes = tal_arr(b11, struct route_info *, 0); - /* BOLT #11: - * - * If a URI scheme is desired, the current recommendation is to either - * use 'lightning:' as a prefix before the BOLT-11 encoding - */ - if (strstarts(str, "lightning:") || strstarts(str, "LIGHTNING:")) - str += strlen("lightning:"); - + assert(!has_lightning_prefix(str)); if (strlen(str) < 8) return decode_fail(b11, fail, "Bad bech32 string"); @@ -893,6 +916,17 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, "h: does not match description"); } + /* BOLT #11: + * A writer: + *... + * - MUST include either exactly one `d` or exactly one `h` field. + */ + /* FIXME: It doesn't actually say the reader must check though! */ + if (!have_field[bech32_charset_rev['d']] + && !have_field[bech32_charset_rev['h']]) + return decode_fail(b11, fail, + "must have either 'd' or 'h' field"); + hash_u5_done(&hu5, hash); *sig = tal_dup_arr(ctx, u5, data, data_len, 0); @@ -1106,12 +1140,12 @@ static void encode_f(u5 **data, const u8 *fallback) push_fallback_addr(data, 0, &pkh, sizeof(pkh)); } else if (is_p2wsh(fallback, &wsh)) { push_fallback_addr(data, 0, &wsh, sizeof(wsh)); - } else if (tal_count(fallback) + } else if (tal_count(fallback) > 1 && fallback[0] >= 0x50 && fallback[0] < (0x50+16)) { /* Other (future) witness versions: turn OP_N into N */ - push_fallback_addr(data, fallback[0] - 0x50, fallback + 1, - tal_count(fallback) - 1); + push_fallback_addr(data, fallback[0] - 0x50, fallback + 2, + tal_count(fallback) - 2); } else { /* Copy raw. */ push_field(data, 'f', diff --git a/common/bolt11.h b/common/bolt11.h index ebcf991926cc..fa403f1ae771 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -125,6 +125,12 @@ char *bolt11_encode_(const tal_t *ctx, secp256k1_ecdsa_recoverable_signature *rsig), \ (arg)) +/** to_canonical_invstr - return a canonical string where the following constrains are follow: + * - There is no `lightning:` prefix; + * - all the string is in lower case. + */ +const char *to_canonical_invstr(const tal_t *ctx, const char *invstring); + #if DEVELOPER /* Flag for tests to suppress `min_final_cltv_expiry` field generation, to match test vectors */ extern bool dev_bolt11_no_c_generation; diff --git a/common/bolt12.h b/common/bolt12.h index 277a3f80bf31..e8213a56ae49 100644 --- a/common/bolt12.h +++ b/common/bolt12.h @@ -1,11 +1,7 @@ #ifndef LIGHTNING_COMMON_BOLT12_H #define LIGHTNING_COMMON_BOLT12_H #include "config.h" -#if EXPERIMENTAL_FEATURES -#include -#else #include -#endif struct feature_set; diff --git a/common/channel_type.c b/common/channel_type.c index 80a9138f706a..546b21a90173 100644 --- a/common/channel_type.c +++ b/common/channel_type.c @@ -55,6 +55,17 @@ void channel_type_set_scid_alias(struct channel_type *type) COMPULSORY_FEATURE(OPT_SCID_ALIAS)); } +struct channel_type *channel_type_anchors_zero_fee_htlc(const tal_t *ctx) +{ + struct channel_type *type = channel_type_none(ctx); + + set_feature_bit(&type->features, + COMPULSORY_FEATURE(OPT_ANCHORS_ZERO_FEE_HTLC_TX)); + set_feature_bit(&type->features, + COMPULSORY_FEATURE(OPT_STATIC_REMOTEKEY)); + return type; +} + struct channel_type *default_channel_type(const tal_t *ctx, const struct feature_set *our_features, const u8 *their_features) @@ -64,12 +75,15 @@ struct channel_type *default_channel_type(const tal_t *ctx, * - if `channel_type` was present in both `open_channel` and `accept_channel`: * - This is the `channel_type` (they must be equal, required above) * - otherwise: + * - if `option_anchors_zero_fee_htlc_tx` was negotiated: + * - the `channel_type` is `option_anchors_zero_fee_htlc_tx` and `option_static_remotekey` (bits 22 and 12) + * - otherwise, if `option_anchor_outputs` was negotiated: + * - the `channel_type` is `option_anchor_outputs` and + * `option_static_remotekey` (bits 20 and 12) */ - /* BOLT #2: - * - otherwise, if `option_anchor_outputs` was negotiated: - * - the `channel_type` is `option_anchor_outputs` and - * `option_static_remotekey` (bits 20 and 12) - */ + if (feature_negotiated(our_features, their_features, + OPT_ANCHORS_ZERO_FEE_HTLC_TX)) + return channel_type_anchors_zero_fee_htlc(ctx); if (feature_negotiated(our_features, their_features, OPT_ANCHOR_OUTPUTS)) return channel_type_anchor_outputs(ctx); @@ -97,6 +111,12 @@ bool channel_type_has(const struct channel_type *type, int feature) return feature_offered(type->features, feature); } +bool channel_type_has_anchors(const struct channel_type *type) +{ + return feature_offered(type->features, OPT_ANCHOR_OUTPUTS) + || feature_offered(type->features, OPT_ANCHORS_ZERO_FEE_HTLC_TX); +} + bool channel_type_eq(const struct channel_type *a, const struct channel_type *b) { @@ -131,6 +151,7 @@ struct channel_type *channel_type_accept(const tal_t *ctx, static const size_t feats[] = { OPT_ANCHOR_OUTPUTS, + OPT_ANCHORS_ZERO_FEE_HTLC_TX, OPT_STATIC_REMOTEKEY, OPT_SCID_ALIAS, OPT_ZEROCONF, @@ -179,6 +200,8 @@ struct channel_type *channel_type_accept(const tal_t *ctx, if (channel_type_eq(&proposed, channel_type_none(tmpctx)) || channel_type_eq(&proposed, channel_type_static_remotekey(tmpctx)) || + channel_type_eq(&proposed, + channel_type_anchors_zero_fee_htlc(tmpctx)) || channel_type_eq(&proposed, channel_type_anchor_outputs(tmpctx))) { /* At this point we know it matches, and maybe has * a couple of extra options. So let's just reply diff --git a/common/channel_type.h b/common/channel_type.h index 28096f3c253b..eb22244eb377 100644 --- a/common/channel_type.h +++ b/common/channel_type.h @@ -9,6 +9,7 @@ struct channel_type *channel_type_none(const tal_t *ctx); struct channel_type *channel_type_static_remotekey(const tal_t *ctx); struct channel_type *channel_type_anchor_outputs(const tal_t *ctx); +struct channel_type *channel_type_anchors_zero_fee_htlc(const tal_t *ctx); /* channel_type variants */ void channel_type_set_zeroconf(struct channel_type *channel_type); @@ -30,6 +31,9 @@ struct channel_type *default_channel_type(const tal_t *ctx, /* Does this type include this feature? */ bool channel_type_has(const struct channel_type *type, int feature); +/* Convenience for querying either anchor_outputs or anchors_zero_fee_htlc_tx */ +bool channel_type_has_anchors(const struct channel_type *type); + /* Are these two channel_types equivalent? */ bool channel_type_eq(const struct channel_type *a, const struct channel_type *b); diff --git a/common/close_tx.c b/common/close_tx.c index 48e54d1e9c02..6a54e81985ff 100644 --- a/common/close_tx.c +++ b/common/close_tx.c @@ -52,7 +52,7 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL)); if (local_wallet_index) psbt_add_keypath_to_last_output( - tx, *local_wallet_index, local_wallet_ext_key); + tx, *local_wallet_index, local_wallet_ext_key, is_p2tr(script, NULL)); num_outputs++; } diff --git a/common/codex32.c b/common/codex32.c new file mode 100644 index 000000000000..f74587a634d0 --- /dev/null +++ b/common/codex32.c @@ -0,0 +1,464 @@ +/* Implementation of BIP-93 "codex32: Checksummed SSSS-aware BIP32 seeds". + * + * There are two representations, short and long: + * + * CODEX32 := HRP "1" SHORT-DATA | LONG-DATA + * HRP := "ms" | "MS" + * SHORT-DATA := THRESHOLD IDENTIFIER SHAREINDEX SHORT-PAYLOAD SHORT-CHECKSUM + * LONG-DATA := THRESHOLD IDENTIFIER SHAREINDEX LONG-PAYLOAD LONG-CHECKSUM + * + * THRESHOLD = "0" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" + * IDENTIFER := BECH32*4 + * SHAREINDEX := BECH32 + * + * SHORT-PAYLOAD := BECH32 [0 - 74 times] + * SHORT-CHECKSUM := BECH32*13 + * + * LONG-PAYLOAD := BECH32 [75 - 103 times] + * LONG-CHECKSUM := BECH32*15 + * + * Thus, a short codex32 string has 22 bytes of non-payload, so 22 to 96 characters long. + * A long codex32 string has 24 bytes of non-payload, so 99 to 127 characters. + */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct checksum_engine { + u8 generator[15]; + u8 residue[15]; + u8 target[15]; + size_t len; + size_t max_payload_len; +}; + +static const struct checksum_engine initial_engine_csum[] = { + /* Short Codex32 Engine */ + { + { + 25, 27, 17, 8, 0, 25, + 25, 25, 31, 27, 24, 16, + 16, + }, + { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, + }, + { + 16, 25, 24, 3, 25, 11, + 16, 23, 29, 3, 25, 17, + 10, + }, + 13, + 74, + }, + /* Long Codex32 Engine */ + { + { + 15, 10, 25, 26, 9, 25, + 21, 6, 23, 21, 6, 5, + 22, 4, 23 + }, + { + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1 + }, + { + 16, 25, 24, 3, 25, 11, + 16, 23, 29, 3, 25, 17, + 10, 25, 6 + }, + 15, + 103, + } +}; + +static const uint8_t logi[32] = +{ + 0, 0, 1, 14, 2, 28, 15, 22, + 3, 5, 29, 26, 16, 7, 23, 11, + 4, 25, 6, 10, 30, 13, 27, 21, + 17, 18, 8, 19, 24, 9, 12, 20, +}; + +static const uint8_t log_inv[31] = +{ + 1, 2, 4, 8, 16, 9, 18, 13, + 26, 29, 19, 15, 30, 21, 3, 6, + 12, 24, 25, 27, 31, 23, 7, 14, + 28, 17, 11, 22, 5, 10, 20, +}; + +static void addition_gf32(uint8_t *x, uint8_t y) +{ + *x = *x ^ y; + return; +} + +static void multiply_gf32(uint8_t *x, uint8_t y) +{ + if (*x == 0 || y == 0) { + *x = 0; + } else { + *x = log_inv[(logi[*x] + logi[y]) % 31]; + } + return; +} + +/* Helper to input a single field element in the checksum engine. */ +static void input_fe(const u8 *generator, u8 *residue, uint8_t e, size_t len) +{ + size_t res_len = len; + u8 xn = residue[0]; + + for(size_t i = 1; i < res_len; i++) { + residue[i - 1] = residue[i]; + } + + residue[res_len - 1] = e; + + for(size_t i = 0; i < res_len; i++) { + u8 x = generator[i]; + multiply_gf32(&x, xn); + addition_gf32(&residue[i], x); + } +} + +/* Helper to input the HRP of codex32 string into the checksum engine */ +static void input_hrp(const u8 *generator, u8 *residue, const char *hrp, size_t len) +{ + size_t i = 0; + for (i = 0; i < strlen(hrp); i++) { + input_fe(generator, residue, hrp[i] >> 5, len); + } + input_fe(generator, residue, hrp[i] >> 0, len); + for (i = 0; i < strlen(hrp); i++) { + input_fe(generator, residue, hrp[i] & 0x1f, len); + } + return; +} + +/* Helper to input data strong of codex32 into the checksum engine. */ +static void input_data_str(u8 *generator, u8 *residue, const char *datastr, size_t len) +{ + size_t i = 0; + + for (i = 0; i < strlen(datastr); i++) { + input_fe(generator, residue, bech32_charset_rev[(int)datastr[i]], len); + } + + return; +} + +static void input_own_target(const u8 *generator, u8 *residue, const u8 *target, size_t len) +{ + for (size_t i = 0; i < len; i++) { + input_fe(generator, residue, target[i], len); + } +} + +/* Helper to verify codex32 checksum */ +static bool checksum_verify(const char *hrp, const char *codex_datastr, + const struct checksum_engine *initial_engine) +{ + struct checksum_engine engine = *initial_engine; + + input_hrp(engine.generator, engine.residue ,hrp, engine.len); + input_data_str(engine.generator, engine.residue, codex_datastr, engine.len); + + return memcmp(engine.target, engine.residue, engine.len) == 0; +} + +static void calculate_checksum(const char *hrp, char *csum, const char *codex_datastr, + const struct checksum_engine *initial_engine) +{ + struct checksum_engine engine = *initial_engine; + + input_hrp(engine.generator, engine.residue, hrp, engine.len); + input_data_str(engine.generator, engine.residue, codex_datastr, engine.len); + input_own_target(engine.generator, engine.residue, engine.target, engine.len); + + for (size_t i = 0; i < engine.len; i++) + csum[i] = bech32_charset[engine.residue[i]]; +} + +/* Pull len chars from cursor into dst. */ +static bool pull_chars(char *dst, size_t len, const char **cursor, size_t *max) +{ + if (*max < len) + return false; + memcpy(dst, *cursor, len); + *cursor += len; + *max -= len; + return true; +} + +/* Truncate length of *cursor (i.e. trim from end) */ +static bool trim_chars(size_t len, const char **cursor, size_t *max) +{ + if (*max < len) + return false; + *max -= len; + return true; +} + +/* Helper to fetch data from payload as a valid hex buffer */ +static const u8 *decode_payload(const tal_t *ctx, const char *payload, size_t payload_len) +{ + u8 *ret = tal_arr(ctx, u8, (payload_len * 5 + 7) / 8); + uint8_t next_byte = 0; + uint8_t rem = 0; + size_t j = 0; + + /* We have already checked this is a valid bech32 string! */ + for (size_t i = 0; i < payload_len; i++) { + int ch = payload[i]; + uint8_t fe = bech32_charset_rev[ch]; + + if (rem < 3) { + // If we are within 3 bits of the start we can fit the whole next char in + next_byte |= fe << (3 - rem); + } + else if (rem == 3) { + // If we are exactly 3 bits from the start then this char fills in the byte + ret[j++] = next_byte | fe; + next_byte = 0; + } + else { // rem > 3 + // Otherwise we have to break it in two + u8 overshoot = rem - 3; + assert(overshoot > 0); + ret[j++] = next_byte | (fe >> overshoot); + next_byte = fe << (8 - overshoot); + } + + rem = (rem + 5) % 8; + } + + /* BIP-93: + * Any incomplete group at the end MUST be 4 bits or less, and is discarded. + */ + if (rem > 4) + return tal_free(ret); + + /* As a result, we often don't use the final byte */ + tal_resize(&ret, j); + return ret; +} + +/* Checks case inconsistency, and for non-bech32 chars. */ +static const char *bech32_case_fixup(const tal_t *ctx, + const char *codex32str, + const char **sep) +{ + size_t str_len = strlen(codex32str); + char *was_upper_str; + + *sep = NULL; + + /* If first is upper, lower-case the rest */ + if (cisupper(codex32str[0])) { + /* We need a non-const str var, and a flag */ + was_upper_str = tal_strdup(ctx, codex32str); + codex32str = was_upper_str; + } else { + was_upper_str = NULL; + } + + for (size_t i = 0; i < str_len; i++) { + int c = codex32str[i]; + if (c == '1') { + /* Two separators? */ + if (*sep) + goto fail; + *sep = codex32str + i; + continue; + } + if (c < 0 || c > 128) + goto fail; + if (was_upper_str) { + /* Mixed case not allowed! */ + if (cislower(c)) + goto fail; + was_upper_str[i] = c = tolower(c); + } else { + if (cisupper(c)) + goto fail; + } + if (bech32_charset_rev[c] == -1) + goto fail; + } + + return codex32str; + +fail: + return tal_free(was_upper_str); +} + +/* Return NULL if the codex32 is invalid */ +struct codex32 *codex32_decode(const tal_t *ctx, + const char *hrp, + const char *codex32str, + char **fail) +{ + struct codex32 *parts = tal(ctx, struct codex32); + const char *sep, *codex_datastr; + char threshold_char; + size_t maxlen; + const struct checksum_engine *csum_engine; + + /* Lowercase it all, iff it's all uppercase. */ + codex32str = bech32_case_fixup(tmpctx, codex32str, &sep); + if (!codex32str) { + *fail = tal_fmt(ctx, "Not a valid bech32 string!"); + return tal_free(parts); + } + + if (!sep) { + *fail = tal_fmt(ctx, "Separator doesn't exist!"); + return tal_free(parts); + } + + parts->hrp = tal_strndup(parts, codex32str, sep - codex32str); + if (hrp && !streq(parts->hrp, hrp)) { + *fail = tal_fmt(ctx, "Invalid hrp %s!", parts->hrp); + return tal_free(parts); + } + + codex_datastr = sep + 1; + maxlen = strlen(codex_datastr); + + /* If it's short, use short checksum engine. If it's invalid, + * use short checksum and we'll fail when payload is too long. */ + csum_engine = &initial_engine_csum[maxlen >= 96]; + if (!checksum_verify(parts->hrp, codex_datastr, csum_engine)) { + *fail = tal_fmt(ctx, "Invalid checksum!"); + return tal_free(parts); + } + + /* Pull fixed parts and discard checksum */ + if (!pull_chars(&threshold_char, 1, &codex_datastr, &maxlen) + || !pull_chars(parts->id, ARRAY_SIZE(parts->id) - 1, &codex_datastr, &maxlen) + || !pull_chars(&parts->share_idx, 1, &codex_datastr, &maxlen) + || !trim_chars(csum_engine->len, &codex_datastr, &maxlen)) { + *fail = tal_fmt(ctx, "Too short!"); + return tal_free(parts); + } + parts->id[ARRAY_SIZE(parts->id)-1] = 0; + /* Is payload too long for this checksum? */ + if (maxlen > csum_engine->max_payload_len) { + *fail = tal_fmt(ctx, "Invalid length!"); + return tal_free(parts); + } + + parts->payload = decode_payload(parts, codex_datastr, maxlen); + if (!parts->payload) { + *fail = tal_fmt(ctx, "Invalid payload!"); + return tal_free(parts); + } + + if (parts->share_idx == 's') { + parts->type = CODEX32_ENCODING_SECRET; + } else { + parts->type = CODEX32_ENCODING_SHARE; + } + + parts->threshold = threshold_char - '0'; + if (parts->threshold > 9 || + parts->threshold < 0 || + /* Can't happen because bech32 `1` is invalid, but worth noting */ + parts->threshold == 1) { + *fail = tal_fmt(ctx, "Invalid threshold!"); + return tal_free(parts); + } + + if (parts->threshold == 0 && parts->type != CODEX32_ENCODING_SECRET) { + *fail = tal_fmt(ctx, "Expected share index s for threshold 0!"); + return tal_free(parts); + } + + return parts; +} + +/* Returns Codex32 encoded secret of the seed provided. */ +const char *codex32_secret_encode(const tal_t *ctx, + const char *hrp, + const char *id, + const u32 threshold, + const u8 *seed, + size_t seedlen, + char **bip93) +{ + const struct checksum_engine *csum_engine; + + /* FIXME: Our code assumes a two-letter HRP! Larger won't allow a + * 128-bit secret in a "standard billfold metal wallet" acording to + * Russell O'Connor */ + assert(strlen(hrp) == 2); + + if (threshold > 9 || threshold < 0 || threshold == 1) + return tal_fmt(ctx, "Invalid threshold %u", threshold); + + if (strlen(id) != 4) + return tal_fmt(ctx, "Invalid id: must be 4 characters"); + + for (size_t i = 0; id[i]; i++) { + s8 rev; + + if (id[i] & 0x80) + return tal_fmt(ctx, "Invalid id: must be ASCII"); + + rev = bech32_charset_rev[(int)id[i]]; + if (rev == -1) + return tal_fmt(ctx, "Invalid id: must be valid bech32 string"); + if (bech32_charset[rev] != id[i]) + return tal_fmt(ctx, "Invalid id: must be lower-case"); + } + + /* Every codex32 has hrp `ms` and since we are generating a + * secret it's share index would be `s` and threshold given by user. */ + *bip93 = tal_fmt(ctx, "%s1%d%ss", hrp, threshold, id); + + uint8_t next_u5 = 0, rem = 0; + + for (size_t i = 0; i < seedlen; i++) { + /* Each byte provides at least one u5. Push that. */ + uint8_t u5 = (next_u5 << (5 - rem)) | seed[i] >> (3 + rem); + + tal_append_fmt(bip93, "%c", bech32_charset[u5]); + next_u5 = seed[i] & ((1 << (3 + rem)) - 1); + + /* If there were 2 or more bits from the last iteration, then + * this iteration will push *two* u5s. */ + if(rem >= 2) { + tal_append_fmt(bip93, "%c", bech32_charset[next_u5 >> (rem - 2)]); + next_u5 &= (1 << (rem - 2)) - 1; + } + rem = (rem + 8) % 5; + } + if(rem > 0) { + tal_append_fmt(bip93, "%c", bech32_charset[next_u5 << (5 - rem)]); + } + + csum_engine = &initial_engine_csum[seedlen >= 51]; + char csum[csum_engine->len]; + calculate_checksum(hrp, csum, *bip93 + 3, csum_engine); + tal_append_fmt(bip93, "%.*s", (int)csum_engine->len, csum); + return NULL; +} diff --git a/common/codex32.h b/common/codex32.h new file mode 100644 index 000000000000..765b7fd39929 --- /dev/null +++ b/common/codex32.h @@ -0,0 +1,66 @@ +#ifndef LIGHTNING_COMMON_CODEX32_H +#define LIGHTNING_COMMON_CODEX32_H +#include "config.h" +#include +#include + +/* Supported encodings. */ +typedef enum { + CODEX32_ENCODING_SHARE, + CODEX32_ENCODING_SECRET +} codex32_encoding; + +/* Decoded codex32 parts */ +struct codex32 { + /* "ms" */ + const char *hrp; + /* 0, or 2-9 */ + uint8_t threshold; + /* Four valid bech32 characters which identify this complete codex32 secret, the last char is null */ + char id[4 + 1]; + /* Valid bech32 character identifying this share of the secret, or `s` for unshared */ + char share_idx; + /* The actual data payload */ + const u8 *payload; + /* Is this a share, or a secret? */ + codex32_encoding type; +}; + +/** Decode a codex32 or codex32l string + * + * Out: parts: Pointer to a codex32. Will be + * updated to contain the details extracted from the codex32 string. + * fail: Pointer to a char *, that would be updated with the reason + * of failure in case this function returns a NULL. + * In: ctx: Allocation context for *fail or return. + * hrp: If non-NULL, a hrp which must match. + * codex32str: Pointer to a nul-terminated codex32 string. + * + * Returns Parts to indicate decoding was successful. NULL is returned if decoding failed, + * with appropriate reason in the fail param + */ +struct codex32 *codex32_decode(const tal_t *ctx, + const char *hrp, + const char *codex32str, + char **fail); + +/** Encode a seed into codex32 secret format. + * + * In: input: hrp: 2 character human-readable-prefix + * id: Valid 4 char string identifying the secret + * threshold: Threshold according to the bip93 + * seed: The secret in u8* + * seedlen: Length of the seed provided. + * Returns a string which contains the seed provided in bip93 format. + * + * Returns an error string, or returns NULL and sets @bip93. + */ +const char *codex32_secret_encode(const tal_t *ctx, + const char *hrp, + const char *id, + const u32 threshold, + const u8 *seed, + size_t seedlen, + char **bip93); + +#endif /* LIGHTNING_COMMON_CODEX32_H */ diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 4b087a73a190..f7d82b12812d 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -98,11 +98,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, { struct chain_coin_mvt *mvt = tal(ctx, struct chain_coin_mvt); - if (account_name) - mvt->account_name = tal_strdup(mvt, account_name); - else - mvt->account_name = NULL; - + mvt->account_name = tal_strdup_or_null(mvt, account_name); mvt->tx_txid = tx_txid; mvt->outpoint = outpoint; mvt->originating_acct = NULL; @@ -393,11 +389,8 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *mvt = tal(ctx, struct coin_mvt); mvt->account_id = tal_strdup(mvt, chain_mvt->account_name); - if (chain_mvt->originating_acct) - mvt->originating_acct = - tal_strdup(mvt, chain_mvt->originating_acct); - else - mvt->originating_acct = NULL; + mvt->originating_acct = + tal_strdup_or_null(mvt, chain_mvt->originating_acct); mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHAIN_MVT; diff --git a/common/configdir.c b/common/configdir.c index c03abe18cf9e..8446ba16a959 100644 --- a/common/configdir.c +++ b/common/configdir.c @@ -4,17 +4,20 @@ #include #include #include +#include #include #include #include #include +#include #include #include -bool deprecated_apis = true; +int opt_exitcode = 1; /* The regrettable globals */ static const tal_t *options_ctx; +static struct configvar *current_cv; /* Override a tal string; frees the old one. */ char *opt_set_talstr(const char *arg, char **p) @@ -50,113 +53,55 @@ static void tal_freefn(void *ptr) tal_free(ptr); } -static int config_parse_line_number; - -static void config_log_stderr_exit(const char *fmt, ...) -{ - char *msg; - va_list ap; - - va_start(ap, fmt); - - /* This is the format we expect:*/ - if (streq(fmt, "%s: %.*s: %s")) { - const char *argv0 = va_arg(ap, const char *); - unsigned int len = va_arg(ap, unsigned int); - const char *arg = va_arg(ap, const char *); - const char *problem = va_arg(ap, const char *); - - assert(argv0 != NULL); - assert(arg != NULL); - assert(problem != NULL); - /*mangle it to remove '--' and add the line number.*/ - msg = tal_fmt(NULL, "%s line %d: %.*s: %s", - argv0, - config_parse_line_number, len-2, arg+2, problem); - } else { - msg = tal_vfmt(NULL, fmt, ap); - } - va_end(ap); - - errx(1, "%s", msg); -} - -static void parse_include(const char *filename, bool must_exist, bool early, - size_t depth) +static struct configvar **gather_file_configvars(const tal_t *ctx, + enum configvar_src src, + const char *filename, + bool must_exist, + size_t include_depth) { char *contents, **lines; - char **all_args; /*For each line: either `--`argument, include file, or NULL*/ - char *argv[3]; - int i, argc; + struct configvar **cvs = tal_arr(ctx, struct configvar *, 0); - contents = grab_file(NULL, filename); + contents = grab_file(tmpctx, filename); /* The default config doesn't have to exist, but if the config was * specified on the command line it has to exist. */ if (!contents) { if (must_exist) err(1, "Opening and reading %s", filename); - return; + return cvs; } + /* Break into lines. */ lines = tal_strsplit(contents, contents, "\r\n", STR_EMPTY_OK); + for (size_t i = 0; i < tal_count(lines) - 1; i++) { + /* Comments & blank lines*/ + if (strstarts(lines[i], "#") || streq(lines[i], "")) + continue; - /* We have to keep all_args around, since opt will point into it: use - * magic tal name to tell memleak this isn't one. */ - all_args = tal_arr_label(options_ctx, char *, tal_count(lines) - 1, - TAL_LABEL(options_array_notleak, "")); + if (strstarts(lines[i], "include ")) { + const char *included = lines[i] + strlen("include "); + struct configvar **sub; - for (i = 0; i < tal_count(lines) - 1; i++) { - if (strstarts(lines[i], "#")) { - all_args[i] = NULL; - } else if (strstarts(lines[i], "include ")) { - /* If relative, it's relative to current config file */ - all_args[i] = path_join(all_args, - take(path_dirname(NULL, - filename)), - lines[i] + strlen("include ")); - } else { - /* Only valid forms are "foo" and "foo=bar" */ - all_args[i] = tal_fmt(all_args, "--%s", lines[i]); - } - /* This isn't a leak either */ - if (all_args[i]) - tal_set_name(all_args[i], TAL_LABEL(config_notleak, "")); - } - - /* - For each line we construct a fake argc,argv commandline. - argv[1] is the only element that changes between iterations. - */ - argc = 2; - argv[0] = cast_const(char *, filename); - argv[argc] = NULL; + if (include_depth > 100) + errx(1, "Include loop with %s and %s", filename, included); - for (i = 0; i < tal_count(all_args); i++) { - if (all_args[i] == NULL) - continue; - - if (!strstarts(all_args[i], "--")) { - /* There could be more, but this gives a hint. */ - if (depth > 100) - errx(1, "Include loop with %s and %s", - filename, all_args[i]); - parse_include(all_args[i], true, early, ++depth); + /* If relative, it's relative to current config file */ + sub = gather_file_configvars(NULL, + src, + path_join(tmpctx, + take(path_dirname(NULL, filename)), + included), + true, + include_depth + 1); + cvs = configvar_join(ctx, take(cvs), take(sub)); continue; } - config_parse_line_number = i + 1; - argv[1] = all_args[i]; - if (early) { - opt_early_parse_incomplete(argc, argv, - config_log_stderr_exit); - } else { - opt_parse(&argc, argv, config_log_stderr_exit); - argc = 2; /* opt_parse might have changed it */ - } + tal_arr_expand(&cvs, + configvar_new(cvs, src, filename, i+1, lines[i])); } - - tal_free(contents); + return cvs; } static char *default_base_configdir(const tal_t *ctx) @@ -179,6 +124,12 @@ static char *opt_set_network(const char *arg, void *unused) { assert(arg != NULL); + /* Ignore if called directly from opt (e.g. lightning-cli) */ + if (!current_cv) + return NULL; + + if (current_cv->src == CONFIGVAR_NETWORK_CONF) + return "not permitted in network-specific configuration files"; /* Set the global chainparams instance */ chainparams = chainparams_for_network(arg); if (!chainparams) @@ -191,159 +142,190 @@ static char *opt_set_specific_network(const char *network) return opt_set_network(network, NULL); } -static void opt_show_network(char buf[OPT_SHOW_LEN], const void *unused) +static bool opt_show_network(char *buf, size_t len, const void *unused) { - snprintf(buf, OPT_SHOW_LEN, "%s", chainparams->network_name); + snprintf(buf, len, "%s", chainparams->network_name); + return true; } -/* We track where we're getting options from, so we can detect misuse */ -enum parse_state { - CMDLINE = 1, - FORCED_CONFIG = 2, - TOPLEVEL_CONFIG = 4, - NETWORK_CONFIG = 8, -}; -static enum parse_state parse_state = CMDLINE; - -static char *opt_restricted_cmdline(const char *arg, const void *unused) +static char *opt_set_config_filename(const char *arg, char **p) { - if (parse_state != CMDLINE) - return "not permitted in configuration files"; - return NULL; -} + /* Ignore if called directly from opt (e.g. lightning-cli) */ + if (!current_cv) + return NULL; -static char *opt_restricted_toplevel_noarg(const void *unused) -{ - if (parse_state == NETWORK_CONFIG) - return "not permitted in network-specific configuration files"; - return NULL; + if (current_cv->src == CONFIGVAR_CMDLINE) + return opt_set_abspath(arg, p); + return "not permitted in configuration files"; } -static char *opt_restricted_toplevel(const char *arg, const void *unused) +static char *opt_set_lightning_dir(const char *arg, char **p) { - return opt_restricted_toplevel_noarg(NULL); + /* Ignore if called directly from opt (e.g. lightning-cli) */ + if (!current_cv) + return NULL; + + if (current_cv->src == CONFIGVAR_CMDLINE + || current_cv->src == CONFIGVAR_EXPLICIT_CONF) + return opt_set_abspath(arg, p); + return "not permitted in implicit configuration files"; } -static char *opt_restricted_forceconf_only(const char *arg, const void *unused) +void setup_option_allocators(void) { - if (parse_state != CMDLINE && parse_state != FORCED_CONFIG) - return "not permitted in implicit configuration files"; - return NULL; + /*~ These functions make ccan/opt use tal for allocations */ + opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); } -bool is_restricted_ignored(const void *fn) +static void parse_configvars(struct configvar **cvs, + bool early, + bool full_knowledge) { - return fn == opt_restricted_toplevel_noarg - || fn == opt_restricted_toplevel - || fn == opt_restricted_forceconf_only; -} + for (size_t i = 0; i < tal_count(cvs); i++) { + const char *problem; + bool should_know; + + should_know = full_knowledge; + /* We should always know cmdline args in final parse */ + if (!early && cvs[i]->src == CONFIGVAR_CMDLINE) + should_know = true; + + current_cv = cvs[i]; + problem = configvar_parse(cvs[i], + early, + should_know, + IFDEV(true, false)); + current_cv = NULL; + if (!problem) + continue; -bool is_restricted_print_if_nonnull(const void *fn) -{ - return fn == opt_restricted_cmdline; + if (cvs[i]->file) { + errx(opt_exitcode, "Config file %s line %u: %s: %s", + cvs[i]->file, cvs[i]->linenum, + cvs[i]->configline, problem); + } else { + errx(opt_exitcode, "--%s: %s", cvs[i]->configline, problem); + } + } } -void setup_option_allocators(void) +static void finished_arg(int *argc, char **argv, size_t *idx, + bool remove_args) { - /*~ These functions make ccan/opt use tal for allocations */ - opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); + if (!remove_args) { + (*idx)++; + return; + } + memmove(argv + *idx, argv + 1 + *idx, (*argc - *idx) * sizeof(char *)); + (*argc)--; } -/* network is NULL for parsing top-level config file. */ -static void parse_implied_config_file(const char *config_basedir, - const char *network, - bool early) +/* Now all options are known, we can turn cmdline into configvars */ +static struct configvar **gather_cmdline_args(const tal_t *ctx, + int *argc, char **argv, + bool remove_args) { - const char *dir, *filename; + struct configvar **cvs = tal_arr(ctx, struct configvar *, 0); - if (config_basedir) - dir = path_join(NULL, take(path_cwd(NULL)), config_basedir); - else - dir = default_base_configdir(NULL); + assert(argv[*argc] == NULL); + for (size_t i = 1; argv[i];) { + struct opt_table *ot; + const char *configline, *arg, *optarg; + enum configvar_src src; + bool extra_arg; - if (network) - dir = path_join(NULL, take(dir), network); + /* End of options? */ + if (streq(argv[i], "--")) + break; - filename = path_join(NULL, take(dir), "config"); - parse_include(filename, false, early, 0); - tal_free(filename); -} + if (!strstarts(argv[i], "-")) { + i++; + continue; + } -/* If they specify --conf, we just read that. - * Otherwise we read /config then //config - */ -void parse_config_files(const char *config_filename, - const char *config_basedir, - bool early) -{ - if (config_filename) { - parse_state = FORCED_CONFIG; - parse_include(config_filename, true, early, 0); - parse_state = CMDLINE; - return; + if (strstarts(argv[i], "--")) { + arg = argv[i] + 2; + ot = opt_find_long(arg, &optarg); + src = CONFIGVAR_CMDLINE; + } else { + /* FIXME: We don't handle multiple short + * options here! */ + arg = argv[i] + 1; + ot = opt_find_short(arg[0]); + optarg = NULL; + src = CONFIGVAR_CMDLINE_SHORT; + } + if (ot) { + extra_arg = (ot->type & OPT_HASARG) && !optarg; + } else { + /* Unknown (yet!). Guess if next arg is for this! */ + extra_arg = ((src == CONFIGVAR_CMDLINE_SHORT + || !strchr(arg, '=')) + && argv[i+1] + && !strstarts(argv[i+1], "-")); + } + finished_arg(argc, argv, &i, remove_args); + /* We turn `--foo bar` into `--foo=bar` here */ + if (extra_arg) { + configline = tal_fmt(tmpctx, "%s=%s", arg, argv[i]); + finished_arg(argc, argv, &i, remove_args); + } else { + configline = arg; + } + tal_arr_expand(&cvs, configvar_new(cvs, src, + NULL, 0, configline)); } - - parse_state = TOPLEVEL_CONFIG; - parse_implied_config_file(config_basedir, NULL, early); - parse_state = NETWORK_CONFIG; - parse_implied_config_file(config_basedir, chainparams->network_name, early); - parse_state = CMDLINE; + assert(argv[*argc] == NULL); + return cvs; } -void initial_config_opts(const tal_t *ctx, +void minimal_config_opts(const tal_t *ctx, int argc, char *argv[], - char **config_filename, - char **config_basedir, char **config_netdir, char **rpc_filename) { - options_ctx = ctx; - - /* First, they could specify a config, which specifies a lightning dir - * or a network. */ - *config_filename = NULL; - opt_register_early_arg("--conf=", opt_set_abspath, NULL, - config_filename, - "Specify configuration file"); + char *unused_filename, *unused_basedir; + + initial_config_opts(tmpctx, &argc, argv, false, + &unused_filename, + &unused_basedir, + config_netdir, + rpc_filename); + tal_steal(ctx, *config_netdir); + tal_steal(ctx, *rpc_filename); +} - /* Cmdline can also set lightning-dir. */ - *config_basedir = NULL; - opt_register_early_arg("--lightning-dir=", - opt_set_abspath, NULL, - config_basedir, - "Set base directory: network-specific subdirectory is under here"); +struct configvar **initial_config_opts(const tal_t *ctx, + int *argc, char *argv[], + bool remove_args, + char **config_filename, + char **config_basedir, + char **config_netdir, + char **rpc_filename) +{ + struct configvar **cmdline_cvs, **config_cvs, **cvs; - /* Handle --version (and exit) here too */ - opt_register_version(); + options_ctx = ctx; - opt_early_parse_incomplete(argc, argv, opt_log_stderr_exit); + /* This helps opt_usage. */ + opt_argv0 = argv[0]; - /* Now, reset and ignore --conf option from now on. */ - opt_free_table(); + /* Default chain (a global) is bitcoin. */ + chainparams = chainparams_for_network("bitcoin"); - /* This is only ever valid on cmdline */ + /* First, they could specify a config, or base dir. */ + *config_filename = NULL; opt_register_early_arg("--conf=", - opt_restricted_cmdline, NULL, + opt_set_config_filename, + /* Doesn't show if it's NULL! */ + opt_show_charp, config_filename, "Specify configuration file"); - - /* If they set --conf it can still set --lightning-dir */ - if (!*config_filename) { - opt_register_early_arg("--lightning-dir=", - opt_restricted_forceconf_only, opt_show_charp, - config_basedir, - "Set base directory: network-specific subdirectory is under here"); - } else { - opt_register_early_arg("--lightning-dir=", - opt_set_abspath, NULL, - config_basedir, - "Set base directory: network-specific subdirectory is under here"); - } - - /* Now, config file (or cmdline) can set network and lightning-dir */ - - /* We need to know network early, so we can set defaults (which normal - * options can change) and default config_netdir */ + *config_basedir = default_base_configdir(ctx); + opt_register_early_arg("--lightning-dir=", + opt_set_lightning_dir, opt_show_charp, + config_basedir, + "Set base directory: network-specific subdirectory is under here"); opt_register_early_arg("--network", opt_set_network, opt_show_network, NULL, "Select the network parameters (bitcoin, testnet," @@ -357,68 +339,78 @@ void initial_config_opts(const tal_t *ctx, opt_register_early_noarg("--mainnet", opt_set_specific_network, "bitcoin", "Alias for --network=bitcoin"); - opt_register_early_arg("--allow-deprecated-apis", - opt_set_bool_arg, opt_show_bool, - &deprecated_apis, - "Enable deprecated options, JSONRPC commands, fields, etc."); - - /* Read config file first, since cmdline must override */ - if (*config_filename) - parse_include(*config_filename, true, true, 0); - else - parse_implied_config_file(*config_basedir, NULL, true); - opt_early_parse_incomplete(argc, argv, opt_log_stderr_exit); - - /* We use a global (in common/utils.h) for the chainparams. */ - if (!chainparams) - chainparams = chainparams_for_network("bitcoin"); + opt_register_early_noarg("--regtest", + opt_set_specific_network, "regtest", + "Alias for --network=regtest"); + /* Handle --version (and exit) here too */ + opt_register_version(); + + /* Allow them to override rpc-file too. */ + *rpc_filename = default_rpcfile(ctx); + opt_register_early_arg("--rpc-file", opt_set_talstr, opt_show_charp, + rpc_filename, + "Set JSON-RPC socket (or /dev/tty)"); + + cmdline_cvs = gather_cmdline_args(tmpctx, argc, argv, remove_args); + parse_configvars(cmdline_cvs, true, false); + + /* Base default or direct config can set network */ + if (*config_filename) { + config_cvs = gather_file_configvars(NULL, + CONFIGVAR_EXPLICIT_CONF, + *config_filename, true, 0); + } else { + struct configvar **base_cvs, **net_cvs; + char *dir = path_join(tmpctx, take(path_cwd(NULL)), *config_basedir); + /* Optional: .lightning/config */ + base_cvs = gather_file_configvars(tmpctx, + CONFIGVAR_BASE_CONF, + path_join(tmpctx, dir, "config"), + false, 0); + /* This might set network! */ + parse_configvars(configvar_join(tmpctx, base_cvs, cmdline_cvs), + true, false); + + /* Now, we can get network config */ + dir = path_join(tmpctx, dir, chainparams->network_name); + net_cvs = gather_file_configvars(tmpctx, + CONFIGVAR_NETWORK_CONF, + path_join(tmpctx, dir, "config"), + false, 0); + config_cvs = configvar_join(NULL, take(base_cvs), take(net_cvs)); + } + cvs = configvar_join(ctx, take(config_cvs), cmdline_cvs); - if (!*config_basedir) - *config_basedir = default_base_configdir(ctx); + /* This will be called again, once caller has added their own + * early vars! */ + parse_configvars_early(cvs); *config_netdir = path_join(NULL, *config_basedir, chainparams->network_name); /* Make sure it's absolute */ *config_netdir = path_join(ctx, take(path_cwd(NULL)), take(*config_netdir)); + return cvs; +} - /* Now, reset and ignore those options from now on. */ - opt_free_table(); - - opt_register_early_arg("--conf=", - opt_restricted_cmdline, NULL, - config_filename, - "Specify configuration file"); +void parse_configvars_early(struct configvar **cvs) +{ + parse_configvars(cvs, true, false); +} - /* This is never in a default config file (since we used the defaults to find it!). */ - opt_register_early_arg("--lightning-dir=", - opt_restricted_forceconf_only, opt_show_charp, - config_basedir, - "Set base directory: network-specific subdirectory is under here"); - opt_register_early_arg("--network", - opt_restricted_toplevel, opt_show_network, - NULL, - "Select the network parameters (bitcoin, testnet," - " signet, regtest, litecoin or litecoin-testnet)"); - opt_register_early_noarg("--mainnet", - opt_restricted_toplevel_noarg, NULL, - "Alias for --network=bitcoin"); - opt_register_early_noarg("--testnet", - opt_restricted_toplevel_noarg, NULL, - "Alias for --network=testnet"); - opt_register_early_noarg("--signet", - opt_restricted_toplevel_noarg, NULL, - "Alias for --network=signet"); +void parse_configvars_final(struct configvar **cvs, + bool full_knowledge) +{ + parse_configvars(cvs, false, full_knowledge); + configvar_finalize_overrides(cvs); +} - /* They can set this later, it's just less effective. */ - opt_register_early_arg("--allow-deprecated-apis", - opt_set_bool_arg, opt_show_bool, - &deprecated_apis, - "Enable deprecated options, JSONRPC commands, fields, etc."); +bool is_restricted_ignored(const void *fn) +{ + return fn == opt_set_specific_network; +} - /* Set this up for when they parse cmdline proper. */ - *rpc_filename = default_rpcfile(ctx); - opt_register_arg("--rpc-file", opt_set_talstr, opt_show_charp, - rpc_filename, - "Set JSON-RPC socket (or /dev/tty)"); +bool is_restricted_print_if_nonnull(const void *fn) +{ + return fn == opt_set_config_filename; } diff --git a/common/configdir.h b/common/configdir.h index 1f2d713d2299..283b4ec9cbef 100644 --- a/common/configdir.h +++ b/common/configdir.h @@ -3,9 +3,8 @@ #include "config.h" #include -/* Put things we're going to get rid of behind this, so testers can catch - * them early. */ -extern bool deprecated_apis; +/* Unless overridden, we exit with status 1 when option parsing fails */ +extern int opt_exitcode; /* Helper for options which are tal() strings. */ char *opt_set_talstr(const char *arg, char **p); @@ -13,20 +12,28 @@ char *opt_set_talstr(const char *arg, char **p); /* Initial options setup */ void setup_option_allocators(void); -/* Parse minimal config options and files */ -void initial_config_opts(const tal_t *ctx, +/* Minimal config parsing for tools: use opt_early_parse/opt_parse after */ +void minimal_config_opts(const tal_t *ctx, int argc, char *argv[], - char **config_filename, - char **config_basedir, char **config_netdir, char **rpc_filename); -/* If they specify --conf, we just read that. - * Otherwise, we read basedir/config (toplevel), and basedir//config - * (network-level) */ -void parse_config_files(const char *config_filename, - const char *config_basedir, - bool early); +/* Parse initial config options and files */ +struct configvar **initial_config_opts(const tal_t *ctx, + int *argc, char *argv[], + bool remove_args, + char **config_filename, + char **config_basedir, + char **config_netdir, + char **rpc_filename); + +/* This is called before we know all the options. */ +void parse_configvars_early(struct configvar **cvs); + +/* This is called once, after we know all the options (if full_knowledge + * is false, ignore unknown non-cmdline options). */ +void parse_configvars_final(struct configvar **cvs, + bool full_knowledge); /* For listconfigs to detect. */ bool is_restricted_ignored(const void *fn); diff --git a/common/configvar.c b/common/configvar.c new file mode 100644 index 000000000000..84ddb164689f --- /dev/null +++ b/common/configvar.c @@ -0,0 +1,229 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +struct configvar *configvar_new(const tal_t *ctx, + enum configvar_src src, + const char *file, + size_t linenum, + const char *configline) +{ + struct configvar *cv = tal(ctx, struct configvar); + cv->file = tal_strdup_or_null(cv, file); + cv->src = src; + cv->linenum = linenum; + cv->configline = tal_strdup(cv, configline); + cv->overridden = false; + cv->optvar = NULL; + /* We fill in cv->optvar and cv->optarg when parsing! */ + return cv; +} + +const struct opt_table *configvar_unparsed(struct configvar *cv) +{ + const struct opt_table *ot; + + if (cv->src == CONFIGVAR_CMDLINE_SHORT) { + ot = opt_find_short(cv->configline[0]); + cv->optarg = NULL; + } else { + ot = opt_find_long(cv->configline, &cv->optarg); + } + if (!ot) + return NULL; + + /* We get called multiple times, but we're expected to always + * finish the cv vars, even if they're added at the last minute + * on the cmdline, so we check this is only done once, and we + * do it even if we're not going to use it now. */ + if (!cv->optvar) { + /* optvar is up to the = (i.e. one char before optarg) */ + if (!cv->optarg) + cv->optvar = cv->configline; + else + cv->optvar = tal_strndup(cv, cv->configline, + cv->optarg - cv->configline - 1); + } + return ot; +} + +const char *configvar_parse(struct configvar *cv, + bool early, + bool full_knowledge, + bool developer) +{ + const struct opt_table *ot; + + ot = configvar_unparsed(cv); + if (!ot) { + /* Do we ignore unknown entries? */ + if (!full_knowledge) + return NULL; + return "unknown option"; + } + + if ((ot->type & OPT_DEV) && !developer) + return "requires DEVELOPER"; + + /* If we're early and we want late, or vv, ignore. */ + if (!!(ot->type & OPT_EARLY) != early) + return NULL; + + if (ot->type & OPT_NOARG) { + /* MULTI doesn't make sense with single args */ + assert(!(ot->type & OPT_MULTI)); + if (cv->optarg) + return "doesn't allow an argument"; + return ot->cb(ot->u.arg); + } else { + if (!cv->optarg) + return "requires an argument"; + return ot->cb_arg(cv->optarg, ot->u.arg); + } +} + +/* This is O(N^2) but nobody cares */ +void configvar_finalize_overrides(struct configvar **cvs) +{ + /* Map to options: two different names can be the same option, + * given aliases! */ + const struct opt_table **opts; + + opts = tal_arr(tmpctx, const struct opt_table *, tal_count(cvs)); + for (size_t i = 0; i < tal_count(cvs); i++) { + opts[i] = opt_find_long(cvs[i]->optvar, NULL); + /* If you're allowed multiple, they don't override */ + if (opts[i]->type & OPT_MULTI) + continue; + for (size_t j = 0; j < i; j++) { + if (opts[j] == opts[i]) + cvs[j]->overridden = true; + } + } +} + +void configvar_remove(struct configvar ***cvs, + const char *name, + enum configvar_src src, + const char *optarg) +{ + /* We remove all from this source, potentially restoring an overridden */ + ssize_t prev = -1; + bool removed; + + removed = false; + for (size_t i = 0; i < tal_count(*cvs); i++) { + /* This can happen if plugin fails during startup! */ + if ((*cvs)[i]->optvar == NULL) + continue; + if (!streq((*cvs)[i]->optvar, name)) + continue; + if (optarg && !streq((*cvs)[i]->optarg, optarg)) + continue; + + if ((*cvs)[i]->src == src) { + tal_free((*cvs)[i]); + tal_arr_remove(cvs, i); + i--; + removed = true; + continue; + } + /* Wrong type, correct name. */ + prev = i; + } + + /* Unmark prev if we removed overriding ones. If it's multi, + * this is a noop. */ + if (removed && prev != -1) + (*cvs)[prev]->overridden = false; +} + +struct configvar *configvar_dup(const tal_t *ctx, const struct configvar *cv) +{ + struct configvar *ret; + + if (taken(cv)) + return tal_steal(ctx, cast_const(struct configvar *, cv)); + + ret = tal_dup(ctx, struct configvar, cv); + if (ret->file) + ret->file = tal_strdup(ret, ret->file); + if (ret->configline) + ret->configline = tal_strdup(ret, ret->configline); + if (ret->optvar) { + ret->optvar = tal_strdup(ret, ret->optvar); + /* Optarg, if non-NULL, points into cmdline! */ + if (ret->optarg) { + size_t off = cv->optarg - cv->configline; + assert(off < strlen(cv->configline)); + ret->optarg = ret->configline + off; + } + } + return ret; +} + +struct configvar **configvar_join(const tal_t *ctx, + struct configvar **first, + struct configvar **second) +{ + struct configvar **cvs; + size_t n = tal_count(first); + + if (taken(first)) { + cvs = tal_steal(ctx, first); + tal_resize(&cvs, n + tal_count(second)); + } else { + cvs = tal_arr(ctx, struct configvar *, n + tal_count(second)); + for (size_t i = 0; i < n; i++) { + cvs[i] = configvar_dup(cvs, first[i]); + } + } + if (taken(second)) { + for (size_t i = 0; i < tal_count(second); i++) + cvs[n + i] = tal_steal(cvs, second[i]); + tal_free(second); + } else { + for (size_t i = 0; i < tal_count(second); i++) + cvs[n + i] = configvar_dup(cvs, second[i]); + } + + return cvs; +} + +static struct configvar *configvar_iter(struct configvar **cvs, + const char **names, + const struct configvar *firstcv) +{ + for (size_t i = 0; i < tal_count(cvs); i++) { + /* Wait until we reach firstcv, if any */ + if (firstcv) { + if (cvs[i] == firstcv) + firstcv = NULL; + continue; + } + for (size_t j = 0; j < tal_count(names); j++) { + /* In case we iterate before initialization! */ + if (!cvs[i]->optvar) + continue; + if (streq(cvs[i]->optvar, names[j]) && !cvs[i]->overridden) + return cvs[i]; + } + } + return NULL; +} + +struct configvar *configvar_first(struct configvar **cvs, const char **names) +{ + return configvar_iter(cvs, names, NULL); +} + +struct configvar *configvar_next(struct configvar **cvs, + const struct configvar *cv, + const char **names) +{ + return configvar_iter(cvs, names, cv); +} diff --git a/common/configvar.h b/common/configvar.h new file mode 100644 index 000000000000..81ffda172960 --- /dev/null +++ b/common/configvar.h @@ -0,0 +1,187 @@ +#ifndef LIGHTNING_COMMON_CONFIGVAR_H +#define LIGHTNING_COMMON_CONFIGVAR_H +#include "config.h" +#include +#include +#include + +/* There are five possible sources of config options: + * 1. The cmdline (and the cmdline, but it's a short option!) + * 2. An explicitly named config (--conf=) (and its includes) + * 3. An implied config (~/.lightning/config) (and includes) + * 4. A network config ((~/.lightning//config) (and includes) + * 5. A plugin start parameter. + * + * Turns out we care: you can't set network in a network config for + * example. + */ +enum configvar_src { + CONFIGVAR_CMDLINE, + CONFIGVAR_CMDLINE_SHORT, + CONFIGVAR_EXPLICIT_CONF, + CONFIGVAR_BASE_CONF, + CONFIGVAR_NETWORK_CONF, + CONFIGVAR_PLUGIN_START, +}; + +/* This represents the configuration variables specified; they are + * matched with ccan/option's opt_table which contains the + * available options. */ +struct configvar { + /* NULL if CONFIGVAR_CMDLINE* or CONFIGVAR_PLUGIN_START */ + const char *file; + /* 1-based line number (unused if !file) */ + u32 linenum; + /* Where did we get this from? */ + enum configvar_src src; + /* Never NULL, the whole line */ + const char *configline; + + /* These are filled in by configvar_parse */ + /* The variable name (without any =) */ + const char *optvar; + /* NULL for no-arg options, otherwise points after =. */ + const char *optarg; + /* Was this overridden by a following option? */ + bool overridden; +}; + +/* Set if multiple options accumulate (for listconfigs) */ +#define OPT_MULTI (1 << OPT_USER_START) +/* Set if developer-only */ +#define OPT_DEV (1 << (OPT_USER_START+1)) +/* Doesn't return, so don't show in listconfigs */ +#define OPT_EXITS (1 << (OPT_USER_START+2)) +/* listconfigs should treat as a literal number */ +#define OPT_SHOWINT (1 << (OPT_USER_START+3)) +/* listconfigs should treat as a literal msat number */ +#define OPT_SHOWMSATS (1 << (OPT_USER_START+4)) +/* listconfigs should treat as a literal boolean `true` or `false` */ +#define OPT_SHOWBOOL (1 << (OPT_USER_START+5)) +/* Can be changed at runtime */ +#define OPT_DYNAMIC (1 << (OPT_USER_START+6)) + +/* Use this instead of opt_register_*_arg if you want OPT_* from above */ +#define clnopt_witharg(names, type, cb, show, arg, desc) \ + _opt_register((names), \ + OPT_CB_ARG((cb), (type), (show), (arg)), \ + (arg), (desc)) + +#define clnopt_noarg(names, type, cb, arg, desc) \ + _opt_register((names), \ + OPT_CB_NOARG((cb), (type), (arg)), \ + (arg), (desc)) + +/** + * configvar_new: allocate a fresh configvar + * @ctx: parent to tallocate off + * @src: where this came from + * @file: filename (or NULL if cmdline) + * @linenum: 1-based line number (or 0 for cmdline) + * @configline: literal option (for argv[], after `--`) + * + * optvar/optarg/multi/overridden are only set by configvar_parse. + */ +struct configvar *configvar_new(const tal_t *ctx, + enum configvar_src src, + const char *file TAKES, + size_t linenum, + const char *configline TAKES) + NON_NULL_ARGS(5); + +/** + * configvar_dup: copy a configvar + * @ctx: parent to tallocate off + * @cv: configvar to copy. + */ +struct configvar *configvar_dup(const tal_t *ctx, + const struct configvar *cv TAKES) + NON_NULL_ARGS(2); + +/** + * configvar_join: join two configvar arrays + * @ctx: parent to tallocate off + * @first: configvars to copy first + * @second: configvars to copy second. + */ +struct configvar **configvar_join(const tal_t *ctx, + struct configvar **first TAKES, + struct configvar **second TAKES); + +/** + * configvar_parse: parse this configuration variable + * @cv: the configuration setting. + * @early: if we're doing early parsing. + * @full_knowledge: error if we don't know this option. + * @developer: if we're in developer mode (allow OPT_DEV options). + * + * This returns a string if parsing failed: if early_and_incomplete is + * set, it doesn't complain about unknown options, and only parses + * OPT_EARLY options. Otherwise it parses all non-OPT_EARLY options, + * and returns an error if they don't exist. + * + * On NULL return (success), cv->optvar, cv->optarg, cv->mult are set. + */ +const char *configvar_parse(struct configvar *cv, + bool early, + bool full_knowledge, + bool developer) + NON_NULL_ARGS(1); + +/** + * configvar_unparsed: set up configuration variable, but don't parse it + * @cv: the configuration setting. + * + * This returns the opt_table which matches this configvar, if any, + * and if successful initializes cv->optvar and cv->optarg. + */ +const struct opt_table *configvar_unparsed(struct configvar *cv) + NON_NULL_ARGS(1); + +/** + * configvar_finalize_overrides: figure out which vars were overridden + * @cvs: the tal_arr of configuration settings. + * + * Any non-multi variables are overridden by successive ones. Sets + * cv->overridden for each configvar. + */ +void configvar_finalize_overrides(struct configvar **cvs); + +/** + * configvar_remove: remove the last configvar with this name if any. + * @cvs: pointer to tal_arr of configuration settings. + * @name: name to remove. + * @src: source type to remove. + * @optarg: if non-NULL, the argument to match too. + * + * We have to un-override the now-last setting, if any. + */ +void configvar_remove(struct configvar ***cvs, + const char *name, + enum configvar_src src, + const char *optarg) + NON_NULL_ARGS(1, 2); + +/** + * configvar_first: get the first non-overridden configvar of this name. + * @cvs: the tal_arr of configuration settings. + * @names: the tal_arr() of names to look for. + * + * Returns NULL if it wasn't set. + */ +struct configvar *configvar_first(struct configvar **cvs, const char **names); + +/** + * configvar_next: get the next non-overridden configvar of same name. + * @cvs: the tal_arr of configuration settings. + * @prev: the non-NULL return from configvar_first/configvar_next + * @names: the tal_arr() of names to look for. + * + * This can only return non-NULL for OPT_MULTI options which are actually + * specified multiple times. + */ +struct configvar *configvar_next(struct configvar **cvs, + const struct configvar *prev, + const char **names); + +#endif /* LIGHTNING_COMMON_CONFIGVAR_H */ diff --git a/common/cryptomsg.c b/common/cryptomsg.c index 8c4bd93c25be..1b50a1f189a4 100644 --- a/common/cryptomsg.c +++ b/common/cryptomsg.c @@ -118,7 +118,8 @@ u8 *cryptomsg_decrypt_body(const tal_t *ctx, return decrypted; } -bool cryptomsg_decrypt_header(struct crypto_state *cs, u8 hdr[18], u16 *lenp) +bool cryptomsg_decrypt_header(struct crypto_state *cs, const u8 hdr[18], + u16 *lenp) { unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; unsigned long long mlen; diff --git a/common/cryptomsg.h b/common/cryptomsg.h index 19c6d3e90786..3da33879c4bb 100644 --- a/common/cryptomsg.h +++ b/common/cryptomsg.h @@ -25,7 +25,8 @@ u8 *cryptomsg_encrypt_msg(const tal_t *ctx, struct crypto_state *cs, const u8 *msg); -bool cryptomsg_decrypt_header(struct crypto_state *cs, u8 hdr[18], u16 *lenp); +bool cryptomsg_decrypt_header(struct crypto_state *cs, const u8 hdr[18], + u16 *lenp); u8 *cryptomsg_decrypt_body(const tal_t *ctx, struct crypto_state *cs, const u8 *in); #endif /* LIGHTNING_COMMON_CRYPTOMSG_H */ diff --git a/common/features.c b/common/features.c index 8e0918db117c..89c1bd04746d 100644 --- a/common/features.c +++ b/common/features.c @@ -142,6 +142,10 @@ static const struct feature_style feature_styles[] = { { OPT_PROVIDE_PEER_BACKUP_STORAGE, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT } }, + { OPT_SPLICE, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, }; struct dependency { @@ -467,7 +471,7 @@ const char *feature_name(const tal_t *ctx, size_t f) "option_trampoline_routing", /* https://github.com/lightning/bolts/pull/836 */ NULL, NULL, /* 60/61 */ - NULL, + "option_splice", NULL, NULL, NULL, diff --git a/common/features.h b/common/features.h index 768cd0f68f3a..31728d1f2758 100644 --- a/common/features.h +++ b/common/features.h @@ -99,7 +99,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, #define COMPULSORY_FEATURE(x) ((x) & 0xFFFFFFFE) #define OPTIONAL_FEATURE(x) ((x) | 1) -/* BOLT #9: +/* BOLT-a526652801a541ed33b34d000a3b686a857c811f #9: * * | Bits | Name |... * | 0/1 | `option_data_loss_protect` |... IN ... @@ -118,6 +118,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, * | 26/27 | `option_shutdown_anysegwit` |... IN ... * | 44/45 | `option_channel_type` |... IN ... * | 48/49 | `option_payment_metadata` |... 9 ... + * | 62/63 | `option_splice` |... IN ... */ #define OPT_DATA_LOSS_PROTECT 0 #define OPT_INITIAL_ROUTING_SYNC 2 @@ -135,6 +136,7 @@ struct feature_set *feature_set_dup(const tal_t *ctx, #define OPT_SHUTDOWN_ANYSEGWIT 26 #define OPT_CHANNEL_TYPE 44 #define OPT_PAYMENT_METADATA 48 +#define OPT_SPLICE 62 /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: * | 28/29 | `option_dual_fund` | ... IN9 ... diff --git a/common/gossip_constants.h b/common/gossip_constants.h index ebfa8ed58217..32218a45b25f 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -7,7 +7,7 @@ * only onion tlv payloads. */ #define ROUTING_MAX_HOPS 20 -/* BOLT #7: +/* BOLT-f3a9f7f4e9e7a5a2997f3129e13d94090091846a #7: * * The `channel_flags` bitfield...individual bits: *... diff --git a/common/gossip_store.h b/common/gossip_store.h index d57af87fe1fe..ef6394dd7ce6 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -44,6 +44,11 @@ struct gossip_rcvd_filter; */ #define GOSSIP_STORE_ZOMBIE_BIT 0x1000U +/** + * Bit of flags used to mark a channel announcement closed (not deleted for 12 blocks) + */ +#define GOSSIP_STORE_DYING_BIT 0x0800U + /** * gossip_hdr -- On-disk format header. diff --git a/common/hsm_version.h b/common/hsm_version.h index 5c5c22af01c9..8470dce64226 100644 --- a/common/hsm_version.h +++ b/common/hsm_version.h @@ -13,6 +13,9 @@ * v4: 41a730986c51b930e2d8d12b3169d24966c2004e08d424bdda310edbbde5ba70 * v4 with check_pubkey: 48b3992745aa3c6ab6ce5cdaee9082cb7d70017f523d322015e9710bf49fd193 * v4 with sign_any_penalty_to_us: ead7963185194a515d1f14d2c44401392575299d68ce9a13d8a12baff3cf4f35 + * v4 with sign_anchorspend: 8a30722e38b56e82af566b9629ff18da01fcebd1e80ec67f04d8b3a2fa66d81c + * v4 with sign_htlc_tx_mingle: b9247e75d41ee1b3fc2f7db0bac8f4e92d544ab2f017d430ae3a000589c384e5 + * v4 with splicing: 06f21012936f825913af289fa81af1512c9ada1cb97c611698975a8fd287edbb */ #define HSM_MIN_VERSION 3 #define HSM_MAX_VERSION 4 diff --git a/common/htlc_trim.c b/common/htlc_trim.c index 711f9ec7d857..8f8fabfcca40 100644 --- a/common/htlc_trim.c +++ b/common/htlc_trim.c @@ -8,7 +8,8 @@ bool htlc_is_trimmed(enum side htlc_owner, u32 feerate_per_kw, struct amount_sat dust_limit, enum side side, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct amount_sat htlc_fee, htlc_min; @@ -24,7 +25,8 @@ bool htlc_is_trimmed(enum side htlc_owner, */ if (htlc_owner == side) htlc_fee = htlc_timeout_fee(feerate_per_kw, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); /* BOLT #3: * * - for every received HTLC: @@ -36,7 +38,8 @@ bool htlc_is_trimmed(enum side htlc_owner, */ else htlc_fee = htlc_success_fee(feerate_per_kw, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); /* If these overflow, it implies htlc must be less. */ if (!amount_sat_add(&htlc_min, dust_limit, htlc_fee)) diff --git a/common/htlc_trim.h b/common/htlc_trim.h index 0044de45c00b..777f632a33b9 100644 --- a/common/htlc_trim.h +++ b/common/htlc_trim.h @@ -10,7 +10,8 @@ bool htlc_is_trimmed(enum side htlc_owner, u32 feerate_per_kw, struct amount_sat dust_limit, enum side side, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Calculate the our htlc-trimming buffer feerate * (max(25%, 10s/vbyte) above feerate_per_kw) */ diff --git a/common/htlc_tx.c b/common/htlc_tx.c index 7427b2e152f4..798cbb381ccb 100644 --- a/common/htlc_tx.c +++ b/common/htlc_tx.c @@ -13,7 +13,8 @@ struct bitcoin_tx *htlc_tx(const tal_t *ctx, const u8 *htlc_tx_wscript, struct amount_sat htlc_fee, u32 locktime, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { /* BOLT #3: * * locktime: `0` for HTLC-success, `cltv_expiry` for HTLC-timeout @@ -43,7 +44,7 @@ struct bitcoin_tx *htlc_tx(const tal_t *ctx, * * `txin[0]` sequence: `0` (set to `1` for `option_anchors`) */ bitcoin_tx_add_input(tx, commit, - option_anchor_outputs ? 1 : 0, + (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) ? 1 : 0, NULL, amount, NULL, commit_wscript); /* BOLT #3: @@ -54,6 +55,7 @@ struct bitcoin_tx *htlc_tx(const tal_t *ctx, * * `txout[0]` script: version-0 P2WSH with witness script as shown * below */ + /* Note: for option_anchors_zero_fee_htlc_tx, htlc_fee is 0 */ if (!amount_sat_sub(&amount, amount, htlc_fee)) return tal_free(tx); @@ -74,7 +76,8 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { const u8 *htlc_wscript; @@ -90,9 +93,11 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx, amount_msat_to_sat_round_down(htlc_msatoshi), htlc_wscript, htlc_success_fee(feerate_per_kw, - option_anchor_outputs), + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx), 0, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } /* Fill in the witness for HTLC-success tx produced above. */ @@ -104,7 +109,8 @@ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, const struct bitcoin_signature *remotehtlcsig, const struct preimage *payment_preimage, const struct pubkey *revocationkey, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { struct sha256 hash; u8 *wscript, **witness; @@ -114,7 +120,8 @@ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, htlc_abstimeout, localhtlckey, remotehtlckey, &hash, revocationkey, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); witness = bitcoin_witness_htlc_success_tx(htlc_success, localhtlcsig, remotehtlcsig, @@ -132,7 +139,8 @@ struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { const u8 *htlc_wscript; @@ -148,9 +156,11 @@ struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx, amount_msat_to_sat_round_down(htlc_msatoshi), htlc_wscript, htlc_timeout_fee(feerate_per_kw, - option_anchor_outputs), + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx), cltv_expiry, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } /* Fill in the witness for HTLC-timeout tx produced above. */ @@ -161,13 +171,15 @@ void htlc_timeout_tx_add_witness(struct bitcoin_tx *htlc_timeout, const struct pubkey *revocationkey, const struct bitcoin_signature *localhtlcsig, const struct bitcoin_signature *remotehtlcsig, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { u8 **witness; u8 *wscript = bitcoin_wscript_htlc_offer(htlc_timeout, localhtlckey, remotehtlckey, payment_hash, revocationkey, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); witness = bitcoin_witness_htlc_timeout_tx(htlc_timeout, localhtlcsig, remotehtlcsig, wscript); @@ -178,21 +190,24 @@ void htlc_timeout_tx_add_witness(struct bitcoin_tx *htlc_timeout, u8 *htlc_offered_wscript(const tal_t *ctx, const struct ripemd160 *ripemd, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { return bitcoin_wscript_htlc_offer_ripemd160(ctx, &keyset->self_htlc_key, &keyset->other_htlc_key, ripemd, &keyset->self_revocation_key, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } u8 *htlc_received_wscript(const tal_t *ctx, const struct ripemd160 *ripemd, const struct abs_locktime *expiry, const struct keyset *keyset, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { return bitcoin_wscript_htlc_receive_ripemd(ctx, expiry, @@ -200,5 +215,6 @@ u8 *htlc_received_wscript(const tal_t *ctx, &keyset->other_htlc_key, ripemd, &keyset->self_revocation_key, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); } diff --git a/common/htlc_tx.h b/common/htlc_tx.h index f58608376456..67c7f42adb36 100644 --- a/common/htlc_tx.h +++ b/common/htlc_tx.h @@ -14,17 +14,23 @@ struct pubkey; struct ripemd160; static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { /* BOLT #3: * * The fee for an HTLC-timeout transaction: - *... + * - If `option_anchors_zero_fee_htlc_tx` applies: + * 1. MUST be 0. * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 663 (666 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ u32 base; + + if (option_anchors_zero_fee_htlc_tx) + return AMOUNT_SAT(0); + if (option_anchor_outputs) base = 666; else @@ -34,17 +40,23 @@ static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw, } static inline struct amount_sat htlc_success_fee(u32 feerate_per_kw, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { /* BOLT #3: * * The fee for an HTLC-success transaction: - *... + * - If `option_anchors_zero_fee_htlc_tx` applies: + * 1. MUST be 0. * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 703 (706 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ u32 base; + + if (option_anchors_zero_fee_htlc_tx) + return AMOUNT_SAT(0); + if (option_anchor_outputs) base = 706; else @@ -63,7 +75,8 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Fill in the witness for HTLC-success tx produced above. */ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, @@ -74,7 +87,8 @@ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, const struct bitcoin_signature *remotesig, const struct preimage *payment_preimage, const struct pubkey *revocationkey, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Create HTLC-timeout tx to spend an offered HTLC commitment tx * output; doesn't fill in input witness. */ @@ -87,7 +101,8 @@ struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Fill in the witness for HTLC-timeout tx produced above. */ void htlc_timeout_tx_add_witness(struct bitcoin_tx *htlc_timeout, @@ -97,8 +112,8 @@ void htlc_timeout_tx_add_witness(struct bitcoin_tx *htlc_timeout, const struct pubkey *revocationkey, const struct bitcoin_signature *localsig, const struct bitcoin_signature *remotesig, - bool option_anchor_outputs); - + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Generate the witness script for an HTLC the other side offered: * scriptpubkey_p2wsh(ctx, wscript) gives the scriptpubkey */ @@ -106,14 +121,16 @@ u8 *htlc_received_wscript(const tal_t *ctx, const struct ripemd160 *ripemd, const struct abs_locktime *expiry, const struct keyset *keyset, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Generate the witness script for an HTLC this side offered: * scriptpubkey_p2wsh(ctx, wscript) gives the scriptpubkey */ u8 *htlc_offered_wscript(const tal_t *ctx, const struct ripemd160 *ripemd, const struct keyset *keyset, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); /* Low-level HTLC tx creator */ struct bitcoin_tx *htlc_tx(const tal_t *ctx, @@ -124,5 +141,6 @@ struct bitcoin_tx *htlc_tx(const tal_t *ctx, const u8 *htlc_tx_wscript, struct amount_sat htlc_fee, u32 locktime, - bool option_anchor_outputs); + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx); #endif /* LIGHTNING_COMMON_HTLC_TX_H */ diff --git a/common/initial_channel.c b/common/initial_channel.c index cae1ad140201..8d073c32fe56 100644 --- a/common/initial_channel.c +++ b/common/initial_channel.c @@ -65,6 +65,11 @@ struct channel *new_initial_channel(const tal_t *ctx, = channel->view[LOCAL].owed[REMOTE] = remote_msatoshi; + channel->view[LOCAL].lowest_splice_amnt[LOCAL] = 0; + channel->view[LOCAL].lowest_splice_amnt[REMOTE] = 0; + channel->view[REMOTE].lowest_splice_amnt[LOCAL] = 0; + channel->view[REMOTE].lowest_splice_amnt[REMOTE] = 0; + channel->basepoints[LOCAL] = *local_basepoints; channel->basepoints[REMOTE] = *remote_basepoints; @@ -134,18 +139,47 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, direct_outputs, side, csv_lock, channel_has(channel, OPT_ANCHOR_OUTPUTS), + channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX), err_reason); if (init_tx) { psbt_input_add_pubkey(init_tx->psbt, 0, - &channel->funding_pubkey[side]); + &channel->funding_pubkey[side], false /* is_taproot */); psbt_input_add_pubkey(init_tx->psbt, 0, - &channel->funding_pubkey[!side]); + &channel->funding_pubkey[!side], false /* is_taproot */); } return init_tx; } +const char *channel_update_funding(struct channel *channel, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + s64 splice_amnt) +{ + s64 funding_diff = (s64)funding_sats.satoshis - (s64)channel->funding_sats.satoshis; /* Raw: splicing */ + s64 remote_splice_amnt = funding_diff - splice_amnt; + + channel->funding = *funding; + channel->funding_sats = funding_sats; + + if (splice_amnt * 1000 + channel->view[LOCAL].owed[LOCAL].millisatoshis < 0) /* Raw: splicing */ + return tal_fmt(tmpctx, "Channel funding update would make local" + " balance negative."); + + channel->view[LOCAL].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + channel->view[REMOTE].owed[LOCAL].millisatoshis += splice_amnt * 1000; /* Raw: splicing */ + + if (remote_splice_amnt * 1000 + channel->view[LOCAL].owed[REMOTE].millisatoshis < 0) /* Raw: splicing */ + return tal_fmt(tmpctx, "Channel funding update would make" + " remote balance negative."); + + channel->view[LOCAL].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */ + channel->view[REMOTE].owed[REMOTE].millisatoshis += remote_splice_amnt * 1000; /* Raw: splicing */ + + return NULL; +} + u32 channel_feerate(const struct channel *channel, enum side side) { return get_feerate(channel->fee_states, channel->opener, side); @@ -169,6 +203,10 @@ struct channel_type *channel_upgradable_type(const tal_t *ctx, struct channel_type *channel_desired_type(const tal_t *ctx, const struct channel *channel) { + /* We don't actually want to downgrade anchors! */ + if (channel_has(channel, OPT_ANCHORS_ZERO_FEE_HTLC_TX)) + return channel_type_anchors_zero_fee_htlc(ctx); + /* We don't actually want to downgrade anchors! */ if (channel_has(channel, OPT_ANCHOR_OUTPUTS)) return channel_type_anchor_outputs(ctx); @@ -182,6 +220,11 @@ bool channel_has(const struct channel *channel, int feature) return channel_type_has(channel->type, feature); } +bool channel_has_anchors(const struct channel *channel) +{ + return channel_type_has_anchors(channel->type); +} + static char *fmt_channel_view(const tal_t *ctx, const struct channel_view *view) { return tal_fmt(ctx, "{ owed_local=%s," diff --git a/common/initial_channel.h b/common/initial_channel.h index 713d55f5fc76..3c20270e523e 100644 --- a/common/initial_channel.h +++ b/common/initial_channel.h @@ -16,8 +16,19 @@ struct fulfilled_htlc; /* View from each side */ struct channel_view { - /* How much is owed to each side (includes pending changes) */ + /* How much is owed to each side (includes pending changes). + * The index of `owed` array is always relative to the machine + * this code is running on, so REMOTE is always the other machine + * and LOCAL is always this machine (regardless of view). + * + * For example: + * view[REMOTE].owed[REMOTE] == view[LOCAL].owed[REMOTE] + * view[REMOTE].owed[LOCAL] == view[LOCAL].owed[LOCAL] + */ struct amount_msat owed[NUM_SIDES]; + /* Lowest splice relative change amount of all candidate splices. + * This will be 0 or negative -- never positive. */ + s64 lowest_splice_amnt[NUM_SIDES]; }; struct channel { @@ -135,6 +146,17 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, struct wally_tx_output *direct_outputs[NUM_SIDES], char** err_reason); +/* channel_update_funding: Changes the funding for the channel and updates the + * balance by the difference between `old_local_funding_msatoshi` and + * `new_local_funding_msatoshi`. + * + * Returns NULL on success or an error on failure. + */ +const char *channel_update_funding(struct channel *channel, + const struct bitcoin_outpoint *funding, + struct amount_sat funding_sats, + s64 splice_amnt); + /** * channel_feerate: Get fee rate for this side of channel. * @channel: The channel @@ -159,4 +181,8 @@ struct channel_type *channel_desired_type(const tal_t *ctx, /* Convenience for querying channel->type */ bool channel_has(const struct channel *channel, int feature); + +/* Convenience for querying either anchor_outputs or anchors_zero_fee_htlc_tx */ +bool channel_has_anchors(const struct channel *channel); + #endif /* LIGHTNING_COMMON_INITIAL_CHANNEL_H */ diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index 5d6ade5b37d1..73e1a37b4b55 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -88,6 +88,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, enum side side, u32 csv_lock, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, char** err_reason) { struct amount_sat base_fee; @@ -122,15 +123,16 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * fee](#fee-calculation). */ base_fee = commit_tx_base_fee(feerate_per_kw, untrimmed, - option_anchor_outputs); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx); - /* BOLT: - * If `option_anchor_outputs` applies to the commitment + /* BOLT #3: + * If `option_anchors` applies to the commitment * transaction, also subtract two times the fixed anchor size * of 330 sats from the funder (either `to_local` or * `to_remote`). */ - if (option_anchor_outputs + if ((option_anchor_outputs || option_anchors_zero_fee_htlc_tx) && !amount_sat_add(&base_fee, base_fee, AMOUNT_SAT(660))) { *err_reason = "Funder cannot afford anchor outputs"; return NULL; @@ -246,7 +248,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, u8 *redeem; amount = amount_msat_to_sat_round_down(other_pay); - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { redeem = bitcoin_wscript_to_remote_anchored(tmpctx, &keyset->other_payment_key, (!side) == lessor ? csv_lock : 1); @@ -271,7 +273,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * * if `to_remote` exists or there are untrimmed HTLCs, add a * [`to_remote_anchor` output] */ - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { if (to_local || untrimmed != 0) { tx_add_anchor_output(tx, &funding_key[side]); output_order[n] = NULL; diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index 4e7e39b3b106..6e715831b09b 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -25,7 +25,8 @@ u64 commit_number_obscurer(const struct pubkey *opener_payment_basepoint, /* The base weight of a commitment tx */ static inline size_t commit_tx_base_weight(size_t num_untrimmed_htlcs, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { size_t weight; size_t num_outputs; @@ -36,7 +37,7 @@ static inline size_t commit_tx_base_weight(size_t num_untrimmed_htlcs, * - MUST be calculated to match: * 1. Start with `weight` = 724 (1124 if `option_anchors` applies). */ - if (option_anchor_outputs) { + if (option_anchor_outputs || option_anchors_zero_fee_htlc_tx) { weight = 1124; num_outputs = 4; } else { @@ -61,11 +62,13 @@ static inline size_t commit_tx_base_weight(size_t num_untrimmed_htlcs, /* Helper to calculate the base fee if we have this many htlc outputs */ static inline struct amount_sat commit_tx_base_fee(u32 feerate_per_kw, size_t num_untrimmed_htlcs, - bool option_anchor_outputs) + bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx) { return amount_tx_fee(feerate_per_kw, commit_tx_base_weight(num_untrimmed_htlcs, - option_anchor_outputs)); + option_anchor_outputs, + option_anchors_zero_fee_htlc_tx)); } /** @@ -107,6 +110,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, enum side side, u32 csv_lock, bool option_anchor_outputs, + bool option_anchors_zero_fee_htlc_tx, char** err_reason); /* try_subtract_fee - take away this fee from the opener (and return true), or all if insufficient (and return false). */ diff --git a/common/interactivetx.c b/common/interactivetx.c index 76b6de927678..b3cb21f18d51 100644 --- a/common/interactivetx.c +++ b/common/interactivetx.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include @@ -17,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include /* * BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2: @@ -64,14 +62,13 @@ struct interactivetx_context *new_interactivetx_context(const tal_t *ctx, { struct interactivetx_context *ictx = tal(ctx, struct interactivetx_context); - ictx->ctx = NULL; ictx->our_role = our_role; ictx->pps = pps; ictx->channel_id = channel_id; ictx->tx_add_input_count = 0; ictx->tx_add_output_count = 0; - ictx->next_update = default_next_update; - ictx->current_psbt = NULL; + ictx->next_update_fn = default_next_update; + ictx->current_psbt = create_psbt(ictx, 0, 0, 0); ictx->desired_psbt = NULL; ictx->pause_when_complete = false; ictx->change_set = NULL; @@ -83,13 +80,13 @@ static bool is_segwit_output(const tal_t *ctx, struct wally_tx_output *output, const u8 *redeemscript) { - const u8 *wit_prog; + const u8 *maybe_witness; if (tal_bytelen(redeemscript) > 0) - wit_prog = redeemscript; + maybe_witness = redeemscript; else - wit_prog = wally_tx_output_get_script(ctx, output); + maybe_witness = wally_tx_output_get_script(ctx, output); - return is_p2wsh(wit_prog, NULL) || is_p2wpkh(wit_prog, NULL); + return is_known_segwit_scripttype(maybe_witness); } /* Return first non-handled message or NULL if connection is aborted */ @@ -102,26 +99,14 @@ static u8 *read_next_msg(const tal_t *ctx, for (;;) { char *desc; bool warning; - struct channel_id actual; enum peer_wire t; - bool from_gossipd; /* Prevent runaway memory usage from many messages */ if (msg) tal_free(msg); /* This helper routine polls the peer. */ - msg = peer_or_gossip_sync_read(ctx, state->pps, &from_gossipd); - - /* Line should be in STFU mode and not receiving gossip */ - if (from_gossipd) { - *error = tal_fmt(ctx, "interactivetx got gossip but" - " should be in STFU mode."); - - tal_free(msg); - /* Return NULL so caller knows to stop negotiating. */ - return NULL; - } + msg = peer_read(ctx, state->pps); /* BOLT #1: * @@ -140,7 +125,7 @@ static u8 *read_next_msg(const tal_t *ctx, if (!desc) continue; - *error = tal_fmt(ctx, "They sent a %s: %s" + *error = tal_fmt(ctx, "They sent a %s: %s", warning ? "warning" : "error", desc); @@ -160,9 +145,11 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_TX_REMOVE_OUTPUT: case WIRE_TX_COMPLETE: return msg; + case WIRE_TX_ABORT: + /* TODO */ case WIRE_TX_SIGNATURES: - case WIRE_FUNDING_LOCKED: - case WIRE_INIT_RBF: + case WIRE_CHANNEL_READY: + case WIRE_TX_INIT_RBF: case WIRE_OPEN_CHANNEL2: case WIRE_INIT: case WIRE_ERROR: @@ -184,7 +171,7 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: - case WIRE_ACK_RBF: + case WIRE_TX_ACK_RBF: case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: case WIRE_NODE_ANNOUNCEMENT: @@ -196,9 +183,12 @@ static u8 *read_next_msg(const tal_t *ctx, case WIRE_PING: case WIRE_PONG: case WIRE_SHUTDOWN: -#if EXPERIMENTAL_FEATURES case WIRE_STFU: -#endif + case WIRE_PEER_STORAGE: + case WIRE_YOUR_PEER_STORAGE: + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: *error = tal_fmt(ctx, "Received invalid message from peer: %d", t); return NULL; @@ -213,7 +203,7 @@ static char *send_next(const tal_t *ctx, struct channel_id *cid = &ictx->channel_id; struct psbt_changeset *set = ictx->change_set; u64 serial_id; - u8 *msg = NULL; + u8 *msg; *finished = false; if (!set) @@ -221,35 +211,29 @@ static char *send_next(const tal_t *ctx, if (tal_count(set->added_ins) != 0) { const struct input_set *in = &set->added_ins[0]; - struct bitcoin_outpoint outpoint; u8 *prevtx; if (!psbt_get_serial_id(&in->input.unknowns, &serial_id)) - return "interactivetx ADD_INPUT PSBT has invalid serial_id."; + return "interactivetx ADD_INPUT PSBT has invalid" + " serial_id."; if (in->input.utxo) - prevtx = linearize_wtx(ctx, - in->input.utxo); + prevtx = linearize_wtx(ctx, in->input.utxo); else - return "interactivetx ADD_INPUT PSBT needs the previous transaction set."; - - memcpy(outpoint.txid.shad.sha.u.u8, - in->tx_input.txhash, - WALLY_TXHASH_LEN); - - outpoint.n = in->tx_input.index; + return "interactivetx ADD_INPUT PSBT needs the previous" + " transaction set."; msg = towire_tx_add_input(NULL, cid, serial_id, - prevtx, in->tx_input.index, - in->tx_input.sequence, - NULL); + prevtx, in->input.index, + in->input.sequence); tal_arr_remove(&set->added_ins, 0); } else if (tal_count(set->rm_ins) != 0) { if (!psbt_get_serial_id(&set->rm_ins[0].input.unknowns, &serial_id)) - return "interactivetx RM_INPUT PSBT has invalid serial_id."; + return "interactivetx RM_INPUT PSBT has invalid" + " serial_id."; msg = towire_tx_remove_input(NULL, cid, serial_id); @@ -264,11 +248,12 @@ static char *send_next(const tal_t *ctx, out = &set->added_outs[0]; if (!psbt_get_serial_id(&out->output.unknowns, &serial_id)) - return "interactivetx ADD_OUTPUT PSBT has invalid serial_id."; + return "interactivetx ADD_OUTPUT PSBT has invalid" + " serial_id."; - asset_amt = wally_tx_output_get_amount(&out->tx_output); + asset_amt = wally_psbt_output_get_amount(&out->output); sats = amount_asset_to_sat(&asset_amt); - script = wally_tx_output_get_script(ctx, &out->tx_output); + script = wally_psbt_output_get_script(ctx, &out->output); msg = towire_tx_add_output(NULL, cid, @@ -292,33 +277,74 @@ static char *send_next(const tal_t *ctx, if (!msg) return "Interactivetx send_next failed to build a message"; - sync_crypto_write(ictx->pps, take(msg)); + peer_write(ictx->pps, take(msg)); return NULL; tx_complete: - *finished = true; if (!ictx->pause_when_complete) { if (ictx->current_psbt->num_inputs > MAX_FUNDING_INPUTS) return tal_fmt(ctx, "Humbly refusing to `tx_complete` " "because we have too many inputs (%zu). " - "Limit is %zu." + "Limit is %d.", ictx->current_psbt->num_inputs, MAX_FUNDING_INPUTS); if (ictx->current_psbt->num_outputs > MAX_FUNDING_OUTPUTS) return tal_fmt(ctx, "Humbly refusing to `tx_complete` " "because we have too many outputs (%zu). " - "Limit is %zu." + "Limit is %d.", ictx->current_psbt->num_outputs, MAX_FUNDING_OUTPUTS); - msg = towire_tx_complete(ctx, cid); - sync_crypto_write(ictx->pps, msg); + msg = towire_tx_complete(NULL, cid); + peer_write(ictx->pps, take(msg)); } + *finished = true; return NULL; } +static struct psbt_changeset *get_changes(const tal_t *ctx, + struct interactivetx_context *ictx, + struct wally_psbt *next_psbt) +{ + u64 serial_id; + struct psbt_changeset *set = psbt_get_changeset(tmpctx, + ictx->current_psbt, + next_psbt); + + /* Remove duplicate serial_ids from the change set. */ + for (int i = 0; i < tal_count(set->added_ins); i++) { + struct bitcoin_outpoint point; + wally_psbt_input_get_outpoint(&set->added_ins[i].input, &point); + if (psbt_get_serial_id(&set->added_ins[i].input.unknowns, + &serial_id)) { + if (psbt_find_serial_input(ictx->current_psbt, + serial_id) != -1) + tal_arr_remove(&set->added_ins, i--); + else if (psbt_has_input(ictx->current_psbt, &point)) + tal_arr_remove(&set->added_ins, i--); + } + } + for (int i = 0; i < tal_count(set->added_outs); i++) + if (psbt_get_serial_id(&set->added_outs[i].output.unknowns, + &serial_id)) + if (psbt_find_serial_output(ictx->current_psbt, + serial_id) != -1) + tal_arr_remove(&set->added_outs, i--); + + return set; +} + +bool interactivetx_has_changes(struct interactivetx_context *ictx, + struct wally_psbt *next_psbt) +{ + struct psbt_changeset *set = get_changes(tmpctx, ictx, next_psbt); + + return tal_count(set->added_ins) || tal_count(set->rm_ins) + || tal_count(set->added_outs) || tal_count(set->rm_outs); +} + char *process_interactivetx_updates(const tal_t *ctx, struct interactivetx_context *ictx, bool *received_tx_complete) @@ -328,26 +354,21 @@ char *process_interactivetx_updates(const tal_t *ctx, char *error = NULL; struct wally_psbt *next_psbt; - if (ictx->current_psbt == NULL) - ictx->current_psbt = create_psbt(ictx, 0, 0, 0); - if (received_tx_complete) they_complete = *received_tx_complete; /* Build change_set and handle PSBT variables */ ictx->change_set = tal_free(ictx->change_set); - /* Call next_update or default to 'desired_psbt' */ - next_psbt = ictx->next_update(ictx, ictx); + /* Call next_update_fn or default to 'desired_psbt' */ + next_psbt = ictx->next_update_fn(ictx, ictx); - /* Returning NULL from next_update is the same as using `current_psbt` + /* Returning NULL from next_update_fn is the same as using `current_psbt` * with no changes -- both indicate no changes */ if (!next_psbt) next_psbt = ictx->current_psbt; - ictx->change_set = psbt_get_changeset(ictx, - ictx->current_psbt, - next_psbt); + ictx->change_set = get_changes(ctx, ictx, next_psbt); /* If current_psbt and next_psbt are the same, dont double free it! * Otherwise we advance `current_psbt` to `next_psbt` and begin @@ -355,7 +376,7 @@ char *process_interactivetx_updates(const tal_t *ctx, if (ictx->current_psbt != next_psbt) { /* psbt_get_changeset requires we keep the current_psbt until * we're done withh change_set */ - tal_steal(ictx->change_set, current_psbt); + tal_steal(ictx->change_set, ictx->current_psbt); ictx->current_psbt = next_psbt; } @@ -391,7 +412,7 @@ char *process_interactivetx_updates(const tal_t *ctx, t = fromwire_peektype(msg); switch (t) { case WIRE_TX_ADD_INPUT: { - const u8 *tx_bytes, *redeemscript; + const u8 *tx_bytes; u32 sequence; size_t len; struct bitcoin_tx *tx; @@ -401,9 +422,7 @@ char *process_interactivetx_updates(const tal_t *ctx, &serial_id, cast_const2(u8 **, &tx_bytes), - &outpoint.n, &sequence, - cast_const2(u8 **, - &redeemscript))) + &outpoint.n, &sequence)) return tal_fmt(ctx, "Parsing tx_add_input %s", tal_hex(ctx, msg)); @@ -437,7 +456,7 @@ char *process_interactivetx_updates(const tal_t *ctx, * the transaction */ if (psbt_find_serial_input(ictx->current_psbt, serial_id) != -1) - return tal_fmt(ctx, "Duplicate serial_id rcvd." + return tal_fmt(ctx, "Duplicate serial_id rcvd" " %"PRIu64, serial_id); /* Convert tx_bytes to a tx! */ @@ -461,7 +480,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (!is_segwit_output(ctx, &tx->wtx->outputs[outpoint.n], - redeemscript)) + NULL)) return tal_fmt(ctx, "Invalid tx sent. Not SegWit %s", type_to_string(ctx, @@ -493,7 +512,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (ictx->current_psbt->num_inputs + 1 > MAX_FUNDING_INPUTS) return tal_fmt(ctx, "Too many inputs. Have %zu," - " Max allowed %zu", + " Max allowed %d", ictx->current_psbt->num_inputs + 1, MAX_FUNDING_INPUTS); @@ -620,7 +639,7 @@ char *process_interactivetx_updates(const tal_t *ctx, */ if (ictx->current_psbt->num_outputs + 1 > MAX_FUNDING_OUTPUTS) return tal_fmt(ctx, "Too many inputs. Have %zu," - " Max allowed %zu", + " Max allowed %d", ictx->current_psbt->num_outputs + 1, MAX_FUNDING_OUTPUTS); @@ -678,6 +697,8 @@ char *process_interactivetx_updates(const tal_t *ctx, if (received_tx_complete) *received_tx_complete = true; break; + case WIRE_TX_ABORT: + /* Todo */ case WIRE_INIT: case WIRE_ERROR: case WIRE_WARNING: @@ -685,7 +706,7 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: @@ -699,13 +720,12 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: - case WIRE_INIT_RBF: - case WIRE_ACK_RBF: + case WIRE_TX_INIT_RBF: + case WIRE_TX_ACK_RBF: case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: case WIRE_NODE_ANNOUNCEMENT: @@ -715,11 +735,12 @@ char *process_interactivetx_updates(const tal_t *ctx, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_PING: case WIRE_PONG: -#if EXPERIMENTAL_FEATURES + case WIRE_PEER_STORAGE: + case WIRE_YOUR_PEER_STORAGE: case WIRE_SPLICE: case WIRE_SPLICE_ACK: case WIRE_STFU: -#endif + case WIRE_SPLICE_LOCKED: return tal_fmt(ctx, "Unexpected wire message %s", tal_hex(ctx, msg)); } @@ -731,5 +752,7 @@ char *process_interactivetx_updates(const tal_t *ctx, /* Sort psbt! */ psbt_sort_by_serial_id(ictx->current_psbt); + tal_steal(ictx, ictx->current_psbt); + return NULL; } diff --git a/common/interactivetx.h b/common/interactivetx.h index 0d8a27588947..a63d01896dd9 100644 --- a/common/interactivetx.h +++ b/common/interactivetx.h @@ -26,9 +26,6 @@ enum tx_msgs { struct interactivetx_context { - /* Users can set this to their own context */ - void *ctx; - enum tx_role our_role; struct per_peer_state *pps; struct channel_id channel_id; @@ -45,16 +42,14 @@ struct interactivetx_context { * If no more changes are demanded, return NULL or current_psbt * unchanged to signal completion. */ - struct wally_psbt *(*next_update)(const tal_t *ctx, + struct wally_psbt *(*next_update_fn)(const tal_t *ctx, struct interactivetx_context *ictx); - /* Set this to the intial psbt. If NULL will be filled with an empty - * psbt. - */ + /* Set this to the intial psbt. Defaults to an empty PSBT. */ struct wally_psbt *current_psbt; /* Optional field for storing your side's desired psbt state, to be - * used inside 'next_update'. + * used inside 'next_update_fn'. */ struct wally_psbt *desired_psbt; @@ -74,8 +69,8 @@ struct interactivetx_context *new_interactivetx_context(const tal_t *ctx, struct channel_id channel_id); /* Blocks the thread until we run out of changes (and we send tx_complete), - * or an error occurs. If 'pause_when_complete' is set, this behavior changes - * and we return without sending tx_complete. + * or an error occurs. If 'pause_when_complete' on the `interactivetx_context` + * is set, this behavior changes and we return without sending tx_complete. * * If received_tx_complete is not NULL: * in -> true means we assume we've received tx_complete in a previous round. @@ -87,4 +82,11 @@ char *process_interactivetx_updates(const tal_t *ctx, struct interactivetx_context *ictx, bool *received_tx_complete); +/* If the given ictx would cause `process_interactivetx_updates to send tx + * changes when called. Returns true if an error occurs + * (call `process_interactivetx_updates` for a description of the error). + */ +bool interactivetx_has_changes(struct interactivetx_context *ictx, + struct wally_psbt *next_psbt); + #endif /* LIGHTNING_COMMON_INTERACTIVETX_H */ diff --git a/common/json_command.h b/common/json_command.h index 8586ba72d1b0..8cbfc4db4f4c 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -36,6 +36,9 @@ command_fail_badparam(struct command *cmd, /* Also caller supplied: is this invoked simply to get usage? */ bool command_usage_only(const struct command *cmd); +/* Do we allow deprecated apis? */ +bool command_deprecated_apis(const struct command *cmd); + /* If so, this is called. */ void command_set_usage(struct command *cmd, const char *usage); diff --git a/common/json_param.c b/common/json_param.c index 7aa2174645eb..ceda9c7c3178 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -15,6 +15,11 @@ #include #include +/* Overridden by run-param.c */ +#ifndef paramcheck_assert +#define paramcheck_assert assert +#endif + struct param { const char *name; bool is_set; @@ -23,17 +28,17 @@ struct param { void *arg; }; -static bool param_add(struct param **params, +static void param_add(struct param **params, const char *name, enum param_style style, param_cbx cbx, void *arg) { -#if DEVELOPER - if (!(name && cbx && arg)) - return false; -#endif struct param last; + paramcheck_assert(name); + paramcheck_assert(cbx); + paramcheck_assert(arg); + last.is_set = false; last.name = name; last.style = style; @@ -41,7 +46,6 @@ static bool param_add(struct param **params, last.arg = arg; tal_arr_expand(params, last); - return true; } /* FIXME: To support the deprecated p_req_dup_ok */ @@ -115,7 +119,8 @@ static struct command_result *parse_by_position(struct command *cmd, return post_check(cmd, params); } -static struct param *find_param(struct param *params, const char *start, +static struct param *find_param(struct command *cmd, + struct param *params, const char *start, size_t n) { struct param *first = params; @@ -125,11 +130,11 @@ static struct param *find_param(struct param *params, const char *start, size_t arglen = strcspn(first->name, "|"); if (memeq(first->name, arglen, start, n)) return first; - if (deprecated_apis - && first->name[arglen] + if (first->name[arglen] && memeq(first->name + arglen + 1, strlen(first->name + arglen + 1), - start, n)) + start, n) + && command_deprecated_apis(cmd)) return first; first++; } @@ -146,7 +151,7 @@ static struct command_result *parse_by_name(struct command *cmd, const jsmntok_t *t; json_for_each_obj(i, t, tokens) { - struct param *p = find_param(params, buffer + t->start, + struct param *p = find_param(cmd, params, buffer + t->start, t->end - t->start); if (!p) { if (!allow_extra) { @@ -174,7 +179,6 @@ static struct command_result *parse_by_name(struct command *cmd, return post_check(cmd, params); } -#if DEVELOPER static int comp_by_name(const struct param *a, const struct param *b, void *unused) { @@ -207,54 +211,47 @@ static int comp_req_order(const struct param *a, const struct param *b, * Make sure 2 sequential items in @params are not equal (based on * provided comparator). */ -static bool check_distinct(struct param *params, - int (*compar) (const struct param *a, - const struct param *b, void *unused)) +static void check_distinct(const struct param *params, + int (*compar)(const struct param *a, + const struct param *b, void *unused)) { - struct param *first = params; - struct param *last = first + tal_count(params); + const struct param *first = params; + const struct param *last = first + tal_count(params); first++; while (first != last) { - if (compar(first - 1, first, NULL) == 0) - return false; + paramcheck_assert(compar(first - 1, first, NULL) != 0); first++; } - return true; } -static bool check_unique(struct param *copy, +static void check_unique(struct param *copy, int (*compar) (const struct param *a, const struct param *b, void *unused)) { asort(copy, tal_count(copy), compar, NULL); - return check_distinct(copy, compar); + check_distinct(copy, compar); } /* * Verify consistent internal state. */ -static bool check_params(struct param *params) +static void check_params(const struct param *params) { if (tal_count(params) < 2) - return true; + return; /* make sure there are no required params following optional */ - if (!check_distinct(params, comp_req_order)) - return false; + check_distinct(params, comp_req_order); /* duplicate so we can sort */ struct param *copy = tal_dup_talarr(params, struct param, params); /* check for repeated names and args */ - if (!check_unique(copy, comp_by_name)) - return false; - if (!check_unique(copy, comp_by_arg)) - return false; + check_unique(copy, comp_by_name); + check_unique(copy, comp_by_arg); tal_free(copy); - return true; } -#endif static char *param_usage(const tal_t *ctx, const struct param *params) @@ -278,12 +275,6 @@ static struct command_result *param_arr(struct command *cmd, const char *buffer, struct param *params, bool allow_extra) { -#if DEVELOPER - if (!check_params(params)) { - return command_fail(cmd, PARAM_DEV_ERROR, - "developer error: check_params"); - } -#endif if (tokens->type == JSMN_ARRAY) return parse_by_position(cmd, params, buffer, tokens, allow_extra); else if (tokens->type == JSMN_OBJECT) @@ -314,6 +305,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, for (size_t i = 0; i < tal_count(names); i++) tal_append_fmt(&usage, "%c%s", i == 0 ? '=' : '|', names[i]); + check_params(params); command_set_usage(cmd, usage); return NULL; } @@ -353,19 +345,12 @@ bool param(struct command *cmd, const char *buffer, allow_extra = true; continue; } - if (!param_add(¶ms, name, style, cbx, arg)) { - /* We really do ignore this return! */ - struct command_result *ignore; - ignore = command_fail(cmd, PARAM_DEV_ERROR, - "developer error: param_add %s", name); - assert(ignore); - va_end(ap); - return false; - } + param_add(¶ms, name, style, cbx, arg); } va_end(ap); if (command_usage_only(cmd)) { + check_params(params); command_set_usage(cmd, param_usage(cmd, params)); return false; } @@ -439,6 +424,15 @@ struct command_result *param_string(struct command *cmd, const char *name, return NULL; } +struct command_result *param_invstring(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str) +{ + const char *strtmp = json_strdup(cmd, buffer, tok); + *str = to_canonical_invstr(cmd, strtmp); + return NULL; +} + struct command_result *param_ignore(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const void *unused) @@ -509,6 +503,18 @@ struct command_result *param_u64(struct command *cmd, const char *name, "should be an unsigned 64 bit integer"); } +struct command_result *param_s64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + int64_t **num) +{ + *num = tal(cmd, int64_t); + if (json_to_s64(buffer, tok, *num)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be an sign 64 bit integer"); +} + struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_msat **msat) @@ -686,13 +692,14 @@ struct command_result *param_secrets_array(struct command *cmd, } /** - * segwit_addr_net_decode - Try to decode a Bech32 address and detect + * segwit_addr_net_decode - Try to decode a Bech32(m) address and detect * testnet/mainnet/regtest/signet * * This processes the address and returns a string if it is a Bech32 - * address specified by BIP173. The string is set whether it is - * testnet or signet (both "tb"), mainnet ("bc"), regtest ("bcrt") - * It does not check, witness version and program size restrictions. + * address specified by BIP173 or Bech32m as by BIP350. The string is + * set whether it is testnet or signet (both "tb"), mainnet ("bc"), + * regtest ("bcrt"). It does not check witness version and program size + * restrictions. * * Out: witness_version: Pointer to an int that will be updated to contain * the witness program version (between 0 and 16 inclusive). @@ -732,7 +739,7 @@ json_to_address_scriptpubkey(const tal_t *ctx, size_t witness_program_len; char *addrz; - const char *bip173; + const char *bech32; u8 addr_version; @@ -756,12 +763,12 @@ json_to_address_scriptpubkey(const tal_t *ctx, addrz = tal_dup_arr(tmpctx, char, buffer + tok->start, tok->end - tok->start, 1); addrz[tok->end - tok->start] = '\0'; - bip173 = segwit_addr_net_decode(&witness_version, witness_program, + bech32 = segwit_addr_net_decode(&witness_version, witness_program, &witness_program_len, addrz, chainparams); - if (bip173) { + if (bech32) { bool witness_ok; - /* We know the rules for v0, rest remain undefined */ + /* Only V0 has restricted lengths of witness programs */ if (witness_version == 0) { witness_ok = (witness_program_len == 20 || witness_program_len == 32); @@ -771,7 +778,7 @@ json_to_address_scriptpubkey(const tal_t *ctx, if (!witness_ok) return ADDRESS_PARSE_UNRECOGNIZED; - if (!streq(bip173, chainparams->onchain_hrp)) + if (!streq(bech32, chainparams->onchain_hrp)) return ADDRESS_PARSE_WRONG_NETWORK; *scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version, diff --git a/common/json_param.h b/common/json_param.h index 19c924b66b92..f920791cd90a 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -181,6 +181,12 @@ struct command_result *param_string(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, const char **str); +/* Extract an invoice string from a generic string, strip the `lightning:` + * prefix from it if needed. */ +struct command_result *param_invstring(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str); + /* Extract a label. It is either an escaped string or a number. */ struct command_result *param_label(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, @@ -206,6 +212,11 @@ struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num); +/* Extract number from this (may be a string, or a number literal) */ +struct command_result *param_s64(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + int64_t **num); + /* Extract msatoshi amount from this string */ struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, diff --git a/common/json_parse.c b/common/json_parse.c index 14e5d6102c60..0791ce2e1c08 100644 --- a/common/json_parse.c +++ b/common/json_parse.c @@ -23,30 +23,6 @@ #include #include -bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num) -{ - char *end; - long long l; - - l = strtoll(buffer + tok->start, &end, 0); - if (end != buffer + tok->end) - return false; - - BUILD_ASSERT(sizeof(l) >= sizeof(*num)); - *num = l; - - /* Check for overflow/underflow */ - if ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) - return false; - - /* Check if the number did not fit in `s64` (in case `long long` - is a bigger type). */ - if (*num != l) - return false; - - return true; -} - bool json_to_millionths(const char *buffer, const jsmntok_t *tok, u64 *millionths) { @@ -193,6 +169,9 @@ static const char *handle_percent(const char *buffer, void **p; p = va_arg(*ap, void **); talfmt = va_arg(*ap, void *(*)(void *, const char *, const jsmntok_t *)); + /* If we're skipping this, don't call fmt! */ + if (!tok) + return NULL; *p = talfmt(ctx, buffer, tok); if (*p != NULL) return NULL; @@ -202,6 +181,9 @@ static const char *handle_percent(const char *buffer, p = va_arg(*ap, void *); fmt = va_arg(*ap, bool (*)(const char *, const jsmntok_t *, void *)); + /* If we're skipping this, don't call fmt! */ + if (!tok) + return NULL; if (fmt(buffer, tok, p)) return NULL; } @@ -215,7 +197,8 @@ static const char *handle_percent(const char *buffer, /* GUIDE := OBJ | ARRAY | '%' * OBJ := '{' FIELDLIST '}' * FIELDLIST := FIELD [',' FIELD]* - * FIELD := LITERAL ':' FIELDVAL + * FIELD := MEMBER ':' FIELDVAL + * MEMBER := LITERAL | LITERAL '?' * FIELDVAL := OBJ | ARRAY | LITERAL | '%' * ARRAY := '[' ARRLIST ']' * ARRLIST := ARRELEM [',' ARRELEM]* @@ -264,6 +247,15 @@ static void guide_must_be(const char **guide, char c) assert(actual == c); } +static bool guide_maybe_optional(const char **guide) +{ + if (**guide == '?') { + guide_consume_one(guide); + return true; + } + return false; +} + /* Recursion: return NULL on success, errmsg on fail */ static const char *parse_obj(const char *buffer, const jsmntok_t *tok, @@ -325,8 +317,8 @@ static const char *parse_fieldval(const char *buffer, /* Literal must match exactly (modulo quotes for strings) */ parse_literal(guide, &literal, &len); - if (!memeq(buffer + tok->start, tok->end - tok->start, - literal, len)) { + if (tok && !memeq(buffer + tok->start, tok->end - tok->start, + literal, len)) { return tal_fmt(tmpctx, "%.*s does not match expected %.*s", json_tok_full_len(tok), @@ -345,14 +337,20 @@ static const char *parse_field(const char *buffer, const jsmntok_t *member; size_t len; const char *memname; + bool optional; parse_literal(guide, &memname, &len); + optional = guide_maybe_optional(guide); guide_must_be(guide, ':'); - member = json_get_membern(buffer, tok, memname, len); - if (!member) { - return tal_fmt(tmpctx, "object does not have member %.*s", - (int)len, memname); + if (tok) { + member = json_get_membern(buffer, tok, memname, len); + if (!member && !optional) { + return tal_fmt(tmpctx, "object does not have member %.*s", + (int)len, memname); + } + } else { + member = NULL; } return parse_fieldval(buffer, member, guide, ap); @@ -385,7 +383,7 @@ static const char *parse_obj(const char *buffer, guide_must_be(guide, '{'); - if (tok->type != JSMN_OBJECT) { + if (tok && tok->type != JSMN_OBJECT) { return tal_fmt(tmpctx, "token is not an object: %.*s", json_tok_full_len(tok), json_tok_full(buffer, tok)); @@ -410,12 +408,16 @@ static const char *parse_arrelem(const char *buffer, parse_number(guide, &idx); guide_must_be(guide, ':'); - member = json_get_arr(tok, idx); - if (!member) { - return tal_fmt(tmpctx, "token has no index %u: %.*s", - idx, - json_tok_full_len(tok), - json_tok_full(buffer, tok)); + if (tok) { + member = json_get_arr(tok, idx); + if (!member) { + return tal_fmt(tmpctx, "token has no index %u: %.*s", + idx, + json_tok_full_len(tok), + json_tok_full(buffer, tok)); + } + } else { + member = NULL; } return parse_fieldval(buffer, member, guide, ap); @@ -448,7 +450,7 @@ static const char *parse_arr(const char *buffer, guide_must_be(guide, '['); - if (tok->type != JSMN_ARRAY) { + if (tok && tok->type != JSMN_ARRAY) { return tal_fmt(tmpctx, "token is not an array: %.*s", json_tok_full_len(tok), json_tok_full(buffer, tok)); @@ -683,27 +685,6 @@ json_to_blinded_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) return rpath; } -bool json_to_uintarr(const char *buffer, const jsmntok_t *tok, u64 **dest) -{ - char *str = json_strdup(NULL, buffer, tok); - char *endp, **elements = tal_strsplit(str, str, ",", STR_NO_EMPTY); - unsigned long long l; - u64 u; - for (int i = 0; elements[i] != NULL; i++) { - /* This is how the manpage says to do it. Yech. */ - errno = 0; - l = strtoull(elements[i], &endp, 0); - if (*endp || !str[0]) - return tal_fmt(NULL, "'%s' is not a number", elements[i]); - u = l; - if (errno || u != l) - return tal_fmt(NULL, "'%s' is out of range", elements[i]); - tal_arr_expand(dest, u); - } - tal_free(str); - return NULL; -} - bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid) diff --git a/common/json_parse.h b/common/json_parse.h index fef86b7b3e8b..27cae148ad3e 100644 --- a/common/json_parse.h +++ b/common/json_parse.h @@ -114,9 +114,6 @@ bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, enum mvt_tag *tag); -/* Read a JSON value into an array of u64 */ -bool json_to_uintarr(const char *buffer, const jsmntok_t *tok, u64 **dest); - /* Extract reply path from this JSON */ struct blinded_path * json_to_blinded_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); @@ -125,7 +122,7 @@ bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid); /* Guide is % for a token: each must be followed by JSON_SCAN(). - * Returns NULL on error (asserts() on bad guide). */ + * Returns NULL on success, otherwise errmsg (asserts() on bad guide). */ const char *json_scan(const tal_t *ctx, const char *buffer, const jsmntok_t *tok, diff --git a/common/json_parse_simple.c b/common/json_parse_simple.c index 0ccbd9573a5c..7f9c630ebeb2 100644 --- a/common/json_parse_simple.c +++ b/common/json_parse_simple.c @@ -88,6 +88,30 @@ bool json_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num) return true; } +bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num) +{ + char *end; + long long l; + + l = strtoll(buffer + tok->start, &end, 0); + if (end != buffer + tok->end) + return false; + + BUILD_ASSERT(sizeof(l) >= sizeof(*num)); + *num = l; + + /* Check for overflow/underflow */ + if ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) + return false; + + /* Check if the number did not fit in `s64` (in case `long long` + is a bigger type). */ + if (*num != l) + return false; + + return true; +} + bool json_str_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num) { jsmntok_t temp; diff --git a/common/json_parse_simple.h b/common/json_parse_simple.h index 2c7c3c8975a6..710520d6f327 100644 --- a/common/json_parse_simple.h +++ b/common/json_parse_simple.h @@ -35,6 +35,9 @@ char *json_strdup(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); /* Extract number from this (may be a string, or a number literal) */ bool json_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num); +/* Extract signed 64 bit integer from this (may be a string, or a number literal) */ +bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num); + /* Extract number from string. The number must be the entirety of the * string between the '"' */ bool json_str_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num); diff --git a/common/json_stream.c b/common/json_stream.c index a778d98bd98d..7ef3750ddf9b 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -38,7 +38,7 @@ static void adjust_io_write(struct json_out *jout, struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer, - struct log *log) + struct logger *log) { struct json_stream *js = tal(ctx, struct json_stream); @@ -73,7 +73,7 @@ const char *json_stream_detach_filter(const tal_t *ctx, struct json_stream *js) struct json_stream *json_stream_dup(const tal_t *ctx, struct json_stream *original, - struct log *log) + struct logger *log) { struct json_stream *js = tal_dup(ctx, struct json_stream, original); @@ -95,13 +95,6 @@ static bool json_stream_still_writing(const struct json_stream *js) return js->writer != NULL; } -void json_stream_log_suppress(struct json_stream *js, const char *cmd_name) -{ - /* Really shouldn't be used for anything else */ - assert(streq(cmd_name, "getlog")); - js->log = NULL; -} - void json_stream_append(struct json_stream *js, const char *str, size_t len) { @@ -493,38 +486,54 @@ void json_add_short_channel_id(struct json_stream *response, short_channel_id_outnum(scid)); } -void json_add_address(struct json_stream *response, const char *fieldname, - const struct wireaddr *addr) +static void json_add_address_fields(struct json_stream *response, + const struct wireaddr *addr, + const char *typefield) { - json_object_start(response, fieldname); - if (addr->type == ADDR_TYPE_IPV4) { + switch (addr->type) { + case ADDR_TYPE_IPV4: { char addrstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN); - json_add_string(response, "type", "ipv4"); + json_add_string(response, typefield, "ipv4"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_IPV6) { + return; + } + case ADDR_TYPE_IPV6: { char addrstr[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN); - json_add_string(response, "type", "ipv6"); + json_add_string(response, typefield, "ipv6"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_TOR_V2_REMOVED) { - json_add_string(response, "type", "torv2"); + return; + } + case ADDR_TYPE_TOR_V2_REMOVED: { + json_add_string(response, typefield, "torv2"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_TOR_V3) { - json_add_string(response, "type", "torv3"); + return; + } + case ADDR_TYPE_TOR_V3: { + json_add_string(response, typefield, "torv3"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_DNS) { - json_add_string(response, "type", "dns"); + return; + } + case ADDR_TYPE_DNS: { + json_add_string(response, typefield, "dns"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_WEBSOCKET) { - json_add_string(response, "type", "websocket"); - json_add_num(response, "port", addr->port); + return; + } } + abort(); +} + +void json_add_address(struct json_stream *response, const char *fieldname, + const struct wireaddr *addr) +{ + json_object_start(response, fieldname); + json_add_address_fields(response, addr, "type"); json_object_end(response); } @@ -541,8 +550,13 @@ void json_add_address_internal(struct json_stream *response, return; case ADDR_INTERNAL_ALLPROTO: json_object_start(response, fieldname); - json_add_string(response, "type", "any protocol"); - json_add_num(response, "port", addr->u.port); + if (addr->u.allproto.is_websocket) { + json_add_string(response, "type", "websocket"); + json_add_string(response, "subtype", "any protocol"); + } else { + json_add_string(response, "type", "any protocol"); + } + json_add_num(response, "port", addr->u.allproto.port); json_object_end(response); return; case ADDR_INTERNAL_AUTOTOR: @@ -565,7 +579,14 @@ void json_add_address_internal(struct json_stream *response, json_object_end(response); return; case ADDR_INTERNAL_WIREADDR: - json_add_address(response, fieldname, &addr->u.wireaddr); + json_object_start(response, fieldname); + if (addr->u.wireaddr.is_websocket) { + json_add_string(response, "type", "websocket"); + json_add_address_fields(response, &addr->u.wireaddr.wireaddr, "subtype"); + } else { + json_add_address_fields(response, &addr->u.wireaddr.wireaddr, "type"); + } + json_object_end(response); return; } abort(); diff --git a/common/json_stream.h b/common/json_stream.h index 55a0e16a69b1..bc68569caa08 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -17,7 +17,7 @@ struct command; struct io_conn; -struct log; +struct logger; struct json_escape; struct pubkey; struct bip340sig; @@ -53,7 +53,7 @@ struct json_stream { struct json_filter *filter; /* Where to log I/O */ - struct log *log; + struct logger *log; }; @@ -64,7 +64,7 @@ struct json_stream { * @log: where to log the IO */ struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer, - struct log *log); + struct logger *log); /** * Duplicate an existing stream. @@ -80,7 +80,7 @@ struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer, */ struct json_stream *json_stream_dup(const tal_t *ctx, struct json_stream *original, - struct log *log); + struct logger *log); /* Attach a filter. Usually this works at the result level: you don't * want to filter out id, etc! */ @@ -97,9 +97,6 @@ const char *json_stream_detach_filter(const tal_t *ctx, struct json_stream *js); */ void json_stream_close(struct json_stream *js, struct command *writer); -/* For low-level JSON stream access: */ -void json_stream_log_suppress(struct json_stream *js, const char *cmd_name); - /* '"fieldname" : [ ' or '[ ' if fieldname is NULL */ void json_array_start(struct json_stream *js, const char *fieldname); /* '"fieldname" : { ' or '{ ' if fieldname is NULL */ diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index 6744e87d30c6..30163d1790a6 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -63,6 +63,16 @@ enum jsonrpc_errcode { FUNDING_V2_NOT_SUPPORTED = 310, FUNDING_UNKNOWN_CHANNEL = 311, FUNDING_STATE_INVALID = 312, + FUND_CANNOT_AFFORD_WITH_EMERGENCY = 313, + + /* Splice errors */ + SPLICE_BROADCAST_FAIL = 350, + SPLICE_WRONG_OWNER = 351, + SPLICE_UNKNOWN_CHANNEL = 352, + SPLICE_INVALID_CHANNEL_STATE = 353, + SPLICE_NOT_SUPPORTED = 354, + SPLICE_BUSY_ERROR = 355, + SPLICE_INPUT_ERROR = 356, /* `connect` errors */ CONNECT_NO_KNOWN_ADDRESS = 400, @@ -110,6 +120,11 @@ enum jsonrpc_errcode { /* Errors from delforward command */ DELFORWARD_NOT_FOUND = 1401, + /* Errors from runes */ + RUNE_NOT_AUTHORIZED = 1501, + RUNE_NOT_PERMITTED = 1502, + RUNE_BLACKLISTED = 1503, + /* Errors from wait* commands */ WAIT_TIMEOUT = 2000, }; diff --git a/common/peer_failed.c b/common/peer_failed.c index 5281bf86da9f..900a8c5b8cb4 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -42,6 +42,7 @@ peer_failed(struct per_peer_state *pps, msg = towire_status_peer_error(NULL, channel_id, desc, warn, + false, msg); peer_billboard(true, desc); peer_fatal_continue(take(msg), pps); @@ -80,7 +81,8 @@ void peer_failed_err(struct per_peer_state *pps, void peer_failed_received_errmsg(struct per_peer_state *pps, const char *desc, const struct channel_id *channel_id, - bool warning) + bool warning, + bool abort_restart) { u8 *msg; @@ -94,7 +96,7 @@ void peer_failed_received_errmsg(struct per_peer_state *pps, warning = true; } msg = towire_status_peer_error(NULL, channel_id, desc, warning, - NULL); + abort_restart, NULL); peer_billboard(true, "Received %s", desc); peer_fatal_continue(take(msg), pps); } diff --git a/common/peer_failed.h b/common/peer_failed.h index db1ff26a2827..0cff74b2a5d9 100644 --- a/common/peer_failed.h +++ b/common/peer_failed.h @@ -37,7 +37,8 @@ void peer_failed_err(struct per_peer_state *pps, void peer_failed_received_errmsg(struct per_peer_state *pps, const char *desc, const struct channel_id *channel_id, - bool soft_error) + bool soft_error, + bool abort_restart) NORETURN; /* I/O error */ diff --git a/common/peer_status_wire.csv b/common/peer_status_wire.csv index 1fedb7ccc678..7ad49eb4201b 100644 --- a/common/peer_status_wire.csv +++ b/common/peer_status_wire.csv @@ -8,5 +8,7 @@ msgdata,status_peer_error,channel,channel_id, msgdata,status_peer_error,desc,wirestring, # Take a deep breath, then try reconnecting to the precious little snowflake. msgdata,status_peer_error,warning,bool, +# From an abort, no reconnect but restart daemon +msgdata,status_peer_error,abort_do_restart,bool, msgdata,status_peer_error,len,u16, msgdata,status_peer_error,error_for_them,u8,len diff --git a/common/psbt_internal.c b/common/psbt_internal.c index 475b4cabc3ae..aca36b6ad96a 100644 --- a/common/psbt_internal.c +++ b/common/psbt_internal.c @@ -1,33 +1,97 @@ #include "config.h" +#include +#include #include +#include #include #include #include + +static bool next_size(const u8 **cursor, size_t *max, size_t *size) +{ + size_t len; + varint_t varint; + + if (*max < 1) + return false; + + len = varint_get(*cursor, *max, &varint); + + if (len < 1) + return false; + + if (*max < len) { + *max = 0; + return false; + } + + *cursor += len; + *max -= len; + *size = varint; + return true; +} + +static u8 *next_script(const tal_t *ctx, const u8 **cursor, size_t *max) +{ + const u8 *p; + size_t size; + u8 *ret; + + if (!next_size(cursor, max, &size)) + return NULL; + + if (*max < size) { + *max = 0; + return NULL; + } + + p = *cursor; + *max -= size; + *cursor += size; + + ret = tal_arr(ctx, u8, size); + memcpy(ret, p, size); + return ret; +} + static void psbt_input_set_final_witness_stack(const tal_t *ctx, struct wally_psbt_input *in, - const struct witness_element **elements) + const struct witness *witness) { + u8 *script, *sctx; + const u8 *data = witness->witness_data; + size_t size, max = tal_count(data); + bool ok; + wally_tx_witness_stack_free(in->final_witness); + /* FIXME: return an error?? */ + if (!next_size(&data, &max, &size)) + return; + tal_wally_start(); - wally_tx_witness_stack_init_alloc(tal_count(elements), - &in->final_witness); + sctx = tal(NULL, u8); + + wally_tx_witness_stack_init_alloc(size, &in->final_witness); + + while ((script = next_script(sctx, &data, &max)) && script != NULL) { + ok = (wally_tx_witness_stack_add(in->final_witness, + script, tal_count(script)) == WALLY_OK); + assert(ok); + } - for (size_t i = 0; i < tal_count(elements); i++) - wally_tx_witness_stack_add(in->final_witness, - elements[i]->witness_data, - tal_bytelen(elements[i]->witness_data)); tal_wally_end(ctx); + tal_free(sctx); } void psbt_finalize_input(const tal_t *ctx, struct wally_psbt_input *in, - const struct witness_element **elements) + const struct witness *witness) { const struct wally_map_item *redeem_script; - psbt_input_set_final_witness_stack(ctx, in, elements); + psbt_input_set_final_witness_stack(ctx, in, witness); /* There's this horrible edgecase where we set the final_witnesses * directly onto the PSBT, but the input is a P2SH-wrapped input @@ -49,22 +113,24 @@ void psbt_finalize_input(const tal_t *ctx, } } -const struct witness_stack ** -psbt_to_witness_stacks(const tal_t *ctx, - const struct wally_psbt *psbt, - enum tx_role side_to_stack) +const struct witness ** +psbt_to_witnesses(const tal_t *ctx, + const struct wally_psbt *psbt, + enum tx_role side_to_stack, + int input_index_to_ignore) { - size_t stack_index; u64 serial_id; - const struct witness_stack **stacks - = tal_arr(ctx, const struct witness_stack *, psbt->num_inputs); + const struct witness **witnesses = + tal_arr(ctx, const struct witness *, 0); - stack_index = 0; for (size_t i = 0; i < psbt->num_inputs; i++) { if (!psbt_get_serial_id(&psbt->inputs[i].unknowns, &serial_id)) /* FIXME: throw an error ? */ - return NULL; + return tal_free(witnesses); + + if (input_index_to_ignore == i) + continue; /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2: * - if is the *initiator*: @@ -73,32 +139,29 @@ psbt_to_witness_stacks(const tal_t *ctx, if (serial_id % 2 == side_to_stack) { struct wally_tx_witness_stack *wtx_s = psbt->inputs[i].final_witness; - struct witness_stack *stack = - tal(stacks, struct witness_stack); - /* Convert the wally_tx_witness_stack to - * a witness_stack entry */ - stack->witness_elements = - tal_arr(stack, struct witness_element *, - wtx_s->num_items); - for (size_t j = 0; j < tal_count(stack->witness_elements); j++) { - stack->witness_elements[j] = tal(stack, - struct witness_element); - stack->witness_elements[j]->witness_data = - tal_dup_arr(stack, u8, - wtx_s->items[j].witness, - wtx_s->items[j].witness_len, - 0); + /* BOLT-e299850cb5ebd8bd9c55763bbc498fcdf94a9567 #2: + * + * The `witness_data` is encoded as per bitcoin's + * wire protocol (a CompactSize number of elements, + * with each element a CompactSize length and that + * many bytes following. Each `witness_data` field + * contains all of the witness elements for a single input, + * including the leading counter of elements. + */ + struct witness *wit = tal(witnesses, struct witness); + wit->witness_data = tal_arr(wit, u8, 0); + add_varint(&wit->witness_data, wtx_s->num_items); + for (size_t j = 0; j < wtx_s->num_items; j++) { + add_varint(&wit->witness_data, wtx_s->items[j].witness_len); + tal_expand(&wit->witness_data, wtx_s->items[j].witness, + wtx_s->items[j].witness_len); } - stacks[stack_index++] = stack; + tal_arr_expand(&witnesses, wit); } } - if (stack_index == 0) - return tal_free(stacks); - - tal_resize(&stacks, stack_index); - return stacks; + return witnesses; } diff --git a/common/psbt_internal.h b/common/psbt_internal.h index b0f0e5ba5ec6..9b172505c9e5 100644 --- a/common/psbt_internal.h +++ b/common/psbt_internal.h @@ -7,7 +7,7 @@ struct wally_psbt; struct wally_psbt_input; -struct witness_element; +struct witness; /* psbt_finalize_input - Finalize an input with a given witness stack * @@ -15,21 +15,24 @@ struct witness_element; * the redeem_script, if any. * @ctx - the context to allocate onto * @in - input to set final_witness for - * @witness_element - elements to add to witness stack + * @witness - witness data to add to witness stack */ void psbt_finalize_input(const tal_t *ctx, struct wally_psbt_input *in, - const struct witness_element **elements); -/* psbt_to_witness_stacks - Take all sigs on a PSBT and copy to a - * witness_stack + const struct witness *witness); + +/* psbt_to_witnesses - Take a side's sigs from a PSBT and copy to a + * wire witness * * @ctx - allocation context * @psbt - PSBT to copy sigs from - * @opener - which side initiated this tx + * @side_to_stack - which side to stack witnesses of + * @input_index_to_ignore - which input to not include. Pass -1 to include all. */ -const struct witness_stack ** -psbt_to_witness_stacks(const tal_t *ctx, - const struct wally_psbt *psbt, - enum tx_role side_to_stack); +const struct witness ** +psbt_to_witnesses(const tal_t *ctx, + const struct wally_psbt *psbt, + enum tx_role side_to_stack, + int input_index_to_ignore); #endif /* LIGHTNING_COMMON_PSBT_INTERNAL_H */ diff --git a/common/psbt_keypath.c b/common/psbt_keypath.c index f163614e4926..dbc055f6e530 100644 --- a/common/psbt_keypath.c +++ b/common/psbt_keypath.c @@ -3,9 +3,8 @@ #include #include #include -#include -void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *map_in) { +void psbt_output_set_keypath(u32 index, const struct ext_key *ext, bool is_taproot, struct wally_psbt_output *output) { u8 fingerprint[BIP32_KEY_FINGERPRINT_LEN]; if (bip32_key_get_fingerprint( (struct ext_key *) ext, fingerprint, sizeof(fingerprint)) != WALLY_OK) @@ -14,20 +13,30 @@ void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *ma u32 path[1]; path[0] = index; - if (wally_map_keypath_add(map_in, - ext->pub_key, sizeof(ext->pub_key), - fingerprint, sizeof(fingerprint), - path, 1) != WALLY_OK) - abort(); + if (is_taproot) { + if (wally_psbt_output_taproot_keypath_add(output, + ext->pub_key + 1, sizeof(ext->pub_key) - 1, + NULL, 0, + fingerprint, sizeof(fingerprint), + path, 1) != WALLY_OK) + abort(); + } else { + if (wally_psbt_output_keypath_add(output, + ext->pub_key, sizeof(ext->pub_key), + fingerprint, sizeof(fingerprint), + path, 1) != WALLY_OK) + abort(); + } + } void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx, u32 key_index, - const struct ext_key *ext) { + const struct ext_key *ext, + bool is_taproot) { size_t outndx = tx->psbt->num_outputs - 1; - struct wally_map *map_in = &tx->psbt->outputs[outndx].keypaths; tal_wally_start(); - psbt_set_keypath(key_index, ext, map_in); + psbt_output_set_keypath(key_index, ext, is_taproot, &tx->psbt->outputs[outndx]); tal_wally_end(tx->psbt); } diff --git a/common/psbt_keypath.h b/common/psbt_keypath.h index f19d85761cbd..ddafa03662a5 100644 --- a/common/psbt_keypath.h +++ b/common/psbt_keypath.h @@ -3,20 +3,23 @@ #include "config.h" #include +#include struct bitcoin_tx; struct ext_key; struct wally_map; -/* psbt_set_keypath - Set the keypath of a PSBT output. +/* psbt_output_set_keypath - Set the keypath of a PSBT output. * * @index - child index of the wallet key * @ext - extended public key of the immediate parent of the wallet key - * @map_in - wally keypaths map + * @is_taproot - PSBT output has taproot script + * @output - PSBT output to set */ -void psbt_set_keypath(u32 index, +void psbt_output_set_keypath(u32 index, const struct ext_key *ext, - struct wally_map *map_in); + bool is_taproot, + struct wally_psbt_output *output); /* psbt_add_keypath_to_last_output - augment the last output with the * given wallet keypath @@ -24,9 +27,11 @@ void psbt_set_keypath(u32 index, * @tx - transaction to modify * @index - child index of the wallet key * @ext - extended public key of the immediate parent of the wallet key + * @is_taproot - if the output is taproot */ void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx, u32 index, - const struct ext_key *ext); + const struct ext_key *ext, + bool is_taproot); #endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */ diff --git a/common/psbt_open.c b/common/psbt_open.c index 80cbe7fb4b3d..fbd2e9c40578 100644 --- a/common/psbt_open.c +++ b/common/psbt_open.c @@ -75,8 +75,13 @@ static const u8 *linearize_input(const tal_t *ctx, wally_psbt_input_set_final_scriptsig(&psbt->inputs[0], NULL, 0); wally_psbt_input_set_witness_script(&psbt->inputs[0], NULL, 0); wally_psbt_input_set_redeem_script(&psbt->inputs[0], NULL, 0); + wally_psbt_input_set_taproot_signature(&psbt->inputs[0], NULL, 0); + psbt->inputs[0].taproot_leaf_hashes.num_items = 0; + psbt->inputs[0].taproot_leaf_paths.num_items = 0; psbt->inputs[0].keypaths.num_items = 0; psbt->inputs[0].signatures.num_items = 0; + psbt->inputs[0].utxo = NULL; + psbt->inputs[0].witness_utxo = NULL; const u8 *bytes = psbt_get_bytes(ctx, psbt, &byte_len); @@ -104,6 +109,8 @@ static const u8 *linearize_output(const tal_t *ctx, /* We don't care if the keypaths change */ psbt->outputs[0].keypaths.num_items = 0; + psbt->outputs[0].taproot_leaf_hashes.num_items = 0; + psbt->outputs[0].taproot_leaf_paths.num_items = 0; /* And you can add scripts, no problem */ wally_psbt_output_set_witness_script(&psbt->outputs[0], NULL, 0); wally_psbt_output_set_redeem_script(&psbt->outputs[0], NULL, 0); diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index d61abfd53f56..830c51c53835 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -83,7 +83,7 @@ bool handle_peer_error(struct per_peer_state *pps, } /* We hang up when a warning is received. */ - peer_failed_received_errmsg(pps, err, channel_id, warning); + peer_failed_received_errmsg(pps, err, channel_id, warning, false); } return false; diff --git a/common/scb_wire.csv b/common/scb_wire.csv index f27667caf5fb..6410ff4942e6 100644 --- a/common/scb_wire.csv +++ b/common/scb_wire.csv @@ -10,10 +10,12 @@ subtype,scb_chan subtypedata,scb_chan,id,u64, subtypedata,scb_chan,cid,channel_id, subtypedata,scb_chan,node_id,node_id, -subtypedata,scb_chan,addr,wireaddr_internal, +subtypedata,scb_chan,unused,u8, +subtypedata,scb_chan,addr,wireaddr, subtypedata,scb_chan,funding,bitcoin_outpoint, subtypedata,scb_chan,funding_sats,amount_sat, subtypedata,scb_chan,type,channel_type, + msgtype,static_chan_backup,6135, msgdata,static_chan_backup,version,u64, msgdata,static_chan_backup,timestamp,u32, diff --git a/common/test/Makefile b/common/test/Makefile index 2df9e1284cad..46b579a41f3d 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -1,9 +1,5 @@ COMMON_TEST_SRC := $(wildcard common/test/run-*.c) -ifeq ($(EXPERIMENTAL_FEATURES),1) -COMMON_TEST_SRC += $(wildcard common/test/exp-run-*.c) -endif - COMMON_TEST_OBJS := $(COMMON_TEST_SRC:.c=.o) COMMON_TEST_PROGRAMS := $(COMMON_TEST_OBJS:.o=) @@ -19,10 +15,10 @@ ALL_C_SOURCES += $(COMMON_TEST_SRC) ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. -common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o -common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o -common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o -common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o +common/test/run-sphinx: wire/onion_wiregen.o wire/towire.o wire/fromwire.o +common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: common/base32.o common/wireaddr.o wire/onion_wiregen.o wire/peer_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-route_blinding_test: wire/onion_wiregen.o wire/peer_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o +common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion_wiregen.o wire/peer_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o common/test/run-param \ common/test/run-json: \ @@ -38,27 +34,27 @@ common/test/run-json: \ common/type_to_string.o \ common/wireaddr.o \ wire/fromwire.o \ - wire/onion$(EXP)_wiregen.o \ - wire/peer$(EXP)_wiregen.o \ + wire/onion_wiregen.o \ + wire/peer_wiregen.o \ wire/towire.o common/test/run-route common/test/run-route-specific: \ common/amount.o \ common/dijkstra.o \ - common/fp16.o \ + common/fp16.o \ common/gossmap.o \ common/node_id.o \ common/pseudorand.o \ common/route.o \ wire/fromwire.o \ - wire/peer$(EXP)_wiregen.o \ + wire/peer_wiregen.o \ wire/towire.o common/test/run-gossmap_local: \ common/base32.o \ common/wireaddr.o \ wire/fromwire.o \ - wire/peer$(EXP)_wiregen.o \ + wire/peer_wiregen.o \ wire/tlvstream.o \ wire/towire.o @@ -66,7 +62,7 @@ common/test/run-gossmap_canned: \ common/base32.o \ common/wireaddr.o \ wire/fromwire.o \ - wire/peer$(EXP)_wiregen.o \ + wire/peer_wiregen.o \ wire/tlvstream.o \ wire/towire.o @@ -80,10 +76,22 @@ common/test/run-bolt12_merkle: \ common/node_id.o \ common/type_to_string.o \ common/wireaddr.o \ - wire/bolt12$(EXP)_wiregen.o \ + wire/bolt12_wiregen.o \ wire/fromwire.o \ wire/tlvstream.o \ - wire/peer$(EXP)_wiregen.o \ + wire/peer_wiregen.o \ + wire/towire.o + +common/test/run-bolt12-format-string-test: \ + common/amount.o \ + common/bigsize.o \ + common/base32.o \ + common/bech32.o \ + common/bech32_util.o \ + common/bolt12.o \ + common/node_id.o \ + wire/bolt12$(EXP)_wiregen.o \ + wire/tlvstream.o \ wire/towire.o common/test/run-bolt12_merkle-json: \ diff --git a/common/test/run-bolt12-format-string-test.c b/common/test/run-bolt12-format-string-test.c new file mode 100644 index 000000000000..2a6ed854d393 --- /dev/null +++ b/common/test/run-bolt12-format-string-test.c @@ -0,0 +1,210 @@ +/* Pipe through jq to create format-string-test.json */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for features_unsupported */ +int features_unsupported(const struct feature_set *our_features UNNEEDED, + const u8 *their_features UNNEEDED, + enum feature_place p UNNEEDED) +{ fprintf(stderr, "features_unsupported called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_blinded_path */ +struct blinded_path *fromwire_blinded_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) +{ fprintf(stderr, "fromwire_blinded_path called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_pad */ +void fromwire_pad(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_pad called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_tu32 */ +u32 fromwire_tu32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu32 called!\n"); abort(); } +/* Generated stub for fromwire_tu64 */ +u64 fromwire_tu64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu64 called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_utf8_array */ +void fromwire_utf8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, char *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_utf8_array called!\n"); abort(); } +/* Generated stub for merkle_tlv */ +void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED) +{ fprintf(stderr, "merkle_tlv called!\n"); abort(); } +/* Generated stub for sighash_from_merkle */ +void sighash_from_merkle(const char *messagename UNNEEDED, + const char *fieldname UNNEEDED, + const struct sha256 *merkle UNNEEDED, + struct sha256 *sighash UNNEEDED) +{ fprintf(stderr, "sighash_from_merkle called!\n"); abort(); } +/* Generated stub for towire_blinded_path */ +void towire_blinded_path(u8 **p UNNEEDED, const struct blinded_path *blinded_path UNNEEDED) +{ fprintf(stderr, "towire_blinded_path called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static void example2(const char *comment, bool valid, + const char *str, bool final) +{ + printf("{\n"); + printf("\"comment\": \"%s\",\n", comment); + printf("\"valid\": %s,\n", valid ? "true": "false"); + printf("\"string\": \"%s\"\n", str); + printf("}%s\n", final ? "" : ","); +} + +static void example(const char *comment, bool valid, const char *str) +{ + example2(comment, valid, str, false); +} + +static void example_final(const char *comment, bool valid, const char *str) +{ + example2(comment, valid, str, true); +} + +static utf8 *tal_utf8(const tal_t *ctx, const char *str) +{ + /* Non-NUL terminated! */ + return tal_dup_arr(ctx, char, str, strlen(str), 0); +} + +int main(int argc, char *argv[]) +{ + struct tlv_offer *offer; + struct secret alice; + char *str; + + common_setup(argv[0]); + + offer = tlv_offer_new(tmpctx); + /* BOLT-offers #12: + * A writer of an offer: + * - MUST NOT set any tlv fields greater or equal to 80, or tlv field 0. + * - MUST set `offer_node_id` to the node's public key to request the invoice from. + * - MUST set `offer_description` to a complete description of the purpose + * of the payment. + * - if the chain for the invoice is not solely bitcoin: + * - MUST specify `offer_chains` the offer is valid for. + * - otherwise: + * - MAY omit `offer_chains`, implying that bitcoin is only chain. + * - if a specific minimum `offer_amount` is required for successful payment: + * - MUST set `offer_amount` to the amount expected (per item). + * - if the currency for `offer_amount` is that of all entries in `chains`: + * - MUST specify `amount` in multiples of the minimum lightning-payable unit + * (e.g. milli-satoshis for bitcoin). + * - otherwise: + * - MUST specify `offer_currency` `iso4217` as an ISO 4712 three-letter code. + * - MUST specify `offer_amount` in the currency unit adjusted by the ISO 4712 + * exponent (e.g. USD cents). + * - otherwise: + * - MUST NOT set `offer_amount` + * - MUST NOT set `offer_currency` + * - MAY set `offer_metadata` for its own use. + * - if it supports bolt12 offer features: + * - MUST set `offer_features`.`features` to the bitmap of bolt12 features. + * - if the offer expires: + * - MUST set `offer_absolute_expiry` `seconds_from_epoch` to the number of seconds + * after midnight 1 January 1970, UTC that invoice_request should not be + * attempted. + * - if it is connected only by private channels: + * - MUST include `offer_paths` containing one or more paths to the node from + * publicly reachable nodes. + * - otherwise: + * - MAY include `offer_paths`. + * - if it includes `offer_paths`: + * - SHOULD ignore any invoice_request which does not use the path. + * - if it sets `offer_issuer`: + * - SHOULD set it to identify the issuer of the invoice clearly. + * - if it includes a domain name: + * - SHOULD begin it with either user@domain or domain + * - MAY follow with a space and more text + * - if it can supply more than one item for a single invoice: + * - if the maximum quantity is known: + * - MUST set that maximum in `offer_quantity_max`. + * - MUST NOT set `offer_quantity_max` to 0. + * - otherwise: + * - MUST set `offer_quantity_max` to 0. + * - otherwise: + * - MUST NOT set `offer_quantity_max`. + */ + memset(&alice, 'A', sizeof(alice)); + offer->offer_node_id = tal(offer, struct pubkey); + assert(pubkey_from_secret(&alice, offer->offer_node_id)); + offer->offer_description = tal_utf8(offer, "An example description"); + offer->offer_issuer = tal_utf8(offer, "BOLT 12 industries"); + offer->offer_amount = tal(offer, u64); + /* 1M msat */ + *offer->offer_amount = 1000000; + + str = offer_encode(tmpctx, offer); + printf("[\n"); + + example("A complete string is valid", true, str); + example("+ can join anywhere", true, + tal_fmt(tmpctx, "%.*s+%s", 1, str, str + 1)); + example("Multiple + can join", true, + tal_fmt(tmpctx, "%.*s+%.*s+%.*s+%.*s+%s", + 15, str, + 31, str + 15, + 18, str + 15 + 31, + 70, str + 15 + 31 + 18, + str + 15 + 31 + 18 + 70)); + example("+ can be followed by whitespace", true, + tal_fmt(tmpctx, "%.*s+ %.*s+ %.*s+\\n%.*s+\\r\\n %s", + 15, str, + 31, str + 15, + 18, str + 15 + 31, + 70, str + 15 + 31 + 18, + str + 15 + 31 + 18 + 70)); + + example("+ must be surrounded by bech32 characters", false, + tal_fmt(tmpctx, "%s+", str)); + example("+ must be surrounded by bech32 characters", false, + tal_fmt(tmpctx, "%s+ ", str)); + example("+ must be surrounded by bech32 characters", false, + tal_fmt(tmpctx, "+%s", str)); + example("+ must be surrounded by bech32 characters", false, + tal_fmt(tmpctx, "+ %s", str)); + example_final("+ must be surrounded by bech32 characters", false, + tal_fmt(tmpctx, "%.*s++%s", 2, str, str+2)); + printf("]\n"); + common_shutdown(); +} diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index 6ddf6656dc46..5bbb6f7a1cc4 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -203,6 +203,7 @@ int main(int argc, char *argv[]) actual = (string_to_data(tmpctx, str, strlen(str), "lno", &dlen, &fail) != NULL); assert(actual == valid); + printf("%s %s\n", str, valid ? "OK": "INVALID"); } out: common_shutdown(); diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index d1eaf16d9011..b7519fb7f4f7 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -4,15 +4,10 @@ #include "../bolt12_merkle.c" #include "../json_parse.c" #include "../json_parse_simple.c" +#include "../../wire/bolt12_wiregen.c" #include "../../wire/fromwire.c" +#include "../../wire/peer_wiregen.c" #include "../../wire/tlvstream.c" -#if EXPERIMENTAL_FEATURES - #include "../../wire/peer_exp_wiregen.c" - #include "../../wire/bolt12_exp_wiregen.c" -#else - #include "../../wire/peer_wiregen.c" - #include "../../wire/bolt12_wiregen.c" -#endif #include #include #include @@ -72,6 +67,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_s64 */ +void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) +{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/common/test/run-codex32.c b/common/test/run-codex32.c new file mode 100644 index 000000000000..5e0c78041858 --- /dev/null +++ b/common/test/run-codex32.c @@ -0,0 +1,580 @@ +#include "config.h" +#include "../bech32_util.c" +#include "../bech32.c" +#include "../codex32.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_div */ +struct amount_sat amount_sat_div(struct amount_sat sat UNNEEDED, u64 div UNNEEDED) +{ fprintf(stderr, "amount_sat_div called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_mul */ +bool amount_sat_mul(struct amount_sat *res UNNEEDED, struct amount_sat sat UNNEEDED, u64 mul UNNEEDED) +{ fprintf(stderr, "amount_sat_mul called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Print the "cl" variant of the vector */ +static void print_cl_vec(const char *desc, const struct codex32 *parts) +{ + const char *err; + char *bip93; + + err = codex32_secret_encode(tmpctx, "cl", parts->id, parts->threshold, + parts->payload, tal_bytelen(parts->payload), + &bip93); + assert(!err); + printf("%s: %s\n", desc, bip93); +} + +int main(int argc, char *argv[]) +{ + common_setup(argv[0]); + char *fail, *c; + struct codex32 *parts; + + /* Test vector for codex32_secret_encode*/ + u8 seed_b[32] = { + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, + }; + + assert(codex32_secret_encode(tmpctx, "ms", "leet", 0, seed_b, ARRAY_SIZE(seed_b), &c) == NULL); + assert(streq(c, + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma")); + + /* + * Test vector 1 + * + * This example shows the codex32 format, when used without splitting the secret into any shares. The payload contains 26 bech32 characters, which corresponds to 130 bits. We truncate the last two bits in order to obtain a 128-bit master seed. + * + * codex32 secret (bech32): ms10testsxxxxxxxxxxxxxxxxxxxxxxxxxx4nzvca9cmczlw + * + * Master secret (hex): 318c6318c6318c6318c6318c6318c631 + * + * * human-readable part: ms + * * separator: 1 + * * k value: 0 (no secret splitting) + * * identifier: test + * * share index: s (the secret) + * * payload: xxxxxxxxxxxxxxxxxxxxxxxxxx + * * checksum: 4nzvca9cmczlw + * * master node xprv: xprv9s21ZrQH143K3taPNekMd9oV5K6szJ8ND7vVh6fxicRUMDcChr3bFFzuxY8qP3xFFBL6DWc2uEYCfBFZ2nFWbAqKPhtCLRjgv78EZJDEfpL + */ + + parts = codex32_decode(tmpctx, NULL, "ms10testsxxxxxxxxxxxxxxxxxxxxxxxxxx4nzvca9cmczlw", &fail); + if (parts) { + assert(streq(parts->hrp, "ms")); + assert(parts->threshold == 0); + assert(strcmp(parts->id,"test") == 0); + assert(parts->share_idx == 's'); + assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), + "318c6318c6318c6318c6318c6318c631")); + print_cl_vec("Test vector 1", parts); + } else { + abort(); + } + tal_free(parts); + + /* + * Test vector 2 + * This example shows generating a new master seed using "random" codex32 shares, as well as deriving an additional codex32 share, using k=2 and an identifier of NAME. Although codex32 strings are canonically all lowercase, it's also valid to use all uppercase. + * + * Share with index A: MS12NAMEA320ZYXWVUTSRQPNMLKJHGFEDCAXRPP870HKKQRM + * + * Share with index C: MS12NAMECACDEFGHJKLMNPQRSTUVWXYZ023FTR2GDZMPY6PN + * + * * Derived share with index D: MS12NAMEDLL4F8JLH4E5VDVULDLFXU2JHDNLSM97XVENRXEG + * * Secret share with index S: MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW + * * Master secret (hex): d1808e096b35b209ca12132b264662a5 + * * master node xprv: xprv9s21ZrQH143K2NkobdHxXeyFDqE44nJYvzLFtsriatJNWMNKznGoGgW5UMTL4fyWtajnMYb5gEc2CgaKhmsKeskoi9eTimpRv2N11THhPTU + */ + + parts = codex32_decode(tmpctx, "ms", "MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW", &fail); + + if(parts) { + assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), "d1808e096b35b209ca12132b264662a5")); + print_cl_vec("Test vector 2", parts); + } else { + abort(); + } + tal_free(parts); + + + /* + * Test vector 3 + * This example shows splitting an existing 128-bit master seed into "random" codex32 shares, using k=3 and an identifier of cash. We appended two zero bits in order to obtain 26 bech32 characters (130 bits of data) from the 128-bit master seed. + * + * Master secret (hex): ffeeddccbbaa99887766554433221100 + * + * * Secret share with index s: ms13cashsllhdmn9m42vcsamx24zrxgs3qqjzqud4m0d6nln + * + * * Share with index a: ms13casha320zyxwvutsrqpnmlkjhgfedca2a8d0zehn8a0t + * + * * Share with index c: ms13cashcacdefghjklmnpqrstuvwxyz023949xq35my48dr + * + * * Derived share with index d: ms13cashd0wsedstcdcts64cd7wvy4m90lm28w4ffupqs7rm + * * Derived share with index e: ms13casheekgpemxzshcrmqhaydlp6yhms3ws7320xyxsar9 + * * Derived share with index f: ms13cashf8jh6sdrkpyrsp5ut94pj8ktehhw2hfvyrj48704 + * master node xprv: xprv9s21ZrQH143K266qUcrDyYJrSG7KA3A7sE5UHndYRkFzsPQ6xwUhEGK1rNuyyA57Vkc1Ma6a8boVqcKqGNximmAe9L65WsYNcNitKRPnABd + * Any three of the five shares among acdef can be used to recover the secret. + * Note that the choice to append two zero bits was arbitrary, and any of the following four secret shares would have been valid choices. However, each choice would have resulted in a different set of derived shares. + * + * * ms13cashsllhdmn9m42vcsamx24zrxgs3qqjzqud4m0d6nln + * * ms13cashsllhdmn9m42vcsamx24zrxgs3qpte35dvzkjpt0r + * * ms13cashsllhdmn9m42vcsamx24zrxgs3qzfatvdwq5692k6 + * * ms13cashsllhdmn9m42vcsamx24zrxgs3qrsx6ydhed97jx2 + */ + + char *addr_vec3[] = { + "ms13cashsllhdmn9m42vcsamx24zrxgs3qqjzqud4m0d6nln", + "ms13cashsllhdmn9m42vcsamx24zrxgs3qpte35dvzkjpt0r", + "ms13cashsllhdmn9m42vcsamx24zrxgs3qzfatvdwq5692k6", + "ms13cashsllhdmn9m42vcsamx24zrxgs3qrsx6ydhed97jx2", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_vec3); i++) { + parts = codex32_decode(tmpctx, NULL, addr_vec3[i], &fail); + if(parts) { + assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), + "ffeeddccbbaa99887766554433221100")); + print_cl_vec("Test vector 3", parts); + } else { + abort(); + } + tal_free(parts); + } + + /* + * Test vector 4 + * 256-bit secret (hex): ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100 + + * * codex32 secret: ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma + * * master node xprv: xprv9s21ZrQH143K3s41UCWxXTsU4TRrhkpD1t21QJETan3hjo8DP5LFdFcB5eaFtV8x6Y9aZotQyP8KByUjgLTbXCUjfu2iosTbMv98g8EQoqr + * + * Note that the choice to append four zero bits was arbitrary, and any of the following sixteen codex32 secrets would have been valid: + * + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqpj82dp34u6lqtd + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqzsrs4pnh7jmpj5 + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqrfcpap2w8dqezy + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqy5tdvphn6znrf0 + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq9dsuypw2ragmel + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqx05xupvgp4v6qx + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq8k0h5p43c2hzsk + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqgum7hplmjtr8ks + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqf9q0lpxzt5clxq + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq28y48pyqfuu7le + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqt7ly0paesr8x0f + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqvrvg7pqydv5uyz + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqd6hekpea5n0y5j + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqwcnrwpmlkmt9dt + * * ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq0pgjxpzx0ysaam + */ + + char *addr_vec4[] = { + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqpj82dp34u6lqtd", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqzsrs4pnh7jmpj5", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqrfcpap2w8dqezy", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqy5tdvphn6znrf0", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq9dsuypw2ragmel", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqx05xupvgp4v6qx", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq8k0h5p43c2hzsk", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqgum7hplmjtr8ks", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqf9q0lpxzt5clxq", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq28y48pyqfuu7le", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqt7ly0paesr8x0f", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqvrvg7pqydv5uyz", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqd6hekpea5n0y5j", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqwcnrwpmlkmt9dt", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq0pgjxpzx0ysaam", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_vec4); i++) { + parts = codex32_decode(tmpctx, NULL, addr_vec4[i], &fail); + if (parts) { + assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), + "ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100")); + print_cl_vec("Test vector 4", parts); + } else { + abort(); + } + tal_free(parts); + } + + /* + * Test vector 5 + * This example shows generating a new 512-bit master seed using "random" codex32 characters and appending a checksum. The payload contains 103 bech32 characters, which corresponds to 515 bits. The last three bits are discarded when converting to a 512-bit master seed. + * + * This is an example of a Long codex32 String. + * + * * Secret share with index S: MS100C8VSM32ZXFGUHPCHTLUPZRY9X8GF2TVDW0S3JN54KHCE6MUA7LQPZYGSFJD6AN074RXVCEMLH8WU3TK925ACDEFGHJKLMNPQRSTUVWXY06FHPV80UNDVARHRAK + * * Master secret (hex): dc5423251cb87175ff8110c8531d0952d8d73e1194e95b5f19d6f9df7c01111104c9baecdfea8cccc677fb9ddc8aec5553b86e528bcadfdcc201c17c638c47e9 + * * master node xprv: xprv9s21ZrQH143K4UYT4rP3TZVKKbmRVmfRqTx9mG2xCy2JYipZbkLV8rwvBXsUbEv9KQiUD7oED1Wyi9evZzUn2rqK9skRgPkNaAzyw3YrpJN + * + */ + + parts = codex32_decode(tmpctx, NULL, "MS100C8VSM32ZXFGUHPCHTLUPZRY9X8GF2TVDW0S3JN54KHCE6MUA7LQPZYGSFJD6AN074RXVCEMLH8WU3TK925ACDEFGHJKLMNPQRSTUVWXY06FHPV80UNDVARHRAK", &fail); + if (parts) { + assert(streq(tal_hexstr(tmpctx, parts->payload, tal_bytelen(parts->payload)), + "dc5423251cb87175ff8110c8531d0952d8d73e1194e95b5f19d6f9df7c01111104c9baecdfea8cccc677fb9ddc8aec5553b86e528bcadfdcc201c17c638c47e9")); + print_cl_vec("Test vector 5", parts); + } else { + abort(); + } + tal_free(parts); + + + /* ---- Invalid codex test vectors ---- + * These examples have incorrect checksums. + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghq + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghp + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxlk3yepcstwr + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx6pgnv7jnpcsp + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxx0cpvr7n4geq + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxm5252y7d3lr + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxrd9sukzl05ej + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxc55srw5jrm0 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxgc7rwhtudwc + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx4gy22afwghvs + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxe8yfm0 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvm597d + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pe0 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pew + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxqyadsp3nywm8a + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzvg7ar4hgaejk + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcznau0advgxqe + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxch3jrc6j5040j + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx52gxl6ppv40mcv + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx7g4g2nhhle8fk + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx63m45uj8ss4x8 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy4r708q7kg65x + * + */ + + char *addr_invalid[] = { + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghq", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghp", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxlk3yepcstwr", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx6pgnv7jnpcsp", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxx0cpvr7n4geq", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxm5252y7d3lr", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxrd9sukzl05ej", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxc55srw5jrm0", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxgc7rwhtudwc", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx4gy22afwghvs", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxe8yfm0", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvm597d", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pe0", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pew", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxqyadsp3nywm8a", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzvg7ar4hgaejk", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcznau0advgxqe", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxch3jrc6j5040j", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx52gxl6ppv40mcv", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx7g4g2nhhle8fk", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx63m45uj8ss4x8", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy4r708q7kg65x", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_invalid); i++) { + parts = codex32_decode(tmpctx, NULL, addr_invalid[i], &fail); + if (parts) { + abort(); + } else { + assert(streq(fail, "Invalid checksum!")); + } + tal_free(parts); + } + + /* These examples use the wrong checksum for their given data sizes. + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxurfvwmdcmymdufv + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxcsyppjkd8lz4hx3 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxu6hwvl5p0l9xf3c + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwqey9rfs6smenxa + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxv70wkzrjr4ntqet + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3hmlrmpa4zl0v + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxrfggf88znkaup + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpt7l4aycv9qzj + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxus27z9xtyxyw3 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcwm4re8fs78vn + * + */ + + char *addr_invalid1[] = { + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxurfvwmdcmymdufv", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxcsyppjkd8lz4hx3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxu6hwvl5p0l9xf3c", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwqey9rfs6smenxa", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxv70wkzrjr4ntqet", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3hmlrmpa4zl0v", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxrfggf88znkaup", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpt7l4aycv9qzj", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxus27z9xtyxyw3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcwm4re8fs78vn", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_invalid1); i++) { + parts = codex32_decode(tmpctx, NULL, addr_invalid1[i], &fail); + if (parts) { + printf("payload == %ld\n", tal_bytelen(parts->payload)); + abort(); + } else { + assert(streq(fail, "Invalid checksum!") || + streq(fail, "Invalid length!") || + streq(fail, "Invalid payload!")); + } + tal_free(parts); + } + + /* + * These examples have improper lengths. They are either too short, too long, or would decode to byte sequence with an incomplete group greater than 4 bits. + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxw0a4c70rfefn4 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxk4pavy5n46nea + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx9lrwar5zwng4w + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxr335l5tv88js3 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvu7q9nz8p7dj68v + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpq6k542scdxndq3 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkmfw6jm270mz6ej + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxzhddxw99w7xws + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx42cux6um92rz + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxarja5kqukdhy9 + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxky0ua3ha84qk8 + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9eheesxadh2n2n9 + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9llwmgesfulcj2z + * * ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx02ev7caq6n9fgkf + */ + + char *addr_invalid2[] = { + // "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxw0a4c70rfefn4", FALSE POSITIVE + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxk4pavy5n46nea", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx9lrwar5zwng4w", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxr335l5tv88js3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvu7q9nz8p7dj68v", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpq6k542scdxndq3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkmfw6jm270mz6ej", + // "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxzhddxw99w7xws", FALSE POSITIVE + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx42cux6um92rz", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxarja5kqukdhy9", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxky0ua3ha84qk8", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9eheesxadh2n2n9", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9llwmgesfulcj2z", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx02ev7caq6n9fgkf", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_invalid2); i++) { + parts = codex32_decode(tmpctx, NULL, addr_invalid2[i], &fail); + if (parts) { + printf("payload %ld\n", tal_bytelen(parts->payload)); + abort(); + } else { + assert(streq(fail, "Invalid payload!") || + streq(fail, "Invalid length!")); + } + tal_free(parts); + } + + /* + * This example uses a "0" threshold with a non-"s" index + * * ms10fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx0z26tfn0ulw3p + */ + + parts = codex32_decode(tmpctx, NULL, "ms10fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx0z26tfn0ulw3p", &fail); + if (parts) { + abort(); + } else { + assert(streq(fail, "Expected share index s for threshold 0!")); + } + tal_free(parts); + + /* + * This example has a threshold that is not a digit. + * * ms1fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxda3kr3s0s2swg + */ + + parts = codex32_decode(tmpctx, NULL, "ms1fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxda3kr3s0s2swg", &fail); + if (parts) { + abort(); + } else { + assert(streq(fail, "Invalid threshold!")); + } + tal_free(parts); + + /* + * These examples do not begin with the required "ms" or "MS" prefix and/or are missing the "1" separator. + * * 0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * 10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * ms0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * 0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws + * * 10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws + * * m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxx8t28z74x8hs4l + * * s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxh9d0fhnvfyx3x + */ + + char *addr_invalid3[] = { + "0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws", + "10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws", + "m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxx8t28z74x8hs4l", + "s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxh9d0fhnvfyx3x", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_invalid3); i++) { + parts = codex32_decode(tmpctx, "ms", addr_invalid3[i], &fail); + if (parts) { + abort(); + } else { + assert(strstr(fail, "Invalid hrp ") || + streq(fail, "Separator doesn't exist!")); + } + tal_free(parts); + } + + /* + * These examples all incorrectly mix upper and lower case characters. + * * Ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * mS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * MS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * ms10FAUXsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * ms10fauxSxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2 + * * ms10fauxsXXXXXXXXXXXXXXXXXXXXXXXXXXuqxkk05lyf3x2 + * * ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxUQXKK05LYF3X2 + */ + + char *addr_invalid4[] = { + "Ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "mS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "MS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10FAUXsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10fauxSxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10fauxsXXXXXXXXXXXXXXXXXXXXXXXXXXuqxkk05lyf3x2", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxUQXKK05LYF3X2", + }; + + for (size_t i = 0; i < ARRAY_SIZE(addr_invalid4); i++) { + parts = codex32_decode(tmpctx, NULL, addr_invalid4[i], &fail); + if (parts) { + abort(); + } else { + assert(streq(fail, "Not a valid bech32 string!")); + } + tal_free(parts); + } + + common_shutdown(); + return 0; +} diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index cb2f1b27e5de..4685f50c84de 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -196,37 +196,36 @@ int main(int argc, char *argv[]) assert(!separate_address_and_port(tmpctx, "[::1]:http", &ip, &port)); // localhost hostnames for backward compat - assert(parse_wireaddr("localhost", &addr, 200, false, NULL)); + assert(parse_wireaddr(tmpctx, "localhost", 200, false, &addr) == NULL); assert(addr.port == 200); // string should win the port battle - assert(parse_wireaddr("[::1]:9735", &addr, 500, false, NULL)); + assert(parse_wireaddr(tmpctx, "[::1]:9735", 500, false, &addr) == NULL); assert(addr.port == 9735); ip = fmt_wireaddr(tmpctx, &addr); assert(streq(ip, "[::1]:9735")); // should use argument if we have no port in string - assert(parse_wireaddr("2001:db8:85a3::8a2e:370:7334", &addr, 9777, false, NULL)); + assert(parse_wireaddr(tmpctx, "2001:db8:85a3::8a2e:370:7334", 9777, false, &addr) == NULL); assert(addr.port == 9777); ip = fmt_wireaddr(tmpctx, &addr); assert(streq(ip, "[2001:db8:85a3::8a2e:370:7334]:9777")); - assert(parse_wireaddr("[::ffff:127.0.0.1]:49150", &addr, 1, false, NULL)); + assert(parse_wireaddr(tmpctx, "[::ffff:127.0.0.1]:49150", 1, false, &addr) == NULL); assert(addr.port == 49150); - assert(parse_wireaddr("4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion:49150", &addr, 1, false, NULL)); + assert(parse_wireaddr(tmpctx, "4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion:49150", 1, false, &addr) == NULL); assert(addr.port == 49150); - assert(parse_wireaddr("4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion", &addr, 1, false, NULL)); + assert(parse_wireaddr(tmpctx, "4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion", 1, false, &addr) == NULL); assert(addr.port == 1); /* We don't accept torv2 any more */ - assert(!parse_wireaddr("odpzvneidqdf5hdq.onion:49150", &addr, 1, false, NULL)); - assert(!parse_wireaddr("odpzvneidqdf5hdq.onion", &addr, 1, false, NULL)); + assert(parse_wireaddr(tmpctx, "odpzvneidqdf5hdq.onion:49150", 1, false, &addr) != NULL); + assert(parse_wireaddr(tmpctx, "odpzvneidqdf5hdq.onion", 1, false, &addr) != NULL); - assert(!parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, - false, false, false, NULL)); + assert(parse_wireaddr_internal(tmpctx, "odpzvneidqdf5hdq.onion", 1, false, &addr_int) != NULL); assert(wireaddr_from_hostname(tmpctx, "odpzvneidqdf5hdq.onion", 1, NULL, NULL, NULL) == NULL); assert(wireaddr_from_hostname(tmpctx, "aaa.onion", 1, NULL, NULL, NULL) == NULL); diff --git a/common/test/run-json_filter.c b/common/test/run-json_filter.c index 594eb00f7ef7..58be0ca0c3e7 100644 --- a/common/test/run-json_filter.c +++ b/common/test/run-json_filter.c @@ -15,6 +15,9 @@ struct command; /* Generated stub for command_check_only */ bool command_check_only(const struct command *cmd UNNEEDED) { fprintf(stderr, "command_check_only called!\n"); abort(); } +/* Generated stub for command_deprecated_apis */ +bool command_deprecated_apis(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_deprecated_apis called!\n"); abort(); } /* Generated stub for command_fail */ struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) @@ -124,6 +127,9 @@ int segwit_addr_decode( const char* addr ) { fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } +/* Generated stub for to_canonical_invstr */ +const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED) +{ fprintf(stderr, "to_canonical_invstr called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 3b25bd141f71..526611768a79 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -47,6 +47,9 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for command_check_only */ bool command_check_only(const struct command *cmd UNNEEDED) { fprintf(stderr, "command_check_only called!\n"); abort(); } +/* Generated stub for command_deprecated_apis */ +bool command_deprecated_apis(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_deprecated_apis called!\n"); abort(); } /* Generated stub for command_fail */ struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) @@ -58,8 +61,6 @@ void command_set_usage(struct command *cmd UNNEEDED, const char *usage UNNEEDED) /* Generated stub for command_usage_only */ bool command_usage_only(const struct command *cmd UNNEEDED) { fprintf(stderr, "command_usage_only called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } @@ -86,6 +87,9 @@ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u64 */ u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_s64 */ +s64 fromwire_s64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_s64 called!\n"); abort(); } /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } @@ -161,6 +165,9 @@ int segwit_addr_decode( const char* addr ) { fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } +/* Generated stub for to_canonical_invstr */ +const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED) +{ fprintf(stderr, "to_canonical_invstr called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } @@ -180,6 +187,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_s64 */ +void towire_s64(u8 **pptr UNNEEDED, s64 v UNNEEDED) +{ fprintf(stderr, "towire_s64 called!\n"); abort(); } /* Generated stub for towire_u8 */ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) { fprintf(stderr, "towire_u8 called!\n"); abort(); } diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index 64f931470dea..4b8838b8fc04 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -146,6 +146,24 @@ int main(int argc, char *argv[]) assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,3:{three:{deeper:17}},arr:[1:2]}")); assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,3:{three:{deeper:17}},arr:[1:2,2:[0:3,1:4]]}")); + /* Optional fields which are present are fine. */ + assert(!json_scan(tmpctx, buf, toks, "{1?:one}")); + assert(!json_scan(tmpctx, buf, toks, "{1?:one,2?:two}")); + assert(!json_scan(tmpctx, buf, toks, "{2?:two,1?:one}")); + assert(!json_scan(tmpctx, buf, toks, "{2?:two,1?:one,3?:{three?:{deeper?:17}}}")); + assert(!json_scan(tmpctx, buf, toks, "{2?:two,1?:one,3?:{three?:{deeper?:17}},arr?:[0:{1?:arrone}]}")); + assert(!json_scan(tmpctx, buf, toks, "{2?:two,1?:one,3?:{three?:{deeper?:17}},arr?:[1:2]}")); + assert(!json_scan(tmpctx, buf, toks, "{2?:two,1?:one,3?:{three?:{deeper?:17}},arr?:[1:2,2:[0:3,1:4]]}")); + + /* Optional field which are missing are fine too */ + assert(!json_scan(tmpctx, buf, toks, "{5?:one}")); + assert(!json_scan(tmpctx, buf, toks, "{1:one,5?:two}")); + assert(!json_scan(tmpctx, buf, toks, "{2:two,5?:one}")); + assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,5?:{three:{deeper:17}}}")); + assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,3:{five?:{deeper:17}},arr:[0:{1:arrone}]}")); + assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,3:{three:{notdeeper?:17}},arr:[1:2]}")); + assert(!json_scan(tmpctx, buf, toks, "{2:two,1:one,3:{three:{deeper:17}},notarr?:[1:2,2:[0:3,1:4]]}")); + /* These do not match */ err = json_scan(tmpctx, buf, toks, "{2:one}"); assert(streq(err, "Parsing '{2:one': \"two\" does not match expected one")); diff --git a/common/test/run-onion-message-test.c b/common/test/run-onion-message-test.c index c3ef156b8786..dc6ae3aa1b43 100644 --- a/common/test/run-onion-message-test.c +++ b/common/test/run-onion-message-test.c @@ -16,13 +16,8 @@ static void maybe_print(const char *fmt, ...); #include "../onion_message_parse.c" #include "../sphinx.c" #include "../type_to_string.c" -#if EXPERIMENTAL_FEATURES - #include "../../wire/onion_exp_wiregen.c" - #include "../../wire/peer_exp_wiregen.c" -#else - #include "../../wire/onion_wiregen.c" - #include "../../wire/peer_wiregen.c" -#endif +#include "../../wire/onion_wiregen.c" +#include "../../wire/peer_wiregen.c" #include #include #include diff --git a/common/test/run-onion-test-vector.c b/common/test/run-onion-test-vector.c index d760262c8007..d1583ae0a109 100644 --- a/common/test/run-onion-test-vector.c +++ b/common/test/run-onion-test-vector.c @@ -8,11 +8,7 @@ #include "../type_to_string.c" #include "../../wire/towire.c" #include "../../wire/fromwire.c" -#if EXPERIMENTAL_FEATURES -#include "../../wire/onion_exp_wiregen.c" -#else #include "../../wire/onion_wiregen.c" -#endif #include #include #include diff --git a/common/test/run-param.c b/common/test/run-param.c index 6609247db72e..6ee699a29750 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -1,13 +1,26 @@ #include "config.h" +#include "../bech32.c" #include "../json_filter.c" #include "../json_parse.c" #include "../json_parse_simple.c" -#include "../json_param.c" +#include #include #include #include #include +/* We want to catch parameter checs for bad_programmer() */ +#define paramcheck_assert save_paramcheck_assert + +static bool paramcheck_assert_failed; +static void save_paramcheck_assert(bool cond) +{ + if (!cond) + paramcheck_assert_failed = true; +} + +#include "../json_param.c" + char *fail_msg = NULL; bool failed = false; @@ -39,23 +52,15 @@ struct command_result *command_fail(struct command *cmd, /* Generated stub for command_filter_ptr */ struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED) { fprintf(stderr, "command_filter_ptr called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED, const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for segwit_addr_decode */ -int segwit_addr_decode( - int* ver UNNEEDED, - uint8_t* prog UNNEEDED, - size_t* prog_len UNNEEDED, - const char* hrp UNNEEDED, - const char* addr -) -{ fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } +/* Generated stub for to_canonical_invstr */ +const char *to_canonical_invstr(const tal_t *ctx UNNEEDED, const char *invstring UNNEEDED) +{ fprintf(stderr, "to_canonical_invstr called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -72,6 +77,7 @@ enum command_mode { struct command { enum command_mode mode; + bool deprecated_apis; const char *usage; }; @@ -90,6 +96,11 @@ bool command_check_only(const struct command *cmd) return cmd->mode == CMD_CHECK; } +bool command_deprecated_apis(const struct command *cmd) +{ + return cmd->deprecated_apis; +} + struct json { jsmntok_t *toks; char *buffer; @@ -300,8 +311,6 @@ static void no_params(void) assert(!param(cmd, j->buffer, j->toks, NULL)); } - -#if DEVELOPER /* * Check to make sure there are no programming mistakes. */ @@ -312,56 +321,60 @@ static void bad_programmer(void) u64 *fpval; struct json *j = json_parse(cmd, "[ '25', '546', '26' ]"); - /* check for repeated names */ - assert(!param(cmd, j->buffer, j->toks, - p_req("repeat", param_u64, &ival), - p_req("fp", param_millionths, &fpval), - p_req("repeat", param_u64, &ival2), NULL)); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); - - assert(!param(cmd, j->buffer, j->toks, - p_req("repeat", param_u64, &ival), - p_req("fp", param_millionths, &fpval), - p_req("repeat", param_u64, &ival), NULL)); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); + /* Usage mode makes it check parameters are sane */ + cmd->mode = CMD_USAGE; - assert(!param(cmd, j->buffer, j->toks, - p_req("u64", param_u64, &ival), - p_req("repeat", param_millionths, &fpval), - p_req("repeat", param_millionths, &fpval), NULL)); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); + /* check for repeated names */ + paramcheck_assert_failed = false; + param(cmd, j->buffer, j->toks, + p_req("repeat", param_u64, &ival), + p_req("fp", param_millionths, &fpval), + p_req("repeat", param_u64, &ival2), NULL); + assert(paramcheck_assert_failed); + + paramcheck_assert_failed = false; + param(cmd, j->buffer, j->toks, + p_req("repeat", param_u64, &ival), + p_req("fp", param_millionths, &fpval), + p_req("repeat", param_u64, &ival), NULL); + assert(paramcheck_assert_failed); + + paramcheck_assert_failed = false; + param(cmd, j->buffer, j->toks, + p_req("u64", param_u64, &ival), + p_req("repeat", param_millionths, &fpval), + p_req("repeat", param_millionths, &fpval), NULL); + assert(paramcheck_assert_failed); /* check for repeated arguments */ - assert(!param(cmd, j->buffer, j->toks, - p_req("u64", param_u64, &ival), - p_req("repeated-arg", param_u64, &ival), NULL)); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); + paramcheck_assert_failed = false; + param(cmd, j->buffer, j->toks, + p_req("u64", param_u64, &ival), + p_req("repeated-arg", param_u64, &ival), NULL); + assert(paramcheck_assert_failed); - assert(!param(cmd, j->buffer, j->toks, - p_req("u64", (param_cbx) NULL, NULL), NULL)); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); + paramcheck_assert_failed = false; + param(cmd, j->buffer, j->toks, + p_req("u64", (param_cbx) NULL, NULL), NULL); + assert(paramcheck_assert_failed); /* Add required param after optional */ j = json_parse(cmd, "[ '25', '546', '26', '1.1' ]"); unsigned int *msatoshi; u64 *riskfactor_millionths; - assert(!param( + paramcheck_assert_failed = false; + param( cmd, j->buffer, j->toks, p_req("u64", param_u64, &ival), p_req("fp", param_millionths, &fpval), p_opt_def("msatoshi", param_number, &msatoshi, 100), p_req("riskfactor", param_millionths, &riskfactor_millionths), - NULL)); + NULL); assert(*msatoshi); assert(*msatoshi == 100); - assert(check_fail()); - assert(strstr(fail_msg, "developer error")); + assert(paramcheck_assert_failed); + + cmd->mode = CMD_NORMAL; } -#endif static void add_members(struct param **params, char **obj, @@ -454,17 +467,54 @@ static void deprecated_rename(void) NULL)); assert(*u64 == 42); - deprecated_apis = true; + cmd->deprecated_apis = true; j = json_parse(cmd, "{ 'old_u64': 42 }"); assert(param(cmd, j->buffer, j->toks, p_req("u64|old_u64", param_u64, &u64), NULL)); - deprecated_apis = false; + cmd->deprecated_apis = false; assert(!param(cmd, j->buffer, j->toks, p_req("u64|old_u64", param_u64, &u64), NULL)); } +static void invalid_bech32m(void) +{ + int wit_version; + uint8_t data_out[500]; + size_t data_out_len; + + /* Taken from BIP-350 */ + + /* Correct */ + assert(segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "BC1SW50QGDZ25J")); + assert(wit_version == 16); + assert(data_out_len == 2); + + /* Correct encoding, but expecting the wrong hrp*/ + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "tb", "BC1SW50QGDZ25J")); + + /* BIP350-valid, but fake HRP so was put in "invalid" section of BIP */ + assert(segwit_addr_decode(&wit_version, data_out, &data_out_len, "tc", "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut")); + + /* Incorrect for various reasons (including wrong checksum between bech32 <->bech32m */ + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "tb", "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "tb", "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1pw5dgrnzv")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "tb", "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "tb", "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j")); + assert(!segwit_addr_decode(&wit_version, data_out, &data_out_len, "bc", "bc1gmk9yu")); + +} + static void sendpay_nulltok(void) { struct json *j = json_parse(cmd, "[ 'A', '123']"); @@ -629,9 +679,7 @@ int main(int argc, char *argv[]) tok_tok(); null_params(); no_params(); -#if DEVELOPER bad_programmer(); -#endif dup_names(); five_hundred_params(); sendpay(); @@ -641,6 +689,7 @@ int main(int argc, char *argv[]) param_tests(); usage(); deprecated_rename(); + invalid_bech32m(); printf("run-params ok\n"); common_shutdown(); diff --git a/common/test/run-route_blinding_onion_test.c b/common/test/run-route_blinding_onion_test.c index df9452c3f01a..29a52c3e2218 100644 --- a/common/test/run-route_blinding_onion_test.c +++ b/common/test/run-route_blinding_onion_test.c @@ -14,11 +14,7 @@ #include "../onion_encode.c" #include "../sphinx.c" #include "../type_to_string.c" -#if EXPERIMENTAL_FEATURES - #include "../../wire/onion_exp_wiregen.c" -#else - #include "../../wire/onion_wiregen.c" -#endif +#include "../../wire/onion_wiregen.c" #include #include #include diff --git a/common/test/run-softref.c b/common/test/run-softref.c deleted file mode 100644 index e4294219b4c4..000000000000 --- a/common/test/run-softref.c +++ /dev/null @@ -1,161 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for amount_asset_is_main */ -bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } -/* Generated stub for amount_asset_to_sat */ -struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } -/* Generated stub for amount_sat */ -struct amount_sat amount_sat(u64 satoshis UNNEEDED) -{ fprintf(stderr, "amount_sat called!\n"); abort(); } -/* Generated stub for amount_sat_add */ - bool amount_sat_add(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } -/* Generated stub for amount_sat_div */ -struct amount_sat amount_sat_div(struct amount_sat sat UNNEEDED, u64 div UNNEEDED) -{ fprintf(stderr, "amount_sat_div called!\n"); abort(); } -/* Generated stub for amount_sat_eq */ -bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } -/* Generated stub for amount_sat_greater_eq */ -bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } -/* Generated stub for amount_sat_mul */ -bool amount_sat_mul(struct amount_sat *res UNNEEDED, struct amount_sat sat UNNEEDED, u64 mul UNNEEDED) -{ fprintf(stderr, "amount_sat_mul called!\n"); abort(); } -/* Generated stub for amount_sat_sub */ - bool amount_sat_sub(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } -/* Generated stub for amount_sat_to_asset */ -struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) -{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } -/* Generated stub for amount_tx_fee */ -struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) -{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for fromwire */ -const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) -{ fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_bool */ -bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } -/* Generated stub for fromwire_fail */ -void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_secp256k1_ecdsa_signature */ -void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for fromwire_sha256 */ -void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u32 */ -u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } -/* Generated stub for fromwire_u64 */ -u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } -/* Generated stub for fromwire_u8 */ -u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } -/* Generated stub for fromwire_u8_array */ -void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for towire */ -void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_secp256k1_ecdsa_signature */ -void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, - const secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for towire_sha256 */ -void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u32 */ -void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_u32 called!\n"); abort(); } -/* Generated stub for towire_u64 */ -void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_u8 */ -void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) -{ fprintf(stderr, "towire_u8 called!\n"); abort(); } -/* Generated stub for towire_u8_array */ -void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -struct objtype { - char *c_softref, *c_softref2; -}; - -int main(int argc, char *argv[]) -{ - const void *ctx = tal(NULL, char); - struct objtype *o; - char *c; - - common_setup(argv[0]); - - /* Simple test: freeing obj NULLs softref */ - o = tal(ctx, struct objtype); - c = tal(ctx, char); - set_softref(o, &o->c_softref, c); - assert(o->c_softref == c); - - tal_free(c); - assert(o->c_softref == NULL);\ - - /* Duplicate ptrs work */ - o = tal(ctx, struct objtype); - c = tal(ctx, char); - set_softref(o, &o->c_softref, c); - assert(o->c_softref == c); - set_softref(o, &o->c_softref2, c); - assert(o->c_softref2 == c); - - tal_free(c); - assert(o->c_softref == NULL); - assert(o->c_softref2 == NULL); - - /* Cleans up properly if o is freed first. */ - c = tal(ctx, char); - set_softref(o, &o->c_softref, c); - tal_free(o); - tal_free(c); - - /* Setting to NULL works. */ - o = tal(ctx, struct objtype); - c = tal(ctx, char); - set_softref(o, &o->c_softref, c); - assert(o->c_softref == c); - clear_softref(o, &o->c_softref); - assert(o->c_softref == NULL); - - /* Now it's not a softref, won't clear! */ - o->c_softref = c; - tal_free(c); - assert(o->c_softref == c); - tal_free(o); - - tal_free(ctx); - common_shutdown(); -} diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index 2b1c852d9838..d5f72b251c09 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -128,120 +128,163 @@ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNE int main(int argc, char *argv[]) { - const char *err; struct wireaddr_internal addr, *expect = tal(NULL, struct wireaddr_internal); common_setup(argv[0]); /* Simple IPv4 address. */ - assert(parse_wireaddr_internal("127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "127.0.0.1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_WIREADDR; - assert(parse_wireaddr("127.0.0.1:9735", &expect->u.wireaddr, 0, NULL, &err)); + expect->u.wireaddr.is_websocket = false; + assert(parse_wireaddr(tmpctx, "127.0.0.1:9735", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* IPv4 address with port. */ - assert(parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "127.0.0.1:1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_WIREADDR; - assert(parse_wireaddr("127.0.0.1:1", &expect->u.wireaddr, 0, NULL, &err)); + expect->u.wireaddr.is_websocket = false; + assert(parse_wireaddr(tmpctx, "127.0.0.1:1", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* Simple IPv6 address. */ - assert(parse_wireaddr_internal("::1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "::1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_WIREADDR; - assert(parse_wireaddr("::1", &expect->u.wireaddr, DEFAULT_PORT, NULL, &err)); + expect->u.wireaddr.is_websocket = false; + assert(parse_wireaddr(tmpctx, "::1", DEFAULT_PORT, NULL, &expect->u.wireaddr.wireaddr) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* IPv6 address with port. */ - assert(parse_wireaddr_internal("[::1]:1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "[::1]:1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_WIREADDR; - assert(parse_wireaddr("::1", &expect->u.wireaddr, 1, NULL, &err)); + expect->u.wireaddr.is_websocket = false; + assert(parse_wireaddr(tmpctx, "::1", 1, NULL, &expect->u.wireaddr.wireaddr) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address */ - assert(parse_wireaddr_internal("autotor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "autotor:127.0.0.1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9051, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port */ - assert(parse_wireaddr_internal("autotor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "autotor:127.0.0.1:9055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9055, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with torport */ - assert(parse_wireaddr_internal("autotor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "autotor:127.0.0.1/torport=9055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 9055; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9051, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port and torport */ - assert(parse_wireaddr_internal("autotor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "autotor:127.0.0.1:9055/torport=10055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 10055; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9055, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address */ - assert(parse_wireaddr_internal("statictor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "statictor:127.0.0.1", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; memset(expect->u.torservice.blob, 0, sizeof(expect->u.torservice.blob)); strcpy((char *)expect->u.torservice.blob, STATIC_TOR_MAGIC_STRING); - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9051, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "statictor:127.0.0.1:9055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9055, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with torport */ - assert(parse_wireaddr_internal("statictor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "statictor:127.0.0.1/torport=9055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 9055; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9051, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port and torport */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "statictor:127.0.0.1:9055/torport=10055", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 10055; - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9055, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port and torport and torblob */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055/torblob=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "statictor:127.0.0.1:9055/torport=10055/torblob=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 10055; /* This is actually nul terminated */ memset(expect->u.torservice.blob, 'x', sizeof(expect->u.torservice.blob)-1); - assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(parse_wireaddr(tmpctx, "127.0.0.1", 9055, NULL, &expect->u.torservice.address) == NULL); assert(wireaddr_internal_eq(&addr, expect)); /* local socket path */ - assert(parse_wireaddr_internal("/tmp/foo.sock", &addr, DEFAULT_PORT, false, false, false, &err)); + assert(parse_wireaddr_internal(tmpctx, "/tmp/foo.sock", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_SOCKNAME; strcpy(expect->u.sockname, "/tmp/foo.sock"); assert(wireaddr_internal_eq(&addr, expect)); + /* Websocket (only for IP addresses) */ + assert(parse_wireaddr_internal(tmpctx, "ws:/tmp/foo.sock", DEFAULT_PORT, false, &addr) != NULL); + + assert(parse_wireaddr_internal(tmpctx, "ws:127.0.0.1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "127.0.0.1:9735", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 address with port. */ + assert(parse_wireaddr_internal(tmpctx, "ws:127.0.0.1:1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "127.0.0.1:1", 0, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: Simple IPv6 address. */ + assert(parse_wireaddr_internal(tmpctx, "ws:::1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "::1", DEFAULT_PORT, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv6 address with port. */ + assert(parse_wireaddr_internal(tmpctx, "ws:[::1]:1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_WIREADDR; + expect->u.wireaddr.is_websocket = true; + assert(parse_wireaddr(tmpctx, "::1", 1, NULL, &expect->u.wireaddr.wireaddr) == NULL); + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 & v6 address. */ + assert(parse_wireaddr_internal(tmpctx, "ws:", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_ALLPROTO; + expect->u.allproto.is_websocket = true; + expect->u.allproto.port = DEFAULT_PORT; + assert(wireaddr_internal_eq(&addr, expect)); + + /* Websocket: IPv4 & v6 address with port */ + assert(parse_wireaddr_internal(tmpctx, "ws::1", DEFAULT_PORT, false, &addr) == NULL); + expect->itype = ADDR_INTERNAL_ALLPROTO; + expect->u.allproto.is_websocket = true; + expect->u.allproto.port = 1; + assert(wireaddr_internal_eq(&addr, expect)); + /* Unresolved */ - assert(!parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, false, &err)); - assert(streq(err, "Needed DNS, but lookups suppressed")); - assert(parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, true, &err)); + assert(parse_wireaddr_internal(tmpctx, "ozlabs.org", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = DEFAULT_PORT; assert(wireaddr_internal_eq(&addr, expect)); /* Unresolved with port */ - assert(!parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, false, &err)); - assert(streq(err, "Needed DNS, but lookups suppressed")); - assert(parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, true, &err)); + assert(parse_wireaddr_internal(tmpctx, "ozlabs.org:1234", DEFAULT_PORT, false, &addr) == NULL); expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = 1234; diff --git a/common/trace.c b/common/trace.c new file mode 100644 index 000000000000..3cbb6c2ca716 --- /dev/null +++ b/common/trace.c @@ -0,0 +1,206 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_USDT +#include + +#define MAX_ACTIVE_SPANS 128 +#define SPAN_ID_LEN 33 + +const char *trace_service_name = "lightningd"; + +struct span_tag { + char *name, *value; +}; + +struct span { + size_t key; + u64 id; + struct span *parent; + u64 start_time; + u64 end_time; + struct span_tag *tags; + char *name; +}; + +/* All traces we emit are prefixed with this constant. */ +static u64 trace_prefix = 0; +static struct span *active_spans; +static struct span *current; +static size_t last_span_id; + +static void trace_span_id_serialize(struct span *s, char *dest) +{ + hex_encode(&trace_prefix, 8, dest, 17); + hex_encode(&s->id, 8, 16 + dest, 17); +} + +static void trace_init(void) { + randombytes_buf(&trace_prefix, sizeof(u64)); + active_spans = malloc(sizeof(struct span) * MAX_ACTIVE_SPANS); + last_span_id = 1; + current = NULL; +} + +/** + * Convert the pointer to a context object to a numeric key. + */ +static size_t trace_key(const void *key) +{ + return (size_t)key; +} + +static struct span *trace_span_find(size_t key) +{ + for (size_t i = 0; i < MAX_ACTIVE_SPANS; i++) + if (active_spans[i].key == key) + return &active_spans[i]; + + return NULL; +} + +/** + * Find an empty slot for a new span. + */ +static struct span *trace_span_slot(void) +{ + struct span *s = trace_span_find(0); + + /* Might end up here if we have more than MAX_ACTIVE_SPANS + * concurrent spans. */ + assert(s); + assert(s->parent == NULL); + assert(s != current); + return s; +} + +static void trace_emit(struct span *s) +{ + char id[SPAN_ID_LEN]; + trace_span_id_serialize(s, id); + + char *res = tal_fmt( + NULL, + "[{\"id\": \"%s\", \"name\": \"%s\", " + "\"timestamp\": %" PRIu64 ", \"duration\": %" PRIu64 ",", + id + 16, s->name, s->start_time, s->end_time - s->start_time); + + tal_append_fmt(&res, "\"localEndpoint\": { \"serviceName\": \"%s\"}, ", + trace_service_name); + + if (s->parent != NULL) { + trace_span_id_serialize(s->parent, id); + tal_append_fmt(&res, "\"parentId\": \"%s\",", id + 16); + } + + tal_append_fmt(&res, "\"tags\": {"); + for (size_t i = 0; i < tal_count(s->tags); i++) { + tal_append_fmt(&res, "%s\"%s\": \"%s\"", i == 0 ? "" : ", ", + s->tags[i].name, s->tags[i].value); + } + + trace_span_id_serialize(s, id); + tal_append_fmt(&res, "}, \"traceId\": \"%.*s\"}]", 16, id); + DTRACE_PROBE2(lightningd, span_emit, id, res); + tal_free(res); +} + +/** + * Release the span back into the pool of available spans. + */ +static void trace_span_clear(struct span *s) +{ + s->key = 0; + s->id = 0; + s->parent = NULL; + s->name = tal_free(s->name); + s->tags = tal_free(s->tags); +} + +void trace_span_start(const char *name, const void *key) +{ + if (!trace_prefix) + trace_init(); + + size_t numkey = trace_key(key); + struct timeabs now = time_now(); + + assert(trace_span_find(numkey) == NULL); + struct span *s = trace_span_slot(); + assert(current == NULL || current->id != 0); + // assert(current != s); + s->key = numkey; + s->id = last_span_id++; + s->start_time = (now.ts.tv_sec * 1000000) + now.ts.tv_nsec / 1000; + s->parent = current; + s->tags = notleak(tal_arr(NULL, struct span_tag, 0)); + s->name = notleak(tal_strdup(NULL, name)); + current = s; + DTRACE_PROBE1(lightningd, span_start, s->id); +} + +void trace_span_end(const void *key) +{ + size_t numkey = trace_key(key); + struct span *s = trace_span_find(numkey); + assert(s && "Span to end not found"); + assert(s == current && "Ending a span that isn't the current one"); + + struct timeabs now = time_now(); + s->end_time = (now.ts.tv_sec * 1000000) + now.ts.tv_nsec / 1000; + DTRACE_PROBE1(lightningd, span_end, s->id); + trace_emit(s); + + /* Reset the context span we are in. */ + current = s->parent; + + /* Now reset the span */ + trace_span_clear(s); +} + +void trace_span_tag(const void *key, const char *name, const char *value) +{ + size_t numkey = trace_key(key); + struct span *span = trace_span_find(numkey); + assert(span); + + size_t s = tal_count(span->tags); + tal_resize(&span->tags, s + 1); + span->tags[s].name = tal_strdup(span->tags, name); + span->tags[s].value = tal_strdup(span->tags, value); +} + +void trace_span_suspend(const void *key) +{ + size_t numkey = trace_key(key); + struct span *span = trace_span_find(numkey); + current = NULL; + DTRACE_PROBE1(lightningd, span_suspend, span->id); +} + +void trace_span_resume(const void *key) +{ + size_t numkey = trace_key(key); + current = trace_span_find(numkey); + DTRACE_PROBE1(lightningd, span_resume, current->id); +} +#else /* HAVE_USDT */ + +void trace_span_start(const char *name, const void *key) {} +void trace_span_end(const void *key) {} +void trace_span_suspend(const void *key) {} +void trace_span_resume(const void *key) {} +void trace_span_tag(const void *key, const char *name, const char *value) {} + +#endif /* HAVE_USDT */ diff --git a/common/trace.h b/common/trace.h new file mode 100644 index 000000000000..7143659c9a8c --- /dev/null +++ b/common/trace.h @@ -0,0 +1,11 @@ +#ifndef LIGHTNING_COMMON_TRACE_H +#define LIGHTNING_COMMON_TRACE_H +#include "config.h" + +void trace_span_start(const char *name, const void *key); +void trace_span_end(const void *key); +void trace_span_suspend(const void *key); +void trace_span_resume(const void *key); +void trace_span_tag(const void *key, const char *name, const char *value); + +#endif /* LIGHTNING_COMMON_TRACE_H */ diff --git a/common/utils.c b/common/utils.c index c3c5b0fb9f2d..0c9ecc5ae270 100644 --- a/common/utils.c +++ b/common/utils.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -57,78 +58,6 @@ void tal_wally_end_onto_(const tal_t *parent, tal_wally_end(tal_steal(parent, from_wally)); } -#if DEVELOPER -/* If you've got a softref, we assume no reallocs. */ -static void dont_move_softref(tal_t *ctx, enum tal_notify_type ntype, void *info) -{ - abort(); -} -#endif - -static void softref_nullify(tal_t *obj, void **ptr) -{ - *ptr = NULL; -#if DEVELOPER - tal_del_notifier(obj, dont_move_softref); -#endif -} - -static void softref_cleanup(const tal_t *outer, void **ptr) -{ - if (*ptr) { - tal_del_destructor2(*ptr, softref_nullify, ptr); - } -#if DEVELOPER - tal_del_notifier(outer, dont_move_softref); -#endif -} - -void set_softref_(const tal_t *outer, size_t outersize, void **ptr, tal_t *obj) -{ - /* pointer is inside outer, right? */ - assert((char *)ptr >= (char *)outer); - assert((char *)ptr < (char *)outer + outersize); - - /* This is harmless if there was no prior, otherwise constrains the - * leak: we don't have enough information in softref_nullify to - * clear softref_cleanup */ - tal_del_destructor2(outer, softref_cleanup, ptr); - - if (obj) { - tal_add_destructor2(outer, softref_cleanup, ptr); - tal_add_destructor2(obj, softref_nullify, ptr); -#if DEVELOPER - tal_add_notifier(obj, TAL_NOTIFY_MOVE, dont_move_softref); -#endif - } - -#if DEVELOPER - tal_add_notifier(outer, TAL_NOTIFY_MOVE, dont_move_softref); -#endif - - *ptr = obj; -} - -void clear_softref_(const tal_t *outer, size_t outersize, void **ptr) -{ - assert((char *)ptr >= (char *)outer); - assert((char *)ptr < (char *)outer + outersize); - - if (*ptr) { - tal_del_destructor2(outer, softref_cleanup, ptr); - tal_del_destructor2(*ptr, softref_nullify, ptr); -#if DEVELOPER - tal_del_notifier(*ptr, dont_move_softref); -#endif - } - -#if DEVELOPER - tal_del_notifier(outer, dont_move_softref); -#endif - - *ptr = NULL; -} - char *tal_hexstr(const tal_t *ctx, const void *data, size_t len) { char *str = tal_arr(ctx, char, hex_str_size(len)); @@ -219,6 +148,16 @@ char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen) return ret; } +char *tal_strdup_or_null(const tal_t *ctx, const char *str) +{ + if (!str) { + /* You might have taken NULL; that's legal! Release now. */ + taken(str); + return NULL; + } + return tal_strdup(ctx, str); +} + int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created) { char *tmpdir = getenv("TMPDIR"); @@ -232,3 +171,12 @@ int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created) return fd; } + +char *str_lowering(const void *ctx, const char *string TAKES) +{ + char *ret; + + ret = tal_strdup(ctx, string); + for (char *p = ret; *p; p++) *p = tolower(*p); + return ret; +} diff --git a/common/utils.h b/common/utils.h index 2aadec5fc3cf..c110777a9ff9 100644 --- a/common/utils.h +++ b/common/utils.h @@ -54,20 +54,6 @@ char *tal_hex(const tal_t *ctx, const tal_t *data); /* Allocate and fill a buffer with the data of this hex string. */ u8 *tal_hexdata(const tal_t *ctx, const void *str, size_t len); -/* Macro to set memberptr in tal object outer to point to tal object obj, - * if it isn't NULL. - * The 0*sizeof() checks that *memberptr = obj is valid */ -#define set_softref(outer, memberptr, obj) \ - set_softref_((outer), sizeof(*(outer)) + 0*sizeof(*(memberptr) = obj), \ - (void **)(memberptr), (obj)) - -/* Macro to clear a (set) softref ptr to NULL */ -#define clear_softref(outer, memberptr) \ - clear_softref_((outer), sizeof(*(outer)), (void **)(memberptr)) - -void set_softref_(const tal_t *outer, size_t outersize, void **ptr, tal_t *obj); -void clear_softref_(const tal_t *outer, size_t outersize, void **ptr); - /* Note: p is never a complex expression, otherwise this multi-evaluates! */ #define tal_arr_expand(p, s) \ do { \ @@ -102,6 +88,9 @@ bool utf8_check(const void *buf, size_t buflen); /* Check it's UTF-8, return copy (or same if TAKES), or NULL if not valid. */ char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen); +/* Strdup, or pass through NULL */ +char *tal_strdup_or_null(const tal_t *ctx, const char *str); + /* Use the POSIX C locale. */ void setup_locale(void); @@ -123,10 +112,10 @@ void tal_wally_end(const tal_t *parent); /* ... or this if you want to reparent onto something which is * allocated by libwally here. Fixes up this from_wally obj to have a * proper tal_name, too! */ -#define tal_wally_end_onto(parent, from_wally, type) \ - tal_wally_end_onto_((parent), \ - (from_wally) + 0*sizeof((from_wally) == (type *)0), \ - stringify(type)) +#define tal_wally_end_onto(parent, from_wally, type) \ + tal_wally_end_onto_( \ + (parent), (from_wally), \ + &stringify(type)[0 * sizeof((from_wally) == (type *)0)]) void tal_wally_end_onto_(const tal_t *parent, tal_t *from_wally, const char *from_wally_name); @@ -153,18 +142,20 @@ STRUCTEQ_DEF(ripemd160, 0, u); #define IFDEV(dev, nondev) (nondev) #endif -#if EXPERIMENTAL_FEATURES -/* Make sure that nondev is evaluated, and valid, but is a constant */ -#define IFEXPERIMENTAL(exp, nonexp) (0 ? (nonexp) : (exp)) -#else -#define IFEXPERIMENTAL(exp, nonexp) (nonexp) -#endif - /* Context which all wally allocations use (see common/setup.c) */ extern const tal_t *wally_tal_ctx; /* Like mkstemp but resolves template relative to $TMPDIR (or /tmp if unset). * Returns created temporary path name at *created if successful. */ -int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created); +int tmpdir_mkstemp(const tal_t *ctx, const char *tmplt TAKES, char **created); + +/** + * tal_strlowering - return the same string by in lower case. + * @ctx: the context to tal from (often NULL) + * @string: the string that is going to be lowered (can be take()) + * + * FIXME: move this in ccan + */ +char *str_lowering(const void *ctx, const char *string TAKES); #endif /* LIGHTNING_COMMON_UTILS_H */ diff --git a/common/utxo.c b/common/utxo.c index b2192f4a5d74..a28e99cd3b79 100644 --- a/common/utxo.c +++ b/common/utxo.c @@ -22,7 +22,7 @@ void towire_utxo(u8 **pptr, const struct utxo *utxo) towire_bool(pptr, utxo->close_info->commitment_point != NULL); if (utxo->close_info->commitment_point) towire_pubkey(pptr, utxo->close_info->commitment_point); - towire_bool(pptr, utxo->close_info->option_anchor_outputs); + towire_bool(pptr, utxo->close_info->option_anchors); towire_u32(pptr, utxo->close_info->csv); } @@ -51,7 +51,7 @@ struct utxo *fromwire_utxo(const tal_t *ctx, const u8 **ptr, size_t *max) utxo->close_info->commitment_point); } else utxo->close_info->commitment_point = NULL; - utxo->close_info->option_anchor_outputs + utxo->close_info->option_anchors = fromwire_bool(ptr, max); utxo->close_info->csv = fromwire_u32(ptr, max); } else { diff --git a/common/utxo.h b/common/utxo.h index 6c5365c8e32c..830b865762d4 100644 --- a/common/utxo.h +++ b/common/utxo.h @@ -11,7 +11,7 @@ struct ext_key; struct unilateral_close_info { u64 channel_id; struct node_id peer_id; - bool option_anchor_outputs; + bool option_anchors; /* NULL if this is an option_static_remotekey commitment */ struct pubkey *commitment_point; u32 csv; @@ -71,8 +71,11 @@ static inline bool utxo_is_csv_locked(const struct utxo *utxo, u32 current_heigh { if (!utxo->close_info) return false; - /* All close outputs are csv locked for option_anchor_outputs */ - if (!utxo->blockheight && utxo->close_info->option_anchor_outputs) + /* BOLT #3: + * If `option_anchors` applies to the commitment transaction, the + * `to_remote` output is encumbered by a one block csv lock. + */ + if (!utxo->blockheight && utxo->close_info->option_anchors) return true; assert(*utxo->blockheight + utxo->close_info->csv > *utxo->blockheight); return *utxo->blockheight + utxo->close_info->csv > current_height; diff --git a/common/version.c b/common/version.c index 4fc20b6667e0..0840968fdb75 100644 --- a/common/version.c +++ b/common/version.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -13,7 +14,7 @@ const char *version(void) return VERSION; } -char *version_and_exit(const void *unused UNUSED) +static char *version_and_exit(const void *unused UNUSED) { printf("%s\n", VERSION); if (BUILD_FEATURES[0]) { @@ -22,6 +23,13 @@ char *version_and_exit(const void *unused UNUSED) exit(0); } +void opt_register_version(void) +{ + clnopt_noarg("--version|-V", OPT_EARLY|OPT_EXITS, + version_and_exit, NULL, + "Print version and exit"); +} + static bool cmp_release_version(const char *version) { if (version[0] != 'v') return false; diff --git a/common/version.h b/common/version.h index 90b7c824d12b..aebc002ed24a 100644 --- a/common/version.h +++ b/common/version.h @@ -3,15 +3,14 @@ #include "config.h" #include -char *version_and_exit(const void *unused); +/* Add --version|-V option */ +void opt_register_version(void); + const char *version(void); /* check if the current version is a release version. * * Released versions are of form v[year].[month]?(.patch)* */ bool is_released_version(void); -#define opt_register_version() \ - opt_register_early_noarg("--version|-V", version_and_exit, NULL, \ - "Print version and exit") #endif /* LIGHTNING_COMMON_VERSION_H */ diff --git a/common/wire_error.c b/common/wire_error.c index 457dc72b738d..e4376924e8be 100644 --- a/common/wire_error.c +++ b/common/wire_error.c @@ -94,7 +94,7 @@ char *sanitize_error(const tal_t *ctx, const u8 *errmsg, else if (fromwire_warning(ctx, errmsg, channel_id, &data)) warning = true; else if (fromwire_tx_abort(ctx, errmsg, channel_id, &data)) - warning = false; + warning = true; else return tal_fmt(ctx, "Invalid ERROR message '%s'", tal_hex(ctx, errmsg)); diff --git a/common/wireaddr.c b/common/wireaddr.c index 1c8be2fa88fa..174c5c22ca47 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -50,9 +50,6 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) memset(&addr->addr, 0, sizeof(addr->addr)); addr->addr[addr->addrlen] = 0; break; - case ADDR_TYPE_WEBSOCKET: - addr->addrlen = 0; - break; default: return false; } @@ -101,10 +98,12 @@ void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr) towire_u16(pptr, addr->u.torservice.port); return; case ADDR_INTERNAL_ALLPROTO: - towire_u16(pptr, addr->u.port); + towire_bool(pptr, addr->u.allproto.is_websocket); + towire_u16(pptr, addr->u.allproto.port); return; case ADDR_INTERNAL_WIREADDR: - towire_wireaddr(pptr, &addr->u.wireaddr); + towire_bool(pptr, addr->u.wireaddr.is_websocket); + towire_wireaddr(pptr, &addr->u.wireaddr.wireaddr); return; case ADDR_INTERNAL_FORPROXY: towire_u8_array(pptr, (const u8 *)addr->u.unresolved.name, @@ -128,7 +127,8 @@ bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, fromwire_fail(cursor, max); return *cursor != NULL; case ADDR_INTERNAL_ALLPROTO: - addr->u.port = fromwire_u16(cursor, max); + addr->u.allproto.is_websocket = fromwire_bool(cursor, max); + addr->u.allproto.port = fromwire_u16(cursor, max); return *cursor != NULL; case ADDR_INTERNAL_AUTOTOR: fromwire_wireaddr(cursor, max, &addr->u.torservice.address); @@ -141,7 +141,8 @@ bool fromwire_wireaddr_internal(const u8 **cursor, size_t *max, addr->u.torservice.port = fromwire_u16(cursor, max); return *cursor != NULL; case ADDR_INTERNAL_WIREADDR: - return fromwire_wireaddr(cursor, max, &addr->u.wireaddr); + addr->u.wireaddr.is_websocket = fromwire_bool(cursor, max); + return fromwire_wireaddr(cursor, max, &addr->u.wireaddr.wireaddr); case ADDR_INTERNAL_FORPROXY: fromwire_u8_array(cursor, max, (u8 *)addr->u.unresolved.name, sizeof(addr->u.unresolved.name)); @@ -178,14 +179,6 @@ void wireaddr_from_ipv6(struct wireaddr *addr, memcpy(&addr->addr, ip6, addr->addrlen); } -void wireaddr_from_websocket(struct wireaddr *addr, const u16 port) -{ - addr->type = ADDR_TYPE_WEBSOCKET; - addr->addrlen = 0; - addr->port = port; - memset(addr->addr, 0, sizeof(addr->addr)); -} - bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4) { if (addr->type != ADDR_TYPE_IPV4) @@ -210,14 +203,6 @@ bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6) return true; } -bool wireaddr_to_websocket(const struct wireaddr *addr, u16 *port) -{ - if (addr->type != ADDR_TYPE_WEBSOCKET) - return false; - *port = addr->port; - return true; -} - bool wireaddr_is_wildcard(const struct wireaddr *addr) { switch (addr->type) { @@ -227,7 +212,6 @@ bool wireaddr_is_wildcard(const struct wireaddr *addr) case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: return false; } abort(); @@ -240,9 +224,13 @@ char *fmt_wireaddr_internal(const tal_t *ctx, case ADDR_INTERNAL_SOCKNAME: return tal_fmt(ctx, "%s", a->u.sockname); case ADDR_INTERNAL_ALLPROTO: - return tal_fmt(ctx, ":%u", a->u.port); + return tal_fmt(ctx, "%s:%u", a->u.allproto.is_websocket ? "(ws)": "", + a->u.allproto.port); case ADDR_INTERNAL_WIREADDR: - return fmt_wireaddr(ctx, &a->u.wireaddr); + if (a->u.wireaddr.is_websocket) + return tal_fmt(ctx, "(ws)%s", + fmt_wireaddr(tmpctx, &a->u.wireaddr.wireaddr)); + return fmt_wireaddr(ctx, &a->u.wireaddr.wireaddr); case ADDR_INTERNAL_FORPROXY: return tal_fmt(ctx, "%s:%u", a->u.unresolved.name, a->u.unresolved.port); @@ -277,8 +265,6 @@ char *fmt_wireaddr_without_port(const tal_t * ctx, const struct wireaddr *a) b32_encode(tmpctx, a->addr, a->addrlen)); case ADDR_TYPE_DNS: return tal_fmt(ctx, "%s", a->addr); - case ADDR_TYPE_WEBSOCKET: - return tal_strdup(ctx, "websocket"); } hex = tal_hexstr(ctx, a->addr, a->addrlen); @@ -516,53 +502,73 @@ wireaddr_from_hostname(const tal_t *ctx, return tal_free(addrs); } -bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, - bool *no_dns, const char **err_msg) +const char *parse_wireaddr(const tal_t *ctx, + const char *arg, + u16 defport, + bool *no_dns, + struct wireaddr *addr) { struct in6_addr v6; struct in_addr v4; u16 port; char *ip; - bool res; + const char *err_msg; + struct wireaddr *addresses; + bool is_dns_waddr; - res = false; port = defport; - if (err_msg) - *err_msg = NULL; - if (!separate_address_and_port(tmpctx, arg, &ip, &port)) - goto finish; + if (strstarts(arg, "dns:")) { + is_dns_waddr = true; + arg += 4; + } else + is_dns_waddr = false; - if (streq(ip, "localhost")) - ip = "127.0.0.1"; - else if (streq(ip, "ip6-localhost")) - ip = "::1"; + if (!separate_address_and_port(tmpctx, arg, &ip, &port)) + return tal_strdup(ctx, "Error parsing hostname"); + + if (!is_dns_waddr) { + /* We resolved these even without DNS */ + if (streq(ip, "localhost")) + ip = "127.0.0.1"; + else if (streq(ip, "ip6-localhost")) + ip = "::1"; + } memset(&addr->addr, 0, sizeof(addr->addr)); if (inet_pton(AF_INET, ip, &v4) == 1) { + if (is_dns_waddr) + return tal_strdup(ctx, "dns: must be followed by a name"); wireaddr_from_ipv4(addr, &v4, port); - res = true; + return NULL; } else if (inet_pton(AF_INET6, ip, &v6) == 1) { + if (is_dns_waddr) + return tal_strdup(ctx, "dns: must be followed by a name"); wireaddr_from_ipv6(addr, &v6, port); - res = true; + return NULL; } - /* Resolve with getaddrinfo */ - if (!res) { - struct wireaddr *addresses = wireaddr_from_hostname(NULL, ip, port, - no_dns, NULL, err_msg); - if (addresses) { - *addr = addresses[0]; - tal_free(addresses); - res = true; - } + if (is_dns_waddr) { + addr->type = ADDR_TYPE_DNS; + if (strlen(ip) > DNS_ADDRLEN) + return "DNS address too long"; + + addr->addrlen = strlen(ip); + memcpy(addr->addr, ip, addr->addrlen); + addr->port = port; + return NULL; } -finish: - if (!res && err_msg && !*err_msg) - *err_msg = "Error parsing hostname"; - return res; + /* Resolve with getaddrinfo */ + addresses = wireaddr_from_hostname(NULL, ip, port, no_dns, NULL, &err_msg); + if (!addresses) + return tal_strdup(ctx, err_msg); + + /* FIXME: Allow return of multiple addresses? */ + *addr = addresses[0]; + tal_free(addresses); + return NULL; } bool wireaddr_internal_eq(const struct wireaddr_internal *a, @@ -575,7 +581,8 @@ bool wireaddr_internal_eq(const struct wireaddr_internal *a, case ADDR_INTERNAL_SOCKNAME: return streq(a->u.sockname, b->u.sockname); case ADDR_INTERNAL_ALLPROTO: - return a->u.port == b->u.port; + return a->u.allproto.is_websocket == b->u.allproto.is_websocket + && a->u.allproto.port == b->u.allproto.port; case ADDR_INTERNAL_STATICTOR: if (!memeq(a->u.torservice.blob, sizeof(a->u.torservice.blob), b->u.torservice.blob, sizeof(b->u.torservice.blob))) @@ -591,35 +598,36 @@ bool wireaddr_internal_eq(const struct wireaddr_internal *a, return false; return a->u.unresolved.port == b->u.unresolved.port; case ADDR_INTERNAL_WIREADDR: - return wireaddr_eq(&a->u.wireaddr, &b->u.wireaddr); + return a->u.wireaddr.is_websocket == b->u.wireaddr.is_websocket + && wireaddr_eq(&a->u.wireaddr.wireaddr, &b->u.wireaddr.wireaddr); } abort(); } -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, - u16 port, bool wildcard_ok, bool dns_ok, - bool unresolved_ok, - const char **err_msg) +const char *parse_wireaddr_internal(const tal_t *ctx, + const char *arg, + u16 default_port, + bool dns_lookup_ok, + struct wireaddr_internal *addr) { u16 splitport; - char *ip = NULL; + char *ip; char *service_addr; - bool needed_dns = false; + const char *err; + bool needed_dns, is_websocket; /* Addresses starting with '/' are local socket paths */ if (arg[0] == '/') { addr->itype = ADDR_INTERNAL_SOCKNAME; /* Check if the path is too long */ - if (strlen(arg) >= sizeof(addr->u.sockname)) { - if (err_msg) - *err_msg = "Socket name too long"; - return false; - } + if (strlen(arg) >= sizeof(addr->u.sockname)) + return "Socket name too long"; + /* Zero it out for passing across the wire */ memset(addr->u.sockname, 0, sizeof(addr->u.sockname)); strcpy(addr->u.sockname, arg); - return true; + return NULL; } /* 'autotor:' is a special prefix meaning talk to Tor to create @@ -635,22 +643,17 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, char *endp = NULL; addr->u.torservice.port = strtol(parts[i]+strlen("torport="), &endp, 10); if (addr->u.torservice.port <= 0 || *endp != '\0') { - if (err_msg) - *err_msg = "Bad :torport: number"; - return false; + return "Bad :torport: number"; } } else { - if (err_msg) - *err_msg = tal_fmt(tmpctx, "unknown tor arg %s", parts[i]); - return false; + return tal_fmt(ctx, "unknown tor arg %s", parts[i]); } } service_addr = tal_fmt(tmpctx, "%s", parts[0] + strlen("autotor:")); - - return parse_wireaddr(service_addr, - &addr->u.torservice.address, 9051, - dns_ok ? NULL : &needed_dns, err_msg); + return parse_wireaddr(ctx, service_addr, 9051, + dns_lookup_ok ? NULL : &needed_dns, + &addr->u.torservice.address); } /* 'statictor:' is a special prefix meaning talk to Tor to create @@ -668,23 +671,17 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, char *endp = NULL; addr->u.torservice.port = strtol(parts[i]+strlen("torport="), &endp, 10); if (addr->u.torservice.port <= 0 || *endp != '\0') { - if (err_msg) - *err_msg = "Bad :torport: number"; - return false; + return "Bad :torport: number"; } } else if (strstarts(parts[i], "torblob=")) { const char *blobdata = parts[i] + strlen("torblob="); if (strlen(blobdata) > TOR_V3_BLOBLEN) { - if (err_msg) - *err_msg = "torblob too long"; - return false; + return "torblob too long"; } strcpy(addr->u.torservice.blob, blobdata); use_magic_blob = false; } else { - if (err_msg) - *err_msg = tal_fmt(tmpctx, "unknown tor arg %s", parts[i]); - return false; + return tal_fmt(ctx, "unknown tor arg %s", parts[i]); } } @@ -695,49 +692,60 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, service_addr = tal_fmt(tmpctx, "%s", parts[0] + strlen("statictor:")); - return parse_wireaddr(service_addr, - &addr->u.torservice.address, 9051, - dns_ok ? NULL : &needed_dns, err_msg); + return parse_wireaddr(ctx, service_addr, 9051, + dns_lookup_ok ? NULL : &needed_dns, + &addr->u.torservice.address); } - splitport = port; - if (!separate_address_and_port(tmpctx, arg, &ip, &splitport)) { - if (err_msg) { - *err_msg = tal_fmt(tmpctx, "Error parsing hostname %s %s", (char *)arg, ip); - } - return false; + /* Do this before we get to separate_address_and_port! */ + if (strstarts(arg, "ws:")) { + arg += 3; + is_websocket = true; + } else { + is_websocket = false; } + /* This can fail, but that may be OK! */ + splitport = default_port; + if (!separate_address_and_port(tmpctx, arg, &ip, &splitport)) + ip = NULL; + /* An empty string means IPv4 and IPv6 (which under Linux by default * means just IPv6, and IPv4 gets autobound). */ - if (wildcard_ok && is_wildcardaddr(ip)) { + if (ip && streq(ip, "")) { addr->itype = ADDR_INTERNAL_ALLPROTO; - addr->u.port = splitport; - return true; + addr->u.allproto.is_websocket = is_websocket; + addr->u.allproto.port = splitport; + return NULL; } - addr->itype = ADDR_INTERNAL_WIREADDR; - if (parse_wireaddr(arg, &addr->u.wireaddr, port, - dns_ok ? NULL : &needed_dns, err_msg)) { - if (addr->u.wireaddr.type == ADDR_TYPE_TOR_V2_REMOVED) { - if (err_msg) - *err_msg = "v2 Tor onion services not supported"; - return false; - } - - return true; + needed_dns = false; + err = parse_wireaddr(ctx, arg, default_port, + dns_lookup_ok ? NULL : &needed_dns, + &addr->u.wireaddr.wireaddr); + if (!err) { + addr->itype = ADDR_INTERNAL_WIREADDR; + addr->u.wireaddr.is_websocket = is_websocket; + return NULL; } - if (!needed_dns || !unresolved_ok) - return false; + /* Did we fail because we needed DNS lookup? If not, we just failed. */ + if (!needed_dns) + return err; - /* We can't do DNS, so keep unresolved. */ - if (!wireaddr_from_unresolved(addr, ip, splitport)) { - if (err_msg) - *err_msg = "Name too long"; - return false; - } - return true; + /* Invalid port, like foo:xxx or foo:0 */ + if (!ip) + return "Malformed port"; + + if (is_websocket) + return "Could not resolve websocket address"; + + /* Keep unresolved. */ + tal_free(err); + if (!wireaddr_from_unresolved(addr, ip, splitport)) + return "Name too long"; + + return NULL; } bool wireaddr_from_unresolved(struct wireaddr_internal *addr, @@ -794,7 +802,7 @@ struct addrinfo *wireaddr_internal_to_addrinfo(const tal_t *ctx, case ADDR_INTERNAL_FORPROXY: break; case ADDR_INTERNAL_WIREADDR: - return wireaddr_to_addrinfo(ctx, &wireaddr->u.wireaddr); + return wireaddr_to_addrinfo(ctx, &wireaddr->u.wireaddr.wireaddr); } abort(); } @@ -826,7 +834,6 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx, case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: break; } abort(); @@ -875,14 +882,13 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr) case ADDR_INTERNAL_STATICTOR: continue; case ADDR_INTERNAL_WIREADDR: - switch (wireaddr[i].u.wireaddr.type) { + switch (wireaddr[i].u.wireaddr.wireaddr.type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: case ADDR_TYPE_DNS: return false; case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: - case ADDR_TYPE_WEBSOCKET: continue; } } diff --git a/common/wireaddr.h b/common/wireaddr.h index bdae7788fb30..7a34f70c442f 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -30,10 +30,6 @@ struct sockaddr_un; * * `5`: DNS hostname; data = `[byte:len][len*byte:hostname][u16:port]` (length up to 258) */ -/* BOLT-websockets #7: - * * `6`: WebSocket port; data = `[2:port]` (length 2) - */ - #define TOR_V2_ADDRLEN 10 #define TOR_V3_ADDRLEN 35 #define DNS_ADDRLEN 255 @@ -47,7 +43,6 @@ enum wire_addr_type { ADDR_TYPE_TOR_V2_REMOVED = 3, ADDR_TYPE_TOR_V3 = 4, ADDR_TYPE_DNS = 5, - ADDR_TYPE_WEBSOCKET = 6 }; struct wireaddr { @@ -75,10 +70,25 @@ enum addr_listen_announce fromwire_addr_listen_announce(const u8 **cursor, size_t *max); void towire_addr_listen_announce(u8 **pptr, enum addr_listen_announce ala); -/* If no_dns is non-NULL, we will set it to true and return false if - * we wanted to do a DNS lookup. */ -bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, - bool *no_dns, const char **err_msg); +/** + * parse_wireaddr - parse a string into the various defaults we have. + * @ctx: context to allocate returned error string + * @arg: the string + * @defport: the port to use if none specified in string + * @no_dns: if non-NULL, don't do DNS lookups. + * @addr: the addr to write, set if non-NULL return. + * + * If it returns NULL, check addr->itype to see if it's suitable for + * you! Otherwise, it returns a string allocated off @ctx. If you + * handed @no_dns, it will be set to true if the failure was due to + * the fact we wanted to do an DNS lookup, and false for other + * failures. + */ +const char *parse_wireaddr(const tal_t *ctx, + const char *arg, + u16 defport, + bool *no_dns, + struct wireaddr *addr); char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); char *fmt_wireaddr_without_port(const tal_t *ctx, const struct wireaddr *a); @@ -98,10 +108,8 @@ void wireaddr_from_ipv4(struct wireaddr *addr, void wireaddr_from_ipv6(struct wireaddr *addr, const struct in6_addr *ip6, const u16 port); -void wireaddr_from_websocket(struct wireaddr *addr, const u16 port); bool wireaddr_to_ipv4(const struct wireaddr *addr, struct sockaddr_in *s4); bool wireaddr_to_ipv6(const struct wireaddr *addr, struct sockaddr_in6 *s6); -bool wireaddr_to_websocket(const struct wireaddr *addr, u16 *port); bool wireaddr_is_wildcard(const struct wireaddr *addr); @@ -119,9 +127,15 @@ struct wireaddr_internal { enum wireaddr_internal_type itype; union { /* ADDR_INTERNAL_WIREADDR */ - struct wireaddr wireaddr; + struct waddr { + struct wireaddr wireaddr; + bool is_websocket; + } wireaddr; /* ADDR_INTERNAL_ALLPROTO */ - u16 port; + struct allproto { + u16 port; + bool is_websocket; + } allproto; /* ADDR_INTERNAL_AUTOTOR * ADDR_INTERNAL_STATICTOR */ struct torservice { @@ -156,10 +170,21 @@ bool is_wildcardaddr(const char *arg); bool is_dnsaddr(const char *arg); -bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, - u16 port, bool wildcard_ok, bool dns_ok, - bool unresolved_ok, - const char **err_msg); +/** + * parse_wireaddr_internal - parse a string into the various defaults we have. + * @ctx: context to allocate returned error string + * @arg: the string + * @default_port: the port to use if none specified in string + * @dns_lookup_ok: true if it's OK to do DNS name lookups. + * @addr: the addr to write, set if non-NULL return. + * + * If it returns NULL, you want to check addr->itype to see if it's + * suitable for you! */ +const char *parse_wireaddr_internal(const tal_t *ctx, + const char *arg, + u16 default_port, + bool dns_lookup_ok, + struct wireaddr_internal *addr); void towire_wireaddr_internal(u8 **pptr, const struct wireaddr_internal *addr); diff --git a/configure b/configure index 9295c461ac68..807d4ab466d3 100755 --- a/configure +++ b/configure @@ -43,7 +43,7 @@ usage_with_default() echo " $1 (default $DEF)" } -# Given DEVELOPER, what COPTFLAGS do we default to. +# Given DEBUGBUILD, what COPTFLAGS do we default to. default_coptflags() { if [ "$1" = 0 ]; then @@ -142,31 +142,36 @@ set_defaults() CC=${CC:-cc} CDEBUGFLAGS=${CDEBUGFLAGS--std=gnu11 -g -fstack-protector-strong} DEVELOPER=${DEVELOPER:-0} - EXPERIMENTAL_FEATURES=${EXPERIMENTAL_FEATURES:-0} + DEBUGBUILD=${DEBUGBUILD:-0} + # --enable-developer forces debug build + [ "$DEVELOPER" = 0 ] || DEBUGBUILD=1 COMPAT=${COMPAT:-1} STATIC=${STATIC:-0} + CLANG_COVERAGE=${CLANG_COVERAGE:-0} ASAN=${ASAN:-0} UBSAN=${UBSAN:-0} FUZZING=${FUZZING:-0} + FUZZFLAGS="" CSANFLAGS="" if [ "$ASAN" != 0 ]; then CSANFLAGS="$CSANFLAGS -fsanitize=address" - if [ "$DEVELOPER" != 0 ]; then + if [ "$DEBUGBUILD" != 0 ]; then CSANFLAGS="$CSANFLAGS -fno-sanitize-recover=address" fi fi if [ "$UBSAN" != 0 ]; then CSANFLAGS="$CSANFLAGS -fsanitize=undefined" - if [ "$DEVELOPER" != 0 ]; then + if [ "$DEBUGBUILD" != 0 ]; then CSANFLAGS="$CSANFLAGS -fno-sanitize-recover=undefined" fi fi if [ "$FUZZING" != 0 ]; then - CSANFLAGS="$CSANFLAGS -fsanitize=fuzzer-no-link" + FUZZFLAGS="-fsanitize=fuzzer-no-link" + CSANFLAGS="$CSANFLAGS $FUZZFLAGS" fi echo CSANFLAGS = $CSANFLAGS PYTEST=${PYTEST-$(default_pytest)} - COPTFLAGS=${COPTFLAGS-$(default_coptflags "$DEVELOPER")} + COPTFLAGS=${COPTFLAGS-$(default_coptflags "$DEBUGBUILD")} CONFIGURATOR_CC=${CONFIGURATOR_CC-$CC} VALGRIND=${VALGRIND:-$(default_valgrind_setting)} TEST_NETWORK=${TEST_NETWORK:-regtest} @@ -179,7 +184,7 @@ usage() echo "If --reconfigure is specified, $CONFIG_VAR_FILE will set defaults." echo "Default settings:" set_defaults - DEFAULT_COPTFLAGS="$(default_coptflags $DEVELOPER)" + DEFAULT_COPTFLAGS="$(default_coptflags $DEBUGBUILD)" # We assume we have a modern gcc. DEFAULT_CWARNFLAGS="$(default_cwarnflags ""$DEFAULT_COPTFLAGS"" 1 1)" usage_with_default "CC" "$CC" @@ -196,14 +201,16 @@ usage() echo " Prefix for make install" usage_with_default "--enable/disable-developer" "$DEVELOPER" "enable" "disable" echo " Developer mode, good for testing" - usage_with_default "--enable/disable-experimental-features" "$EXPERIMENTAL_FEATURES" "enable" "disable" - echo " Enable experimental features" + usage_with_default "--enable/disable-debug" "$DEBUGBUILD" "enable" "disable" + echo " Extra debug checks in the build, good for testing" usage_with_default "--enable/disable-compat" "$COMPAT" "enable" "disable" echo " Compatibility mode, good to disable to see if your software breaks" usage_with_default "--enable/disable-valgrind" "(autodetect)" echo " Run tests with Valgrind" usage_with_default "--enable/disable-static" "$STATIC" "enable" "disable" - echo " Static link sqlite3, gmp and zlib libraries" + echo " Static link sqlite3 and zlib libraries" + usage_with_default "--enable/disable-coverage" "$CLANG_COVERAGE" "enable" "disable" + echo " Compile with Clang coverage instrumentation" usage_with_default "--enable/disable-address-sanitizer" "$ASAN" "enable" "disable" echo " Compile with address-sanitizer" usage_with_default "--enable/disable-ub-sanitizer" "$UBSAN" "enable" "disable" @@ -234,20 +241,21 @@ for opt in "$@"; do # Set from values if not already set. while IFS='=' read VAR VAL; do if eval [ -z \${$VAR+x} ]; then eval $VAR=\"$VAL\"; fi + # If they had an old config, it might set DEVELOPER. if [ "$VAR" = DEVELOPER ]; then + DEFAULT_COPTFLAGS=$(default_coptflags "$VAL") + DEBUGBUILD="$VAL" + VAR=DEBUGBUILD + fi + if [ "$VAR" = DEBUGBUILD ]; then DEFAULT_COPTFLAGS=$(default_coptflags "$VAL") fi done < $CONFIG_VAR_FILE # If we were those defaults, unset so we get new defaults in - # case DEVELOPER has changed. + # case DEBUGBUILD has changed. if [ x"$COPTFLAGS" = x"$DEFAULT_COPTFLAGS" ]; then unset COPTFLAGS fi - # If they didn't have -Wshadow=local, add it on --reconfigure - # (we only added it later) - if [ x"${CWARNFLAGS%*-Wshadow=local}" = x"$CWARNFLAGS" ]; then - unset CWARNFLAGS - fi ;; CC=*) CC="${opt#CC=}";; CONFIGURATOR_CC=*) CONFIGURATOR_CC="${opt#CONFIGURATOR_CC=}";; @@ -258,14 +266,16 @@ for opt in "$@"; do --prefix=*) PREFIX="${opt#--prefix=}";; --enable-developer) DEVELOPER=1;; --disable-developer) DEVELOPER=0;; - --enable-experimental-features) EXPERIMENTAL_FEATURES=1;; - --disable-experimental-features) EXPERIMENTAL_FEATURES=0;; + --enable-debugbuild) DEBUGBUILD=1;; + --disable-debugbuild) DEBUGBUILD=0;; --enable-compat) COMPAT=1;; --disable-compat) COMPAT=0;; --enable-valgrind) VALGRIND=1;; --disable-valgrind) VALGRIND=0;; --enable-static) STATIC=1;; --disable-static) STATIC=0;; + --enable-coverage) CLANG_COVERAGE=1;; + --disable-coverage) CLANG_COVERAGE=0;; --enable-address-sanitizer) ASAN=1;; --disable-address-sanitizer) ASAN=0;; --enable-ub-sanitizer) UBSAN=1;; @@ -297,6 +307,17 @@ if [ "$ASAN" = "1" ]; then fi fi +if [ "$CLANG_COVERAGE" = "1" ]; then + case "$CC" in + (*"clang"*) + ;; + (*) + echo "Clang coverage requires building with CC=clang." + exit 1 + ;; + esac +fi + if [ "$FUZZING" = "1" ]; then case "$CC" in (*"clang"*) @@ -392,6 +413,18 @@ int main(void) return 0; } /*END*/ +var=HAVE_USDT +desc=User Statically-Defined Tracing (USDT) +style=DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE +code= +#include + +int main(void) +{ + DTRACE_PROBE(lightningd, test_sdt); + return 0; +} +/*END*/ var=HAVE_GCC desc=compiler is GCC style=OUTSIDE_MAIN @@ -473,16 +506,18 @@ add_var CWARNFLAGS "$CWARNFLAGS" add_var CDEBUGFLAGS "$CDEBUGFLAGS" add_var COPTFLAGS "$COPTFLAGS" add_var CSANFLAGS "$CSANFLAGS" +add_var FUZZFLAGS "$FUZZFLAGS" add_var SQLITE3_CFLAGS "$SQLITE3_CFLAGS" add_var SQLITE3_LDLIBS "$SQLITE3_LDLIBS" add_var POSTGRES_INCLUDE "$POSTGRES_INCLUDE" add_var POSTGRES_LDLIBS "$POSTGRES_LDLIBS" add_var VALGRIND "$VALGRIND" add_var DEVELOPER "$DEVELOPER" $CONFIG_HEADER.$$ -add_var EXPERIMENTAL_FEATURES "$EXPERIMENTAL_FEATURES" $CONFIG_HEADER.$$ +add_var DEBUGBUILD "$DEBUGBUILD" add_var COMPAT "$COMPAT" $CONFIG_HEADER.$$ add_var PYTEST "$PYTEST" add_var STATIC "$STATIC" +add_var CLANG_COVERAGE "$CLANG_COVERAGE" add_var ASAN "$ASAN" add_var UBSAN "$UBSAN" add_var TEST_NETWORK "$TEST_NETWORK" diff --git a/connectd/connectd.c b/connectd/connectd.c index 7ceb488cc7b9..6d78a26aca81 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -410,12 +410,12 @@ static bool get_remote_address(struct io_conn *conn, if (s.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (void *)&s; addr->itype = ADDR_INTERNAL_WIREADDR; - wireaddr_from_ipv6(&addr->u.wireaddr, + wireaddr_from_ipv6(&addr->u.wireaddr.wireaddr, &s6->sin6_addr, ntohs(s6->sin6_port)); } else if (s.ss_family == AF_INET) { struct sockaddr_in *s4 = (void *)&s; addr->itype = ADDR_INTERNAL_WIREADDR; - wireaddr_from_ipv4(&addr->u.wireaddr, + wireaddr_from_ipv4(&addr->u.wireaddr.wireaddr, &s4->sin_addr, ntohs(s4->sin_port)); } else if (s.ss_family == AF_UNIX) { struct sockaddr_un *sun = (void *)&s; @@ -468,6 +468,7 @@ static struct io_plan *connection_in(struct io_conn *conn, { struct conn_in conn_in_arg; + conn_in_arg.addr.u.wireaddr.is_websocket = false; if (!get_remote_address(conn, &conn_in_arg.addr)) return io_close(conn); @@ -487,6 +488,7 @@ static struct io_plan *websocket_connection_in(struct io_conn *conn, int err; struct conn_in conn_in_arg; + conn_in_arg.addr.u.wireaddr.is_websocket = true; if (!get_remote_address(conn, &conn_in_arg.addr)) return io_close(conn); @@ -724,10 +726,10 @@ static struct io_plan *conn_init(struct io_conn *conn, break; case ADDR_INTERNAL_WIREADDR: /* DNS should have been resolved before */ - assert(addr->u.wireaddr.type != ADDR_TYPE_DNS); + assert(addr->u.wireaddr.wireaddr.type != ADDR_TYPE_DNS); /* If it was a Tor address, we wouldn't be here. */ - assert(!is_toraddr((char*)addr->u.wireaddr.addr)); - ai = wireaddr_to_addrinfo(tmpctx, &addr->u.wireaddr); + assert(!is_toraddr((char*)addr->u.wireaddr.wireaddr.addr)); + ai = wireaddr_to_addrinfo(tmpctx, &addr->u.wireaddr.wireaddr); break; } assert(ai); @@ -751,8 +753,8 @@ static struct io_plan *conn_proxy_init(struct io_conn *conn, port = addr->u.unresolved.port; break; case ADDR_INTERNAL_WIREADDR: - host = fmt_wireaddr_without_port(tmpctx, &addr->u.wireaddr); - port = addr->u.wireaddr.port; + host = fmt_wireaddr_without_port(tmpctx, &addr->u.wireaddr.wireaddr); + port = addr->u.wireaddr.wireaddr.port; break; case ADDR_INTERNAL_SOCKNAME: case ADDR_INTERNAL_ALLPROTO: @@ -818,7 +820,7 @@ static void try_connect_one_addr(struct connecting *connect) use_proxy = true; break; case ADDR_INTERNAL_WIREADDR: - switch (addr->u.wireaddr.type) { + switch (addr->u.wireaddr.wireaddr.type) { case ADDR_TYPE_TOR_V2_REMOVED: af = -1; break; @@ -848,9 +850,9 @@ static void try_connect_one_addr(struct connecting *connect) hints.ai_family = AF_UNSPEC; hints.ai_protocol = 0; hints.ai_flags = AI_ADDRCONFIG; - gai_err = getaddrinfo((char *)addr->u.wireaddr.addr, + gai_err = getaddrinfo((char *)addr->u.wireaddr.wireaddr.addr, tal_fmt(tmpctx, "%d", - addr->u.wireaddr.port), + addr->u.wireaddr.wireaddr.port), &hints, &ais); if (gai_err != 0) { tal_append_fmt(&connect->errors, @@ -864,16 +866,17 @@ static void try_connect_one_addr(struct connecting *connect) /* create new addrhints on-the-fly per result ... */ for (aii = ais; aii; aii = aii->ai_next) { addrhint.itype = ADDR_INTERNAL_WIREADDR; + addrhint.u.wireaddr.is_websocket = false; if (aii->ai_family == AF_INET) { sa4 = (struct sockaddr_in *) aii->ai_addr; - wireaddr_from_ipv4(&addrhint.u.wireaddr, + wireaddr_from_ipv4(&addrhint.u.wireaddr.wireaddr, &sa4->sin_addr, - addr->u.wireaddr.port); + addr->u.wireaddr.wireaddr.port); } else if (aii->ai_family == AF_INET6) { sa6 = (struct sockaddr_in6 *) aii->ai_addr; - wireaddr_from_ipv6(&addrhint.u.wireaddr, + wireaddr_from_ipv6(&addrhint.u.wireaddr.wireaddr, &sa6->sin6_addr, - addr->u.wireaddr.port); + addr->u.wireaddr.wireaddr.port); } else { /* skip unsupported ai_family */ continue; @@ -884,9 +887,6 @@ static void try_connect_one_addr(struct connecting *connect) } freeaddrinfo(ais); goto next; - case ADDR_TYPE_WEBSOCKET: - af = -1; - break; } } @@ -1041,15 +1041,15 @@ static struct listen_fd *make_listen_fd(const tal_t *ctx, static struct listen_fd *handle_wireaddr_listen(const tal_t *ctx, const struct wireaddr_internal *wi, bool listen_mayfail, - enum is_websocket is_websocket, char **errstr) { struct sockaddr_in addr; struct sockaddr_in6 addr6; const struct wireaddr *wireaddr; + bool is_websocket = wi->u.wireaddr.is_websocket; assert(wi->itype == ADDR_INTERNAL_WIREADDR); - wireaddr = &wi->u.wireaddr; + wireaddr = &wi->u.wireaddr.wireaddr; /* Note the use of a switch() over enum here, even though it must be * IPv4 or IPv6 here; that will catch future changes. */ @@ -1064,7 +1064,6 @@ static struct listen_fd *handle_wireaddr_listen(const tal_t *ctx, return make_listen_fd(ctx, wi, AF_INET6, &addr6, sizeof(addr6), listen_mayfail, is_websocket, errstr); /* Handle specially by callers. */ - case ADDR_TYPE_WEBSOCKET: case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: @@ -1102,10 +1101,12 @@ find_local_address(const struct listen_fd **listen_fds) for (size_t i = 0; i < tal_count(listen_fds); i++) { if (listen_fds[i]->wi.itype != ADDR_INTERNAL_WIREADDR) continue; - if (listen_fds[i]->wi.u.wireaddr.type != ADDR_TYPE_IPV4 - && listen_fds[i]->wi.u.wireaddr.type != ADDR_TYPE_IPV6) + if (listen_fds[i]->wi.u.wireaddr.is_websocket) + continue; + if (listen_fds[i]->wi.u.wireaddr.wireaddr.type != ADDR_TYPE_IPV4 + && listen_fds[i]->wi.u.wireaddr.wireaddr.type != ADDR_TYPE_IPV6) continue; - return &listen_fds[i]->wi.u.wireaddr; + return &listen_fds[i]->wi.u.wireaddr.wireaddr; } return NULL; } @@ -1168,7 +1169,7 @@ setup_listeners(const tal_t *ctx, /* You can only announce wiretypes, not internal formats! */ assert(proposed_wireaddr[i].itype == ADDR_INTERNAL_WIREADDR); - add_announceable(announceable, &wa.u.wireaddr); + add_announceable(announceable, &wa.u.wireaddr.wireaddr); } /* Now look for listening addresses. */ @@ -1208,42 +1209,41 @@ setup_listeners(const tal_t *ctx, bool ipv6_ok; wa.itype = ADDR_INTERNAL_WIREADDR; - wa.u.wireaddr.port = wa.u.port; + wa.u.wireaddr.wireaddr.port = wa.u.allproto.port; + wa.u.wireaddr.is_websocket = wa.u.allproto.is_websocket; /* First, create wildcard IPv6 address. */ - wa.u.wireaddr.type = ADDR_TYPE_IPV6; - wa.u.wireaddr.addrlen = 16; - memset(wa.u.wireaddr.addr, 0, - sizeof(wa.u.wireaddr.addr)); + wa.u.wireaddr.wireaddr.type = ADDR_TYPE_IPV6; + wa.u.wireaddr.wireaddr.addrlen = 16; + memset(wa.u.wireaddr.wireaddr.addr, 0, + sizeof(wa.u.wireaddr.wireaddr.addr)); /* This may fail due to no IPv6 support. */ - lfd = handle_wireaddr_listen(ctx, &wa, false, - NORMAL_SOCKET, errstr); + lfd = handle_wireaddr_listen(ctx, &wa, false, errstr); if (lfd) { tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); if (announce - && public_address(daemon, &wa.u.wireaddr)) + && public_address(daemon, &wa.u.wireaddr.wireaddr)) add_announceable(announceable, - &wa.u.wireaddr); + &wa.u.wireaddr.wireaddr); } ipv6_ok = (lfd != NULL); /* Now, create wildcard IPv4 address. */ - wa.u.wireaddr.type = ADDR_TYPE_IPV4; - wa.u.wireaddr.addrlen = 4; - memset(wa.u.wireaddr.addr, 0, - sizeof(wa.u.wireaddr.addr)); + wa.u.wireaddr.wireaddr.type = ADDR_TYPE_IPV4; + wa.u.wireaddr.wireaddr.addrlen = 4; + memset(wa.u.wireaddr.wireaddr.addr, 0, + sizeof(wa.u.wireaddr.wireaddr.addr)); /* This listen *may* fail, as long as IPv6 succeeds! */ - lfd = handle_wireaddr_listen(ctx, &wa, ipv6_ok, - NORMAL_SOCKET, errstr); + lfd = handle_wireaddr_listen(ctx, &wa, ipv6_ok, errstr); if (lfd) { tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); if (announce - && public_address(daemon, &wa.u.wireaddr)) + && public_address(daemon, &wa.u.wireaddr.wireaddr)) add_announceable(announceable, - &wa.u.wireaddr); + &wa.u.wireaddr.wireaddr); } else if (!ipv6_ok) { /* Both failed, return now, errstr set. */ return NULL; @@ -1252,13 +1252,12 @@ setup_listeners(const tal_t *ctx, } /* This is a vanilla wireaddr as per BOLT #7 */ case ADDR_INTERNAL_WIREADDR: - lfd = handle_wireaddr_listen(ctx, &wa, false, - NORMAL_SOCKET, errstr); + lfd = handle_wireaddr_listen(ctx, &wa, false, errstr); if (!lfd) return NULL; tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); - if (announce && public_address(daemon, &wa.u.wireaddr)) - add_announceable(announceable, &wa.u.wireaddr); + if (announce && public_address(daemon, &wa.u.wireaddr.wireaddr)) + add_announceable(announceable, &wa.u.wireaddr.wireaddr); continue; case ADDR_INTERNAL_FORPROXY: break; @@ -1285,8 +1284,6 @@ setup_listeners(const tal_t *ctx, /* Only consider bindings added before this! */ size_t num_nonws_listens = tal_count(listen_fds); - /* If not overriden below, this is the default. */ - *errstr = "Cannot listen on websocket: not listening on any IPv4/6 addresses"; for (size_t i = 0; i < num_nonws_listens; i++) { /* Ignore UNIX sockets */ if (listen_fds[i]->wi.itype != ADDR_INTERNAL_WIREADDR) @@ -1294,51 +1291,26 @@ setup_listeners(const tal_t *ctx, /* Override with websocket port */ addr = listen_fds[i]->wi; - addr.u.wireaddr.port = daemon->websocket_port; + addr.u.wireaddr.is_websocket = true; + addr.u.wireaddr.wireaddr.port = daemon->websocket_port; /* We set mayfail on all but the first websocket; * it's quite common to have multple overlapping * addresses. */ - lfd = handle_wireaddr_listen(ctx, &addr, - announced_some, - WEBSOCKET, errstr); + lfd = handle_wireaddr_listen(ctx, &addr, announced_some, + errstr); if (!lfd) continue; - if (!announced_some) { - /* BOLT-websocket #7: - * - MUST NOT add a `type 6` address unless - * there is also at least one address of - * different type. - */ - if (tal_count(*announceable) != 0) { - /* See https://github.com/lightningnetwork/lnd/issues/6432: - * if we add websocket to the node_announcement, it doesn't propagate. - * So we do not do this for now in general! */ - if (daemon->announce_websocket) { - wireaddr_from_websocket(&addr.u.wireaddr, - daemon->websocket_port); - add_announceable(announceable, - &addr.u.wireaddr); - } - } else { - status_unusual("Bound to websocket %s," - " but we cannot announce" - " the websocket as we don't" - " announce anything else!", - type_to_string(tmpctx, - struct wireaddr_internal, - &addr)); - } - announced_some = true; - } - + announced_some = true; tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); } /* If none of those was possible, it's a configuration error? */ - if (tal_count(listen_fds) == num_nonws_listens) + if (tal_count(listen_fds) == num_nonws_listens) { + *errstr = "Cannot listen on websocket: not listening on any IPv4/6 addresses"; return NULL; + } } /* FIXME: Websocket over Tor (difficult for autotor, since we need @@ -1641,10 +1613,11 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, continue; struct wireaddr_internal a; a.itype = ADDR_INTERNAL_WIREADDR; - a.u.wireaddr = new_addrs[j]; + a.u.wireaddr.is_websocket = false; + a.u.wireaddr.wireaddr = new_addrs[j]; status_peer_debug(id, "Resolved %s to %s", hostnames[i], type_to_string(tmpctx, struct wireaddr, - &a.u.wireaddr)); + &a.u.wireaddr.wireaddr)); tal_arr_expand(addrs, a); } /* Other seeds will likely have the same information. */ @@ -1654,33 +1627,27 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, } } -static bool wireaddr_int_equals_wireaddr(const struct wireaddr_internal *addr_a, - const struct wireaddr *addr_b) -{ - if (!addr_a || !addr_b) - return false; - return wireaddr_eq(&addr_a->u.wireaddr, addr_b); -} - /*~ Adds just one address type. * * Ignores deprecated and the `addrhint`. */ -static void add_gossip_addrs_bytype(struct wireaddr_internal **addrs, - const struct wireaddr *normal_addrs, - const struct wireaddr_internal *addrhint, - const enum wire_addr_type type) +static void add_gossip_addrs_bytypes(struct wireaddr_internal **addrs, + const struct wireaddr *normal_addrs, + const struct wireaddr *addrhint, + u64 types) { for (size_t i = 0; i < tal_count(normal_addrs); i++) { - if (normal_addrs[i].type == ADDR_TYPE_TOR_V2_REMOVED) - continue; - if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) + if (addrhint && wireaddr_eq(addrhint, &normal_addrs[i])) continue; - if (normal_addrs[i].type != type) + /* I guess this is possible in future! */ + if (normal_addrs[i].type > 63) continue; - struct wireaddr_internal addr; - addr.itype = ADDR_INTERNAL_WIREADDR; - addr.u.wireaddr = normal_addrs[i]; - tal_arr_expand(addrs, addr); + if (((u64)1 << normal_addrs[i].type) & types) { + struct wireaddr_internal addr; + addr.itype = ADDR_INTERNAL_WIREADDR; + addr.u.wireaddr.is_websocket = false; + addr.u.wireaddr.wireaddr = normal_addrs[i]; + tal_arr_expand(addrs, addr); + } } } @@ -1694,29 +1661,38 @@ static void add_gossip_addrs_bytype(struct wireaddr_internal **addrs, * direct (faster) IPv4 and finally (less stable) TOR connections. */ static void add_gossip_addrs(struct wireaddr_internal **addrs, const struct wireaddr *normal_addrs, - const struct wireaddr_internal *addrhint) + const struct wireaddr *addrhint) { - /* Wrap each one in a wireaddr_internal and add to addrs. */ - for (size_t i = 0; i < tal_count(normal_addrs); i++) { - /* This is not supported, ignore. */ - if (normal_addrs[i].type == ADDR_TYPE_TOR_V2_REMOVED) - continue; - /* The hint was already added earlier */ - if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) - continue; - /* We add IPv4 and TOR in separate loops to prefer IPv6 */ - if (normal_addrs[i].type == ADDR_TYPE_IPV4) - continue; - if (normal_addrs[i].type == ADDR_TYPE_TOR_V3) - continue; - struct wireaddr_internal addr; - addr.itype = ADDR_INTERNAL_WIREADDR; - addr.u.wireaddr = normal_addrs[i]; - tal_arr_expand(addrs, addr); + u64 types[] = { 0, 0, 0 }; + + /* Note gratuitous use of switch() means we'll know if a new one + * appears! */ + for (size_t i = ADDR_TYPE_IPV4; i <= ADDR_TYPE_DNS; i++) { + switch ((enum wire_addr_type)i) { + /* First priority */ + case ADDR_TYPE_IPV6: + case ADDR_TYPE_DNS: + types[0] |= ((u64)1 << i); + break; + /* Second priority */ + case ADDR_TYPE_IPV4: + types[1] |= ((u64)1 << i); + break; + case ADDR_TYPE_TOR_V3: + /* Third priority */ + types[2] |= ((u64)1 << i); + break; + /* We can't use these to connect to! */ + case ADDR_TYPE_TOR_V2_REMOVED: + break; + } + /* Other results returned are possible, but we don't understand + * them anyway! */ } - /* Do the loop for skipped protocols in preferred order. */ - add_gossip_addrs_bytype(addrs, normal_addrs, addrhint, ADDR_TYPE_IPV4); - add_gossip_addrs_bytype(addrs, normal_addrs, addrhint, ADDR_TYPE_TOR_V3); + + /* Add in priority order */ + for (size_t i = 0; i < ARRAY_SIZE(types); i++) + add_gossip_addrs_bytypes(addrs, normal_addrs, addrhint, types[i]); } /*~ Consumes addrhint if not NULL. @@ -1758,7 +1734,12 @@ static void try_connect_peer(struct daemon *daemon, if (addrhint) tal_arr_expand(&addrs, *addrhint); - add_gossip_addrs(&addrs, gossip_addrs, addrhint); + /* Tell it to omit the existing hint (if that's a wireaddr itself) */ + add_gossip_addrs(&addrs, gossip_addrs, + addrhint + && addrhint->itype == ADDR_INTERNAL_WIREADDR + && !addrhint->u.wireaddr.is_websocket + ? &addrhint->u.wireaddr.wireaddr : NULL); if (tal_count(addrs) == 0) { /* Don't resolve via DNS seed if we're supposed to use proxy. */ @@ -2180,6 +2161,11 @@ static void memleak_daemon_cb(struct htable *memtable, struct daemon *daemon) } #endif /* DEVELOPER */ +static void gossipd_failed(struct daemon_conn *gossipd) +{ + status_failed(STATUS_FAIL_GOSSIP_IO, "gossipd exited?"); +} + int main(int argc, char *argv[]) { setup_locale(); @@ -2218,6 +2204,7 @@ int main(int argc, char *argv[]) daemon->gossipd = daemon_conn_new(daemon, GOSSIPCTL_FD, recv_gossip, NULL, daemon); + tal_add_destructor(daemon->gossipd, gossipd_failed); /* Set up ecdh() function so it uses our HSM fd, and calls * status_failed on error. */ diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 740b52957ffe..f977ca1a7811 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -91,9 +91,10 @@ static bool is_msg_gossip_broadcast(const u8 *cursor) case WIRE_YOUR_PEER_STORAGE: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: -#if EXPERIMENTAL_FEATURES case WIRE_STFU: -#endif + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: break; } return false; diff --git a/connectd/gossip_store.c b/connectd/gossip_store.c index 2dbf6bb5164e..ca1fdd4deec5 100644 --- a/connectd/gossip_store.c +++ b/connectd/gossip_store.c @@ -94,9 +94,10 @@ static bool public_msg_type(enum peer_wire type) case WIRE_ONION_MESSAGE: case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: -#if EXPERIMENTAL_FEATURES case WIRE_STFU: -#endif + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: return false; case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_NODE_ANNOUNCEMENT: @@ -132,8 +133,8 @@ u8 *gossip_store_next(const tal_t *ctx, flags = be16_to_cpu(hdr.flags); ratelimited = (flags & GOSSIP_STORE_RATELIMIT_BIT); - /* Skip any deleted entries. */ - if (flags & GOSSIP_STORE_DELETED_BIT) { + /* Skip any deleted/dying entries. */ + if (flags & (GOSSIP_STORE_DELETED_BIT|GOSSIP_STORE_DYING_BIT)) { *off += r + msglen; continue; } diff --git a/connectd/handshake.c b/connectd/handshake.c index 79f6f763da02..a4d54a1e3b6d 100644 --- a/connectd/handshake.c +++ b/connectd/handshake.c @@ -481,6 +481,7 @@ static struct io_plan *act_three_initiator(struct io_conn *conn, * 3. `se = ECDH(s.priv, re)` * * where `re` is the ephemeral public key of the responder */ + tal_free(h->ss); h->ss = tal(h, struct secret); ecdh(&h->re, h->ss); SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss))); diff --git a/connectd/multiplex.c b/connectd/multiplex.c index beb4706d8b54..ce69b1dcc997 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -389,9 +389,10 @@ static bool is_urgent(enum peer_wire type) case WIRE_ONION_MESSAGE: case WIRE_PEER_STORAGE: case WIRE_YOUR_PEER_STORAGE: -#if EXPERIMENTAL_FEATURES case WIRE_STFU: -#endif + case WIRE_SPLICE: + case WIRE_SPLICE_ACK: + case WIRE_SPLICE_LOCKED: return false; /* These are time-sensitive, and so send without delay. */ diff --git a/connectd/netaddress.c b/connectd/netaddress.c index 5f9fb57fd3fa..bcd389353b3e 100644 --- a/connectd/netaddress.c +++ b/connectd/netaddress.c @@ -259,7 +259,6 @@ bool guess_address(struct wireaddr *addr) case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: status_broken("Cannot guess address type %u", addr->type); break; } diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 4dae9839aaec..55f73107bcda 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -118,7 +118,6 @@ static struct io_plan *peer_init_received(struct io_conn *conn, case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: remote_addr = tal_free(remote_addr); break; } @@ -237,19 +236,20 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, * incoming connection, if the node is the receiver and the connection was done * via IP. */ - if (incoming && addr->itype == ADDR_INTERNAL_WIREADDR && - address_routable(&addr->u.wireaddr, true)) { - switch (addr->u.wireaddr.type) { + if (incoming + && addr->itype == ADDR_INTERNAL_WIREADDR + && !addr->u.wireaddr.is_websocket + && address_routable(&addr->u.wireaddr.wireaddr, true)) { + switch (addr->u.wireaddr.wireaddr.type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: tlvs->remote_addr = tal_arr(tlvs, u8, 0); - towire_wireaddr(&tlvs->remote_addr, &addr->u.wireaddr); + towire_wireaddr(&tlvs->remote_addr, &addr->u.wireaddr.wireaddr); break; /* Only report IP addresses back for now */ case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: break; } } diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index cb92f51c77e9..f6d46d0639cb 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -327,7 +327,7 @@ int main(int argc, char *argv[]) e_pub = pubkey("036360e856310ce5d294e8be33fc807077dc56ac80d95d9cd4ddbd21325eff73f7"); dummy.itype = ADDR_INTERNAL_WIREADDR; - dummy.u.wireaddr.addrlen = 0; + dummy.u.wireaddr.wireaddr.addrlen = 0; initiator_handshake((void *)tmpctx, &ls_pub, &rs_pub, &dummy, NULL, NORMAL_SOCKET, success, NULL); /* Should not exit! */ abort(); diff --git a/connectd/test/run-netaddress.c b/connectd/test/run-netaddress.c index 91fc359260fc..74f06c3d0c96 100644 --- a/connectd/test/run-netaddress.c +++ b/connectd/test/run-netaddress.c @@ -168,16 +168,19 @@ int main(int argc, char *argv[]) common_setup(argv[0]); // start with IPv4 - parse_wireaddr("0.0.0.0", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "0.0.0.0", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(!IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); - parse_wireaddr("255.255.255.255", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "255.255.255.255", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(!IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); - parse_wireaddr("127.0.0.1", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "127.0.0.1", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); @@ -185,7 +188,8 @@ int main(int argc, char *argv[]) assert(address_routable(&wa, true)); assert(!address_routable(&wa, false)); - parse_wireaddr("0.1.2.3", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "0.1.2.3", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); @@ -193,7 +197,8 @@ int main(int argc, char *argv[]) assert(address_routable(&wa, true)); assert(!address_routable(&wa, false)); - parse_wireaddr("10.0.21.42", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "10.0.21.42", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); @@ -202,7 +207,8 @@ int main(int argc, char *argv[]) assert(!address_routable(&wa, true)); assert(!address_routable(&wa, false)); - parse_wireaddr("192.168.123.4", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "192.168.123.4", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); @@ -211,7 +217,8 @@ int main(int argc, char *argv[]) assert(!address_routable(&wa, true)); assert(!address_routable(&wa, false)); - parse_wireaddr("1.2.3.4", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "1.2.3.4", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(IsIPv4(&wa)); assert(!IsIPv6(&wa)); @@ -220,7 +227,8 @@ int main(int argc, char *argv[]) assert(address_routable(&wa, false)); // now IPv6 stuff - parse_wireaddr("2142:DEAD:beef::1", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "2142:DEAD:beef::1", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(IsValid(&wa)); assert(!IsIPv4(&wa)); assert(IsIPv6(&wa)); @@ -228,12 +236,14 @@ int main(int argc, char *argv[]) assert(address_routable(&wa, true)); assert(address_routable(&wa, false)); - parse_wireaddr("0::0", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "0::0", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(!IsValid(&wa)); assert(!IsIPv4(&wa)); assert(IsIPv6(&wa)); - parse_wireaddr("2001:db8::1", &wa, DEFAULT_PORT, NULL, &err); + err = parse_wireaddr(tmpctx, "2001:db8::1", DEFAULT_PORT, NULL, &wa); + assert(!err); assert(!IsValid(&wa)); assert(!IsIPv4(&wa)); assert(IsIPv6(&wa)); diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index fe6cbf70f3af..0b0d8c002e8f 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -321,7 +321,7 @@ int main(int argc, char *argv[]) e_pub = pubkey("02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27"); dummy.itype = ADDR_INTERNAL_WIREADDR; - dummy.u.wireaddr.addrlen = 0; + dummy.u.wireaddr.wireaddr.addrlen = 0; responder_handshake((void *)tmpctx, &ls_pub, &dummy, NULL, NORMAL_SOCKET, success, NULL); /* Should not exit! */ abort(); diff --git a/connectd/tor_autoservice.c b/connectd/tor_autoservice.c index 054a220156b4..4b08f9235f82 100644 --- a/connectd/tor_autoservice.c +++ b/connectd/tor_autoservice.c @@ -113,7 +113,7 @@ static struct wireaddr *make_onion(const tal_t *ctx, port, fmt_wireaddr(tmpctx, local))); while ((line = tor_response_line(rbuf)) != NULL) { - const char *name; + const char *name, *err; if (!strstarts(line, "ServiceID=")) continue; @@ -124,9 +124,10 @@ static struct wireaddr *make_onion(const tal_t *ctx, name = tal_fmt(tmpctx, "%s.onion", line); onion = tal(ctx, struct wireaddr); - if (!parse_wireaddr(name, onion, local->port, false, NULL)) + err = parse_wireaddr(tmpctx, name, local->port, NULL, onion); + if (err) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Tor gave bad onion name '%s'", name); + "Tor gave bad onion name '%s': %s", name, err); status_info("New autotor service onion address: \"%s:%d\" bound from extern port:%d", name, local->port, port); discard_remaining_response(rbuf); return onion; @@ -150,7 +151,7 @@ static struct wireaddr *make_fixed_onion(const tal_t *ctx, blob64, port, fmt_wireaddr(tmpctx, local))); while ((line = tor_response_line_wfail(rbuf))) { - const char *name; + const char *name, *err; if (strstarts(line, "Onion address collision")) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Tor address in use"); @@ -164,9 +165,10 @@ static struct wireaddr *make_fixed_onion(const tal_t *ctx, name = tal_fmt(tmpctx, "%s.onion", line); onion = tal(ctx, struct wireaddr); - if (!parse_wireaddr(name, onion, port, false, NULL)) + err = parse_wireaddr(tmpctx, name, port, false, onion); + if (err) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Tor gave bad onion name '%s'", name); + "Tor gave bad onion name '%s': %s", name, err); #ifdef SUPERVERBOSE status_info("Static Tor service onion address: \"%s:%d,%s\"from blob %s base64 %s ", name, port ,fmt_wireaddr(tmpctx, local), blob ,blob64); diff --git a/contrib/.gitignore b/contrib/.gitignore new file mode 100644 index 000000000000..f44fc41890ac --- /dev/null +++ b/contrib/.gitignore @@ -0,0 +1 @@ +/remote_hsmd diff --git a/contrib/clang-coverage-report.sh b/contrib/clang-coverage-report.sh new file mode 100755 index 000000000000..5116ae9d31d8 --- /dev/null +++ b/contrib/clang-coverage-report.sh @@ -0,0 +1,26 @@ +#!/bin/bash -eu +# +# Generates an HTML coverage report from a raw Clang coverage profile. See +# https://clang.llvm.org/docs/SourceBasedCodeCoverage.html for more details. +# +# Example usage to create full_channel.html from full_channel.profraw for the +# run-full_channel unit test: +# ./contrib/clang-coverage-report.sh channeld/test/run-full_channel \ +# full_channel.profraw full_channel.html + +if [[ "$#" -ne 3 ]]; then + echo "Usage: $0 BINARY RAW_PROFILE_FILE TARGET_HTML_FILE" + exit 1 +fi + +readonly BINARY="$1" +readonly RAW_PROFILE_FILE="$2" +readonly TARGET_HTML_FILE="$3" + +readonly MERGED_PROFILE_FILE=$(mktemp) + +llvm-profdata merge -sparse "${RAW_PROFILE_FILE}" -o "${MERGED_PROFILE_FILE}" +llvm-cov show "${BINARY}" -instr-profile="${MERGED_PROFILE_FILE}" -format=html \ + > "${TARGET_HTML_FILE}" + +rm "${MERGED_PROFILE_FILE}" diff --git a/contrib/config-example b/contrib/config-example new file mode 100644 index 000000000000..69da60a4aad7 --- /dev/null +++ b/contrib/config-example @@ -0,0 +1,105 @@ +### +## Core Lightning Configurations +### +## Edit this file to your desired configurations +## Uncomment any settings you wish to configure +### +## Copy this file to the root directory `~.lightning` +## Or a network directory such as `~.lightning/bitcoin` +## Edit this files name to `config` +### +## To view all documentation & options run: +## `man lightningd config` +### + + +## +# General Settings +## + +## Give your node a name +#alias= + +## Pick your favorite color as a hex code +#rgb=FFA500 + +## Set the network for Core Lightning to sync to, Bitcoin Mainnet for most users +## Not required if the config file is in a network directory +#network=bitcoin + +## Run `lightningd` as a background daemon instead of in the terminal +## Requires `log-file` path +#daemon + +## Log output to specified file instead of the terminal +## Required for `daemon` +#log-file=/path/to/your/.lightning/log + +## Set to debug for more verbose log output +#log-level=info + +## Password encrypt your `hsm_secret` +## You must supply the password on startup if you choose to do this +#encrypted-hsm + + +## +# Networking Settings +## + +## INBOUND connections - default PORT 9735 +## 0.0.0.0 for clearnet | localhost+torhiddenservice for tor +#addr=:9735 + +## Peers can find your node here +#announce-addr=:9735 + +## Bind Core Lightning RPC server to localhost PORT 9734 +#bind-addr=127.0.0.1:9734 + +## Configure proxy/tor for OUTBOUND connections. +#proxy=127.0.0.1:9050 + +## Force all outbound connections through the proxy/tor +#always-use-proxy=false + + +## +# Channel Settings +# !! Please read the manual before editing these !! +# !! and for a full list of available options !! +## + +## Removes capacity limit for channel creation +#large-channels + +## Base fee to charge for every payment which passes through in MILLISATOSHI (1/1000 of a satoshi) +#fee-base=1000 + +## In millionths (10 is 0.001%, 100 is 0.01%, 1000 is 0.1% etc.) +#fee-per-satoshi=10 + +## Minimum value, in SATOSHI, to accept for channel open requests +#min-capacity-sat=10000 + +## Sets the minimum HTLC value for new channels +#htlc-minimum-msat=0 + +## Blockchain confirmations required for channel to be considered valid +#funding-confirms=3 + +## Max number of HTLC channels can handle in each direction +#max-concurrent-htlcs=30 + +## +# Plugins +# Plugins allow you to extend Core Lightnings functionality +# For a community curated list of available plugins visit: +# "https://github.com/lightningd/plugins" +## + +## Load your plugins from a directory +#plugin-dir=/path/to/your/.lightning/plugins + +## Load plugins individually +#plugin=path/to/plugin diff --git a/contrib/docker/Dockerfile.alpine b/contrib/docker/Dockerfile.alpine index 375de7faf348..ddae6b2a1c37 100644 --- a/contrib/docker/Dockerfile.alpine +++ b/contrib/docker/Dockerfile.alpine @@ -12,7 +12,6 @@ RUN apk update && \ cargo \ gettext \ git \ - gmp-dev \ libsodium \ libtool \ net-tools \ diff --git a/contrib/docker/linuxarm32v7.Dockerfile b/contrib/docker/Dockerfile.arm32v7 similarity index 56% rename from contrib/docker/linuxarm32v7.Dockerfile rename to contrib/docker/Dockerfile.arm32v7 index 8bea5cbfadc9..9bab2500cbe0 100644 --- a/contrib/docker/linuxarm32v7.Dockerfile +++ b/contrib/docker/Dockerfile.arm32v7 @@ -1,22 +1,19 @@ -# This dockerfile is meant to cross compile with a x64 machine for a arm32v7 host -# It is using multi stage build: +# This dockerfile is meant to compile a core-lightning arm32v7 image +# It is using multi stage build: # * downloader: Download litecoin/bitcoin and qemu binaries needed for core-lightning -# * builder: Cross compile c-lightning dependencies, then c-lightning itself with static linking +# * builder: Compile core-lightning dependencies, then core-lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. -# From the root of the repository, run "docker build -t yourimage:yourtag -f contrib/linuxarm32v7.Dockerfile ." +# From the root of the repository, run "docker build -t yourimage:yourtag ." FROM debian:bullseye-slim as downloader RUN set -ex \ - && apt-get update \ - && apt-get install -qq --no-install-recommends ca-certificates dirmngr wget \ - qemu-user-static binfmt-support + && apt-get update \ + && apt-get install -qq --no-install-recommends ca-certificates dirmngr qemu-user-static wget -WORKDIR /opt - -RUN wget -qO /opt/tini "https://github.com/krallin/tini/releases/download/v0.18.0/tini-armhf" \ - && echo "01b54b934d5f5deb32aa4eb4b0f71d0e76324f4f0237cc262d59376bf2bdc269 /opt/tini" | sha256sum -c - \ - && chmod +x /opt/tini +ADD https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-armel /tini +ADD https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-armel.asc /tini.asc +RUN chmod +x /tini ARG BITCOIN_VERSION=22.0 ENV BITCOIN_TARBALL bitcoin-$BITCOIN_VERSION-arm-linux-gnueabihf.tar.gz @@ -47,7 +44,6 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:bullseye-slim as builder ENV LIGHTNINGD_VERSION=master - RUN apt-get update -qq && \ apt-get install -qq -y --no-install-recommends \ autoconf \ @@ -56,86 +52,95 @@ RUN apt-get update -qq && \ ca-certificates \ curl \ dirmngr \ + gcc-10 \ gettext \ git \ gnupg \ libpq-dev \ libtool \ libffi-dev \ - python3 \ + pkg-config \ + libssl-dev \ + protobuf-compiler \ + python3.9 \ python3-dev \ python3-mako \ python3-pip \ python3-venv \ python3-setuptools \ - wget && \ - # arm32v7 compilers - apt-get install -qq -y --no-install-recommends \ - libc6-armhf-cross \ - gcc-arm-linux-gnueabihf \ - g++-arm-linux-gnueabihf + libev-dev \ + libevent-dev \ + qemu-user-static \ + wget -ENV target_host=arm-linux-gnueabihf - -ENV AR=${target_host}-ar \ -AS=${target_host}-as \ -CC=${target_host}-gcc \ -CXX=${target_host}-g++ \ -LD=${target_host}-ld \ -STRIP=${target_host}-strip \ -QEMU_LD_PREFIX=/usr/${target_host} \ -HOST=${target_host} +ENV PKG_CONFIG_PATH /usr/lib/arm-linux-gnueabihf/pkgconfig RUN wget -q https://zlib.net/fossils/zlib-1.2.13.tar.gz \ -&& tar xvf zlib-1.2.13.tar.gz \ -&& cd zlib-1.2.13 \ -&& ./configure --prefix=$QEMU_LD_PREFIX \ -&& make \ -&& make install && cd .. && rm zlib-1.2.13.tar.gz && rm -rf zlib-1.2.13 + && tar xvf zlib-1.2.13.tar.gz \ + && cd zlib-1.2.13 \ + && ./configure \ + && make \ + && make install && cd .. && \ + rm zlib-1.2.13.tar.gz && \ + rm -rf zlib-1.2.13 RUN apt-get install -y --no-install-recommends unzip tclsh \ -&& wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ -&& unzip sqlite-src-3290000.zip \ -&& cd sqlite-src-3290000 \ -&& ./configure --enable-static --disable-readline --disable-threadsafe --disable-load-extension --host=${target_host} --prefix=$QEMU_LD_PREFIX \ -&& make \ -&& make install && cd .. && rm sqlite-src-3290000.zip && rm -rf sqlite-src-3290000 - + && wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ + && unzip sqlite-src-3290000.zip \ + && cd sqlite-src-3290000 \ + && ./configure --enable-static --disable-readline --disable-threadsafe --disable-load-extension \ + && make \ + && make install && cd .. && rm sqlite-src-3290000.zip && rm -rf sqlite-src-3290000 + +USER root RUN wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz \ -&& tar xvf gmp-6.1.2.tar.xz \ -&& cd gmp-6.1.2 \ -&& ./configure --disable-assembly --prefix=$QEMU_LD_PREFIX --host=${target_host} \ -&& make \ -&& make install && cd .. && rm gmp-6.1.2.tar.xz && rm -rf gmp-6.1.2 + && tar xvf gmp-6.1.2.tar.xz \ + && cd gmp-6.1.2 \ + && ./configure --disable-assembly \ + && make \ + && make install && cd .. && rm gmp-6.1.2.tar.xz && rm -rf gmp-6.1.2 + +ENV RUST_PROFILE=release +ENV PATH=$PATH:/root/.cargo/bin/ +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +RUN rustup toolchain install stable --component rustfmt --allow-downgrade -COPY --from=downloader /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static WORKDIR /opt/lightningd COPY . /tmp/lightning + RUN git clone --recursive /tmp/lightning . && \ git checkout $(git --work-tree=/tmp/lightning --git-dir=/tmp/lightning/.git rev-parse HEAD) -ARG DEVELOPER=0 +ARG DEVELOPER=1 ENV PYTHON_VERSION=3 +RUN curl -sSL https://install.python-poetry.org | python3 - + +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1 +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 + +RUN pip3 install --upgrade pip setuptools wheel +RUN pip3 wheel cryptography +RUN pip3 install grpcio-tools -RUN curl -sSL https://install.python-poetry.org | python3 - \ -&& pip3 install -U pip \ -&& pip3 install -U wheel \ -&& /root/.local/bin/poetry install +RUN /root/.local/bin/poetry install RUN ./configure --prefix=/tmp/lightning_install --enable-static && \ -make DEVELOPER=${DEVELOPER} && \ -/root/.local/bin/poetry run make install + make DEVELOPER=${DEVELOPER} && \ + /root/.local/bin/poetry run make install -FROM arm32v7/debian:bullseye-slim as final -COPY --from=downloader /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static -COPY --from=downloader /opt/tini /usr/bin/tini +# RUN pip3 install -r plugins/clnrest/requirements.txt +RUN pip3 install flask gunicorn json5 flask_restx flask-socketio gevent gevent-websocket +RUN pip3 install ./contrib/pyln-client + +FROM debian:bullseye-slim as final RUN apt-get update && \ apt-get install -y --no-install-recommends \ socat \ inotify-tools \ - python3 \ + python3.9 \ python3-pip \ + qemu-user-static \ libpq5 && \ rm -rf /var/lib/apt/lists/* @@ -147,9 +152,12 @@ ENV LIGHTNINGD_NETWORK=bitcoin RUN mkdir $LIGHTNINGD_DATA && \ touch $LIGHTNINGD_DATA/config VOLUME [ "/root/.lightning" ] + COPY --from=builder /tmp/lightning_install/ /usr/local/ +COPY --from=builder /usr/local/lib/python3.9/dist-packages/ /usr/local/lib/python3.9/dist-packages/ COPY --from=downloader /opt/bitcoin/bin /usr/bin COPY --from=downloader /opt/litecoin/bin /usr/bin +COPY --from=downloader "/tini" /usr/bin/tini COPY tools/docker-entrypoint.sh entrypoint.sh EXPOSE 9735 9835 diff --git a/contrib/docker/linuxarm64v8.Dockerfile b/contrib/docker/Dockerfile.arm64v8 similarity index 56% rename from contrib/docker/linuxarm64v8.Dockerfile rename to contrib/docker/Dockerfile.arm64v8 index 79f3ed8bad59..d6ffa4d03f4b 100644 --- a/contrib/docker/linuxarm64v8.Dockerfile +++ b/contrib/docker/Dockerfile.arm64v8 @@ -1,25 +1,23 @@ -# This dockerfile is meant to cross compile with a x64 machine for a arm64v8 host -# It is using multi stage build: -# * downloader: Download litecoin/bitcoin and qemu binaries needed for Core Lightning -# * builder: Cross compile Core Lightning dependencies, then Core Lightning itself with static linking +# This dockerfile is meant to compile a core-lightning arm64v8 image +# It is using multi stage build: +# * downloader: Download litecoin/bitcoin and qemu binaries needed for core-lightning +# * builder: Compile core-lightning dependencies, then core-lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. -# From the root of the repository, run "docker build -t yourimage:yourtag -f contrib/linuxarm64v8.Dockerfile ." +# From the root of the repository, run "docker build -t yourimage:yourtag ." FROM debian:bullseye-slim as downloader RUN set -ex \ && apt-get update \ - && apt-get install -qq --no-install-recommends ca-certificates dirmngr wget \ - qemu-user-static binfmt-support + && apt-get install -qq --no-install-recommends ca-certificates dirmngr qemu-user-static wget -WORKDIR /opt +ADD https://github.com/krallin/tini/releases/download/v0.19.0/tini-static-arm64 /tini +RUN chmod +x /tini -RUN wget -qO /opt/tini "https://github.com/krallin/tini/releases/download/v0.18.0/tini-arm64" \ - && echo "7c5463f55393985ee22357d976758aaaecd08defb3c5294d353732018169b019 /opt/tini" | sha256sum -c - \ - && chmod +x /opt/tini +WORKDIR /opt ARG BITCOIN_VERSION=22.0 -ENV BITCOIN_TARBALL bitcoin-$BITCOIN_VERSION-aarch64-linux-gnu.tar.gz +ENV BITCOIN_TARBALL bitcoin-${BITCOIN_VERSION}-aarch64-linux-gnu.tar.gz ENV BITCOIN_URL https://bitcoincore.org/bin/bitcoin-core-$BITCOIN_VERSION/$BITCOIN_TARBALL ENV BITCOIN_ASC_URL https://bitcoincore.org/bin/bitcoin-core-$BITCOIN_VERSION/SHA256SUMS @@ -32,9 +30,8 @@ RUN mkdir /opt/bitcoin && cd /opt/bitcoin \ && tar -xzvf $BITCOIN_TARBALL $BD/bitcoin-cli --strip-components=1 \ && rm $BITCOIN_TARBALL - ENV LITECOIN_VERSION 0.16.3 -ENV LITECOIN_URL https://download.litecoin.org/litecoin-${LITECOIN_VERSION}/linux/litecoin-${LITECOIN_VERSION}-aarch64-linux-gnu.tar.gz +ENV LITECOIN_URL https://download.litecoin.org/litecoin-${LITECOIN_VERSION}/linux/litecoin-${LITECOIN_VERSION}-aarch64-linux-gnu.tar.gz ENV LITECOIN_SHA256 3284316bdf10496528b3cd730877be3a1ea34add49dfc88fe0e96eb9925c1f08 # install litecoin binaries @@ -48,7 +45,6 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:bullseye-slim as builder ENV LIGHTNINGD_VERSION=master - RUN apt-get update -qq && \ apt-get install -qq -y --no-install-recommends \ autoconf \ @@ -57,78 +53,87 @@ RUN apt-get update -qq && \ ca-certificates \ curl \ dirmngr \ + gcc-10 \ gettext \ git \ gnupg \ libpq-dev \ libtool \ libffi-dev \ - python3 \ + pkg-config \ + libssl-dev \ + protobuf-compiler \ + python3.9 \ python3-dev \ python3-mako \ python3-pip \ python3-venv \ python3-setuptools \ - wget && \ - # arm64v8 compilers - apt-get install -qq -y --no-install-recommends \ - libc6-arm64-cross \ - gcc-aarch64-linux-gnu \ - g++-aarch64-linux-gnu - -ENV target_host=aarch64-linux-gnu + libev-dev \ + libevent-dev \ + qemu-user-static \ + wget -ENV AR=${target_host}-ar \ -AS=${target_host}-as \ -CC=${target_host}-gcc \ -CXX=${target_host}-g++ \ -LD=${target_host}-ld \ -STRIP=${target_host}-strip \ -QEMU_LD_PREFIX=/usr/${target_host} \ -HOST=${target_host} +ENV PKG_CONFIG_PATH /usr/lib/arm-linux-gnueabihf/pkgconfig RUN wget -q https://zlib.net/fossils/zlib-1.2.13.tar.gz \ -&& tar xvf zlib-1.2.13.tar.gz \ -&& cd zlib-1.2.13 \ -&& ./configure --prefix=$QEMU_LD_PREFIX \ -&& make \ -&& make install && cd .. && rm zlib-1.2.13.tar.gz && rm -rf zlib-1.2.13 + && tar xvf zlib-1.2.13.tar.gz \ + && cd zlib-1.2.13 \ + && ./configure \ + && make \ + && make install && cd .. && \ + rm zlib-1.2.13.tar.gz && \ + rm -rf zlib-1.2.13 RUN apt-get install -y --no-install-recommends unzip tclsh \ -&& wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ -&& unzip sqlite-src-3290000.zip \ -&& cd sqlite-src-3290000 \ -&& ./configure --enable-static --disable-readline --disable-threadsafe --disable-load-extension --host=${target_host} --prefix=$QEMU_LD_PREFIX \ -&& make \ -&& make install && cd .. && rm sqlite-src-3290000.zip && rm -rf sqlite-src-3290000 - + && wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ + && unzip sqlite-src-3290000.zip \ + && cd sqlite-src-3290000 \ + && ./configure --enable-static --disable-readline --disable-threadsafe --disable-load-extension \ + && make \ + && make install && cd .. && rm sqlite-src-3290000.zip && rm -rf sqlite-src-3290000 + +USER root RUN wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz \ -&& tar xvf gmp-6.1.2.tar.xz \ -&& cd gmp-6.1.2 \ -&& ./configure --disable-assembly --prefix=$QEMU_LD_PREFIX --host=${target_host} \ -&& make \ -&& make install && cd .. && rm gmp-6.1.2.tar.xz && rm -rf gmp-6.1.2 -COPY --from=downloader /usr/bin/qemu-aarch64-static /usr/bin/qemu-aarch64-static + && tar xvf gmp-6.1.2.tar.xz \ + && cd gmp-6.1.2 \ + && ./configure --disable-assembly \ + && make \ + && make install && cd .. && rm gmp-6.1.2.tar.xz && rm -rf gmp-6.1.2 + +ENV RUST_PROFILE=release +ENV PATH=$PATH:/root/.cargo/bin/ +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +RUN rustup toolchain install stable --component rustfmt --allow-downgrade + WORKDIR /opt/lightningd COPY . /tmp/lightning + RUN git clone --recursive /tmp/lightning . && \ git checkout $(git --work-tree=/tmp/lightning --git-dir=/tmp/lightning/.git rev-parse HEAD) -ARG DEVELOPER=0 +ARG DEVELOPER=1 ENV PYTHON_VERSION=3 +RUN curl -sSL https://install.python-poetry.org | python3 - + +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1 +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 --slave /usr/bin/g++ g++ /usr/bin/g++-10 -RUN curl -sSL https://install.python-poetry.org | python3 - \ -&& pip3 install -U pip \ -&& pip3 install -U wheel \ -&& /root/.local/bin/poetry install +RUN pip3 install --upgrade pip setuptools wheel +RUN pip3 wheel cryptography +RUN pip3 install grpcio-tools + +RUN /root/.local/bin/poetry install RUN ./configure --prefix=/tmp/lightning_install --enable-static && \ -make DEVELOPER=${DEVELOPER} && \ -/root/.local/bin/poetry run make install + make DEVELOPER=${DEVELOPER} && \ + /root/.local/bin/poetry run make install + +# RUN pip3 install -r plugins/clnrest/requirements.txt +RUN pip3 install flask gunicorn json5 flask_restx flask-socketio gevent gevent-websocket +RUN pip3 install ./contrib/pyln-client -FROM arm64v8/debian:bullseye-slim as final -COPY --from=downloader /usr/bin/qemu-aarch64-static /usr/bin/qemu-aarch64-static -COPY --from=downloader /opt/tini /usr/bin/tini +FROM debian:bullseye-slim as final RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -136,6 +141,7 @@ RUN apt-get update && \ inotify-tools \ python3 \ python3-pip \ + qemu-user-static \ libpq5 && \ rm -rf /var/lib/apt/lists/* @@ -147,9 +153,12 @@ ENV LIGHTNINGD_NETWORK=bitcoin RUN mkdir $LIGHTNINGD_DATA && \ touch $LIGHTNINGD_DATA/config VOLUME [ "/root/.lightning" ] + COPY --from=builder /tmp/lightning_install/ /usr/local/ +COPY --from=builder /usr/local/lib/python3/dist-packages/ /usr/local/lib/python3/dist-packages/ COPY --from=downloader /opt/bitcoin/bin /usr/bin COPY --from=downloader /opt/litecoin/bin /usr/bin +COPY --from=downloader "/tini" /usr/bin/tini COPY tools/docker-entrypoint.sh entrypoint.sh EXPOSE 9735 9835 diff --git a/contrib/docker/Dockerfile.builder b/contrib/docker/Dockerfile.builder index 26e3be204baa..2c717a907fcb 100644 --- a/contrib/docker/Dockerfile.builder +++ b/contrib/docker/Dockerfile.builder @@ -22,7 +22,6 @@ RUN apt-get -qq update && \ libtool \ libprotobuf-c-dev \ libsqlite3-dev \ - libgmp-dev \ git \ python3 \ valgrind \ diff --git a/contrib/docker/Dockerfile.builder.fedora b/contrib/docker/Dockerfile.builder.fedora index 4a43c033e2e8..d35c5bcbe5e9 100644 --- a/contrib/docker/Dockerfile.builder.fedora +++ b/contrib/docker/Dockerfile.builder.fedora @@ -1,6 +1,6 @@ -FROM fedora:29 +FROM fedora:35 -ENV BITCOIN_VERSION 0.18.1 +ENV BITCOIN_VERSION 22.0 WORKDIR /tmp RUN dnf update -y && \ @@ -9,7 +9,6 @@ RUN dnf update -y && \ 'Development Tools' && \ dnf install -y \ clang \ - gmp-devel \ libsq3-devel \ python3-devel \ python3-mako \ @@ -32,4 +31,6 @@ RUN wget https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-$BITCOIN_ rm -rf bitcoin.tar.gz bitcoin-$BITCOIN_VERSION RUN python3 -m pip install --upgrade pip && \ - python3 -m pip install python-bitcoinlib==0.10.2 pytest==3.0.5 setuptools==36.6.0 pytest-test-groups==1.0.3 flake8==3.5.0 pytest-rerunfailures==3.1 ephemeral-port-reserve==1.1.0 + python3 -m pip install python-bitcoinlib pytest setuptools pytest-test-groups flake8 pytest-rerunfailures ephemeral-port-reserve + +RUN python3 -m pip install flask gunicorn json5 flask_restx flask-socketio gevent gevent-websocket diff --git a/contrib/docker/Dockerfile.tester b/contrib/docker/Dockerfile.tester index 3b1e47b3e5c5..6da1aad543f7 100644 --- a/contrib/docker/Dockerfile.tester +++ b/contrib/docker/Dockerfile.tester @@ -27,7 +27,6 @@ RUN apt-get -qq update && \ git \ libc6-dev-arm64-cross \ libc6-dev-armhf-cross \ - libgmp-dev \ libpq-dev \ libprotobuf-c-dev \ libsqlite3-dev \ diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index 806e28e439e9..c9cb48228702 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -7,7 +7,6 @@ export CC=${COMPILER:-gcc} export COMPAT=${COMPAT:-1} export TEST_CHECK_DBSTMTS=${TEST_CHECK_DBSTMTS:-0} export DEVELOPER=${DEVELOPER:-1} -export EXPERIMENTAL_FEATURES=${EXPERIMENTAL_FEATURES:-0} export PATH=$CWD/dependencies/bin:"$HOME"/.local/bin:"$PATH" export PYTEST_OPTS="--maxfail=5 --suppress-no-test-exit-code ${PYTEST_OPTS}" export PYTEST_PAR=${PYTEST_PAR:-10} @@ -25,6 +24,7 @@ pip3 install --upgrade pip pip3 install --user poetry poetry export --dev --without-hashes -o requirements.txt pip3 install -r requirements.txt +pip3 install -r plugins/clnrest/requirements.txt git clone https://github.com/lightning/bolts.git ../bolts git submodule update --init --recursive @@ -78,16 +78,6 @@ then rm sqlite-src-3260000.zip rm -rf sqlite-src-3260000 - wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz - tar xf gmp-6.1.2.tar.xz - cd gmp-6.1.2 || exit 1 - ./configure --disable-assembly --prefix="$QEMU_LD_PREFIX" --host="$TARGET_HOST" - make - sudo make install - cd .. - rm gmp-6.1.2.tar.xz - rm -rf gmp-6.1.2 - ./configure CC="$TARGET_HOST-gcc" --enable-static make -j32 CC="$TARGET_HOST-gcc" > /dev/null diff --git a/contrib/docker/scripts/setup.sh b/contrib/docker/scripts/setup.sh index 18cf124f3f22..6f9cbdb76022 100755 --- a/contrib/docker/scripts/setup.sh +++ b/contrib/docker/scripts/setup.sh @@ -24,7 +24,6 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ git \ libc6-dev-arm64-cross \ libc6-dev-armhf-cross \ - libgmp-dev \ libpq-dev \ libprotobuf-c-dev \ libsqlite3-dev \ diff --git a/contrib/init/lightningd.service b/contrib/init/lightningd.service index 6973d86d0124..fa3e6dfa2fa6 100644 --- a/contrib/init/lightningd.service +++ b/contrib/init/lightningd.service @@ -15,7 +15,7 @@ Wants=network-online.target After=network-online.target [Service] -ExecStart=/usr/bin/lightningd --daemon --conf /etc/lightningd/lightningd.conf --pid-file=/run/lightningd/lightningd.pid +ExecStart=/usr/bin/lightningd --conf /etc/lightningd/lightningd.conf --pid-file=/run/lightningd/lightningd.pid # Creates /run/lightningd owned by bitcoin RuntimeDirectory=lightningd @@ -24,7 +24,7 @@ ConfigurationDirectory=lightningd User=bitcoin Group=bitcoin -Type=forking +Type=simple PIDFile=/run/lightningd/lightningd.pid Restart=on-failure diff --git a/contrib/msggen/dist/msggen-0.1.0.tar.gz b/contrib/msggen/dist/msggen-0.1.0.tar.gz new file mode 100644 index 000000000000..66e666ce05dc Binary files /dev/null and b/contrib/msggen/dist/msggen-0.1.0.tar.gz differ diff --git a/contrib/msggen/msggen/gen/grpc.py b/contrib/msggen/msggen/gen/grpc.py index 920dfe3bad47..3b69189090c9 100644 --- a/contrib/msggen/msggen/gen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -22,6 +22,10 @@ 'u8': 'uint32', # Yep, this is the smallest integer type in grpc... 'u32': 'uint32', 'u64': 'uint64', + 's8': 'int32', + 's16': 'int32', + 's32': 'int32', + 's64': 'int64', 'u16': 'uint32', # Yeah, I know... 'f32': 'float', 'integer': 'sint64', @@ -247,9 +251,17 @@ def generate_composite(self, prefix, field: CompositeField): self.generate_composite(prefix, f) pbname = self.to_camel_case(field.typename) + + # If any of the field accesses would result in a deprecated + # warning we mark the construction here to allow deprecated + # fields being access. + + has_deprecated = any([f.deprecated for f in field.fields]) + deprecated = ",deprecated" if has_deprecated else "" + # And now we can convert the current field: self.write(f"""\ - #[allow(unused_variables,deprecated)] + #[allow(unused_variables{deprecated})] impl From<{prefix}::{field.typename}> for pb::{pbname} {{ fn from(c: {prefix}::{field.typename}) -> Self {{ Self {{ @@ -406,10 +418,13 @@ def generate_composite(self, prefix, field: CompositeField) -> None: elif isinstance(f, CompositeField): self.generate_composite(prefix, f) + has_deprecated = any([f.deprecated for f in field.fields]) + deprecated = ",deprecated" if has_deprecated else "" + pbname = self.to_camel_case(field.typename) # And now we can convert the current field: self.write(f"""\ - #[allow(unused_variables,deprecated)] + #[allow(unused_variables{deprecated})] impl From for {prefix}::{field.typename} {{ fn from(c: pb::{pbname}) -> Self {{ Self {{ diff --git a/contrib/msggen/msggen/gen/grpc2py.py b/contrib/msggen/msggen/gen/grpc2py.py index 503eff1629b9..1a0c159ed143 100644 --- a/contrib/msggen/msggen/gen/grpc2py.py +++ b/contrib/msggen/msggen/gen/grpc2py.py @@ -40,6 +40,11 @@ def __init__(self, dest: TextIO): 'u16': "m.{name}", 'u32': "m.{name}", 'u64': "m.{name}", + 's8': "m.{name}", + 's16': "m.{name}", + 's32': "m.{name}", + 's64': "m.{name}", + 'integer': "m.{name}", 'boolean': "m.{name}", 'short_channel_id': "m.{name}", 'msat': "amount2msat(m.{name})", diff --git a/doc/user/index.md b/contrib/msggen/msggen/gen/json2grpc.py similarity index 100% rename from doc/user/index.md rename to contrib/msggen/msggen/gen/json2grpc.py diff --git a/contrib/msggen/msggen/gen/rust.py b/contrib/msggen/msggen/gen/rust.py index 88a113b920f8..44072d808556 100644 --- a/contrib/msggen/msggen/gen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -35,6 +35,7 @@ 'outputdesc': 'OutputDesc', 'hash': 'Sha256', 'secret': 'Secret', + 'integer': 'i64', } header = f"""#![allow(non_camel_case_types)] @@ -85,7 +86,7 @@ def gen_enum(e): if e.deprecated: decl += "#[deprecated]\n" - decl += f"#[derive(Copy, Clone, Debug, Deserialize, Serialize)]\npub enum {e.typename} {{\n" + decl += f"#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]\npub enum {e.typename} {{\n" for v in e.variants: if v is None: continue @@ -111,6 +112,24 @@ def gen_enum(e): }} }} }} + + """) + + # Implement ToString for enums so we can print them nicely as they + # appear in the schemas. + decl += dedent(f"""\ + impl ToString for {e.typename} {{ + fn to_string(&self) -> String {{ + match self {{ + """) + for v in e.variants: + norm = v.normalized() + decl += f" {e.typename}::{norm} => \"{norm}\",\n" + decl += dedent(f"""\ + }}.to_string() + }} + }} + """) typename = e.typename @@ -124,7 +143,7 @@ def gen_enum(e): defi += rename_if_necessary(str(e.name), e.name.normalized()) defi += f" pub {e.name.normalized()}: {typename},\n" else: - defi = f' #[serde(skip_serializing_if = "Option::is_none")]\n' + defi = f" #[serde(skip_serializing_if = \"Option::is_none\")]\n" defi += f" pub {e.name.normalized()}: Option<{typename}>,\n" return defi, decl @@ -293,8 +312,6 @@ def generate_enums(self, service: Service): """ self.write(f"""\ use serde::{{Deserialize, Serialize}}; - pub use requests::*; - pub use responses::*; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "method", content = "params")] diff --git a/contrib/msggen/msggen/patch.py b/contrib/msggen/msggen/patch.py index 22d1fa8eb9b2..ea1e0a07a58d 100644 --- a/contrib/msggen/msggen/patch.py +++ b/contrib/msggen/msggen/patch.py @@ -107,11 +107,12 @@ class OptionalPatch(Patch): 'v22.11', 'v23.02', 'v23.05', + 'v23.08', ] # Oldest supported versions. Bump this if you no longer want to # support older versions, and you want to make required fields # more stringent. - supported = 'v0.12.0' + supported = 'v0.10.1' def visit(self, f: model.Field) -> None: if f.added not in self.versions: @@ -176,6 +177,7 @@ class OverridePatch(Patch): 'ListPeers.peers[].channels[].state_changes[].old_state': "ChannelState", 'ListPeers.peers[].channels[].htlcs[].state': "HtlcState", 'ListPeerChannels.channels[].htlcs[].state': "HtlcState", + 'ListHtlcs.htlcs[].state': "HtlcState", } def visit(self, f: model.Field) -> None: diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index d5da0c9738e9..14dcd0fea0cc 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -83,6 +83,7 @@ def load_jsonrpc_service(schema_dir: str): "ListForwards", # "listoffers", "ListPays", + "ListHtlcs", # "multifundchannel", # "multiwithdraw", # "offer", @@ -108,6 +109,8 @@ def load_jsonrpc_service(schema_dir: str): "Stop", # "notifications", # No point in mapping this # "help", + "PreApproveKeysend", + "PreApproveInvoice", ] methods = [load_jsonrpc_method(name, schema_dir=schema_dir) for name in method_names] service = Service(name="Node", methods=methods) diff --git a/contrib/plugins/cowsay.sh b/contrib/plugins/cowsay.sh index 8d8a40dd48df..1762aee0a152 100755 --- a/contrib/plugins/cowsay.sh +++ b/contrib/plugins/cowsay.sh @@ -31,7 +31,7 @@ echo '{"jsonrpc":"2.0","id":'"$id"',"result":{}}' # eg. { "jsonrpc" : "2.0", "method" : "cowsay", "id" : 6, "params" :[ "hello"] } while read -r JSON; do read -r _ - id=$(echo "$JSON" | sed 's/.*"id" *: *\([0-9]*\),.*/\1/') + id=$(echo "$JSON" | sed 's/.*"id" *: *\([^,]*\),.*/\1/') params=$(echo "$JSON" | sed 's/.*"params" *: *//' | tr -d '[{}]"') echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"format-hint":"simple","cowsay":"' # FIXME: lightning-cli does not unescape \\, so we replace with an L. diff --git a/contrib/plugins/helloworld.py b/contrib/plugins/helloworld.py index 73c4cc7c0aef..ccfdac161ae4 100755 --- a/contrib/plugins/helloworld.py +++ b/contrib/plugins/helloworld.py @@ -31,13 +31,13 @@ def init(options, configuration, plugin, **kwargs): @plugin.subscribe("connect") -def on_connect(plugin, id, address, **kwargs): - plugin.log("Received connect event for peer {}".format(id)) +def on_connect(plugin, connect, **kwargs): + plugin.log("Received connect event for peer {}".format(connect)) @plugin.subscribe("disconnect") -def on_disconnect(plugin, id, **kwargs): - plugin.log("Received disconnect event for peer {}".format(id)) +def on_disconnect(plugin, disconnect, **kwargs): + plugin.log("Received disconnect event for peer {}".format(disconnect)) @plugin.subscribe("invoice_payment") diff --git a/contrib/pylightning/README.md b/contrib/pylightning/README.md index 4487607ac2cd..e4303fa87254 100644 --- a/contrib/pylightning/README.md +++ b/contrib/pylightning/README.md @@ -94,8 +94,8 @@ def init(options, configuration, plugin): @plugin.subscribe("connect") -def on_connect(plugin, id, address): - plugin.log("Received connect event for peer {}".format(id)) +def on_connect(plugin, connect): + plugin.log("Received connect event for peer {}".format(connect)) plugin.add_option('greeting', 'Hello', 'The greeting I should use.') diff --git a/contrib/pyln-client/README.md b/contrib/pyln-client/README.md index 2e781bddbbcd..959127044261 100644 --- a/contrib/pyln-client/README.md +++ b/contrib/pyln-client/README.md @@ -96,8 +96,8 @@ def init(options, configuration, plugin): @plugin.subscribe("connect") -def on_connect(plugin, id, address, **kwargs): - plugin.log("Received connect event for peer {}".format(id)) +def on_connect(plugin, connect, **kwargs): + plugin.log("Received connect event for peer {}".format(connect)) plugin.add_option('greeting', 'Hello', 'The greeting I should use.') diff --git a/contrib/pyln-client/pyln/client/__init__.py b/contrib/pyln-client/pyln/client/__init__.py index ed736ba84bc2..ee0023bcfc20 100644 --- a/contrib/pyln-client/pyln/client/__init__.py +++ b/contrib/pyln-client/pyln/client/__init__.py @@ -3,7 +3,7 @@ from .gossmap import Gossmap, GossmapNode, GossmapChannel, GossmapHalfchannel, GossmapNodeId, LnFeatureBits from .gossmapstats import GossmapStats -__version__ = "23.05" +__version__ = "23.08" __all__ = [ "LightningRpc", diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 15272b1d5a76..b535f88e9bab 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -503,12 +503,14 @@ def replace_amounts(obj): """ if isinstance(obj, dict): for k, v in obj.items(): - if k.endswith('msat'): + # Objects ending in msat are not treated specially! + if k.endswith('msat') and not isinstance(v, dict): if isinstance(v, list): obj[k] = [Millisatoshi(e) for e in v] # FIXME: Deprecated "listconfigs" gives two 'null' fields: # "lease-fee-base-msat": null, # "channel-fee-max-base-msat": null, + # FIXME: Removed for v23.08, delete this code in 24.08? elif v is None: obj[k] = None else: @@ -1021,7 +1023,7 @@ def listtransactions(self): """ return self.call("listtransactions") - def listinvoices(self, label=None, payment_hash=None, invstring=None, offer_id=None): + def listinvoices(self, label=None, payment_hash=None, invstring=None, offer_id=None, index=None, start=None, limit=None): """Query invoices Show invoice matching {label}, {payment_hash}, {invstring} or {offer_id} @@ -1033,6 +1035,9 @@ def listinvoices(self, label=None, payment_hash=None, invstring=None, offer_id=N "payment_hash": payment_hash, "invstring": invstring, "offer_id": offer_id, + "index": index, + "start": start, + "limit": limit, } return self.call("listinvoices", payload) @@ -1207,6 +1212,32 @@ def openchannel_abort(self, channel_id): } return self.call("openchannel_abort", payload) + def splice_init(self, chan_id, amount, initialpsbt=None, feerate_per_kw=None): + """ Initiate a splice """ + payload = { + "channel_id": chan_id, + "relative_amount": amount, + "initialpsbt": initialpsbt, + "feerate_per_kw": feerate_per_kw, + } + return self.call("splice_init", payload) + + def splice_update(self, chan_id, psbt): + """ Update a splice """ + payload = { + "channel_id": chan_id, + "psbt": psbt + } + return self.call("splice_update", payload) + + def splice_signed(self, chan_id, psbt): + """ Initiate a splice """ + payload = { + "channel_id": chan_id, + "psbt": psbt + } + return self.call("splice_signed", payload) + def paystatus(self, bolt11=None): """Detail status of attempts to pay {bolt11} or any.""" payload = { @@ -1319,7 +1350,7 @@ def sendonion( } return self.call("sendonion", payload) - def setchannel(self, id, feebase=None, feeppm=None, htlcmin=None, htlcmax=None, enforcedelay=None): + def setchannel(self, id, feebase=None, feeppm=None, htlcmin=None, htlcmax=None, enforcedelay=None, ignorefeelimits=None): """Set configuration a channel/peer {id} (or 'all'). {feebase} is a value in millisatoshi that is added as base fee @@ -1337,6 +1368,8 @@ def setchannel(self, id, feebase=None, feeppm=None, htlcmin=None, htlcmax=None, {enforcedelay} is the number of seconds before enforcing this change. + {ignorefeelimits} is a flag to indicate peer can set any feerate (dangerous!) + """ payload = { "id": id, @@ -1345,6 +1378,7 @@ def setchannel(self, id, feebase=None, feeppm=None, htlcmin=None, htlcmax=None, "htlcmin": htlcmin, "htlcmax": htlcmax, "enforcedelay": enforcedelay, + "ignorefeelimits": ignorefeelimits, } return self.call("setchannel", payload) diff --git a/contrib/pyln-client/pyln/client/plugin.py b/contrib/pyln-client/pyln/client/plugin.py index e53bcbeda6df..d14ae5d3c8bc 100644 --- a/contrib/pyln-client/pyln/client/plugin.py +++ b/contrib/pyln-client/pyln/client/plugin.py @@ -659,10 +659,13 @@ def _dispatch_request(self, request: Request) -> None: self.log(traceback.format_exc()) def _dispatch_notification(self, request: Request) -> None: - if request.method not in self.subscriptions: - raise ValueError("No subscription for {name} found.".format( - name=request.method)) - func = self.subscriptions[request.method] + if request.method in self.subscriptions: + func = self.subscriptions[request.method] + # Wildcard 'all' subscriptions using asterisk + elif '*' in self.subscriptions: + func = self.subscriptions['*'] + else: + raise ValueError(f"No subscription for {request.method} found.") try: self._exec_func(func, request) @@ -912,7 +915,7 @@ def _getmanifest(self, **kwargs) -> JSONType: m["long_description"] = method.long_desc manifest = { - 'options': list(self.options.values()), + 'options': list({k: v for k, v in d.items() if v is not None} for d in self.options.values()), 'rpcmethods': methods, 'subscriptions': list(self.subscriptions.keys()), 'hooks': hooks, diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index aa6f701fc99c..c729fbae2746 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "23.05" +version = "23.08" description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -11,12 +11,12 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" -pyln-proto = ">=0.12" +python = "^3.8" +pyln-proto = ">=23" pyln-bolt7 = ">=1.0" [tool.poetry.dev-dependencies] -pytest = "^7.0.1" +pytest = "^7" pyln-bolt7 = { path = "../pyln-spec/bolt7", develop = true } pyln-proto = { path = "../pyln-proto", develop = true} diff --git a/contrib/pyln-grpc-proto/Makefile b/contrib/pyln-grpc-proto/Makefile new file mode 100644 index 000000000000..65bb6431a849 --- /dev/null +++ b/contrib/pyln-grpc-proto/Makefile @@ -0,0 +1,30 @@ +PROTOS = \ + pyln/grpc/node_pb2.py \ + pyln/grpc/node_pb2_grpc.py \ + pyln/grpc/primitives_pb2.py \ + +PROTOSRC = \ + ../../cln-grpc/proto/node.proto \ + ../../cln-grpc/proto/primitives.proto + +${PROTOS} &: ${PROTOSRC} + python \ + -m grpc_tools.protoc \ + -I ../../cln-grpc/proto \ + ../../cln-grpc/proto/node.proto \ + --python_out=pyln/grpc \ + --grpc_python_out=pyln/grpc \ + --experimental_allow_proto3_optional + + python \ + -m grpc_tools.protoc \ + -I ../../cln-grpc/proto \ + ../../cln-grpc/proto/primitives.proto \ + --python_out=pyln/grpc \ + --experimental_allow_proto3_optional + + + # The package logic in grpc is very inflexible, let's rewrite + # the references between the generated sources + sed -i 's/import primitives_pb2 as primitives__pb2/from pyln.grpc import primitives_pb2 as primitives__pb2/g' pyln/grpc/node_pb2.py + sed -i 's/import node_pb2 as node__pb2/from pyln.grpc import node_pb2 as node__pb2/g' pyln/grpc/node_pb2_grpc.py diff --git a/contrib/pyln-grpc-proto/README.md b/contrib/pyln-grpc-proto/README.md new file mode 100644 index 000000000000..8460a81564b5 --- /dev/null +++ b/contrib/pyln-grpc-proto/README.md @@ -0,0 +1,6 @@ +# pyln-grpc-proto + +The python protobuf library is rather restrictive when it comes to +importing them multiple times. So this package just contains the +compiled protobuf files and serves as the canonical place to load them +from. diff --git a/contrib/pyln-grpc-proto/poetry.lock b/contrib/pyln-grpc-proto/poetry.lock new file mode 100644 index 000000000000..478ea60aeb1b --- /dev/null +++ b/contrib/pyln-grpc-proto/poetry.lock @@ -0,0 +1,706 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "bleach" +version = "6.0.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.7" +files = [ + {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, + {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.2)"] + +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "cryptography" +version = "41.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699"}, + {file = "cryptography-41.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b"}, + {file = "cryptography-41.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3"}, + {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db"}, + {file = "cryptography-41.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31"}, + {file = "cryptography-41.0.1-cp37-abi3-win32.whl", hash = "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5"}, + {file = "cryptography-41.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039"}, + {file = "cryptography-41.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a"}, + {file = "cryptography-41.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5"}, + {file = "cryptography-41.0.1.tar.gz", hash = "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +nox = ["nox"] +pep8test = ["black", "check-sdist", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, +] + +[[package]] +name = "grpcio" +version = "1.54.2" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-1.54.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40e1cbf69d6741b40f750f3cccc64326f927ac6145a9914d33879e586002350c"}, + {file = "grpcio-1.54.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2288d76e4d4aa7ef3fe7a73c1c470b66ea68e7969930e746a8cd8eca6ef2a2ea"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0e3155fc5335ec7b3b70f15230234e529ca3607b20a562b6c75fb1b1218874c"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bf88004fe086c786dc56ef8dd6cb49c026833fdd6f42cb853008bce3f907148"}, + {file = "grpcio-1.54.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be88c081e33f20630ac3343d8ad9f1125f32987968e9c8c75c051c9800896e8"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33d40954199bddbb6a78f8f6f2b2082660f381cd2583ec860a6c2fa7c8400c08"}, + {file = "grpcio-1.54.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b52d00d1793d290c81ad6a27058f5224a7d5f527867e5b580742e1bd211afeee"}, + {file = "grpcio-1.54.2-cp310-cp310-win32.whl", hash = "sha256:881d058c5ccbea7cc2c92085a11947b572498a27ef37d3eef4887f499054dca8"}, + {file = "grpcio-1.54.2-cp310-cp310-win_amd64.whl", hash = "sha256:0212e2f7fdf7592e4b9d365087da30cb4d71e16a6f213120c89b4f8fb35a3ab3"}, + {file = "grpcio-1.54.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:1e623e0cf99a0ac114f091b3083a1848dbc64b0b99e181473b5a4a68d4f6f821"}, + {file = "grpcio-1.54.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:66233ccd2a9371158d96e05d082043d47dadb18cbb294dc5accfdafc2e6b02a7"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:4cb283f630624ebb16c834e5ac3d7880831b07cbe76cb08ab7a271eeaeb8943e"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a1e601ee31ef30a9e2c601d0867e236ac54c922d32ed9f727b70dd5d82600d5"}, + {file = "grpcio-1.54.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8da84bbc61a4e92af54dc96344f328e5822d574f767e9b08e1602bb5ddc254a"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5008964885e8d23313c8e5ea0d44433be9bfd7e24482574e8cc43c02c02fc796"}, + {file = "grpcio-1.54.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a2f5a1f1080ccdc7cbaf1171b2cf384d852496fe81ddedeb882d42b85727f610"}, + {file = "grpcio-1.54.2-cp311-cp311-win32.whl", hash = "sha256:b74ae837368cfffeb3f6b498688a123e6b960951be4dec0e869de77e7fa0439e"}, + {file = "grpcio-1.54.2-cp311-cp311-win_amd64.whl", hash = "sha256:8cdbcbd687e576d48f7886157c95052825ca9948c0ed2afdc0134305067be88b"}, + {file = "grpcio-1.54.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:782f4f8662a2157c4190d0f99eaaebc602899e84fb1e562a944e5025929e351c"}, + {file = "grpcio-1.54.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:714242ad0afa63a2e6dabd522ae22e1d76e07060b5af2ddda5474ba4f14c2c94"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:f900ed4ad7a0f1f05d35f955e0943944d5a75f607a836958c6b8ab2a81730ef2"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96a41817d2c763b1d0b32675abeb9179aa2371c72aefdf74b2d2b99a1b92417b"}, + {file = "grpcio-1.54.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70fcac7b94f4c904152809a050164650ac81c08e62c27aa9f156ac518029ebbe"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fd6c6c29717724acf9fc1847c4515d57e4dc12762452457b9cb37461f30a81bb"}, + {file = "grpcio-1.54.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c2392f5b5d84b71d853918687d806c1aa4308109e5ca158a16e16a6be71041eb"}, + {file = "grpcio-1.54.2-cp37-cp37m-win_amd64.whl", hash = "sha256:51630c92591d6d3fe488a7c706bd30a61594d144bac7dee20c8e1ce78294f474"}, + {file = "grpcio-1.54.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:b04202453941a63b36876a7172b45366dc0cde10d5fd7855c0f4a4e673c0357a"}, + {file = "grpcio-1.54.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:89dde0ac72a858a44a2feb8e43dc68c0c66f7857a23f806e81e1b7cc7044c9cf"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:09d4bfd84686cd36fd11fd45a0732c7628308d094b14d28ea74a81db0bce2ed3"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc2b4edb938c8faa4b3c3ea90ca0dd89b7565a049e8e4e11b77e60e4ed2cc05"}, + {file = "grpcio-1.54.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61f7203e2767800edee7a1e1040aaaf124a35ce0c7fe0883965c6b762defe598"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e416c8baf925b5a1aff31f7f5aecc0060b25d50cce3a5a7255dc5cf2f1d4e5eb"}, + {file = "grpcio-1.54.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dc80c9c6b608bf98066a038e0172013a49cfa9a08d53335aefefda2c64fc68f4"}, + {file = "grpcio-1.54.2-cp38-cp38-win32.whl", hash = "sha256:8d6192c37a30a115f4663592861f50e130caed33efc4eec24d92ec881c92d771"}, + {file = "grpcio-1.54.2-cp38-cp38-win_amd64.whl", hash = "sha256:46a057329938b08e5f0e12ea3d7aed3ecb20a0c34c4a324ef34e00cecdb88a12"}, + {file = "grpcio-1.54.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:2296356b5c9605b73ed6a52660b538787094dae13786ba53080595d52df13a98"}, + {file = "grpcio-1.54.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:c72956972e4b508dd39fdc7646637a791a9665b478e768ffa5f4fe42123d5de1"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:9bdbb7624d65dc0ed2ed8e954e79ab1724526f09b1efa88dcd9a1815bf28be5f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c44e1a765b31e175c391f22e8fc73b2a2ece0e5e6ff042743d8109b5d2eff9f"}, + {file = "grpcio-1.54.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cc928cfe6c360c1df636cf7991ab96f059666ac7b40b75a769410cc6217df9c"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a08920fa1a97d4b8ee5db2f31195de4a9def1a91bc003544eb3c9e6b8977960a"}, + {file = "grpcio-1.54.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4864f99aac207e3e45c5e26c6cbb0ad82917869abc2f156283be86c05286485c"}, + {file = "grpcio-1.54.2-cp39-cp39-win32.whl", hash = "sha256:b38b3de8cff5bc70f8f9c615f51b48eff7313fc9aca354f09f81b73036e7ddfa"}, + {file = "grpcio-1.54.2-cp39-cp39-win_amd64.whl", hash = "sha256:be48496b0e00460717225e7680de57c38be1d8629dc09dadcd1b3389d70d942b"}, + {file = "grpcio-1.54.2.tar.gz", hash = "sha256:50a9f075eeda5097aa9a182bb3877fe1272875e45370368ac0ee16ab9e22d019"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.54.2)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "6.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, + {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "jaraco-classes" +version = "3.2.3" +description = "Utility functions for Python class constructs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, + {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, +] + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] + +[package.extras] +test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] +trio = ["async_generator", "trio"] + +[[package]] +name = "keyring" +version = "24.0.0" +description = "Store and access your passwords safely." +optional = false +python-versions = ">=3.7" +files = [ + {file = "keyring-24.0.0-py3-none-any.whl", hash = "sha256:770f609eed2a16c65a6349f3ba1545d00c73f9fed4254c13766c674fe6d0d22b"}, + {file = "keyring-24.0.0.tar.gz", hash = "sha256:4e87665a19c514c7edada8b15015cf89bd99b8d7edabc5c43cca77166fa8dfad"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +"jaraco.classes" = "*" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +completion = ["shtab"] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" +typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "more-itertools" +version = "9.1.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.7" +files = [ + {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, + {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, +] + +[[package]] +name = "pkginfo" +version = "1.9.6" +description = "Query metadata from sdists / bdists / installed packages." +optional = false +python-versions = ">=3.6" +files = [ + {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, + {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, +] + +[package.extras] +testing = ["pytest", "pytest-cov"] + +[[package]] +name = "protobuf3" +version = "0.2.1" +description = "Protocol buffers library for Python 3" +optional = false +python-versions = "*" +files = [ + {file = "protobuf3-0.2.1.tar.gz", hash = "sha256:ddd878b3f991beff566ab384d3588cf8e89758e3a16a78f4099dbe70de3c41a2"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.1" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.1.tar.gz", hash = "sha256:934a2def1e5cbc472b2b6bf80680c0f03cd87df65dfd58bfd1846969de095b03"}, + {file = "pywin32_ctypes-0.2.1-py3-none-any.whl", hash = "sha256:b9a53ef754c894a525469933ab2a447c74ec1ea6b9d2ef446f40ec50d3dcec9f"}, +] + +[[package]] +name = "readme-renderer" +version = "37.3" +description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" +optional = false +python-versions = ">=3.7" +files = [ + {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, + {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, +] + +[package.dependencies] +bleach = ">=2.1.0" +docutils = ">=0.13.1" +Pygments = ">=2.5.1" + +[package.extras] +md = ["cmarkgfm (>=0.8.0)"] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rfc3986" +version = "2.0.0" +description = "Validating URI References per RFC 3986" +optional = false +python-versions = ">=3.7" +files = [ + {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, + {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, +] + +[package.extras] +idna2008 = ["idna"] + +[[package]] +name = "rich" +version = "13.4.2" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"}, + {file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "secretstorage" +version = "3.3.3" +description = "Python bindings to FreeDesktop.org Secret Service API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, + {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, +] + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "twine" +version = "4.0.2" +description = "Collection of utilities for publishing packages on PyPI" +optional = false +python-versions = ">=3.7" +files = [ + {file = "twine-4.0.2-py3-none-any.whl", hash = "sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8"}, + {file = "twine-4.0.2.tar.gz", hash = "sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"}, +] + +[package.dependencies] +importlib-metadata = ">=3.6" +keyring = ">=15.1" +pkginfo = ">=1.8.1" +readme-renderer = ">=35.0" +requests = ">=2.20" +requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" +rfc3986 = ">=1.4.0" +rich = ">=12.0.0" +urllib3 = ">=1.26.0" + +[[package]] +name = "typing-extensions" +version = "4.6.3" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, +] + +[[package]] +name = "urllib3" +version = "2.0.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.7" +files = [ + {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, + {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "4d150f7c296ea43b6dee747d856349f4422accfc7a6981a8a6010d8e4fb07ff0" diff --git a/contrib/pyln-grpc-proto/pyln/grpc/__init__.py b/contrib/pyln-grpc-proto/pyln/grpc/__init__.py new file mode 100644 index 000000000000..3b533942f25d --- /dev/null +++ b/contrib/pyln-grpc-proto/pyln/grpc/__init__.py @@ -0,0 +1,3 @@ +from pyln.grpc.primitives_pb2 import * # noqa: F401,F403 +from pyln.grpc.node_pb2 import * # noqa: F401,F403 +from pyln.grpc.node_pb2_grpc import * # noqa: F401,F403 diff --git a/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py new file mode 100644 index 000000000000..94c5aa99f510 --- /dev/null +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py @@ -0,0 +1,438 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: node.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from pyln.grpc import primitives_pb2 as primitives__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xc1\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x61lias\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x01\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x02\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_aliasB\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xc4\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"G\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"\x8a\x02\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"_\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\r\n\tWEBSOCKET\x10\x05\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\xd6\x17\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH*\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xf3\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x00\x88\x01\x01\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\x42\x10\n\x0e_splice_amount\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf1\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\x83\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x11\n\tdirection\x18\x10 \x01(\r\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xaf\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x03\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x04\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\t\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x10\n\x0e_created_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xa1\x04\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1a\n\rcreated_index\x18\x0c \x01(\x04H\x04\x88\x01\x01\x12\x1a\n\rupdated_index\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x06\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x07\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\x95\x03\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1a\n\rcreated_index\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x03\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x05\x88\x01\x01\x42\x10\n\x0e_created_indexB\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xde\x02\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x12>\n\x05index\x18\x05 \x01(\x0e\x32*.cln.ListinvoicesRequest.ListinvoicesIndexH\x04\x88\x01\x01\x12\x12\n\x05start\x18\x06 \x01(\x04H\x05\x88\x01\x01\x12\x12\n\x05limit\x18\x07 \x01(\rH\x06\x88\x01\x01\"-\n\x11ListinvoicesIndex\x12\x0b\n\x07\x43REATED\x10\x00\x12\x0b\n\x07UPDATED\x10\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_idB\x08\n\x06_indexB\x08\n\x06_startB\x08\n\x06_limit\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xfe\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x1a\n\rcreated_index\x18\x10 \x01(\x04H\x06\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x11 \x01(\x04H\x07\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x08\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\t\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\n\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xf4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_partidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"S\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\"l\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\"\xda\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x10\n\x0e_localinvreqidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xe8\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"P\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\xef\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\xe3\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rcreated_index\x18\r \x01(\x04H\x03\x88\x01\x01\x12\x1a\n\rupdated_index\x18\x0e \x01(\x04H\x04\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x05\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x08\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x10\n\x0e_created_indexB\x10\n\x0e_updated_indexB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x97\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\"3\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x08\n\x04P2TR\x10\x03\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"w\n\x0fNewaddrResponse\x12\x11\n\x04p2tr\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_p2trB\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\x82\x03\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehintsB\x0c\n\n_extratlvs\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\xa4\x03\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x12\x17\n\nnonwrapped\x18\t \x01(\x08H\x05\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x06\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_changeB\r\n\x0b_nonwrappedB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\x9b\x03\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x12#\n\x16opening_anchor_channel\x18\n \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_changeB\x19\n\x17_opening_anchor_channel\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"1\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\x9b\x19\n\x18ListpeerchannelsChannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0epeer_connected\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12O\n\x05state\x18\x03 \x01(\x0e\x32;.cln.ListpeerchannelsChannels.ListpeerchannelsChannelsStateH\x02\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x12\x1e\n\x11ignore_fee_limits\x18\x36 \x01(\x08H\x04\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x05\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x07\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x08\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\t\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\n\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x0c\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\r\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\x0e\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0f\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x10\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x11\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x12\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x13\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x19\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1f\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH!\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH#\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH$\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH%\x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH&\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H\'\x88\x01\x01\x12)\n\x0fin_offered_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH(\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18. \x01(\x04H)\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18/ \x01(\x0b\x32\x0b.cln.AmountH*\x88\x01\x01\x12!\n\x14out_payments_offered\x18\x30 \x01(\x04H+\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH,\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H-\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH.\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH/\x88\x01\x01\"\xc1\x02\n\x1dListpeerchannelsChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\n\x12\x1c\n\x18\x43HANNELD_AWAITING_SPLICE\x10\x0b\x42\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\x14\n\x12_ignore_fee_limitsB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_openerB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"]\n\x1fListpeerchannelsChannelsFeerate\x12\x12\n\x05perkw\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05perkb\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x08\n\x06_perkwB\x08\n\x06_perkb\"\x80\x03\n ListpeerchannelsChannelsInflight\x12\x19\n\x0c\x66unding_txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x03 \x01(\tH\x02\x88\x01\x01\x12,\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x1a\n\rsplice_amount\x18\x07 \x01(\x12H\x04\x88\x01\x01\x12*\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x06\x88\x01\x01\x42\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\n\n\x08_feerateB\x15\n\x13_total_funding_msatB\x10\n\x0e_splice_amountB\x13\n\x11_our_funding_msatB\x0f\n\r_scratch_txid\"\xd2\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12*\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x13\n\x11_local_funds_msatB\x14\n\x12_remote_funds_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xe2\x03\n\x1dListpeerchannelsChannelsHtlcs\x12\x61\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirectionH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x05 \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\"\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcStateH\x07\x88\x01\x01\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x0c\n\n_directionB\x05\n\x03_idB\x0e\n\x0c_amount_msatB\t\n\x07_expiryB\x0f\n\r_payment_hashB\x10\n\x0e_local_trimmedB\t\n\x07_statusB\x08\n\x06_state\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xb2\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x66\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32Q.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsClose_cause\"v\n+ListclosedchannelsClosedchannelsClose_cause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msat\"e\n%ListclosedchannelsClosedchannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtraB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadata\"\xc6\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"D\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xaa!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\"l\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hex\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\xd3\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xd3\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12$\n\x17unilateral_anchor_close\x18\x0b \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x06\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x07\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x1a\n\x18_unilateral_anchor_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x9b\x02\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x30\n#unilateral_close_nonanchor_satoshis\x18\x06 \x01(\x04H\x00\x88\x01\x01\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\x42&\n$_unilateral_close_nonanchor_satoshis\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xff\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\t\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\n\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"*\n\x10ListhtlcsRequest\x12\x0f\n\x02id\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListhtlcsResponse\x12\"\n\x05htlcs\x18\x01 \x03(\x0b\x32\x13.cln.ListhtlcsHtlcs\"\x89\x02\n\x0eListhtlcsHtlcs\x12\x18\n\x10short_channel_id\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12>\n\tdirection\x18\x05 \x01(\x0e\x32+.cln.ListhtlcsHtlcs.ListhtlcsHtlcsDirection\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x1d\n\x05state\x18\x07 \x01(\x0e\x32\x0e.cln.HtlcState\"*\n\x17ListhtlcsHtlcsDirection\x12\x07\n\x03OUT\x10\x00\x12\x06\n\x02IN\x10\x01\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xaa\x02\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fignorefeelimits\x18\x07 \x01(\x08H\x05\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelayB\x12\n\x10_ignorefeelimits\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\xca\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12\x1e\n\x11ignore_fee_limits\x18\n \x01(\x08H\x01\x88\x01\x01\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x02\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x03\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x14\n\x12_ignore_fee_limitsB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse\"\xa7\x01\n\x18PreapprovekeysendRequest\x12\x18\n\x0b\x64\x65stination\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_destinationB\x0f\n\r_payment_hashB\x0e\n\x0c_amount_msat\"\x1b\n\x19PreapprovekeysendResponse\":\n\x18PreapproveinvoiceRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_bolt11\"\x1b\n\x19PreapproveinvoiceResponse2\xc8\x1c\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12<\n\tListHtlcs\x12\x15.cln.ListhtlcsRequest\x1a\x16.cln.ListhtlcsResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x12T\n\x11PreApproveKeysend\x12\x1d.cln.PreapprovekeysendRequest\x1a\x1e.cln.PreapprovekeysendResponse\"\x00\x12T\n\x11PreApproveInvoice\x12\x1d.cln.PreapproveinvoiceRequest\x1a\x1e.cln.PreapproveinvoiceResponse\"\x00\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'node_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _GETINFOREQUEST._serialized_start=37 + _GETINFOREQUEST._serialized_end=53 + _GETINFORESPONSE._serialized_start=56 + _GETINFORESPONSE._serialized_end=633 + _GETINFOOUR_FEATURES._serialized_start=635 + _GETINFOOUR_FEATURES._serialized_end=718 + _GETINFOADDRESS._serialized_start=721 + _GETINFOADDRESS._serialized_end=917 + _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_start=834 + _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_end=905 + _GETINFOBINDING._serialized_start=920 + _GETINFOBINDING._serialized_end=1186 + _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_start=1059 + _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_end=1154 + _LISTPEERSREQUEST._serialized_start=1188 + _LISTPEERSREQUEST._serialized_end=1260 + _LISTPEERSRESPONSE._serialized_start=1262 + _LISTPEERSRESPONSE._serialized_end=1317 + _LISTPEERSPEERS._serialized_start=1320 + _LISTPEERSPEERS._serialized_end=1590 + _LISTPEERSPEERSLOG._serialized_start=1593 + _LISTPEERSPEERSLOG._serialized_end=1974 + _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_start=1804 + _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_end=1909 + _LISTPEERSPEERSCHANNELS._serialized_start=1977 + _LISTPEERSPEERSCHANNELS._serialized_end=5007 + _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_start=3877 + _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_end=4166 + _LISTPEERSPEERSCHANNELSFEERATE._serialized_start=5009 + _LISTPEERSPEERSCHANNELSFEERATE._serialized_end=5070 + _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_start=5073 + _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=5316 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=5319 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5602 + _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5604 + _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5695 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5698 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=6067 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5983 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=6038 + _LISTFUNDSREQUEST._serialized_start=6069 + _LISTFUNDSREQUEST._serialized_end=6117 + _LISTFUNDSRESPONSE._serialized_start=6119 + _LISTFUNDSRESPONSE._serialized_end=6220 + _LISTFUNDSOUTPUTS._serialized_start=6223 + _LISTFUNDSOUTPUTS._serialized_end=6610 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=6484 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=6565 + _LISTFUNDSCHANNELS._serialized_start=6613 + _LISTFUNDSCHANNELS._serialized_end=6912 + _SENDPAYREQUEST._serialized_start=6915 + _SENDPAYREQUEST._serialized_end=7264 + _SENDPAYRESPONSE._serialized_start=7267 + _SENDPAYRESPONSE._serialized_end=7860 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7681 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7723 + _SENDPAYROUTE._serialized_start=7862 + _SENDPAYROUTE._serialized_end=7954 + _LISTCHANNELSREQUEST._serialized_start=7957 + _LISTCHANNELSREQUEST._serialized_end=8104 + _LISTCHANNELSRESPONSE._serialized_start=8106 + _LISTCHANNELSRESPONSE._serialized_end=8173 + _LISTCHANNELSCHANNELS._serialized_start=8176 + _LISTCHANNELSCHANNELS._serialized_end=8611 + _ADDGOSSIPREQUEST._serialized_start=8613 + _ADDGOSSIPREQUEST._serialized_end=8648 + _ADDGOSSIPRESPONSE._serialized_start=8650 + _ADDGOSSIPRESPONSE._serialized_end=8669 + _AUTOCLEANINVOICEREQUEST._serialized_start=8671 + _AUTOCLEANINVOICEREQUEST._serialized_end=8782 + _AUTOCLEANINVOICERESPONSE._serialized_start=8785 + _AUTOCLEANINVOICERESPONSE._serialized_end=8914 + _CHECKMESSAGEREQUEST._serialized_start=8916 + _CHECKMESSAGEREQUEST._serialized_end=9001 + _CHECKMESSAGERESPONSE._serialized_start=9003 + _CHECKMESSAGERESPONSE._serialized_end=9059 + _CLOSEREQUEST._serialized_start=9062 + _CLOSEREQUEST._serialized_end=9393 + _CLOSERESPONSE._serialized_start=9396 + _CLOSERESPONSE._serialized_end=9567 + _CLOSERESPONSE_CLOSETYPE._serialized_start=9498 + _CLOSERESPONSE_CLOSETYPE._serialized_end=9551 + _CONNECTREQUEST._serialized_start=9569 + _CONNECTREQUEST._serialized_end=9653 + _CONNECTRESPONSE._serialized_start=9656 + _CONNECTRESPONSE._serialized_end=9836 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9801 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9836 + _CONNECTADDRESS._serialized_start=9839 + _CONNECTADDRESS._serialized_end=10090 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9978 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=10058 + _CREATEINVOICEREQUEST._serialized_start=10092 + _CREATEINVOICEREQUEST._serialized_end=10166 + _CREATEINVOICERESPONSE._serialized_start=10169 + _CREATEINVOICERESPONSE._serialized_end=10856 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10631 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10687 + _DATASTOREREQUEST._serialized_start=10859 + _DATASTOREREQUEST._serialized_end=11167 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=11012 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=11124 + _DATASTORERESPONSE._serialized_start=11170 + _DATASTORERESPONSE._serialized_end=11300 + _CREATEONIONREQUEST._serialized_start=11303 + _CREATEONIONREQUEST._serialized_end=11460 + _CREATEONIONRESPONSE._serialized_start=11462 + _CREATEONIONRESPONSE._serialized_end=11522 + _CREATEONIONHOPS._serialized_start=11524 + _CREATEONIONHOPS._serialized_end=11574 + _DELDATASTOREREQUEST._serialized_start=11576 + _DELDATASTOREREQUEST._serialized_end=11650 + _DELDATASTORERESPONSE._serialized_start=11653 + _DELDATASTORERESPONSE._serialized_end=11786 + _DELEXPIREDINVOICEREQUEST._serialized_start=11788 + _DELEXPIREDINVOICEREQUEST._serialized_end=11860 + _DELEXPIREDINVOICERESPONSE._serialized_start=11862 + _DELEXPIREDINVOICERESPONSE._serialized_end=11889 + _DELINVOICEREQUEST._serialized_start=11892 + _DELINVOICEREQUEST._serialized_end=12074 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=12008 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=12061 + _DELINVOICERESPONSE._serialized_start=12077 + _DELINVOICERESPONSE._serialized_end=12622 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=12008 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=12061 + _INVOICEREQUEST._serialized_start=12625 + _INVOICEREQUEST._serialized_end=12875 + _INVOICERESPONSE._serialized_start=12878 + _INVOICERESPONSE._serialized_end=13283 + _LISTDATASTOREREQUEST._serialized_start=13285 + _LISTDATASTOREREQUEST._serialized_end=13320 + _LISTDATASTORERESPONSE._serialized_start=13322 + _LISTDATASTORERESPONSE._serialized_end=13393 + _LISTDATASTOREDATASTORE._serialized_start=13396 + _LISTDATASTOREDATASTORE._serialized_end=13531 + _LISTINVOICESREQUEST._serialized_start=13534 + _LISTINVOICESREQUEST._serialized_end=13884 + _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_start=13755 + _LISTINVOICESREQUEST_LISTINVOICESINDEX._serialized_end=13800 + _LISTINVOICESRESPONSE._serialized_start=13886 + _LISTINVOICESRESPONSE._serialized_end=13953 + _LISTINVOICESINVOICES._serialized_start=13956 + _LISTINVOICESINVOICES._serialized_end=14722 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=14456 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=14519 + _SENDONIONREQUEST._serialized_start=14725 + _SENDONIONREQUEST._serialized_end=15119 + _SENDONIONRESPONSE._serialized_start=15122 + _SENDONIONRESPONSE._serialized_end=15645 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=15493 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=15537 + _SENDONIONFIRST_HOP._serialized_start=15647 + _SENDONIONFIRST_HOP._serialized_end=15728 + _LISTSENDPAYSREQUEST._serialized_start=15731 + _LISTSENDPAYSREQUEST._serialized_end=15966 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=15868 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15927 + _LISTSENDPAYSRESPONSE._serialized_start=15968 + _LISTSENDPAYSRESPONSE._serialized_end=16035 + _LISTSENDPAYSPAYMENTS._serialized_start=16038 + _LISTSENDPAYSPAYMENTS._serialized_end=16666 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=16472 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=16539 + _LISTTRANSACTIONSREQUEST._serialized_start=16668 + _LISTTRANSACTIONSREQUEST._serialized_end=16693 + _LISTTRANSACTIONSRESPONSE._serialized_start=16695 + _LISTTRANSACTIONSRESPONSE._serialized_end=16778 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=16781 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=17029 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=17031 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=17114 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=17116 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17224 + _PAYREQUEST._serialized_start=17227 + _PAYREQUEST._serialized_end=17701 + _PAYRESPONSE._serialized_start=17704 + _PAYRESPONSE._serialized_end=18083 + _PAYRESPONSE_PAYSTATUS._serialized_start=17986 + _PAYRESPONSE_PAYSTATUS._serialized_end=18036 + _LISTNODESREQUEST._serialized_start=18085 + _LISTNODESREQUEST._serialized_end=18127 + _LISTNODESRESPONSE._serialized_start=18129 + _LISTNODESRESPONSE._serialized_end=18184 + _LISTNODESNODES._serialized_start=18187 + _LISTNODESNODES._serialized_end=18412 + _LISTNODESNODESADDRESSES._serialized_start=18415 + _LISTNODESNODESADDRESSES._serialized_end=18647 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18555 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18635 + _WAITANYINVOICEREQUEST._serialized_start=18649 + _WAITANYINVOICEREQUEST._serialized_end=18752 + _WAITANYINVOICERESPONSE._serialized_start=18755 + _WAITANYINVOICERESPONSE._serialized_end=19378 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19187 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19232 + _WAITINVOICEREQUEST._serialized_start=19380 + _WAITINVOICEREQUEST._serialized_end=19415 + _WAITINVOICERESPONSE._serialized_start=19418 + _WAITINVOICERESPONSE._serialized_end=20029 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19841 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19883 + _WAITSENDPAYREQUEST._serialized_start=20032 + _WAITSENDPAYREQUEST._serialized_end=20174 + _WAITSENDPAYRESPONSE._serialized_start=20177 + _WAITSENDPAYRESPONSE._serialized_end=20739 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20581 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20614 + _NEWADDRREQUEST._serialized_start=20742 + _NEWADDRREQUEST._serialized_end=20893 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20826 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20877 + _NEWADDRRESPONSE._serialized_start=20895 + _NEWADDRRESPONSE._serialized_end=21014 + _WITHDRAWREQUEST._serialized_start=21017 + _WITHDRAWREQUEST._serialized_end=21219 + _WITHDRAWRESPONSE._serialized_start=21221 + _WITHDRAWRESPONSE._serialized_end=21279 + _KEYSENDREQUEST._serialized_start=21282 + _KEYSENDREQUEST._serialized_end=21668 + _KEYSENDRESPONSE._serialized_start=21671 + _KEYSENDRESPONSE._serialized_end=22041 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21965 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21994 + _FUNDPSBTREQUEST._serialized_start=22044 + _FUNDPSBTREQUEST._serialized_end=22464 + _FUNDPSBTRESPONSE._serialized_start=22467 + _FUNDPSBTRESPONSE._serialized_end=22684 + _FUNDPSBTRESERVATIONS._serialized_start=22686 + _FUNDPSBTRESERVATIONS._serialized_end=22803 + _SENDPSBTREQUEST._serialized_start=22805 + _SENDPSBTREQUEST._serialized_end=22870 + _SENDPSBTRESPONSE._serialized_start=22872 + _SENDPSBTRESPONSE._serialized_end=22916 + _SIGNPSBTREQUEST._serialized_start=22918 + _SIGNPSBTREQUEST._serialized_end=22967 + _SIGNPSBTRESPONSE._serialized_start=22969 + _SIGNPSBTRESPONSE._serialized_end=23008 + _UTXOPSBTREQUEST._serialized_start=23011 + _UTXOPSBTREQUEST._serialized_end=23422 + _UTXOPSBTRESPONSE._serialized_start=23425 + _UTXOPSBTRESPONSE._serialized_end=23642 + _UTXOPSBTRESERVATIONS._serialized_start=23644 + _UTXOPSBTRESERVATIONS._serialized_end=23761 + _TXDISCARDREQUEST._serialized_start=23763 + _TXDISCARDREQUEST._serialized_end=23795 + _TXDISCARDRESPONSE._serialized_start=23797 + _TXDISCARDRESPONSE._serialized_end=23851 + _TXPREPAREREQUEST._serialized_start=23854 + _TXPREPAREREQUEST._serialized_end=24018 + _TXPREPARERESPONSE._serialized_start=24020 + _TXPREPARERESPONSE._serialized_end=24088 + _TXSENDREQUEST._serialized_start=24090 + _TXSENDREQUEST._serialized_end=24119 + _TXSENDRESPONSE._serialized_start=24121 + _TXSENDRESPONSE._serialized_end=24177 + _LISTPEERCHANNELSREQUEST._serialized_start=24179 + _LISTPEERCHANNELSREQUEST._serialized_end=24228 + _LISTPEERCHANNELSRESPONSE._serialized_start=24230 + _LISTPEERCHANNELSRESPONSE._serialized_end=24305 + _LISTPEERCHANNELSCHANNELS._serialized_start=24308 + _LISTPEERCHANNELSCHANNELS._serialized_end=27535 + _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_start=26299 + _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_end=26620 + _LISTPEERCHANNELSCHANNELSFEERATE._serialized_start=27537 + _LISTPEERCHANNELSCHANNELSFEERATE._serialized_end=27630 + _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_start=27633 + _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_end=28017 + _LISTPEERCHANNELSCHANNELSFUNDING._serialized_start=28020 + _LISTPEERCHANNELSCHANNELSFUNDING._serialized_end=28358 + _LISTPEERCHANNELSCHANNELSALIAS._serialized_start=28360 + _LISTPEERCHANNELSCHANNELSALIAS._serialized_end=28453 + _LISTPEERCHANNELSCHANNELSHTLCS._serialized_start=28456 + _LISTPEERCHANNELSCHANNELSHTLCS._serialized_end=28938 + _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_start=28777 + _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_end=28834 + _LISTCLOSEDCHANNELSREQUEST._serialized_start=28940 + _LISTCLOSEDCHANNELSREQUEST._serialized_end=28991 + _LISTCLOSEDCHANNELSRESPONSE._serialized_start=28993 + _LISTCLOSEDCHANNELSRESPONSE._serialized_end=29084 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_start=29087 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_end=30289 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_start=29987 + _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_end=30105 + _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_start=30291 + _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_end=30392 + _DECODEPAYREQUEST._serialized_start=30394 + _DECODEPAYREQUEST._serialized_end=30470 + _DECODEPAYRESPONSE._serialized_start=30473 + _DECODEPAYRESPONSE._serialized_end=30998 + _DECODEPAYFALLBACKS._serialized_start=31001 + _DECODEPAYFALLBACKS._serialized_end=31199 + _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_start=31122 + _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_end=31190 + _DECODEPAYEXTRA._serialized_start=31201 + _DECODEPAYEXTRA._serialized_end=31244 + _DECODEREQUEST._serialized_start=31246 + _DECODEREQUEST._serialized_end=31277 + _DECODERESPONSE._serialized_start=31280 + _DECODERESPONSE._serialized_end=35546 + _DECODERESPONSE_DECODETYPE._serialized_start=33848 + _DECODERESPONSE_DECODETYPE._serialized_end=33956 + _DECODEOFFER_PATHS._serialized_start=35548 + _DECODEOFFER_PATHS._serialized_end=35608 + _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_start=35611 + _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_end=35749 + _DECODEINVOICE_PATHSPATH._serialized_start=35751 + _DECODEINVOICE_PATHSPATH._serialized_end=35835 + _DECODEINVOICE_FALLBACKS._serialized_start=35837 + _DECODEINVOICE_FALLBACKS._serialized_end=35926 + _DECODEFALLBACKS._serialized_start=35928 + _DECODEFALLBACKS._serialized_end=36047 + _DECODEEXTRA._serialized_start=36049 + _DECODEEXTRA._serialized_end=36089 + _DECODERESTRICTIONS._serialized_start=36091 + _DECODERESTRICTIONS._serialized_end=36150 + _DISCONNECTREQUEST._serialized_start=36152 + _DISCONNECTREQUEST._serialized_end=36213 + _DISCONNECTRESPONSE._serialized_start=36215 + _DISCONNECTRESPONSE._serialized_end=36235 + _FEERATESREQUEST._serialized_start=36237 + _FEERATESREQUEST._serialized_end=36344 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=36307 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=36344 + _FEERATESRESPONSE._serialized_start=36347 + _FEERATESRESPONSE._serialized_end=36631 + _FEERATESPERKB._serialized_start=36634 + _FEERATESPERKB._serialized_end=37101 + _FEERATESPERKBESTIMATES._serialized_start=37104 + _FEERATESPERKBESTIMATES._serialized_end=37254 + _FEERATESPERKW._serialized_start=37257 + _FEERATESPERKW._serialized_end=37724 + _FEERATESPERKWESTIMATES._serialized_start=37727 + _FEERATESPERKWESTIMATES._serialized_end=37877 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=37880 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=38163 + _FUNDCHANNELREQUEST._serialized_start=38166 + _FUNDCHANNELREQUEST._serialized_end=38651 + _FUNDCHANNELRESPONSE._serialized_start=38654 + _FUNDCHANNELRESPONSE._serialized_end=38809 + _GETROUTEREQUEST._serialized_start=38812 + _GETROUTEREQUEST._serialized_end=39048 + _GETROUTERESPONSE._serialized_start=39050 + _GETROUTERESPONSE._serialized_end=39103 + _GETROUTEROUTE._serialized_start=39106 + _GETROUTEROUTE._serialized_end=39303 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=39274 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=39303 + _LISTFORWARDSREQUEST._serialized_start=39306 + _LISTFORWARDSREQUEST._serialized_end=39564 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=39446 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=39522 + _LISTFORWARDSRESPONSE._serialized_start=39566 + _LISTFORWARDSRESPONSE._serialized_end=39633 + _LISTFORWARDSFORWARDS._serialized_start=39636 + _LISTFORWARDSFORWARDS._serialized_end=40242 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=40025 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=40109 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=40111 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=40159 + _LISTPAYSREQUEST._serialized_start=40245 + _LISTPAYSREQUEST._serialized_end=40464 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=40370 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=40425 + _LISTPAYSRESPONSE._serialized_start=40466 + _LISTPAYSRESPONSE._serialized_end=40517 + _LISTPAYSPAYS._serialized_start=40520 + _LISTPAYSPAYS._serialized_end=41159 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=40934 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=40993 + _LISTHTLCSREQUEST._serialized_start=41161 + _LISTHTLCSREQUEST._serialized_end=41203 + _LISTHTLCSRESPONSE._serialized_start=41205 + _LISTHTLCSRESPONSE._serialized_end=41260 + _LISTHTLCSHTLCS._serialized_start=41263 + _LISTHTLCSHTLCS._serialized_end=41528 + _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_start=41486 + _LISTHTLCSHTLCS_LISTHTLCSHTLCSDIRECTION._serialized_end=41528 + _PINGREQUEST._serialized_start=41530 + _PINGREQUEST._serialized_end=41619 + _PINGRESPONSE._serialized_start=41621 + _PINGRESPONSE._serialized_end=41651 + _SENDCUSTOMMSGREQUEST._serialized_start=41653 + _SENDCUSTOMMSGREQUEST._serialized_end=41705 + _SENDCUSTOMMSGRESPONSE._serialized_start=41707 + _SENDCUSTOMMSGRESPONSE._serialized_end=41746 + _SETCHANNELREQUEST._serialized_start=41749 + _SETCHANNELREQUEST._serialized_end=42047 + _SETCHANNELRESPONSE._serialized_start=42049 + _SETCHANNELRESPONSE._serialized_end=42112 + _SETCHANNELCHANNELS._serialized_start=42115 + _SETCHANNELCHANNELS._serialized_end=42573 + _SIGNINVOICEREQUEST._serialized_start=42575 + _SIGNINVOICEREQUEST._serialized_end=42614 + _SIGNINVOICERESPONSE._serialized_start=42616 + _SIGNINVOICERESPONSE._serialized_end=42653 + _SIGNMESSAGEREQUEST._serialized_start=42655 + _SIGNMESSAGEREQUEST._serialized_end=42692 + _SIGNMESSAGERESPONSE._serialized_start=42694 + _SIGNMESSAGERESPONSE._serialized_end=42764 + _STOPREQUEST._serialized_start=42766 + _STOPREQUEST._serialized_end=42779 + _STOPRESPONSE._serialized_start=42781 + _STOPRESPONSE._serialized_end=42795 + _PREAPPROVEKEYSENDREQUEST._serialized_start=42798 + _PREAPPROVEKEYSENDREQUEST._serialized_end=42965 + _PREAPPROVEKEYSENDRESPONSE._serialized_start=42967 + _PREAPPROVEKEYSENDRESPONSE._serialized_end=42994 + _PREAPPROVEINVOICEREQUEST._serialized_start=42996 + _PREAPPROVEINVOICEREQUEST._serialized_end=43054 + _PREAPPROVEINVOICERESPONSE._serialized_start=43056 + _PREAPPROVEINVOICERESPONSE._serialized_end=43083 + _NODE._serialized_start=43086 + _NODE._serialized_end=46742 +# @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py similarity index 94% rename from contrib/pyln-testing/pyln/testing/node_pb2_grpc.py rename to contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py index 661478b05a78..78517431b648 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py +++ b/contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py @@ -2,7 +2,7 @@ """Client and server classes corresponding to protobuf-defined services.""" import grpc -from . import node_pb2 as node__pb2 +from pyln.grpc import node_pb2 as node__pb2 class NodeStub(object): @@ -249,6 +249,11 @@ def __init__(self, channel): request_serializer=node__pb2.ListpaysRequest.SerializeToString, response_deserializer=node__pb2.ListpaysResponse.FromString, ) + self.ListHtlcs = channel.unary_unary( + '/cln.Node/ListHtlcs', + request_serializer=node__pb2.ListhtlcsRequest.SerializeToString, + response_deserializer=node__pb2.ListhtlcsResponse.FromString, + ) self.Ping = channel.unary_unary( '/cln.Node/Ping', request_serializer=node__pb2.PingRequest.SerializeToString, @@ -279,6 +284,16 @@ def __init__(self, channel): request_serializer=node__pb2.StopRequest.SerializeToString, response_deserializer=node__pb2.StopResponse.FromString, ) + self.PreApproveKeysend = channel.unary_unary( + '/cln.Node/PreApproveKeysend', + request_serializer=node__pb2.PreapprovekeysendRequest.SerializeToString, + response_deserializer=node__pb2.PreapprovekeysendResponse.FromString, + ) + self.PreApproveInvoice = channel.unary_unary( + '/cln.Node/PreApproveInvoice', + request_serializer=node__pb2.PreapproveinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.PreapproveinvoiceResponse.FromString, + ) class NodeServicer(object): @@ -566,6 +581,12 @@ def ListPays(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ListHtlcs(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def Ping(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -602,6 +623,18 @@ def Stop(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def PreApproveKeysend(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def PreApproveInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_NodeServicer_to_server(servicer, server): rpc_method_handlers = { @@ -840,6 +873,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.ListpaysRequest.FromString, response_serializer=node__pb2.ListpaysResponse.SerializeToString, ), + 'ListHtlcs': grpc.unary_unary_rpc_method_handler( + servicer.ListHtlcs, + request_deserializer=node__pb2.ListhtlcsRequest.FromString, + response_serializer=node__pb2.ListhtlcsResponse.SerializeToString, + ), 'Ping': grpc.unary_unary_rpc_method_handler( servicer.Ping, request_deserializer=node__pb2.PingRequest.FromString, @@ -870,6 +908,16 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.StopRequest.FromString, response_serializer=node__pb2.StopResponse.SerializeToString, ), + 'PreApproveKeysend': grpc.unary_unary_rpc_method_handler( + servicer.PreApproveKeysend, + request_deserializer=node__pb2.PreapprovekeysendRequest.FromString, + response_serializer=node__pb2.PreapprovekeysendResponse.SerializeToString, + ), + 'PreApproveInvoice': grpc.unary_unary_rpc_method_handler( + servicer.PreApproveInvoice, + request_deserializer=node__pb2.PreapproveinvoiceRequest.FromString, + response_serializer=node__pb2.PreapproveinvoiceResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'cln.Node', rpc_method_handlers) @@ -1679,6 +1727,23 @@ def ListPays(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def ListHtlcs(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListHtlcs', + node__pb2.ListhtlcsRequest.SerializeToString, + node__pb2.ListhtlcsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def Ping(request, target, @@ -1780,3 +1845,37 @@ def Stop(request, node__pb2.StopResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def PreApproveKeysend(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/PreApproveKeysend', + node__pb2.PreapprovekeysendRequest.SerializeToString, + node__pb2.PreapprovekeysendResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def PreApproveInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/PreApproveInvoice', + node__pb2.PreapproveinvoiceRequest.SerializeToString, + node__pb2.PreapproveinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py b/contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py new file mode 100644 index 000000000000..532ffae00dcc --- /dev/null +++ b/contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: primitives.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10primitives.proto\x12\x03\x63ln\"\x16\n\x06\x41mount\x12\x0c\n\x04msat\x18\x01 \x01(\x04\"D\n\x0b\x41mountOrAll\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ll\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"D\n\x0b\x41mountOrAny\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ny\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"\x19\n\x17\x43hannelStateChangeCause\"(\n\x08Outpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"h\n\x07\x46\x65\x65rate\x12\x0e\n\x04slow\x18\x01 \x01(\x08H\x00\x12\x10\n\x06normal\x18\x02 \x01(\x08H\x00\x12\x10\n\x06urgent\x18\x03 \x01(\x08H\x00\x12\x0f\n\x05perkb\x18\x04 \x01(\rH\x00\x12\x0f\n\x05perkw\x18\x05 \x01(\rH\x00\x42\x07\n\x05style\":\n\nOutputDesc\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x1b\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\"t\n\x08RouteHop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x02 \x01(\t\x12\x1c\n\x07\x66\x65\x65\x62\x61se\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0f\n\x07\x66\x65\x65prop\x18\x04 \x01(\r\x12\x13\n\x0b\x65xpirydelta\x18\x05 \x01(\r\"(\n\tRoutehint\x12\x1b\n\x04hops\x18\x01 \x03(\x0b\x32\r.cln.RouteHop\".\n\rRoutehintList\x12\x1d\n\x05hints\x18\x02 \x03(\x0b\x32\x0e.cln.Routehint\"\'\n\x08TlvEntry\x12\x0c\n\x04type\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x0c\"+\n\tTlvStream\x12\x1e\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\r.cln.TlvEntry*$\n\x0b\x43hannelSide\x12\t\n\x05LOCAL\x10\x00\x12\n\n\x06REMOTE\x10\x01*\x84\x02\n\x0c\x43hannelState\x12\x0c\n\x08Openingd\x10\x00\x12\x1a\n\x16\x43hanneldAwaitingLockin\x10\x01\x12\x12\n\x0e\x43hanneldNormal\x10\x02\x12\x18\n\x14\x43hanneldShuttingDown\x10\x03\x12\x17\n\x13\x43losingdSigexchange\x10\x04\x12\x14\n\x10\x43losingdComplete\x10\x05\x12\x16\n\x12\x41waitingUnilateral\x10\x06\x12\x14\n\x10\x46undingSpendSeen\x10\x07\x12\x0b\n\x07Onchain\x10\x08\x12\x15\n\x11\x44ualopendOpenInit\x10\t\x12\x1b\n\x17\x44ualopendAwaitingLockin\x10\n*\xd5\x03\n\tHtlcState\x12\x0f\n\x0bSentAddHtlc\x10\x00\x12\x11\n\rSentAddCommit\x10\x01\x12\x15\n\x11RcvdAddRevocation\x10\x02\x12\x14\n\x10RcvdAddAckCommit\x10\x03\x12\x18\n\x14SentAddAckRevocation\x10\x04\x12\x18\n\x14RcvdAddAckRevocation\x10\x05\x12\x12\n\x0eRcvdRemoveHtlc\x10\x06\x12\x14\n\x10RcvdRemoveCommit\x10\x07\x12\x18\n\x14SentRemoveRevocation\x10\x08\x12\x17\n\x13SentRemoveAckCommit\x10\t\x12\x1b\n\x17RcvdRemoveAckRevocation\x10\n\x12\x0f\n\x0bRcvdAddHtlc\x10\x0b\x12\x11\n\rRcvdAddCommit\x10\x0c\x12\x15\n\x11SentAddRevocation\x10\r\x12\x14\n\x10SentAddAckCommit\x10\x0e\x12\x12\n\x0eSentRemoveHtlc\x10\x0f\x12\x14\n\x10SentRemoveCommit\x10\x10\x12\x18\n\x14RcvdRemoveRevocation\x10\x11\x12\x17\n\x13RcvdRemoveAckCommit\x10\x12\x12\x1b\n\x17SentRemoveAckRevocation\x10\x13\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'primitives_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CHANNELSIDE._serialized_start=718 + _CHANNELSIDE._serialized_end=754 + _CHANNELSTATE._serialized_start=757 + _CHANNELSTATE._serialized_end=1017 + _HTLCSTATE._serialized_start=1020 + _HTLCSTATE._serialized_end=1489 + _AMOUNT._serialized_start=25 + _AMOUNT._serialized_end=47 + _AMOUNTORALL._serialized_start=49 + _AMOUNTORALL._serialized_end=117 + _AMOUNTORANY._serialized_start=119 + _AMOUNTORANY._serialized_end=187 + _CHANNELSTATECHANGECAUSE._serialized_start=189 + _CHANNELSTATECHANGECAUSE._serialized_end=214 + _OUTPOINT._serialized_start=216 + _OUTPOINT._serialized_end=256 + _FEERATE._serialized_start=258 + _FEERATE._serialized_end=362 + _OUTPUTDESC._serialized_start=364 + _OUTPUTDESC._serialized_end=422 + _ROUTEHOP._serialized_start=424 + _ROUTEHOP._serialized_end=540 + _ROUTEHINT._serialized_start=542 + _ROUTEHINT._serialized_end=582 + _ROUTEHINTLIST._serialized_start=584 + _ROUTEHINTLIST._serialized_end=630 + _TLVENTRY._serialized_start=632 + _TLVENTRY._serialized_end=671 + _TLVSTREAM._serialized_start=673 + _TLVSTREAM._serialized_end=716 +# @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-grpc-proto/pyproject.toml b/contrib/pyln-grpc-proto/pyproject.toml new file mode 100644 index 000000000000..8860566d8d58 --- /dev/null +++ b/contrib/pyln-grpc-proto/pyproject.toml @@ -0,0 +1,25 @@ + +[tool.poetry] +name = "pyln-grpc-proto" +version = "0.1.1" +description = "The compiled GRPC proto for CLN" +authors = ["Christian Decker "] +license = "MIT" +readme = "README.md" + +packages = [ + { include = "pyln/grpc/*.py" } +] + +[tool.poetry.dependencies] +python = "^3.8" +grpcio = "*" +protobuf3 = "*" + +[tool.poetry.group.dev.dependencies] +grpcio = "*" +twine = "^4.0.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-proto/pyln/proto/__init__.py b/contrib/pyln-proto/pyln/proto/__init__.py index 44ac9994fe16..709db757ad28 100644 --- a/contrib/pyln-proto/pyln/proto/__init__.py +++ b/contrib/pyln-proto/pyln/proto/__init__.py @@ -4,7 +4,7 @@ from .onion import OnionPayload, TlvPayload, LegacyOnionPayload from .wire import LightningConnection, LightningServerSocket -__version__ = "23.05" +__version__ = "23.08" __all__ = [ "Invoice", diff --git a/contrib/pyln-proto/pyln/proto/message/__init__.py b/contrib/pyln-proto/pyln/proto/message/__init__.py index 0b4493107cfc..c153e28aabee 100644 --- a/contrib/pyln-proto/pyln/proto/message/__init__.py +++ b/contrib/pyln-proto/pyln/proto/message/__init__.py @@ -1,5 +1,5 @@ from .array_types import SizedArrayType, DynamicArrayType, EllipsisArrayType -from .message import MessageNamespace, MessageType, Message, SubtypeType +from .message import MessageNamespace, MessageType, Message, SubtypeType, TlvStreamType, TlvMessageType from .fundamental_types import split_field, FieldType __all__ = [ @@ -14,12 +14,18 @@ "SizedArrayType", "DynamicArrayType", "EllipsisArrayType", + "TlvStreamType", + "TlvMessageType", # fundamental_types 'byte', 'u16', 'u32', 'u64', + 's8', + 's16', + 's32', + 's64', 'tu16', 'tu32', 'tu64', diff --git a/contrib/pyln-proto/pyln/proto/message/fundamental_types.py b/contrib/pyln-proto/pyln/proto/message/fundamental_types.py index 75aa4d640a61..36ef31749458 100644 --- a/contrib/pyln-proto/pyln/proto/message/fundamental_types.py +++ b/contrib/pyln-proto/pyln/proto/message/fundamental_types.py @@ -257,6 +257,10 @@ def fundamental_types() -> List[FieldType]: # * `u16`: a 2 byte unsigned integer # * `u32`: a 4 byte unsigned integer # * `u64`: an 8 byte unsigned integer + # * `s8`: an 8-bit signed integer + # * `s16`: a 2 byte signed integer + # * `s32`: a 4 byte signed integer + # * `s64`: an 8 byte signed integer # # Inside TLV records which contain a single value, leading zeros in # integers can be omitted: @@ -284,6 +288,10 @@ def fundamental_types() -> List[FieldType]: IntegerType('u16', 2, '>H'), IntegerType('u32', 4, '>I'), IntegerType('u64', 8, '>Q'), + IntegerType('s8', 1, 'b'), + IntegerType('s16', 2, '>h'), + IntegerType('s32', 4, '>i'), + IntegerType('s64', 8, '>q'), TruncatedIntType('tu16', 2), TruncatedIntType('tu32', 4), TruncatedIntType('tu64', 8), diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index 0830c9d45190..9b6bdb71ea88 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "23.05" +version = "23.08" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" @@ -11,12 +11,12 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" base58 = "^2.1.1" -bitstring = "^3.1.9" -coincurve = "^17.0.0" -cryptography = "^36.0.1" -PySocks = "^1.7.1" +bitstring = "^3" +coincurve = "^18" +cryptography = "^41" +PySocks = "^1" [tool.poetry.dev-dependencies] pytest = "^7" diff --git a/contrib/pyln-proto/tests/test_fundamental_types.py b/contrib/pyln-proto/tests/test_fundamental_types.py index 1c22f3a21596..8da878cb571f 100644 --- a/contrib/pyln-proto/tests/test_fundamental_types.py +++ b/contrib/pyln-proto/tests/test_fundamental_types.py @@ -13,6 +13,29 @@ def test_fundamental_types(): 'u64': [['18446744073709551615', b'\xff\xff\xff\xff\xff\xff\xff\xff'], ['0', b'\x00\x00\x00\x00\x00\x00\x00\x00']], + 's8': [['0', b'\x00'], + ['42', b'\x2a'], + ['-42', b'\xd6'], + ['127', b'\x7f'], + ['-128', b'\x80']], + 's16': [['128', b'\x00\x80'], + ['-129', b'\xff\x7f'], + ['15000', b'\x3a\x98'], + ['-15000', b'\xc5\x68'], + ['32767', b'\x7f\xff'], + ['-32768', b'\x80\x00']], + 's32': [['32768', b'\x00\x00\x80\x00'], + ['-32769', b'\xff\xff\x7f\xff'], + ['21000000', b'\x01\x40\x6f\x40'], + ['-21000000', b'\xfe\xbf\x90\xc0'], + ['2147483647', b'\x7f\xff\xff\xff'], + ['-2147483648', b'\x80\x00\x00\x00']], + 's64': [['2147483648', b'\x00\x00\x00\x00\x80\x00\x00\x00'], + ['-2147483649', b'\xff\xff\xff\xff\x7f\xff\xff\xff'], + ['500000000000', b'\x00\x00\x00\x74\x6a\x52\x88\x00'], + ['-500000000000', b'\xff\xff\xff\x8b\x95\xad\x78\x00'], + ['9223372036854775807', b'\x7f\xff\xff\xff\xff\xff\xff\xff'], + ['-9223372036854775808', b'\x80\x00\x00\x00\x00\x00\x00\x00']], 'tu16': [['65535', b'\xff\xff'], ['256', b'\x01\x00'], ['255', b'\xff'], diff --git a/contrib/pyln-spec/Makefile b/contrib/pyln-spec/Makefile index 14ad23aa7f0b..e497bb580710 100755 --- a/contrib/pyln-spec/Makefile +++ b/contrib/pyln-spec/Makefile @@ -100,5 +100,5 @@ $(CODE_DIRS:%=%/csv.py): $(CODE_DIRS:%=%/text.py): @echo 'desc = "'`head -n1 $< | cut -c3-`'"' > $@.tmp - @(echo -n 'text = """'; sed 's,\\,\\\\,g' < $<; echo '"""') >> $@.tmp + @(printf '%s' 'text = """'; sed 's,\\,\\\\,g' < $<; echo '"""') >> $@.tmp @if cmp $@ $@.tmp >/dev/null 2>&1; then rm $@.tmp; echo '$@ unchanged'; else mv $@.tmp $@; fi diff --git a/contrib/pyln-spec/bolt1/pyproject.toml b/contrib/pyln-spec/bolt1/pyproject.toml index 743fa0a5d64b..1d26e5a977b6 100644 --- a/contrib/pyln-spec/bolt1/pyproject.toml +++ b/contrib/pyln-spec/bolt1/pyproject.toml @@ -10,7 +10,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" [tool.poetry.dev-dependencies] pyln-proto = "^0.10.2" diff --git a/contrib/pyln-spec/bolt2/pyproject.toml b/contrib/pyln-spec/bolt2/pyproject.toml index bbb708866440..bd1217d03182 100644 --- a/contrib/pyln-spec/bolt2/pyproject.toml +++ b/contrib/pyln-spec/bolt2/pyproject.toml @@ -10,7 +10,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" [tool.poetry.dev-dependencies] pyln-proto = "^0.10.2" diff --git a/contrib/pyln-spec/bolt4/pyproject.toml b/contrib/pyln-spec/bolt4/pyproject.toml index eb261be16ae9..261c5b6b8c50 100644 --- a/contrib/pyln-spec/bolt4/pyproject.toml +++ b/contrib/pyln-spec/bolt4/pyproject.toml @@ -10,7 +10,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" [tool.poetry.dev-dependencies] pyln-proto = "^0.10.2" diff --git a/contrib/pyln-spec/bolt7/pyproject.toml b/contrib/pyln-spec/bolt7/pyproject.toml index 1048f43bd1e1..c6d34e7afcf3 100644 --- a/contrib/pyln-spec/bolt7/pyproject.toml +++ b/contrib/pyln-spec/bolt7/pyproject.toml @@ -10,7 +10,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" [tool.poetry.dev-dependencies] pyln-proto = "^0.10.2" diff --git a/contrib/pyln-testing/README.md b/contrib/pyln-testing/README.md index 469dc33a57ba..2c89fa9bd8c7 100644 --- a/contrib/pyln-testing/README.md +++ b/contrib/pyln-testing/README.md @@ -31,3 +31,31 @@ checked out source code will also result in the environment picking up these changes. Notice however that unreleased versions may change API without warning, so test thoroughly with the released version. +## Testing GRPC Bindings + +The grpc bindings can be tested by setting the `CLN_TEST_GRPC=1` +environment variable. This will cause the testing framework to use a +grpc client to talk to the `cln-grpc` plugin, rather than talking +directly to the node's JSON-RPC interface. Since the GRPC related +dependencies are guarded behind a feature flag in `pyln-testing` +you'll need to install it with the `grpc` feature enabled in order to +be able to run in this mode. + +Below is a diagram of how the normal JSON-RPC interaction looks like, +followed by one that display the grpc interaction: + +``` +CLN -- JSON-RPC -- LightningRpc -- pytest +\_____CLN_____/ \_______pytest_______/ +``` + +``` +CLN -- JSON-RPC -- cln-rpc -- rpc2grpc converters -- grpc interface -- python grpc client -- python grpc2json converter -- pytest +\_____CLN_____/ \___________cln-grpc-plugin____________________/ \__________________________pytest________________________/ +``` + +As you can see the grpc mode attempts to emulate the simple JSON-RPC +mode by passing the call through a number of conversions. The last +step `grpc2json` is rather incomplete, and will cause quite a few +tests to fail for now, until the conversion is completed and we reach +feature parity between the interaction modes. diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 9cf6bca081eb..e84f312e5afe 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -__version__ = "23.05" +__version__ = "23.08" __all__ = [ "__version__", diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index bf3293d24452..73445b969fb5 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -1,6 +1,6 @@ from concurrent import futures from pyln.testing.db import SqliteDbProvider, PostgresDbProvider -from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, DEVELOPER, LightningNode, TEST_DEBUG +from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, DEVELOPER, LightningNode, TEST_DEBUG, LssD from pyln.client import Millisatoshi from typing import Dict @@ -92,7 +92,7 @@ def directory(request, test_base_dir, test_name): outcome = 'passed' if rep_call is None else rep_call.outcome failed = not outcome or request.node.has_errors or outcome != 'passed' - if not failed: + if not failed and not bool(int(os.getenv('TEST_KEEPDIR', '0'))): try: shutil.rmtree(directory) except OSError: @@ -164,6 +164,25 @@ def bitcoind(directory, teardown_checks): bitcoind.proc.wait() +@pytest.fixture +def lssd(directory, teardown_checks): + lssd = LssD(directory) + + try: + lssd.start() + except Exception: + lssd.stop() + raise + + yield lssd + + try: + lssd.stop() + except Exception: + lssd.proc.kill() + lssd.proc.wait() + + class TeardownErrors(object): def __init__(self): self.errors = [] @@ -446,11 +465,12 @@ def jsonschemas(): @pytest.fixture -def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, jsonschemas): +def node_factory(request, directory, test_name, bitcoind, lssd, executor, db_provider, teardown_checks, node_cls, jsonschemas): nf = NodeFactory( request, test_name, bitcoind, + lssd, executor, directory=directory, db_provider=db_provider, diff --git a/contrib/pyln-testing/pyln/testing/grpc.py b/contrib/pyln-testing/pyln/testing/grpc.py index d8ff7bc08925..ded2827f2b50 100644 --- a/contrib/pyln-testing/pyln/testing/grpc.py +++ b/contrib/pyln-testing/pyln/testing/grpc.py @@ -7,9 +7,8 @@ import grpc from pyln.testing import grpc2py -from pyln.testing import node_pb2 as pb -from pyln.testing import node_pb2_grpc as pbgrpc -from pyln.testing import primitives_pb2 as primpb + +from pyln import grpc as clnpb DUMMY_CA_PEM = b"""-----BEGIN CERTIFICATE----- MIIBcTCCARigAwIBAgIJAJhah1bqO05cMAoGCCqGSM49BAMCMBYxFDASBgNVBAMM @@ -48,24 +47,24 @@ -----END CERTIFICATE-----""" -def int2msat(amount: int) -> primpb.Amount: - return primpb.Amount(msat=amount) +def int2msat(amount: int) -> clnpb.Amount: + return clnpb.Amount(msat=amount) -def int2amount_or_all(amount: Tuple[int, str]) -> primpb.AmountOrAll: +def int2amount_or_all(amount: Tuple[int, str]) -> clnpb.AmountOrAll: if amount == "all": - return primpb.AmountOrAll(all=True) + return clnpb.AmountOrAll(all=True) else: assert isinstance(amount, int) - return primpb.AmountOrAll(amount=int2msat(amount)) + return clnpb.AmountOrAll(amount=int2msat(amount)) -def int2amount_or_any(amount: Tuple[int, str]) -> primpb.AmountOrAny: +def int2amount_or_any(amount: Tuple[int, str]) -> clnpb.AmountOrAny: if amount == "any": - return primpb.AmountOrAny(any=True) + return clnpb.AmountOrAny(any=True) else: assert isinstance(amount, int) - return primpb.AmountOrAny(amount=int2msat(amount)) + return clnpb.AmountOrAny(amount=int2msat(amount)) class LightningGrpc(object): @@ -89,20 +88,20 @@ def __init__( self.credentials, options=(("grpc.ssl_target_name_override", "cln"),), ) - self.stub = pbgrpc.NodeStub(self.channel) + self.stub = clnpb.NodeStub(self.channel) def getinfo(self): - return grpc2py.getinfo2py(self.stub.Getinfo(pb.GetinfoRequest())) + return grpc2py.getinfo2py(self.stub.Getinfo(clnpb.GetinfoRequest())) def connect(self, peer_id, host=None, port=None): """ Connect to {peer_id} at {host} and {port}. """ - payload = pb.ConnectRequest(id=peer_id, host=host, port=port) + payload = clnpb.ConnectRequest(id=peer_id, host=host, port=port) return grpc2py.connect2py(self.stub.ConnectPeer(payload)) def listpeers(self, peerid=None, level=None): - payload = pb.ListpeersRequest( + payload = clnpb.ListpeersRequest( id=unhexlify(peerid) if peerid is not None else None, level=level, ) @@ -132,7 +131,7 @@ def newaddr(self, addresstype=None): f"Unknown addresstype {addresstype}, known values are {enum.values()}" ) - payload = pb.NewaddrRequest(addresstype=atype) + payload = clnpb.NewaddrRequest(addresstype=atype) res = grpc2py.newaddr2py(self.stub.NewAddr(payload)) # Need to remap the bloody spelling of p2sh-segwit to match @@ -143,7 +142,7 @@ def newaddr(self, addresstype=None): return res def listfunds(self, spent=None): - payload = pb.ListfundsRequest(spent=spent) + payload = clnpb.ListfundsRequest(spent=spent) return grpc2py.listfunds2py(self.stub.ListFunds(payload)) def fundchannel( @@ -160,7 +159,7 @@ def fundchannel( # request_amt=None, compact_lease: Optional[str] = None, ): - payload = pb.FundchannelRequest( + payload = clnpb.FundchannelRequest( id=unhexlify(node_id), amount=int2amount_or_all(amount * 1000), # This is satoshis after all # TODO Parse and insert `feerate` @@ -173,7 +172,7 @@ def fundchannel( return grpc2py.fundchannel2py(self.stub.FundChannel(payload)) def listchannels(self, short_channel_id=None, source=None, destination=None): - payload = pb.ListchannelsRequest( + payload = clnpb.ListchannelsRequest( short_channel_id=short_channel_id, source=unhexlify(source) if source else None, destination=unhexlify(destination) if destination else None, @@ -197,7 +196,7 @@ def pay( description: Optional[str] = None, msatoshi: Optional[int] = None, ): - payload = pb.PayRequest( + payload = clnpb.PayRequest( bolt11=bolt11, amount_msat=int2msat(amount_msat), label=label, @@ -227,7 +226,7 @@ def invoice( deschashonly: Optional[bool] = None, # msatoshi=None ): - payload = pb.InvoiceRequest( + payload = clnpb.InvoiceRequest( amount_msat=int2amount_or_any(amount_msat), label=label, description=description, @@ -241,14 +240,14 @@ def invoice( return grpc2py.invoice2py(self.stub.Invoice(payload)) def stop(self): - payload = pb.StopRequest() + payload = clnpb.StopRequest() try: self.stub.Stop(payload) except Exception: pass def listnodes(self, node_id=None): - payload = pb.ListnodesRequest(id=unhexlify(node_id) if node_id else None) + payload = clnpb.ListnodesRequest(id=unhexlify(node_id) if node_id else None) return grpc2py.listnodes2py(self.stub.ListNodes(payload)) def close( @@ -261,7 +260,7 @@ def close( # TODO: not mapped yet # feerange: Optional[List[str]]=None ): - payload = pb.CloseRequest( + payload = clnpb.CloseRequest( id=peer_id, unilateraltimeout=unilateraltimeout, destination=destination, @@ -279,7 +278,7 @@ def listinvoices( invstring=None, offer_id=None ): - payload = pb.ListinvoicesRequest( + payload = clnpb.ListinvoicesRequest( label=label, invstring=invstring, payment_hash=unhexlify(payment_hash) if payment_hash else None, diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 1193bd9e0017..7c7788dd09af 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -91,6 +91,7 @@ def listpeers_peers_channels_inflight2py(m): "feerate": m.feerate, # PrimitiveField in generate_composite "total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite "our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite + "splice_amount": m.splice_amount, # PrimitiveField in generate_composite "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite }) @@ -332,6 +333,7 @@ def createinvoice2py(m): "status": str(m.status), # EnumField in generate_composite "description": m.description, # PrimitiveField in generate_composite "expires_at": m.expires_at, # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite "pay_index": m.pay_index, # PrimitiveField in generate_composite "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite "paid_at": m.paid_at, # PrimitiveField in generate_composite @@ -379,6 +381,8 @@ def delinvoice2py(m): "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "description": m.description, # PrimitiveField in generate_composite "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite + "updated_index": m.updated_index, # PrimitiveField in generate_composite "status": str(m.status), # EnumField in generate_composite "expires_at": m.expires_at, # PrimitiveField in generate_composite "local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite @@ -392,6 +396,7 @@ def invoice2py(m): "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite "payment_secret": hexlify(m.payment_secret), # PrimitiveField in generate_composite "expires_at": m.expires_at, # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite "warning_capacity": m.warning_capacity, # PrimitiveField in generate_composite "warning_offline": m.warning_offline, # PrimitiveField in generate_composite "warning_deadends": m.warning_deadends, # PrimitiveField in generate_composite @@ -427,6 +432,8 @@ def listinvoices_invoices2py(m): "bolt12": m.bolt12, # PrimitiveField in generate_composite "local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite "invreq_payer_note": m.invreq_payer_note, # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite + "updated_index": m.updated_index, # PrimitiveField in generate_composite "pay_index": m.pay_index, # PrimitiveField in generate_composite "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite "paid_at": m.paid_at, # PrimitiveField in generate_composite @@ -489,8 +496,6 @@ def listtransactions_transactions_inputs2py(m): "txid": hexlify(m.txid), # PrimitiveField in generate_composite "index": m.index, # PrimitiveField in generate_composite "sequence": m.sequence, # PrimitiveField in generate_composite - "type": str(m.item_type), # EnumField in generate_composite - "channel": m.channel, # PrimitiveField in generate_composite }) @@ -499,8 +504,6 @@ def listtransactions_transactions_outputs2py(m): "index": m.index, # PrimitiveField in generate_composite "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "script_pub_key": hexlify(m.script_pub_key), # PrimitiveField in generate_composite - "type": str(m.item_type), # EnumField in generate_composite - "channel": m.channel, # PrimitiveField in generate_composite }) @@ -572,6 +575,8 @@ def waitanyinvoice2py(m): "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "bolt11": m.bolt11, # PrimitiveField in generate_composite "bolt12": m.bolt12, # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite + "updated_index": m.updated_index, # PrimitiveField in generate_composite "pay_index": m.pay_index, # PrimitiveField in generate_composite "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite "paid_at": m.paid_at, # PrimitiveField in generate_composite @@ -589,6 +594,8 @@ def waitinvoice2py(m): "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "bolt11": m.bolt11, # PrimitiveField in generate_composite "bolt12": m.bolt12, # PrimitiveField in generate_composite + "created_index": m.created_index, # PrimitiveField in generate_composite + "updated_index": m.updated_index, # PrimitiveField in generate_composite "pay_index": m.pay_index, # PrimitiveField in generate_composite "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite "paid_at": m.paid_at, # PrimitiveField in generate_composite @@ -617,6 +624,7 @@ def waitsendpay2py(m): def newaddr2py(m): return remove_default({ + "p2tr": m.p2tr, # PrimitiveField in generate_composite "bech32": m.bech32, # PrimitiveField in generate_composite "p2sh_segwit": m.p2sh_segwit, # PrimitiveField in generate_composite }) @@ -742,6 +750,7 @@ def listpeerchannels_channels_inflight2py(m): "funding_outnum": m.funding_outnum, # PrimitiveField in generate_composite "feerate": m.feerate, # PrimitiveField in generate_composite "total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite + "splice_amount": m.splice_amount, # PrimitiveField in generate_composite "our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite }) @@ -793,6 +802,7 @@ def listpeerchannels_channels2py(m): "peer_connected": m.peer_connected, # PrimitiveField in generate_composite "state": str(m.state), # EnumField in generate_composite "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite + "ignore_fee_limits": m.ignore_fee_limits, # PrimitiveField in generate_composite "owner": m.owner, # PrimitiveField in generate_composite "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite "channel_id": hexlify(m.channel_id), # PrimitiveField in generate_composite @@ -1161,6 +1171,7 @@ def feerates_perkb2py(m): "opening": m.opening, # PrimitiveField in generate_composite "mutual_close": m.mutual_close, # PrimitiveField in generate_composite "unilateral_close": m.unilateral_close, # PrimitiveField in generate_composite + "unilateral_anchor_close": m.unilateral_anchor_close, # PrimitiveField in generate_composite "delayed_to_us": m.delayed_to_us, # PrimitiveField in generate_composite "htlc_resolution": m.htlc_resolution, # PrimitiveField in generate_composite "penalty": m.penalty, # PrimitiveField in generate_composite @@ -1184,6 +1195,7 @@ def feerates_perkw2py(m): "opening": m.opening, # PrimitiveField in generate_composite "mutual_close": m.mutual_close, # PrimitiveField in generate_composite "unilateral_close": m.unilateral_close, # PrimitiveField in generate_composite + "unilateral_anchor_close": m.unilateral_anchor_close, # PrimitiveField in generate_composite "delayed_to_us": m.delayed_to_us, # PrimitiveField in generate_composite "htlc_resolution": m.htlc_resolution, # PrimitiveField in generate_composite "penalty": m.penalty, # PrimitiveField in generate_composite @@ -1195,6 +1207,7 @@ def feerates_onchain_fee_estimates2py(m): "opening_channel_satoshis": m.opening_channel_satoshis, # PrimitiveField in generate_composite "mutual_close_satoshis": m.mutual_close_satoshis, # PrimitiveField in generate_composite "unilateral_close_satoshis": m.unilateral_close_satoshis, # PrimitiveField in generate_composite + "unilateral_close_nonanchor_satoshis": m.unilateral_close_nonanchor_satoshis, # PrimitiveField in generate_composite "htlc_timeout_satoshis": m.htlc_timeout_satoshis, # PrimitiveField in generate_composite "htlc_success_satoshis": m.htlc_success_satoshis, # PrimitiveField in generate_composite }) @@ -1266,6 +1279,8 @@ def listpays_pays2py(m): "bolt11": m.bolt11, # PrimitiveField in generate_composite "description": m.description, # PrimitiveField in generate_composite "bolt12": m.bolt12, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite "preimage": hexlify(m.preimage), # PrimitiveField in generate_composite "number_of_parts": m.number_of_parts, # PrimitiveField in generate_composite "erroronion": hexlify(m.erroronion), # PrimitiveField in generate_composite @@ -1278,6 +1293,24 @@ def listpays2py(m): }) +def listhtlcs_htlcs2py(m): + return remove_default({ + "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite + "id": m.id, # PrimitiveField in generate_composite + "expiry": m.expiry, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "direction": str(m.direction), # EnumField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "state": str(m.state), # EnumField in generate_composite + }) + + +def listhtlcs2py(m): + return remove_default({ + "htlcs": [listhtlcs_htlcs2py(i) for i in m.htlcs], # ArrayField[composite] in generate_composite + }) + + def ping2py(m): return remove_default({ "totlen": m.totlen, # PrimitiveField in generate_composite @@ -1297,6 +1330,7 @@ def setchannel_channels2py(m): "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite "fee_base_msat": amount2msat(m.fee_base_msat), # PrimitiveField in generate_composite "fee_proportional_millionths": m.fee_proportional_millionths, # PrimitiveField in generate_composite + "ignore_fee_limits": m.ignore_fee_limits, # PrimitiveField in generate_composite "minimum_htlc_out_msat": amount2msat(m.minimum_htlc_out_msat), # PrimitiveField in generate_composite "warning_htlcmin_too_low": m.warning_htlcmin_too_low, # PrimitiveField in generate_composite "maximum_htlc_out_msat": amount2msat(m.maximum_htlc_out_msat), # PrimitiveField in generate_composite @@ -1327,3 +1361,13 @@ def signmessage2py(m): def stop2py(m): return remove_default({ }) + + +def preapprovekeysend2py(m): + return remove_default({ + }) + + +def preapproveinvoice2py(m): + return remove_default({ + }) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py deleted file mode 100644 index 95d3f636221d..000000000000 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ /dev/null @@ -1,1745 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: node.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from . import primitives_pb2 as primitives__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xb2\x04\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x33\n\x0cour_features\x18\n \x01(\x0b\x32\x18.cln.GetinfoOur_featuresH\x00\x88\x01\x01\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x01\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x02\x88\x01\x01\x42\x0f\n\r_our_featuresB\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\x8e\x02\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12\x19\n\x0cnum_channels\x18\x08 \x01(\rH\x00\x88\x01\x01\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x42\x0f\n\r_num_channelsB\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\xd6\x17\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x38\n\x07\x66\x65\x65rate\x18\x03 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFeerateH\x01\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x05\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x08\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\t\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\n\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\x0b\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0c\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\r\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12\x38\n\x07\x66unding\x18\x13 \x01(\x0b\x32\".cln.ListpeersPeersChannelsFundingH\x0e\x88\x01\x01\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x12\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x14\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1e\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1f\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH \x88\x01\x01\x12\x34\n\x05\x61lias\x18\x32 \x01(\x0b\x32 .cln.ListpeersPeersChannelsAliasH!\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\"\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H$\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH%\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H&\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H(\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH*\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x9b\x02\n\x1dListpeersPeersChannelsFunding\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xf1\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcState\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\x83\x03\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"Q\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x12\x0c\n\x08IMMATURE\x10\x03\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\xab\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x00\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x01\x88\x01\x01\x42\r\n\x0b_channel_idB\x13\n\x11_short_channel_id\"\xdd\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0b \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xb3\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x11\n\tdirection\x18\x10 \x01(\r\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\xb4\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\x12$\n\x07\x61\x64\x64ress\x18\x04 \x01(\x0b\x32\x13.cln.ConnectAddress\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\x81\x05\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xc5\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0b \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_note\"\xfa\x01\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x03\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\xa2\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x0f \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\x14\n\x12_invreq_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8a\x03\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12*\n\tfirst_hop\x18\x02 \x01(\x0b\x32\x17.cln.SendonionFirst_hop\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\r \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x10\n\x0e_localinvreqidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xf4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x13\n\x06partid\x18\x0f \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\t\n\x07_partidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\xf8\x01\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputs\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xda\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1a\n\rlocalinvreqid\x18\x0e \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x10\n\x0e_localinvreqidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x8d\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\")\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\x82\x03\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x12&\n\textratlvs\x18\t \x01(\x0b\x32\x0e.cln.TlvStreamH\x06\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehintsB\x0c\n\n_extratlvs\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"1\n\x17ListpeerchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"K\n\x18ListpeerchannelsResponse\x12/\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1d.cln.ListpeerchannelsChannels\"\xc7\x18\n\x18ListpeerchannelsChannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0epeer_connected\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12O\n\x05state\x18\x03 \x01(\x0e\x32;.cln.ListpeerchannelsChannels.ListpeerchannelsChannelsStateH\x02\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x04 \x01(\x0cH\x03\x88\x01\x01\x12:\n\x07\x66\x65\x65rate\x18\x06 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFeerateH\x04\x88\x01\x01\x12\x12\n\x05owner\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x17\n\nchannel_id\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\n \x01(\x0cH\x08\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x0b \x01(\rH\t\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\x0c \x01(\tH\n\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\r \x01(\tH\x0b\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0e \x01(\tH\x0c\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0f \x01(\rH\r\x88\x01\x01\x12\x37\n\x08inflight\x18\x10 \x03(\x0b\x32%.cln.ListpeerchannelsChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x11 \x01(\x0cH\x0e\x88\x01\x01\x12\x14\n\x07private\x18\x12 \x01(\x08H\x0f\x88\x01\x01\x12%\n\x06opener\x18\x13 \x01(\x0e\x32\x10.cln.ChannelSideH\x10\x88\x01\x01\x12%\n\x06\x63loser\x18\x14 \x01(\x0e\x32\x10.cln.ChannelSideH\x11\x88\x01\x01\x12:\n\x07\x66unding\x18\x16 \x01(\x0b\x32$.cln.ListpeerchannelsChannelsFundingH\x12\x88\x01\x01\x12$\n\nto_us_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x19 \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12$\n\ntotal_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x1c \x01(\rH\x18\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12*\n\x10our_reserve_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x1c\x88\x01\x01\x12(\n\x0espendable_msat\x18! \x01(\x0b\x32\x0b.cln.AmountH\x1d\x88\x01\x01\x12)\n\x0freceivable_msat\x18\" \x01(\x0b\x32\x0b.cln.AmountH\x1e\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18# \x01(\x0b\x32\x0b.cln.AmountH\x1f\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18$ \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18% \x01(\x0b\x32\x0b.cln.AmountH!\x88\x01\x01\x12 \n\x13their_to_self_delay\x18& \x01(\rH\"\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\' \x01(\rH#\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18( \x01(\rH$\x88\x01\x01\x12\x36\n\x05\x61lias\x18) \x01(\x0b\x32\".cln.ListpeerchannelsChannelsAliasH%\x88\x01\x01\x12\x0e\n\x06status\x18+ \x03(\t\x12 \n\x13in_payments_offered\x18, \x01(\x04H&\x88\x01\x01\x12)\n\x0fin_offered_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH\'\x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18. \x01(\x04H(\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18/ \x01(\x0b\x32\x0b.cln.AmountH)\x88\x01\x01\x12!\n\x14out_payments_offered\x18\x30 \x01(\x04H*\x88\x01\x01\x12*\n\x10out_offered_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH+\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18\x32 \x01(\x04H,\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18\x33 \x01(\x0b\x32\x0b.cln.AmountH-\x88\x01\x01\x12\x31\n\x05htlcs\x18\x34 \x03(\x0b\x32\".cln.ListpeerchannelsChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18\x35 \x01(\tH.\x88\x01\x01\"\xa3\x02\n\x1dListpeerchannelsChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\n\n\x08_peer_idB\x11\n\x0f_peer_connectedB\x08\n\x06_stateB\x0f\n\r_scratch_txidB\n\n\x08_feerateB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_openerB\t\n\x07_closerB\n\n\x08_fundingB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x08\n\x06_aliasB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"]\n\x1fListpeerchannelsChannelsFeerate\x12\x12\n\x05perkw\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05perkb\x18\x02 \x01(\rH\x01\x88\x01\x01\x42\x08\n\x06_perkwB\x08\n\x06_perkb\"\xd2\x02\n ListpeerchannelsChannelsInflight\x12\x19\n\x0c\x66unding_txid\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x03 \x01(\tH\x02\x88\x01\x01\x12,\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12*\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x19\n\x0cscratch_txid\x18\x06 \x01(\x0cH\x05\x88\x01\x01\x42\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\n\n\x08_feerateB\x15\n\x13_total_funding_msatB\x13\n\x11_our_funding_msatB\x0f\n\r_scratch_txid\"\xd2\x02\n\x1fListpeerchannelsChannelsFunding\x12%\n\x0bpushed_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12*\n\x10local_funds_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12+\n\x11remote_funds_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\'\n\rfee_paid_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\x0e\n\x0c_pushed_msatB\x13\n\x11_local_funds_msatB\x14\n\x12_remote_funds_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"]\n\x1dListpeerchannelsChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xe2\x03\n\x1dListpeerchannelsChannelsHtlcs\x12\x61\n\tdirection\x18\x01 \x01(\x0e\x32I.cln.ListpeerchannelsChannelsHtlcs.ListpeerchannelsChannelsHtlcsDirectionH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18\x04 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x05 \x01(\x0cH\x04\x88\x01\x01\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x05\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\"\n\x05state\x18\x08 \x01(\x0e\x32\x0e.cln.HtlcStateH\x07\x88\x01\x01\"9\n&ListpeerchannelsChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x0c\n\n_directionB\x05\n\x03_idB\x0e\n\x0c_amount_msatB\t\n\x07_expiryB\x0f\n\r_payment_hashB\x10\n\x0e_local_trimmedB\t\n\x07_statusB\x08\n\x06_state\"3\n\x19ListclosedchannelsRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"[\n\x1aListclosedchannelsResponse\x12=\n\x0e\x63losedchannels\x18\x01 \x03(\x0b\x32%.cln.ListclosedchannelsClosedchannels\"\xb2\t\n ListclosedchannelsClosedchannels\x12\x14\n\x07peer_id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x01\x88\x01\x01\x12>\n\x05\x61lias\x18\x04 \x01(\x0b\x32*.cln.ListclosedchannelsClosedchannelsAliasH\x02\x88\x01\x01\x12 \n\x06opener\x18\x05 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x06 \x01(\x0e\x32\x10.cln.ChannelSideH\x03\x88\x01\x01\x12\x0f\n\x07private\x18\x07 \x01(\x08\x12\x1f\n\x17total_local_commitments\x18\t \x01(\x04\x12 \n\x18total_remote_commitments\x18\n \x01(\x04\x12\x18\n\x10total_htlcs_sent\x18\x0b \x01(\x04\x12\x14\n\x0c\x66unding_txid\x18\x0c \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\r \x01(\r\x12\x0e\n\x06leased\x18\x0e \x01(\x08\x12/\n\x15\x66unding_fee_paid_msat\x18\x0f \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12/\n\x15\x66unding_fee_rcvd_msat\x18\x10 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12-\n\x13\x66unding_pushed_msat\x18\x11 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1f\n\ntotal_msat\x18\x12 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x66inal_to_us_msat\x18\x13 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emin_to_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x0emax_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.Amount\x12!\n\x14last_commitment_txid\x18\x16 \x01(\x0cH\x07\x88\x01\x01\x12\x32\n\x18last_commitment_fee_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x66\n\x0b\x63lose_cause\x18\x18 \x01(\x0e\x32Q.cln.ListclosedchannelsClosedchannels.ListclosedchannelsClosedchannelsClose_cause\"v\n+ListclosedchannelsClosedchannelsClose_cause\x12\x0b\n\x07UNKNOWN\x10\x00\x12\t\n\x05LOCAL\x10\x01\x12\x08\n\x04USER\x10\x02\x12\n\n\x06REMOTE\x10\x03\x12\x0c\n\x08PROTOCOL\x10\x04\x12\x0b\n\x07ONCHAIN\x10\x05\x42\n\n\x08_peer_idB\x13\n\x11_short_channel_idB\x08\n\x06_aliasB\t\n\x07_closerB\x18\n\x16_funding_fee_paid_msatB\x18\n\x16_funding_fee_rcvd_msatB\x16\n\x14_funding_pushed_msatB\x17\n\x15_last_commitment_txidB\x1b\n\x19_last_commitment_fee_msat\"e\n%ListclosedchannelsClosedchannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"L\n\x10\x44\x65\x63odepayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_description\"\x8d\x04\n\x11\x44\x65\x63odepayResponse\x12\x10\n\x08\x63urrency\x18\x01 \x01(\t\x12\x12\n\ncreated_at\x18\x02 \x01(\x04\x12\x0e\n\x06\x65xpiry\x18\x03 \x01(\x04\x12\r\n\x05payee\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x11\n\tsignature\x18\x07 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18\t \x01(\x0cH\x02\x88\x01\x01\x12\x1d\n\x15min_final_cltv_expiry\x18\n \x01(\r\x12\x1b\n\x0epayment_secret\x18\x0b \x01(\x0cH\x03\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x0c \x01(\x0cH\x04\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\r \x01(\x0cH\x05\x88\x01\x01\x12*\n\tfallbacks\x18\x0e \x03(\x0b\x32\x17.cln.DecodepayFallbacks\x12\"\n\x05\x65xtra\x18\x10 \x03(\x0b\x32\x13.cln.DecodepayExtraB\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x13\n\x11_description_hashB\x11\n\x0f_payment_secretB\x0b\n\t_featuresB\x13\n\x11_payment_metadata\"\xc6\x01\n\x12\x44\x65\x63odepayFallbacks\x12\x41\n\titem_type\x18\x01 \x01(\x0e\x32..cln.DecodepayFallbacks.DecodepayFallbacksType\x12\x11\n\x04\x61\x64\x64r\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0b\n\x03hex\x18\x03 \x01(\x0c\"D\n\x16\x44\x65\x63odepayFallbacksType\x12\t\n\x05P2PKH\x10\x00\x12\x08\n\x04P2SH\x10\x01\x12\n\n\x06P2WPKH\x10\x02\x12\t\n\x05P2WSH\x10\x03\x42\x07\n\x05_addr\"+\n\x0e\x44\x65\x63odepayExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\"\x1f\n\rDecodeRequest\x12\x0e\n\x06string\x18\x01 \x01(\t\"\xaa!\n\x0e\x44\x65\x63odeResponse\x12\x31\n\titem_type\x18\x01 \x01(\x0e\x32\x1e.cln.DecodeResponse.DecodeType\x12\r\n\x05valid\x18\x02 \x01(\x08\x12\x15\n\x08offer_id\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0coffer_chains\x18\x04 \x03(\x0c\x12\x1b\n\x0eoffer_metadata\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x1b\n\x0eoffer_currency\x18\x06 \x01(\tH\x02\x88\x01\x01\x12+\n\x1ewarning_unknown_offer_currency\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x63urrency_minor_unit\x18\x08 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0coffer_amount\x18\t \x01(\x04H\x05\x88\x01\x01\x12+\n\x11offer_amount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x1e\n\x11offer_description\x18\x0b \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0coffer_issuer\x18\x0c \x01(\tH\x08\x88\x01\x01\x12\x1b\n\x0eoffer_features\x18\r \x01(\x0cH\t\x88\x01\x01\x12\"\n\x15offer_absolute_expiry\x18\x0e \x01(\x04H\n\x88\x01\x01\x12\x1f\n\x12offer_quantity_max\x18\x0f \x01(\x04H\x0b\x88\x01\x01\x12+\n\x0boffer_paths\x18\x10 \x03(\x0b\x32\x16.cln.DecodeOffer_paths\x12\x1a\n\roffer_node_id\x18\x11 \x01(\x0cH\x0c\x88\x01\x01\x12*\n\x1dwarning_missing_offer_node_id\x18\x14 \x01(\tH\r\x88\x01\x01\x12.\n!warning_invalid_offer_description\x18\x15 \x01(\tH\x0e\x88\x01\x01\x12.\n!warning_missing_offer_description\x18\x16 \x01(\tH\x0f\x88\x01\x01\x12+\n\x1ewarning_invalid_offer_currency\x18\x17 \x01(\tH\x10\x88\x01\x01\x12)\n\x1cwarning_invalid_offer_issuer\x18\x18 \x01(\tH\x11\x88\x01\x01\x12\x1c\n\x0finvreq_metadata\x18\x19 \x01(\x0cH\x12\x88\x01\x01\x12\x1c\n\x0finvreq_payer_id\x18\x1a \x01(\x0cH\x13\x88\x01\x01\x12\x19\n\x0cinvreq_chain\x18\x1b \x01(\x0cH\x14\x88\x01\x01\x12,\n\x12invreq_amount_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12\x1c\n\x0finvreq_features\x18\x1d \x01(\x0cH\x16\x88\x01\x01\x12\x1c\n\x0finvreq_quantity\x18\x1e \x01(\x04H\x17\x88\x01\x01\x12\x1e\n\x11invreq_payer_note\x18\x1f \x01(\tH\x18\x88\x01\x01\x12&\n\x19invreq_recurrence_counter\x18 \x01(\rH\x19\x88\x01\x01\x12$\n\x17invreq_recurrence_start\x18! \x01(\rH\x1a\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_metadata\x18# \x01(\tH\x1b\x88\x01\x01\x12,\n\x1fwarning_missing_invreq_payer_id\x18$ \x01(\tH\x1c\x88\x01\x01\x12.\n!warning_invalid_invreq_payer_note\x18% \x01(\tH\x1d\x88\x01\x01\x12\x36\n)warning_missing_invoice_request_signature\x18& \x01(\tH\x1e\x88\x01\x01\x12\x36\n)warning_invalid_invoice_request_signature\x18\' \x01(\tH\x1f\x88\x01\x01\x12\x1f\n\x12invoice_created_at\x18) \x01(\x04H \x88\x01\x01\x12$\n\x17invoice_relative_expiry\x18* \x01(\rH!\x88\x01\x01\x12!\n\x14invoice_payment_hash\x18+ \x01(\x0cH\"\x88\x01\x01\x12-\n\x13invoice_amount_msat\x18, \x01(\x0b\x32\x0b.cln.AmountH#\x88\x01\x01\x12\x37\n\x11invoice_fallbacks\x18- \x03(\x0b\x32\x1c.cln.DecodeInvoice_fallbacks\x12\x1d\n\x10invoice_features\x18. \x01(\x0cH$\x88\x01\x01\x12\x1c\n\x0finvoice_node_id\x18/ \x01(\x0cH%\x88\x01\x01\x12(\n\x1binvoice_recurrence_basetime\x18\x30 \x01(\x04H&\x88\x01\x01\x12*\n\x1dwarning_missing_invoice_paths\x18\x32 \x01(\tH\'\x88\x01\x01\x12/\n\"warning_missing_invoice_blindedpay\x18\x33 \x01(\tH(\x88\x01\x01\x12/\n\"warning_missing_invoice_created_at\x18\x34 \x01(\tH)\x88\x01\x01\x12\x31\n$warning_missing_invoice_payment_hash\x18\x35 \x01(\tH*\x88\x01\x01\x12+\n\x1ewarning_missing_invoice_amount\x18\x36 \x01(\tH+\x88\x01\x01\x12\x38\n+warning_missing_invoice_recurrence_basetime\x18\x37 \x01(\tH,\x88\x01\x01\x12,\n\x1fwarning_missing_invoice_node_id\x18\x38 \x01(\tH-\x88\x01\x01\x12.\n!warning_missing_invoice_signature\x18\x39 \x01(\tH.\x88\x01\x01\x12.\n!warning_invalid_invoice_signature\x18: \x01(\tH/\x88\x01\x01\x12\'\n\tfallbacks\x18; \x03(\x0b\x32\x14.cln.DecodeFallbacks\x12\x17\n\ncreated_at\x18< \x01(\x04H0\x88\x01\x01\x12\x13\n\x06\x65xpiry\x18= \x01(\x04H1\x88\x01\x01\x12\x12\n\x05payee\x18> \x01(\x0cH2\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18? \x01(\x0cH3\x88\x01\x01\x12\x1d\n\x10\x64\x65scription_hash\x18@ \x01(\x0cH4\x88\x01\x01\x12\"\n\x15min_final_cltv_expiry\x18\x41 \x01(\rH5\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x42 \x01(\x0cH6\x88\x01\x01\x12\x1d\n\x10payment_metadata\x18\x43 \x01(\x0cH7\x88\x01\x01\x12\x1f\n\x05\x65xtra\x18\x45 \x03(\x0b\x32\x10.cln.DecodeExtra\x12\x16\n\tunique_id\x18\x46 \x01(\tH8\x88\x01\x01\x12\x14\n\x07version\x18G \x01(\tH9\x88\x01\x01\x12\x13\n\x06string\x18H \x01(\tH:\x88\x01\x01\x12-\n\x0crestrictions\x18I \x03(\x0b\x32\x17.cln.DecodeRestrictions\x12&\n\x19warning_rune_invalid_utf8\x18J \x01(\tH;\x88\x01\x01\x12\x10\n\x03hex\x18K \x01(\x0cH<\x88\x01\x01\"l\n\nDecodeType\x12\x10\n\x0c\x42OLT12_OFFER\x10\x00\x12\x12\n\x0e\x42OLT12_INVOICE\x10\x01\x12\x1a\n\x16\x42OLT12_INVOICE_REQUEST\x10\x02\x12\x12\n\x0e\x42OLT11_INVOICE\x10\x03\x12\x08\n\x04RUNE\x10\x04\x42\x0b\n\t_offer_idB\x11\n\x0f_offer_metadataB\x11\n\x0f_offer_currencyB!\n\x1f_warning_unknown_offer_currencyB\x16\n\x14_currency_minor_unitB\x0f\n\r_offer_amountB\x14\n\x12_offer_amount_msatB\x14\n\x12_offer_descriptionB\x0f\n\r_offer_issuerB\x11\n\x0f_offer_featuresB\x18\n\x16_offer_absolute_expiryB\x15\n\x13_offer_quantity_maxB\x10\n\x0e_offer_node_idB \n\x1e_warning_missing_offer_node_idB$\n\"_warning_invalid_offer_descriptionB$\n\"_warning_missing_offer_descriptionB!\n\x1f_warning_invalid_offer_currencyB\x1f\n\x1d_warning_invalid_offer_issuerB\x12\n\x10_invreq_metadataB\x12\n\x10_invreq_payer_idB\x0f\n\r_invreq_chainB\x15\n\x13_invreq_amount_msatB\x12\n\x10_invreq_featuresB\x12\n\x10_invreq_quantityB\x14\n\x12_invreq_payer_noteB\x1c\n\x1a_invreq_recurrence_counterB\x1a\n\x18_invreq_recurrence_startB\"\n _warning_missing_invreq_metadataB\"\n _warning_missing_invreq_payer_idB$\n\"_warning_invalid_invreq_payer_noteB,\n*_warning_missing_invoice_request_signatureB,\n*_warning_invalid_invoice_request_signatureB\x15\n\x13_invoice_created_atB\x1a\n\x18_invoice_relative_expiryB\x17\n\x15_invoice_payment_hashB\x16\n\x14_invoice_amount_msatB\x13\n\x11_invoice_featuresB\x12\n\x10_invoice_node_idB\x1e\n\x1c_invoice_recurrence_basetimeB \n\x1e_warning_missing_invoice_pathsB%\n#_warning_missing_invoice_blindedpayB%\n#_warning_missing_invoice_created_atB\'\n%_warning_missing_invoice_payment_hashB!\n\x1f_warning_missing_invoice_amountB.\n,_warning_missing_invoice_recurrence_basetimeB\"\n _warning_missing_invoice_node_idB$\n\"_warning_missing_invoice_signatureB$\n\"_warning_invalid_invoice_signatureB\r\n\x0b_created_atB\t\n\x07_expiryB\x08\n\x06_payeeB\x0f\n\r_payment_hashB\x13\n\x11_description_hashB\x18\n\x16_min_final_cltv_expiryB\x11\n\x0f_payment_secretB\x13\n\x11_payment_metadataB\x0c\n\n_unique_idB\n\n\x08_versionB\t\n\x07_stringB\x1c\n\x1a_warning_rune_invalid_utf8B\x06\n\x04_hex\"<\n\x11\x44\x65\x63odeOffer_paths\x12\x15\n\rfirst_node_id\x18\x01 \x01(\x0c\x12\x10\n\x08\x62linding\x18\x02 \x01(\x0c\"\x8a\x01\n\x1f\x44\x65\x63odeOffer_recurrencePaywindow\x12\x16\n\x0eseconds_before\x18\x01 \x01(\r\x12\x15\n\rseconds_after\x18\x02 \x01(\r\x12 \n\x13proportional_amount\x18\x03 \x01(\x08H\x00\x88\x01\x01\x42\x16\n\x14_proportional_amount\"T\n\x17\x44\x65\x63odeInvoice_pathsPath\x12\x17\n\x0f\x62linded_node_id\x18\x01 \x01(\x0c\x12 \n\x18\x65ncrypted_recipient_data\x18\x02 \x01(\x0c\"Y\n\x17\x44\x65\x63odeInvoice_fallbacks\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0b\n\x03hex\x18\x02 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_address\"w\n\x0f\x44\x65\x63odeFallbacks\x12\x36\n)warning_invoice_fallbacks_version_invalid\x18\x01 \x01(\tH\x00\x88\x01\x01\x42,\n*_warning_invoice_fallbacks_version_invalid\"(\n\x0b\x44\x65\x63odeExtra\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\";\n\x12\x44\x65\x63odeRestrictions\x12\x14\n\x0c\x61lternatives\x18\x01 \x03(\t\x12\x0f\n\x07summary\x18\x02 \x01(\t\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"\x9c\x02\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x12&\n\x05perkb\x18\x02 \x01(\x0b\x32\x12.cln.FeeratesPerkbH\x01\x88\x01\x01\x12&\n\x05perkw\x18\x03 \x01(\x0b\x32\x12.cln.FeeratesPerkwH\x02\x88\x01\x01\x12\x46\n\x15onchain_fee_estimates\x18\x04 \x01(\x0b\x32\".cln.FeeratesOnchain_fee_estimatesH\x03\x88\x01\x01\x42\x1b\n\x19_warning_missing_feeratesB\x08\n\x06_perkbB\x08\n\x06_perkwB\x18\n\x16_onchain_fee_estimates\"\x91\x03\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkbEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x05\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x06\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkbEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\x91\x03\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x12\n\x05\x66loor\x18\n \x01(\rH\x00\x88\x01\x01\x12.\n\testimates\x18\t \x03(\x0b\x32\x1b.cln.FeeratesPerkwEstimates\x12\x14\n\x07opening\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x02\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x03\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x04\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x05\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x06\x88\x01\x01\x42\x08\n\x06_floorB\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\x96\x01\n\x16\x46\x65\x65ratesPerkwEstimates\x12\x17\n\nblockcount\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07\x66\x65\x65rate\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10smoothed_feerate\x18\x03 \x01(\rH\x02\x88\x01\x01\x42\r\n\x0b_blockcountB\n\n\x08_feerateB\x13\n\x11_smoothed_feerate\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"4\n\x14SendcustommsgRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\x0c\x12\x0b\n\x03msg\x18\x02 \x01(\x0c\"\'\n\x15SendcustommsgResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\"\xf8\x01\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelay\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\x94\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"\'\n\x12SigninvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\"%\n\x13SigninvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xde\x1a\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12Q\n\x10ListPeerChannels\x12\x1c.cln.ListpeerchannelsRequest\x1a\x1d.cln.ListpeerchannelsResponse\"\x00\x12W\n\x12ListClosedChannels\x12\x1e.cln.ListclosedchannelsRequest\x1a\x1f.cln.ListclosedchannelsResponse\"\x00\x12<\n\tDecodePay\x12\x15.cln.DecodepayRequest\x1a\x16.cln.DecodepayResponse\"\x00\x12\x33\n\x06\x44\x65\x63ode\x12\x12.cln.DecodeRequest\x1a\x13.cln.DecodeResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12H\n\rSendCustomMsg\x12\x19.cln.SendcustommsgRequest\x1a\x1a.cln.SendcustommsgResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignInvoice\x12\x17.cln.SigninvoiceRequest\x1a\x18.cln.SigninvoiceResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') - - - -_GETINFOREQUEST = DESCRIPTOR.message_types_by_name['GetinfoRequest'] -_GETINFORESPONSE = DESCRIPTOR.message_types_by_name['GetinfoResponse'] -_GETINFOOUR_FEATURES = DESCRIPTOR.message_types_by_name['GetinfoOur_features'] -_GETINFOADDRESS = DESCRIPTOR.message_types_by_name['GetinfoAddress'] -_GETINFOBINDING = DESCRIPTOR.message_types_by_name['GetinfoBinding'] -_LISTPEERSREQUEST = DESCRIPTOR.message_types_by_name['ListpeersRequest'] -_LISTPEERSRESPONSE = DESCRIPTOR.message_types_by_name['ListpeersResponse'] -_LISTPEERSPEERS = DESCRIPTOR.message_types_by_name['ListpeersPeers'] -_LISTPEERSPEERSLOG = DESCRIPTOR.message_types_by_name['ListpeersPeersLog'] -_LISTPEERSPEERSCHANNELS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannels'] -_LISTPEERSPEERSCHANNELSFEERATE = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFeerate'] -_LISTPEERSPEERSCHANNELSINFLIGHT = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsInflight'] -_LISTPEERSPEERSCHANNELSFUNDING = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFunding'] -_LISTPEERSPEERSCHANNELSALIAS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsAlias'] -_LISTPEERSPEERSCHANNELSHTLCS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsHtlcs'] -_LISTFUNDSREQUEST = DESCRIPTOR.message_types_by_name['ListfundsRequest'] -_LISTFUNDSRESPONSE = DESCRIPTOR.message_types_by_name['ListfundsResponse'] -_LISTFUNDSOUTPUTS = DESCRIPTOR.message_types_by_name['ListfundsOutputs'] -_LISTFUNDSCHANNELS = DESCRIPTOR.message_types_by_name['ListfundsChannels'] -_SENDPAYREQUEST = DESCRIPTOR.message_types_by_name['SendpayRequest'] -_SENDPAYRESPONSE = DESCRIPTOR.message_types_by_name['SendpayResponse'] -_SENDPAYROUTE = DESCRIPTOR.message_types_by_name['SendpayRoute'] -_LISTCHANNELSREQUEST = DESCRIPTOR.message_types_by_name['ListchannelsRequest'] -_LISTCHANNELSRESPONSE = DESCRIPTOR.message_types_by_name['ListchannelsResponse'] -_LISTCHANNELSCHANNELS = DESCRIPTOR.message_types_by_name['ListchannelsChannels'] -_ADDGOSSIPREQUEST = DESCRIPTOR.message_types_by_name['AddgossipRequest'] -_ADDGOSSIPRESPONSE = DESCRIPTOR.message_types_by_name['AddgossipResponse'] -_AUTOCLEANINVOICEREQUEST = DESCRIPTOR.message_types_by_name['AutocleaninvoiceRequest'] -_AUTOCLEANINVOICERESPONSE = DESCRIPTOR.message_types_by_name['AutocleaninvoiceResponse'] -_CHECKMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['CheckmessageRequest'] -_CHECKMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['CheckmessageResponse'] -_CLOSEREQUEST = DESCRIPTOR.message_types_by_name['CloseRequest'] -_CLOSERESPONSE = DESCRIPTOR.message_types_by_name['CloseResponse'] -_CONNECTREQUEST = DESCRIPTOR.message_types_by_name['ConnectRequest'] -_CONNECTRESPONSE = DESCRIPTOR.message_types_by_name['ConnectResponse'] -_CONNECTADDRESS = DESCRIPTOR.message_types_by_name['ConnectAddress'] -_CREATEINVOICEREQUEST = DESCRIPTOR.message_types_by_name['CreateinvoiceRequest'] -_CREATEINVOICERESPONSE = DESCRIPTOR.message_types_by_name['CreateinvoiceResponse'] -_DATASTOREREQUEST = DESCRIPTOR.message_types_by_name['DatastoreRequest'] -_DATASTORERESPONSE = DESCRIPTOR.message_types_by_name['DatastoreResponse'] -_CREATEONIONREQUEST = DESCRIPTOR.message_types_by_name['CreateonionRequest'] -_CREATEONIONRESPONSE = DESCRIPTOR.message_types_by_name['CreateonionResponse'] -_CREATEONIONHOPS = DESCRIPTOR.message_types_by_name['CreateonionHops'] -_DELDATASTOREREQUEST = DESCRIPTOR.message_types_by_name['DeldatastoreRequest'] -_DELDATASTORERESPONSE = DESCRIPTOR.message_types_by_name['DeldatastoreResponse'] -_DELEXPIREDINVOICEREQUEST = DESCRIPTOR.message_types_by_name['DelexpiredinvoiceRequest'] -_DELEXPIREDINVOICERESPONSE = DESCRIPTOR.message_types_by_name['DelexpiredinvoiceResponse'] -_DELINVOICEREQUEST = DESCRIPTOR.message_types_by_name['DelinvoiceRequest'] -_DELINVOICERESPONSE = DESCRIPTOR.message_types_by_name['DelinvoiceResponse'] -_INVOICEREQUEST = DESCRIPTOR.message_types_by_name['InvoiceRequest'] -_INVOICERESPONSE = DESCRIPTOR.message_types_by_name['InvoiceResponse'] -_LISTDATASTOREREQUEST = DESCRIPTOR.message_types_by_name['ListdatastoreRequest'] -_LISTDATASTORERESPONSE = DESCRIPTOR.message_types_by_name['ListdatastoreResponse'] -_LISTDATASTOREDATASTORE = DESCRIPTOR.message_types_by_name['ListdatastoreDatastore'] -_LISTINVOICESREQUEST = DESCRIPTOR.message_types_by_name['ListinvoicesRequest'] -_LISTINVOICESRESPONSE = DESCRIPTOR.message_types_by_name['ListinvoicesResponse'] -_LISTINVOICESINVOICES = DESCRIPTOR.message_types_by_name['ListinvoicesInvoices'] -_SENDONIONREQUEST = DESCRIPTOR.message_types_by_name['SendonionRequest'] -_SENDONIONRESPONSE = DESCRIPTOR.message_types_by_name['SendonionResponse'] -_SENDONIONFIRST_HOP = DESCRIPTOR.message_types_by_name['SendonionFirst_hop'] -_LISTSENDPAYSREQUEST = DESCRIPTOR.message_types_by_name['ListsendpaysRequest'] -_LISTSENDPAYSRESPONSE = DESCRIPTOR.message_types_by_name['ListsendpaysResponse'] -_LISTSENDPAYSPAYMENTS = DESCRIPTOR.message_types_by_name['ListsendpaysPayments'] -_LISTTRANSACTIONSREQUEST = DESCRIPTOR.message_types_by_name['ListtransactionsRequest'] -_LISTTRANSACTIONSRESPONSE = DESCRIPTOR.message_types_by_name['ListtransactionsResponse'] -_LISTTRANSACTIONSTRANSACTIONS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactions'] -_LISTTRANSACTIONSTRANSACTIONSINPUTS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactionsInputs'] -_LISTTRANSACTIONSTRANSACTIONSOUTPUTS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactionsOutputs'] -_PAYREQUEST = DESCRIPTOR.message_types_by_name['PayRequest'] -_PAYRESPONSE = DESCRIPTOR.message_types_by_name['PayResponse'] -_LISTNODESREQUEST = DESCRIPTOR.message_types_by_name['ListnodesRequest'] -_LISTNODESRESPONSE = DESCRIPTOR.message_types_by_name['ListnodesResponse'] -_LISTNODESNODES = DESCRIPTOR.message_types_by_name['ListnodesNodes'] -_LISTNODESNODESADDRESSES = DESCRIPTOR.message_types_by_name['ListnodesNodesAddresses'] -_WAITANYINVOICEREQUEST = DESCRIPTOR.message_types_by_name['WaitanyinvoiceRequest'] -_WAITANYINVOICERESPONSE = DESCRIPTOR.message_types_by_name['WaitanyinvoiceResponse'] -_WAITINVOICEREQUEST = DESCRIPTOR.message_types_by_name['WaitinvoiceRequest'] -_WAITINVOICERESPONSE = DESCRIPTOR.message_types_by_name['WaitinvoiceResponse'] -_WAITSENDPAYREQUEST = DESCRIPTOR.message_types_by_name['WaitsendpayRequest'] -_WAITSENDPAYRESPONSE = DESCRIPTOR.message_types_by_name['WaitsendpayResponse'] -_NEWADDRREQUEST = DESCRIPTOR.message_types_by_name['NewaddrRequest'] -_NEWADDRRESPONSE = DESCRIPTOR.message_types_by_name['NewaddrResponse'] -_WITHDRAWREQUEST = DESCRIPTOR.message_types_by_name['WithdrawRequest'] -_WITHDRAWRESPONSE = DESCRIPTOR.message_types_by_name['WithdrawResponse'] -_KEYSENDREQUEST = DESCRIPTOR.message_types_by_name['KeysendRequest'] -_KEYSENDRESPONSE = DESCRIPTOR.message_types_by_name['KeysendResponse'] -_FUNDPSBTREQUEST = DESCRIPTOR.message_types_by_name['FundpsbtRequest'] -_FUNDPSBTRESPONSE = DESCRIPTOR.message_types_by_name['FundpsbtResponse'] -_FUNDPSBTRESERVATIONS = DESCRIPTOR.message_types_by_name['FundpsbtReservations'] -_SENDPSBTREQUEST = DESCRIPTOR.message_types_by_name['SendpsbtRequest'] -_SENDPSBTRESPONSE = DESCRIPTOR.message_types_by_name['SendpsbtResponse'] -_SIGNPSBTREQUEST = DESCRIPTOR.message_types_by_name['SignpsbtRequest'] -_SIGNPSBTRESPONSE = DESCRIPTOR.message_types_by_name['SignpsbtResponse'] -_UTXOPSBTREQUEST = DESCRIPTOR.message_types_by_name['UtxopsbtRequest'] -_UTXOPSBTRESPONSE = DESCRIPTOR.message_types_by_name['UtxopsbtResponse'] -_UTXOPSBTRESERVATIONS = DESCRIPTOR.message_types_by_name['UtxopsbtReservations'] -_TXDISCARDREQUEST = DESCRIPTOR.message_types_by_name['TxdiscardRequest'] -_TXDISCARDRESPONSE = DESCRIPTOR.message_types_by_name['TxdiscardResponse'] -_TXPREPAREREQUEST = DESCRIPTOR.message_types_by_name['TxprepareRequest'] -_TXPREPARERESPONSE = DESCRIPTOR.message_types_by_name['TxprepareResponse'] -_TXSENDREQUEST = DESCRIPTOR.message_types_by_name['TxsendRequest'] -_TXSENDRESPONSE = DESCRIPTOR.message_types_by_name['TxsendResponse'] -_LISTPEERCHANNELSREQUEST = DESCRIPTOR.message_types_by_name['ListpeerchannelsRequest'] -_LISTPEERCHANNELSRESPONSE = DESCRIPTOR.message_types_by_name['ListpeerchannelsResponse'] -_LISTPEERCHANNELSCHANNELS = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannels'] -_LISTPEERCHANNELSCHANNELSFEERATE = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannelsFeerate'] -_LISTPEERCHANNELSCHANNELSINFLIGHT = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannelsInflight'] -_LISTPEERCHANNELSCHANNELSFUNDING = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannelsFunding'] -_LISTPEERCHANNELSCHANNELSALIAS = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannelsAlias'] -_LISTPEERCHANNELSCHANNELSHTLCS = DESCRIPTOR.message_types_by_name['ListpeerchannelsChannelsHtlcs'] -_LISTCLOSEDCHANNELSREQUEST = DESCRIPTOR.message_types_by_name['ListclosedchannelsRequest'] -_LISTCLOSEDCHANNELSRESPONSE = DESCRIPTOR.message_types_by_name['ListclosedchannelsResponse'] -_LISTCLOSEDCHANNELSCLOSEDCHANNELS = DESCRIPTOR.message_types_by_name['ListclosedchannelsClosedchannels'] -_LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS = DESCRIPTOR.message_types_by_name['ListclosedchannelsClosedchannelsAlias'] -_DECODEPAYREQUEST = DESCRIPTOR.message_types_by_name['DecodepayRequest'] -_DECODEPAYRESPONSE = DESCRIPTOR.message_types_by_name['DecodepayResponse'] -_DECODEPAYFALLBACKS = DESCRIPTOR.message_types_by_name['DecodepayFallbacks'] -_DECODEPAYEXTRA = DESCRIPTOR.message_types_by_name['DecodepayExtra'] -_DECODEREQUEST = DESCRIPTOR.message_types_by_name['DecodeRequest'] -_DECODERESPONSE = DESCRIPTOR.message_types_by_name['DecodeResponse'] -_DECODEOFFER_PATHS = DESCRIPTOR.message_types_by_name['DecodeOffer_paths'] -_DECODEOFFER_RECURRENCEPAYWINDOW = DESCRIPTOR.message_types_by_name['DecodeOffer_recurrencePaywindow'] -_DECODEINVOICE_PATHSPATH = DESCRIPTOR.message_types_by_name['DecodeInvoice_pathsPath'] -_DECODEINVOICE_FALLBACKS = DESCRIPTOR.message_types_by_name['DecodeInvoice_fallbacks'] -_DECODEFALLBACKS = DESCRIPTOR.message_types_by_name['DecodeFallbacks'] -_DECODEEXTRA = DESCRIPTOR.message_types_by_name['DecodeExtra'] -_DECODERESTRICTIONS = DESCRIPTOR.message_types_by_name['DecodeRestrictions'] -_DISCONNECTREQUEST = DESCRIPTOR.message_types_by_name['DisconnectRequest'] -_DISCONNECTRESPONSE = DESCRIPTOR.message_types_by_name['DisconnectResponse'] -_FEERATESREQUEST = DESCRIPTOR.message_types_by_name['FeeratesRequest'] -_FEERATESRESPONSE = DESCRIPTOR.message_types_by_name['FeeratesResponse'] -_FEERATESPERKB = DESCRIPTOR.message_types_by_name['FeeratesPerkb'] -_FEERATESPERKBESTIMATES = DESCRIPTOR.message_types_by_name['FeeratesPerkbEstimates'] -_FEERATESPERKW = DESCRIPTOR.message_types_by_name['FeeratesPerkw'] -_FEERATESPERKWESTIMATES = DESCRIPTOR.message_types_by_name['FeeratesPerkwEstimates'] -_FEERATESONCHAIN_FEE_ESTIMATES = DESCRIPTOR.message_types_by_name['FeeratesOnchain_fee_estimates'] -_FUNDCHANNELREQUEST = DESCRIPTOR.message_types_by_name['FundchannelRequest'] -_FUNDCHANNELRESPONSE = DESCRIPTOR.message_types_by_name['FundchannelResponse'] -_GETROUTEREQUEST = DESCRIPTOR.message_types_by_name['GetrouteRequest'] -_GETROUTERESPONSE = DESCRIPTOR.message_types_by_name['GetrouteResponse'] -_GETROUTEROUTE = DESCRIPTOR.message_types_by_name['GetrouteRoute'] -_LISTFORWARDSREQUEST = DESCRIPTOR.message_types_by_name['ListforwardsRequest'] -_LISTFORWARDSRESPONSE = DESCRIPTOR.message_types_by_name['ListforwardsResponse'] -_LISTFORWARDSFORWARDS = DESCRIPTOR.message_types_by_name['ListforwardsForwards'] -_LISTPAYSREQUEST = DESCRIPTOR.message_types_by_name['ListpaysRequest'] -_LISTPAYSRESPONSE = DESCRIPTOR.message_types_by_name['ListpaysResponse'] -_LISTPAYSPAYS = DESCRIPTOR.message_types_by_name['ListpaysPays'] -_PINGREQUEST = DESCRIPTOR.message_types_by_name['PingRequest'] -_PINGRESPONSE = DESCRIPTOR.message_types_by_name['PingResponse'] -_SENDCUSTOMMSGREQUEST = DESCRIPTOR.message_types_by_name['SendcustommsgRequest'] -_SENDCUSTOMMSGRESPONSE = DESCRIPTOR.message_types_by_name['SendcustommsgResponse'] -_SETCHANNELREQUEST = DESCRIPTOR.message_types_by_name['SetchannelRequest'] -_SETCHANNELRESPONSE = DESCRIPTOR.message_types_by_name['SetchannelResponse'] -_SETCHANNELCHANNELS = DESCRIPTOR.message_types_by_name['SetchannelChannels'] -_SIGNINVOICEREQUEST = DESCRIPTOR.message_types_by_name['SigninvoiceRequest'] -_SIGNINVOICERESPONSE = DESCRIPTOR.message_types_by_name['SigninvoiceResponse'] -_SIGNMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['SignmessageRequest'] -_SIGNMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['SignmessageResponse'] -_STOPREQUEST = DESCRIPTOR.message_types_by_name['StopRequest'] -_STOPRESPONSE = DESCRIPTOR.message_types_by_name['StopResponse'] -_GETINFOADDRESS_GETINFOADDRESSTYPE = _GETINFOADDRESS.enum_types_by_name['GetinfoAddressType'] -_GETINFOBINDING_GETINFOBINDINGTYPE = _GETINFOBINDING.enum_types_by_name['GetinfoBindingType'] -_LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE = _LISTPEERSPEERSLOG.enum_types_by_name['ListpeersPeersLogType'] -_LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE = _LISTPEERSPEERSCHANNELS.enum_types_by_name['ListpeersPeersChannelsState'] -_LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION = _LISTPEERSPEERSCHANNELSHTLCS.enum_types_by_name['ListpeersPeersChannelsHtlcsDirection'] -_LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS = _LISTFUNDSOUTPUTS.enum_types_by_name['ListfundsOutputsStatus'] -_SENDPAYRESPONSE_SENDPAYSTATUS = _SENDPAYRESPONSE.enum_types_by_name['SendpayStatus'] -_CLOSERESPONSE_CLOSETYPE = _CLOSERESPONSE.enum_types_by_name['CloseType'] -_CONNECTRESPONSE_CONNECTDIRECTION = _CONNECTRESPONSE.enum_types_by_name['ConnectDirection'] -_CONNECTADDRESS_CONNECTADDRESSTYPE = _CONNECTADDRESS.enum_types_by_name['ConnectAddressType'] -_CREATEINVOICERESPONSE_CREATEINVOICESTATUS = _CREATEINVOICERESPONSE.enum_types_by_name['CreateinvoiceStatus'] -_DATASTOREREQUEST_DATASTOREMODE = _DATASTOREREQUEST.enum_types_by_name['DatastoreMode'] -_DELINVOICEREQUEST_DELINVOICESTATUS = _DELINVOICEREQUEST.enum_types_by_name['DelinvoiceStatus'] -_DELINVOICERESPONSE_DELINVOICESTATUS = _DELINVOICERESPONSE.enum_types_by_name['DelinvoiceStatus'] -_LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS = _LISTINVOICESINVOICES.enum_types_by_name['ListinvoicesInvoicesStatus'] -_SENDONIONRESPONSE_SENDONIONSTATUS = _SENDONIONRESPONSE.enum_types_by_name['SendonionStatus'] -_LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS = _LISTSENDPAYSREQUEST.enum_types_by_name['ListsendpaysStatus'] -_LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS = _LISTSENDPAYSPAYMENTS.enum_types_by_name['ListsendpaysPaymentsStatus'] -_LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE = _LISTTRANSACTIONSTRANSACTIONSINPUTS.enum_types_by_name['ListtransactionsTransactionsInputsType'] -_LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE = _LISTTRANSACTIONSTRANSACTIONSOUTPUTS.enum_types_by_name['ListtransactionsTransactionsOutputsType'] -_PAYRESPONSE_PAYSTATUS = _PAYRESPONSE.enum_types_by_name['PayStatus'] -_LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE = _LISTNODESNODESADDRESSES.enum_types_by_name['ListnodesNodesAddressesType'] -_WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS = _WAITANYINVOICERESPONSE.enum_types_by_name['WaitanyinvoiceStatus'] -_WAITINVOICERESPONSE_WAITINVOICESTATUS = _WAITINVOICERESPONSE.enum_types_by_name['WaitinvoiceStatus'] -_WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS = _WAITSENDPAYRESPONSE.enum_types_by_name['WaitsendpayStatus'] -_NEWADDRREQUEST_NEWADDRADDRESSTYPE = _NEWADDRREQUEST.enum_types_by_name['NewaddrAddresstype'] -_KEYSENDRESPONSE_KEYSENDSTATUS = _KEYSENDRESPONSE.enum_types_by_name['KeysendStatus'] -_LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE = _LISTPEERCHANNELSCHANNELS.enum_types_by_name['ListpeerchannelsChannelsState'] -_LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION = _LISTPEERCHANNELSCHANNELSHTLCS.enum_types_by_name['ListpeerchannelsChannelsHtlcsDirection'] -_LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE = _LISTCLOSEDCHANNELSCLOSEDCHANNELS.enum_types_by_name['ListclosedchannelsClosedchannelsClose_cause'] -_DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE = _DECODEPAYFALLBACKS.enum_types_by_name['DecodepayFallbacksType'] -_DECODERESPONSE_DECODETYPE = _DECODERESPONSE.enum_types_by_name['DecodeType'] -_FEERATESREQUEST_FEERATESSTYLE = _FEERATESREQUEST.enum_types_by_name['FeeratesStyle'] -_GETROUTEROUTE_GETROUTEROUTESTYLE = _GETROUTEROUTE.enum_types_by_name['GetrouteRouteStyle'] -_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS = _LISTFORWARDSREQUEST.enum_types_by_name['ListforwardsStatus'] -_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS = _LISTFORWARDSFORWARDS.enum_types_by_name['ListforwardsForwardsStatus'] -_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE = _LISTFORWARDSFORWARDS.enum_types_by_name['ListforwardsForwardsStyle'] -_LISTPAYSREQUEST_LISTPAYSSTATUS = _LISTPAYSREQUEST.enum_types_by_name['ListpaysStatus'] -_LISTPAYSPAYS_LISTPAYSPAYSSTATUS = _LISTPAYSPAYS.enum_types_by_name['ListpaysPaysStatus'] -GetinfoRequest = _reflection.GeneratedProtocolMessageType('GetinfoRequest', (_message.Message,), { - 'DESCRIPTOR' : _GETINFOREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetinfoRequest) - }) -_sym_db.RegisterMessage(GetinfoRequest) - -GetinfoResponse = _reflection.GeneratedProtocolMessageType('GetinfoResponse', (_message.Message,), { - 'DESCRIPTOR' : _GETINFORESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetinfoResponse) - }) -_sym_db.RegisterMessage(GetinfoResponse) - -GetinfoOur_features = _reflection.GeneratedProtocolMessageType('GetinfoOur_features', (_message.Message,), { - 'DESCRIPTOR' : _GETINFOOUR_FEATURES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetinfoOur_features) - }) -_sym_db.RegisterMessage(GetinfoOur_features) - -GetinfoAddress = _reflection.GeneratedProtocolMessageType('GetinfoAddress', (_message.Message,), { - 'DESCRIPTOR' : _GETINFOADDRESS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetinfoAddress) - }) -_sym_db.RegisterMessage(GetinfoAddress) - -GetinfoBinding = _reflection.GeneratedProtocolMessageType('GetinfoBinding', (_message.Message,), { - 'DESCRIPTOR' : _GETINFOBINDING, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetinfoBinding) - }) -_sym_db.RegisterMessage(GetinfoBinding) - -ListpeersRequest = _reflection.GeneratedProtocolMessageType('ListpeersRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersRequest) - }) -_sym_db.RegisterMessage(ListpeersRequest) - -ListpeersResponse = _reflection.GeneratedProtocolMessageType('ListpeersResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersResponse) - }) -_sym_db.RegisterMessage(ListpeersResponse) - -ListpeersPeers = _reflection.GeneratedProtocolMessageType('ListpeersPeers', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeers) - }) -_sym_db.RegisterMessage(ListpeersPeers) - -ListpeersPeersLog = _reflection.GeneratedProtocolMessageType('ListpeersPeersLog', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSLOG, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersLog) - }) -_sym_db.RegisterMessage(ListpeersPeersLog) - -ListpeersPeersChannels = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannels', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannels) - }) -_sym_db.RegisterMessage(ListpeersPeersChannels) - -ListpeersPeersChannelsFeerate = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsFeerate', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSFEERATE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsFeerate) - }) -_sym_db.RegisterMessage(ListpeersPeersChannelsFeerate) - -ListpeersPeersChannelsInflight = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsInflight', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSINFLIGHT, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsInflight) - }) -_sym_db.RegisterMessage(ListpeersPeersChannelsInflight) - -ListpeersPeersChannelsFunding = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsFunding', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSFUNDING, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsFunding) - }) -_sym_db.RegisterMessage(ListpeersPeersChannelsFunding) - -ListpeersPeersChannelsAlias = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsAlias', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSALIAS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsAlias) - }) -_sym_db.RegisterMessage(ListpeersPeersChannelsAlias) - -ListpeersPeersChannelsHtlcs = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsHtlcs', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSHTLCS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsHtlcs) - }) -_sym_db.RegisterMessage(ListpeersPeersChannelsHtlcs) - -ListfundsRequest = _reflection.GeneratedProtocolMessageType('ListfundsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTFUNDSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListfundsRequest) - }) -_sym_db.RegisterMessage(ListfundsRequest) - -ListfundsResponse = _reflection.GeneratedProtocolMessageType('ListfundsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTFUNDSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListfundsResponse) - }) -_sym_db.RegisterMessage(ListfundsResponse) - -ListfundsOutputs = _reflection.GeneratedProtocolMessageType('ListfundsOutputs', (_message.Message,), { - 'DESCRIPTOR' : _LISTFUNDSOUTPUTS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListfundsOutputs) - }) -_sym_db.RegisterMessage(ListfundsOutputs) - -ListfundsChannels = _reflection.GeneratedProtocolMessageType('ListfundsChannels', (_message.Message,), { - 'DESCRIPTOR' : _LISTFUNDSCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListfundsChannels) - }) -_sym_db.RegisterMessage(ListfundsChannels) - -SendpayRequest = _reflection.GeneratedProtocolMessageType('SendpayRequest', (_message.Message,), { - 'DESCRIPTOR' : _SENDPAYREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendpayRequest) - }) -_sym_db.RegisterMessage(SendpayRequest) - -SendpayResponse = _reflection.GeneratedProtocolMessageType('SendpayResponse', (_message.Message,), { - 'DESCRIPTOR' : _SENDPAYRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendpayResponse) - }) -_sym_db.RegisterMessage(SendpayResponse) - -SendpayRoute = _reflection.GeneratedProtocolMessageType('SendpayRoute', (_message.Message,), { - 'DESCRIPTOR' : _SENDPAYROUTE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendpayRoute) - }) -_sym_db.RegisterMessage(SendpayRoute) - -ListchannelsRequest = _reflection.GeneratedProtocolMessageType('ListchannelsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTCHANNELSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListchannelsRequest) - }) -_sym_db.RegisterMessage(ListchannelsRequest) - -ListchannelsResponse = _reflection.GeneratedProtocolMessageType('ListchannelsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTCHANNELSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListchannelsResponse) - }) -_sym_db.RegisterMessage(ListchannelsResponse) - -ListchannelsChannels = _reflection.GeneratedProtocolMessageType('ListchannelsChannels', (_message.Message,), { - 'DESCRIPTOR' : _LISTCHANNELSCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListchannelsChannels) - }) -_sym_db.RegisterMessage(ListchannelsChannels) - -AddgossipRequest = _reflection.GeneratedProtocolMessageType('AddgossipRequest', (_message.Message,), { - 'DESCRIPTOR' : _ADDGOSSIPREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.AddgossipRequest) - }) -_sym_db.RegisterMessage(AddgossipRequest) - -AddgossipResponse = _reflection.GeneratedProtocolMessageType('AddgossipResponse', (_message.Message,), { - 'DESCRIPTOR' : _ADDGOSSIPRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.AddgossipResponse) - }) -_sym_db.RegisterMessage(AddgossipResponse) - -AutocleaninvoiceRequest = _reflection.GeneratedProtocolMessageType('AutocleaninvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _AUTOCLEANINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.AutocleaninvoiceRequest) - }) -_sym_db.RegisterMessage(AutocleaninvoiceRequest) - -AutocleaninvoiceResponse = _reflection.GeneratedProtocolMessageType('AutocleaninvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _AUTOCLEANINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.AutocleaninvoiceResponse) - }) -_sym_db.RegisterMessage(AutocleaninvoiceResponse) - -CheckmessageRequest = _reflection.GeneratedProtocolMessageType('CheckmessageRequest', (_message.Message,), { - 'DESCRIPTOR' : _CHECKMESSAGEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CheckmessageRequest) - }) -_sym_db.RegisterMessage(CheckmessageRequest) - -CheckmessageResponse = _reflection.GeneratedProtocolMessageType('CheckmessageResponse', (_message.Message,), { - 'DESCRIPTOR' : _CHECKMESSAGERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CheckmessageResponse) - }) -_sym_db.RegisterMessage(CheckmessageResponse) - -CloseRequest = _reflection.GeneratedProtocolMessageType('CloseRequest', (_message.Message,), { - 'DESCRIPTOR' : _CLOSEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CloseRequest) - }) -_sym_db.RegisterMessage(CloseRequest) - -CloseResponse = _reflection.GeneratedProtocolMessageType('CloseResponse', (_message.Message,), { - 'DESCRIPTOR' : _CLOSERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CloseResponse) - }) -_sym_db.RegisterMessage(CloseResponse) - -ConnectRequest = _reflection.GeneratedProtocolMessageType('ConnectRequest', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ConnectRequest) - }) -_sym_db.RegisterMessage(ConnectRequest) - -ConnectResponse = _reflection.GeneratedProtocolMessageType('ConnectResponse', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ConnectResponse) - }) -_sym_db.RegisterMessage(ConnectResponse) - -ConnectAddress = _reflection.GeneratedProtocolMessageType('ConnectAddress', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTADDRESS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ConnectAddress) - }) -_sym_db.RegisterMessage(ConnectAddress) - -CreateinvoiceRequest = _reflection.GeneratedProtocolMessageType('CreateinvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _CREATEINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CreateinvoiceRequest) - }) -_sym_db.RegisterMessage(CreateinvoiceRequest) - -CreateinvoiceResponse = _reflection.GeneratedProtocolMessageType('CreateinvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _CREATEINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CreateinvoiceResponse) - }) -_sym_db.RegisterMessage(CreateinvoiceResponse) - -DatastoreRequest = _reflection.GeneratedProtocolMessageType('DatastoreRequest', (_message.Message,), { - 'DESCRIPTOR' : _DATASTOREREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DatastoreRequest) - }) -_sym_db.RegisterMessage(DatastoreRequest) - -DatastoreResponse = _reflection.GeneratedProtocolMessageType('DatastoreResponse', (_message.Message,), { - 'DESCRIPTOR' : _DATASTORERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DatastoreResponse) - }) -_sym_db.RegisterMessage(DatastoreResponse) - -CreateonionRequest = _reflection.GeneratedProtocolMessageType('CreateonionRequest', (_message.Message,), { - 'DESCRIPTOR' : _CREATEONIONREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CreateonionRequest) - }) -_sym_db.RegisterMessage(CreateonionRequest) - -CreateonionResponse = _reflection.GeneratedProtocolMessageType('CreateonionResponse', (_message.Message,), { - 'DESCRIPTOR' : _CREATEONIONRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CreateonionResponse) - }) -_sym_db.RegisterMessage(CreateonionResponse) - -CreateonionHops = _reflection.GeneratedProtocolMessageType('CreateonionHops', (_message.Message,), { - 'DESCRIPTOR' : _CREATEONIONHOPS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.CreateonionHops) - }) -_sym_db.RegisterMessage(CreateonionHops) - -DeldatastoreRequest = _reflection.GeneratedProtocolMessageType('DeldatastoreRequest', (_message.Message,), { - 'DESCRIPTOR' : _DELDATASTOREREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DeldatastoreRequest) - }) -_sym_db.RegisterMessage(DeldatastoreRequest) - -DeldatastoreResponse = _reflection.GeneratedProtocolMessageType('DeldatastoreResponse', (_message.Message,), { - 'DESCRIPTOR' : _DELDATASTORERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DeldatastoreResponse) - }) -_sym_db.RegisterMessage(DeldatastoreResponse) - -DelexpiredinvoiceRequest = _reflection.GeneratedProtocolMessageType('DelexpiredinvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _DELEXPIREDINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DelexpiredinvoiceRequest) - }) -_sym_db.RegisterMessage(DelexpiredinvoiceRequest) - -DelexpiredinvoiceResponse = _reflection.GeneratedProtocolMessageType('DelexpiredinvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _DELEXPIREDINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DelexpiredinvoiceResponse) - }) -_sym_db.RegisterMessage(DelexpiredinvoiceResponse) - -DelinvoiceRequest = _reflection.GeneratedProtocolMessageType('DelinvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _DELINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DelinvoiceRequest) - }) -_sym_db.RegisterMessage(DelinvoiceRequest) - -DelinvoiceResponse = _reflection.GeneratedProtocolMessageType('DelinvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _DELINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DelinvoiceResponse) - }) -_sym_db.RegisterMessage(DelinvoiceResponse) - -InvoiceRequest = _reflection.GeneratedProtocolMessageType('InvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _INVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.InvoiceRequest) - }) -_sym_db.RegisterMessage(InvoiceRequest) - -InvoiceResponse = _reflection.GeneratedProtocolMessageType('InvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _INVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.InvoiceResponse) - }) -_sym_db.RegisterMessage(InvoiceResponse) - -ListdatastoreRequest = _reflection.GeneratedProtocolMessageType('ListdatastoreRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTDATASTOREREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListdatastoreRequest) - }) -_sym_db.RegisterMessage(ListdatastoreRequest) - -ListdatastoreResponse = _reflection.GeneratedProtocolMessageType('ListdatastoreResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTDATASTORERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListdatastoreResponse) - }) -_sym_db.RegisterMessage(ListdatastoreResponse) - -ListdatastoreDatastore = _reflection.GeneratedProtocolMessageType('ListdatastoreDatastore', (_message.Message,), { - 'DESCRIPTOR' : _LISTDATASTOREDATASTORE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListdatastoreDatastore) - }) -_sym_db.RegisterMessage(ListdatastoreDatastore) - -ListinvoicesRequest = _reflection.GeneratedProtocolMessageType('ListinvoicesRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTINVOICESREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListinvoicesRequest) - }) -_sym_db.RegisterMessage(ListinvoicesRequest) - -ListinvoicesResponse = _reflection.GeneratedProtocolMessageType('ListinvoicesResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTINVOICESRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListinvoicesResponse) - }) -_sym_db.RegisterMessage(ListinvoicesResponse) - -ListinvoicesInvoices = _reflection.GeneratedProtocolMessageType('ListinvoicesInvoices', (_message.Message,), { - 'DESCRIPTOR' : _LISTINVOICESINVOICES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListinvoicesInvoices) - }) -_sym_db.RegisterMessage(ListinvoicesInvoices) - -SendonionRequest = _reflection.GeneratedProtocolMessageType('SendonionRequest', (_message.Message,), { - 'DESCRIPTOR' : _SENDONIONREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendonionRequest) - }) -_sym_db.RegisterMessage(SendonionRequest) - -SendonionResponse = _reflection.GeneratedProtocolMessageType('SendonionResponse', (_message.Message,), { - 'DESCRIPTOR' : _SENDONIONRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendonionResponse) - }) -_sym_db.RegisterMessage(SendonionResponse) - -SendonionFirst_hop = _reflection.GeneratedProtocolMessageType('SendonionFirst_hop', (_message.Message,), { - 'DESCRIPTOR' : _SENDONIONFIRST_HOP, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendonionFirst_hop) - }) -_sym_db.RegisterMessage(SendonionFirst_hop) - -ListsendpaysRequest = _reflection.GeneratedProtocolMessageType('ListsendpaysRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTSENDPAYSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListsendpaysRequest) - }) -_sym_db.RegisterMessage(ListsendpaysRequest) - -ListsendpaysResponse = _reflection.GeneratedProtocolMessageType('ListsendpaysResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTSENDPAYSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListsendpaysResponse) - }) -_sym_db.RegisterMessage(ListsendpaysResponse) - -ListsendpaysPayments = _reflection.GeneratedProtocolMessageType('ListsendpaysPayments', (_message.Message,), { - 'DESCRIPTOR' : _LISTSENDPAYSPAYMENTS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListsendpaysPayments) - }) -_sym_db.RegisterMessage(ListsendpaysPayments) - -ListtransactionsRequest = _reflection.GeneratedProtocolMessageType('ListtransactionsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTTRANSACTIONSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListtransactionsRequest) - }) -_sym_db.RegisterMessage(ListtransactionsRequest) - -ListtransactionsResponse = _reflection.GeneratedProtocolMessageType('ListtransactionsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTTRANSACTIONSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListtransactionsResponse) - }) -_sym_db.RegisterMessage(ListtransactionsResponse) - -ListtransactionsTransactions = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactions', (_message.Message,), { - 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactions) - }) -_sym_db.RegisterMessage(ListtransactionsTransactions) - -ListtransactionsTransactionsInputs = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactionsInputs', (_message.Message,), { - 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONSINPUTS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactionsInputs) - }) -_sym_db.RegisterMessage(ListtransactionsTransactionsInputs) - -ListtransactionsTransactionsOutputs = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactionsOutputs', (_message.Message,), { - 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONSOUTPUTS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactionsOutputs) - }) -_sym_db.RegisterMessage(ListtransactionsTransactionsOutputs) - -PayRequest = _reflection.GeneratedProtocolMessageType('PayRequest', (_message.Message,), { - 'DESCRIPTOR' : _PAYREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.PayRequest) - }) -_sym_db.RegisterMessage(PayRequest) - -PayResponse = _reflection.GeneratedProtocolMessageType('PayResponse', (_message.Message,), { - 'DESCRIPTOR' : _PAYRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.PayResponse) - }) -_sym_db.RegisterMessage(PayResponse) - -ListnodesRequest = _reflection.GeneratedProtocolMessageType('ListnodesRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTNODESREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListnodesRequest) - }) -_sym_db.RegisterMessage(ListnodesRequest) - -ListnodesResponse = _reflection.GeneratedProtocolMessageType('ListnodesResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTNODESRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListnodesResponse) - }) -_sym_db.RegisterMessage(ListnodesResponse) - -ListnodesNodes = _reflection.GeneratedProtocolMessageType('ListnodesNodes', (_message.Message,), { - 'DESCRIPTOR' : _LISTNODESNODES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListnodesNodes) - }) -_sym_db.RegisterMessage(ListnodesNodes) - -ListnodesNodesAddresses = _reflection.GeneratedProtocolMessageType('ListnodesNodesAddresses', (_message.Message,), { - 'DESCRIPTOR' : _LISTNODESNODESADDRESSES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListnodesNodesAddresses) - }) -_sym_db.RegisterMessage(ListnodesNodesAddresses) - -WaitanyinvoiceRequest = _reflection.GeneratedProtocolMessageType('WaitanyinvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _WAITANYINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitanyinvoiceRequest) - }) -_sym_db.RegisterMessage(WaitanyinvoiceRequest) - -WaitanyinvoiceResponse = _reflection.GeneratedProtocolMessageType('WaitanyinvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _WAITANYINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitanyinvoiceResponse) - }) -_sym_db.RegisterMessage(WaitanyinvoiceResponse) - -WaitinvoiceRequest = _reflection.GeneratedProtocolMessageType('WaitinvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _WAITINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitinvoiceRequest) - }) -_sym_db.RegisterMessage(WaitinvoiceRequest) - -WaitinvoiceResponse = _reflection.GeneratedProtocolMessageType('WaitinvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _WAITINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitinvoiceResponse) - }) -_sym_db.RegisterMessage(WaitinvoiceResponse) - -WaitsendpayRequest = _reflection.GeneratedProtocolMessageType('WaitsendpayRequest', (_message.Message,), { - 'DESCRIPTOR' : _WAITSENDPAYREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitsendpayRequest) - }) -_sym_db.RegisterMessage(WaitsendpayRequest) - -WaitsendpayResponse = _reflection.GeneratedProtocolMessageType('WaitsendpayResponse', (_message.Message,), { - 'DESCRIPTOR' : _WAITSENDPAYRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WaitsendpayResponse) - }) -_sym_db.RegisterMessage(WaitsendpayResponse) - -NewaddrRequest = _reflection.GeneratedProtocolMessageType('NewaddrRequest', (_message.Message,), { - 'DESCRIPTOR' : _NEWADDRREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.NewaddrRequest) - }) -_sym_db.RegisterMessage(NewaddrRequest) - -NewaddrResponse = _reflection.GeneratedProtocolMessageType('NewaddrResponse', (_message.Message,), { - 'DESCRIPTOR' : _NEWADDRRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.NewaddrResponse) - }) -_sym_db.RegisterMessage(NewaddrResponse) - -WithdrawRequest = _reflection.GeneratedProtocolMessageType('WithdrawRequest', (_message.Message,), { - 'DESCRIPTOR' : _WITHDRAWREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WithdrawRequest) - }) -_sym_db.RegisterMessage(WithdrawRequest) - -WithdrawResponse = _reflection.GeneratedProtocolMessageType('WithdrawResponse', (_message.Message,), { - 'DESCRIPTOR' : _WITHDRAWRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.WithdrawResponse) - }) -_sym_db.RegisterMessage(WithdrawResponse) - -KeysendRequest = _reflection.GeneratedProtocolMessageType('KeysendRequest', (_message.Message,), { - 'DESCRIPTOR' : _KEYSENDREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.KeysendRequest) - }) -_sym_db.RegisterMessage(KeysendRequest) - -KeysendResponse = _reflection.GeneratedProtocolMessageType('KeysendResponse', (_message.Message,), { - 'DESCRIPTOR' : _KEYSENDRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.KeysendResponse) - }) -_sym_db.RegisterMessage(KeysendResponse) - -FundpsbtRequest = _reflection.GeneratedProtocolMessageType('FundpsbtRequest', (_message.Message,), { - 'DESCRIPTOR' : _FUNDPSBTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FundpsbtRequest) - }) -_sym_db.RegisterMessage(FundpsbtRequest) - -FundpsbtResponse = _reflection.GeneratedProtocolMessageType('FundpsbtResponse', (_message.Message,), { - 'DESCRIPTOR' : _FUNDPSBTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FundpsbtResponse) - }) -_sym_db.RegisterMessage(FundpsbtResponse) - -FundpsbtReservations = _reflection.GeneratedProtocolMessageType('FundpsbtReservations', (_message.Message,), { - 'DESCRIPTOR' : _FUNDPSBTRESERVATIONS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FundpsbtReservations) - }) -_sym_db.RegisterMessage(FundpsbtReservations) - -SendpsbtRequest = _reflection.GeneratedProtocolMessageType('SendpsbtRequest', (_message.Message,), { - 'DESCRIPTOR' : _SENDPSBTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendpsbtRequest) - }) -_sym_db.RegisterMessage(SendpsbtRequest) - -SendpsbtResponse = _reflection.GeneratedProtocolMessageType('SendpsbtResponse', (_message.Message,), { - 'DESCRIPTOR' : _SENDPSBTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendpsbtResponse) - }) -_sym_db.RegisterMessage(SendpsbtResponse) - -SignpsbtRequest = _reflection.GeneratedProtocolMessageType('SignpsbtRequest', (_message.Message,), { - 'DESCRIPTOR' : _SIGNPSBTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SignpsbtRequest) - }) -_sym_db.RegisterMessage(SignpsbtRequest) - -SignpsbtResponse = _reflection.GeneratedProtocolMessageType('SignpsbtResponse', (_message.Message,), { - 'DESCRIPTOR' : _SIGNPSBTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SignpsbtResponse) - }) -_sym_db.RegisterMessage(SignpsbtResponse) - -UtxopsbtRequest = _reflection.GeneratedProtocolMessageType('UtxopsbtRequest', (_message.Message,), { - 'DESCRIPTOR' : _UTXOPSBTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.UtxopsbtRequest) - }) -_sym_db.RegisterMessage(UtxopsbtRequest) - -UtxopsbtResponse = _reflection.GeneratedProtocolMessageType('UtxopsbtResponse', (_message.Message,), { - 'DESCRIPTOR' : _UTXOPSBTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.UtxopsbtResponse) - }) -_sym_db.RegisterMessage(UtxopsbtResponse) - -UtxopsbtReservations = _reflection.GeneratedProtocolMessageType('UtxopsbtReservations', (_message.Message,), { - 'DESCRIPTOR' : _UTXOPSBTRESERVATIONS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.UtxopsbtReservations) - }) -_sym_db.RegisterMessage(UtxopsbtReservations) - -TxdiscardRequest = _reflection.GeneratedProtocolMessageType('TxdiscardRequest', (_message.Message,), { - 'DESCRIPTOR' : _TXDISCARDREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxdiscardRequest) - }) -_sym_db.RegisterMessage(TxdiscardRequest) - -TxdiscardResponse = _reflection.GeneratedProtocolMessageType('TxdiscardResponse', (_message.Message,), { - 'DESCRIPTOR' : _TXDISCARDRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxdiscardResponse) - }) -_sym_db.RegisterMessage(TxdiscardResponse) - -TxprepareRequest = _reflection.GeneratedProtocolMessageType('TxprepareRequest', (_message.Message,), { - 'DESCRIPTOR' : _TXPREPAREREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxprepareRequest) - }) -_sym_db.RegisterMessage(TxprepareRequest) - -TxprepareResponse = _reflection.GeneratedProtocolMessageType('TxprepareResponse', (_message.Message,), { - 'DESCRIPTOR' : _TXPREPARERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxprepareResponse) - }) -_sym_db.RegisterMessage(TxprepareResponse) - -TxsendRequest = _reflection.GeneratedProtocolMessageType('TxsendRequest', (_message.Message,), { - 'DESCRIPTOR' : _TXSENDREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxsendRequest) - }) -_sym_db.RegisterMessage(TxsendRequest) - -TxsendResponse = _reflection.GeneratedProtocolMessageType('TxsendResponse', (_message.Message,), { - 'DESCRIPTOR' : _TXSENDRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.TxsendResponse) - }) -_sym_db.RegisterMessage(TxsendResponse) - -ListpeerchannelsRequest = _reflection.GeneratedProtocolMessageType('ListpeerchannelsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsRequest) - }) -_sym_db.RegisterMessage(ListpeerchannelsRequest) - -ListpeerchannelsResponse = _reflection.GeneratedProtocolMessageType('ListpeerchannelsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsResponse) - }) -_sym_db.RegisterMessage(ListpeerchannelsResponse) - -ListpeerchannelsChannels = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannels', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannels) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannels) - -ListpeerchannelsChannelsFeerate = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannelsFeerate', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELSFEERATE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannelsFeerate) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannelsFeerate) - -ListpeerchannelsChannelsInflight = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannelsInflight', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELSINFLIGHT, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannelsInflight) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannelsInflight) - -ListpeerchannelsChannelsFunding = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannelsFunding', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELSFUNDING, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannelsFunding) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannelsFunding) - -ListpeerchannelsChannelsAlias = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannelsAlias', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELSALIAS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannelsAlias) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannelsAlias) - -ListpeerchannelsChannelsHtlcs = _reflection.GeneratedProtocolMessageType('ListpeerchannelsChannelsHtlcs', (_message.Message,), { - 'DESCRIPTOR' : _LISTPEERCHANNELSCHANNELSHTLCS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpeerchannelsChannelsHtlcs) - }) -_sym_db.RegisterMessage(ListpeerchannelsChannelsHtlcs) - -ListclosedchannelsRequest = _reflection.GeneratedProtocolMessageType('ListclosedchannelsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTCLOSEDCHANNELSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListclosedchannelsRequest) - }) -_sym_db.RegisterMessage(ListclosedchannelsRequest) - -ListclosedchannelsResponse = _reflection.GeneratedProtocolMessageType('ListclosedchannelsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTCLOSEDCHANNELSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListclosedchannelsResponse) - }) -_sym_db.RegisterMessage(ListclosedchannelsResponse) - -ListclosedchannelsClosedchannels = _reflection.GeneratedProtocolMessageType('ListclosedchannelsClosedchannels', (_message.Message,), { - 'DESCRIPTOR' : _LISTCLOSEDCHANNELSCLOSEDCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListclosedchannelsClosedchannels) - }) -_sym_db.RegisterMessage(ListclosedchannelsClosedchannels) - -ListclosedchannelsClosedchannelsAlias = _reflection.GeneratedProtocolMessageType('ListclosedchannelsClosedchannelsAlias', (_message.Message,), { - 'DESCRIPTOR' : _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListclosedchannelsClosedchannelsAlias) - }) -_sym_db.RegisterMessage(ListclosedchannelsClosedchannelsAlias) - -DecodepayRequest = _reflection.GeneratedProtocolMessageType('DecodepayRequest', (_message.Message,), { - 'DESCRIPTOR' : _DECODEPAYREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodepayRequest) - }) -_sym_db.RegisterMessage(DecodepayRequest) - -DecodepayResponse = _reflection.GeneratedProtocolMessageType('DecodepayResponse', (_message.Message,), { - 'DESCRIPTOR' : _DECODEPAYRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodepayResponse) - }) -_sym_db.RegisterMessage(DecodepayResponse) - -DecodepayFallbacks = _reflection.GeneratedProtocolMessageType('DecodepayFallbacks', (_message.Message,), { - 'DESCRIPTOR' : _DECODEPAYFALLBACKS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodepayFallbacks) - }) -_sym_db.RegisterMessage(DecodepayFallbacks) - -DecodepayExtra = _reflection.GeneratedProtocolMessageType('DecodepayExtra', (_message.Message,), { - 'DESCRIPTOR' : _DECODEPAYEXTRA, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodepayExtra) - }) -_sym_db.RegisterMessage(DecodepayExtra) - -DecodeRequest = _reflection.GeneratedProtocolMessageType('DecodeRequest', (_message.Message,), { - 'DESCRIPTOR' : _DECODEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeRequest) - }) -_sym_db.RegisterMessage(DecodeRequest) - -DecodeResponse = _reflection.GeneratedProtocolMessageType('DecodeResponse', (_message.Message,), { - 'DESCRIPTOR' : _DECODERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeResponse) - }) -_sym_db.RegisterMessage(DecodeResponse) - -DecodeOffer_paths = _reflection.GeneratedProtocolMessageType('DecodeOffer_paths', (_message.Message,), { - 'DESCRIPTOR' : _DECODEOFFER_PATHS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeOffer_paths) - }) -_sym_db.RegisterMessage(DecodeOffer_paths) - -DecodeOffer_recurrencePaywindow = _reflection.GeneratedProtocolMessageType('DecodeOffer_recurrencePaywindow', (_message.Message,), { - 'DESCRIPTOR' : _DECODEOFFER_RECURRENCEPAYWINDOW, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeOffer_recurrencePaywindow) - }) -_sym_db.RegisterMessage(DecodeOffer_recurrencePaywindow) - -DecodeInvoice_pathsPath = _reflection.GeneratedProtocolMessageType('DecodeInvoice_pathsPath', (_message.Message,), { - 'DESCRIPTOR' : _DECODEINVOICE_PATHSPATH, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeInvoice_pathsPath) - }) -_sym_db.RegisterMessage(DecodeInvoice_pathsPath) - -DecodeInvoice_fallbacks = _reflection.GeneratedProtocolMessageType('DecodeInvoice_fallbacks', (_message.Message,), { - 'DESCRIPTOR' : _DECODEINVOICE_FALLBACKS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeInvoice_fallbacks) - }) -_sym_db.RegisterMessage(DecodeInvoice_fallbacks) - -DecodeFallbacks = _reflection.GeneratedProtocolMessageType('DecodeFallbacks', (_message.Message,), { - 'DESCRIPTOR' : _DECODEFALLBACKS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeFallbacks) - }) -_sym_db.RegisterMessage(DecodeFallbacks) - -DecodeExtra = _reflection.GeneratedProtocolMessageType('DecodeExtra', (_message.Message,), { - 'DESCRIPTOR' : _DECODEEXTRA, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeExtra) - }) -_sym_db.RegisterMessage(DecodeExtra) - -DecodeRestrictions = _reflection.GeneratedProtocolMessageType('DecodeRestrictions', (_message.Message,), { - 'DESCRIPTOR' : _DECODERESTRICTIONS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DecodeRestrictions) - }) -_sym_db.RegisterMessage(DecodeRestrictions) - -DisconnectRequest = _reflection.GeneratedProtocolMessageType('DisconnectRequest', (_message.Message,), { - 'DESCRIPTOR' : _DISCONNECTREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DisconnectRequest) - }) -_sym_db.RegisterMessage(DisconnectRequest) - -DisconnectResponse = _reflection.GeneratedProtocolMessageType('DisconnectResponse', (_message.Message,), { - 'DESCRIPTOR' : _DISCONNECTRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.DisconnectResponse) - }) -_sym_db.RegisterMessage(DisconnectResponse) - -FeeratesRequest = _reflection.GeneratedProtocolMessageType('FeeratesRequest', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesRequest) - }) -_sym_db.RegisterMessage(FeeratesRequest) - -FeeratesResponse = _reflection.GeneratedProtocolMessageType('FeeratesResponse', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesResponse) - }) -_sym_db.RegisterMessage(FeeratesResponse) - -FeeratesPerkb = _reflection.GeneratedProtocolMessageType('FeeratesPerkb', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESPERKB, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesPerkb) - }) -_sym_db.RegisterMessage(FeeratesPerkb) - -FeeratesPerkbEstimates = _reflection.GeneratedProtocolMessageType('FeeratesPerkbEstimates', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESPERKBESTIMATES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesPerkbEstimates) - }) -_sym_db.RegisterMessage(FeeratesPerkbEstimates) - -FeeratesPerkw = _reflection.GeneratedProtocolMessageType('FeeratesPerkw', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESPERKW, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesPerkw) - }) -_sym_db.RegisterMessage(FeeratesPerkw) - -FeeratesPerkwEstimates = _reflection.GeneratedProtocolMessageType('FeeratesPerkwEstimates', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESPERKWESTIMATES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesPerkwEstimates) - }) -_sym_db.RegisterMessage(FeeratesPerkwEstimates) - -FeeratesOnchain_fee_estimates = _reflection.GeneratedProtocolMessageType('FeeratesOnchain_fee_estimates', (_message.Message,), { - 'DESCRIPTOR' : _FEERATESONCHAIN_FEE_ESTIMATES, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FeeratesOnchain_fee_estimates) - }) -_sym_db.RegisterMessage(FeeratesOnchain_fee_estimates) - -FundchannelRequest = _reflection.GeneratedProtocolMessageType('FundchannelRequest', (_message.Message,), { - 'DESCRIPTOR' : _FUNDCHANNELREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FundchannelRequest) - }) -_sym_db.RegisterMessage(FundchannelRequest) - -FundchannelResponse = _reflection.GeneratedProtocolMessageType('FundchannelResponse', (_message.Message,), { - 'DESCRIPTOR' : _FUNDCHANNELRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.FundchannelResponse) - }) -_sym_db.RegisterMessage(FundchannelResponse) - -GetrouteRequest = _reflection.GeneratedProtocolMessageType('GetrouteRequest', (_message.Message,), { - 'DESCRIPTOR' : _GETROUTEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetrouteRequest) - }) -_sym_db.RegisterMessage(GetrouteRequest) - -GetrouteResponse = _reflection.GeneratedProtocolMessageType('GetrouteResponse', (_message.Message,), { - 'DESCRIPTOR' : _GETROUTERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetrouteResponse) - }) -_sym_db.RegisterMessage(GetrouteResponse) - -GetrouteRoute = _reflection.GeneratedProtocolMessageType('GetrouteRoute', (_message.Message,), { - 'DESCRIPTOR' : _GETROUTEROUTE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.GetrouteRoute) - }) -_sym_db.RegisterMessage(GetrouteRoute) - -ListforwardsRequest = _reflection.GeneratedProtocolMessageType('ListforwardsRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTFORWARDSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListforwardsRequest) - }) -_sym_db.RegisterMessage(ListforwardsRequest) - -ListforwardsResponse = _reflection.GeneratedProtocolMessageType('ListforwardsResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTFORWARDSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListforwardsResponse) - }) -_sym_db.RegisterMessage(ListforwardsResponse) - -ListforwardsForwards = _reflection.GeneratedProtocolMessageType('ListforwardsForwards', (_message.Message,), { - 'DESCRIPTOR' : _LISTFORWARDSFORWARDS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListforwardsForwards) - }) -_sym_db.RegisterMessage(ListforwardsForwards) - -ListpaysRequest = _reflection.GeneratedProtocolMessageType('ListpaysRequest', (_message.Message,), { - 'DESCRIPTOR' : _LISTPAYSREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpaysRequest) - }) -_sym_db.RegisterMessage(ListpaysRequest) - -ListpaysResponse = _reflection.GeneratedProtocolMessageType('ListpaysResponse', (_message.Message,), { - 'DESCRIPTOR' : _LISTPAYSRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpaysResponse) - }) -_sym_db.RegisterMessage(ListpaysResponse) - -ListpaysPays = _reflection.GeneratedProtocolMessageType('ListpaysPays', (_message.Message,), { - 'DESCRIPTOR' : _LISTPAYSPAYS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.ListpaysPays) - }) -_sym_db.RegisterMessage(ListpaysPays) - -PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), { - 'DESCRIPTOR' : _PINGREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.PingRequest) - }) -_sym_db.RegisterMessage(PingRequest) - -PingResponse = _reflection.GeneratedProtocolMessageType('PingResponse', (_message.Message,), { - 'DESCRIPTOR' : _PINGRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.PingResponse) - }) -_sym_db.RegisterMessage(PingResponse) - -SendcustommsgRequest = _reflection.GeneratedProtocolMessageType('SendcustommsgRequest', (_message.Message,), { - 'DESCRIPTOR' : _SENDCUSTOMMSGREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendcustommsgRequest) - }) -_sym_db.RegisterMessage(SendcustommsgRequest) - -SendcustommsgResponse = _reflection.GeneratedProtocolMessageType('SendcustommsgResponse', (_message.Message,), { - 'DESCRIPTOR' : _SENDCUSTOMMSGRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SendcustommsgResponse) - }) -_sym_db.RegisterMessage(SendcustommsgResponse) - -SetchannelRequest = _reflection.GeneratedProtocolMessageType('SetchannelRequest', (_message.Message,), { - 'DESCRIPTOR' : _SETCHANNELREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SetchannelRequest) - }) -_sym_db.RegisterMessage(SetchannelRequest) - -SetchannelResponse = _reflection.GeneratedProtocolMessageType('SetchannelResponse', (_message.Message,), { - 'DESCRIPTOR' : _SETCHANNELRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SetchannelResponse) - }) -_sym_db.RegisterMessage(SetchannelResponse) - -SetchannelChannels = _reflection.GeneratedProtocolMessageType('SetchannelChannels', (_message.Message,), { - 'DESCRIPTOR' : _SETCHANNELCHANNELS, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SetchannelChannels) - }) -_sym_db.RegisterMessage(SetchannelChannels) - -SigninvoiceRequest = _reflection.GeneratedProtocolMessageType('SigninvoiceRequest', (_message.Message,), { - 'DESCRIPTOR' : _SIGNINVOICEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SigninvoiceRequest) - }) -_sym_db.RegisterMessage(SigninvoiceRequest) - -SigninvoiceResponse = _reflection.GeneratedProtocolMessageType('SigninvoiceResponse', (_message.Message,), { - 'DESCRIPTOR' : _SIGNINVOICERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SigninvoiceResponse) - }) -_sym_db.RegisterMessage(SigninvoiceResponse) - -SignmessageRequest = _reflection.GeneratedProtocolMessageType('SignmessageRequest', (_message.Message,), { - 'DESCRIPTOR' : _SIGNMESSAGEREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SignmessageRequest) - }) -_sym_db.RegisterMessage(SignmessageRequest) - -SignmessageResponse = _reflection.GeneratedProtocolMessageType('SignmessageResponse', (_message.Message,), { - 'DESCRIPTOR' : _SIGNMESSAGERESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.SignmessageResponse) - }) -_sym_db.RegisterMessage(SignmessageResponse) - -StopRequest = _reflection.GeneratedProtocolMessageType('StopRequest', (_message.Message,), { - 'DESCRIPTOR' : _STOPREQUEST, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.StopRequest) - }) -_sym_db.RegisterMessage(StopRequest) - -StopResponse = _reflection.GeneratedProtocolMessageType('StopResponse', (_message.Message,), { - 'DESCRIPTOR' : _STOPRESPONSE, - '__module__' : 'node_pb2' - # @@protoc_insertion_point(class_scope:cln.StopResponse) - }) -_sym_db.RegisterMessage(StopResponse) - -_NODE = DESCRIPTOR.services_by_name['Node'] -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - _GETINFOREQUEST._serialized_start=37 - _GETINFOREQUEST._serialized_end=53 - _GETINFORESPONSE._serialized_start=56 - _GETINFORESPONSE._serialized_end=618 - _GETINFOOUR_FEATURES._serialized_start=620 - _GETINFOOUR_FEATURES._serialized_end=703 - _GETINFOADDRESS._serialized_start=706 - _GETINFOADDRESS._serialized_end=917 - _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_start=819 - _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_end=905 - _GETINFOBINDING._serialized_start=920 - _GETINFOBINDING._serialized_end=1171 - _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_start=1059 - _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_end=1139 - _LISTPEERSREQUEST._serialized_start=1173 - _LISTPEERSREQUEST._serialized_end=1245 - _LISTPEERSRESPONSE._serialized_start=1247 - _LISTPEERSRESPONSE._serialized_end=1302 - _LISTPEERSPEERS._serialized_start=1305 - _LISTPEERSPEERS._serialized_end=1575 - _LISTPEERSPEERSLOG._serialized_start=1578 - _LISTPEERSPEERSLOG._serialized_end=1959 - _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_start=1789 - _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_end=1894 - _LISTPEERSPEERSCHANNELS._serialized_start=1962 - _LISTPEERSPEERSCHANNELS._serialized_end=4992 - _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_start=3862 - _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_end=4151 - _LISTPEERSPEERSCHANNELSFEERATE._serialized_start=4994 - _LISTPEERSPEERSCHANNELSFEERATE._serialized_end=5055 - _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_start=5058 - _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=5255 - _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=5258 - _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5541 - _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5543 - _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5634 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5637 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=6006 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5922 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5977 - _LISTFUNDSREQUEST._serialized_start=6008 - _LISTFUNDSREQUEST._serialized_end=6056 - _LISTFUNDSRESPONSE._serialized_start=6058 - _LISTFUNDSRESPONSE._serialized_end=6159 - _LISTFUNDSOUTPUTS._serialized_start=6162 - _LISTFUNDSOUTPUTS._serialized_end=6549 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=6423 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=6504 - _LISTFUNDSCHANNELS._serialized_start=6552 - _LISTFUNDSCHANNELS._serialized_end=6851 - _SENDPAYREQUEST._serialized_start=6854 - _SENDPAYREQUEST._serialized_end=7203 - _SENDPAYRESPONSE._serialized_start=7206 - _SENDPAYRESPONSE._serialized_end=7799 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7620 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7662 - _SENDPAYROUTE._serialized_start=7801 - _SENDPAYROUTE._serialized_end=7893 - _LISTCHANNELSREQUEST._serialized_start=7896 - _LISTCHANNELSREQUEST._serialized_end=8043 - _LISTCHANNELSRESPONSE._serialized_start=8045 - _LISTCHANNELSRESPONSE._serialized_end=8112 - _LISTCHANNELSCHANNELS._serialized_start=8115 - _LISTCHANNELSCHANNELS._serialized_end=8550 - _ADDGOSSIPREQUEST._serialized_start=8552 - _ADDGOSSIPREQUEST._serialized_end=8587 - _ADDGOSSIPRESPONSE._serialized_start=8589 - _ADDGOSSIPRESPONSE._serialized_end=8608 - _AUTOCLEANINVOICEREQUEST._serialized_start=8610 - _AUTOCLEANINVOICEREQUEST._serialized_end=8721 - _AUTOCLEANINVOICERESPONSE._serialized_start=8724 - _AUTOCLEANINVOICERESPONSE._serialized_end=8853 - _CHECKMESSAGEREQUEST._serialized_start=8855 - _CHECKMESSAGEREQUEST._serialized_end=8940 - _CHECKMESSAGERESPONSE._serialized_start=8942 - _CHECKMESSAGERESPONSE._serialized_end=8998 - _CLOSEREQUEST._serialized_start=9001 - _CLOSEREQUEST._serialized_end=9332 - _CLOSERESPONSE._serialized_start=9335 - _CLOSERESPONSE._serialized_end=9506 - _CLOSERESPONSE_CLOSETYPE._serialized_start=9437 - _CLOSERESPONSE_CLOSETYPE._serialized_end=9490 - _CONNECTREQUEST._serialized_start=9508 - _CONNECTREQUEST._serialized_end=9592 - _CONNECTRESPONSE._serialized_start=9595 - _CONNECTRESPONSE._serialized_end=9775 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9740 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9775 - _CONNECTADDRESS._serialized_start=9778 - _CONNECTADDRESS._serialized_end=10029 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9917 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9997 - _CREATEINVOICEREQUEST._serialized_start=10031 - _CREATEINVOICEREQUEST._serialized_end=10105 - _CREATEINVOICERESPONSE._serialized_start=10108 - _CREATEINVOICERESPONSE._serialized_end=10749 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10542 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10598 - _DATASTOREREQUEST._serialized_start=10752 - _DATASTOREREQUEST._serialized_end=11060 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10905 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=11017 - _DATASTORERESPONSE._serialized_start=11063 - _DATASTORERESPONSE._serialized_end=11193 - _CREATEONIONREQUEST._serialized_start=11196 - _CREATEONIONREQUEST._serialized_end=11353 - _CREATEONIONRESPONSE._serialized_start=11355 - _CREATEONIONRESPONSE._serialized_end=11415 - _CREATEONIONHOPS._serialized_start=11417 - _CREATEONIONHOPS._serialized_end=11467 - _DELDATASTOREREQUEST._serialized_start=11469 - _DELDATASTOREREQUEST._serialized_end=11543 - _DELDATASTORERESPONSE._serialized_start=11546 - _DELDATASTORERESPONSE._serialized_end=11679 - _DELEXPIREDINVOICEREQUEST._serialized_start=11681 - _DELEXPIREDINVOICEREQUEST._serialized_end=11753 - _DELEXPIREDINVOICERESPONSE._serialized_start=11755 - _DELEXPIREDINVOICERESPONSE._serialized_end=11782 - _DELINVOICEREQUEST._serialized_start=11785 - _DELINVOICEREQUEST._serialized_end=11967 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11901 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11954 - _DELINVOICERESPONSE._serialized_start=11970 - _DELINVOICERESPONSE._serialized_end=12423 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11901 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11954 - _INVOICEREQUEST._serialized_start=12426 - _INVOICEREQUEST._serialized_end=12676 - _INVOICERESPONSE._serialized_start=12679 - _INVOICERESPONSE._serialized_end=13038 - _LISTDATASTOREREQUEST._serialized_start=13040 - _LISTDATASTOREREQUEST._serialized_end=13075 - _LISTDATASTORERESPONSE._serialized_start=13077 - _LISTDATASTORERESPONSE._serialized_end=13148 - _LISTDATASTOREDATASTORE._serialized_start=13151 - _LISTDATASTOREDATASTORE._serialized_end=13286 - _LISTINVOICESREQUEST._serialized_start=13289 - _LISTINVOICESREQUEST._serialized_end=13458 - _LISTINVOICESRESPONSE._serialized_start=13460 - _LISTINVOICESRESPONSE._serialized_end=13527 - _LISTINVOICESINVOICES._serialized_start=13530 - _LISTINVOICESINVOICES._serialized_end=14204 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13974 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=14037 - _SENDONIONREQUEST._serialized_start=14207 - _SENDONIONREQUEST._serialized_end=14601 - _SENDONIONRESPONSE._serialized_start=14604 - _SENDONIONRESPONSE._serialized_end=15127 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14975 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=15019 - _SENDONIONFIRST_HOP._serialized_start=15129 - _SENDONIONFIRST_HOP._serialized_end=15210 - _LISTSENDPAYSREQUEST._serialized_start=15213 - _LISTSENDPAYSREQUEST._serialized_end=15448 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=15350 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15409 - _LISTSENDPAYSRESPONSE._serialized_start=15450 - _LISTSENDPAYSRESPONSE._serialized_end=15517 - _LISTSENDPAYSPAYMENTS._serialized_start=15520 - _LISTSENDPAYSPAYMENTS._serialized_end=16148 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15954 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=16021 - _LISTTRANSACTIONSREQUEST._serialized_start=16150 - _LISTTRANSACTIONSREQUEST._serialized_end=16175 - _LISTTRANSACTIONSRESPONSE._serialized_start=16177 - _LISTTRANSACTIONSRESPONSE._serialized_end=16260 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=16263 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16511 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16514 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=17030 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16726 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=17004 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=17033 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17577 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=17272 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17551 - _PAYREQUEST._serialized_start=17580 - _PAYREQUEST._serialized_end=18054 - _PAYRESPONSE._serialized_start=18057 - _PAYRESPONSE._serialized_end=18436 - _PAYRESPONSE_PAYSTATUS._serialized_start=18339 - _PAYRESPONSE_PAYSTATUS._serialized_end=18389 - _LISTNODESREQUEST._serialized_start=18438 - _LISTNODESREQUEST._serialized_end=18480 - _LISTNODESRESPONSE._serialized_start=18482 - _LISTNODESRESPONSE._serialized_end=18537 - _LISTNODESNODES._serialized_start=18540 - _LISTNODESNODES._serialized_end=18765 - _LISTNODESNODESADDRESSES._serialized_start=18768 - _LISTNODESNODESADDRESSES._serialized_end=19015 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18908 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=19003 - _WAITANYINVOICEREQUEST._serialized_start=19017 - _WAITANYINVOICEREQUEST._serialized_end=19120 - _WAITANYINVOICERESPONSE._serialized_start=19123 - _WAITANYINVOICERESPONSE._serialized_end=19654 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19499 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19544 - _WAITINVOICEREQUEST._serialized_start=19656 - _WAITINVOICEREQUEST._serialized_end=19691 - _WAITINVOICERESPONSE._serialized_start=19694 - _WAITINVOICERESPONSE._serialized_end=20213 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=20061 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=20103 - _WAITSENDPAYREQUEST._serialized_start=20216 - _WAITSENDPAYREQUEST._serialized_end=20358 - _WAITSENDPAYRESPONSE._serialized_start=20361 - _WAITSENDPAYRESPONSE._serialized_end=20923 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20765 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20798 - _NEWADDRREQUEST._serialized_start=20926 - _NEWADDRREQUEST._serialized_end=21067 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=21010 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=21051 - _NEWADDRRESPONSE._serialized_start=21069 - _NEWADDRRESPONSE._serialized_end=21160 - _WITHDRAWREQUEST._serialized_start=21163 - _WITHDRAWREQUEST._serialized_end=21365 - _WITHDRAWRESPONSE._serialized_start=21367 - _WITHDRAWRESPONSE._serialized_end=21425 - _KEYSENDREQUEST._serialized_start=21428 - _KEYSENDREQUEST._serialized_end=21814 - _KEYSENDRESPONSE._serialized_start=21817 - _KEYSENDRESPONSE._serialized_end=22187 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=22111 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=22140 - _FUNDPSBTREQUEST._serialized_start=22190 - _FUNDPSBTREQUEST._serialized_end=22506 - _FUNDPSBTRESPONSE._serialized_start=22509 - _FUNDPSBTRESPONSE._serialized_end=22726 - _FUNDPSBTRESERVATIONS._serialized_start=22728 - _FUNDPSBTRESERVATIONS._serialized_end=22845 - _SENDPSBTREQUEST._serialized_start=22847 - _SENDPSBTREQUEST._serialized_end=22912 - _SENDPSBTRESPONSE._serialized_start=22914 - _SENDPSBTRESPONSE._serialized_end=22958 - _SIGNPSBTREQUEST._serialized_start=22960 - _SIGNPSBTREQUEST._serialized_end=23009 - _SIGNPSBTRESPONSE._serialized_start=23011 - _SIGNPSBTRESPONSE._serialized_end=23050 - _UTXOPSBTREQUEST._serialized_start=23053 - _UTXOPSBTREQUEST._serialized_end=23400 - _UTXOPSBTRESPONSE._serialized_start=23403 - _UTXOPSBTRESPONSE._serialized_end=23620 - _UTXOPSBTRESERVATIONS._serialized_start=23622 - _UTXOPSBTRESERVATIONS._serialized_end=23739 - _TXDISCARDREQUEST._serialized_start=23741 - _TXDISCARDREQUEST._serialized_end=23773 - _TXDISCARDRESPONSE._serialized_start=23775 - _TXDISCARDRESPONSE._serialized_end=23829 - _TXPREPAREREQUEST._serialized_start=23832 - _TXPREPAREREQUEST._serialized_end=23996 - _TXPREPARERESPONSE._serialized_start=23998 - _TXPREPARERESPONSE._serialized_end=24066 - _TXSENDREQUEST._serialized_start=24068 - _TXSENDREQUEST._serialized_end=24097 - _TXSENDRESPONSE._serialized_start=24099 - _TXSENDRESPONSE._serialized_end=24155 - _LISTPEERCHANNELSREQUEST._serialized_start=24157 - _LISTPEERCHANNELSREQUEST._serialized_end=24206 - _LISTPEERCHANNELSRESPONSE._serialized_start=24208 - _LISTPEERCHANNELSRESPONSE._serialized_end=24283 - _LISTPEERCHANNELSCHANNELS._serialized_start=24286 - _LISTPEERCHANNELSCHANNELS._serialized_end=27429 - _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_start=26245 - _LISTPEERCHANNELSCHANNELS_LISTPEERCHANNELSCHANNELSSTATE._serialized_end=26536 - _LISTPEERCHANNELSCHANNELSFEERATE._serialized_start=27431 - _LISTPEERCHANNELSCHANNELSFEERATE._serialized_end=27524 - _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_start=27527 - _LISTPEERCHANNELSCHANNELSINFLIGHT._serialized_end=27865 - _LISTPEERCHANNELSCHANNELSFUNDING._serialized_start=27868 - _LISTPEERCHANNELSCHANNELSFUNDING._serialized_end=28206 - _LISTPEERCHANNELSCHANNELSALIAS._serialized_start=28208 - _LISTPEERCHANNELSCHANNELSALIAS._serialized_end=28301 - _LISTPEERCHANNELSCHANNELSHTLCS._serialized_start=28304 - _LISTPEERCHANNELSCHANNELSHTLCS._serialized_end=28786 - _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_start=28625 - _LISTPEERCHANNELSCHANNELSHTLCS_LISTPEERCHANNELSCHANNELSHTLCSDIRECTION._serialized_end=28682 - _LISTCLOSEDCHANNELSREQUEST._serialized_start=28788 - _LISTCLOSEDCHANNELSREQUEST._serialized_end=28839 - _LISTCLOSEDCHANNELSRESPONSE._serialized_start=28841 - _LISTCLOSEDCHANNELSRESPONSE._serialized_end=28932 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_start=28935 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS._serialized_end=30137 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_start=29835 - _LISTCLOSEDCHANNELSCLOSEDCHANNELS_LISTCLOSEDCHANNELSCLOSEDCHANNELSCLOSE_CAUSE._serialized_end=29953 - _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_start=30139 - _LISTCLOSEDCHANNELSCLOSEDCHANNELSALIAS._serialized_end=30240 - _DECODEPAYREQUEST._serialized_start=30242 - _DECODEPAYREQUEST._serialized_end=30318 - _DECODEPAYRESPONSE._serialized_start=30321 - _DECODEPAYRESPONSE._serialized_end=30846 - _DECODEPAYFALLBACKS._serialized_start=30849 - _DECODEPAYFALLBACKS._serialized_end=31047 - _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_start=30970 - _DECODEPAYFALLBACKS_DECODEPAYFALLBACKSTYPE._serialized_end=31038 - _DECODEPAYEXTRA._serialized_start=31049 - _DECODEPAYEXTRA._serialized_end=31092 - _DECODEREQUEST._serialized_start=31094 - _DECODEREQUEST._serialized_end=31125 - _DECODERESPONSE._serialized_start=31128 - _DECODERESPONSE._serialized_end=35394 - _DECODERESPONSE_DECODETYPE._serialized_start=33696 - _DECODERESPONSE_DECODETYPE._serialized_end=33804 - _DECODEOFFER_PATHS._serialized_start=35396 - _DECODEOFFER_PATHS._serialized_end=35456 - _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_start=35459 - _DECODEOFFER_RECURRENCEPAYWINDOW._serialized_end=35597 - _DECODEINVOICE_PATHSPATH._serialized_start=35599 - _DECODEINVOICE_PATHSPATH._serialized_end=35683 - _DECODEINVOICE_FALLBACKS._serialized_start=35685 - _DECODEINVOICE_FALLBACKS._serialized_end=35774 - _DECODEFALLBACKS._serialized_start=35776 - _DECODEFALLBACKS._serialized_end=35895 - _DECODEEXTRA._serialized_start=35897 - _DECODEEXTRA._serialized_end=35937 - _DECODERESTRICTIONS._serialized_start=35939 - _DECODERESTRICTIONS._serialized_end=35998 - _DISCONNECTREQUEST._serialized_start=36000 - _DISCONNECTREQUEST._serialized_end=36061 - _DISCONNECTRESPONSE._serialized_start=36063 - _DISCONNECTRESPONSE._serialized_end=36083 - _FEERATESREQUEST._serialized_start=36085 - _FEERATESREQUEST._serialized_end=36192 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=36155 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=36192 - _FEERATESRESPONSE._serialized_start=36195 - _FEERATESRESPONSE._serialized_end=36479 - _FEERATESPERKB._serialized_start=36482 - _FEERATESPERKB._serialized_end=36883 - _FEERATESPERKBESTIMATES._serialized_start=36886 - _FEERATESPERKBESTIMATES._serialized_end=37036 - _FEERATESPERKW._serialized_start=37039 - _FEERATESPERKW._serialized_end=37440 - _FEERATESPERKWESTIMATES._serialized_start=37443 - _FEERATESPERKWESTIMATES._serialized_end=37593 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=37596 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=37789 - _FUNDCHANNELREQUEST._serialized_start=37792 - _FUNDCHANNELREQUEST._serialized_end=38277 - _FUNDCHANNELRESPONSE._serialized_start=38280 - _FUNDCHANNELRESPONSE._serialized_end=38435 - _GETROUTEREQUEST._serialized_start=38438 - _GETROUTEREQUEST._serialized_end=38674 - _GETROUTERESPONSE._serialized_start=38676 - _GETROUTERESPONSE._serialized_end=38729 - _GETROUTEROUTE._serialized_start=38732 - _GETROUTEROUTE._serialized_end=38929 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=38900 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=38929 - _LISTFORWARDSREQUEST._serialized_start=38932 - _LISTFORWARDSREQUEST._serialized_end=39190 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=39072 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=39148 - _LISTFORWARDSRESPONSE._serialized_start=39192 - _LISTFORWARDSRESPONSE._serialized_end=39259 - _LISTFORWARDSFORWARDS._serialized_start=39262 - _LISTFORWARDSFORWARDS._serialized_end=39868 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=39651 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=39735 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=39737 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=39785 - _LISTPAYSREQUEST._serialized_start=39871 - _LISTPAYSREQUEST._serialized_end=40090 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=39996 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=40051 - _LISTPAYSRESPONSE._serialized_start=40092 - _LISTPAYSRESPONSE._serialized_end=40143 - _LISTPAYSPAYS._serialized_start=40146 - _LISTPAYSPAYS._serialized_end=40665 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=40477 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=40536 - _PINGREQUEST._serialized_start=40667 - _PINGREQUEST._serialized_end=40756 - _PINGRESPONSE._serialized_start=40758 - _PINGRESPONSE._serialized_end=40788 - _SENDCUSTOMMSGREQUEST._serialized_start=40790 - _SENDCUSTOMMSGREQUEST._serialized_end=40842 - _SENDCUSTOMMSGRESPONSE._serialized_start=40844 - _SENDCUSTOMMSGRESPONSE._serialized_end=40883 - _SETCHANNELREQUEST._serialized_start=40886 - _SETCHANNELREQUEST._serialized_end=41134 - _SETCHANNELRESPONSE._serialized_start=41136 - _SETCHANNELRESPONSE._serialized_end=41199 - _SETCHANNELCHANNELS._serialized_start=41202 - _SETCHANNELCHANNELS._serialized_end=41606 - _SIGNINVOICEREQUEST._serialized_start=41608 - _SIGNINVOICEREQUEST._serialized_end=41647 - _SIGNINVOICERESPONSE._serialized_start=41649 - _SIGNINVOICERESPONSE._serialized_end=41686 - _SIGNMESSAGEREQUEST._serialized_start=41688 - _SIGNMESSAGEREQUEST._serialized_end=41725 - _SIGNMESSAGERESPONSE._serialized_start=41727 - _SIGNMESSAGERESPONSE._serialized_end=41797 - _STOPREQUEST._serialized_start=41799 - _STOPREQUEST._serialized_end=41812 - _STOPRESPONSE._serialized_start=41814 - _STOPRESPONSE._serialized_end=41828 - _NODE._serialized_start=41831 - _NODE._serialized_end=45253 -# @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/primitives_pb2.py b/contrib/pyln-testing/pyln/testing/primitives_pb2.py deleted file mode 100644 index f8d2b6c59118..000000000000 --- a/contrib/pyln-testing/pyln/testing/primitives_pb2.py +++ /dev/null @@ -1,181 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: primitives.proto -"""Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10primitives.proto\x12\x03\x63ln\"\x16\n\x06\x41mount\x12\x0c\n\x04msat\x18\x01 \x01(\x04\"D\n\x0b\x41mountOrAll\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ll\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"D\n\x0b\x41mountOrAny\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ny\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"\x19\n\x17\x43hannelStateChangeCause\"(\n\x08Outpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"h\n\x07\x46\x65\x65rate\x12\x0e\n\x04slow\x18\x01 \x01(\x08H\x00\x12\x10\n\x06normal\x18\x02 \x01(\x08H\x00\x12\x10\n\x06urgent\x18\x03 \x01(\x08H\x00\x12\x0f\n\x05perkb\x18\x04 \x01(\rH\x00\x12\x0f\n\x05perkw\x18\x05 \x01(\rH\x00\x42\x07\n\x05style\":\n\nOutputDesc\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x1b\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\"t\n\x08RouteHop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x02 \x01(\t\x12\x1c\n\x07\x66\x65\x65\x62\x61se\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0f\n\x07\x66\x65\x65prop\x18\x04 \x01(\r\x12\x13\n\x0b\x65xpirydelta\x18\x05 \x01(\r\"(\n\tRoutehint\x12\x1b\n\x04hops\x18\x01 \x03(\x0b\x32\r.cln.RouteHop\".\n\rRoutehintList\x12\x1d\n\x05hints\x18\x02 \x03(\x0b\x32\x0e.cln.Routehint\"\'\n\x08TlvEntry\x12\x0c\n\x04type\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x0c\"+\n\tTlvStream\x12\x1e\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\r.cln.TlvEntry*$\n\x0b\x43hannelSide\x12\t\n\x05LOCAL\x10\x00\x12\n\n\x06REMOTE\x10\x01*\x84\x02\n\x0c\x43hannelState\x12\x0c\n\x08Openingd\x10\x00\x12\x1a\n\x16\x43hanneldAwaitingLockin\x10\x01\x12\x12\n\x0e\x43hanneldNormal\x10\x02\x12\x18\n\x14\x43hanneldShuttingDown\x10\x03\x12\x17\n\x13\x43losingdSigexchange\x10\x04\x12\x14\n\x10\x43losingdComplete\x10\x05\x12\x16\n\x12\x41waitingUnilateral\x10\x06\x12\x14\n\x10\x46undingSpendSeen\x10\x07\x12\x0b\n\x07Onchain\x10\x08\x12\x15\n\x11\x44ualopendOpenInit\x10\t\x12\x1b\n\x17\x44ualopendAwaitingLockin\x10\n*\x8a\x02\n\tHtlcState\x12\x0f\n\x0bSentAddHtlc\x10\x00\x12\x11\n\rSentAddCommit\x10\x01\x12\x15\n\x11RcvdAddRevocation\x10\x02\x12\x14\n\x10RcvdAddAckCommit\x10\x03\x12\x18\n\x14SentAddAckRevocation\x10\x04\x12\x18\n\x14RcvdAddAckRevocation\x10\x05\x12\x12\n\x0eRcvdRemoveHtlc\x10\x06\x12\x14\n\x10RcvdRemoveCommit\x10\x07\x12\x18\n\x14SentRemoveRevocation\x10\x08\x12\x17\n\x13SentRemoveAckCommit\x10\t\x12\x1b\n\x17RcvdRemoveAckRevocation\x10\nb\x06proto3') - -_CHANNELSIDE = DESCRIPTOR.enum_types_by_name['ChannelSide'] -ChannelSide = enum_type_wrapper.EnumTypeWrapper(_CHANNELSIDE) -_CHANNELSTATE = DESCRIPTOR.enum_types_by_name['ChannelState'] -ChannelState = enum_type_wrapper.EnumTypeWrapper(_CHANNELSTATE) -_HTLCSTATE = DESCRIPTOR.enum_types_by_name['HtlcState'] -HtlcState = enum_type_wrapper.EnumTypeWrapper(_HTLCSTATE) -LOCAL = 0 -REMOTE = 1 -Openingd = 0 -ChanneldAwaitingLockin = 1 -ChanneldNormal = 2 -ChanneldShuttingDown = 3 -ClosingdSigexchange = 4 -ClosingdComplete = 5 -AwaitingUnilateral = 6 -FundingSpendSeen = 7 -Onchain = 8 -DualopendOpenInit = 9 -DualopendAwaitingLockin = 10 -SentAddHtlc = 0 -SentAddCommit = 1 -RcvdAddRevocation = 2 -RcvdAddAckCommit = 3 -SentAddAckRevocation = 4 -RcvdAddAckRevocation = 5 -RcvdRemoveHtlc = 6 -RcvdRemoveCommit = 7 -SentRemoveRevocation = 8 -SentRemoveAckCommit = 9 -RcvdRemoveAckRevocation = 10 - - -_AMOUNT = DESCRIPTOR.message_types_by_name['Amount'] -_AMOUNTORALL = DESCRIPTOR.message_types_by_name['AmountOrAll'] -_AMOUNTORANY = DESCRIPTOR.message_types_by_name['AmountOrAny'] -_CHANNELSTATECHANGECAUSE = DESCRIPTOR.message_types_by_name['ChannelStateChangeCause'] -_OUTPOINT = DESCRIPTOR.message_types_by_name['Outpoint'] -_FEERATE = DESCRIPTOR.message_types_by_name['Feerate'] -_OUTPUTDESC = DESCRIPTOR.message_types_by_name['OutputDesc'] -_ROUTEHOP = DESCRIPTOR.message_types_by_name['RouteHop'] -_ROUTEHINT = DESCRIPTOR.message_types_by_name['Routehint'] -_ROUTEHINTLIST = DESCRIPTOR.message_types_by_name['RoutehintList'] -_TLVENTRY = DESCRIPTOR.message_types_by_name['TlvEntry'] -_TLVSTREAM = DESCRIPTOR.message_types_by_name['TlvStream'] -Amount = _reflection.GeneratedProtocolMessageType('Amount', (_message.Message,), { - 'DESCRIPTOR' : _AMOUNT, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.Amount) - }) -_sym_db.RegisterMessage(Amount) - -AmountOrAll = _reflection.GeneratedProtocolMessageType('AmountOrAll', (_message.Message,), { - 'DESCRIPTOR' : _AMOUNTORALL, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.AmountOrAll) - }) -_sym_db.RegisterMessage(AmountOrAll) - -AmountOrAny = _reflection.GeneratedProtocolMessageType('AmountOrAny', (_message.Message,), { - 'DESCRIPTOR' : _AMOUNTORANY, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.AmountOrAny) - }) -_sym_db.RegisterMessage(AmountOrAny) - -ChannelStateChangeCause = _reflection.GeneratedProtocolMessageType('ChannelStateChangeCause', (_message.Message,), { - 'DESCRIPTOR' : _CHANNELSTATECHANGECAUSE, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.ChannelStateChangeCause) - }) -_sym_db.RegisterMessage(ChannelStateChangeCause) - -Outpoint = _reflection.GeneratedProtocolMessageType('Outpoint', (_message.Message,), { - 'DESCRIPTOR' : _OUTPOINT, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.Outpoint) - }) -_sym_db.RegisterMessage(Outpoint) - -Feerate = _reflection.GeneratedProtocolMessageType('Feerate', (_message.Message,), { - 'DESCRIPTOR' : _FEERATE, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.Feerate) - }) -_sym_db.RegisterMessage(Feerate) - -OutputDesc = _reflection.GeneratedProtocolMessageType('OutputDesc', (_message.Message,), { - 'DESCRIPTOR' : _OUTPUTDESC, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.OutputDesc) - }) -_sym_db.RegisterMessage(OutputDesc) - -RouteHop = _reflection.GeneratedProtocolMessageType('RouteHop', (_message.Message,), { - 'DESCRIPTOR' : _ROUTEHOP, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.RouteHop) - }) -_sym_db.RegisterMessage(RouteHop) - -Routehint = _reflection.GeneratedProtocolMessageType('Routehint', (_message.Message,), { - 'DESCRIPTOR' : _ROUTEHINT, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.Routehint) - }) -_sym_db.RegisterMessage(Routehint) - -RoutehintList = _reflection.GeneratedProtocolMessageType('RoutehintList', (_message.Message,), { - 'DESCRIPTOR' : _ROUTEHINTLIST, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.RoutehintList) - }) -_sym_db.RegisterMessage(RoutehintList) - -TlvEntry = _reflection.GeneratedProtocolMessageType('TlvEntry', (_message.Message,), { - 'DESCRIPTOR' : _TLVENTRY, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.TlvEntry) - }) -_sym_db.RegisterMessage(TlvEntry) - -TlvStream = _reflection.GeneratedProtocolMessageType('TlvStream', (_message.Message,), { - 'DESCRIPTOR' : _TLVSTREAM, - '__module__' : 'primitives_pb2' - # @@protoc_insertion_point(class_scope:cln.TlvStream) - }) -_sym_db.RegisterMessage(TlvStream) - -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - _CHANNELSIDE._serialized_start=718 - _CHANNELSIDE._serialized_end=754 - _CHANNELSTATE._serialized_start=757 - _CHANNELSTATE._serialized_end=1017 - _HTLCSTATE._serialized_start=1020 - _HTLCSTATE._serialized_end=1286 - _AMOUNT._serialized_start=25 - _AMOUNT._serialized_end=47 - _AMOUNTORALL._serialized_start=49 - _AMOUNTORALL._serialized_end=117 - _AMOUNTORANY._serialized_start=119 - _AMOUNTORANY._serialized_end=187 - _CHANNELSTATECHANGECAUSE._serialized_start=189 - _CHANNELSTATECHANGECAUSE._serialized_end=214 - _OUTPOINT._serialized_start=216 - _OUTPOINT._serialized_end=256 - _FEERATE._serialized_start=258 - _FEERATE._serialized_end=362 - _OUTPUTDESC._serialized_start=364 - _OUTPUTDESC._serialized_end=422 - _ROUTEHOP._serialized_start=424 - _ROUTEHOP._serialized_end=540 - _ROUTEHINT._serialized_start=542 - _ROUTEHINT._serialized_end=582 - _ROUTEHINTLIST._serialized_start=584 - _ROUTEHINTLIST._serialized_end=630 - _TLVENTRY._serialized_start=632 - _TLVENTRY._serialized_end=671 - _TLVSTREAM._serialized_start=673 - _TLVSTREAM._serialized_end=716 -# @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 00194e2d3872..7b875d22f8b6 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -10,7 +10,6 @@ from decimal import Decimal from pyln.client import LightningRpc from pyln.client import Millisatoshi -from pyln.testing import grpc import ephemeral_port_reserve # type: ignore import json @@ -80,7 +79,9 @@ def env(name, default=None): SLOW_MACHINE = env("SLOW_MACHINE", "0") == "1" DEPRECATED_APIS = env("DEPRECATED_APIS", "0") == "1" TIMEOUT = int(env("TIMEOUT", 180 if SLOW_MACHINE else 60)) +SUBDAEMON = env("SUBDAEMON", "") EXPERIMENTAL_DUAL_FUND = env("EXPERIMENTAL_DUAL_FUND", "0") == "1" +EXPERIMENTAL_SPLICING = env("EXPERIMENTAL_SPLICING", "0") == "1" def wait_for(success, timeout=TIMEOUT): @@ -384,6 +385,45 @@ def f(*args): return f +class LssD(TailableProc): + def __init__(self, directory, rpcport=None): + lss_dir = os.path.join(directory, 'lss') + TailableProc.__init__(self, lss_dir, verbose=False) + + if rpcport is None: + self.reserved_rpcport = reserve_unused_port() + rpcport = self.reserved_rpcport + else: + self.reserved_rpcport = None + + self.rpcport = rpcport + self.prefix = 'lss' + + if not os.path.exists(lss_dir): + os.makedirs(lss_dir) + + self.cmd_line = [ + 'lssd', + '--datadir={}'.format(lss_dir), + '--port={}'.format(rpcport), + ] + + def __del__(self): + if self.reserved_rpcport is not None: + drop_unused_port(self.reserved_rpcport) + + def start(self): + self.env['RUST_LOG'] = 'debug' + TailableProc.start(self) + self.wait_for_log("ready on", timeout=TIMEOUT) + + logging.info("LssD started") + + def stop(self): + logging.info("Stopping LssD") + return TailableProc.stop(self) + + class BitcoinD(TailableProc): def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None): @@ -579,11 +619,49 @@ def getnewaddress(self): return info['unconfidential'] +class ValidatingLightningSignerD(TailableProc): + def __init__(self, vlsd_dir, vlsd_port, node_id, network): + TailableProc.__init__(self, vlsd_dir, verbose=True) + self.executable = env("REMOTE_SIGNER_CMD", 'vlsd2') + os.environ['ALLOWLIST'] = env( + 'REMOTE_SIGNER_ALLOWLIST', + 'contrib/remote_hsmd/TESTING_ALLOWLIST') + self.opts = [ + '--network={}'.format(network), + '--datadir={}'.format(vlsd_dir), + '--connect=http://localhost:{}'.format(vlsd_port), + '--integration-test', + ] + self.prefix = 'vlsd2-%d' % (node_id) + self.vlsd_port = vlsd_port + + @property + def cmd_line(self): + return [self.executable] + self.opts + + def start(self, stdin=None, stdout_redir=True, stderr_redir=True, + wait_for_initialized=True): + TailableProc.start(self, stdin, stdout_redir, stderr_redir) + # We need to always wait for initialization + self.wait_for_log("vlsd2 git_desc") + logging.info("vlsd2 started") + + def stop(self, timeout=10): + logging.info("stopping vlsd2") + rc = TailableProc.stop(self, timeout) + logging.info("vlsd2 stopped") + self.logs_catchup() + return rc + + def __del__(self): + self.logs_catchup() + class LightningD(TailableProc): def __init__( self, lightning_dir, bitcoindproxy, + lssd_port, port=9735, random_hsm=False, node_id=0, @@ -596,9 +674,16 @@ def __init__( self.port = port self.cmd_prefix = [] self.disconnect_file = None + self.lightning_dir = lightning_dir + self.use_vlsd = False + self.vlsd_dir = os.path.join(lightning_dir, "vlsd") + self.vlsd_port = None + self.vlsd = None + self.node_id = node_id self.rpcproxy = bitcoindproxy self.env['CLN_PLUGIN_LOG'] = "cln_plugin=trace,cln_rpc=trace,cln_grpc=trace,debug" + self.lssd_port = lssd_port self.opts = LIGHTNINGD_CONFIG.copy() opts = { @@ -618,12 +703,38 @@ def __init__( if grpc_port is not None: opts['grpc-port'] = grpc_port + if SUBDAEMON: + assert node_id > 0 + subdaemons = SUBDAEMON.split(',') + # VLS_SERIAL_SELECT "swaps" the selected item with the first item + select = env("VLS_SERIAL_SELECT", '1') + if node_id == int(select): + ndx = 1 + elif node_id == 1: + ndx = int(select) + else: + ndx = node_id + if ndx > len(subdaemons): + # use the last element if not as many specifiers as nodes + opts['subdaemon'] = subdaemons[-1] + else: + # use the matching specifier + opts['subdaemon'] = subdaemons[ndx - 1] + + print(f"starting node {node_id} with subdaemon {opts['subdaemon']}") + if SUBDAEMON == 'hsmd:remote_hsmd_socket': + self.use_vlsd = True + for k, v in opts.items(): self.opts[k] = v if not os.path.exists(os.path.join(lightning_dir, TEST_NETWORK)): os.makedirs(os.path.join(lightning_dir, TEST_NETWORK)) + if self.use_vlsd: + if not os.path.exists(self.vlsd_dir): + os.makedirs(self.vlsd_dir) + # Last 32-bytes of final part of dir -> seed. seed = (bytes(re.search('([^/]+)/*$', lightning_dir).group(1), encoding='utf-8') + bytes(32))[:32] if not random_hsm: @@ -640,6 +751,10 @@ def __init__( self.early_opts = [] def cleanup(self): + if self.use_vlsd: + # Make sure the remotesigner is shutdown + self.vlsd.stop() + # To force blackhole to exit, disconnect file must be truncated! if self.disconnect_file: with open(self.disconnect_file, "w") as f: @@ -660,12 +775,65 @@ def cmd_line(self): return self.cmd_prefix + [self.executable] + self.early_opts + opts + def __del__(self): + if self.vlsd_port is not None: + drop_unused_port(self.vlsd_port) + def start(self, stdin=None, wait_for_initialized=True, stderr_redir=False): - self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport - TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir) - if wait_for_initialized: - self.wait_for_log("Server started with public key") - logging.info("LightningD started") + try: + self.env['VLS_LSS'] = f"http://localhost:{self.lssd_port}" + self.env['RUST_LOG'] = 'debug' + # Some of the remote hsmd proxies need a bitcoind RPC connection + self.env['BITCOIND_RPC_URL'] = 'http://{}:{}@localhost:{}'.format( + BITCOIND_CONFIG['rpcuser'], + BITCOIND_CONFIG['rpcpassword'], + BITCOIND_CONFIG['rpcport']) + + # The remote hsmd proxies need to know which network we are using + if 'network' in self.opts: + self.env['VLS_NETWORK'] = self.opts['network'] + + self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport + + if self.use_vlsd: + self.vlsd_port = reserve_unused_port() + # We can't do this in the constructor because we need a new port on each restart. + self.env['VLS_PORT'] = str(self.vlsd_port) + # Kill any previous vlsd (we may have been restarted) + if self.vlsd is not None: + logging.info("killing prior vlsd") + self.vlsd.kill() + + TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir) + + if self.use_vlsd: + # Start the remote signer first + self.vlsd = ValidatingLightningSignerD( + self.vlsd_dir, self.vlsd_port, self.node_id, self.opts['network']) + self.vlsd.start( + stdin, stdout_redir=True, stderr_redir=True, + wait_for_initialized=wait_for_initialized) + + if wait_for_initialized: + self.wait_for_log("Server started with public key") + logging.info("LightningD started") + except Exception: + if self.use_vlsd: + # LightningD didn't start, stop the remotesigner + self.vlsd.stop() + raise + + def stop(self, timeout=10): + if self.use_vlsd: + # Stop the remote signer first + self.vlsd.stop(timeout) + return TailableProc.stop(self, timeout) + + def kill(self): + if self.use_vlsd: + # Kill the remote signer first + self.vlsd.kill() + TailableProc.kill(self) def wait(self, timeout=TIMEOUT): """Wait for the daemon to stop for up to timeout seconds @@ -732,7 +900,7 @@ def call(self, method, payload=None, cmdprefix=None, filter=None): class LightningNode(object): - def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fail=False, + def __init__(self, node_id, lightning_dir, bitcoind, lssd, executor, valgrind, may_fail=False, may_reconnect=False, allow_broken_log=False, allow_warning=False, @@ -742,6 +910,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai valgrind_plugins=True, **kwargs): self.bitcoin = bitcoind + self.lssd = lssd self.executor = executor self.may_fail = may_fail self.may_reconnect = may_reconnect @@ -761,6 +930,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.daemon = LightningD( lightning_dir, bitcoindproxy=bitcoind.get_proxy(), + lssd_port=lssd.rpcport, port=port, random_hsm=random_hsm, node_id=node_id, grpc_port=self.grpc_port, ) @@ -788,6 +958,8 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.daemon.opts["dev-no-reconnect"] = None if EXPERIMENTAL_DUAL_FUND: self.daemon.opts["experimental-dual-fund"] = None + if EXPERIMENTAL_SPLICING: + self.daemon.opts["experimental-splicing"] = None if options is not None: self.daemon.opts.update(options) @@ -820,6 +992,7 @@ def _create_rpc(self, jsonschemas): self._create_jsonrpc_rpc(jsonschemas) def _create_grpc_rpc(self): + from pyln.testing import grpc self.grpc_port = reserve_unused_port() d = self.lightning_dir / TEST_NETWORK d.mkdir(parents=True, exist_ok=True) @@ -881,8 +1054,8 @@ def grpc(self): creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) - from pyln.testing import node_pb2_grpc as nodegrpc - return nodegrpc.NodeStub(channel) + from pyln import grpc as clnpb + return clnpb.NodeStub(channel) def connect(self, remote_node): self.rpc.connect(remote_node.info['id'], '127.0.0.1', remote_node.port) @@ -1185,6 +1358,9 @@ def pay(self, dst, amt, label=None): 'channel': scid } + # let the signer know this payment is coming + self.rpc.preapproveinvoice(bolt11=inv['bolt11']) + # sendpay is async now self.rpc.sendpay([routestep], rhash, payment_secret=psecret, bolt11=inv['bolt11']) # wait for sendpay to comply @@ -1281,7 +1457,8 @@ def mine_txid_or_rbf(self, txid, numblocks=1): # Hack so we can mutate the txid: pass it in a list def rbf_or_txid_broadcast(txids): # RBF onchain txid d4b597505b543a4b8b42ab4d481fd7a533febb7e7df150ca70689e6d046612f7 (fee 6564sat) with txid 979878b8f855d3895d1cd29bd75a60b21492c4842e38099186a8e649bee02c7c (fee 8205sat) - line = self.daemon.is_in_log("RBF onchain txid {}".format(txids[-1])) + # We can have noop RBFs: ignore those (increases are logged INFO level) + line = self.daemon.is_in_log(" INFO .*RBF (onchain|HTLC) txid {}".format(txids[-1])) if line is not None: newtxid = re.search(r'with txid ([0-9a-fA-F]*)', line).group(1) txids.append(newtxid) @@ -1348,11 +1525,18 @@ def passes_filters(hmsg, filters): def config(self, config_name): try: - opt = self.rpc.listconfigs(config_name) - return opt[config_name] + config = self.rpc.listconfigs(config_name) except RpcError: return None + config = config['configs'][config_name] + for valfield in ('set', + 'value_str', 'value_bool', 'value_int', + 'values_str', 'values_bool', 'values_int'): + if valfield in config: + return config[valfield] + raise ValueError("Unknown value in config {}".format(config)) + def dev_pay(self, bolt11, amount_msat=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): @@ -1427,7 +1611,7 @@ def flock(directory: Path): class NodeFactory(object): """A factory to setup and start `lightningd` daemons. """ - def __init__(self, request, testname, bitcoind, executor, directory, + def __init__(self, request, testname, bitcoind, lssd, executor, directory, db_provider, node_cls, jsonschemas): if request.node.get_closest_marker("slow_test") and SLOW_MACHINE: self.valgrind = False @@ -1439,6 +1623,7 @@ def __init__(self, request, testname, bitcoind, executor, directory, self.reserved_ports = [] self.executor = executor self.bitcoind = bitcoind + self.lssd = lssd self.directory = directory self.lock = threading.Lock() self.db_provider = db_provider @@ -1524,7 +1709,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, db = self.db_provider.get_db(os.path.join(lightning_dir, TEST_NETWORK), self.testname, node_id) db.provider = self.db_provider node = self.node_cls( - node_id, lightning_dir, self.bitcoind, self.executor, self.valgrind, db=db, + node_id, lightning_dir, self.bitcoind, self.lssd, self.executor, self.valgrind, db=db, port=port, options=options, may_fail=may_fail or expect_fail, jsonschemas=self.jsonschemas, **kwargs diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 04739676c469..b991ba276ce1 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-testing" -version = "23.05" +version = "23.08" description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" @@ -11,21 +11,26 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.7" -pytest = "^7.0.1" +python = "^3.8" +pytest = "^7" ephemeral-port-reserve = "^1.1.4" -psycopg2-binary = "^2.9.3" +psycopg2-binary = "^2.9" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" -pyln-client = ">=0.12.1" -Flask = "^2.0.3" -cheroot = "^8.6.0" -psutil = "^5.9.0" -grpcio = ">=1.47" -protobuf = ">=3.20.3,<4" +pyln-client = ">=23" +Flask = "^2" +cheroot = "^8" +psutil = "^5.9" + +grpcio = { version = "^1", optional = true } +pyln-grpc-proto = { version = "^0.1", optional = true } [tool.poetry.dev-dependencies] pyln-client = { path = "../pyln-client", develop = true} +pyln-grpc-proto = { path = "../pyln-grpc-proto", develop = true, optional = true} + +[tool.poetry.extras] +grpc = ["pyln-grpc-proto", "grpcio"] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/contrib/reprobuild/Dockerfile.bionic b/contrib/reprobuild/Dockerfile.bionic deleted file mode 100644 index 0395a8140c41..000000000000 --- a/contrib/reprobuild/Dockerfile.bionic +++ /dev/null @@ -1,73 +0,0 @@ -FROM bionic - -ENV TZ=UTC -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -ENV RUST_PROFILE=release -ENV PATH=/root/.cargo/bin:/root/.pyenv/shims:/root/.pyenv/bin:$PATH -ENV PROTOC_VERSION=22.0 - -RUN sed -i '/updates/d' /etc/apt/sources.list && \ - sed -i '/security/d' /etc/apt/sources.list - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - ca-certificates \ - sudo \ - build-essential \ - libsodium23 \ - libpq-dev \ - git \ - file \ - autoconf \ - debianutils \ - gettext \ - zip \ - unzip \ - wget - -# Need to fetch a python version that is >= 3.7 since that's the -# lowest version supported by pyln. This is just temporary until we -# drop support for ubuntu 18.04 -RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv \ - && apt-get install -y --no-install-recommends \ - libbz2-dev \ - libffi-dev \ - libreadline-dev \ - libsqlite3-dev \ - libssl-dev \ - zlib1g-dev \ - && pyenv install 3.7.0 \ - && pyenv global 3.7.0 - -RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp/get-pip.py \ - && rm /tmp/get-pip.py \ - && pip install poetry - -RUN wget https://sh.rustup.rs -O rustup-install.sh && \ - bash rustup-install.sh --default-toolchain none --quiet -y && \ - rm rustup-install.sh && \ - /root/.cargo/bin/rustup install 1.62 - -# Download protoc manually, it is in the update repos which we -# disabled above, so `apt-get` can't find it anymore. -RUN cd /tmp/ && \ - wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip && \ - unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip && \ - mv bin/protoc /usr/local/bin && \ - rm -rf include bin protoc-${PROTOC_VERSION}-linux-x86_64.zip - -RUN mkdir /build -WORKDIR /build - -CMD git clone /repo /build \ - && poetry export -o requirements.txt --without-hashes \ - && pip install -r requirements.txt\ - && tools/build-release.sh zipfile \ - && mkdir -p /repro \ - && cd /repro \ - && unzip /build/release/*.zip \ - && cd clightning* \ - && tools/repro-build.sh \ - && cp *.xz /build/release/* /repo/release/ \ - && cd /repo/release \ - && sha256sum * diff --git a/contrib/reprobuild/Dockerfile.focal b/contrib/reprobuild/Dockerfile.focal index e8be966cf880..c744b55e1a84 100644 --- a/contrib/reprobuild/Dockerfile.focal +++ b/contrib/reprobuild/Dockerfile.focal @@ -17,7 +17,6 @@ RUN apt-get update \ file \ gettext \ git \ - libgmp-dev \ libpq-dev \ libsodium23 \ libtool \ @@ -43,10 +42,13 @@ RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp && rm /tmp/get-pip.py \ && pip install poetry +RUN pip3 install flask gunicorn json5 +RUN pip3 install --user flask_restx pyln-client + RUN wget https://sh.rustup.rs -O rustup-install.sh && \ bash rustup-install.sh --default-toolchain none --quiet -y && \ rm rustup-install.sh && \ - /root/.cargo/bin/rustup install 1.62 + /root/.cargo/bin/rustup install 1.65 # Download protoc manually, it is in the update repos which we # disabled above, so `apt-get` can't find it anymore. @@ -59,16 +61,12 @@ RUN cd /tmp/ && \ RUN mkdir /build WORKDIR /build -CMD git clone /repo /build \ - && poetry export -o requirements.txt --without-hashes \ +CMD poetry export -o requirements.txt --without-hashes \ && pip install -r requirements.txt \ - && tools/build-release.sh zipfile \ + && pip install -r plugins/clnrest/requirements.txt \ && mkdir -p /repro \ && cd /repro \ && unzip /build/release/*.zip \ && cd clightning* \ - && tools/repro-build.sh \ - && mkdir -p /repo/release \ - && cp *.xz /build/release/* /repo/release/ \ - && cd /repo/release/ \ - && sha256sum * + && tools/repro-build.sh \ + && cp *.xz /build/release/ diff --git a/contrib/reprobuild/Dockerfile.jammy b/contrib/reprobuild/Dockerfile.jammy index 9aed77177a04..8181cf8e597c 100644 --- a/contrib/reprobuild/Dockerfile.jammy +++ b/contrib/reprobuild/Dockerfile.jammy @@ -17,8 +17,7 @@ RUN apt-get update \ file \ gettext \ git \ - libgmp-dev \ - libsqlite3-dev \ + libsqlite3-dev \ libpq-dev \ libsodium23 \ libtool \ @@ -44,10 +43,13 @@ RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp && rm /tmp/get-pip.py \ && pip install poetry +RUN pip3 install flask gunicorn json5 +RUN pip3 install --user flask_restx pyln-client + RUN wget https://sh.rustup.rs -O rustup-install.sh && \ bash rustup-install.sh --default-toolchain none --quiet -y && \ rm rustup-install.sh && \ - /root/.cargo/bin/rustup install 1.62 + /root/.cargo/bin/rustup install 1.65 # Download protoc manually, it is in the update repos which we # disabled above, so `apt-get` can't find it anymore. @@ -60,16 +62,12 @@ RUN cd /tmp/ && \ RUN mkdir /build WORKDIR /build -CMD git clone /repo /build \ - && poetry export -o requirements.txt --without-hashes \ +CMD poetry export -o requirements.txt --without-hashes \ && pip install -r requirements.txt \ - && tools/build-release.sh zipfile \ + && pip install -r plugins/clnrest/requirements.txt \ && mkdir -p /repro \ && cd /repro \ && unzip /build/release/*.zip \ && cd clightning* \ - && tools/repro-build.sh \ - && mkdir -p /repo/release \ - && cp *.xz /build/release/* /repo/release/ \ - && cd /repo/release/ \ - && sha256sum * + && tools/repro-build.sh \ + && cp *.xz /build/release/ diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 07fab1c3dc00..0cc348668242 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -82,7 +82,7 @@ start_nodes() { LN_NODES=$node_count - for i in $(seq $node_count); do + for i in $(seq "$node_count"); do socket=$(( 7070 + i * 101)) mkdir -p "/tmp/l$i-$network" # Node config @@ -100,6 +100,7 @@ start_nodes() { dev-fast-gossip dev-bitcoind-poll=5 experimental-dual-fund + experimental-splicing experimental-offers funder-policy=match funder-policy-mod=100 @@ -114,7 +115,7 @@ start_nodes() { # Start the lightning nodes test -f "/tmp/l$i-$network/lightningd-$network.pid" || \ - $EATMYDATA "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" & + $EATMYDATA "$LIGHTNINGD" "--network=$network" "--lightning-dir=/tmp/l$i-$network" "--bitcoin-datadir=$PATH_TO_BITCOIN" "--database-upgrade=true" & # shellcheck disable=SC2139 disable=SC2086 alias l$i-cli="$LCLI --lightning-dir=/tmp/l$i-$network" # shellcheck disable=SC2139 disable=SC2086 @@ -126,7 +127,7 @@ start_nodes() { fi # Give a hint. echo "Commands: " - for i in $(seq $node_count); do + for i in $(seq "$node_count"); do echo " l$i-cli, l$i-log," done } @@ -134,23 +135,23 @@ start_nodes() { start_ln() { # Start bitcoind in the background test -f "$PATH_TO_BITCOIN/regtest/bitcoind.pid" || \ - bitcoind -regtest -txindex -fallbackfee=0.00000253 -daemon + bitcoind -datadir="$PATH_TO_BITCOIN" -regtest -txindex -fallbackfee=0.00000253 -daemon # Wait for it to start. - while ! bitcoin-cli -regtest ping 2> /tmp/null; do echo "awaiting bitcoind..." && sleep 1; done + while ! bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest ping 2> /tmp/null; do echo "awaiting bitcoind..." && sleep 1; done # Kick it out of initialblockdownload if necessary - if bitcoin-cli -regtest getblockchaininfo | grep -q 'initialblockdownload.*true'; then + if bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest getblockchaininfo | grep -q 'initialblockdownload.*true'; then # Modern bitcoind needs createwallet echo "Making \"default\" bitcoind wallet." - bitcoin-cli -regtest createwallet default >/dev/null 2>&1 - # But it might already exist, load it - bitcoin-cli -regtest loadwallet default - bitcoin-cli -regtest generatetoaddress 1 "$(bitcoin-cli -regtest getnewaddress)" > /dev/null + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest createwallet default >/dev/null 2>&1 + # But it might already exist, load it + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest loadwallet default + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest generatetoaddress 1 "$(bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest getnewaddress)" > /dev/null else - bitcoin-cli -regtest loadwallet default + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest loadwallet default fi - alias bt-cli='bitcoin-cli -regtest' + alias bt-cli='bitcoin-cli -datadir=$PATH_TO_BITCOIN -regtest' if [ -z "$1" ]; then nodes=2 @@ -164,16 +165,16 @@ start_ln() { ensure_bitcoind_funds() { if [ -z "$ADDRESS" ]; then - ADDRESS=$(bitcoin-cli "$WALLET" -regtest getnewaddress) + ADDRESS=$(bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest "$WALLET" getnewaddress) fi - balance=$(bitcoin-cli -regtest "$WALLET" getbalance) + balance=$(bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest "$WALLET" getbalance) if [ 1 -eq "$(echo "$balance"'<1' | bc -l)" ]; then printf "%s" "Mining into address " "$ADDRESS""... " - bitcoin-cli -regtest generatetoaddress 100 "$ADDRESS" > /dev/null + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest generatetoaddress 100 "$ADDRESS" > /dev/null echo "done." fi @@ -195,16 +196,16 @@ fund_nodes() { done if [ -z "$NODES" ]; then - NODES=$(seq $node_count) + NODES=$(seq "$node_count") fi WALLET="-rpcwallet=$WALLET" - ADDRESS=$(bitcoin-cli "$WALLET" -regtest getnewaddress) + ADDRESS=$(bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest "$WALLET" getnewaddress) ensure_bitcoind_funds - echo "bitcoind balance:" "$(bitcoin-cli -regtest "$WALLET" getbalance)" + echo "bitcoind balance:" "$(bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest "$WALLET" getbalance)" last_node="" @@ -228,9 +229,9 @@ fund_nodes() { ensure_bitcoind_funds - bitcoin-cli -regtest "$WALLET" sendtoaddress "$L1_WALLET_ADDR" 1 > /dev/null + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest "$WALLET" sendtoaddress "$L1_WALLET_ADDR" 1 > /dev/null - bitcoin-cli -regtest generatetoaddress 1 "$ADDRESS" > /dev/null + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest generatetoaddress 1 "$ADDRESS" > /dev/null printf "%s" "Waiting for lightning node funds... " @@ -245,7 +246,7 @@ fund_nodes() { $LCLI --lightning-dir=/tmp/l"$node1"-regtest fundchannel "$L2_NODE_ID" 1000000 > /dev/null - bitcoin-cli -regtest generatetoaddress 6 "$ADDRESS" > /dev/null + bitcoin-cli -datadir="$PATH_TO_BITCOIN" -regtest generatetoaddress 6 "$ADDRESS" > /dev/null printf "%s" "Waiting for confirmation... " @@ -262,7 +263,7 @@ fund_nodes() { stop_nodes() { network=${1:-regtest} if [ -n "$LN_NODES" ]; then - for i in $(seq $LN_NODES); do + for i in $(seq "$LN_NODES"); do test ! -f "/tmp/l$i-$network/lightningd-$network.pid" || \ (kill "$(cat "/tmp/l$i-$network/lightningd-$network.pid")"; \ rm "/tmp/l$i-$network/lightningd-$network.pid") diff --git a/db/bindings.c b/db/bindings.c index 119f82a62c6f..7a0951e31760 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -16,6 +16,14 @@ #define NSEC_IN_SEC 1000000000 +static size_t check_bind_pos(struct db_stmt *stmt) +{ + size_t pos = ++stmt->bind_pos; + assert(pos < tal_count(stmt->bindings)); + + return pos; +} + /* Local helpers once you have column number */ static bool db_column_is_null(struct db_stmt *stmt, int col) { @@ -29,17 +37,15 @@ static bool db_column_null_warn(struct db_stmt *stmt, const char *colname, if (!db_column_is_null(stmt, col)) return false; - /* FIXME: log broken? */ -#if DEVELOPER - db_fatal("Accessing a null column %s/%i in query %s", - colname, col, stmt->query->query); -#endif /* DEVELOPER */ + db_warn(stmt->db, "Accessing a null column %s/%i in query %s", + colname, col, stmt->query->query); + return true; } -void db_bind_int(struct db_stmt *stmt, int pos, int val) +void db_bind_int(struct db_stmt *stmt, int val) { - assert(pos < tal_count(stmt->bindings)); + size_t pos = check_bind_pos(stmt); memcheck(&val, sizeof(val)); stmt->bindings[pos].type = DB_BINDING_INT; stmt->bindings[pos].v.i = val; @@ -60,58 +66,65 @@ int db_col_is_null(struct db_stmt *stmt, const char *colname) return db_column_is_null(stmt, db_query_colnum(stmt, colname)); } -void db_bind_null(struct db_stmt *stmt, int pos) +void db_bind_null(struct db_stmt *stmt) { - assert(pos < tal_count(stmt->bindings)); + size_t pos = check_bind_pos(stmt); stmt->bindings[pos].type = DB_BINDING_NULL; } -void db_bind_u64(struct db_stmt *stmt, int pos, u64 val) +void db_bind_u64(struct db_stmt *stmt, u64 val) { + size_t pos = check_bind_pos(stmt); + memcheck(&val, sizeof(val)); - assert(pos < tal_count(stmt->bindings)); stmt->bindings[pos].type = DB_BINDING_UINT64; stmt->bindings[pos].v.u64 = val; } -void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len) +void db_bind_s64(struct db_stmt *stmt, s64 val) { - assert(pos < tal_count(stmt->bindings)); + u64 uval = val; + db_bind_u64(stmt, uval); +} + +void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len) +{ + size_t pos = check_bind_pos(stmt); stmt->bindings[pos].type = DB_BINDING_BLOB; stmt->bindings[pos].v.blob = memcheck(val, len); stmt->bindings[pos].len = len; } -void db_bind_text(struct db_stmt *stmt, int pos, const char *val) +void db_bind_text(struct db_stmt *stmt, const char *val) { - assert(pos < tal_count(stmt->bindings)); + size_t pos = check_bind_pos(stmt); stmt->bindings[pos].type = DB_BINDING_TEXT; stmt->bindings[pos].v.text = val; stmt->bindings[pos].len = strlen(val); } -void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p) +void db_bind_preimage(struct db_stmt *stmt, const struct preimage *p) { - db_bind_blob(stmt, pos, p->r, sizeof(struct preimage)); + db_bind_blob(stmt, p->r, sizeof(struct preimage)); } -void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s) +void db_bind_sha256(struct db_stmt *stmt, const struct sha256 *s) { - db_bind_blob(stmt, pos, s->u.u8, sizeof(struct sha256)); + db_bind_blob(stmt, s->u.u8, sizeof(struct sha256)); } -void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s) +void db_bind_sha256d(struct db_stmt *stmt, const struct sha256_double *s) { - db_bind_sha256(stmt, pos, &s->sha); + db_bind_sha256(stmt, &s->sha); } -void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s) +void db_bind_secret(struct db_stmt *stmt, const struct secret *s) { assert(sizeof(s->data) == 32); - db_bind_blob(stmt, pos, s->data, sizeof(s->data)); + db_bind_blob(stmt, s->data, sizeof(s->data)); } -void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s) +void db_bind_secret_arr(struct db_stmt *stmt, const struct secret *s) { size_t num = tal_count(s), elsize = sizeof(s->data); u8 *ser = tal_arr(stmt, u8, num * elsize); @@ -119,30 +132,30 @@ void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s) for (size_t i = 0; i < num; ++i) memcpy(ser + i * elsize, &s[i], elsize); - db_bind_blob(stmt, col, ser, tal_count(ser)); + db_bind_blob(stmt, ser, tal_count(ser)); } -void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t) +void db_bind_txid(struct db_stmt *stmt, const struct bitcoin_txid *t) { - db_bind_sha256d(stmt, pos, &t->shad); + db_bind_sha256d(stmt, &t->shad); } -void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id) +void db_bind_channel_id(struct db_stmt *stmt, const struct channel_id *id) { - db_bind_blob(stmt, pos, id->id, sizeof(id->id)); + db_bind_blob(stmt, id->id, sizeof(id->id)); } -void db_bind_channel_type(struct db_stmt *stmt, int pos, const struct channel_type *type) +void db_bind_channel_type(struct db_stmt *stmt, const struct channel_type *type) { - db_bind_talarr(stmt, pos, type->features); + db_bind_talarr(stmt, type->features); } -void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *id) +void db_bind_node_id(struct db_stmt *stmt, const struct node_id *id) { - db_bind_blob(stmt, pos, id->k, sizeof(id->k)); + db_bind_blob(stmt, id->k, sizeof(id->k)); } -void db_bind_node_id_arr(struct db_stmt *stmt, int col, +void db_bind_node_id_arr(struct db_stmt *stmt, const struct node_id *ids) { /* Copy into contiguous array: ARM will add padding to struct node_id! */ @@ -155,23 +168,23 @@ void db_bind_node_id_arr(struct db_stmt *stmt, int col, ids[i].k, sizeof(ids[i].k)); } - db_bind_blob(stmt, col, arr, tal_count(arr)); + db_bind_blob(stmt, arr, tal_count(arr)); } -void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) +void db_bind_pubkey(struct db_stmt *stmt, const struct pubkey *pk) { u8 *der = tal_arr(stmt, u8, PUBKEY_CMPR_LEN); pubkey_to_der(der, pk); - db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); + db_bind_blob(stmt, der, PUBKEY_CMPR_LEN); } -void db_bind_short_channel_id(struct db_stmt *stmt, int col, +void db_bind_short_channel_id(struct db_stmt *stmt, const struct short_channel_id *id) { - db_bind_u64(stmt, col, id->u64); + db_bind_u64(stmt, id->u64); } -void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, +void db_bind_short_channel_id_arr(struct db_stmt *stmt, const struct short_channel_id *id) { u8 *ser = tal_arr(stmt, u8, 0); @@ -180,69 +193,69 @@ void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, for (size_t i = 0; i < num; ++i) towire_short_channel_id(&ser, &id[i]); - db_bind_talarr(stmt, col, ser); + db_bind_talarr(stmt, ser); } -void db_bind_signature(struct db_stmt *stmt, int col, +void db_bind_signature(struct db_stmt *stmt, const secp256k1_ecdsa_signature *sig) { u8 *buf = tal_arr(stmt, u8, 64); int ret = secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, buf, sig); assert(ret == 1); - db_bind_blob(stmt, col, buf, 64); + db_bind_blob(stmt, buf, 64); } -void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t) +void db_bind_timeabs(struct db_stmt *stmt, struct timeabs t) { u64 timestamp = t.ts.tv_nsec + (((u64) t.ts.tv_sec) * ((u64) NSEC_IN_SEC)); - db_bind_u64(stmt, col, timestamp); + db_bind_u64(stmt, timestamp); } -void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx) +void db_bind_tx(struct db_stmt *stmt, const struct wally_tx *tx) { u8 *ser = linearize_wtx(stmt, tx); assert(ser); - db_bind_talarr(stmt, col, ser); + db_bind_talarr(stmt, ser); } -void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt) +void db_bind_psbt(struct db_stmt *stmt, const struct wally_psbt *psbt) { size_t bytes_written; const u8 *ser = psbt_get_bytes(stmt, psbt, &bytes_written); assert(ser); - db_bind_blob(stmt, col, ser, bytes_written); + db_bind_blob(stmt, ser, bytes_written); } -void db_bind_amount_msat(struct db_stmt *stmt, int pos, +void db_bind_amount_msat(struct db_stmt *stmt, const struct amount_msat *msat) { - db_bind_u64(stmt, pos, msat->millisatoshis); /* Raw: low level function */ + db_bind_u64(stmt, msat->millisatoshis); /* Raw: low level function */ } -void db_bind_amount_sat(struct db_stmt *stmt, int pos, +void db_bind_amount_sat(struct db_stmt *stmt, const struct amount_sat *sat) { - db_bind_u64(stmt, pos, sat->satoshis); /* Raw: low level function */ + db_bind_u64(stmt, sat->satoshis); /* Raw: low level function */ } -void db_bind_json_escape(struct db_stmt *stmt, int pos, +void db_bind_json_escape(struct db_stmt *stmt, const struct json_escape *esc) { - db_bind_text(stmt, pos, esc->s); + db_bind_text(stmt, esc->s); } -void db_bind_onionreply(struct db_stmt *stmt, int pos, const struct onionreply *r) +void db_bind_onionreply(struct db_stmt *stmt, const struct onionreply *r) { - db_bind_talarr(stmt, pos, r->contents); + db_bind_talarr(stmt, r->contents); } -void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr) +void db_bind_talarr(struct db_stmt *stmt, const u8 *arr) { if (!arr) - db_bind_null(stmt, col); + db_bind_null(stmt); else - db_bind_blob(stmt, col, arr, tal_bytelen(arr)); + db_bind_blob(stmt, arr, tal_bytelen(arr)); } static size_t db_column_bytes(struct db_stmt *stmt, int col) @@ -270,6 +283,11 @@ u64 db_col_u64(struct db_stmt *stmt, const char *colname) return stmt->db->config->column_u64_fn(stmt, col); } +u64 db_col_s64(struct db_stmt *stmt, const char *colname) +{ + return db_col_u64(stmt, colname); +} + int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) { size_t col = db_query_colnum(stmt, colname); @@ -312,6 +330,17 @@ char *db_col_strdup(const tal_t *ctx, return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); } +char *db_col_strdup_optional(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + if (db_column_is_null(stmt, col)) + return NULL; + + return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); +} + void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage) { @@ -339,17 +368,22 @@ void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *d memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); } +/* We don't assume sizeof(struct node_id) == sizeof(struct node_id.k), + * otherwise this would simply be a call to db_col_arr! + * Thanks ARM! */ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - const char *colname) + const char *colname) { size_t col = db_query_colnum(stmt, colname); struct node_id *ret; size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); const u8 *arr = db_column_blob(stmt, col); assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); - ret = tal_arr(ctx, struct node_id, n); - db_column_null_warn(stmt, colname, col); + if (db_column_is_null(stmt, col)) + return NULL; + + ret = tal_arr(ctx, struct node_id, n); for (size_t i = 0; i < n; i++) memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); @@ -392,7 +426,9 @@ db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char * size_t len; struct short_channel_id *ret; - db_column_null_warn(stmt, colname, col); + if (db_column_is_null(stmt, col)) + return NULL; + ser = db_column_blob(stmt, col); len = db_column_bytes(stmt, col); ret = tal_arr(ctx, struct short_channel_id, 0); @@ -430,19 +466,34 @@ struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char size_t col = db_query_colnum(stmt, colname); const u8 *src = db_column_blob(stmt, col); size_t len = db_column_bytes(stmt, col); + struct bitcoin_tx *tx; + bool is_null; - db_column_null_warn(stmt, colname, col); - return pull_bitcoin_tx(ctx, &src, &len); + is_null = db_column_null_warn(stmt, colname, col); + tx = pull_bitcoin_tx(ctx, &src, &len); + + if (is_null || tx) return tx; + + /* Column wasn't null, but we couldn't retrieve a valid wally_tx! */ + u8 *tx_dup = tal_dup_arr(stmt, u8, src, len, 0); + + db_fatal(stmt->db, + "db_col_tx: Invalid bitcoin transaction bytes retrieved: %s", + tal_hex(stmt, tx_dup)); + return NULL; } struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { + struct wally_psbt *psbt; size_t col = db_query_colnum(stmt, colname); const u8 *src = db_column_blob(stmt, col); size_t len = db_column_bytes(stmt, col); db_column_null_warn(stmt, colname, col); - return psbt_from_bytes(ctx, src, len); + psbt = psbt_from_bytes(ctx, src, len); + psbt_set_version(psbt, 2); + return psbt; } struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) @@ -472,11 +523,12 @@ void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, sourcelen = db_column_bytes(stmt, col); if (sourcelen % bytes != 0) - db_fatal("%s: %s/%zu column size for %zu not a multiple of %s (%zu)", + db_fatal(stmt->db, "%s: %s/%zu column size for %zu not a multiple of %s (%zu)", caller, colname, col, sourcelen, label, bytes); p = tal_arr_label(ctx, char, sourcelen, label); - memcpy(p, db_column_blob(stmt, col), sourcelen); + if (sourcelen != 0) + memcpy(p, db_column_blob(stmt, col), sourcelen); return p; } @@ -493,15 +545,14 @@ void db_col_amount_msat_or_default(struct db_stmt *stmt, msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ } -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, - struct amount_msat *msat) +struct amount_msat db_col_amount_msat(struct db_stmt *stmt, const char *colname) { - msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ + return amount_msat(db_col_u64(stmt, colname)); } -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) +struct amount_sat db_col_amount_sat(struct db_stmt *stmt, const char *colname) { - sat->satoshis = db_col_u64(stmt, colname); /* Raw: low level function */ + return amount_sat(db_col_u64(stmt, colname)); } struct json_escape *db_col_json_escape(const tal_t *ctx, diff --git a/db/bindings.h b/db/bindings.h index 4a5556eb07ae..f29fde99cab3 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -17,61 +17,65 @@ struct onionreply; struct wally_psbt; struct wally_tx; -int db_col_is_null(struct db_stmt *stmt, const char *colname); - -void db_bind_int(struct db_stmt *stmt, int pos, int val); -int db_col_int(struct db_stmt *stmt, const char *colname); - -void db_bind_null(struct db_stmt *stmt, int pos); -void db_bind_int(struct db_stmt *stmt, int pos, int val); -void db_bind_u64(struct db_stmt *stmt, int pos, u64 val); -void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len); -void db_bind_text(struct db_stmt *stmt, int pos, const char *val); -void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p); -void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s); -void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s); -void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s); -void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s); -void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t); -void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id); -void db_bind_channel_type(struct db_stmt *stmt, int pos, const struct channel_type *type); -void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); -void db_bind_node_id_arr(struct db_stmt *stmt, int col, +/* These bind the next `?` in stmt (they keep an internal counter). */ +void db_bind_null(struct db_stmt *stmt); +void db_bind_int(struct db_stmt *stmt, int val); +void db_bind_u64(struct db_stmt *stmt, u64 val); +void db_bind_s64(struct db_stmt *stmt, s64 val); +void db_bind_blob(struct db_stmt *stmt, const u8 *val, size_t len); +void db_bind_text(struct db_stmt *stmt, const char *val); +void db_bind_preimage(struct db_stmt *stmt, const struct preimage *p); +void db_bind_sha256(struct db_stmt *stmt, const struct sha256 *s); +void db_bind_sha256d(struct db_stmt *stmt, const struct sha256_double *s); +void db_bind_secret(struct db_stmt *stmt, const struct secret *s); +void db_bind_secret_arr(struct db_stmt *stmt, const struct secret *s); +void db_bind_txid(struct db_stmt *stmt, const struct bitcoin_txid *t); +void db_bind_channel_id(struct db_stmt *stmt, const struct channel_id *id); +void db_bind_channel_type(struct db_stmt *stmt, const struct channel_type *type); +void db_bind_node_id(struct db_stmt *stmt, const struct node_id *ni); +void db_bind_node_id_arr(struct db_stmt *stmt, const struct node_id *ids); -void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); -void db_bind_short_channel_id(struct db_stmt *stmt, int col, +void db_bind_pubkey(struct db_stmt *stmt, const struct pubkey *p); +void db_bind_short_channel_id(struct db_stmt *stmt, const struct short_channel_id *id); -void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, +void db_bind_short_channel_id_arr(struct db_stmt *stmt, const struct short_channel_id *id); -void db_bind_signature(struct db_stmt *stmt, int col, +void db_bind_signature(struct db_stmt *stmt, const secp256k1_ecdsa_signature *sig); -void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t); -void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx); -void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt); -void db_bind_amount_msat(struct db_stmt *stmt, int pos, +void db_bind_timeabs(struct db_stmt *stmt, struct timeabs t); +void db_bind_tx(struct db_stmt *stmt, const struct wally_tx *tx); +void db_bind_psbt(struct db_stmt *stmt, const struct wally_psbt *psbt); +void db_bind_amount_msat(struct db_stmt *stmt, const struct amount_msat *msat); -void db_bind_amount_sat(struct db_stmt *stmt, int pos, +void db_bind_amount_sat(struct db_stmt *stmt, const struct amount_sat *sat); -void db_bind_json_escape(struct db_stmt *stmt, int pos, +void db_bind_json_escape(struct db_stmt *stmt, const struct json_escape *esc); -void db_bind_onionreply(struct db_stmt *stmt, int col, +void db_bind_onionreply(struct db_stmt *stmt, const struct onionreply *r); -void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr); +void db_bind_talarr(struct db_stmt *stmt, const u8 *arr); /* Modern variants: get columns by name from SELECT */ /* Bridge function to get column number from SELECT (must exist) */ size_t db_query_colnum(const struct db_stmt *stmt, const char *colname); +int db_col_is_null(struct db_stmt *stmt, const char *colname); +int db_col_int(struct db_stmt *stmt, const char *colname); u64 db_col_u64(struct db_stmt *stmt, const char *colname); +u64 db_col_s64(struct db_stmt *stmt, const char *colname); size_t db_col_bytes(struct db_stmt *stmt, const char *colname); const void* db_col_blob(struct db_stmt *stmt, const char *colname); char *db_col_strdup(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +/* string or NULL */ +char *db_col_strdup_optional(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname); void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); +struct amount_msat db_col_amount_msat(struct db_stmt *stmt, const char *colname); +struct amount_sat db_col_amount_sat(struct db_stmt *stmt, const char *colname); struct json_escape *db_col_json_escape(const tal_t *ctx, struct db_stmt *stmt, const char *colname); void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha); void db_col_sha256d(struct db_stmt *stmt, const char *colname, struct sha256_double *shad); diff --git a/db/common.h b/db/common.h index 4ced5132a231..2e87bf05eadd 100644 --- a/db/common.h +++ b/db/common.h @@ -6,6 +6,7 @@ #include #include #include +#include /** * Macro to annotate a named SQL query. @@ -35,6 +36,10 @@ struct db { /* DB-specific context */ void *conn; + /* function to log warnings, or fail (if fatal == true). vprintf-style */ + void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap); + void *errorfn_arg; + /* The configuration for the current database driver */ const struct db_config *config; @@ -99,6 +104,9 @@ struct db_stmt { /* Our entry in the list of pending statements. */ struct list_node list; + /* Bind counter */ + int bind_pos; + /* Database we are querying */ struct db *db; @@ -183,9 +191,6 @@ struct db_config { const char **colnames, size_t num_cols); }; -void db_fatal(const char *fmt, ...) - PRINTF_FMT(1, 2); - /* Provide a way for DB backends to register themselves */ AUTODATA_TYPE(db_backends, struct db_config); diff --git a/db/db_postgres.c b/db/db_postgres.c index 0d3eb7f0bd2a..20323a122d04 100644 --- a/db/db_postgres.c +++ b/db/db_postgres.c @@ -92,7 +92,7 @@ static PGresult *db_postgres_do_exec(struct db_stmt *stmt) switch (b->type) { case DB_BINDING_UNINITIALIZED: - db_fatal("DB binding not initialized: position=%zu, " + db_fatal(stmt->db, "DB binding not initialized: position=%zu, " "query=\"%s\n", i, stmt->query->query); case DB_BINDING_UINT64: @@ -172,7 +172,7 @@ static u64 db_postgres_column_u64(struct db_stmt *stmt, int col) size_t expected = sizeof(bin), actual = PQgetlength(res, stmt->row, col); if (expected != actual) - db_fatal( + db_fatal(stmt->db, "u64 field doesn't match size: expected %zu, actual %zu\n", expected, actual); @@ -187,7 +187,7 @@ static s64 db_postgres_column_int(struct db_stmt *stmt, int col) size_t expected = sizeof(bin), actual = PQgetlength(res, stmt->row, col); if (expected != actual) - db_fatal( + db_fatal(stmt->db, "s32 field doesn't match size: expected %zu, actual %zu\n", expected, actual); diff --git a/db/db_sqlite3.c b/db/db_sqlite3.c index 26cbe2e6af07..876049ed304c 100644 --- a/db/db_sqlite3.c +++ b/db/db_sqlite3.c @@ -28,7 +28,8 @@ sqlite3 *conn2sql(void *conn) return wrapper->conn; } -static void replicate_statement(struct db_sqlite3 *wrapper, +static void replicate_statement(const struct db *db, + struct db_sqlite3 *wrapper, const char *qry) { sqlite3_stmt *stmt; @@ -43,7 +44,7 @@ static void replicate_statement(struct db_sqlite3 *wrapper, sqlite3_finalize(stmt); if (err != SQLITE_DONE) - db_fatal("Failed to replicate query: %s: %s: %s", + db_fatal(db, "Failed to replicate query: %s: %s: %s", sqlite3_errstr(err), sqlite3_errmsg(wrapper->backup_conn), qry); @@ -53,7 +54,7 @@ static void db_sqlite3_changes_add(struct db_sqlite3 *wrapper, struct db_stmt *stmt, const char *qry) { - replicate_statement(wrapper, qry); + replicate_statement(stmt->db, wrapper, qry); db_changes_add(stmt, qry); } @@ -125,7 +126,7 @@ static bool db_sqlite3_setup(struct db *db) struct db_sqlite3 *wrapper; if (!strstarts(db->filename, "sqlite3://") || strlen(db->filename) < 10) - db_fatal("Could not parse the wallet DSN: %s", db->filename); + db_fatal(db, "Could not parse the wallet DSN: %s", db->filename); /* Strip the scheme from the dsn. */ filename = db->filename + strlen("sqlite3://"); @@ -143,14 +144,14 @@ static bool db_sqlite3_setup(struct db *db) err = sqlite3_open_v2(filename, &sql, flags, NULL); if (err != SQLITE_OK) { - db_fatal("failed to open database %s: %s", filename, + db_fatal(db, "failed to open database %s: %s", filename, sqlite3_errstr(err)); } wrapper->conn = sql; err = sqlite3_extended_result_codes(wrapper->conn, 1); if (err != SQLITE_OK) { - db_fatal("failed to enable extended result codes: %s", + db_fatal(db, "failed to enable extended result codes: %s", sqlite3_errstr(err)); } @@ -161,7 +162,7 @@ static bool db_sqlite3_setup(struct db *db) &wrapper->backup_conn, flags, NULL); if (err != SQLITE_OK) { - db_fatal("failed to open backup database %s: %s", + db_fatal(db, "failed to open backup database %s: %s", backup_filename, sqlite3_errstr(err)); } @@ -173,7 +174,7 @@ static bool db_sqlite3_setup(struct db *db) sqlite3_finalize(stmt); if (err != SQLITE_DONE) { - db_fatal("failed to use backup database %s: %s", + db_fatal(db, "failed to use backup database %s: %s", backup_filename, sqlite3_errstr(err)); } @@ -189,13 +190,13 @@ static bool db_sqlite3_setup(struct db *db) wrapper->conn, "main"); if (!copier) { - db_fatal("failed to initiate copy to %s: %s", + db_fatal(db, "failed to initiate copy to %s: %s", backup_filename, sqlite3_errmsg(wrapper->backup_conn)); } err = sqlite3_backup_step(copier, -1); if (err != SQLITE_DONE) { - db_fatal("failed to copy database to %s: %s", + db_fatal(db, "failed to copy database to %s: %s", backup_filename, sqlite3_errstr(err)); } @@ -232,7 +233,7 @@ static bool db_sqlite3_query(struct db_stmt *stmt) int pos = i+1; switch (b->type) { case DB_BINDING_UNINITIALIZED: - db_fatal("DB binding not initialized: position=%zu, " + db_fatal(stmt->db, "DB binding not initialized: position=%zu, " "query=\"%s\n", i, stmt->query->query); case DB_BINDING_UINT64: @@ -332,7 +333,7 @@ static bool db_sqlite3_begin_tx(struct db *db) db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); return false; } - replicate_statement(wrapper, "BEGIN TRANSACTION;"); + replicate_statement(db, wrapper, "BEGIN TRANSACTION;"); return true; } @@ -349,7 +350,7 @@ static bool db_sqlite3_commit_tx(struct db *db) db->error = tal_fmt(db, "Failed to commit a transaction: %s", errmsg); return false; } - replicate_statement(wrapper, "COMMIT;"); + replicate_statement(db, wrapper, "COMMIT;"); return true; } @@ -432,7 +433,7 @@ static bool db_sqlite3_vacuum(struct db *db) db->error = tal_fmt(db, "%s", sqlite3_errmsg(conn2sql(db->conn))); sqlite3_finalize(stmt); - replicate_statement(wrapper, "VACUUM;"); + replicate_statement(db, wrapper, "VACUUM;"); return err == SQLITE_DONE; } @@ -495,7 +496,7 @@ static char **prepare_table_manip(const tal_t *ctx, * mirror changes to the db hook! */ bracket = strchr(sql, '('); if (!strstarts(sql, "CREATE TABLE") || !bracket) - db_fatal("Bad sql from prepare_table_manip %s: %s", + db_fatal(db, "Bad sql from prepare_table_manip %s: %s", tablename, sql); /* Split after ( by commas: any lower case is assumed to be a field */ diff --git a/db/exec.c b/db/exec.c index 21b9beef36b3..6d16d4588c39 100644 --- a/db/exec.c +++ b/db/exec.c @@ -62,16 +62,16 @@ void db_set_intvar(struct db *db, const char *varname, s64 val) { size_t changes; struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET intval=? WHERE name=?;")); - db_bind_int(stmt, 0, val); - db_bind_text(stmt, 1, varname); + db_bind_int(stmt, val); + db_bind_text(stmt, varname); db_exec_prepared_v2(stmt); changes = db_count_changes(stmt); tal_free(stmt); if (changes == 0) { stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, intval) VALUES (?, ?);")); - db_bind_text(stmt, 0, varname); - db_bind_int(stmt, 1, val); + db_bind_text(stmt, varname); + db_bind_int(stmt, val); db_exec_prepared_v2(stmt); tal_free(stmt); } @@ -82,7 +82,7 @@ s64 db_get_intvar(struct db *db, const char *varname, s64 defval) s64 res = defval; struct db_stmt *stmt = db_prepare_v2( db, SQL("SELECT intval FROM vars WHERE name= ? LIMIT 1")); - db_bind_text(stmt, 0, varname); + db_bind_text(stmt, varname); if (db_query_prepared_canfail(stmt) && db_step(stmt)) res = db_col_int(stmt, "intval"); @@ -110,10 +110,10 @@ static void db_data_version_incr(struct db *db) "SET intval = intval + 1 " "WHERE name = 'data_version'" " AND intval = ?")); - db_bind_int(stmt, 0, db->data_version); + db_bind_int(stmt, db->data_version); db_exec_prepared_v2(stmt); if (db_count_changes(stmt) != 1) - db_fatal("Optimistic lock on the database failed. There" + db_fatal(stmt->db, "Optimistic lock on the database failed. There" " may be a concurrent access to the database." " Aborting since concurrent access is unsafe."); tal_free(stmt); @@ -124,7 +124,7 @@ void db_begin_transaction_(struct db *db, const char *location) { bool ok; if (db->in_transaction) - db_fatal("Already in transaction from %s", db->in_transaction); + db_fatal(db, "Already in transaction from %s", db->in_transaction); /* No writes yet. */ db->dirty = false; @@ -132,7 +132,7 @@ void db_begin_transaction_(struct db *db, const char *location) db_prepare_for_changes(db); ok = db->config->begin_tx_fn(db); if (!ok) - db_fatal("Failed to start DB transaction: %s", db->error); + db_fatal(db, "Failed to start DB transaction: %s", db->error); db->in_transaction = location; } @@ -156,7 +156,7 @@ void db_commit_transaction(struct db *db) ok = db->config->commit_tx_fn(db); if (!ok) - db_fatal("Failed to commit DB transaction: %s", db->error); + db_fatal(db, "Failed to commit DB transaction: %s", db->error); db->in_transaction = NULL; db->dirty = false; diff --git a/db/utils.c b/db/utils.c index 9449e0160267..33e797ab1d9c 100644 --- a/db/utils.c +++ b/db/utils.c @@ -24,7 +24,7 @@ size_t db_query_colnum(const struct db_stmt *stmt, for (;;) { const char *n = stmt->query->colnames[col].sqlname; if (!n) - db_fatal("Unknown column name %s in query %s", + db_fatal(stmt->db, "Unknown column name %s in query %s", colname, stmt->query->query); if (streq(n, colname)) break; @@ -41,7 +41,7 @@ size_t db_query_colnum(const struct db_stmt *stmt, static void db_stmt_free(struct db_stmt *stmt) { if (!stmt->executed) - db_fatal("Freeing an un-executed statement from %s: %s", + db_fatal(stmt->db, "Freeing an un-executed statement from %s: %s", stmt->location, stmt->query->query); #if DEVELOPER /* If they never got a db_step, we don't track */ @@ -51,7 +51,7 @@ static void db_stmt_free(struct db_stmt *stmt) continue; if (!strset_get(stmt->cols_used, stmt->query->colnames[i].sqlname)) { - db_fatal("Never accessed column %s in query %s", + db_fatal(stmt->db, "Never accessed column %s in query %s", stmt->query->colnames[i].sqlname, stmt->query->query); } @@ -81,6 +81,7 @@ static struct db_stmt *db_prepare_core(struct db *db, stmt->query = db_query; stmt->executed = false; stmt->inner_stmt = NULL; + stmt->bind_pos = -1; tal_add_destructor(stmt, db_stmt_free); @@ -104,14 +105,14 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, query_id += 2; if (!db->in_transaction) - db_fatal("Attempting to prepare a db_stmt outside of a " + db_fatal(db, "Attempting to prepare a db_stmt outside of a " "transaction: %s", location); /* Look up the query by its ID */ pos = hash_djb2(query_id) % db->queries->query_table_size; for (;;) { if (!db->queries->query_table[pos].name) - db_fatal("Could not resolve query %s", query_id); + db_fatal(db, "Could not resolve query %s", query_id); if (streq(query_id, db->queries->query_table[pos].name)) break; pos = (pos + 1) % db->queries->query_table_size; @@ -154,7 +155,7 @@ bool db_query_prepared_canfail(struct db_stmt *stmt) void db_query_prepared(struct db_stmt *stmt) { if (!db_query_prepared_canfail(stmt)) - db_fatal("query failed: %s: %s", + db_fatal(stmt->db, "query failed: %s: %s", stmt->location, stmt->query->query); } @@ -190,7 +191,7 @@ void db_exec_prepared_v2(struct db_stmt *stmt TAKES) * we report an error. */ if (!ret) { assert(stmt->error); - db_fatal("Error executing statement: %s", stmt->error); + db_fatal(stmt->db, "Error executing statement: %s", stmt->error); } if (taken(stmt)) @@ -260,7 +261,7 @@ void db_assert_no_outstanding_statements(struct db *db) stmt = list_top(&db->pending_statements, struct db_stmt, list); if (stmt) - db_fatal("Unfinalized statement %s", stmt->location); + db_fatal(stmt->db, "Unfinalized statement %s", stmt->location); } #else void db_assert_no_outstanding_statements(struct db *db) @@ -277,7 +278,7 @@ static void destroy_db(struct db *db) db->config->teardown_fn(db); } -static struct db_config *db_config_find(const char *dsn) +static struct db_config *db_config_find(const struct db *db, const char *dsn) { size_t num_configs; struct db_config **configs = autodata_get(db_backends, &num_configs); @@ -285,7 +286,7 @@ static struct db_config *db_config_find(const char *dsn) sep = strstr(dsn, "://"); if (!sep) - db_fatal("%s doesn't look like a valid data-source name (missing \"://\" separator.", dsn); + db_fatal(db, "%s doesn't look like a valid data-source name (missing \"://\" separator.", dsn); driver_name = tal_strndup(tmpctx, dsn, sep - dsn); @@ -319,23 +320,43 @@ void db_prepare_for_changes(struct db *db) db->changes = tal_arr(db, const char *, 0); } -struct db *db_open(const tal_t *ctx, const char *filename) +void db_fatal(const struct db *db, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + db->errorfn(db->errorfn_arg, true, fmt, ap); + va_end(ap); +} + +void db_warn(const struct db *db, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + db->errorfn(db->errorfn_arg, false, fmt, ap); + va_end(ap); +} + +struct db *db_open_(const tal_t *ctx, const char *filename, + void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap), + void *arg) { struct db *db; db = tal(ctx, struct db); db->filename = tal_strdup(db, filename); + db->errorfn = errorfn; + db->errorfn_arg = arg; list_head_init(&db->pending_statements); if (!strstr(db->filename, "://")) - db_fatal("Could not extract driver name from \"%s\"", db->filename); + db_fatal(db, "Could not extract driver name from \"%s\"", db->filename); - db->config = db_config_find(db->filename); + db->config = db_config_find(db, db->filename); if (!db->config) - db_fatal("Unable to find DB driver for %s", db->filename); + db_fatal(db, "Unable to find DB driver for %s", db->filename); db->queries = db_queries_find(db->config); if (!db->queries) - db_fatal("Unable to find DB queries for %s", db->config->name); + db_fatal(db, "Unable to find DB queries for %s", db->config->name); tal_add_destructor(db, destroy_db); db->in_transaction = NULL; @@ -346,7 +367,7 @@ struct db *db_open(const tal_t *ctx, const char *filename) db_prepare_for_changes(db); if (db->config->setup_fn && !db->config->setup_fn(db)) - db_fatal("Error calling DB setup: %s", db->error); + db_fatal(db, "Error calling DB setup: %s", db->error); db_report_changes(db, NULL, 0); return db; diff --git a/db/utils.h b/db/utils.h index 8793d5c09953..f83ea87b559d 100644 --- a/db/utils.h +++ b/db/utils.h @@ -85,7 +85,15 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, /** * db_open - Open or create a database */ -struct db *db_open(const tal_t *ctx, const char *filename); +#define db_open(ctx, filename, errfn, arg) \ + db_open_((ctx), (filename), \ + typesafe_cb_postargs(void, void *, (errfn), (arg), \ + bool, const char *, va_list), \ + (arg)) + +struct db *db_open_(const tal_t *ctx, const char *filename, + void (*errorfn)(void *arg, bool fatal, const char *fmt, va_list ap), + void *arg); /** * Report a statement that changes the wallet @@ -110,4 +118,11 @@ const char **db_changes(struct db *db); * to re-use the normal db hook and replication logic. */ struct db_stmt *db_prepare_untranslated(struct db *db, const char *query); + +/* Errors and warnings... */ +void db_fatal(const struct db *db, const char *fmt, ...) + PRINTF_FMT(2, 3); +void db_warn(const struct db *db, const char *fmt, ...) + PRINTF_FMT(2, 3); + #endif /* LIGHTNING_DB_UTILS_H */ diff --git a/devtools/Makefile b/devtools/Makefile index 0d5e3cf9a90c..dc1a22b2485a 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -8,7 +8,7 @@ DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) # Make sure these depend on everything. ALL_C_SOURCES += $(DEVTOOLS_TOOL_SRC) -ALL_C_HEADERS += +ALL_C_HEADERS += ALL_PROGRAMS += $(DEVTOOLS) DEVTOOLS_COMMON_OBJS := \ @@ -24,7 +24,6 @@ DEVTOOLS_COMMON_OBJS := \ common/bolt11.o \ common/blockheight_states.o \ common/channel_id.o \ - common/configdir.o \ common/decode_array.o \ common/features.o \ common/fee_states.o \ @@ -43,8 +42,8 @@ DEVTOOLS_COMMON_OBJS := \ common/utils.o \ common/version.o \ common/wireaddr.o \ - wire/onion$(EXP)_wiregen.o \ - wire/peer$(EXP)_wiregen.o \ + wire/onion_wiregen.o \ + wire/peer_wiregen.o \ wire/channel_type_wiregen.o \ wire/tlvstream.o @@ -58,7 +57,7 @@ devtools/bolt11-cli: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/f devtools/encodeaddr: common/utils.o common/bech32.o devtools/encodeaddr.o -devtools/bolt12-cli: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/bolt12$(EXP)_wiregen.o wire/fromwire.o wire/towire.o common/bolt12.o common/bolt12_merkle.o devtools/bolt12-cli.o common/setup.o common/iso4217.o +devtools/bolt12-cli: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/bolt12_wiregen.o wire/fromwire.o wire/towire.o common/bolt12.o common/bolt12_merkle.o devtools/bolt12-cli.o common/setup.o common/iso4217.o devtools/decodemsg: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) $(WIRE_PRINT_OBJS) wire/fromwire.o wire/towire.o devtools/print_wire.o devtools/decodemsg.o @@ -73,7 +72,7 @@ devtools/onion.c: ccan/config.h devtools/onion: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) common/onion_decode.o common/onion_encode.o common/onionreply.o wire/fromwire.o wire/towire.o devtools/onion.o common/sphinx.o -devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o +devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS): wire/wire.h @@ -87,7 +86,7 @@ devtools/mkgossip: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/ devtools/mkencoded: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkencoded.o -devtools/checkchannels: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/configdir.o wire/fromwire.o wire/towire.o devtools/checkchannels.o +devtools/checkchannels: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/checkchannels.o common/configdir.o common/configvar.o devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkquery.o diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index 9d815cff8f49..610a6ec18bf3 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -414,6 +414,8 @@ static bool print_extra_fields(const struct tlv_field *fields) return ok; } +bool deprecated_apis = true; + int main(int argc, char *argv[]) { const tal_t *ctx = tal(NULL, char); @@ -424,7 +426,6 @@ int main(int argc, char *argv[]) bool to_hex = false; common_setup(argv[0]); - deprecated_apis = true; opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); opt_register_noarg("--help|-h", opt_usage_and_exit, diff --git a/devtools/checkchannels.c b/devtools/checkchannels.c index 85d4184dd364..538912938fee 100644 --- a/devtools/checkchannels.c +++ b/devtools/checkchannels.c @@ -109,7 +109,7 @@ static void copy_column(void *dst, size_t size, int main(int argc, char *argv[]) { - char *config_dir, *net_dir, *config_filename, *rpc_filename, *hsmfile, *dbfile; + char *net_dir, *rpc_filename, *hsmfile, *dbfile; sqlite3 *sql; sqlite3_stmt *stmt; int flags = SQLITE_OPEN_READONLY, dberr; @@ -124,14 +124,13 @@ int main(int argc, char *argv[]) setup_option_allocators(); - initial_config_opts(top_ctx, argc, argv, - &config_filename, &config_dir, &net_dir, - &rpc_filename); + minimal_config_opts(top_ctx, argc, argv, &net_dir, &rpc_filename); opt_register_noarg("-v|--verbose", opt_set_bool, &verbose, "Print everything"); opt_parse(&argc, argv, opt_log_stderr_exit); + if (argc != 1) errx(1, "no arguments accepted"); diff --git a/devtools/credit b/devtools/credit index 710bc0a4e9e9..cdcae728ea71 100755 --- a/devtools/credit +++ b/devtools/credit @@ -25,8 +25,8 @@ NAMER="" BACKUP_NAMER="" TOTAL=0 while read LINE; do - COUNT=${LINE%% [^ 0123456789]*} - TOTAL=$(($TOTAL + $COUNT)) + COUNT=${LINE%% [! 0123456789]*} + TOTAL=$((TOTAL + COUNT)) LINE=${LINE#*[1234567890] } NAME=${LINE%%|*} EMAIL=${LINE#*|} diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index fdead6bf9e92..2a5d05c7efe9 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -2,14 +2,10 @@ #include #include #include +#include #include -#if EXPERIMENTAL_FEATURES - #include - #include -#else - #include - #include -#endif +#include +#include static char *opt_set_tlvname(const char *arg, bool (**printwire)(const char *fieldname, @@ -55,6 +51,7 @@ int main(int argc, char *argv[]) bool (*printwire)(const u8 *msg) = printpeer_wire_message; bool ok = true; + common_setup(argv[0]); setup_locale(); opt_register_noarg("--onion", opt_set_onionprint, &printwire, diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 1f0df55a0720..3df77a92682e 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -68,13 +68,14 @@ int main(int argc, char *argv[]) u16 flags = be16_to_cpu(hdr.flags); u16 msglen = be16_to_cpu(hdr.len); u8 *msg, *inner; - bool deleted, push, ratelimit, zombie; + bool deleted, push, ratelimit, zombie, dying; u32 blockheight; deleted = (flags & GOSSIP_STORE_DELETED_BIT); push = (flags & GOSSIP_STORE_PUSH_BIT); ratelimit = (flags & GOSSIP_STORE_RATELIMIT_BIT); zombie = (flags & GOSSIP_STORE_ZOMBIE_BIT); + dying = (flags & GOSSIP_STORE_DYING_BIT); msg = tal_arr(NULL, u8, msglen); if (read(fd, msg, msglen) != msglen) @@ -84,11 +85,12 @@ int main(int argc, char *argv[]) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) warnx("Checksum verification failed"); - printf("%zu: %s%s%s%s", off, + printf("%zu: %s%s%s%s%s", off, deleted ? "DELETED " : "", push ? "PUSH " : "", ratelimit ? "RATE-LIMITED " : "", - zombie ? "ZOMBIE " : ""); + zombie ? "ZOMBIE " : "", + dying ? "DYING " : ""); if (print_timestamp) printf("T=%u ", be32_to_cpu(hdr.timestamp)); if (deleted && !print_deleted) { diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 61785633157f..8785ed915186 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -82,9 +82,10 @@ static char *opt_set_network(const char *arg, void *unused) return NULL; } -static void opt_show_network(char buf[OPT_SHOW_LEN], const void *unused) +static bool opt_show_network(char *buf, size_t len, const void *unused) { - snprintf(buf, OPT_SHOW_LEN, "%s", chainparams->network_name); + snprintf(buf, len, "%s", chainparams->network_name); + return true; } void ecdh(const struct pubkey *point, struct secret *ss) @@ -245,6 +246,15 @@ static struct io_plan *handshake_success(struct io_conn *conn, --max_messages; } } + + /* Simply closing the fd can lose writes; send shutdown and wait + * for them to close (set alarm just in case!) */ + alarm(30); + + if (shutdown(peer_fd, SHUT_WR) != 0) + err(1, "failed to shutdown write to peer: %s", strerror(errno)); + + while (sync_crypto_read(NULL, peer_fd, cs)); exit(0); } @@ -255,9 +265,9 @@ static char *opt_set_secret(const char *arg, struct secret *s) return NULL; } -static void opt_show_secret(char buf[OPT_SHOW_LEN], const struct secret *s) +static bool opt_show_secret(char *buf, size_t len, const struct secret *s) { - hex_encode(s->data, sizeof(s->data), buf, OPT_SHOW_LEN); + return hex_encode(s->data, sizeof(s->data), buf, len); } static char *opt_set_features(const char *arg, u8 **features) @@ -326,8 +336,8 @@ int main(int argc, char *argv[]) opt_usage_exit_fail("Invalid id %.*s", (int)(at - argv[1]), argv[1]); - if (!parse_wireaddr_internal(at+1, &addr, chainparams_get_ln_port(chainparams), NULL, - true, false, &err_msg)) + err_msg = parse_wireaddr_internal(tmpctx, at+1, chainparams_get_ln_port(chainparams), true, &addr); + if (err_msg) opt_usage_exit_fail("%s '%s'", err_msg, argv[1]); switch (addr.itype) { @@ -342,14 +352,14 @@ int main(int argc, char *argv[]) opt_usage_exit_fail("Don't support proxy use"); case ADDR_INTERNAL_WIREADDR: - switch (addr.u.wireaddr.type) { + if (addr.u.wireaddr.is_websocket) + opt_usage_exit_fail("Don't support websocket use"); + + switch (addr.u.wireaddr.wireaddr.type) { case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: opt_usage_exit_fail("Don't support proxy use"); break; - case ADDR_TYPE_WEBSOCKET: - opt_usage_exit_fail("Don't support websockets"); - break; case ADDR_TYPE_DNS: opt_usage_exit_fail("Don't support DNS"); break; @@ -360,7 +370,7 @@ int main(int argc, char *argv[]) af = AF_INET6; break; } - ai = wireaddr_to_addrinfo(tmpctx, &addr.u.wireaddr); + ai = wireaddr_to_addrinfo(tmpctx, &addr.u.wireaddr.wireaddr); } if (af == -1 || ai == NULL) diff --git a/devtools/mkclose.c b/devtools/mkclose.c index f60b6175fe07..71c1a1a9d7dc 100644 --- a/devtools/mkclose.c +++ b/devtools/mkclose.c @@ -111,7 +111,8 @@ int main(int argc, char *argv[]) argnum++; fee = commit_tx_base_fee(feerate_per_kw, 0, - option_anchor_outputs); + option_anchor_outputs, + false); /* BOLT #3: * If `option_anchors` applies to the commitment * transaction, also subtract two times the fixed anchor size diff --git a/devtools/onion.c b/devtools/onion.c index b08a6f25bd94..4f3e65147dae 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -175,9 +175,9 @@ static char *opt_set_ad(const char *arg, u8 **assocdata) return NULL; } -static void opt_show_ad(char buf[OPT_SHOW_LEN], u8 *const *assocdata) +static bool opt_show_ad(char *buf, size_t len, u8 *const *assocdata) { - hex_encode(*assocdata, tal_bytelen(*assocdata), buf, OPT_SHOW_LEN); + return hex_encode(*assocdata, tal_bytelen(*assocdata), buf, len); } static char *opt_set_node_id(const char *arg, struct node_id *node_id) diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 9b1792f1525b..b3db3cb7cac6 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -51,6 +51,50 @@ bool printwire_u64(const char *fieldname, const u8 **cursor, size_t *plen) return true; } +bool printwire_s8(const char *fieldname, const u8 **cursor, size_t *plen) +{ + s8 v = fromwire_s8(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED s64 %s**\n", fieldname); + return false; + } + printf("%d\n", v); + return true; +} + +bool printwire_s16(const char *fieldname, const u8 **cursor, size_t *plen) +{ + s16 v = fromwire_s16(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED s64 %s**\n", fieldname); + return false; + } + printf("%d\n", v); + return true; +} + +bool printwire_s32(const char *fieldname, const u8 **cursor, size_t *plen) +{ + s32 v = fromwire_s32(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED s64 %s**\n", fieldname); + return false; + } + printf("%d\n", v); + return true; +} + +bool printwire_s64(const char *fieldname, const u8 **cursor, size_t *plen) +{ + s64 v = fromwire_s64(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED s64 %s**\n", fieldname); + return false; + } + printf("%"PRId64"\n", v); + return true; +} + bool printwire_tu16(const char *fieldname, const u8 **cursor, size_t *plen) { u16 v = fromwire_tu16(cursor, plen); diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 74607d1a550c..96ae674f75e3 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -20,6 +20,10 @@ bool printwire_u8(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_u16(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_u32(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_u64(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_s8(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_s16(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_s32(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_s64(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_tu16(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_tu32(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_tu64(const char *fieldname, const u8 **cursor, size_t *plen); diff --git a/devtools/route.c b/devtools/route.c index ebf809301a72..30a87a26796a 100644 --- a/devtools/route.c +++ b/devtools/route.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,8 @@ int main(int argc, char *argv[]) bool clean_topology = false; size_t num_channel_updates_rejected; + common_setup(argv[0]); + opt_register_noarg("--clean-topology", opt_set_bool, &clean_topology, "Clean up topology before run"); opt_register_noarg("-h|--help", opt_usage_and_exit, diff --git a/doc/BACKUP.md b/doc/BACKUP.md deleted file mode 100644 index a607b141698e..000000000000 --- a/doc/BACKUP.md +++ /dev/null @@ -1,643 +0,0 @@ -# Backing Up Your C-Lightning Node - -Lightning Network channels get their scalability and privacy benefits -from the very simple technique of *not telling anyone else about your -in-channel activity*. -This is in contrast to onchain payments, where you have to tell everyone -about each and every payment and have it recorded on the blockchain, -leading to scaling problems (you have to push data to everyone, everyone -needs to validate every transaction) and privacy problems (everyone knows -every payment you were ever involved in). - -Unfortunately, this removes a property that onchain users are so used -to, they react in surprise when learning about this removal. -Your onchain activity is recorded in all archival fullnodes, so if you -forget all your onchain activity because your storage got fried, you -just go redownload the activity from the nearest archival fullnode. - -But in Lightning, since *you* are the only one storing all your -financial information, you ***cannot*** recover this financial -information from anywhere else. - -This means that on Lightning, **you have to** responsibly back up your -financial information yourself, using various processes and automation. - -The discussion below assumes that you know where you put your -`$LIGHTNINGDIR`, and you know the directory structure within. -By default your `$LIGHTNINGDIR` will be in `~/.lightning/${COIN}`. -For example, if you are running `--mainnet`, it will be -`~/.lightning/bitcoin`. - -## `hsm_secret` - -!!! note - - WHO SHOULD DO THIS: Everyone. - -You need a copy of the `hsm_secret` file regardless of whatever backup -strategy you use. - -The `hsm_secret` is created when you first create the node, and does -not change. -Thus, a one-time backup of `hsm_secret` is sufficient. - -This is just 32 bytes, and you can do something like the below and -write the hexadecimal digits a few times on a piece of paper: - - cd $LIGHTNINGDIR - xxd hsm_secret - -You can re-enter the hexdump into a text file later and use `xxd` to -convert it back to a binary `hsm_secret`: - - cat > hsm_secret_hex.txt < hsm_secret - chmod 0400 hsm_secret - -Notice that you need to ensure that the `hsm_secret` is only readable by -the user, and is not writable, as otherwise `lightningd` will refuse to -start. -Hence the `chmod 0400 hsm_secret` command. - -Alternatively, if you are deploying a new node that has no funds and -channels yet, you can generate BIP39 words using any process, and -create the `hsm_secret` using the `hsmtool generatehsm` command. -If you did `make install` then `hsmtool` is installed as -`lightning-hsmtool`, else you can find it in the `tools/` directory -of the build directory. - - lightning-hsmtool generatehsm hsm_secret - -Then enter the BIP39 words, plus an optional passphrase. Then copy the -`hsm_secret` to `${LIGHTNINGDIR}` - -You can regenerate the same `hsm_secret` file using the same BIP39 -words, which again, you can back up on paper. - -Recovery of the `hsm_secret` is sufficient to recover any onchain -funds. -Recovery of the `hsm_secret` is necessary, but insufficient, to recover -any in-channel funds. -To recover in-channel funds, you need to use one or more of the other -backup strategies below. - -## SQLITE3 `--wallet=${main}:${backup}` And Remote NFS Mount - -!!! note - - WHO SHOULD DO THIS: Casual users. - -!!! warning - - This technique is only supported after the version v0.10.2 (not included) or later. - On earlier versions, the `:` character is not special and will be considered part of the path of the database file. - -When using the SQLITE3 backend (the default), you can specify a -second database file to replicate to, by separating the second -file with a single `:` character in the `--wallet` option, after -the main database filename. - -For example, if the user running `lightningd` is named `user`, and -you are on the Bitcoin mainnet with the default `${LIGHTNINGDIR}`, you -can specify in your `config` file: - -```bash -wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 -``` - -Or via command line: - -```bash -lightningd --wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 -``` - -If the second database file does not exist but the directory that would -contain it does exist, the file is created. -If the directory of the second database file does not exist, `lightningd` will -fail at startup. -If the second database file already exists, on startup it will be overwritten -with the main database. -During operation, all database updates will be done on both databases. - -The main and backup files will **not** be identical at every byte, but they -will still contain the same data. - -It is recommended that you use **the same filename** for both files, just on -different directories. - -This has the advantage compared to the `backup` plugin below of requiring -exactly the same amount of space on both the main and backup storage. -The `backup` plugin will take more space on the backup than on the main -storage. -It has the disadvantage that it will only work with the SQLITE3 backend and -is not supported by the PostgreSQL backend, and is unlikely to be supported -on any future database backends. - -You can only specify *one* replica. - -It is recommended that you use a network-mounted filesystem for the backup -destination. -For example, if you have a NAS you can access remotely. - -At the minimum, set the backup to a different storage device. -This is no better than just using RAID-1 (and the RAID-1 will probably be -faster) but this is easier to set up --- just plug in a commodity USB -flash disk (with metal casing, since a lot of writes are done and you need -to dissipate the heat quickly) and use it as the backup location, without -repartitioning your OS disk, for example. - -Do note that files are not stored encrypted, so you should really not do -this with rented space ("cloud storage"). - -To recover, simply get **all** the backup database files. -Note that SQLITE3 will sometimes create a `-journal` or `-wal` file, which -is necessary to ensure correct recovery of the backup; you need to copy -those too, with corresponding renames if you use a different filename for -the backup database, e.g. if you named the backup `backup.sqlite3` and -when you recover you find `backup.sqlite3` and `backup.sqlite3-journal` -files, you rename `backup.sqlite3` to `lightningd.sqlite3` and -`backup.sqlite3-journal` to `lightningd.sqlite3-journal`. -Note that the `-journal` or `-wal` file may or may not exist, but if they -*do*, you *must* recover them as well -(there can be an `-shm` file as well in WAL mode, but it is unnecessary; -it is only used by SQLITE3 as a hack for portable shared memory, and -contains no useful data; SQLITE3 will ignore its contents always). -It is recommended that you use **the same filename** for both main and -backup databases (just on different directories), and put the backup in -its own directory, so that you can just recover all the files in that -directory without worrying about missing any needed files or correctly -renaming. - -If your backup destination is a network-mounted filesystem that is in a -remote location, then even loss of all hardware in one location will allow -you to still recover your Lightning funds. - -However, if instead you are just replicating the database on another -storage device in a single location, you remain vulnerable to disasters -like fire or computer confiscation. - -## `backup` Plugin And Remote NFS Mount - -!!! note - - WHO SHOULD DO THIS: Casual users. - -You can find the full source for the `backup` plugin here: -https://github.com/lightningd/plugins/tree/master/backup - -The `backup` plugin requires Python 3. - -* Download the source for the plugin. - * `git clone https://github.com/lightningd/plugins.git` -* `cd` into its directory and install requirements. - * `cd plugins/backup` - * `pip3 install -r requirements.txt` -* Figure out where you will put the backup files. - * Ideally you have an NFS or other network-based mount on your system, - into which you will put the backup. -* Stop your Lightning node. -* `/path/to/backup-cli init --lightning-dir ${LIGHTNINGDIR} file:///path/to/nfs/mount/file.bkp`. - This creates an initial copy of the database at the NFS mount. -* Add these settings to your `lightningd` configuration: - * `important-plugin=/path/to/backup.py` -* Restart your Lightning node. - -It is recommended that you use a network-mounted filesystem for the backup -destination. -For example, if you have a NAS you can access remotely. - -Do note that files are not stored encrypted, so you should really not do -this with rented space ("cloud storage"). - -Alternately, you *could* put it in another storage device (e.g. USB flash -disk) in the same physical location. - -To recover: - -* Re-download the `backup` plugin and install Python 3 and the - requirements of `backup`. -* `/path/to/backup-cli restore file:///path/to/nfs/mount ${LIGHTNINGDIR}` - -If your backup destination is a network-mounted filesystem that is in a -remote location, then even loss of all hardware in one location will allow -you to still recover your Lightning funds. - -However, if instead you are just replicating the database on another -storage device in a single location, you remain vulnerable to disasters -like fire or computer confiscation. - -## Filesystem Redundancy - -!!! note - - WHO SHOULD DO THIS: Filesystem nerds, data hoarders, home labs, enterprise users. - -You can set up a RAID-1 with multiple storage devices, and point the -`$LIGHTNINGDIR` to the RAID-1 setup. -That way, failure of one storage device will still let you recover -funds. - -You can use a hardware RAID-1 setup, or just buy multiple commodity -storage media you can add to your machine and use a software RAID, -such as (not an exhaustive list!): - -* `mdadm` to create a virtual volume which is the RAID combination - of multiple physical media. -* BTRFS RAID-1 or RAID-10, a filesystem built into Linux. -* ZFS RAID-Z, a filesystem that cannot be legally distributed with the Linux - kernel, but can be distributed in a BSD system, and can be installed - on Linux with some extra effort, see - [ZFSonLinux](https://zfsonlinux.org). - -RAID-1 (whether by hardware, or software) like the above protects against -failure of a single storage device, but does not protect you in case of -certain disasters, such as fire or computer confiscation. - -You can "just" use a pair of high-quality metal-casing USB flash devices -(you need metal-casing since the devices will have a lot of small writes, -which will cause a lot of heating, which needs to dissipate very fast, -otherwise the flash device firmware will internally disconnect the flash -device from your computer, reducing your reliability) in RAID-1, if you -have enough USB ports. - -### Example: BTRFS on Linux - -On a Linux system, one of the simpler things you can do would be to use -BTRFS RAID-1 setup between a partition on your primary storage and a USB -flash disk. -The below "should" work, but assumes you are comfortable with low-level -Linux administration. -If you are on a system that would make you cry if you break it, you **MUST** -stop your Lightning node and back up all files before doing the below. - -* Install `btrfs-progs` or `btrfs-tools` or equivalent. -* Get a 32Gb USB flash disk. -* Stop your Lightning node and back up everything, do not be stupid. -* Repartition your hard disk to have a 30Gb partition. - * This is risky and may lose your data, so this is best done with a - brand-new hard disk that contains no data. -* Connect the USB flash disk. -* Find the `/dev/sdXX` devices for the HDD 30Gb partition and the flash disk. - * `lsblk -o NAME,TYPE,SIZE,MODEL` should help. -* Create a RAID-1 `btrfs` filesystem. - * `mkfs.btrfs -m raid1 -d raid1 /dev/${HDD30GB} /dev/${USB32GB}` - * You may need to add `-f` if the USB flash disk is already formatted. -* Create a mountpoint for the `btrfs` filesystem. -* Create a `/etc/fstab` entry. - * Use the `UUID` option instad of `/dev/sdXX` since the exact device letter - can change across boots. - * You can get the UUID by `lsblk -o NAME,UUID`. - Specifying *either* of the devices is sufficient. - * Add `autodefrag` option, which tends to work better with SQLITE3 - databases. - * e.g. `UUID=${UUID} ${BTRFSMOUNTPOINT} btrfs defaults,autodefrag 0 0` -* `mount -a` then `df` to confirm it got mounted. -* Copy the contents of the `$LIGHTNINGDIR` to the BTRFS mount point. - * Copy the entire directory, then `chown -R` the copy to the user who will - run the `lightningd`. - * If you are paranoid, run `diff -r` on both copies to check. -* Remove the existing `$LIGHTNINGDIR`. -* `ln -s ${BTRFSMOUNTPOINT}/lightningdirname ${LIGHTNINGDIR}`. - * Make sure the `$LIGHTNINGDIR` has the same structure as what you - originally had. -* Add `crontab` entries for `root` that perform regular `btrfs` maintenance - tasks. - * `0 0 * * * /usr/bin/btrfs balance start -dusage=50 -dlimit=2 -musage=50 -mlimit=4 ${BTRFSMOUNTPOINT}` - This prevents BTRFS from running out of blocks even if it has unused - space *within* blocks, and is run at midnight everyday. - You may need to change the path to the `btrfs` binary. - * `0 0 * * 0 /usr/bin/btrfs scrub start -B -c 2 -n 4 ${BTRFSMOUNTPOINT}` - This detects bit rot (i.e. bad sectors) and auto-heals the filesystem, - and is run on Sundays at midnight. -* Restart your Lightning node. - -If one or the other device fails completely, shut down your computer, boot -on a recovery disk or similar, then: - -* Connect the surviving device. -* Mount the partition/USB flash disk in `degraded` mode: - * `mount -o degraded /dev/sdXX /mnt/point` -* Copy the `lightningd.sqlite3` and `hsm_secret` to new media. - * Do **not** write to the degraded `btrfs` mount! -* Start up a `lightningd` using the `hsm_secret` and `lightningd.sqlite3` - and close all channels and move all funds to onchain cold storage you - control, then set up a new Lightning node. - -If the device that fails is the USB flash disk, you can replace it using -BTRFS commands. -You should probably stop your Lightning node while doing this. - -* `btrfs replace start /dev/sdOLD /dev/sdNEW ${BTRFSMOUNTPOINT}`. - * If `/dev/sdOLD` no longer even exists because the device is really - really broken, use `btrfs filesystem show` to see the number after - `devid` of the broken device, and use that number instead of - `/dev/sdOLD`. -* Monitor status with `btrfs replace status ${BTRFSMOUNTPOINT}`. - -More sophisticated setups with more than two devices are possible. -Take note that "RAID 1" in `btrfs` means "data is copied on up to two -devices", meaning only up to one device can fail. -You may be interested in `raid1c3` and `raid1c4` modes if you have -three or four storage devices. -BTRFS would probably work better if you were purchasing an entire set -of new storage devices to set up a new node. - -## PostgreSQL Cluster - -!!! note - - WHO SHOULD DO THIS: Enterprise users, whales. - -`lightningd` may also be compiled with PostgreSQL support. -PostgreSQL is generally faster than SQLITE3, and also supports running a -PostgreSQL cluster to be used by `lightningd`, with automatic replication -and failover in case an entire node of the PostgreSQL cluster fails. - -Setting this up, however, is more involved. - -By default, `lightningd` compiles with PostgreSQL support **only** if it -finds `libpq` installed when you `./configure`. -To enable it, you have to install a developer version of `libpq`. -On most Debian-derived systems that would be `libpq-dev`. -To verify you have it properly installed on your system, check if the -following command gives you a path: - - pg_config --includedir - -Versioning may also matter to you. -For example, Debian Stable ("buster") as of late 2020 provides PostgreSQL 11.9 -for the `libpq-dev` package, but Ubuntu LTS ("focal") of 2020 provides -PostgreSQL 12.5. -Debian Testing ("bullseye") uses PostgreSQL 13.0 as of this writing. -PostgreSQL 12 had a non-trivial change in the way the restore operation is -done for replication. -You should use the same PostgreSQL version of `libpq-dev` as what you run -on your cluster, which probably means running the same distribution on -your cluster. - -Once you have decided on a specific version you will use throughout, refer -as well to the "synchronous replication" document of PostgreSQL for the -**specific version** you are using: - -* [PostgreSQL 11](https://www.postgresql.org/docs/11/runtime-config-replication.html) -* [PostgreSQL 12](https://www.postgresql.org/docs/12/runtime-config-replication.html) -* [PostgreSQL 13](https://www.postgresql.org/docs/13/runtime-config-replication.html) - -You then have to compile `lightningd` with PostgreSQL support. - -* Clone or untar a new source tree for `lightning` and `cd` into it. - * You *could* just use `make clean` on an existing one, but for the - avoidance of doubt (and potential bugs in our `Makefile` cleanup rules), - just create a fresh source tree. -* `./configure` - * Add any options to `configure` that you normally use as well. -* Double-check the `config.vars` file contains `HAVE_POSTGRES=1`. - * `grep 'HAVE_POSTGRES' config.vars` -* `make` -* If you install `lightningd`, `sudo make install`. - -If you were not using PostgreSQL before but have compiled and used -`lightningd` on your system, the resulting `lightningd` will still -continue supporting and using your current SQLITE3 database; -it just gains the option to use a PostgreSQL database as well. - -If you just want to use PostgreSQL without using a cluster (for -example, as an initial test without risking any significant funds), -then after setting up a PostgreSQL database, you just need to add -`--wallet=postgres://${USER}:${PASSWORD}@${HOST}:${PORT}/${DB}` -to your `lightningd` config or invocation. - -To set up a cluster for a brand new node, follow this (external) -[guide by @gabridome][gabridomeguide]. - -[gabridomeguide]: https://bit.ly/3KffmN3 - -The above guide assumes you are setting up a new node from scratch. -It is also specific to PostgreSQL 12, and setting up for other versions -**will** have differences; read the PostgreSQL manuals linked above. - -If you want to continue a node that started using an SQLITE3 database, -note that we do not support this. -You should set up a new PostgreSQL node, move funds from the SQLITE3 -node to the PostgreSQL node, then shut down the SQLITE3 node -permanently. - -There are also more ways to set up PostgreSQL replication. -In general, you should use [synchronous replication (13)][pqsyncreplication], -since `lightningd` assumes that once a transaction is committed, it is -saved in all permanent storage. -This can be difficult to create remote replicas due to the latency. - -[pqsyncreplication]: https://www.postgresql.org/docs/13/warm-standby.html#SYNCHRONOUS-REPLICATION - -## SQLite Litestream Replication - -!!! warning - - Previous versions of this document recommended this technique, but we no longer do so. - According to [issue 4857][], even with a 60-second timeout that we added - in 0.10.2, this leads to constant crashing of `lightningd` in some - situations. - This section will be removed completely six months after 0.10.3. - Consider using - - ``` - --wallet=sqlite3://${main}:${backup} - ``` - - above, instead. - -[issue 4857]: https://github.com/ElementsProject/lightning/issues/4857 - -One of the simpler things on any system is to use Litestream to replicate the SQLite database. -It continuously streams SQLite changes to file or external storage - the cloud storage option -should not be used. -Backups/replication should not be on the same disk as the original SQLite DB. - -You need to enable WAL mode on your database. -To do so, first stop `lightningd`, then: - - $ sqlite3 lightningd.sqlite3 - sqlite3> PRAGMA journal_mode = WAL; - sqlite3> .quit - -Then just restart `lightningd`. - -/etc/litestream.yml : - - dbs: - - path: /home/bitcoin/.lightning/bitcoin/lightningd.sqlite3 - replicas: - - path: /media/storage/lightning_backup - - and start the service using systemctl: - - $ sudo systemctl start litestream - -Restore: - - $ litestream restore -o /media/storage/lightning_backup /home/bitcoin/restore_lightningd.sqlite3 - -Because Litestream only copies small changes and not the entire -database (holding a read lock on the file while doing so), the -60-second timeout on locking should not be reached unless -something has made your backup medium very very slow. - -Litestream has its own timer, so there is a tiny (but -non-negligible) probability that `lightningd` updates the -database, then irrevocably commits to the update by sending -revocation keys to the counterparty, and *then* your main -storage media crashes before Litestream can replicate the -update. -Treat this as a superior version of "Database File Backups" -section below and prefer recovering via other backup methods -first. - -## Database File Backups - -!!! note - - WHO SHOULD DO THIS: Those who already have at least one of the - other backup methods, those who are #reckless. - -This is the least desirable backup strategy, as it *can* lead to loss -of all in-channel funds if you use it. -However, having *no* backup strategy at all *will* lead to loss of all -in-channel funds, so this is still better than nothing. - -This backup method is undesirable, since it cannot recover the following -channels: - -* Channels with peers that do not support `option_dataloss_protect`. - * Most nodes on the network already support `option_dataloss_protect` - as of November 2020. - * If the peer does not support `option_dataloss_protect`, then the entire - channel funds will be revoked by the peer. - * Peers can *claim* to honestly support this, but later steal funds - from you by giving obsolete state when you recover. -* Channels created *after* the copy was made are not recoverable. - * Data for those channels does not exist in the backup, so your node - cannot recover them. - -Because of the above, this strategy is discouraged: you *can* potentially -lose all funds in open channels. - -However, again, note that a "no backups #reckless" strategy leads to -*definite* loss of funds, so you should still prefer *this* strategy rather -than having *no* backups at all. - -Even if you have one of the better options above, you might still want to do -this as a worst-case fallback, as long as you: - -* Attempt to recover using the other backup options above first. - Any one of them will be better than this backup option. -* Recover by this method **ONLY** as a ***last*** resort. -* Recover using the most recent backup you can find. - Take time to look for the most recent available backup. - -Again, this strategy can lead to only ***partial*** recovery of funds, -or even to complete failure to recover, so use the other methods first to -recover! - -### Offline Backup - -While `lightningd` is not running, just copy the `lightningd.sqlite3` file -in the `$LIGHTNINGDIR` on backup media somewhere. - -To recover, just copy the backed up `lightningd.sqlite3` into your new -`$LIGHTNINGDIR` together with the `hsm_secret`. - -You can also use any automated backup system as long as it includes the -`lightningd.sqlite3` file (and optionally `hsm_secret`, but note that -as a secret key, thieves getting a copy of your backups may allow them -to steal your funds, even in-channel funds) and as long as it copies the -file while `lightningd` is not running. - -### Backing Up While `lightningd` Is Running - -Since `sqlite3` will be writing to the file while `lightningd` is running, -`cp`ing the `lightningd.sqlite3` file while `lightningd` is running may -result in the file not being copied properly if `sqlite3` happens to be -committing database transactions at that time, potentially leading to a -corrupted backup file that cannot be recovered from. - -You have to stop `lightningd` before copying the database to backup in -order to ensure that backup files are not corrupted, and in particular, -wait for the `lightningd` process to exit. -Obviously, this is disruptive to node operations, so you might prefer -to just perform the `cp` even if the backup potentially is corrupted. -As long as you maintain multiple backups sampled at different times, -this may be more acceptable than stopping and restarting `lightningd`; -the corruption only exists in the backup, not in the original file. - -If the filesystem or volume manager containing `$LIGHTNINGDIR` has a -snapshot facility, you can take a snapshot of the filesystem, then -mount the snapshot, copy `lightningd.sqlite3`, unmount the snapshot, -and then delete the snapshot. -Similarly, if the filesystem supports a "reflink" feature, such as -`cp -c` on an APFS on MacOS, or `cp --reflink=always` on an XFS or -BTRFS on Linux, you can also use that, then copy the reflinked copy -to a different storage medium; this is equivalent to a snapshot of -a single file. -This *reduces* but does not *eliminate* this race condition, so you -should still maintain multiple backups. - -You can additionally perform a check of the backup by this command: - - echo 'PRAGMA integrity_check;' | sqlite3 ${BACKUPFILE} - -This will result in the string `ok` being printed if the backup is -**likely** not corrupted. -If the result is anything else than `ok`, the backup is definitely -corrupted and you should make another copy. - -In order to make a proper uncorrupted backup of the SQLITE3 file -while `lightningd` is running, we would need to have `lightningd` -perform the backup itself, which, as of the version at the time of -this writing, is not yet implemented. - -Even if the backup is not corrupted, take note that this backup -strategy should still be a last resort; recovery of all funds is -still not assured with this backup strategy. - -`sqlite3` has `.dump` and `VACUUM INTO` commands, but note that -those lock the main database for long time periods, which will -negatively affect your `lightningd` instance. - -### `sqlite3` `.dump` or `VACUUM INTO` Commands - -!!! warning - - Previous versions of this document recommended - this technique, but we no longer do so. - According to [issue 4857][issue 4857], even with a 60-second timeout that we added - in 0.10.2, this may lead to constant crashing of `lightningd` in some - situations; this technique uses substantially the same techniques as - `litestream`. - This section will be removed completely six months after 0.10.3. - Consider using `--wallet=sqlite3://${main}:${backup}` above, instead. - -Use the `sqlite3` command on the `lightningd.sqlite3` file, and -feed it with `.dump "/path/to/backup.sqlite3"` or `VACUUM INTO -"/path/to/backup.sqlite3";`. - -These create a snapshot copy that, unlike the previous technique, -is assuredly uncorrupted (barring any corruption caused by your -backup media). - -However, if the copying process takes a long time (approaching the -timeout of 60 seconds) then you run the risk of `lightningd` -attempting to grab a write lock, waiting up to 60 seconds, and -then failing with a "database is locked" error. -Your backup system could `.dump` to a fast `tmpfs` RAMDISK or -local media, and *then* copy to the final backup media on a remote -system accessed via slow network, for example, to reduce this -risk. - -It is recommended that you use `.dump` instead of `VACUUM INTO`, -as that is assuredly faster; you can just open the backup copy -in a new `sqlite3` session and `VACUUM;` to reduce the size -of the backup. diff --git a/doc/FAQ.md b/doc/FAQ.md deleted file mode 100644 index b56e4b8b6c99..000000000000 --- a/doc/FAQ.md +++ /dev/null @@ -1,289 +0,0 @@ -# Frequently Asked Questions (FAQ) -## General questions - -### I don't know where to start, help me ! - -There is a C-lightning plugin specifically for this purpose, it's called -[`helpme`](https://github.com/lightningd/plugins/tree/master/helpme). - -Assuming you have followed the [installation steps](INSTALL.md), have `lightningd` -up and running, and `lightning-cli` in your `$PATH` you can start the plugin like so: - -``` -# Clone the plugins repository -git clone https://github.com/lightningd/plugins -# Make sure the helpme plugin is executable (git should have already handled this) -chmod +x plugins/helpme/helpme.py -# Install its dependencies (there is only one actually) -pip3 install --user -r plugins/helpme/requirements.txt -# Then just start it :) -lightning-cli plugin start $PWD/plugins/helpme/helpme.py -``` - -The plugin registers a new command `helpme` which will guide you through the main -components of C-lightning: - -``` -lightning-cli helpme -``` - -### How to get the balance of each channel ? - -You can use the `listfunds` command and take a ratio of `our_amount_msat` over -`amount_msat`. Note that this doesn't account for the [channel reserve](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#rationale). - -A better option is to use the [`summary` plugin](https://github.com/lightningd/plugins/tree/master/summary) -which nicely displays channel balances, along with other useful channel information. - -### My channel is in state `STATE`, what does that mean ? - -See the [listpeers command manpage](https://lightning.readthedocs.io/lightning-listpeers.7.html#return-value). - -### My payment is failing / all my payments are failing, why ? - -There are many reasons for a payment failure. The most common one is a -[failure](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#failure-messages) -along the route from you to the payee. -The best (and most common) solution to a route failure problem is to open more channels, -which should increase the available routes to the recipient and lower the probability of a failure. - -Hint: use the [`pay`](lightning-pay.7.md) command which is will iterate through trying all possible routes, -instead of the low-level `sendpay` command which only tries the passed in route. - -### How can I receive payments ? - -In order to receive payments you need inbound liquidity. You get inbound liquidity when -another node opens a channel to you or by successfully completing a payment out through a channel you opened. - -If you need a lot of inbound liquidity, you can use a service that trustlessly swaps on-chain Bitcoin -for Lightning channel capacity. -There are a few online service providers that will create channels to you. -A few of them charge fees for this service. -Note that if you already have a channel open to them, you'll need to close it before requesting another channel. - -### Are there any issues if my node changes its IP address? What happens to the channels if it does? - -There is no risk to your channels if your IP address changes. -Other nodes might not be able to connect to you, but your node can still connect to them. -But Core Lightning also has an integrated IPv4/6 address discovery mechanism. -If your node detects an new public address, it can update its announcement. -For this to work binhind a NAT router you need to forward the default TCP port 9735 to your node. - -Note: Per default and for privacy reasons IP discovery will only be active -if no other addresses would be announced (as kind of a fallback). -You can set `--announce-addr-discovered=true` to explicitly activate it. -Your node will then update discovered IP addresses even if it also announces e.g. a TOR address. - -Alternatively, you can [setup a TOR hidden service](TOR.md) for your node that -will also work well behind NAT firewalls. - -### Can I have two hosts with the same public key and different IP addresses, both online and operating at the same time? - -No. - -### Can I use a single `bitcoind` for multiple `lightningd` ? - -Yes. All `bitcoind` calls are handled by the bundled `bcli` plugin. `lightningd` does not use -`bitcoind`'s wallet. While on the topic, `lightningd` does not require the `-txindex` option on `bitcoind`. - -If you use a single `bitcoind` for multiple `lightningd`'s, be sure to raise the `bitcoind` -max RPC thread limit (`-rpcthreads`), each `lightningd` can use up to 4 threads, which is -the default `bitcoind` max. - -### Can I use Core Lightning on mobile ? - -#### Remote control - -[Spark-wallet](https://github.com/shesek/spark-wallet/) is the most popular remote control -HTTP server for `lightningd`. -**Use it [behind tor](https://github.com/shesek/spark-wallet/blob/master/doc/onion.md)**. - -#### `lightningd` on Android - -Effort has been made to get `lightningd` running on Android, -[see issue #3484](https://github.com/ElementsProject/lightning/issues/3484). Currently unusable. - -### How to "backup my wallet" ? - -See [BACKUP.md](https://lightning.readthedocs.io/BACKUP.html) for a more -comprehensive discussion of your options. - -In summary: as a Bitcoin user, one may be familiar with a file or a seed -(or some mnemonics) from which -it can recover all its funds. - -Core Lightning has an internal bitcoin wallet, which you can use to make "on-chain" -transactions, (see [withdraw](https://lightning.readthedocs.io/lightning-withdraw.7.html)). -These on-chain funds are backed up via the HD wallet seed, stored in byte-form in `hsm_secret`. - -`lightningd` also stores information for funds locked in Lightning Network channels, which are stored -in a database. This database is required for on-going channel updates as well as channel closure. -There is no single-seed backup for funds locked in channels. - -While crucial for node operation, snapshot-style backups of the `lightningd` database is **discouraged**, -as _any_ loss of state may result in permanent loss of funds. -See the [penalty mechanism](https://github.com/lightning/bolts/blob/master/05-onchain.md#revoked-transaction-close-handling) -for more information on why any amount of state-loss results in fund loss. - -Real-time database replication is the recommended approach to backing up node data. -Tools for replication are currently in active development, using the `db_write` -[plugin hook](https://lightning.readthedocs.io/PLUGINS.html#db-write). - - -## Channel Management - -### How to forget about a channel? - -Channels may end up stuck during funding and never confirm -on-chain. There is a variety of causes, the most common ones being -that the funds have been double-spent, or the funding fee was too low -to be confirmed. This is unlikely to happen in normal operation, as -CLN tries to use sane defaults and prevents double-spends whenever -possible, but using custom feerates or when the bitcoin backend has no -good fee estimates it is still possible. - -Before forgetting about a channel it is important to ensure that the -funding transaction will never be confirmable by double-spending the -funds. To do so you have to rescan the UTXOs using -[`dev-rescan-outputs`](#rescanning) to reset any funds that may have -been used in the funding transaction, then move all the funds to a new -address: - -```bash -lightning-cli dev-rescan-outputs -ADDR=$(lightning-cli newaddr bech32 | jq .bech32) -lightning-cli withdraw $ADDR all -``` - -This step is not required if the funding transaction was already -double-spent, however it is safe to do it anyway, just in case. - -Then wait for the transaction moving the funds to confirm. This -ensures any pending funding transaction can no longer be -confirmed. - -As an additional step you can also force-close the unconfirmed channel: - -```bash -lightning-cli close $PEERID 10 # Force close after 10 seconds -``` - -This will store a unilateral close TX in the DB as last resort means -of recovery should the channel unexpectedly confirm anyway. - -Now you can use the `dev-forget-channel` command to remove -the DB entries from the database. - -```bash -lightning-cli dev-forget-channel $NODEID -``` - -This will perform additional checks on whether it is safe to forget -the channel, and only then removes the channel from the DB. Notice -that this command is only available if CLN was compiled with -`DEVELOPER=1`. - -### My channel is stuck in state `CHANNELD_AWAITING_LOCKIN` - -There are two root causes to this issue: - - Funding transaction isn't confirmed yet. In this case we have to - wait longer, or, in the case of a transaction that'll never - confirm, forget the channel safely. - - The peer hasn't sent a lockin message. This message ackowledges - that the node has seen sufficiently many confirmations to consider - the channel funded. - -In the case of a confirmed funding transaction but a missing lockin -message, a simple reconnection may be sufficient to nudge it to -acknowledge the confirmation: - -```bash -lightning-cli disconnect $PEERID true # force a disconnect -lightning-cli connect $PEERID -``` - -The lack of funding locked messages is a bug we are trying to debug -here at issue [#5366][5366], if you have encountered this issue please -drop us a comment and any information that may be helpful. - -If this didn't work it could be that the peer is simply not caught up -with the blockchain and hasn't seen the funding confirm yet. In this -case we can either wait or force a unilateral close: - -```bash -lightning-cli close $PEERID 10 # Force a unilateral after 10 seconds -``` - -If the funding transaction is not confirmed we may either wait or -attempt to double-spend it. Confirmations may take a long time, -especially when the fees used for the funding transaction were -low. You can check if the transaction is still going to confirm by -looking the funding transaction on a block explorer: - -```bash -TXID=$(lightning-cli listpeers $PEERID | jq -r ''.peers[].channels[].funding_txid') -``` - -This will give you the funding transaction ID that can be looked up in -any explorer. - -If you don't want to wait for the channel to confirm, you could forget -the channel (see [How to forget about a channel?](#forget-channel) for -details), however be careful as that may be dangerous and you'll need -to rescan and double-spend the outputs so the funding cannot confirm. - -## Loss of funds - -### Rescanning the block chain for lost utxos - -There are 3 types of 'rescans' you can make: -- `rescanblockchain`: A `bitcoind` RPC call which rescans the blockchain - starting at the given height. This does not have an effect on Core Lightning - as `lightningd` tracks all block and wallet data independently. -- `--rescan=depth`: A `lightningd` configuration flag. This flag is read at node startup - and tells lightningd at what depth from current blockheight to rebuild its internal state. - (You can specify an exact block to start scanning from, instead of depth from current height, - by using a negative number.) -- `dev-rescan-outputs`: A `lightningd` RPC call. Only available if your node has been - configured and built in DEVELOPER mode (i.e. `./configure --enable-developer`) This - will sync the state for known UTXOs in the `lightningd` wallet with `bitcoind`. - As it only operates on outputs already seen on chain by the `lightningd` internal - wallet, this will not find missing wallet funds. - - -### Database corruption / channel state lost - -If you lose data (likely corrupted `lightningd.sqlite3`) about a channel __with `option_static_remotekey` enabled__, -you can wait for your peer to unilateraly close the channel, then use `tools/hsmtool` with the -`guesstoremote` command to attempt to recover your funds from the peer's published unilateral close transaction. - -If `option_static_remotekey` was not enabled, you're probably out of luck. The keys for your funds in your peer's -unilateral close transaction are derived from information you lost. Fortunately, since version `0.7.3` channels -are created with `option_static_remotekey` by default if your peer supports it. -Which is to say that channels created after block [598000](https://blockstream.info/block/0000000000000000000dd93b8fb5c622b9c903bf6f921ef48e266f0ead7faedb) -(short channel id starting with > 598000) have a high chance of supporting `option_static_remotekey`. - -You can verify it using the `features` field from the [`listpeers` command](https://lightning.readthedocs.io/lightning-listpeers.7.html)'s result. - -Here is an example in Python checking if [one of the `option_static_remotekey` bits][spec-features] is set in the negotiated features corresponding to `0x02aaa2`: -```python ->>> bool(0x02aaa2 & ((1 << 12) | (1 << 13))) -True -``` - -If `option_static_remotekey` is enabled you can attempt to recover the -funds in a channel following [this tutorial][mandelbit-recovery] on -how to extract the necessary information from the network topology. If -successful, result will be a private key matching a unilaterally -closed channel, that you can import into any wallet, recovering the -funds into that wallet. - -[spec-features]: https://github.com/lightning/bolts/blob/master/09-features.md -[mandelbit-recovery]: https://github.com/mandelbit/bitcoin-tutorials/blob/master/CLightningRecoverFunds.md -[5366]: https://github.com/ElementsProject/lightning/issues/5366 - -## Technical Questions - -### How do I get the `psbt` for RPC calls that need it? - -A `psbt` is created and returned by a call to [`utxopsbt` with `reservedok=true`](https://lightning.readthedocs.io/lightning-utxopsbt.7.html?highlight=psbt). diff --git a/doc/FUZZING.md b/doc/FUZZING.md deleted file mode 100644 index 0acb710f0d0e..000000000000 --- a/doc/FUZZING.md +++ /dev/null @@ -1,115 +0,0 @@ -# Fuzz testing - -C-lightning currently supports coverage-guided fuzz testing using [LLVM's libfuzzer](https://www.llvm.org/docs/LibFuzzer.html) -when built with `clang`. - -The goal of fuzzing is to generate mutated -and often unexpected- inputs (`seed`s) to pass -to (parts of) a program (`target`) in order to make sure the codepaths used: -- do not crash -- are valid (if combined with sanitizers) -The generated seeds can be stored and form a `corpus`, which we try to optimise (don't -store two seeds that lead to the same codepath). - -For more info about fuzzing see [here](https://github.com/google/fuzzing/tree/master/docs), -and for more about `libfuzzer` in particular see [here](https://www.llvm.org/docs/LibFuzzer.html). - - -## Build the fuzz targets - -In order to build the C-lightning binaries with code coverage you will need a recent -[clang](http://clang.llvm.org/). The more recent the compiler version the better. - -Then you'll need to enable support at configuration time. You likely want to enable -a few sanitizers for bug detections as well as experimental features for an extended -coverage (not required though). - -``` -./configure --enable-developer --enable-experimental-features --enable-address-sanitizer --enable-ub-sanitizer --enable-fuzzing --disable-valgrind CC=clang && make -``` - -The targets will be built in `tests/fuzz/` as `fuzz-` binaries, with their best -known seed corpora stored in `tests/fuzz/corpora/`. - -You can run the fuzz targets on their seed corpora to check for regressions: - -``` -make check-fuzz -``` - - -## Run one or more target(s) - -You can run each target independently. Pass `-help=1` to see available options, for -example: -``` -./tests/fuzz/fuzz-addr -help=1 -``` - -Otherwise, you can use the Python runner to either run the targets against a given seed -corpus: -``` -./tests/fuzz/run.py fuzz_corpus -j2 -``` -Or extend this corpus: -``` -./tests/fuzz/run.py fuzz_corpus -j2 --generate --runs 12345 -``` - -The latter will run all targets two by two `12345` times. - -If you want to contribute new seeds, be sure to merge your corpus with the main one: -``` -./tests/fuzz/run.py my_locally_extended_fuzz_corpus -j2 --generate --runs 12345 -./tests/fuzz/run.py tests/fuzz/corpora --merge_dir my_locally_extended_fuzz_corpus -``` - - -## Improve seed corpora - -If you find coverage increasing inputs while fuzzing, please create a pull -request to add them into `tests/fuzz/corpora`. Be sure to minimize any additions -to the corpora first. - -### Example - -Here's an example workflow to contribute new inputs for the `fuzz-addr` target. - -Create a directory for newly found corpus inputs and begin fuzzing: - -```shell -mkdir -p local_corpora/fuzz-addr -./tests/fuzz/fuzz-addr -jobs=4 local_corpora/fuzz-addr tests/fuzz/corpora/fuzz-addr/ -``` - -After some time, libFuzzer may find some potential coverage increasing inputs -and save them in `local_corpora/fuzz-addr`. We can then merge them into the seed -corpora in `tests/fuzz/corpora`: - -```shell -./tests/fuzz/run.py tests/fuzz/corpora --merge_dir local_corpora -``` - -This will copy over any inputs that improve the coverage of the existing corpus. -If any new inputs were added, create a pull request to improve the upstream seed -corpus: - -```shell -git add tests/fuzz/corpora/fuzz-addr/* -git commit -... -``` - - -## Write new fuzzing targets - -In order to write a new target: - - include the `libfuzz.h` header - - fill two functions: `init()` for static stuff and `run()` which will be called - repeatedly with mutated data. - - read about [what makes a good fuzz target](https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md). - -A simple example is [`fuzz-addr`][fuzz-addr]. It setups the -chainparams and context (wally, tmpctx, ..) in `init()` then -bruteforces the bech32 encoder in `run()`. - -[fuzz-addr]: https://github.com/ElementsProject/lightning/blob/master/tests/fuzz/fuzz-addr.c diff --git a/doc/GOSSIP_STORE.md b/doc/GOSSIP_STORE.md deleted file mode 100644 index 54a6728eb757..000000000000 --- a/doc/GOSSIP_STORE.md +++ /dev/null @@ -1,146 +0,0 @@ -# gossip_store: Direct Access To Lightning Gossip - -Hi! - -The lightning_gossipd dameon stores the gossip messages, along with -some internal data, in a file called the "gossip_store". Various -plugins and daemons access this (in a read-only manner), and the -format is documented here. - -## The File Header - -``` -u8 version; -``` - -The gossmap header consists of one byte. The top 3 bits are the major -version: if these are not all zero, you need to re-read this (updated) -document to see what changed. The lower 5 bits are the minor version, -which won't worry you: currently they will be 11. - -After the file header comes a number of records. - -## The Record Header - -``` -be16 flags; -be16 len; -be32 crc; -be32 timestamp; -``` - -Each record consists of a header and a message. The header is -big-endian, containing flags, the length (of the following body), the -crc32c (of the following message, starting with the timestamp field in -the header) and a timestamp extracted from certain messages (zero -where not relevant, but ignore it in those cases). - -The flags currently defined are: - -``` -#define DELETED 0x8000 -#define PUSH 0x4000 -#define RATELIMIT 0x2000 -``` - -Deleted fields should be ignored: on restart, they will be removed as -the gossip_store is rewritten. - -The push flag indicates gossip which is generated locally: this is -important for gossip timestamp filtering, where peers request gossip -and we always send our own gossip messages even if the timestamp -wasn't within their request. - -The ratelimit flag indicates that this gossip message came too fast: -we record it, but don't relay it to peers. - -Other flags should be ignored. - -## The Message - -Each messages consists of a 16-bit big-endian "type" field (for -efficiency, an implementation may read this along with the header), -and optional data. Some messages are defined by the BOLT 7 gossip -protocol, others are for internal use. Unknown message types should be -skipped over. - -### BOLT 7 Messages - -These are the messages which gossipd has validated, and ensured are in -order. - -* `channel_announcement` (256): a complete, validated channel announcement. This will always come before any `channel_update` which refers to it, or `node_announcement` which refers to a node. -* `channel_update` (258): a complete, validated channel update. Note that you can see multiple of these (old ones will be deleted as they are replaced though). -* `node_announcement` (257): a complete, validated node announcement. Note that you can also see multiple of these (old ones will be deleted as they are replaced). - -### Internal Gossip Daemon Messages - -These messages contain additional data, which may be useful. - -* `gossip_store_channel_amount` (4101) - * `satoshis`: u64 - -This always immediately follows `channel_announcement` messages, and -contains the actual capacity of the channel. - -* `gossip_store_private_channel` (4104) - * `amount_sat`: u64 - * `len`: u16 - * `announcement`: u8[len] - -This contains information about a private (could be made public -later!) channel, with announcement in the same format as a normal -`channel_announcement` with invalid signatures. - -* `gossip_store_private_update` (4102) - * `len`: u16 - * `update`: u8[len] - -This contains a private `channel_update` (i.e. for a channel described -by `gossip_store_private_channel`. - -* `gossip_store_delete_chan` (4103) - * `scid`: u64 - -This is added when a channel is deleted. You won't often see this if -you're reading the file once (as the channel record header will have -been marked `deleted` first), but useful if you are polling the file -for updates. - -* `gossip_store_ended` (4105) - * `equivalent_offset`: u64 - -This is only ever added as the final entry in the gossip_store. It -means the file has been deleted (usually because lightningd has been -restarted), and you should re-open it. As an optimization, the -`equivalent_offset` in the new file reflects the point at which the -new gossip_store is equivalent to this one (with deleted records -removed). However, if lightningd has been restarted multiple times it -is possible that this offset is not valid, so it's really only useful -if you're actively monitoring the file. - -* `gossip_store_chan_dying` (4106) - * `scid`: u64 - * `blockheight`: u32 - -This is placed in the gossip_store file when a funding transaction is -spent. `blockheight` is set to 12 blocks beyond the block containing -the spend: at this point, gossipd will delete the channel. - -## Using the Gossip Store File - -- Always check the major version number! We will increment it if the format - changes in a way that breaks readers. -- Ignore unknown flags in the header. -- Ignore message types you don't know. -- You don't need to check the messages, as they have been validated. -- It is possible to see a partially-written record at the end. Ignore it. - -If you are keeping the file open to watch for changes: - -- The file is append-only, so you can simply try reading more records - using inotify (or equivalent) or simply checking every few seconds. -- If you see a `gossip_store_ended` message, reopen the file. - -Happy hacking! -Rusty. diff --git a/doc/HACKING.md b/doc/HACKING.md deleted file mode 100644 index 26130658e45a..000000000000 --- a/doc/HACKING.md +++ /dev/null @@ -1,332 +0,0 @@ -Hacking -======= - -Welcome, fellow coder! - -This repository contains a code to run a lightning protocol daemon. -It's broken into subdaemons, with the idea being that we can add more -layers of separation between different clients and extra barriers to -exploits. - -It is designed to implement the lightning protocol as specified in -[various BOLTs](https://github.com/lightning/bolts). - - -Getting Started ---------------- -It's in C, to encourage alternate implementations. Patches are welcome! -You should read our [Style Guide](STYLE.md). - -To read the code, you should start from -[lightningd.c](https://github.com/ElementsProject/lightning/blob/master/lightningd/lightningd.c) and hop your way through -the '~' comments at the head of each daemon in the suggested -order. - -The Components --------------- -Here's a list of parts, with notes: - -* ccan - useful routines from http://ccodearchive.net - - Use make update-ccan to update it. - - Use make update-ccan CCAN_NEW="mod1 mod2..." to add modules - - Do not edit this! If you want a wrapper, add one to common/utils.h. - -* bitcoin/ - bitcoin script, signature and transaction routines. - - Not a complete set, but enough for our purposes. - -* external/ - external libraries from other sources - - libbacktrace - library to provide backtraces when things go wrong. - - libsodium - encryption library (should be replaced soon with built-in) - - libwally-core - bitcoin helper library - - secp256k1 - bitcoin curve encryption library within libwally-core - - jsmn - tiny JSON parsing helper - -* tools/ - tools for building - - check-bolt.c: check the source code contains correct BOLT quotes - (as used by check-source) - - generate-wire.py: generates wire marshal/unmarshal-ing - routines for subdaemons and BOLT specs. - - mockup.sh / update-mocks.sh: tools to generate mock functions for - unit tests. - -* tests/ - blackbox tests (mainly) - - unit tests are in tests/ subdirectories in each other directory. - -* doc/ - you are here - -* devtools/ - tools for developers - - Generally for decoding our formats. - -* contrib/ - python support and other stuff which doesn't belong :) - -* wire/ - basic marshalling/un for messages defined in the BOLTs - -* common/ - routines needed by any two or more of the directories below - -* cli/ - commandline utility to control lightning daemon. - -* lightningd/ - master daemon which controls the subdaemons and passes - peer file descriptors between them. - -* wallet/ - database code used by master for tracking what's happening. - -* hsmd/ - daemon which looks after the cryptographic secret, and performs - commitment signing. - -* gossipd/ - daemon to maintain routing information and broadcast gossip. - -* connectd/ - daemon to connect to other peers, and receive incoming. - -* openingd/ - daemon to open a channel for a single peer, and chat to - a peer which doesn't have any channels/ - -* channeld/ - daemon to operate a single peer once channel is operating - normally. - -* closingd/ - daemon to handle mutual closing negotiation with a single peer. - -* onchaind/ - daemon to handle a single channel which has had its funding - transaction spent. - -Debugging ---------- - -You can build Core Lightning with DEVELOPER=1 to use dev commands listed in -``cli/lightning-cli help``. ``./configure --enable-developer`` will do that. -You can log console messages with log_info() in lightningd and status_debug() -in other subdaemons. - -You can debug crashing subdaemons with the argument -`--dev-debugger=channeld`, where `channeld` is the subdaemon name. It -will run `gnome-terminal` by default with a gdb attached to the -subdaemon when it starts. You can change the terminal used by setting -the `DEBUG_TERM` environment variable, such as `DEBUG_TERM="xterm -e"` -or `DEBUG_TERM="konsole -e"`. - -It will also print out (to stderr) the gdb command for manual connection. The -subdaemon will be stopped (it sends itself a SIGSTOP); you'll need to -`continue` in gdb. - -Database --------- - -Core Lightning state is persisted in `lightning-dir`. -It is a sqlite database stored in the `lightningd.sqlite3` file, typically -under `~/.lightning//`. -You can run queries against this file like so: - - $ sqlite3 ~/.lightning/bitcoin/lightningd.sqlite3 \ - "SELECT HEX(prev_out_tx), prev_out_index, status FROM outputs" - -Or you can launch into the sqlite3 repl and check things out from there: - - $ sqlite3 ~/.lightning/bitcoin/lightningd.sqlite3 - SQLite version 3.21.0 2017-10-24 18:55:49 - Enter ".help" for usage hints. - sqlite> .tables - channel_configs invoices peers vars - channel_htlcs outputs shachain_known version - channels payments shachains - sqlite> .schema outputs - ... - -Some data is stored as raw bytes, use `HEX(column)` to pretty print these. - -Make sure that clightning is not running when you query the database, -as some queries may lock the database and cause crashes. - -#### Common variables -Table `vars` contains global variables used by lightning node. - - $ sqlite3 ~/.lightning/bitcoin/lightningd.sqlite3 - SQLite version 3.21.0 2017-10-24 18:55:49 - Enter ".help" for usage hints. - sqlite> .headers on - sqlite> select * from vars; - name|val - next_pay_index|2 - bip32_max_index|4 - ... - -Variables: -* `next_pay_index` next resolved invoice counter that will get assigned. -* `bip32_max_index` last wallet derivation counter. - -Note: Each time `newaddr` command is called, `bip32_max_index` counter -is increased to the last derivation index. -Each address generated after `bip32_max_index` is not included as -lightning funds. - - -Build and Development ---------------------- -Install the following dependencies for best results: - -``` -sudo apt update -sudo apt install valgrind cppcheck shellcheck libsecp256k1-dev libpq-dev -``` - -Re-run `configure` and build using `make`: - -``` -./configure --enable-developer -make -j$(nproc) -``` - - -Testing -------- -Tests are run with: `make check [flags]` where the pertinent flags are: - -``` -DEVELOPER=[0|1] - developer mode increases test coverage -VALGRIND=[0|1] - detects memory leaks during test execution but adds a significant delay -PYTEST_PAR=n - runs pytests in parallel -``` - -A modern desktop can build and run through all the tests in a couple of minutes with: - - make -j12 full-check PYTEST_PAR=24 DEVELOPER=1 VALGRIND=0 - -Adjust `-j` and `PYTEST_PAR` accordingly for your hardware. - -There are four kinds of tests: - -* **source tests** - run by `make check-source`, looks for whitespace, - header order, and checks formatted quotes from BOLTs if BOLTDIR - exists. - -* **unit tests** - standalone programs that can be run individually. You can - also run all of the unit tests with `make check-units`. - They are `run-*.c` files in test/ subdirectories used to test routines - inside C source files. - - You should insert the lines when implementing a unit test: - - `/* AUTOGENERATED MOCKS START */` - - `/* AUTOGENERATED MOCKS END */` - - and `make update-mocks` will automatically generate stub functions which will - allow you to link (and conveniently crash if they're called). - -* **blackbox tests** - These tests setup a mini-regtest environment and test - lightningd as a whole. They can be run individually: - - `PYTHONPATH=contrib/pylightning:contrib/pyln-client:contrib/pyln-testing:contrib/pyln-proto py.test -v tests/` - - You can also append `-k TESTNAME` to run a single test. Environment variables - `DEBUG_SUBD=` and `TIMEOUT=` can be useful for debugging - subdaemons on individual tests. - -* **pylightning tests** - will check contrib pylightning for codestyle and run - the tests in `contrib/pylightning/tests` afterwards: - - `make check-python` - -Our Github Actions instance (see `.github/workflows/*.yml`) runs all these for each -pull request. - -#### Additional Environment Variables - -``` -TEST_CHECK_DBSTMTS=[0|1] - When running blackbox tests, this will - load a plugin that logs all compiled - and expanded database statements. - Note: Only SQLite3. -TEST_DB_PROVIDER=[sqlite3|postgres] - Selects the database to use when running - blackbox tests. -EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests. -``` - -#### Troubleshooting - -##### Valgrind complains about code we don't control - -Sometimes `valgrind` will complain about code we do not control -ourselves, either because it's in a library we use or it's a false -positive. There are generally three ways to address these issues -(in descending order of preference): - - 1. Add a suppression for the one specific call that is causing the - issue. Upon finding an issue `valgrind` is instructed in the - testing framework to print filters that'd match the issue. These - can be added to the suppressions file under - `tests/valgrind-suppressions.txt` in order to explicitly skip - reporting these in future. This is preferred over the other - solutions since it only disables reporting selectively for things - that were manually checked. See the [valgrind docs][vg-supp] for - details. - 2. Add the process that `valgrind` is complaining about to the - `--trace-children-skip` argument in `pyln-testing`. This is used - in cases of full binaries not being under our control, such as the - `python3` interpreter used in tests that run plugins. Do not use - this for binaries that are compiled from our code, as it tends to - mask real issues. - 3. Mark the test as skipped if running under `valgrind`. It's mostly - used to skip tests that otherwise would take considerably too long - to test on CI. We discourage this for suppressions, since it is a - very blunt tool. - -[vg-supp]: https://valgrind.org/docs/manual/manual-core.html#manual-core.suppress - -Making BOLT Modifications -------------------------- - -All of code for marshalling/unmarshalling BOLT protocol messages is generated -directly from the spec. These are pegged to the BOLTVERSION, as specified in -`Makefile`. - - -Source code analysis --------------------- -An updated version of the NCC source code analysis tool is available at - -https://github.com/bitonic-cjp/ncc - -It can be used to analyze the lightningd source code by running -`make clean && make ncc`. The output (which is built in parallel with the -binaries) is stored in .nccout files. You can browse it, for instance, with -a command like `nccnav lightningd/lightningd.nccout`. - -Subtleties ----------- - -There are a few subtleties you should be aware of as you modify deeper -parts of the code: - -* `ccan/structeq`'s STRUCTEQ_DEF will define safe comparison function foo_eq() - for struct foo, failing the build if the structure has implied padding. -* `command_success`, `command_fail`, and `command_fail_detailed` will free the - `cmd` you pass in. - This also means that if you `tal`-allocated anything from the `cmd`, they - will also get freed at those points and will no longer be accessible - afterwards. -* When making a structure part of a list, you will instance a - `struct list_node`. - This has to be the *first* field of the structure, or else `dev-memleak` - command will think your structure has leaked. - - -Protocol Modifications ----------------------- - -The source tree contains CSV files extracted from the v1.0 BOLT -specifications (wire/extracted_peer_wire_csv and -wire/extracted_onion_wire_csv). You can regenerate these by -first deleting the local copy(if any) at directory .tmp.bolts, -setting `BOLTDIR` and `BOLTVERSION` appropriately, and finally running `make -extract-bolt-csv`. By default the bolts will be retrieved from the -directory `../bolts` and a recent git version. - -e.g., `make extract-bolt-csv BOLTDIR=../bolts BOLTVERSION=ee76043271f79f45b3392e629fd35e47f1268dc8` - -Further Information -------------------- - -Feel free to ask questions on the lightning-dev mailing list, or on -`#c-lightning` on IRC, or email me at rusty@rustcorp.com.au. - -Cheers!
-Rusty. diff --git a/doc/INSTALL.md b/doc/INSTALL.md deleted file mode 100644 index 5512c4a8cc60..000000000000 --- a/doc/INSTALL.md +++ /dev/null @@ -1,485 +0,0 @@ -Install -======= - -- [Library Requirements](#library-requirements) -- [Ubuntu](#to-build-on-ubuntu) -- [Fedora](#to-build-on-fedora) -- [FreeBSD](#to-build-on-freebsd) -- [OpenBSD](#to-build-on-openbsd) -- [NixOS](#to-build-on-nixos) -- [macOS](#to-build-on-macos) -- [Arch Linux](#to-build-on-arch-linux) -- [Android](#to-cross-compile-for-android) -- [Raspberry Pi](#to-cross-compile-for-raspberry-pi) -- [Armbian](#to-compile-for-armbian) -- [Alpine](#to-compile-for-alpine) -- [Additional steps](#additional-steps) - -Library Requirements --------------------- - -You will need several development libraries: -* libsqlite3: for database support. -* libgmp: for secp256k1 -* zlib: for compression routines. - -For actually doing development and running the tests, you will also need: -* pip3: to install python-bitcoinlib -* valgrind: for extra debugging checks - -You will also need a version of bitcoind with segregated witness and `estimatesmartfee` with `ECONOMICAL` mode support, such as the 0.16 or above. - -To Build on Ubuntu ---------------------- - -OS version: Ubuntu 15.10 or above - -Get dependencies: - - sudo apt-get update - sudo apt-get install -y \ - autoconf automake build-essential git libtool libgmp-dev libsqlite3-dev \ - python3 python3-pip net-tools zlib1g-dev libsodium-dev gettext - pip3 install --upgrade pip - pip3 install --user poetry - -If you don't have Bitcoin installed locally you'll need to install that -as well. It's now available via [snapd](https://snapcraft.io/bitcoin-core). - - sudo apt-get install snapd - sudo snap install bitcoin-core - # Snap does some weird things with binary names; you'll - # want to add a link to them so everything works as expected - sudo ln -s /snap/bitcoin-core/current/bin/bitcoin{d,-cli} /usr/local/bin/ - -Clone lightning: - - git clone https://github.com/ElementsProject/lightning.git - cd lightning - -Checkout a release tag: - - git checkout v22.11.1 - -For development or running tests, get additional dependencies: - - sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ - libsecp256k1-dev jq lowdown - -If you can't install `lowdown`, a version will be built in-tree. - -If you want to build the Rust plugins (currently, cln-grpc): - - sudo apt-get install -y cargo rustfmt protobuf-compiler - -There are two ways to build core lightning, and this depends on how you want use it. - -To build cln to just install a tagged or master version you can use the following commands: - - pip3 install --upgrade pip - pip3 install mako - ./configure - make - sudo make install - -N.B: if you want disable Rust because you do not want use it or simple you do not want the grpc-plugin, you can use `./configure --disable-rust`. - -To build core lightning for development purpose you can use the following commands: - - pip3 install poetry - poetry shell - -This will put you in a new shell to enter the following commands: - - poetry install - ./configure --enable-developer - make - make check VALGRIND=0 - -optionally, add `-j$(nproc)` after `make` to speed up compilation. (e.g. `make -j$(nproc)`) - -Running lightning: - - bitcoind & - ./lightningd/lightningd & - ./cli/lightning-cli help - -To Build on Fedora ---------------------- - -OS version: Fedora 27 or above - -Get dependencies: -``` -$ sudo dnf update -y && \ - sudo dnf groupinstall -y \ - 'C Development Tools and Libraries' \ - 'Development Tools' && \ - sudo dnf install -y \ - clang \ - gettext \ - git \ - gmp-devel \ - libsq3-devel \ - python3-devel \ - python3-pip \ - python3-setuptools \ - net-tools \ - valgrind \ - wget \ - zlib-devel \ - libsodium-devel && \ - sudo dnf clean all -``` - -Make sure you have [bitcoind](https://github.com/bitcoin/bitcoin) available to run - -Clone lightning: -``` -$ git clone https://github.com/ElementsProject/lightning.git -$ cd lightning -``` - -Checkout a release tag: -``` -$ git checkout v22.11.1 -``` - -Build and install lightning: -``` -$lightning> ./configure -$lightning> make -$lightning> sudo make install -``` - -Running lightning (mainnet): -``` -$ bitcoind & -$ lightningd --network=bitcoin -``` - -Running lightning on testnet: -``` -$ bitcoind -testnet & -$ lightningd --network=testnet -``` - -To Build on FreeBSD -------------------- - -OS version: FreeBSD 11.1-RELEASE or above - -Core Lightning is in the FreeBSD ports, so install it as any other port -(dependencies are handled automatically): - - # pkg install c-lightning - -for a binary, pre-compiled package. If you want to compile locally and -fiddle with compile time options: - - # cd /usr/ports/net-p2p/c-lightning && make install - -See `/usr/ports/net-p2p/c-lightning/Makefile` for instructions on how to -build from an arbitrary git commit, instead of the latest release tag. - -**Note**: Make sure you've set an utf-8 locale, e.g. -`export LC_CTYPE=en_US.UTF-8`, otherwise manpage installation may fail. - -Running lightning: - -Configure bitcoind, if not already: add `rpcuser=` and `rpcpassword=` -to `/usr/local/etc/bitcoin.conf`, maybe also `testnet=1`. - -Configure lightningd: copy `/usr/local/etc/lightningd-bitcoin.conf.sample` to -`/usr/local/etc/lightningd-bitcoin.conf` and edit according to your needs. - - # service bitcoind start - # service lightningd start - # lightning-cli --rpc-file /var/db/c-lightning/bitcoin/lightning-rpc --lightning-dir=/var/db/c-lightning help - -To Build on OpenBSD --------------------- - -OS version: OpenBSD 6.7 - -Install dependencies: -``` -pkg_add git python gmake py3-pip libtool gmp -pkg_add automake # (select highest version, automake1.16.2 at time of writing) -pkg_add autoconf # (select highest version, autoconf-2.69p2 at time of writing) -``` -Install `mako` otherwise we run into build errors: -``` -pip3.7 install --user poetry -poetry install -``` - -Add `/home//.local/bin` to your path: - -`export PATH=$PATH:/home//.local/bin` - -Needed for `configure`: -``` -export AUTOCONF_VERSION=2.69 -export AUTOMAKE_VERSION=1.16 -./configure -``` - -Finally, build `c-lightning`: - -`gmake` - - -To Build on NixOS --------------------- - -Use nix-shell launch a shell with a full clightning dev environment: - -``` -$ nix-shell -Q -p gdb sqlite autoconf git clang libtool gmp sqlite autoconf \ -autogen automake libsodium 'python3.withPackages (p: [p.bitcoinlib])' \ -valgrind --run make -``` - -To Build on macOS ---------------------- - -Assuming you have Xcode and Homebrew installed. Install dependencies: - - $ brew install autoconf automake libtool python3 gmp gnu-sed gettext libsodium protobuf - $ ln -s /usr/local/Cellar/gettext/0.20.1/bin/xgettext /usr/local/opt - $ export PATH="/usr/local/opt:$PATH" - -If you need SQLite (or get a SQLite mismatch build error): - - $ brew install sqlite - $ export LDFLAGS="-L/usr/local/opt/sqlite/lib" - $ export CPPFLAGS="-I/usr/local/opt/sqlite/include" - -Some library paths are different when using `homebrew` with M1 macs, therefore the following two variables need to be set for M1 machines - - $ export CPATH=/opt/homebrew/include - $ export LIBRARY_PATH=/opt/homebrew/lib - -If you need Python 3.x for mako (or get a mako build error): - - $ brew install pyenv - $ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile - $ source ~/.bash_profile - $ pyenv install 3.8.10 - $ pip3 install --upgrade pip - $ pip3 install poetry - -If you don't have bitcoind installed locally you'll need to install that -as well: - - $ brew install berkeley-db4 boost miniupnpc pkg-config libevent - $ git clone https://github.com/bitcoin/bitcoin - $ cd bitcoin - $ ./autogen.sh - $ ./configure - $ make src/bitcoind src/bitcoin-cli && make install - -Clone lightning: - - $ git clone https://github.com/ElementsProject/lightning.git - $ cd lightning - -Checkout a release tag: - - $ git checkout v22.11.1 - -Build lightning: - - $ poetry install - $ ./configure - $ poetry run make - -Running lightning: - -**Note**: Edit your `~/Library/Application\ Support/Bitcoin/bitcoin.conf` -to include `rpcuser=` and `rpcpassword=` first, you may also -need to include `testnet=1` - - bitcoind & - ./lightningd/lightningd & - ./cli/lightning-cli help - - -To install the built binaries into your system, you'll need to run `sudo make install`: - - sudo make install - -On an M1 mac you may need to use this command instead: - - sudo PATH="/usr/local/opt:$PATH" LIBRARY_PATH=/opt/homebrew/lib CPATH=/opt/homebrew/include make install - - -To Build on Arch Linux ---------------------- - -Install dependencies: - -``` -pacman --sync autoconf automake gcc git make python-pip -pip install --user poetry -``` - -Clone Core Lightning: - -``` -$ git clone https://github.com/ElementsProject/lightning.git -$ cd lightning -``` - -Build Core Lightning: - -``` -python -m poetry install -./configure -python -m poetry run make -``` - -Launch Core Lightning: - -``` -./lightningd/lightningd -``` - -To cross-compile for Android --------------------- - -Make a standalone toolchain as per -https://developer.android.com/ndk/guides/standalone_toolchain.html. -For Core Lightning you must target an API level of 24 or higher. - -Depending on your toolchain location and target arch, source env variables -such as: - - export PATH=$PATH:/path/to/android/toolchain/bin - # Change next line depending on target device arch - target_host=arm-linux-androideabi - export AR=$target_host-ar - export AS=$target_host-clang - export CC=$target_host-clang - export CXX=$target_host-clang++ - export LD=$target_host-ld - export STRIP=$target_host-strip - -Two makefile targets should not be cross-compiled so we specify a native CC: - - make CC=clang clean ccan/tools/configurator/configurator - make clean -C ccan/ccan/cdump/tools \ - && make CC=clang -C ccan/ccan/cdump/tools - -Install the `qemu-user` package. -This will allow you to properly configure -the build for the target device environment. -Build with: - - BUILD=x86_64 MAKE_HOST=arm-linux-androideabi \ - make PIE=1 DEVELOPER=0 \ - CONFIGURATOR_CC="arm-linux-androideabi-clang -static" - -To cross-compile for Raspberry Pi --------------------- - -Obtain the [official Raspberry Pi toolchains](https://github.com/raspberrypi/tools). -This document assumes compilation will occur towards the Raspberry Pi 3 -(arm-linux-gnueabihf as of Mar. 2018). - -Depending on your toolchain location and target arch, source env variables -will need to be set. They can be set from the command line as such: - - export PATH=$PATH:/path/to/arm-linux-gnueabihf/bin - # Change next line depending on specific Raspberry Pi device - target_host=arm-linux-gnueabihf - export AR=$target_host-ar - export AS=$target_host-as - export CC=$target_host-gcc - export CXX=$target_host-g++ - export LD=$target_host-ld - export STRIP=$target_host-strip - -Install the `qemu-user` package. This will allow you to properly configure the -build for the target device environment. -Config the arm elf interpreter prefix: - - export QEMU_LD_PREFIX=/path/to/raspberry/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/ - -Obtain and install cross-compiled versions of sqlite3, gmp and zlib: - -Download and build zlib: - - wget https://zlib.net/fossils/zlib-1.2.13.tar.gz - tar xvf zlib-1.2.13.tar.gz - cd zlib-1.2.13 - ./configure --prefix=$QEMU_LD_PREFIX - make - make install - -Download and build sqlite3: - - wget https://www.sqlite.org/2018/sqlite-src-3260000.zip - unzip sqlite-src-3260000.zip - cd sqlite-src-3260000 - ./configure --enable-static --disable-readline --disable-threadsafe --disable-load-extension --host=$target_host --prefix=$QEMU_LD_PREFIX - make - make install - -Download and build gmp: - - wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz - tar xvf gmp-6.1.2.tar.xz - cd gmp-6.1.2 - ./configure --disable-assembly --host=$target_host --prefix=$QEMU_LD_PREFIX - make - make install - -Then, build Core Lightning with the following commands: - - ./configure - make - -To compile for Armbian --------------------- -For all the other Pi devices out there, consider using [Armbian](https://www.armbian.com). - -You can compile in `customize-image.sh` using the instructions for Ubuntu. - -A working example that compiles both bitcoind and Core Lightning for Armbian can -be found [here](https://github.com/Sjors/armbian-bitcoin-core). - -To compile for Alpine ---------------------- -Get dependencies: -``` -apk update -apk add --virtual .build-deps ca-certificates alpine-sdk autoconf automake git libtool \ - gmp-dev sqlite-dev python3 py3-mako net-tools zlib-dev libsodium gettext -``` -Clone lightning: -``` -git clone https://github.com/ElementsProject/lightning.git -cd lightning -git submodule update --init --recursive -``` -Build and install: -``` -./configure -make -make install -``` -Clean up: -``` -cd .. && rm -rf lightning -apk del .build-deps -``` -Install runtime dependencies: -``` -apk add gmp libgcc libsodium sqlite-libs zlib -``` - -Additional steps --------------------- -Go to [README](https://github.com/ElementsProject/lightning/blob/master/README.md) for more information how to create an address, add funds, connect to a node, etc. diff --git a/doc/MAKING-RELEASES.md b/doc/MAKING-RELEASES.md deleted file mode 100644 index d93d8b619903..000000000000 --- a/doc/MAKING-RELEASES.md +++ /dev/null @@ -1,105 +0,0 @@ -## Release checklist - -Here's a checklist for the release process. - -### Leading Up To The Release - -1. Talk to team about whether there are any changes which MUST go in - this release which may cause delay. -2. Look through outstanding issues, to identify any problems that might - be necessary to fixup before the release. Good candidates are reports - of the project not building on different architectures or crashes. -3. Identify a good lead for each outstanding issue, and ask them about - a fix timeline. -4. Create a milestone for the *next* release on Github, and go though - open issues and PRs and mark accordingly. -5. Ask (via email) the most significant contributor who has not - already named a release to name the release (use devtools/credit to - find this contributor). CC previous namers and team. - -### Preparing for -rc1 - -1. Check that `CHANGELOG.md` is well formatted, ordered in areas, - covers all signficant changes, and sub-ordered approximately by user impact - & coolness. -2. Use `devtools/changelog.py` to collect the changelog entries from pull - request commit messages and merge them into the manually maintained - `CHANGELOG.md`. This does API queries to GitHub, which are severely - ratelimited unless you use an API token: set the `GH_TOKEN` environment - variable to a Personal Access Token from https://github.com/settings/tokens -3. Create a new CHANGELOG.md heading to `vrc1`, and create a link at - the bottom. Note that you should exactly copy the date and name format from - a previous release, as the `build-release.sh` script relies on this. -4. Update the contrib/pyln package versions: `make update-pyln-versions NEW_VERSION=` -5. Create a PR with the above. - -### Releasing -rc1 - -1. Merge the above PR. -2. Tag it `git pull && git tag -s vrc1`. Note that you - should get a prompt to give this tag a 'message'. Make sure you fill this in. -3. Confirm that the tag will show up for builds with `git describe` -4. Push the tag to remote `git push --tags`. -3. Update the /topic on #c-lightning on Libera. -4. Prepare draft release notes (see devtools/credit), and share with team for editing. -5. Upgrade your personal nodes to the rc1, to help testing. -6. Test `tools/build-release.sh` to build the non-reproducible images - and reproducible zipfile. -7. Use the zipfile to produce a [reproducible build](REPRODUCIBLE.md). - -### Releasing -rc2, etc - -1. Change rc1 to rc2 in CHANGELOG.md. -2. Add a PR with the rc2. -3. Tag it `git pull && git tag -s vrc2 && git push --tags` -4. Update the /topic on #c-lightning on Libera. -5. Upgrade your personal nodes to the rc2. - -### Tagging the Release - -1. Update the CHANGELOG.md; remove -rcN in both places, update the date and add title and namer. -2. Update the contrib/pyln package versions: `make update-pyln-versions NEW_VERSION=` -3. Add a PR with that release. -4. Merge the PR, then: - - `export VERSION=0.9.3` - - `git pull` - - `git tag -a -s v${VERSION} -m v${VERSION}` - - `git push --tags` -5. Run `tools/build-release.sh` to build the non-reprodicible images - and reproducible zipfile. -6. Use the zipfile to produce a [reproducible build](REPRODUCIBLE.md). -7. To create and sign checksums, start by entering the release dir: `cd release` -8. Create the checksums for signing: `sha256sum * > SHA256SUMS` -9. Create the first signature with `gpg -sb --armor SHA256SUMS` -10. The tarballs may be owned by root, so revert ownership if necessary: - `sudo chown ${USER}:${USER} *${VERSION}*` -11. Upload the resulting files to github and save as a draft. - (https://github.com/ElementsProject/lightning/releases/) -12. Ping the rest of the team to check the SHA256SUMS file and have them send their - `gpg -sb --armor SHA256SUMS`. -13. Append the signatures into a file called `SHA256SUMS.asc`, verify - with `gpg --verify SHA256SUMS.asc` and include the file in the draft - release. -14. `make pyln-release` to upload pyln modules to pypi.org. This requires keys - for each of pyln-client, pyln-proto, and pyln-testing accessible to poetry. - This can be done by configuring the python keyring library along with a - suitable backend. Alternatively, the key can be set as an environment - variable and each of the pyln releases can be built and published - independently: - - `export POETRY_PYPI_TOKEN_PYPI=` - - `make pyln-release-client` - - ... repeat for each pyln package. - -### Performing the Release - -1. Edit the GitHub draft and include the `SHA256SUMS.asc` file. -2. Publish the release as not a draft. -3. Update the /topic on #c-lightning on Libera. -4. Send a mail to c-lightning and lightning-dev mailing lists, using the - same wording as the Release Notes in github. - -### Post-release - -1. Look through PRs which were delayed for release and merge them. -2. Close out the Milestone for the now-shipped release. -3. Update this file with any missing or changed instructions. diff --git a/doc/Makefile b/doc/Makefile index 5bd7dae50d02..ce7ea26c8583 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -18,8 +18,10 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-bkpr-listaccountevents.7 \ doc/lightning-bkpr-listbalances.7 \ doc/lightning-bkpr-listincome.7 \ + doc/lightning-blacklistrune.7 \ doc/lightning-check.7 \ doc/lightning-checkmessage.7 \ + doc/lightning-checkrune.7 \ doc/lightning-close.7 \ doc/lightning-connect.7 \ doc/lightning-commando.7 \ @@ -28,6 +30,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-commando-rune.7 \ doc/lightning-createonion.7 \ doc/lightning-createinvoice.7 \ + doc/lightning-createrune.7 \ doc/lightning-datastore.7 \ doc/lightning-decodepay.7 \ doc/lightning-decode.7 \ @@ -65,6 +68,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-listpays.7 \ doc/lightning-listpeers.7 \ doc/lightning-listpeerchannels.7 \ + doc/lightning-showrunes.7 \ doc/lightning-listsendpays.7 \ doc/lightning-makesecret.7 \ doc/lightning-multifundchannel.7 \ @@ -89,6 +93,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-sendonionmessage.7 \ doc/lightning-sendpay.7 \ doc/lightning-setchannel.7 \ + doc/lightning-setconfig.7 \ doc/lightning-setpsbtversion.7 \ doc/lightning-sendcustommsg.7 \ doc/lightning-signinvoice.7 \ @@ -99,6 +104,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-txsend.7 \ doc/lightning-unreserveinputs.7 \ doc/lightning-utxopsbt.7 \ + doc/lightning-wait.7 \ doc/lightning-waitinvoice.7 \ doc/lightning-waitanyinvoice.7 \ doc/lightning-waitblockheight.7 \ @@ -227,18 +233,19 @@ doc/index.rst: $(MANPAGES:=.md) python3 devtools/blockreplace.py doc/index.rst manpages --language=rst --indent " " \ ) -# For CI to (very roughly!) check that we only deprecated fields, or labelled added ones -# When running on GitHub (CI=true), we need to fetch origin/master +# Overridden by GH CI if necessary. +BASE_REF=master schema-added-check: - @if ! test -z $$CI; then git fetch origin master; fi; \ - if git diff origin/master -- doc/schemas | grep -q '^+.*{' && ! git diff origin/master -- doc/schemas | grep -q '^+.*"added"'; then \ - git diff origin/master -- doc/schemas; \ + if git show --format=%B -s $(BASE_REF).. | grep -q '^No-schema-diff-check'; then echo $@ suppressed; exit 0; fi; \ + if git diff $(BASE_REF) -- doc/schemas | grep -q '^+.*{' && ! git diff $(BASE_REF) -- doc/schemas | grep -q '^+.*"added"'; then \ + git diff $(BASE_REF) -- doc/schemas; \ echo 'New schema fields must have "added": "vNEXTVERSION"' >&2; exit 1; \ fi + schema-removed-check: - @if ! test -z $$CI; then git fetch origin master; fi; \ - if git diff origin/master -- doc/schemas | grep -q '^-.*{' && ! git diff origin/master -- doc/schemas | grep -q '^-.*"deprecated"'; then \ - git diff origin/master -- doc/schemas ; \ + if git show --format=%B -s $(BASE_REF).. | grep -q '^No-schema-diff-check'; then echo $@ suppressed; exit 0; fi; \ + if git diff $(BASE_REF) -- doc/schemas | grep -q '^-.*{' && ! git diff $(BASE_REF) -- doc/schemas | grep -q '^-.*"deprecated"' && ! git diff $(BASE_REF) -- doc/schemas | grep -q '^-.*EXPERIMENTAL_FEATURES'; then \ + git diff $(BASE_REF) -- doc/schemas ; \ echo 'Schema fields must be "deprecated", with version, not removed' >&2; exit 1; \ fi diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md deleted file mode 100644 index bc31025010f2..000000000000 --- a/doc/PLUGINS.md +++ /dev/null @@ -1,1808 +0,0 @@ -# Plugins - -Plugins are a simple yet powerful way to extend the functionality -provided by Core Lightning. They are subprocesses that are started by the -main `lightningd` daemon and can interact with `lightningd` in a -variety of ways: - - - **Command line option passthrough** allows plugins to register their - own command line options that are exposed through `lightningd` so - that only the main process needs to be configured. Option values are not - remembered when a plugin is stopped or killed, but can be passed as parameters - to [`plugin start`][lightning-plugin]. - - **JSON-RPC command passthrough** adds a way for plugins to add their - own commands to the JSON-RPC interface. - - **Event stream subscriptions** provide plugins with a push-based - notification mechanism about events from the `lightningd`. - - **Hooks** are a primitive that allows plugins to be notified about - internal events in `lightningd` and alter its behavior or inject - custom behaviors. - -A plugin may be written in any language, and communicates with -`lightningd` through the plugin's `stdin` and `stdout`. JSON-RPCv2 is -used as protocol on top of the two streams, with the plugin acting as -server and `lightningd` acting as client. The plugin file needs to be -executable (e.g. use `chmod a+x plugin_name`) - -A `helloworld.py` example plugin based on [pyln-client][pyln-client] -can be found [here][contrib/plugins]. -There is also a [repository](https://github.com/lightningd/plugins) with a collection of -actively maintained plugins and finally, `lightningd`'s own internal -[tests][tests] can be a useful (and most reliable) resource. - -### Warning - -As noted, `lightningd` uses `stdin` as an intake mechanism. This can -cause unexpected behavior if one is not careful. To wit, care should -be taken to ensure that debug/logging statements must be routed to -`stderr` or directly to a file. Activities that are benign in other -contexts (`println!`, `dbg!`, etc) will cause the plugin to be killed -with an error along the lines of: - -`UNUSUAL plugin-cln-plugin-startup: Killing plugin: JSON-RPC message -does not contain "jsonrpc" field` - -## A day in the life of a plugin - -During startup of `lightningd` you can use the `--plugin=` option to -register one or more plugins that should be started. In case you wish -to start several plugins you have to use the `--plugin=` argument -once for each plugin (or `--plugin-dir` or place them in the default -plugin dirs, usually `/usr/local/libexec/c-lightning/plugins` and -`~/.lightning/plugins`). An example call might look like: - -``` -lightningd --plugin=/path/to/plugin1 --plugin=path/to/plugin2 -``` - -`lightningd` will run your plugins from the `--lightning-dir`/networkname -as working directory and env variables "LIGHTNINGD_PLUGIN" and "LIGHTNINGD_VERSION" set, then -will write JSON-RPC requests to the plugin's `stdin` and -will read replies from its `stdout`. To initialize the plugin two RPC -methods are required: - - - `getmanifest` asks the plugin for command line options and JSON-RPC - commands that should be passed through. This can be run before - `lightningd` checks that it is the sole user of the `lightning-dir` - directory (for `--help`) so your plugin should not touch files at this - point. - - `init` is called after the command line options have been - parsed and passes them through with the real values (if specified). This is also - the signal that `lightningd`'s JSON-RPC over Unix Socket is now up - and ready to receive incoming requests from the plugin. - -Once those two methods were called `lightningd` will start passing -through incoming JSON-RPC commands that were registered and the plugin -may interact with `lightningd` using the JSON-RPC over Unix-Socket -interface. - -Above is generally valid for plugins that start when `lightningd` starts. -For dynamic plugins that start via the [plugin][lightning-plugin] JSON-RPC command there -is some difference, mainly in options passthrough (see note in [Types of Options](#types-of-options)). - - - `shutdown` (optional): if subscribed to "shutdown" notification, a plugin can - exit cleanly when `lightningd` is shutting down or when stopped via `plugin stop`. - -### The `getmanifest` method - -The `getmanifest` method is required for all plugins and will be -called on startup with optional parameters (in particular, it may have -`allow-deprecated-apis: false`, but you should accept, and ignore, -other parameters). It MUST return a JSON object similar to this -example: - -```json -{ - "options": [ - { - "name": "greeting", - "type": "string", - "default": "World", - "description": "What name should I call you?", - "deprecated": false - } - ], - "rpcmethods": [ - { - "name": "hello", - "usage": "[name]", - "description": "Returns a personalized greeting for {greeting} (set via options)." - }, - { - "name": "gettime", - "usage": "", - "description": "Returns the current time in {timezone}", - "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines.", - "deprecated": false - } - ], - "subscriptions": [ - "connect", - "disconnect" - ], - "hooks": [ - { "name": "openchannel", "before": ["another_plugin"] }, - { "name": "htlc_accepted" } - ], - "featurebits": { - "node": "D0000000", - "channel": "D0000000", - "init": "0E000000", - "invoice": "00AD0000" - }, - "notifications": [ - { - "method": "mycustomnotification" - } - ], - "nonnumericids": true, - "dynamic": true -} -``` - -During startup the `options` will be added to the list of command line options that -`lightningd` accepts. If any `options` "name" is already taken startup will abort. The above will add a `--greeting` option with a -default value of `World` and the specified description. *Notice that -currently string, integers, bool, and flag options are supported.* - -The `rpcmethods` are methods that will be exposed via `lightningd`'s -JSON-RPC over Unix-Socket interface, just like the builtin -commands. Any parameters given to the JSON-RPC calls will be passed -through verbatim. Notice that the `name`, `description` and `usage` fields -are mandatory, while the `long_description` can be omitted (it'll be -set to `description` if it was not provided). `usage` should surround optional -parameter names in `[]`. - -`options` and `rpcmethods` can mark themselves `deprecated: true` if -you plan on removing them: this will disable them if the user sets -`allow-deprecated-apis` to false (which every developer should do, -right?). - -The `nonnumericids` indicates that the plugin can handle -string JSON request `id` fields: prior to v22.11 lightningd used numbers -for these, and the change to strings broke some plugins. If not set, -then strings will be used once this feature is removed after v23.05. -See [the lightning-rpc documentation][lightning-rpc.7.md] for how to handle -JSON `id` fields! - -The `dynamic` indicates if the plugin can be managed after `lightningd` -has been started using the [plugin][lightning-plugin] JSON-RPC command. Critical plugins that should not be stopped should set it -to false. Plugin `options` can be passed to dynamic plugins as argument to the `plugin` command . - -If a `disable` member exists, the plugin will be disabled and the contents -of this member is the reason why. This allows plugins to disable themselves -if they are not supported in this configuration. - -The `featurebits` object allows the plugin to register featurebits that should be -announced in a number of places in [the protocol][bolt9]. They can be used to signal -support for custom protocol extensions to direct peers, remote nodes and in -invoices. Custom protocol extensions can be implemented for example using the -`sendcustommsg` method and the `custommsg` hook, or the `sendonion` method and -the `htlc_accepted` hook. The keys in the `featurebits` object are `node` for -features that should be announced via the `node_announcement` to all nodes in -the network, `init` for features that should be announced to direct peers -during the connection setup, `channel` for features which should apply to `channel_announcement`, and `invoice` for features that should be -announced to a potential sender of a payment in the invoice. The low range of -featurebits is reserved for standardize features, so please pick random, high -position bits for experiments. If you'd like to standardize your extension -please reach out to the [specification repository][spec] to get a featurebit -assigned. - -The `notifications` array allows plugins to announce which custom -notifications they intend to send to `lightningd`. These custom -notifications can then be subscribed to by other plugins, allowing -them to communicate with each other via the existing publish-subscribe -mechanism and react to events that happen in other plugins, or collect -information based on the notification topics. - -Plugins are free to register any `name` for their `rpcmethod` as long -as the name was not previously registered. This includes both built-in -methods, such as `help` and `getinfo`, as well as methods registered -by other plugins. If there is a conflict then `lightningd` will report -an error and kill the plugin, this aborts startup if the plugin is *important*. - -#### Types of Options - -There are currently four supported option 'types': - - string: a string - - bool: a boolean - - int: parsed as a signed integer (64-bit) - - flag: no-arg flag option. Is boolean under the hood. Defaults to false. - -In addition, string and int types can specify `"multi": true` to indicate -they can be specified multiple times. These will always be represented in -`init` as a (possibly empty) JSON array. - -Nota bene: if a `flag` type option is not set, it will not appear -in the options set that is passed to the plugin. - -Here's an example option set, as sent in response to `getmanifest` - -```json - "options": [ - { - "name": "greeting", - "type": "string", - "default": "World", - "description": "What name should I call you?" - }, - { - "name": "run-hot", - "type": "flag", - "default": None, // defaults to false - "description": "If set, overclocks plugin" - }, - { - "name": "is_online", - "type": "bool", - "default": false, - "description": "Set to true if plugin can use network" - }, - { - "name": "service-port", - "type": "int", - "default": 6666, - "description": "Port to use to connect to 3rd-party service" - }, - { - "name": "number", - "type": "int", - "default": 0, - "description": "Another number to add", - "multi": true - } - ], -``` - -**Note**: `lightningd` command line options are only parsed during startup and their -values are not remembered when the plugin is stopped or killed. -For dynamic plugins started with `plugin start`, options can be -passed as extra arguments to that [command][lightning-plugin]. - - -#### Custom notifications - -The plugins may emit custom notifications for topics they have -announced during startup. The list of notification topics declared -during startup must include all topics that may be emitted, in order -to verify that all topics plugins subscribe to are also emitted by -some other plugin, and warn if a plugin subscribes to a non-existent -topic. In case a plugin emits notifications it has not announced the -notification will be ignored and not forwarded to subscribers. - -When forwarding a custom notification `lightningd` will wrap the -payload of the notification in an object that contains metadata about -the notification. The following is an example of this -transformation. The first listing is the original notification emitted -by the `sender` plugin, while the second is the the notification as -received by the `receiver` plugin (both listings show the full -[JSON-RPC][jsonrpc-spec] notification to illustrate the wrapping). - -```json -{ - "jsonrpc": "2.0", - "method": "mycustomnotification", - "params": { - "key": "value", - "message": "Hello fellow plugin!" - } -} -``` - -is delivered as - -```json -{ - "jsonrpc": "2.0", - "method": "mycustomnotification", - "params": { - "origin": "sender", - "payload": { - "key": "value", - "message": "Hello fellow plugin!" - } - } -} - -``` - -The notification topic (`method` in the JSON-RPC message) must not -match one of the internal events in order to prevent breaking -subscribers that expect the existing notification format. Multiple -plugins are allowed to emit notifications for the same topics, -allowing things like metric aggregators where the aggregator -subscribes to a common topic and other plugins publish metrics as -notifications. - -### The `init` method - -The `init` method is required so that `lightningd` can pass back the -filled command line options and notify the plugin that `lightningd` is -now ready to receive JSON-RPC commands. The `params` of the call are a -simple JSON object containing the options: - -```json -{ - "options": { - "greeting": "World", - "number": [0] - }, - "configuration": { - "lightning-dir": "/home/user/.lightning/testnet", - "rpc-file": "lightning-rpc", - "startup": true, - "network": "testnet", - "feature_set": { - "init": "02aaa2", - "node": "8000000002aaa2", - "channel": "", - "invoice": "028200" - }, - "proxy": { - "type": "ipv4", - "address": "127.0.0.1", - "port": 9050 - }, - "torv3-enabled": true, - "always_use_proxy": false - } -} -``` - -The plugin must respond to `init` calls. The response should be a -valid JSON-RPC response to the `init`, but this is not currently -enforced. If the response is an object containing `result` which -contains `disable` then the plugin will be disabled and the contents -of this member is the reason why. - -The `startup` field allows a plugin to detect if it was started at -`lightningd` startup (true), or at runtime (false). - -### Timeouts -During startup ("startup" is true), the plugin has 60 seconds to -return `getmanifest` and another 60 seconds to return `init`, or gets killed. -When started dynamically via the [plugin][lightning-plugin] JSON-RPC command, both `getmanifest` -and `init` should be completed within 60 seconds. - -## JSON-RPC passthrough - -Plugins may register their own JSON-RPC methods that are exposed -through the JSON-RPC provided by `lightningd`. This provides users -with a single interface to interact with, while allowing the addition -of custom methods without having to modify the daemon itself. - -JSON-RPC methods are registered as part of the `getmanifest` -result. Each registered method must provide a `name` and a -`description`. An optional `long_description` may also be -provided. This information is then added to the internal dispatch -table, and used to return the help text when using `lightning-cli -help`, and the methods can be called using the `name`. - -For example the above `getmanifest` result will register two methods, -called `hello` and `gettime`: - -```json - ... - "rpcmethods": [ - { - "name": "hello", - "usage": "[name]", - "description": "Returns a personalized greeting for {greeting} (set via options)." - }, - { - "name": "gettime", - "description": "Returns the current time in {timezone}", - "usage": "", - "long_description": "Returns the current time in the timezone that is given as the only parameter.\nThis description may be quite long and is allowed to span multiple lines." - } - ], - ... -``` - -The RPC call will be passed through unmodified, with the exception of -the JSON-RPC call `id`, which is internally remapped to a unique -integer instead, in order to avoid collisions. When passing the result -back the `id` field is restored to its original value. - -Note that if your `result` for an RPC call includes `"format-hint": -"simple"`, then `lightning-cli` will default to printing your output -in "human-readable" flat form. - -## Event notifications - -Event notifications allow a plugin to subscribe to events in -`lightningd`. `lightningd` will then send a push notification if an -event matching the subscription occurred. A notification is defined in -the JSON-RPC [specification][jsonrpc-spec] as an RPC call that does -not include an `id` parameter: - -> A Notification is a Request object without an "id" member. A Request -> object that is a Notification signifies the Client's lack of -> interest in the corresponding Response object, and as such no -> Response object needs to be returned to the client. The Server MUST -> NOT reply to a Notification, including those that are within a batch -> request. -> -> Notifications are not confirmable by definition, since they do not -> have a Response object to be returned. As such, the Client would not -> be aware of any errors (like e.g. "Invalid params","Internal -> error"). - -Plugins subscribe by returning an array of subscriptions as part of -the `getmanifest` response. The result for the `getmanifest` call -above for example subscribes to the two topics `connect` and -`disconnect`. The topics that are currently defined and the -corresponding payloads are listed below. - - -### `channel_opened` - -A notification for topic `channel_opened` is sent if a peer successfully -funded a channel with us. It contains the peer id, the funding amount -(in millisatoshis), the funding transaction id, and a boolean indicating -if the funding transaction has been included into a block. - -```json -{ - "channel_opened": { - "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "funding_msat": 100000000, - "funding_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", - "channel_ready": false - } -} -``` - -### `channel_open_failed` - -A notification to indicate that a channel open attempt has been unsuccessful. -Useful for cleaning up state for a v2 channel open attempt. See -`plugins/funder.c` for an example of how to use this. - -```json -{ - "channel_open_failed": { - "channel_id": "a2d0851832f0e30a0cf...", - } -} -``` - -### `channel_state_changed` - -A notification for topic `channel_state_changed` is sent every time a channel -changes its state. The notification includes the `peer_id` and `channel_id`, the -old and new channel states, the type of `cause` and a `message`. - -```json -{ - "channel_state_changed": { - "peer_id": "03bc9337c7a28bb784d67742ebedd30a93bacdf7e4ca16436ef3798000242b2251", - "channel_id": "a2d0851832f0e30a0cf778a826d72f077ca86b69f72677e0267f23f63a0599b4", - "short_channel_id" : "561820x1020x1", - "timestamp":"2023-01-05T18:27:12.145Z", - "old_state": "CHANNELD_NORMAL", - "new_state": "CHANNELD_SHUTTING_DOWN", - "cause" : "remote", - "message" : "Peer closes channel" - } -} -``` - -A `cause` can have the following values: - - "unknown" Anything other than the reasons below. Should not happen. - - "local" Unconscious internal reasons, e.g. dev fail of a channel. - - "user" The operator or a plugin opened or closed a channel by intention. - - "remote" The remote closed or funded a channel with us by intention. - - "protocol" We need to close a channel because of bad signatures and such. - - "onchain" A channel was closed onchain, while we were offline. - -Most state changes are caused subsequentially for a prior state change, e.g. -"CLOSINGD_COMPLETE" is followed by "FUNDING_SPEND_SEEN". Because of this, the -`cause` reflects the last known reason in terms of local or remote user -interaction, protocol reasons, etc. More specifically, a `new_state` -"FUNDING_SPEND_SEEN" will likely _not_ have "onchain" as a `cause` but some -value such as "REMOTE" or "LOCAL" depending on who initiated the closing of a -channel. - -Note: If the channel is not closed or being closed yet, the `cause` will reflect -which side "remote" or "local" opened the channel. - -Note: If the cause is "onchain" this was very likely a conscious decision of the -remote peer, but we have been offline. - -### `connect` - -A notification for topic `connect` is sent every time a new connection -to a peer is established. `direction` is either `"in"` or `"out"`. - -```json -{ - "id": "02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432", - "direction": "in", - "address": "1.2.3.4:1234" -} -``` - -### `disconnect` - -A notification for topic `disconnect` is sent every time a connection -to a peer was lost. - -```json -{ - "id": "02f6725f9c1c40333b67faea92fd211c183050f28df32cac3f9d69685fe9665432" -} -``` - -### `invoice_payment` - -A notification for topic `invoice_payment` is sent every time an invoice is paid. - -```json -{ - "invoice_payment": { - "label": "unique-label-for-invoice", - "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "amount_msat": 10000 - } -} - -``` -### `invoice_creation` - -A notification for topic `invoice_creation` is sent every time an invoice is created. - -```json -{ - "invoice_creation": { - "label": "unique-label-for-invoice", - "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "amount_msat": 10000 - } -} -``` - -### `warning` - -A notification for topic `warning` is sent every time a new `BROKEN` -/`UNUSUAL` level(in plugins, we use `error`/`warn`) log generated, -which means an unusual/borken thing happens, such as channel failed, -message resolving failed... - -```json -{ - "warning": { - "level": "warn", - "time": "1559743608.565342521", - "source": "lightningd(17652): 0821f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c chan #7854:", - "log": "Peer permanent failure in CHANNELD_NORMAL: lightning_channeld: sent ERROR bad reestablish dataloss msg" - } -} -``` -1. `level` is `warn` or `error`: `warn` means something seems bad happened - and it's under control, but we'd better check it; `error` means something -extremely bad is out of control, and it may lead to crash; -2. `time` is the second since epoch; -3. `source` means where the event happened, it may have the following -forms: -` chan #:`,`lightningd():`, -`plugin-:`, `():`, `jsonrpc:`, -`jcon fd :`, `plugin-manager`; -4. `log` is the context of the original log entry. - -### `forward_event` - -A notification for topic `forward_event` is sent every time the status -of a forward payment is set. The json format is same as the API -`listforwards`. - -```json -{ - "forward_event": { - "payment_hash": "f5a6a059a25d1e329d9b094aeeec8c2191ca037d3f5b0662e21ae850debe8ea2", - "in_channel": "103x2x1", - "out_channel": "103x1x1", - "in_msat": 100001001, - "out_msat": 100000000, - "fee_msat": 1001, - "status": "settled", - "received_time": 1560696342.368, - "resolved_time": 1560696342.556 - } -} -``` -or - -```json -{ - "forward_event": { - "payment_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "in_channel": "103x2x1", - "out_channel": "110x1x0", - "in_msat": 100001001, - "out_msat": 100000000, - "fee_msat": 1001, - "status": "local_failed", - "failcode": 16392, - "failreason": "WIRE_PERMANENT_CHANNEL_FAILURE", - "received_time": 1560696343.052 - } -} - -``` - - The status includes `offered`, `settled`, `failed` and `local_failed`, - and they are all string type in json. - - When the forward payment is valid for us, we'll set `offered` - and send the forward payment to next hop to resolve; - - When the payment forwarded by us gets paid eventually, the forward - payment will change the status from `offered` to `settled`; - - If payment fails locally(like failing to resolve locally) or the - corresponding htlc with next hop fails(like htlc timeout), we will - set the status as `local_failed`. `local_failed` may be set before - setting `offered` or after setting `offered`. In fact, from the - time we receive the htlc of the previous hop, all we can know the - cause of the failure is treated as `local_failed`. `local_failed` - only occuors locally or happens in the htlc between us and next hop; - - If `local_failed` is set before `offered`, this - means we just received htlc from the previous hop and haven't - generate htlc for next hop. In this case, the json of `forward_event` - sets the fields of `out_msatoshi`, `out_msat`,`fee` and `out_channel` - as 0; - - Note: In fact, for this case we may be not sure if this incoming - htlc represents a pay to us or a payment we need to forward. - We just simply treat all incoming failed to resolve as - `local_failed`. - - Only in `local_failed` case, json includes `failcode` and - `failreason` fields; - - `failed` means the payment forwarded by us fails in the - latter hops, and the failure isn't related to us, so we aren't - accessed to the fail reason. `failed` must be set after - `offered`. - - `failed` case doesn't include `failcode` and `failreason` - fields; - - `received_time` means when we received the htlc of this payment from - the previous peer. It will be contained into all status case; - - `resolved_time` means when the htlc of this payment between us and the - next peer was resolved. The resolved result may success or fail, so - only `settled` and `failed` case contain `resolved_time`; - - The `failcode` and `failreason` are defined in [BOLT 4][bolt4-failure-codes]. - -### `sendpay_success` - -A notification for topic `sendpay_success` is sent every time a sendpay -succeeds (with `complete` status). The json is the same as the return value of -the commands `sendpay`/`waitsendpay` when these commands succeed. - -```json -{ - "sendpay_success": { - "id": 1, - "payment_hash": "5c85bf402b87d4860f4a728e2e58a2418bda92cd7aea0ce494f11670cfbfb206", - "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "amount_msat": 100000000, - "amount_sent_msat": 100001001, - "created_at": 1561390572, - "status": "complete", - "payment_preimage": "9540d98095fd7f37687ebb7759e733934234d4f934e34433d4998a37de3733ee" - } -} -``` -`sendpay` doesn't wait for the result of sendpay and `waitsendpay` -returns the result of sendpay in specified time or timeout, but -`sendpay_success` will always return the result anytime when sendpay -successes if is was subscribed. - -### `sendpay_failure` - -A notification for topic `sendpay_failure` is sent every time a sendpay -completes with `failed` status. The JSON is same as the return value of -the commands `sendpay`/`waitsendpay` when these commands fail. - -```json -{ - "sendpay_failure": { - "code": 204, - "message": "failed: WIRE_UNKNOWN_NEXT_PEER (reply from remote)", - "data": { - "id": 2, - "payment_hash": "9036e3bdbd2515f1e653cb9f22f8e4c49b73aa2c36e937c926f43e33b8db8851", - "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "amount_msat": 100000000, - "amount_sent_msat": 100001001, - "created_at": 1561395134, - "status": "failed", - "erring_index": 1, - "failcode": 16394, - "failcodename": "WIRE_UNKNOWN_NEXT_PEER", - "erring_node": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", - "erring_channel": "103x2x1", - "erring_direction": 0 - } - } -} -``` -`sendpay` doesn't wait for the result of sendpay and `waitsendpay` -returns the result of sendpay in specified time or timeout, but -`sendpay_failure` will always return the result anytime when sendpay -fails if is was subscribed. - - -### `coin_movement` - -A notification for topic `coin_movement` is sent to record the -movement of coins. It is only triggered by finalized ledger updates, -i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. - -```json -{ - "coin_movement": { - "version":2, - "node_id":"03a7103a2322b811f7369cbb27fb213d30bbc0b012082fed3cad7e4498da2dc56b", - "type":"chain_mvt", - "account_id":"wallet", - "originating_account": "wallet", // (`chain_mvt` only, optional) - "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only, optional) - "utxo_txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only) - "vout":1, // (`chain_mvt` only) - "payment_hash": "xxx", // (either type, optional on both) - "part_id": 0, // (`channel_mvt` only, optional) - "credit_msat":2000000000, - "debit_msat":0, - "output_msat": 2000000000, // ('chain_mvt' only) - "output_count": 2, // ('chain_mvt' only, typically only channel closes) - "fees_msat": 382, // ('channel_mvt' only) - "tags": ["deposit"], - "blockheight":102, // 'chain_mvt' only - "timestamp":1585948198, - "coin_type":"bc" - } -} -``` - -`version` indicates which version of the coin movement data struct this -notification adheres to. - -`node_id` specifies the node issuing the coin movement. - -`type` marks the underlying mechanism which moved these coins. There are two -'types' of `coin_movements`: - - `channel_mvt`s, which occur as a result of htlcs being resolved and, - - `chain_mvt`s, which occur as a result of bitcoin txs being mined. - -`account_id` is the name of this account. The node's wallet is named 'wallet', -all channel funds' account are the channel id. - -`originating_account` is the account that this movement originated from. -*Only* tagged on external events (deposits/withdrawals to an external party). - -`txid` is the transaction id of the bitcoin transaction that triggered this -ledger event. `utxo_txid` and `vout` identify the bitcoin output which triggered -this notification. (`chain_mvt` only). Notifications tagged -`journal_entry` do not have a `utxo_txid` as they're not -represented in the utxo set. - -`payment_hash` is the hash of the preimage used to move this payment. Only -present for HTLC mediated moves (both `chain_mvt` and `channel_mvt`) -A `chain_mvt` will have a `payment_hash` iff it's recording an htlc that was -fulfilled onchain. - -`part_id` is an identifier for parts of a multi-part payment. useful for -aggregating payments for an invoice or to indicate why a payment hash appears -multiple times. `channel_mvt` only - -`credit` and `debit` are millisatoshi denominated amounts of the fund movement. A -'credit' is funds deposited into an account; a `debit` is funds withdrawn. - -`output_value` is the total value of the on-chain UTXO. Note that for -channel opens/closes the total output value will not necessarily correspond -to the amount that's credited/debited. - -`output_count` is the total outputs to expect for a channel close. Useful -for figuring out when every onchain output for a close has been resolved. - -`fees` is an HTLC annotation for the amount of fees either paid or -earned. For "invoice" tagged events, the fees are the total fees -paid to send that payment. The end amount can be found by subtracting -the total fees from the `debited` amount. For "routed" tagged events, -both the debit/credit contain fees. Technically routed debits are the -'fee generating' event, however we include them on routed credits as well. - -`tag` is a movement descriptor. Current tags are as follows: - - `deposit`: funds deposited - - `withdrawal`: funds withdrawn - - `penalty`: funds paid or gained from a penalty tx. - - `invoice`: funds paid to or recieved from an invoice. - - `routed`: funds routed through this node. - - `pushed`: funds pushed to peer. - - `channel_open` : channel is opened, initial channel balance - - `channel_close`: channel is closed, final channel balance - - `delayed_to_us`: on-chain output to us, spent back into our wallet - - `htlc_timeout`: on-chain htlc timeout output - - `htlc_fulfill`: on-chian htlc fulfill output - - `htlc_tx`: on-chain htlc tx has happened - - `to_wallet`: output being spent into our wallet - - `ignored`: output is being ignored - - `anchor`: an anchor output - - `to_them`: output intended to peer's wallet - - `penalized`: output we've 'lost' due to a penalty (failed cheat attempt) - - `stolen`: output we've 'lost' due to peer's cheat - - `to_miner`: output we've burned to miner (OP_RETURN) - - `opener`: tags channel_open, we are the channel opener - - `lease_fee`: amount paid as lease fee - - `leased`: tags channel_open, channel contains leased funds - -`blockheight` is the block the txid is included in. `channel_mvt`s will be null, -so will the blockheight for withdrawals to external parties (we issue these events -when we send the tx containing them, before they're included in the chain). - -The `timestamp` is seconds since Unix epoch of the node's machine time -at the time lightningd broadcasts the notification. - -`coin_type` is the BIP173 name for the coin which moved. - -### `balance_snapshot` - -Emitted after we've caught up to the chain head on first start. Lists all -current accounts (`account_id` matches the `account_id` emitted from -`coin_movement`). Useful for checkpointing account balances. - -```json -{ - "balance_snapshots": [ - { - 'node_id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', - 'blockheight': 101, - 'timestamp': 1639076327, - 'accounts': [ - { - 'account_id': 'wallet', - 'balance': '0msat', - 'coin_type': 'bcrt' - } - ] - }, - { - 'node_id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', - 'blockheight': 110, - 'timestamp': 1639076343, - 'accounts': [ - { - 'account_id': 'wallet', - 'balance': '995433000msat', - 'coin_type': 'bcrt' - }, { - 'account_id': '5b65c199ee862f49758603a5a29081912c8816a7c0243d1667489d244d3d055f', - 'balance': '500000000msat', - 'coin_type': 'bcrt' - } - ] - } - ] -} -``` - -### `block_added` - -Emitted after each block is received from bitcoind, either during the initial sync or -throughout the node's life as new blocks appear. - -```json -{ - "block": { - "hash": "000000000000000000034bdb3c01652a0aa8f63d32f949313d55af2509f9d245", - "height": 753304 - } -} -``` - -### `openchannel_peer_sigs` - -When opening a channel with a peer using the collaborative transaction protocol -(`opt_dual_fund`), this notification is fired when the peer sends us their funding -transaction signatures, `tx_signatures`. We update the in-progress PSBT and return it -here, with the peer's signatures attached. - -```json -{ - "openchannel_peer_sigs": { - "channel_id": "252d1b0a1e5789...", - "signed_psbt": "cHNidP8BAKgCAAAAAQ+y+61AQAAAAD9////AzbkHAAAAAAAFgAUwsyrFxwqW+natS7EG4JYYwJMVGZQwwAAAAAAACIAIKYE2s4YZ+RON6BB5lYQESHR9cA7hDm6/maYtTzSLA0hUMMAAAAAAAAiACBbjNO5FM9nzdj6YnPJMDU902R2c0+9liECwt9TuQiAzWYAAAAAAQDfAgAAAAABARtaSZufCbC+P+/G23XVaQ8mDwZQFW1vlCsCYhLbmVrpAAAAAAD+////AvJs5ykBAAAAFgAUT6ORgb3CgFsbwSOzNLzF7jQS5s+AhB4AAAAAABepFNi369DMyAJmqX2agouvGHcDKsZkhwJHMEQCIHELIyqrqlwRjyzquEPvqiorzL2hrvdu9EBxsqppeIKiAiBykC6De/PDElnqWw49y2vTqauSJIVBgGtSc+vq5BQd+gEhAg0f8WITWvA8o4grxNKfgdrNDncqreMLeRFiteUlne+GZQAAAAEBIICEHgAAAAAAF6kU2Lfr0MzIAmapfZqCi68YdwMqxmSHAQcXFgAUAfrZCrzWZpfiWSFkci3kqV6+4WUBCGsCRzBEAiBF31wbNWECsJ0DrPel2inWla2hYpCgaxeVgPAvFEOT2AIgWiFWN0hvUaK6kEnXhED50wQ2fBqnobsRhoy1iDDKXE0BIQPXRURck2JmXyLg2W6edm8nPzJg3qOcina/oF3SaE3czwz8CWxpZ2h0bmluZwEIexhVcpJl8ugM/AlsaWdodG5pbmcCAgABAAz8CWxpZ2h0bmluZwEIR7FutlQgkSoADPwJbGlnaHRuaW5nAQhYT+HjxFBqeAAM/AlsaWdodG5pbmcBCOpQ5iiTTNQEAA==" - } -} -``` - -### `shutdown` - -Send in two situations: lightningd is (almost completely) shutdown, or the plugin -`stop` command has been called for this plugin. In both cases the plugin has 30 -seconds to exit itself, otherwise it's killed. - -In the shutdown case, plugins should not interact with lightnind except via (id-less) -logging or notifications. New rpc calls will fail with error code -5 and (plugin's) -responses will be ignored. Because lightningd can crash or be killed, a plugin cannot -rely on the shutdown notification always been send. - - -## Hooks - -Hooks allow a plugin to define custom behavior for `lightningd` -without having to modify the Core Lightning source code itself. A plugin -declares that it'd like to be consulted on what to do next for certain -events in the daemon. A hook can then decide how `lightningd` should -react to the given event. - -When hooks are registered, they can optionally specify "before" and -"after" arrays of plugin names, which control what order they will be -called in. If a plugin name is unknown, it is ignored, otherwise if the -hook calls cannot be ordered to satisfy the specifications of all -plugin hooks, the plugin registration will fail. - -The call semantics of the hooks, i.e., when and how hooks are called, depend -on the hook type. Most hooks are currently set to `single`-mode. In this mode -only a single plugin can register the hook, and that plugin will get called -for each event of that type. If a second plugin attempts to register the hook -it gets killed and a corresponding log entry will be added to the logs. - -In `chain`-mode multiple plugins can register for the hook type and -they are called in any order they are loaded (i.e. cmdline order -first, configuration order file second: though note that the order of -plugin directories is implementation-dependent), overriden only by -`before` and `after` requirements the plugin's hook registrations specify. -Each plugin can then handle the event or defer by returning a -`continue` result like the following: - -```json -{ - "result": "continue" -} -``` - -The remainder of the response is ignored and if there are any more plugins -that have registered the hook the next one gets called. If there are no more -plugins then the internal handling is resumed as if no hook had been -called. Any other result returned by a plugin is considered an exit from the -chain. Upon exit no more plugin hooks are called for the current event, and -the result is executed. Unless otherwise stated all hooks are `single`-mode. - -Hooks and notifications are very similar, however there are a few -key differences: - - - Notifications are asynchronous, i.e., `lightningd` will send the - notifications but not wait for the plugin to process them. Hooks on - the other hand are synchronous, `lightningd` cannot finish - processing the event until the plugin has returned. - - Any number of plugins can subscribe to a notification topic and get - notified in parallel, however only one plugin may register for - `single`-mode hook types, and in all cases only one plugin may return a - non-`continue` response. This avoids having multiple contradictory - responses. - -Hooks are considered to be an advanced feature due to the fact that -`lightningd` relies on the plugin to tell it what to do next. Use them -carefully, and make sure your plugins always return a valid response -to any hook invocation. - -As a convention, for all hooks, returning the object -`{ "result" : "continue" }` results in `lightningd` behaving exactly as if -no plugin is registered on the hook. - -### `peer_connected` - -This hook is called whenever a peer has connected and successfully completed -the cryptographic handshake. The parameters have the following structure: - -```json -{ - "peer": { - "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "direction": "in", - "addr": "34.239.230.56:9735", - "features": "" - } -} -``` - -The hook is sparse on information, since the plugin can use the JSON-RPC -`listpeers` command to get additional details should they be required. -`direction` is either `"in"` or `"out"`. The `addr` field shows the address -that we are connected to ourselves, not the gossiped list of known -addresses. In particular this means that the port for incoming connections is -an ephemeral port, that may not be available for reconnections. - -The returned result must contain a `result` member which is either -the string `disconnect` or `continue`. If `disconnect` and -there's a member `error_message`, that member is sent to the peer -before disconnection. - -Note that `peer_connected` is a chained hook. The first plugin that decides to -`disconnect` with or without an `error_message` will lead to the subsequent -plugins not being called anymore. - -### `commitment_revocation` - -This hook is called whenever a channel state is updated, and the old state was -revoked. State updates in Lightning consist of the following steps: - - 1. Proposal of a new state commitment in the form of a commitment transaction - 2. Exchange of signatures for the agreed upon commitment transaction - 3. Verification that the signatures match the commitment transaction - 4. Exchange of revocation secrets that could be used to penalize an eventual misbehaving party - -The `commitment_revocation` hook is used to inform the plugin about the state -transition being completed, and deliver the penalty transaction. The penalty -transaction could then be sent to a watchtower that automaticaly reacts in -case one party attempts to settle using a revoked commitment. - -The payload consists of the following information: - -```json -{ - "commitment_txid": "58eea2cf538cfed79f4d6b809b920b40bb6b35962c4bb4cc81f5550a7728ab05", - "penalty_tx": "02000000000101...ac00000000", - "channel_id": "fb16398de93e8690c665873715ef590c038dfac5dd6c49a9d4b61dccfcedc2fb", - "commitnum": 21 -} -``` - -Notice that the `commitment_txid` could also be extracted from the sole input -of the `penalty_tx`, however it is enclosed so plugins don't have to include -the logic to parse transactions. - -Not included are the `htlc_success` and `htlc_failure` transactions that -may also be spending `commitment_tx` outputs. This is because these -transactions are much more dynamic and have a predictable timeout, allowing -wallets to ensure a quick checkin when the CLTV of the HTLC is about to -expire. - -The `commitment_revocation` hook is a chained hook, i.e., multiple plugins can -register it, and they will be called in the order they were registered in. -Plugins should always return `{"result": "continue"}`, otherwise subsequent -hook subscribers would not get called. - -### `db_write` - -This hook is called whenever a change is about to be committed to the database, -if you are using a SQLITE3 database (the default). -This hook will be useless (the `"writes"` field will always be empty) if you are -using a PostgreSQL database. - -It is currently extremely restricted: - -1. a plugin registering for this hook should not perform anything that may cause - a db operation in response (pretty much, anything but logging). -2. a plugin registering for this hook should not register for other hooks or - commands, as these may become intermingled and break rule #1. -3. the hook will be called before your plugin is initialized! - -This hook, unlike all the other hooks, is also strongly synchronous: -`lightningd` will stop almost all the other processing until this -hook responds. - -```json -{ - "data_version": 42, - "writes": [ - "PRAGMA foreign_keys = ON" - ] -} -``` - -This hook is intended for creating continuous backups. -The intent is that your backup plugin maintains three -pieces of information (possibly in separate files): -(1) a snapshot of the database, (2) a log of database queries -that will bring that snapshot up-to-date, and (3) the previous -`data_version`. - -`data_version` is an unsigned 32-bit number that will always -increment by 1 each time `db_write` is called. -Note that this will wrap around on the limit of 32-bit numbers. - -`writes` is an array of strings, each string being a database query -that modifies the database. -If the `data_version` above is validated correctly, then you can -simply append this to the log of database queries. - -Your plugin **MUST** validate the `data_version`. -It **MUST** keep track of the previous `data_version` it got, -and: - -1. If the new `data_version` is ***exactly*** one higher than - the previous, then this is the ideal case and nothing bad - happened and we should save this and continue. -2. If the new `data_version` is ***exactly*** the same value - as the previous, then the previous set of queries was not - committed. - Your plugin **MAY** overwrite the previous set of queries with - the current set, or it **MAY** overwrite its entire backup - with a new snapshot of the database and the current `writes` - array (treating this case as if `data_version` were two or - more higher than the previous). -3. If the new `data_version` is ***less than*** the previous, - your plugin **MUST** halt and catch fire, and have the - operator inspect what exactly happend here. -4. Otherwise, some queries were lost and your plugin **SHOULD** - recover by creating a new snapshot of the database: copy the - database file, back up the given `writes` array, then delete - (or atomically `rename` if in a POSIX filesystem) the previous - backups of the database and SQL statements, or you **MAY** - fail the hook to abort `lightningd`. - -The "rolling up" of the database could be done periodically as well -if the log of SQL statements has grown large. - -Any response other than `{"result": "continue"}` will cause lightningd -to error without -committing to the database! -This is the expected way to halt and catch fire. - -`db_write` is a parallel-chained hook, i.e., multiple plugins can -register it, and all of them will be invoked simultaneously without -regard for order of registration. -The hook is considered handled if all registered plugins return -`{"result": "continue"}`. -If any plugin returns anything else, `lightningd` will error without -committing to the database. - -### `invoice_payment` - -This hook is called whenever a valid payment for an unpaid invoice has arrived. - -```json -{ - "payment": { - "label": "unique-label-for-invoice", - "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "amount_msat": 10000 - } -} -``` - -The hook is deliberately sparse, since the plugin can use the JSON-RPC -`listinvoices` command to get additional details about this invoice. -It can return a `failure_message` field as defined for final -nodes in [BOLT 4][bolt4-failure-messages], a `result` field with the string -`reject` to fail it with `incorrect_or_unknown_payment_details`, or a -`result` field with the string `continue` to accept the payment. - - -### `openchannel` - -This hook is called whenever a remote peer tries to fund a channel to us using -the v1 protocol, and it has passed basic sanity checks: - -```json -{ - "openchannel": { - "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "funding_msat": 100000000, - "push_msat": 0, - "dust_limit_msat": 546000, - "max_htlc_value_in_flight_msat": 18446744073709551615, - "channel_reserve_msat": 1000000, - "htlc_minimum_msat": 0, - "feerate_per_kw": 7500, - "to_self_delay": 5, - "max_accepted_htlcs": 483, - "channel_flags": 1 - } -} -``` - -There may be additional fields, including `shutdown_scriptpubkey` and -a hex-string. You can see the definitions of these fields in [BOLT 2's description of the open_channel message][bolt2-open-channel]. - -The returned result must contain a `result` member which is either -the string `reject` or `continue`. If `reject` and -there's a member `error_message`, that member is sent to the peer -before disconnection. - -For a 'continue'd result, you can also include a `close_to` address, -which will be used as the output address for a mutual close transaction. - -e.g. - -```json -{ - "result": "continue", - "close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2" - "mindepth": 0, - "reserve": "1234sat" -} -``` - -Note that `close_to` must be a valid address for the current chain, -an invalid address will cause the node to exit with an error. - - - `mindepth` is the number of confirmations to require before making - the channel usable. Notice that setting this to 0 (`zeroconf`) or - some other low value might expose you to double-spending issues, so - only lower this value from the default if you trust the peer not to - double-spend, or you reject incoming payments, including forwards, - until the funding is confirmed. - - - `reserve` is an absolute value for the amount in the channel that - the peer must keep on their side. This ensures that they always - have something to lose, so only lower this below the 1% of funding - amount if you trust the peer. The protocol requires this to be - larger than the dust limit, hence it will be adjusted to be the - dust limit if the specified value is below. - -Note that `openchannel` is a chained hook. Therefore `close_to`, `reserve` will only be -evaluated for the first plugin that sets it. If more than one plugin tries to -set a `close_to` address an error will be logged. - -### `openchannel2` - -This hook is called whenever a remote peer tries to fund a channel to us using -the v2 protocol, and it has passed basic sanity checks: - -```json -{ - "openchannel2": { - "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_funding_msat": 100000000, - "dust_limit_msat": 546000, - "max_htlc_value_in_flight_msat": 18446744073709551615, - "htlc_minimum_msat": 0, - "funding_feerate_per_kw": 7500, - "commitment_feerate_per_kw": 7500, - "feerate_our_max": 10000, - "feerate_our_min": 253, - "to_self_delay": 5, - "max_accepted_htlcs": 483, - "channel_flags": 1 - "locktime": 2453, - "channel_max_msat": 16777215000, - "requested_lease_msat": 100000000, - "lease_blockheight_start": 683990, - "node_blockheight": 683990, - "require_confirmed_inputs": false - } -} -``` - -There may be additional fields, such as `shutdown_scriptpubkey`. You can -see the definitions of these fields in [BOLT 2's description of the open_channel message][bolt2-open-channel]. - -`requested_lease_msat`, `lease_blockheight_start`, and `node_blockheight` are -only present if the opening peer has requested a funding lease, -per `option_will_fund`. - -The returned result must contain a `result` member which is either -the string `reject` or `continue`. If `reject` and -there's a member `error_message`, that member is sent to the peer -before disconnection. - -For a 'continue'd result, you can also include a `close_to` address, -which will be used as the output address for a mutual close transaction; you -can include a `psbt` and an `our_funding_msat` to contribute funds, -inputs and outputs to this channel open. - -Note that, like `openchannel_init` RPC call, the `our_funding_msat` amount -must NOT be accounted for in any supplied output. Change, however, should be -included and should use the `funding_feerate_per_kw` to calculate. - -See `plugins/funder.c` for an example of how to use this hook -to contribute funds to a channel open. - -e.g. - -```json -{ - "result": "continue", - "close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2" - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZrrw28Oed52hTw3N7t0HbIyZhFdcZRH3+61AQAAAAD9////AGYAAAAAAQDfAgAAAAABARtaSZufCbC+P+/G23XVaQ8mDwZQFW1vlCsCYhLbmVrpAAAAAAD+////AvJs5ykBAAAAFgAUT6ORgb3CgFsbwSOzNLzF7jQS5s+AhB4AAAAAABepFNi369DMyAJmqX2agouvGHcDKsZkhwJHMEQCIHELIyqrqlwRjyzquEPvqiorzL2hrvdu9EBxsqppeIKiAiBykC6De/PDElnqWw49y2vTqauSJIVBgGtSc+vq5BQd+gEhAg0f8WITWvA8o4grxNKfgdrNDncqreMLeRFiteUlne+GZQAAAAEBIICEHgAAAAAAF6kU2Lfr0MzIAmapfZqCi68YdwMqxmSHAQQWABQB+tkKvNZml+JZIWRyLeSpXr7hZQz8CWxpZ2h0bmluZwEIexhVcpJl8ugM/AlsaWdodG5pbmcCAgABAA==", - "our_funding_msat": 39999000 -} -``` - -Note that `close_to` must be a valid address for the current chain, -an invalid address will cause the node to exit with an error. - -Note that `openchannel` is a chained hook. Therefore `close_to` will only be -evaluated for the first plugin that sets it. If more than one plugin tries to -set a `close_to` address an error will be logged. - - -### `openchannel2_changed` - -This hook is called when we received updates to the funding transaction -from the peer. - -```json -{ - "openchannel2_changed": { - "channel_id": "252d1b0a1e57895e841...", - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZr..." - } -} -``` - -In return, we expect a `result` indicated to `continue` and an updated `psbt`. -If we have no updates to contribute, return the passed in PSBT. Once no -changes to the PSBT are made on either side, the transaction construction -negotation will end and commitment transactions will be exchanged. - -#### Expected Return -```json -{ - "result": "continue", - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZr..." -} -``` - -See `plugins/funder.c` for an example of how to use this hook -to continue a v2 channel open. - - -### `openchannel2_sign` - -This hook is called after we've gotten the commitment transactions for a -channel open. It expects psbt to be returned which contains signatures -for our inputs to the funding transaction. - -```json -{ - "openchannel2_sign": { - "channel_id": "252d1b0a1e57895e841...", - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZr..." - } -} -``` - -In return, we expect a `result` indicated to `continue` and an partially -signed `psbt`. - -If we have no inputs to sign, return the passed in PSBT. Once we have also -received the signatures from the peer, the funding transaction will be -broadcast. - -#### Expected Return -```json -{ - "result": "continue", - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZr..." -} -``` - -See `plugins/funder.c` for an example of how to use this hook -to sign a funding transaction. - - -### `rbf_channel` - -Similar to `openchannel2`, the `rbf_channel` hook is called when a peer -requests an RBF for a channel funding transaction. - -```json -{ - "rbf_channel": { - "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_last_funding_msat": 100000000, - "their_funding_msat": 100000000, - "our_last_funding_msat": 100000000, - "funding_feerate_per_kw": 7500, - "feerate_our_max": 10000, - "feerate_our_min": 253, - "channel_max_msat": 16777215000, - "locktime": 2453, - "requested_lease_msat": 100000000, - "require_confirmed_inputs": false - } -} -``` - -The returned result must contain a `result` member which is either -the string `reject` or `continue`. If `reject` and -there's a member `error_message`, that member is sent to the peer -before disconnection. - -For a 'continue'd result, you can include a `psbt` and an -`our_funding_msat` to contribute funds, inputs and outputs to -this channel open. - -Note that, like the `openchannel_init` RPC call, the `our_funding_msat` -amount must NOT be accounted for in any supplied output. Change, -however, should be included and should use the `funding_feerate_per_kw` -to calculate. - -#### Return - -```json -{ - "result": "continue", - "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZrrw28Oed52hTw3N7t0HbIyZhFdcZRH3+61AQAAAAD9////AGYAAAAAAQDfAgAAAAABARtaSZufCbC+P+/G23XVaQ8mDwZQFW1vlCsCYhLbmVrpAAAAAAD+////AvJs5ykBAAAAFgAUT6ORgb3CgFsbwSOzNLzF7jQS5s+AhB4AAAAAABepFNi369DMyAJmqX2agouvGHcDKsZkhwJHMEQCIHELIyqrqlwRjyzquEPvqiorzL2hrvdu9EBxsqppeIKiAiBykC6De/PDElnqWw49y2vTqauSJIVBgGtSc+vq5BQd+gEhAg0f8WITWvA8o4grxNKfgdrNDncqreMLeRFiteUlne+GZQAAAAEBIICEHgAAAAAAF6kU2Lfr0MzIAmapfZqCi68YdwMqxmSHAQQWABQB+tkKvNZml+JZIWRyLeSpXr7hZQz8CWxpZ2h0bmluZwEIexhVcpJl8ugM/AlsaWdodG5pbmcCAgABAA==", - "our_funding_msat": 39999000 -} -``` - - - -### `htlc_accepted` - -The `htlc_accepted` hook is called whenever an incoming HTLC is accepted, and -its result determines how `lightningd` should treat that HTLC. - -The payload of the hook call has the following format: - -```json -{ - "onion": { - "payload": "", - "short_channel_id": "1x2x3", - "forward_msat": 42, - "outgoing_cltv_value": 500014, - "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", - "next_onion": "[1365bytes of serialized onion]" - }, - "htlc": { - "short_channel_id": "4x5x6", - "id": 27, - "amount_msat": 43, - "cltv_expiry": 500028, - "cltv_expiry_relative": 10, - "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000" - }, - "forward_to": "0000000000000000000000000000000000000000000000000000000000000000" -} -``` - -For detailed information about each field please refer to [BOLT 04 of the specification][bolt4], the following is just a brief summary: - - - `onion`: - - `payload` contains the unparsed payload that was sent to us from the - sender of the payment. - - `short_channel_id` determines the channel that the sender is hinting - should be used next. Not present if we're the final destination. - - `forward_amount` is the amount we should be forwarding to the next hop, - and should match the incoming funds in case we are the recipient. - - `outgoing_cltv_value` determines what the CLTV value for the HTLC that we - forward to the next hop should be. - - `total_msat` specifies the total amount to pay, if present. - - `payment_secret` specifies the payment secret (which the payer should have obtained from the invoice), if present. - - `next_onion` is the fully processed onion that we should be sending to the - next hop as part of the outgoing HTLC. Processed in this case means that we - took the incoming onion, decrypted it, extracted the payload destined for - us, and serialized the resulting onion again. - - `shared_secret` is the shared secret we used to decrypt the incoming - onion. It is shared with the sender that constructed the onion. - - `htlc`: - - `short_channel_id` is the channel this payment is coming from. - - `id` is the low-level sequential HTLC id integer as sent by the channel peer. - - `amount` is the amount that we received with the HTLC. This amount minus - the `forward_amount` is the fee that will stay with us. - - `cltv_expiry` determines when the HTLC reverts back to the - sender. `cltv_expiry` minus `outgoing_cltv_expiry` should be equal or - larger than our `cltv_delta` setting. - - `cltv_expiry_relative` hints how much time we still have to claim the - HTLC. It is the `cltv_expiry` minus the current `blockheight` and is - passed along mainly to avoid the plugin having to look up the current - blockheight. - - `payment_hash` is the hash whose `payment_preimage` will unlock the funds - and allow us to claim the HTLC. - - `forward_to`: if set, the channel_id we intend to forward this to (will not be present if the short_channel_id was invalid or we were the final destination). - -The hook response must have one of the following formats: - -```json -{ - "result": "continue" -} -``` - -This means that the plugin does not want to do anything special and -`lightningd` should continue processing it normally, i.e., resolve the payment -if we're the recipient, or attempt to forward it otherwise. Notice that the -usual checks such as sufficient fees and CLTV deltas are still enforced. - -It can also replace the `onion.payload` by specifying a `payload` in -the response. Note that this is always a TLV-style payload, so unlike -`onion.payload` there is no length prefix (and it must be at least 4 -hex digits long). This will be re-parsed; it's useful for removing -onion fields which a plugin doesn't want lightningd to consider. - -It can also specify `forward_to` in the response, replacing the destination. This usually only makes sense if it wants to choose an alternate channel to the same next peer, but is useful if the `payload` is also replaced. - -```json -{ - "result": "fail", - "failure_message": "2002" -} -``` - -`fail` will tell `lightningd` to fail the HTLC with a given hex-encoded -`failure_message` (please refer to the [spec][bolt4-failure-messages] for -details: `incorrect_or_unknown_payment_details` is the most common). - - -```json -{ - "result": "fail", - "failure_onion": "[serialized error packet]" -} -``` - -Instead of `failure_message` the response can contain a hex-encoded -`failure_onion` that will be used instead (please refer to the -[spec][bolt4-failure-onion] for details). This can be used, for example, -if you're writing a bridge between two Lightning Networks. Note that -`lightningd` will apply the obfuscation step to the value returned here -with its own shared secret (and key type `ammag`) before returning it to -the previous hop. - - -```json -{ - "result": "resolve", - "payment_key": "0000000000000000000000000000000000000000000000000000000000000000" -} -``` - -`resolve` instructs `lightningd` to claim the HTLC by providing the preimage -matching the `payment_hash` presented in the call. Notice that the plugin must -ensure that the `payment_key` really matches the `payment_hash` since -`lightningd` will not check and the wrong value could result in the channel -being closed. - -Warning: `lightningd` will replay the HTLCs for which it doesn't have a final -verdict during startup. This means that, if the plugin response wasn't -processed before the HTLC was forwarded, failed, or resolved, then the plugin -may see the same HTLC again during startup. It is therefore paramount that the -plugin is idempotent if it talks to an external system. - -The `htlc_accepted` hook is a chained hook, i.e., multiple plugins can -register it, and they will be called in the order they were registered in -until the first plugin return a result that is not `{"result": "continue"}`, -after which the event is considered to be handled. After the event has been -handled the remaining plugins will be skipped. - - -### `rpc_command` - -The `rpc_command` hook allows a plugin to take over any RPC command. It sends -the received JSON-RPC request (for any method!) to the registered plugin, - -```json -{ - "rpc_command": { - "id": 3, - "method": "method_name", - "params": { - "param_1": [], - "param_2": {}, - "param_n": "", - } - } -} -``` - -which can in turn: - -Let `lightningd` execute the command with - -```json -{ - "result" : "continue" -} -``` -Replace the request made to `lightningd`: - -```json -{ - "replace": { - "id": 3, - "method": "method_name", - "params": { - "param_1": [], - "param_2": {}, - "param_n": "", - } - } -} -``` - -Return a custom response to the request sender: - -```json -{ - "return": { - "result": { - } - } -} -``` - -Return a custom error to the request sender: - -```json -{ - "return": { - "error": { - } - } -} -``` - -Note: The `rpc_command` hook is chainable. If two or more plugins try to -replace/result/error the same `method`, only the first plugin in the chain -will be respected. Others will be ignored and a warning will be logged. - -### `custommsg` - -The `custommsg` plugin hook is the receiving counterpart to the -[`sendcustommsg`][sendcustommsg] RPC method and allows plugins to handle -messages that are not handled internally. The goal of these two components is -to allow the implementation of custom protocols or prototypes on top of a -Core Lightning node, without having to change the node's implementation itself. - -The payload for a call follows this format: - -```json -{ - "peer_id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "payload": "1337ffffffff" -} -``` - -This payload would have been sent by the peer with the `node_id` matching -`peer_id`, and the message has type `0x1337` and contents `ffffffff`. Notice -that the messages are currently limited to odd-numbered types and must not -match a type that is handled internally by Core Lightning. These limitations are -in place in order to avoid conflicts with the internal state tracking, and -avoiding disconnections or channel closures, since odd-numbered message can be -ignored by nodes (see ["it's ok to be odd" in the specification][oddok] for -details). The plugin must implement the parsing of the message, including the -type prefix, since Core Lightning does not know how to parse the message. - -Because this is a chained hook, the daemon expects the result to be -`{'result': 'continue'}`. It will fail if something else is returned. - -### `onion_message_recv` and `onion_message_recv_secret` - -**(WARNING: experimental-offers only)** - -These two hooks are almost identical, in that they are called when -an onion message is received. - -`onion_message_recv` is used for unsolicited messages (where the -source knows that it is sending to this node), and -`onion_message_recv_secret` is used for messages which use a blinded path -we supplied. The latter hook will have a `pathsecret` field, the -former never will. - -These hooks are separate, because replies MUST be ignored unless they -use the correct path (i.e. `onion_message_recv_secret`, with the expected -`pathsecret`). This avoids the source trying to probe for responses -without using the designated delivery path. - -The payload for a call follows this format: - -```json -{ - "onion_message": { - "pathsecret": "0000000000000000000000000000000000000000000000000000000000000000", - "reply_first_node": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "reply_blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "reply_path": [ {"id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "encrypted_recipient_data": "0a020d0d", - "blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f"} ], - "invoice_request": "0a020d0d", - "invoice": "0a020d0d", - "invoice_error": "0a020d0d", - "unknown_fields": [ {"number": 12345, "value": "0a020d0d"} ] - } -} -``` - -All fields shown here are optional. - -We suggest just returning `{'result': 'continue'}`; any other result -will cause the message not to be handed to any other hooks. - -## Bitcoin backend - -Core Lightning communicates with the Bitcoin network through a plugin. It uses the -`bcli` plugin by default but you can use a custom one, multiple custom ones for -different operations, or write your own for your favourite Bitcoin data source! - -Communication with the plugin is done through 5 JSONRPC commands, `lightningd` -can use from 1 to 5 plugin(s) registering these 5 commands for gathering Bitcoin -data. Each plugin must follow the below specification for `lightningd` to operate. - - -### `getchaininfo` - -Called at startup, it's used to check the network `lightningd` is operating on and to -get the sync status of the backend. - -The plugin must respond to `getchaininfo` with the following fields: - - `chain` (string), the network name as introduced in bip70 - - `headercount` (number), the number of fetched block headers - - `blockcount` (number), the number of fetched block body - - `ibd` (bool), whether the backend is performing initial block download - - -### `estimatefees` - -Polled by `lightningd` to get the current feerate, all values must be passed in sat/kVB. - -The plugin must return `feerate_floor` (e.g. 1000 if mempool is -empty), and an array of 0 or more `feerates`. Each element of -`feerates` is an object with `blocks` and `feerate`, in -ascending-blocks order, for example: - -``` -{ - "feerate_floor": , - "feerates": { - { "blocks": 2, "feerate": }, - { "blocks": 6, "feerate": }, - { "blocks": 12, "feerate": } - { "blocks": 100, "feerate": } - } -} -``` - -lightningd will currently linearly interpolate to estimate between -given blocks (it will not extrapolate, but use the min/max blocks -values). - - -### `getrawblockbyheight` - -This call takes one parameter, `height`, which determines the block height of -the block to fetch. - -The plugin must set all fields to `null` if no block was found at the specified `height`. - -The plugin must respond to `getrawblockbyheight` with the following fields: - - `blockhash` (string), the block hash as a hexadecimal string - - `block` (string), the block content as a hexadecimal string - - -### `getutxout` - -This call takes two parameter, the `txid` (string) and the `vout` (number) -identifying the UTXO we're interested in. - -The plugin must set both fields to `null` if the specified TXO was spent. - -The plugin must respond to `gettxout` with the following fields: - - `amount` (number), the output value in **sats** - - `script` (string), the output scriptPubKey - - -### `sendrawtransaction` - -This call takes two parameters, -a string `tx` representing a hex-encoded Bitcoin transaction, -and a boolean `allowhighfees`, which if set means suppress -any high-fees check implemented in the backend, since the given -transaction may have fees that are very high. - -The plugin must broadcast it and respond with the following fields: - - `success` (boolean), which is `true` if the broadcast succeeded - - `errmsg` (string), if success is `false`, the reason why it failed - - -[jsonrpc-spec]: https://www.jsonrpc.org/specification -[jsonrpc-notification-spec]: https://www.jsonrpc.org/specification#notification -[bolt4]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md -[bolt4-failure-messages]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#failure-messages -[bolt4-failure-onion]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md#returning-errors -[bolt2-open-channel]: https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message -[sendcustommsg]: lightning-sendcustommsg.7.md -[oddok]: https://github.com/lightning/bolts/blob/master/00-introduction.md#its-ok-to-be-odd -[spec]: https://github.com/lightning/bolts -[bolt9]: https://github.com/lightning/bolts/blob/master/09-features.md -[lightning-plugin]: lightning-plugin.7.md -[pyln-client]: https://github.com/ElementsProject/lightning/tree/master/contrib/pyln-client -[contrib/plugins]: https://github.com/ElementsProject/lightning/tree/master/contrib/plugins -[tests]: https://github.com/ElementsProject/lightning/tree/master/tests -[lightning-rpc.7.md]: lightningd-rpc.7.md -[example-plugin]: https://github.com/ElementsProject/lightning/blob/master/contrib/plugins/helloworld.py -[cln-tests]: https://github.com/ElementsProject/lightning/tree/master/tests -[cln-repo]: https://github.com/lightningd/plugins diff --git a/doc/REPRODUCIBLE.md b/doc/REPRODUCIBLE.md deleted file mode 100644 index 94d923331c74..000000000000 --- a/doc/REPRODUCIBLE.md +++ /dev/null @@ -1,269 +0,0 @@ -# Reproducible builds for Core Lightning - -This document describes the steps involved to build Core Lightning in a -reproducible way. Reproducible builds close the final gap in the lifecycle of -open-source projects by allowing maintainers to verify and certify that a -given binary was indeed produced by compiling an unmodified version of the -publicly available source. In particular the maintainer certifies that the -binary corresponds a) to the exact version of the and b) that no malicious -changes have been applied before or after the compilation. - -Core Lightning has provided a manifest of the binaries included in a release, -along with signatures from the maintainers since version 0.6.2. - -The steps involved in creating reproducible builds are: - - - Creation of a known environment in which to build the source code - - Removal of variance during the compilation (randomness, timestamps, etc) - - Packaging of binaries - - Creation of a manifest (`SHA256SUMS` file containing the crytographic - hashes of the binaries and packages) - - Signing of the manifest by maintainers and volunteers that have reproduced - the files in the manifest starting from the source. - -The bulk of these operations is handled by the [`repro-build.sh`][script] -script, but some manual operations are required to setup the build -environment. Since a binary is built against platorm specific libraries we -also need to replicate the steps once for each OS distribution and -architecture, so the majority of this guide will describe how to set up those -starting from a minimal trusted base. This minimal trusted base in most cases -is the official installation medium from the OS provider. - -Note: Since your signature certifies the integrity of the resulting binaries, -please familiarize youself with both the [`repro-build.sh`][script] script, as -well as with the setup instructions for the build environments before signing -anything. - -[script]: https://github.com/ElementsProject/lightning/blob/master/tools/repro-build.sh - -# Build Environment Setup - -The build environments are a set of docker images that are created directly -from the installation mediums and repositories from the OS provider. The -following sections describe how to create those images. Don't worry, you only -have to create each image once and can then reuse the images for future -builds. - -## Base image creation - -Depending on the distribution that we want to build for the instructions to -create a base image can vary. In the following sections we discuss the -specific instructions for each distribution, whereas the instructions are -identical again once we have the base image. - -### Debian / Ubuntu and derivative OSs - -For operating systems derived from Debian we can use the `debootstrap` tool to -build a minimal OS image, that can then be transformed into a docker -image. The packages for the minimal OS image are directly downloaded from the -installation repositories operated by the OS provider. - -We cannot really use the `debian` and `ubuntu` images from the docker hub, -mainly because it'd be yet another trusted third party, but it is also -complicated by the fact that the images have some of the packages updated. The -latter means that if we disable the `updates` and `security` repositories for -`apt` we find ourselves in a situation where we can't install any additional -packages (wrongly updated packages depending on the versions not available in -the non-updated repos). - -The following table lists the codenames of distributions that we -currently support: - -- Ubuntu 18.06: - - Distribution Version: 18.04 - - Codename: bionic -- Ubuntu 20.04: - - Distribution Version: 20.04 - - Codename: focal -- Ubuntu 22.04: - - Distribution Version: 22.04 - - Codename: jammy - -Depending on your host OS release you might not have `debootstrap` -manifests for versions newer than your host OS. Due to this we run the -`debootstrap` commands in a container of the latest version itself: - -```bash -for v in bionic focal jammy; do - echo "Building base image for $v" - sudo docker run --rm -v $(pwd):/build ubuntu:22.04 \ - bash -c "apt-get update && apt-get install -y debootstrap && debootstrap $v /build/$v" - sudo tar -C $v -c . | sudo docker import - $v -done -``` - -Verify that the image corresponds to our expectation and is runnable: - -```bash -sudo docker run bionic cat /etc/lsb-release -``` -Which should result in the following output for `bionic`: - -```text -DISTRIB_ID=Ubuntu -DISTRIB_RELEASE=18.04 -DISTRIB_CODENAME=bionic -DISTRIB_DESCRIPTION="Ubuntu 18.04 LTS" -``` - -## Builder image setup - -Once we have the clean base image we need to customize it to be able to build -Core Lightning. This includes disabling the update repositories, downloading the -build dependencies and specifying the steps required to perform the build. - -For this purpose we have a number of Dockerfiles in the -[`contrib/reprobuild`][repro-dir] directory that have the specific -instructions for each base image. - -We can then build the builder image by calling `docker build` and passing it -the `Dockerfile`: - -```bash -sudo docker build -t cl-repro-bionic - < contrib/reprobuild/Dockerfile.bionic -sudo docker build -t cl-repro-focal - < contrib/reprobuild/Dockerfile.focal -sudo docker build -t cl-repro-jammy - < contrib/reprobuild/Dockerfile.jammy -``` - -Since we pass the `Dockerfile` through `stdin` the build command will not -create a context, i.e., the current directory is not passed to `docker` and -it'll be independent of the currently checked out version. This also means -that you will be able to reuse the docker image for future builds, and don't -have to repeat this dance every time. Verifying the `Dockerfile` therefore is -sufficient to ensure that the resulting `cl-repro-` image is -reproducible. - -The dockerfiles assume that the base image has the codename as its image name. - - -[repro-dir]: https://github.com/ElementsProject/lightning/tree/master/contrib/reprobuild - - -# Building using the builder image - -Finally, after this rather lengthy setup we can perform the actual build. At -this point we have a container image that has been prepared to build -reproducibly. As you can see from the `Dockerfile` above we assume the source -git repository gets mounted as `/repo` in the docker container. The container -will clone the repository to an internal path, in order to keep the repository -clean, build the artifacts there, and then copy them back to `/repo/release`. -We'll need the release directory available for this, so create it now if it -doesn't exist: - -`mkdir release` - -Then we can simply execute the following command inside the git -repository (remember to checkout the tag you are trying to build): - -```bash -sudo docker run --rm -v $(pwd):/repo -ti cl-repro-bionic -sudo docker run --rm -v $(pwd):/repo -ti cl-repro-focal -sudo docker run --rm -v $(pwd):/repo -ti cl-repro-jammy -``` - -The last few lines of output also contain the `sha256sum` hashes of all -artifacts, so if you're just verifying the build those are the lines that are -of interest to you: - -```text -ee83cf4948228ab1f644dbd9d28541fd8ef7c453a3fec90462b08371a8686df8 /repo/release/clightning-v0.9.0rc1-Ubuntu-18.04.tar.xz -94bd77f400c332ac7571532c9f85b141a266941057e8fe1bfa04f054918d8c33 /repo/release/clightning-v0.9.0rc1.zip -``` - -Repeat this step for each distribution and each architecture you wish to -sign. Once all the binaries are in the `release/` subdirectory we can sign the -hashes: - -# (Co-)Signing the release manifest - -The release captain is in charge of creating the manifest, whereas -contributors and interested bystanders may contribute their signatures to -further increase trust in the binaries. - -The release captain creates the manifest as follows: - -```bash -cd release/ -sha256sum *v0.9.0* > SHA256SUMS -gpg -sb --armor SHA256SUMS -``` - -Co-maintainers and contributors wishing to add their own signature verify that -the `SHA256SUMS` and `SHA256SUMS.asc` files created by the release captain -matches their binaries before also signing the manifest: - -```bash -cd release/ -gpg --verify SHA256SUMS.asc -sha256sum -c SHA256SUMS -cat SHA256SUMS | gpg -sb --armor > SHA256SUMS.new -``` - -Then send the resulting `SHA256SUMS.new` file to the release captain so it can -be merged with the other signatures into `SHASUMS.asc`. - -# Verifying a reproducible build - -You can verify the reproducible build in two ways: - - - Repeating the entire reproducible build, making sure from scratch that the - binaries match. Just follow the instructions above for this. - - Verifying that the downloaded binaries match match the hashes in - `SHA256SUMS` and that the signatures in `SHA256SUMS.asc` are valid. - -Assuming you have downloaded the binaries, the manifest and the signatures -into the same directory, you can verify the signatures with the following: - -```bash -gpg --verify SHA256SUMS.asc -``` - -And you should see a list of messages like the following: - -```text -gpg: assuming signed data in 'SHA256SUMS' -gpg: Signature made Fr 08 Mai 2020 07:46:38 CEST -gpg: using RSA key 15EE8D6CAB0E7F0CF999BFCBD9200E6CD1ADB8F1 -gpg: Good signature from "Rusty Russell " [full] -gpg: Signature made Fr 08 Mai 2020 12:30:10 CEST -gpg: using RSA key B7C4BE81184FC203D52C35C51416D83DC4F0E86D -gpg: Good signature from "Christian Decker " [ultimate] -gpg: Signature made Fr 08 Mai 2020 21:35:28 CEST -gpg: using RSA key 30DE693AE0DE9E37B3E7EB6BBFF0F67810C1EED1 -gpg: Good signature from "Lisa Neigut " [full] -``` - -If there are any issues `gpg` will print `Bad signature`, it might be because -the signatures in `SHA256SUMS.asc` do not match the `SHA256SUMS` file, and -could be the result of a filename change. Do not continue using the binaries, -and contact the maintainers, if this is not the case, a failure here means -that the verification failed. - -Next we verify that the binaries match the ones in the manifest: - -```bash -sha256sum -c SHA256SUMS -``` - -Producing output similar to the following: -``` -sha256sum: clightning-v0.9.0-Fedora-28-amd64.tar.gz: No such file or directory -clightning-v0.9.0-Fedora-28-amd64.tar.gz: FAILED open or read -clightning-v0.9.0-Ubuntu-18.04.tar.xz: OK -clightning-v0.9.0.zip: OK -sha256sum: WARNING: 1 listed file could not be read -``` - -Notice that the two files we downloaded are marked as `OK`, but we're missing -one file. If you didn't download that file this is to be expected, and is -nothing to worry about. A failure to verify the hash would give a warning like -the following: - -```text -sha256sum: WARNING: 1 computed checksum did NOT match -``` - -If both the signature verification and the manifest checksum verification -succeeded, then you have just successfully verified a reproducible build and, -assuming you trust the maintainers, are good to install and use the -binaries. Congratulations! 🎉🥳 diff --git a/doc/STYLE.md b/doc/STYLE.md deleted file mode 100644 index 78422daf1aae..000000000000 --- a/doc/STYLE.md +++ /dev/null @@ -1,252 +0,0 @@ -# Care And Feeding of Your Fellow Coders - -Style is an individualistic thing, but working on software is group -activity, so consistency is important. Generally our coding style -is similar to the [Linux coding style][style]. - -[style]: https://www.kernel.org/doc/html/v4.10/process/coding-style.html - -## Communication - -We communicate with each other via code; we polish each others code, -and give nuanced feedback. Exceptions to the rules below always -exist: accept them. Particularly if they're funny! - -## Prefer Short Names - -`num_foos` is better than `number_of_foos`, and `i` is better than -`counter`. But `bool found;` is better than `bool ret;`. Be as -short as you can but still descriptive. - -## Prefer 80 Columns - -We have to stop somewhere. The two tools here are extracting -deeply-indented code into their own functions, and use of short-cuts -using early returns or continues, eg: - -```C - for (i = start; i != end; i++) { - if (i->something) - continue; - - if (!i->something_else) - continue; - - do_something(i); -} -``` - -## Tabs and indentaion - -The C code uses TAB charaters with a visual indentation of 8 whitespaces. -If you submit code for a review, make sure your editor knows this. - -When breaking a line with more than 80 characters, align parameters and -arguments like so: - -```C -static void subtract_received_htlcs(const struct channel *channel, - struct amount_msat *amount) -``` - -Note: For more details, the files `.clang-format` and `.editorconfig` are -located in the projects root directory. - -## Prefer Simple Statements - -Notice the statement above uses separate tests, rather than combining -them. We prefer to only combine conditionals which are fundamentally -related, eg: - -```C - if (i->something != NULL && *i->something < 100) -``` - -## Use of `take()` - -Some functions have parameters marked with `TAKES`, indicating that -they can take lifetime ownership of a parameter which is passed using -`take()`. This can be a useful optimization which allows the function -to avoid making a copy, but if you hand `take(foo)` to something which -doesn't support `take()` you'll probably leak memory! - -In particular, our automatically generated marshalling code doesn't -support `take()`. - -If you're allocating something simply to hand it via `take()` you -should use NULL as the parent for clarity, eg: - -```C - msg = towire_shutdown(NULL, &peer->channel_id, peer->final_scriptpubkey); - enqueue_peer_msg(peer, take(msg)); -``` - -## Use of `tmpctx` - -There's a convenient temporary tal context which gets cleaned -regularly: you should use this for throwaways rather than (as you'll -see some of our older code do!) grabbing some passing object to hang -your temporaries off! - -## Enums and Switch Statements - -If you handle various enumerated values in a `switch`, don't use -`default:` but instead mention every enumeration case-by-case. That -way when a new enumeration case is added, most compilers will warn that you -don't cover it. This is particularly valuable for code auto-generated -from the specification! - -## Initialization of Variables - -Avoid double-initialization of variables; it's better to set them when -they're known, eg: - -```C - bool is_foo; - - if (bar == foo) - is_foo = true; - else - is_foo = false; - - ... - if (is_foo)... -``` - -This way the compiler will warn you if you have one path which doesn't set the -variable. If you initialize with `bool is_foo = false;` then you'll -simply get that value without warning when you change the code and -forget to set it on one path. - -## Initialization of Memory - -`valgrind` warns about decisions made on uninitialized memory. Prefer -`tal` and `tal_arr` to `talz` and `tal_arrz` for this reason, and -initialize only the fields you expect to be used. - -Similarly, you can use `memcheck(mem, len)` to explicitly assert that -memory should have been initialized, rather than having valgrind -trigger later. We use this when placing things on queues, for example. - -## Use of static and const - -Everything should be declared static and const by default. Note that -`tal_free()` can free a const pointer (also, that it returns `NULL`, for -convenience). - -## Typesafety Is Worth Some Pain - -If code is typesafe, refactoring is as simple as changing a type and -compiling to find where to refactor. We rely on this, -so most places in the code will break if you hand the wrong type, eg -`type_to_string` and `structeq`. - -The two tools we have to help us are complicated macros in -`ccan/typesafe_cb` allow you to create callbacks which must match the -type of their argument, rather than using `void *`. The other is -`ARRAY_SIZE`, a macro which won't compile if you hand it a pointer -instead of an actual array. - -## Use of `FIXME` - -There are two cases in which you should use a `/* FIXME: */` comment: -one is where an optimization is possible but it's not clear that it's -yet worthwhile, and the second one is to note an ugly corner case -which could be improved (and may be in a following patch). - -There are always compromises in code: eventually it needs to ship. -`FIXME` is `grep`-fodder for yourself and others, as well as useful -warning signs if we later encounter an issue in some part of the code. - -## If You Don't Know The Right Thing, Do The Simplest Thing - -Sometimes the right way is unclear, so it's best not to spend time on -it. It's far easier to rewrite simple code than complex code, too. - -## Write For Today: Unused Code Is Buggy Code - -Don't overdesign: complexity is a killer. If you need a fancy data -structure, start with a brute force linked list. Once that's working, -perhaps consider your fancy structure, but don't implement a generic -thing. Use `/* FIXME: ...*/` to salve your conscience. - -## Keep Your Patches Reviewable - -Try to make a single change at a time. It's tempting to do "drive-by" -fixes as you see other things, and a minimal amount is unavoidable, but -you can end up shaving infinite yaks. This is a good time to drop a -`/* FIXME: ...*/` comment and move on. - -## Creating JSON APIs - -Our JSON RPCs always return a top-level object. This allows us to add -warnings (e.g. that we're still starting up) or other optional fields -later. - -Prefer to use JSON names which are already in use, or otherwise names -from the BOLT specifications. - -The same command should always return the same JSON format: this is -why e.g. `listchannels` return an array even if given an argument so -there's only zero or one entries. - -All `warning` fields should have unique names which start with -`warning_`, the value of which should be an explanation. This allows -for programs to deal with them sanely, and also perform translations. - -### Documenting JSON APIs - -We use JSON schemas to validate that JSON-RPC returns are in the -correct form, and also to generate documentation. See -[writing schemas manpage](schemas/WRITING_SCHEMAS.md). - -## Changing JSON APIs - -All JSON API changes need a Changelog line (see below). - -You can always add a new output JSON field (Changelog-Added), but you -cannot remove one without going through a 6-month deprecation cycle -(Changelog-Deprecated) - -So, only output it if `allow-deprecated-apis` is true, so users can test -their code is futureproof. In 6 months remove it (Changelog-Removed). - -Changing existing input parameters is harder, and should generally be -avoided. Adding input parameters is possible, but should be done -cautiously as too many parameters get unwieldy quickly. - -## Github Workflows - -We have adopted a number of workflows to facilitate the development of -Core Lightning, and to make things more pleasant for contributors. - -### Changelog Entries in Commit Messages - -We are maintaining a changelog in the top-level directory of this -project. However since every pull request has a tendency to touch the file and -therefore create merge-conflicts we decided to derive the changelog file from -the pull requests that were added between releases. In order for a pull -request to show up in the changelog at least one of its commits will have to -have a line with one of the following prefixes: - - - `Changelog-Added: ` if the pull request adds a new feature - - `Changelog-Changed: ` if a feature has been modified and might require - changes on the user side - - `Changelog-Deprecated: ` if a feature has been marked for deprecation, but - not yet removed - - `Changelog-Fixed: ` if a bug has been fixed - - `Changelog-Removed: ` if a (previously deprecated) feature has been removed - - `Changelog-Experimental: ` if it only affects --enable-experimental-features builds, or experimental- config options. - -In case you think the pull request is small enough not to require a changelog -entry please use `Changelog-None` in one of the commit messages to opt out. - -Under some circumstances a feature may be removed even without deprecation -warning if it was not part of a released version yet, or the removal is -urgent. - -In order to ensure that each pull request has the required `Changelog-*:` line -for the changelog our trusty @bitcoin-bot will check logs whenever a pull -request is created or updated and search for the required line. If there is no -such line it'll mark the pull request as `pending` to call out the need for an -entry. diff --git a/doc/TOR.md b/doc/TOR.md deleted file mode 100644 index d994675b5921..000000000000 --- a/doc/TOR.md +++ /dev/null @@ -1,448 +0,0 @@ -# Setting up TOR with Core Lightning - -To use any Tor features with Core Lightning you must have Tor installed and running. - -Note that we only support Tor v3: you can check your installed Tor version with `tor --version` or `sudo tor --version` - -If Tor is not installed you can install it on Debian based Linux systems (Ubuntu, Debian, etc) with the following command: - -```bash -sudo apt install tor -``` -then `/etc/init.d/tor start` or `sudo systemctl enable --now tor` depending -on your system configuration. - -Most default setting should be sufficient. - -To keep a safe configuration for minimal harassment (See [Tor FAQ]) -just check that this line is present in the Tor config file `/etc/tor/torrc`: - -`ExitPolicy reject *:* # no exits allowed` - -This does not affect Core Lightning connect, listen, etc.. -It will only prevent your node from becoming a Tor exit node. -Only enable this if you are sure about the implications. - -If you don't want to create .onion addresses this should be enough. - -There are several ways by which a Core Lightning node can accept or make connections over Tor. - -The node can be reached over Tor by connecting to its .onion address. - -To provide the node with a .onion address you can: - -* create a **non-persistent** address with an auto service or - -* create a **persistent** address with a hidden service. - - -### Quick Start On Linux - -It is easy to create a single persistent Tor address and not announce a public IP. -This is ideal for most setups where you have an ISP-provided router connecting your -Internet to your local network and computer, as it does not require a stable -public IP from your ISP (which might not give one to you for free), nor port -forwarding (which can be hard to set up for random cheap router models). -Tor provides NAT-traversal for free, so even if you or your ISP has a complex -network between you and the Internet, as long as you can use Tor you can -be connected to. - -Note: Core Lightning also support IPv4/6 address discovery behind NAT routers. -If your node detects an new public address, it can update its announcement. -For this to work you need to forward the TCP port 9735 on your NAT router to your node. -In this case you don't need TOR to punch through your firewall. - -Note: Per default and for privacy reasons IP discovery will only be active -if no other addresses would be announced (as kind of a fallback). -You can set `--announce-addr-discovered=true` to explicitly activate it. -Your node will then update discovered IP addresses even if it also announces e.g. a TOR address. -This usually has the benefit of quicker and more stable connections but does not -offer additional privacy. - -On most Linux distributions, making a standard installation of `tor` will -automatically set it up to have a SOCKS5 proxy at port 9050. -As well, you have to set up the Tor Control Port. -On most Linux distributions there will be commented-out settings below in the -`/etc/tor/torrc`: - -``` -ControlPort 9051 -CookieAuthentication 1 -CookieAuthFile /var/lib/tor/control_auth_cookie -CookieAuthFileGroupReadable 1 -``` - -Uncomment those in, then restart `tor` (usually `systemctl restart tor` or -`sudo systemctl restart tor` on most SystemD-based systems, including recent -Debian and Ubuntu, or just restart the entire computer if you cannot figure -it out). - -On some systems (such as Arch Linux), you may also need to add the following -setting: - -``` -DataDirectoryGroupReadable 1 -``` - -You also need to make your user a member of the Tor group. -"Your user" here is whatever user will run `lightningd`. -On Debian-derived systems, the Tor group will most likely be `debian-tor`. -You can try listing all groups with the below command, and check for a -`debian-tor` or `tor` groupname. - -``` -getent group | cut -d: -f1 | sort -``` - -Alternately, you could check the group of the cookie file directly. -Usually, on most Linux systems, that would be `/run/tor/control.authcookie`: - -``` -stat -c '%G' /run/tor/control.authcookie -``` - -Once you have determined the `${TORGROUP}` and selected the -`${LIGHTNINGUSER}` that will run `lightningd`, run this as root: - -``` -usermod -a -G ${TORGROUP} ${LIGHTNINGUSER} -``` - -Then restart the computer (logging out and logging in again should also -work). -Confirm that `${LIGHTNINGUSER}` is in `${TORGROUP}` by running the -`groups` command as `${LIGHTNINGUSER}` and checking `${TORGROUP}` is listed. - -If the `/run/tor/control.authcookie` exists in your system, then log in as -the user that will run `lightningd` and check this command: - -``` -cat /run/tor/control.authcookie > /dev/null -``` - -If the above prints nothing and returns, then Core Lightning "should" work -with your Tor. -If it prints an error, some configuration problem will likely prevent -Core Lightning from working with your Tor. - -Then make sure these are in your `${LIGHTNING_DIR}/config` or other Core Lightning configuration -(or prepend `--` to each of them and add them to your `lightningd` invocation -command line): - -``` -proxy=127.0.0.1:9050 -bind-addr=127.0.0.1:9735 -addr=statictor:127.0.0.1:9051 -always-use-proxy=true -``` - -1. `proxy` informs Core Lightning that you have a SOCKS5 proxy at port 9050. - Core Lightning will assume that this is a Tor proxy, port 9050 is the - default in most Linux distributions; you can double-check `/etc/tor/torrc` - for a `SocksPort` entry to confirm the port number. -2. `bind-addr` informs Core Lightning to bind itself to port 9735. - This is needed for the subsequent `statictor` to work. - 9735 is the normal Lightning Network port, so this setting may already be present. - If you add a second `bind-addr=...` you may get errors, so choose this new one - or keep the old one, but don't keep both. - This has to appear before any `statictor:` setting. -3. `addr=statictor:` informs Core Lightning that you want to create a persistent - hidden service that is based on your node private key. - This informs Core Lightning as well that the Tor Control Port is 9051. - You can also use `bind-addr=statictor:` instead to not announce the - persistent hidden service, but if anyone wants to make a channel with - you, you either have to connect to them, or you have to reveal your - address to them explicitly (i.e. autopilots and the like will likely - never connect to you). -4. `always-use-proxy` informs Core Lightning to always use Tor even when - connecting to nodes with public IPs. - You can set this to `false` or remove it, - if you are not privacy-conscious **and** find Tor is too slow for you. - -### Tor Browser and Orbot - -It is possible to not install Tor on your computer, and rely on just -Tor Browser. -Tor Browser will run a built-in Tor instance, but with the proxy at port -9150 and the control port at 9151 -(the normal Tor has, by default, the proxy at port 9050 and the control -port at 9051). -The mobile Orbot uses the same defaults as Tor Browser (9150 and 9151). - -You can then use these settings for Core Lightning: - -``` -proxy=127.0.0.1:9150 -bind-addr=127.0.0.1:9735 -addr=statictor:127.0.0.1:9151 -always-use-proxy=true -``` - -You will have to run Core Lightning after launching Tor Browser or Orbot, -and keep Tor Browser or Orbot open as long as Core Lightning is running, -but this is a setup which allows others to connect and fund channels -to you, anywhere (no port forwarding! works wherever Tor works!), and -you do not have to do anything more complicated than download and -install Tor Browser. -This may be useful for operating system distributions that do not have -Tor in their repositories, assuming we can ever get Core Lightning running -on those. - -### Detailed Discussion - -#### Three Ways to Create .onion Addresses for Core Lightning - -1. You can configure Tor to create an onion address for you, and tell Core Lightning to use that address -2. You can have Core Lightning tell Tor to create a new onion address every time -3. You can configure Core Lightning to tell Tor to create the same onion address every time it starts up - -#### Tor-Created .onion Address - -Having Tor create an onion address lets you run other services (e.g. -a web server) at that same address, and you just tell that address to -Core Lightning and it doesn't have to talk to the Tor server at all. - -Put the following in your `/etc/tor/torrc` file: - -``` -HiddenServiceDir /var/lib/tor/lightningd-service_v3/ -HiddenServiceVersion 3 -HiddenServicePort 1234 127.0.0.1:9735 -``` - -The hidden lightning service will be reachable at port 1234 (global port) -of the .onion address, which will be created at the restart of the -Tor service. Both types of addresses can coexist on the same node. - -Save the file and restart the Tor service. In linux: - -`/etc/init.d/tor restart` or `sudo systemctl restart tor` depending -on the configuration of your system. - -You will find the newly created address (myaddress.onion) with: -``` -sudo cat /var/lib/tor/lightningd-service_v3/hostname -``` - -Now you need to tell Core Lightning to advertize that onion hostname and -port, by placing `announce-addr=myaddress.onion` in your lightning -config. - -#### Letting Core Lightning Control Tor - -To have Core Lightning control your Tor addresses, you have to tell Tor -to accept control commands from Core Lightning, either by using a cookie, -or a password. - -##### Service authenticated by cookie - -This tells Tor to create a cookie file each time: lightningd will have -to be in the same group as tor (e.g. debian-tor): you can look at -`/run/tor/control.authcookie` to check the group name. - -Add the following lines in the `/etc/tor/torrc` file: - -``` -ControlPort 9051 -CookieAuthentication 1 -CookieAuthFileGroupReadable 1 -``` - -Save the file and restart the Tor service. - -##### Service authenticated by password - -This tells Tor to allow password access: you also need to tell lightningd -what the password is. - -Create a hash of your password with -``` -tor --hash-password yourpassword -``` - -This returns a line like - -`16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F` - -Put these lines in the `/etc/tor/torrc` file: -``` -ControlPort 9051 -HashedControlPassword 16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F -``` - -Save the file and restart the Tor service. - -Put `tor-service-password=yourpassword` (not the hash) in your -lightning configuration file. - -##### Core Lightning Creating Persistent Hidden Addresses - -This is usually better than transient addresses, as nodes won't have -to wait for gossip propagation to find out your new address each time -you restart. - -Once you've configured access to Tor as described above, you need -to add *two* lines in your lightningd config file: - -1. A local address which lightningd can tell Tor to connect to when - connections come in, e.g. `bind-addr=127.0.0.1:9735`. -2. After that, a `addr=statictor:127.0.0.1:9051` to tell - Core Lightning to set up and announce a Tor onion address (and tell - Tor to send connections to our real address, above). - -You can use `bind-addr` if you want to set up the onion address and -not announce it to the world for some reason. - -You may add more `addr` lines if you want to advertize other -addresses. - -There is an older method, called "autotor" instead of "statictor" -which creates a different Tor address on each restart, which is -usually not very helpful; you need to use `lightning-cli getinfo` to -see what address it is currently using, and other peers need to wait -for fresh gossip messages if you announce it, before they can connect. - - -### What do we support - -| Case # | IP Number | Hidden service |Incoming / Outgoing Tor | -| ------- | ------------- | ------------------------- |------------------------- -| 1 | Public | NO | Outgoing | -| 2 | Public | FIXED BY TOR | Incoming [1] | -| 3 | Public | FIXED BY CORE LIGHTNING | Incoming [1] | -| 4 | Not Announced | FIXED BY TOR | Incoming [1] | -| 5 | Not Announced | FIXED BY CORE LIGHTNING | Incoming [1] | - - -NOTE: - -1. In all the "Incoming" use case, the node can also make "Outgoing" Tor -connections (connect to a .onion address) by adding the `proxy=127.0.0.1:9050` option. - -#### Case #1: Public IP address and no Tor address, but can connect to Tor addresses - -Without a .onion address, the node won't be reachable through Tor by other -nodes but it will always be able to `connect` to a Tor enabled node -(outbound connections), passing the `connect` request through the Tor -service socks5 proxy. When the Tor service starts it creates a socks5 -proxy which is by default at the address 127.0.0.1:9050. - -If the node is started with the option `proxy=127.0.0.1:9050` the node -will be always able to connect to nodes with .onion address through the socks5 -proxy. - -**You can always add this option, also in the other use cases, to add outgoing -Tor capabilities.** - -If you want to `connect` to nodes ONLY via the Tor proxy, you have to add the -`always-use-proxy=true` option (though if you only advertize Tor addresses, -we also assume you want to always use the proxy). - -You can announce your public IP address through the usual method: if -your node is in an internal network: - -``` -bind-addr=internalIPAddress:port -announce-addr=externalIpAddress -``` - -or if it has a public IP address: - -``` -addr=externalIpAddress -``` - -TIP: If you are unsure which of the two is suitable for you, find your internal -and external address and see if they match. - -In linux: - -Discover your external IP address with: `curl ipinfo.io/ip` - -and your internal IP Address with: `ip route get 1 | awk '{print $NF;exit}'` - -If they match you can use the `--addr` command line option. - -#### Case #2: Public IP address, and a fixed Tor address in torrc - -Other nodes can connect to you entirely over Tor, and the Tor address -doesn't change every time you restart. - -You simply tell Core Lightning to advertize both addresses (you can use -`sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your -Tor-assigned onion address). - -If you have an internal IP address: - -``` -bind-addr=yourInternalIPAddress:port -announce-addr=yourexternalIPAddress:port -announce-addr=your.onionAddress:port -``` - -Or an external address: -``` -addr=yourIPAddress:port -announce-addr=your.onionAddress:port -``` - -#### Case #3: Public IP address, and a fixed Tor address set by Core Lightning - -Other nodes can connect to you entirely over Tor, and the Tor address -doesn't change every time you restart. - -See "Letting Core Lightning Control Tor" for how to get Core Lightning -talking to Tor. - -If you have an internal IP address: - -``` -bind-addr=yourInternalIPAddress:port -announce-addr=yourexternalIPAddress:port -addr=statictor:127.0.0.1:9051 -``` - -Or an external address: -``` -addr=yourIPAddress:port -addr=statictor:127.0.0.1:9051 -``` - -#### Case #4: Unannounced IP address, and a fixed Tor address in torrc - -Other nodes can only connect to you over Tor. - -You simply tell Core Lightning to advertize the Tor address (you can use -`sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your -Tor-assigned onion address). - -``` -announce-addr=your.onionAddress:port -proxy=127.0.0.1:9050 -always-use-proxy=true -``` - -#### Case #4: Unannounced IP address, and a fixed Tor address set by Core Lightning - -Other nodes can only connect to you over Tor. - -See "Letting Core Lightning Control Tor" for how to get Core Lightning -talking to Tor. - -``` -addr=statictor:127.0.0.1:9051 -proxy=127.0.0.1:9050 -always-use-proxy=true -``` - -## References - -The lightningd-config manual page covers the various address cases in detail. - -[The Tor project](https://www.torproject.org/) - -[tor FAQ]: https://www.torproject.org/docs/faq.html.en#WhatIsTor - -[Tor Hidden Service]: https://www.torproject.org/docs/onion-services.html.en - -[.onion addresses version 3]: https://blog.torproject.org/we-want-you-test-next-gen-onion-services diff --git a/doc/beginners-guide/backup-and-recovery.md b/doc/beginners-guide/backup-and-recovery.md new file mode 100644 index 000000000000..a4ae4a384750 --- /dev/null +++ b/doc/beginners-guide/backup-and-recovery.md @@ -0,0 +1,438 @@ +--- +title: "Backup and recovery" +slug: "backup-and-recovery" +excerpt: "Learn the various backup and recovery options available for your Core Lightning node." +hidden: false +createdAt: "2022-11-18T16:28:17.292Z" +updatedAt: "2023-04-22T12:51:49.775Z" +--- +Lightning Network channels get their scalability and privacy benefits from the very simple technique of _not telling anyone else about your in-channel activity_. +This is in contrast to onchain payments, where you have to tell everyone about each and every payment and have it recorded on the blockchain, leading to scaling problems (you have to push data to everyone, everyone needs to validate every transaction) and privacy problems (everyone knows every payment you were ever involved in). + +Unfortunately, this removes a property that onchain users are so used to, they react in surprise when learning about this removal. +Your onchain activity is recorded in all archival fullnodes, so if you forget all your onchain activity because your storage got fried, you just go redownload the activity from the nearest archival full node. + +But in Lightning, since _you_ are the only one storing all your financial information, you **_cannot_** recover this financial information from anywhere else. + +This means that on Lightning, **you have to** responsibly back up your financial information yourself, using various processes and automation. + +The discussion below assumes that you know where you put your `$LIGHTNINGDIR`, and you know the directory structure within. By default your `$LIGHTNINGDIR` will be in `~/.lightning/${COIN}`. For example, if you are running `--mainnet`, it will be +`~/.lightning/bitcoin`. + +## `hsm_secret` + +> 📘 Who should do this: +> +> Everyone. + +You need a copy of the `hsm_secret` file regardless of whatever backup strategy you use. + +The `hsm_secret` is created when you first create the node, and does not change. +Thus, a one-time backup of `hsm_secret` is sufficient. + +This is just 32 bytes, and you can do something like the below and write the hexadecimal digits a few times on a piece of paper: + +```shell +cd $LIGHTNINGDIR +xxd hsm_secret +``` + + + +You can re-enter the hexdump into a text file later and use `xxd` to convert it back to a binary `hsm_secret`: + +``` +cat > hsm_secret_hex.txt < hsm_secret +chmod 0400 hsm_secret +``` + + + +Notice that you need to ensure that the `hsm_secret` is only readable by the user, and is not writable, as otherwise `lightningd` will refuse to start. Hence the `chmod 0400 hsm_secret` command. + +Alternatively, if you are deploying a new node that has no funds and channels yet, you can generate BIP39 words using any process, and create the `hsm_secret` using the `hsmtool generatehsm` command. +If you did `make install` then `hsmtool` is installed as [`lightning-hsmtool`](ref:lightning-hsmtool), else you can find it in the `tools/` directory of the build directory. + +```shell +lightning-hsmtool generatehsm hsm_secret +``` + + + +Then enter the BIP39 words, plus an optional passphrase. Then copy the `hsm_secret` to `${LIGHTNINGDIR}` + +You can regenerate the same `hsm_secret` file using the same BIP39 words, which again, you can back up on paper. + +Recovery of the `hsm_secret` is sufficient to recover any onchain funds. +Recovery of the `hsm_secret` is necessary, but insufficient, to recover any in-channel funds. +To recover in-channel funds, you need to use one or more of the other backup strategies below. + +## SQLITE3 `--wallet=${main}:${backup}` And Remote NFS Mount + +> 📘 Who should do this: +> +> Casual users. + +> 🚧 +> +> This technique is only supported after the version v0.10.2 (not included) or later. +> +> On earlier versions, the `:` character is not special and will be considered part of the path of the database file. + +When using the SQLITE3 backend (the default), you can specify a second database file to replicate to, by separating the second file with a single `:` character in the `--wallet` option, after the main database filename. + +For example, if the user running `lightningd` is named `user`, and you are on the Bitcoin mainnet with the default `${LIGHTNINGDIR}`, you can specify in your `config` file: + +```shell +wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 +``` + + + +Or via command line: + +``` +lightningd --wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 +``` + + + +If the second database file does not exist but the directory that would contain it does exist, the file is created. +If the directory of the second database file does not exist, `lightningd` will fail at startup. +If the second database file already exists, on startup it will be overwritten with the main database. +During operation, all database updates will be done on both databases. + +The main and backup files will **not** be identical at every byte, but they will still contain the same data. + +It is recommended that you use **the same filename** for both files, just on different directories. + +This has the advantage compared to the `backup` plugin below of requiring exactly the same amount of space on both the main and backup storage. The `backup` plugin will take more space on the backup than on the main storage. +It has the disadvantage that it will only work with the SQLITE3 backend and is not supported by the PostgreSQL backend, and is unlikely to be supported on any future database backends. + +You can only specify _one_ replica. + +It is recommended that you use a network-mounted filesystem for the backup destination. +For example, if you have a NAS you can access remotely. + +At the minimum, set the backup to a different storage device. +This is no better than just using RAID-1 (and the RAID-1 will probably be faster) but this is easier to set up --- just plug in a commodity USB flash disk (with metal casing, since a lot of writes are done and you need to dissipate the heat quickly) and use it as the backup location, without +repartitioning your OS disk, for example. + +> 📘 +> +> Do note that files are not stored encrypted, so you should really not do this with rented space ("cloud storage"). + +To recover, simply get **all** the backup database files. +Note that SQLITE3 will sometimes create a `-journal` or `-wal` file, which is necessary to ensure correct recovery of the backup; you need to copy those too, with corresponding renames if you use a different filename for the backup database, e.g. if you named the backup `backup.sqlite3` and when you recover you find `backup.sqlite3` and `backup.sqlite3-journal` files, you rename `backup.sqlite3` to `lightningd.sqlite3` and +`backup.sqlite3-journal` to `lightningd.sqlite3-journal`. +Note that the `-journal` or `-wal` file may or may not exist, but if they _do_, you _must_ recover them as well (there can be an `-shm` file as well in WAL mode, but it is unnecessary; +it is only used by SQLITE3 as a hack for portable shared memory, and contains no useful data; SQLITE3 will ignore its contents always). +It is recommended that you use **the same filename** for both main and backup databases (just on different directories), and put the backup in its own directory, so that you can just recover all the files in that directory without worrying about missing any needed files or correctly +renaming. + +If your backup destination is a network-mounted filesystem that is in a remote location, then even loss of all hardware in one location will allow you to still recover your Lightning funds. + +However, if instead you are just replicating the database on another storage device in a single location, you remain vulnerable to disasters like fire or computer confiscation. + +## `backup` Plugin And Remote NFS Mount + +> 📘 Who should do this: +> +> Casual users. + +You can find the full source for the `backup` plugin here: + + +The `backup` plugin requires Python 3. + +- Download the source for the plugin. + - `git clone https://github.com/lightningd/plugins.git` +- `cd` into its directory and install requirements. + - `cd plugins/backup` + - `pip3 install -r requirements.txt` +- Figure out where you will put the backup files. + - Ideally you have an NFS or other network-based mount on your system, into which you will put the backup. +- Stop your Lightning node. +- `/path/to/backup-cli init --lightning-dir ${LIGHTNINGDIR} file:///path/to/nfs/mount/file.bkp`. + This creates an initial copy of the database at the NFS mount. +- Add these settings to your `lightningd` configuration: + - `important-plugin=/path/to/backup.py` +- Restart your Lightning node. + +It is recommended that you use a network-mounted filesystem for the backup destination. +For example, if you have a NAS you can access remotely. + +> 📘 +> +> Do note that files are not stored encrypted, so you should really not do this with rented space ("cloud storage"). + +Alternately, you _could_ put it in another storage device (e.g. USB flash disk) in the same physical location. + +To recover: + +- Re-download the `backup` plugin and install Python 3 and the + requirements of `backup`. +- `/path/to/backup-cli restore file:///path/to/nfs/mount ${LIGHTNINGDIR}` + +If your backup destination is a network-mounted filesystem that is in a remote location, then even loss of all hardware in one location will allow you to still recover your Lightning funds. + +However, if instead you are just replicating the database on another storage device in a single location, you remain vulnerable to disasters like fire or computer confiscation. + +## Filesystem Redundancy + +> 📘 Who should do this: +> +> Filesystem nerds, data hoarders, home labs, enterprise users. + +You can set up a RAID-1 with multiple storage devices, and point the `$LIGHTNINGDIR` to the RAID-1 setup. That way, failure of one storage device will still let you recover funds. + +You can use a hardware RAID-1 setup, or just buy multiple commodity storage media you can add to your machine and use a software RAID, such as (not an exhaustive list!): + +- `mdadm` to create a virtual volume which is the RAID combination of multiple physical media. +- BTRFS RAID-1 or RAID-10, a filesystem built into Linux. +- ZFS RAID-Z, a filesystem that cannot be legally distributed with the Linux kernel, but can be distributed in a BSD system, and can be installed on Linux with some extra effort, see + [ZFSonLinux](https://zfsonlinux.org). + +RAID-1 (whether by hardware, or software) like the above protects against failure of a single storage device, but does not protect you in case of certain disasters, such as fire or computer confiscation. + +You can "just" use a pair of high-quality metal-casing USB flash devices (you need metal-casing since the devices will have a lot of small writes, which will cause a lot of heating, which needs to dissipate very fast, otherwise the flash device firmware will internally disconnect the flash device from your computer, reducing your reliability) in RAID-1, if you have enough USB ports. + +### Example: BTRFS on Linux + +On a Linux system, one of the simpler things you can do would be to use BTRFS RAID-1 setup between a partition on your primary storage and a USB flash disk. + +The below "should" work, but assumes you are comfortable with low-level Linux administration. +If you are on a system that would make you cry if you break it, you **MUST** stop your Lightning node and back up all files before doing the below. + +- Install `btrfs-progs` or `btrfs-tools` or equivalent. +- Get a 32Gb USB flash disk. +- Stop your Lightning node and back up everything, do not be stupid. +- Repartition your hard disk to have a 30Gb partition. + - This is risky and may lose your data, so this is best done with a brand-new hard disk that contains no data. +- Connect the USB flash disk. +- Find the `/dev/sdXX` devices for the HDD 30Gb partition and the flash disk. + - `lsblk -o NAME,TYPE,SIZE,MODEL` should help. +- Create a RAID-1 `btrfs` filesystem. + - `mkfs.btrfs -m raid1 -d raid1 /dev/${HDD30GB} /dev/${USB32GB}` + - You may need to add `-f` if the USB flash disk is already formatted. +- Create a mountpoint for the `btrfs` filesystem. +- Create a `/etc/fstab` entry. + - Use the `UUID` option instad of `/dev/sdXX` since the exact device letter can change across boots. + - You can get the UUID by `lsblk -o NAME,UUID`. Specifying _either_ of the devices is sufficient. + - Add `autodefrag` option, which tends to work better with SQLITE3 databases. + - e.g. `UUID=${UUID} ${BTRFSMOUNTPOINT} btrfs defaults,autodefrag 0 0` +- `mount -a` then `df` to confirm it got mounted. +- Copy the contents of the `$LIGHTNINGDIR` to the BTRFS mount point. + - Copy the entire directory, then `chown -R` the copy to the user who will run the `lightningd`. + - If you are paranoid, run `diff -r` on both copies to check. +- Remove the existing `$LIGHTNINGDIR`. +- `ln -s ${BTRFSMOUNTPOINT}/lightningdirname ${LIGHTNINGDIR}`. + - Make sure the `$LIGHTNINGDIR` has the same structure as what you originally had. +- Add `crontab` entries for `root` that perform regular `btrfs` maintenance tasks. + - `0 0 * * * /usr/bin/btrfs balance start -dusage=50 -dlimit=2 -musage=50 -mlimit=4 ${BTRFSMOUNTPOINT}` + This prevents BTRFS from running out of blocks even if it has unused space _within_ blocks, and is run at midnight everyday. You may need to change the path to the `btrfs` binary. + - `0 0 * * 0 /usr/bin/btrfs scrub start -B -c 2 -n 4 ${BTRFSMOUNTPOINT}` + This detects bit rot (i.e. bad sectors) and auto-heals the filesystem, and is run on Sundays at midnight. +- Restart your Lightning node. + +If one or the other device fails completely, shut down your computer, boot on a recovery disk or similar, then: + +- Connect the surviving device. +- Mount the partition/USB flash disk in `degraded` mode: + - `mount -o degraded /dev/sdXX /mnt/point` +- Copy the `lightningd.sqlite3` and `hsm_secret` to new media. + - Do **not** write to the degraded `btrfs` mount! +- Start up a `lightningd` using the `hsm_secret` and `lightningd.sqlite3` and close all channels and move all funds to onchain cold storage you control, then set up a new Lightning node. + +If the device that fails is the USB flash disk, you can replace it using BTRFS commands. +You should probably stop your Lightning node while doing this. + +- `btrfs replace start /dev/sdOLD /dev/sdNEW ${BTRFSMOUNTPOINT}`. + - If `/dev/sdOLD` no longer even exists because the device is really really broken, use `btrfs filesystem show` to see the number after `devid` of the broken device, and use that number instead of `/dev/sdOLD`. +- Monitor status with `btrfs replace status ${BTRFSMOUNTPOINT}`. + +More sophisticated setups with more than two devices are possible. Take note that "RAID 1" in `btrfs` means "data is copied on up to two devices", meaning only up to one device can fail. +You may be interested in `raid1c3` and `raid1c4` modes if you have three or four storage devices. BTRFS would probably work better if you were purchasing an entire set +of new storage devices to set up a new node. + +## PostgreSQL Cluster + +> 📘 Who should do this: +> +> Enterprise users, whales. + +`lightningd` may also be compiled with PostgreSQL support. + +PostgreSQL is generally faster than SQLITE3, and also supports running a PostgreSQL cluster to be used by `lightningd`, with automatic replication and failover in case an entire node of the PostgreSQL cluster fails. + +Setting this up, however, is more involved. + +By default, `lightningd` compiles with PostgreSQL support **only** if it finds `libpq` installed when you `./configure`. To enable it, you have to install a developer version of `libpq`. On most Debian-derived systems that would be `libpq-dev`. To verify you have it properly installed on your system, check if the following command gives you a path: + +```shell +pg_config --includedir +``` + + + +Versioning may also matter to you. +For example, Debian Stable ("buster") as of late 2020 provides PostgreSQL 11.9 for the `libpq-dev` package, but Ubuntu LTS ("focal") of 2020 provides PostgreSQL 12.5. +Debian Testing ("bullseye") uses PostgreSQL 13.0 as of this writing. PostgreSQL 12 had a non-trivial change in the way the restore operation is done for replication. + +You should use the same PostgreSQL version of `libpq-dev` as what you run on your cluster, which probably means running the same distribution on your cluster. + +Once you have decided on a specific version you will use throughout, refer as well to the "synchronous replication" document of PostgreSQL for the **specific version** you are using: + +- [PostgreSQL 11](https://www.postgresql.org/docs/11/runtime-config-replication.html) +- [PostgreSQL 12](https://www.postgresql.org/docs/12/runtime-config-replication.html) +- [PostgreSQL 13](https://www.postgresql.org/docs/13/runtime-config-replication.html) + +You then have to compile `lightningd` with PostgreSQL support. + +- Clone or untar a new source tree for `lightning` and `cd` into it. + - You _could_ just use `make clean` on an existing one, but for the avoidance of doubt (and potential bugs in our `Makefile` cleanup rules), just create a fresh source tree. +- `./configure` + - Add any options to `configure` that you normally use as well. +- Double-check the `config.vars` file contains `HAVE_POSTGRES=1`. + - `grep 'HAVE_POSTGRES' config.vars` +- `make` +- If you install `lightningd`, `sudo make install`. + +If you were not using PostgreSQL before but have compiled and used `lightningd` on your system, the resulting `lightningd` will still continue supporting and using your current SQLITE3 database; it just gains the option to use a PostgreSQL database as well. + +If you just want to use PostgreSQL without using a cluster (for example, as an initial test without risking any significant funds), then after setting up a PostgreSQL database, you just need to add +`--wallet=postgres://${USER}:${PASSWORD}@${HOST}:${PORT}/${DB}` to your `lightningd` config or invocation. + +To set up a cluster for a brand new node, follow this (external) [guide by @gabridome](https://github.com/gabridome/docs/blob/master/c-lightning_with_postgresql_reliability.md) + +The above guide assumes you are setting up a new node from scratch. It is also specific to PostgreSQL 12, and setting up for other versions **will** have differences; read the PostgreSQL manuals linked above. + +> 🚧 +> +> If you want to continue a node that started using an SQLITE3 database, note that we do not support this. You should set up a new PostgreSQL node, move funds from the SQLITE3 node to the PostgreSQL node, then shut down the SQLITE3 node permanently. + +There are also more ways to set up PostgreSQL replication. +In general, you should use [synchronous replication](https://www.postgresql.org/docs/13/warm-standby.html#SYNCHRONOUS-REPLICATION), since `lightningd` assumes that once a transaction is committed, it is saved in all permanent storage. This can be difficult to create remote replicas due to the latency. + +## SQLite Litestream Replication + + + +> 🚧 +> +> Previous versions of this document recommended this technique, but we no longer do so. +> According to [issue 4857](https://github.com/ElementsProject/lightning/issues/4857), even with a 60-second timeout that we added in 0.10.2, this leads to constant crashing of `lightningd` in some situations. This section will be removed completely six months after 0.10.3. Consider using `--wallet=sqlite3://${main}:${backup}` above instead. + +One of the simpler things on any system is to use Litestream to replicate the SQLite database. It continuously streams SQLite changes to file or external storage - the cloud storage option should not be used. +Backups/replication should not be on the same disk as the original SQLite DB. + +You need to enable WAL mode on your database. +To do so, first stop `lightningd`, then: + +```shell +$ sqlite3 lightningd.sqlite3 +sqlite3> PRAGMA journal_mode = WAL; +sqlite3> .quit +``` + +Then just restart `lightningd`. + +/etc/litestream.yml : + +```shell +dbs: + - path: /home/bitcoin/.lightning/bitcoin/lightningd.sqlite3 + replicas: + - path: /media/storage/lightning_backup +``` + + and start the service using systemctl: + +```shell +$ sudo systemctl start litestream +``` + +Restore: + +```shell +$ litestream restore -o /media/storage/lightning_backup /home/bitcoin/restore_lightningd.sqlite3 +``` + +Because Litestream only copies small changes and not the entire database (holding a read lock on the file while doing so), the 60-second timeout on locking should not be reached unless something has made your backup medium very very slow. + +Litestream has its own timer, so there is a tiny (but non-negligible) probability that `lightningd` updates the +database, then irrevocably commits to the update by sending revocation keys to the counterparty, and _then_ your main storage media crashes before Litestream can replicate the update. + +Treat this as a superior version of "Database File Backups" section below and prefer recovering via other backup methods first. + + + +## Database File Backups + +> 📘 Who should do this: +> +> Those who already have at least one of the other backup methods, those who are #reckless. + +This is the least desirable backup strategy, as it _can_ lead to loss of all in-channel funds if you use it. +However, having _no_ backup strategy at all _will_ lead to loss of all in-channel funds, so this is still better than nothing. + +This backup method is undesirable, since it cannot recover the following channels: + +- Channels with peers that do not support `option_dataloss_protect`. + - Most nodes on the network already support `option_dataloss_protect` as of November 2020. + - If the peer does not support `option_dataloss_protect`, then the entire channel funds will be revoked by the peer. + - Peers can _claim_ to honestly support this, but later steal funds from you by giving obsolete state when you recover. +- Channels created _after_ the copy was made are not recoverable. + - Data for those channels does not exist in the backup, so your node cannot recover them. + +Because of the above, this strategy is discouraged: you _can_ potentially lose all funds in open channels. + +However, again, note that a "no backups #reckless" strategy leads to _definite_ loss of funds, so you should still prefer _this_ strategy rather than having _no_ backups at all. + +Even if you have one of the better options above, you might still want to do this as a worst-case fallback, as long as you: + +- Attempt to recover using the other backup options above first. Any one of them will be better than this backup option. +- Recover by this method **ONLY** as a **_last_** resort. +- Recover using the most recent backup you can find. Take time to look for the most recent available backup. + +Again, this strategy can lead to only **_partial_** recovery of funds, or even to complete failure to recover, so use the other methods first to recover! + +### Offline Backup + +While `lightningd` is not running, just copy the `lightningd.sqlite3` file in the `$LIGHTNINGDIR` on backup media somewhere. + +To recover, just copy the backed up `lightningd.sqlite3` into your new `$LIGHTNINGDIR` together with the `hsm_secret`. + +You can also use any automated backup system as long as it includes the `lightningd.sqlite3` file (and optionally `hsm_secret`, but note that as a secret key, thieves getting a copy of your backups may allow them to steal your funds, even in-channel funds) and as long as it copies the file while `lightningd` is not running. + +### Backing Up While `lightningd` Is Running + +Since `sqlite3` will be writing to the file while `lightningd` is running, `cp`ing the `lightningd.sqlite3` file while `lightningd` is running may result in the file not being copied properly if `sqlite3` happens to be committing database transactions at that time, potentially leading to a corrupted backup file that cannot be recovered from. + +You have to stop `lightningd` before copying the database to backup in order to ensure that backup files are not corrupted, and in particular, wait for the `lightningd` process to exit. +Obviously, this is disruptive to node operations, so you might prefer to just perform the `cp` even if the backup potentially is corrupted. As long as you maintain multiple backups sampled at different times, this may be more acceptable than stopping and restarting `lightningd`; the corruption only exists in the backup, not in the original file. + +If the filesystem or volume manager containing `$LIGHTNINGDIR` has a snapshot facility, you can take a snapshot of the filesystem, then mount the snapshot, copy `lightningd.sqlite3`, unmount the snapshot, and then delete the snapshot. +Similarly, if the filesystem supports a "reflink" feature, such as `cp -c` on an APFS on MacOS, or `cp --reflink=always` on an XFS or BTRFS on Linux, you can also use that, then copy the reflinked copy to a different storage medium; this is equivalent to a snapshot of a single file. +This _reduces_ but does not _eliminate_ this race condition, so you should still maintain multiple backups. + +You can additionally perform a check of the backup by this command: + +```shell +echo 'PRAGMA integrity_check;' | sqlite3 ${BACKUPFILE} +``` + + + +This will result in the string `ok` being printed if the backup is **likely** not corrupted. +If the result is anything else than `ok`, the backup is definitely corrupted and you should make another copy. + +In order to make a proper uncorrupted backup of the SQLITE3 file while `lightningd` is running, we would need to have `lightningd` perform the backup itself, which, as of the version at the time of this writing, is not yet implemented. + +Even if the backup is not corrupted, take note that this backup strategy should still be a last resort; recovery of all funds is still not assured with this backup strategy. + +`sqlite3` has `.dump` and `VACUUM INTO` commands, but note that those lock the main database for long time periods, which will negatively affect your `lightningd` instance. \ No newline at end of file diff --git a/doc/beginners-guide/beginners-guide.md b/doc/beginners-guide/beginners-guide.md new file mode 100644 index 000000000000..e362605837a7 --- /dev/null +++ b/doc/beginners-guide/beginners-guide.md @@ -0,0 +1,68 @@ +--- +title: "Running your node" +slug: "beginners-guide" +excerpt: "A guide to all the basics you need to get up and running immediately." +hidden: false +createdAt: "2022-11-18T14:27:50.098Z" +updatedAt: "2023-02-21T13:49:20.132Z" +--- +## Starting `lightningd` + +#### Regtest (local, fast-start) option + +If you want to experiment with `lightningd`, there's a script to set up a `bitcoind` regtest test network of two local lightning nodes, which provides a convenient `start_ln` helper. See the notes at the top of the `startup_regtest.sh` file for details on how to use it. + +```bash +. contrib/startup_regtest.sh +``` + +Note that your local nodeset will be much faster/more responsive if you've configured your node to expose the developer options, e.g. + +```bash +./configure --enable-developer +``` + +#### Mainnet Option + +To test with real bitcoin, you will need to have a local `bitcoind` node running: + +```bash +bitcoind -daemon +``` + +Wait until `bitcoind` has synchronized with the network. + +Make sure that you do not have `walletbroadcast=0` in your `~/.bitcoin/bitcoin.conf`, or you may run into trouble. +Notice that running `lightningd` against a pruned node may cause some issues if not managed carefully, see [pruning](doc:bitcoin-core##using-a-pruned-bitcoin-core-node) for more information. + +You can start `lightningd` with the following command: + +```bash +lightningd --network=bitcoin --log-level=debug +``` + +This creates a `.lightning/` subdirectory in your home directory: see `man -l doc/lightningd.8` (or [???](???)) for more runtime options. + +## Using The JSON-RPC Interface + +Core Lightning exposes a [JSON-RPC 2.0](https://www.jsonrpc.org/specification) interface over a Unix Domain socket; the [`lightning-cli`](ref:lightning-cli) tool can be used to access it, or there is a [python client library](???). + +You can use `[lightning-cli](ref:lightning-cli) help` to print a table of RPC methods; `[lightning-cli](lightning-cli) help ` will offer specific information on that command. + +Useful commands: + +- [lightning-newaddr](ref:lightning-newaddr): get a bitcoin address to deposit funds into your lightning node. +- [lightning-listfunds](ref:lightning-listfunds): see where your funds are. +- [lightning-connect](ref:lightning-connect): connect to another lightning node. +- [lightning-fundchannel](ref:lightning-fundchannel): create a channel to another connected node. +- [lightning-invoice](ref:lightning-invoice): create an invoice to get paid by another node. +- [lightning-pay](ref:lightning-pay): pay someone else's invoice. +- [lightning-plugin](ref:lightning-plugin): commands to control extensions. + +## Care And Feeding Of Your New Lightning Node + +Once you've started for the first time, there's a script called `contrib/bootstrap-node.sh` which will connect you to other nodes on the lightning network. + +There are also numerous plugins available for Core Lightning which add capabilities: see the [Plugins](doc:plugins) guide, and check out the plugin collection at: , including [helpme](https://github.com/lightningd/plugins/tree/master/helpme) which guides you through setting up your first channels and customising your node. + +For a less reckless experience, you can encrypt the HD wallet seed: see [HD wallet encryption](doc:securing-keys). diff --git a/doc/beginners-guide/opening-channels.md b/doc/beginners-guide/opening-channels.md new file mode 100644 index 000000000000..4341e2188029 --- /dev/null +++ b/doc/beginners-guide/opening-channels.md @@ -0,0 +1,50 @@ +--- +title: "Opening channels" +slug: "opening-channels" +hidden: false +createdAt: "2022-11-18T16:26:57.798Z" +updatedAt: "2023-01-31T15:07:08.196Z" +--- +First you need to transfer some funds to `lightningd` so that it can open a channel: + +```shell +# Returns an address
+lightning-cli newaddr +``` + + + +`lightningd` will register the funds once the transaction is confirmed. + +You may need to generate a p2sh-segwit address if the faucet does not support bech32: + +```shell +# Return a p2sh-segwit address +lightning-cli newaddr p2sh-segwit +``` + + + +Confirm `lightningd` got funds by: + +```shell +# Returns an array of on-chain funds. +lightning-cli listfunds +``` + + + +Once `lightningd` has funds, we can connect to a node and open a channel. Let's assume the **remote** node is accepting connections at `` (and optional ``, if not 9735) and has the node ID ``: + +```shell +lightning-cli connect [] +lightning-cli fundchannel +``` + + + +This opens a connection and, on top of that connection, then opens a channel. + +The funding transaction needs 3 confirmations in order for the channel to be usable, and 6 to be announced for others to use. + +You can check the status of the channel using `lightning-cli listpeers`, which after 3 confirmations (1 on testnet) should say that `state` is `CHANNELD_NORMAL`; after 6 confirmations you can use `lightning-cli listchannels` to verify that the `public` field is now `true`. \ No newline at end of file diff --git a/doc/beginners-guide/securing-keys.md b/doc/beginners-guide/securing-keys.md new file mode 100644 index 000000000000..468a63608196 --- /dev/null +++ b/doc/beginners-guide/securing-keys.md @@ -0,0 +1,10 @@ +--- +title: "Securing keys" +slug: "securing-keys" +hidden: false +createdAt: "2022-11-18T16:28:08.529Z" +updatedAt: "2023-01-31T13:52:27.300Z" +--- +You can encrypt the `hsm_secret` content (which is used to derive the HD wallet's master key) by passing the `--encrypted-hsm` startup argument, or by using the `hsmtool` (which you can find in the `tool/` directory at the root of [Core Lightning repository](https://github.com/ElementsProject/lightning)) with the `encrypt` method. You can unencrypt an encrypted `hsm_secret` using the `hsmtool` with the `decrypt` method. + +If you encrypt your `hsm_secret`, you will have to pass the `--encrypted-hsm` startup option to `lightningd`. Once your `hsm_secret` is encrypted, you **will not** be able to access your funds without your password, so please beware with your password management. Also, beware of not feeling too safe with an encrypted `hsm_secret`: unlike for `bitcoind` where the wallet encryption can restrict the usage of some RPC command, `lightningd` always needs to access keys from the wallet which is thus **not locked** (yet), even with an encrypted BIP32 master seed. \ No newline at end of file diff --git a/doc/beginners-guide/sending-and-receiving-payments.md b/doc/beginners-guide/sending-and-receiving-payments.md new file mode 100644 index 000000000000..cbc757218705 --- /dev/null +++ b/doc/beginners-guide/sending-and-receiving-payments.md @@ -0,0 +1,24 @@ +--- +title: "Sending and receiving payments" +slug: "sending-and-receiving-payments" +hidden: false +createdAt: "2022-11-18T16:27:07.625Z" +updatedAt: "2023-01-31T15:06:02.214Z" +--- +Payments in Lightning are invoice based. + +The recipient creates an invoice with the expected `` in millisatoshi (or `"any"` for a donation), a unique `