diff --git a/.gitignore b/.gitignore index 34fd49561e33..4d04ad4af7bf 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.gcno *.dSYM *.rej +*.pyc .cppcheck-suppress TAGS tags @@ -34,6 +35,9 @@ gossip_store .pytest_cache tools/headerversions .tmp.lightningrfc/ +contrib/pylightning/build/ +contrib/pylightning/dist/ +contrib/pylightning/pylightning.egg-info/ contrib/compose/groestlcoin_datadir/ contrib/compose/clightning_groestlcoin_datadir/ diff --git a/.travis.yml b/.travis.yml index 8fe51209326a..3d98bcad40df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,36 +1,42 @@ language: c -dist: trusty -sudo: true +dist: xenial +sudo: false notifications: email: false +addons: + apt: + packages: + - python3-pip + - libsqlite3-dev + - shellcheck + - cppcheck + - valgrind + - libomp-dev + - gcc-4.8 + env: - - ARCH=32 SOURCE_CHECK_ONLY=true - ARCH=64 SOURCE_CHECK_ONLY=true CDEBUGFLAGS="-std=gnu11 -g -fstack-protector -O3 -flto" LDFLAGS="-O3 -flto" - - VALGRIND=0 ARCH=32 DEVELOPER=1 COMPILER=gcc TEST_GROUP=1 TEST_GROUP_COUNT=2 SOURCE_CHECK_ONLY=false - - VALGRIND=0 ARCH=32 DEVELOPER=1 COMPILER=gcc TEST_GROUP=2 TEST_GROUP_COUNT=2 SOURCE_CHECK_ONLY=false +# - VALGRIND=0 ARCH=32 DEVELOPER=1 COMPILER=gcc TEST_GROUP=1 TEST_GROUP_COUNT=2 SOURCE_CHECK_ONLY=false +# - VALGRIND=0 ARCH=32 DEVELOPER=1 COMPILER=gcc TEST_GROUP=2 TEST_GROUP_COUNT=2 SOURCE_CHECK_ONLY=false - VALGRIND=0 ARCH=64 DEVELOPER=1 COMPILER=gcc SOURCE_CHECK_ONLY=false - VALGRIND=0 ARCH=64 DEVELOPER=0 COMPILER=gcc COMPAT=0 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=0 COMPILER=gcc TEST_GROUP=1 TEST_GROUP_COUNT=3 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=0 COMPILER=gcc TEST_GROUP=2 TEST_GROUP_COUNT=3 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=0 COMPILER=gcc TEST_GROUP=3 TEST_GROUP_COUNT=3 SOURCE_CHECK_ONLY=false - VALGRIND=0 ARCH=64 DEVELOPER=1 COMPILER=clang SOURCE_CHECK_ONLY=false + - VALGRIND=0 ARCH=64 DEVELOPER=1 COMPILER=gcc-4.8 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=1 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=2 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=3 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=4 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=5 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false - VALGRIND=1 ARCH=64 DEVELOPER=1 COMPILER=gcc TEST_GROUP=6 TEST_GROUP_COUNT=6 SOURCE_CHECK_ONLY=false +cache: + directories: + - dependencies + - external -# Trusty (aka 14.04) is way way too old, so run in docker... script: - - docker pull cdecker/lightning-ci:${ARCH}bit | tee - - env | grep -E '^[A-Z_]+\=' | grep -vi travis | tee /tmp/envlist - - echo SLOW_MACHINE=1 >> /tmp/envlist - - docker run --rm=true --env-file=/tmp/envlist -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit ./configure CC=${COMPILER:-gcc} - - $SOURCE_CHECK_ONLY || docker run --rm=true --env-file=/tmp/envlist -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make -j3 - - $SOURCE_CHECK_ONLY || docker run --rm=true --env-file=/tmp/envlist -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make check - - (! $SOURCE_CHECK_ONLY) || git clone https://github.com/lightningnetwork/lightning-rfc.git - - (! $SOURCE_CHECK_ONLY) || docker run --rm=true -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make clean - - (! $SOURCE_CHECK_ONLY) || docker run --rm=true -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make check-source BOLTDIR=lightning-rfc + .travis/build.sh diff --git a/.travis/build.sh b/.travis/build.sh new file mode 100644 index 000000000000..624030b9c229 --- /dev/null +++ b/.travis/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash -x +set -e + +CWD=$(pwd) +export SLOW_MACHINE=1 +export CC=${COMPILER:-gcc} +export DEVELOPER=${DEVELOPER:-1} +export SOURCE_CHECK_ONLY=${SOURCE_CHECK_ONLY:-"false"} +export COMPAT=${COMPAT:-1} +export PATH=$CWD/dependencies/bin:"$HOME"/.local/bin:"$PATH" + +mkdir -p dependencies/bin || true + +# Download bitcoind and bitcoin-cli +if [ ! -f dependencies/bin/bitcoind ]; then + wget https://bitcoin.org/bin/bitcoin-core-0.17.1/bitcoin-0.17.1-x86_64-linux-gnu.tar.gz + tar -xzf bitcoin-0.17.1-x86_64-linux-gnu.tar.gz + mv bitcoin-0.17.1/bin/* dependencies/bin + rm -rf bitcoin-0.17.1-x86_64-linux-gnu.tar.gz bitcoin-0.17.1 +fi + +pyenv global 3.7 +pip3 install --user --quiet -r tests/requirements.txt +pip3 install --quiet \ + pytest-test-groups==1.0.3 + +echo "Configuration which is going to be built:" +echo -en 'travis_fold:start:script.1\\r' +./configure CC="$CC" +cat config.vars +echo -en 'travis_fold:end:script.1\\r' + +if [ "$SOURCE_CHECK_ONLY" == "false" ]; then + echo -en 'travis_fold:start:script.2\\r' + make -j3 > /dev/null + echo -en 'travis_fold:end:script.2\\r' + + echo -en 'travis_fold:start:script.3\\r' + make check + echo -en 'travis_fold:end:script.3\\r' +else + git clone https://github.com/lightningnetwork/lightning-rfc.git + make check-source BOLTDIR=lightning-rfc +fi diff --git a/CHANGELOG.md b/CHANGELOG.md index f0606435b25e..ed64372fddf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,39 +1,84 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.7.0] - 2019-02-28: "Actually an Altcoin" +This release named by Mark Beckwith @wythe. ### Added - plugins: fully enabled, and ready for you to write some! - plugins: `pay` is now a plugin. -- lightning-cli: `help ` finds man pages even if `make install` not run. +- protocol: `pay` will now use routehints in invoices if it needs to. +- build: reproducible source zipfile and Ubuntu 18.04.1 build. +- JSON API: New command `paystatus` gives detailed information on `pay` commands. +- JSON API: `getroute`, `invoice`, `sendpay` and `pay` commands `msatoshi` + parameter can have suffixes `msat`, `sat` (optionally with 3 decimals) or `grs` + (with 1 to 11 decimal places). +- JSON API: `fundchannel` and `withdraw` commands `satoshi` + parameter can have suffixes `msat` (must end in `000`), `sat` or `grs` + (with 1 to 8 decimal places). +- JSON API: `decodepay`, `getroute`, `sendpay`, `pay`, `listpeers`, `listfunds`, `listchannels` and + all invoice commands now return an `amount_msat` field which has an `msat` suffix. +- JSON API: `listfunds` `channels` now has `_msat` fields for each existing raw amount field, with `msat` suffix. - JSON API: `waitsendpay` now has an `erring_direction` field. - JSON API: `listpeers` now has a `direction` field in `channels`. - JSON API: `listchannels` now takes a `source` option to filter by node id. -- JSON API: New command `paystatus` gives detailed information on `pay` commands. +- JSON API: `getroute` `riskfactor` argument is simplified; `pay` now defaults to setting it to 10. +- JSON API: `sendpay` now takes a `bolt11` field, and it's returned in `listpayments` and `waitsendpay`. +- JSON API: `fundchannel` and `withdraw` now have a new parameter `minconf` that limits coinselection to outputs that have at least `minconf` confirmations (default 1). (#2380) +- JSON API: `listfunds` now displays addresses for all outputs owned by the wallet (#2387) +- JSON API: `waitsendpay` and `sendpay` output field `label` as specified by `sendpay` call. +- JSON API: `listpays` command for higher-level payment view than `listpayments`, especially important with multi-part-payments coming. +- JSON API: `listpayments` is now `listsendpays`. +- lightning-cli: `help ` finds man pages even if `make install` not run. +- pylightning: New class 'Millisatoshi' can be used for JSON API, and new '_msat' fields are turned into this on reading. ### Changed -- The `short_channel_id` separator has been changed to be `x` to match the specification. +- protocol: `option_data_loss_protect` is now enabled by default. +- JSON API: The `short_channel_id` separator has been changed to be `x` to match the specification. - JSON API: `listpeers` now includes `funding_allocation_msat`, which returns a map of the amounts initially funded to the channel by each peer, indexed by channel id. -- `option_data_loss_protect` is now enabled by default. +- JSON API: `help` with a `command` argument gives a JSON array, like other commands. +- JSON API: `sendpay` `description` parameter is renamed `label`. +- JSON API: `pay` now takes an optional `label` parameter for labelling payments, in place of never-used `description`. +- build: we'll use the system libbase58 and libsodium if found suitable. ### Deprecated Note: You should always set `allow-deprecated-apis=false` to test for changes. +We recommend that you transition to the reading the new JSON `_msat` +fields for your own sanity checking, and that you similarly +provide appropriate suffixes for JSON input fields. + +- JSON API: `short_channel_id` fields in JSON commands with `:` separators (use `x` instead). +- JSON API: `pay` `description` is deprecated, as is support for BOLT11 strings using `h`. +- JSON API: `sendpay` parameter `description` and `waitsendpay` and `sendpay` output fields `description` (now `label`). +- JSON API: `listpayments` has been deprecated (you probably want `listpays`) + ### Removed +- JSON API: the `waitsendpay` command error return no longer includes `channel_update` + ### Fixed - Protocol: handling `query_channel_range` for large numbers of blocks (eg. 4 billion) was slow due to a bug. +- Fixed occasional deadlock with peers when exchanging huge amounts of gossip. +- Fixed a crash when running in daemon-mode due to db filename overrun (#2348) +- Handle lnd sending premature 'funding_locked' message when we're expected 'reestablish'; + we used to close channel if this happened. +- Cleanup peers that started opening a channel, but then disconnected. These + would leave a dangling entry in the DB that would cause this peer to be + unable to connect. (PR #2371) +- You can no longer make giant unpayable "wumbo" invoices. +- CLTV of total route now correctly evaluated when finding best route. +- `riskfactor` arguments to `pay` and `getroute` now have an effect. ### Security @@ -79,7 +124,7 @@ changes. - Protocol: don't send invalid onion errors if peer says onion was bad. - Protocol: don't crash when peer sends a 0-block-expiry HTLC. - pylightning: handle multiple simultanous RPC replies reliably. - +- build: we use `--prefix` as handed to `./configure` ### Security @@ -267,7 +312,7 @@ There predate the BOLT specifications, and are only of vague historic interest: 6. [0.5.1] - 2016-10-21 7. [0.5.2] - 2016-11-21: "Bitcoin Savings & Trust Daily Interest II" -[Unreleased]: https://github.com/ElementsProject/lightning/compare/v0.6.3...HEAD +[0.7.0]: https://github.com/ElementsProject/lightning/compare/v0.6.3...HEAD [0.6.3]: https://github.com/ElementsProject/lightning/releases/tag/v0.6.3 [0.6.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.6.2 [0.6.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.6.1 diff --git a/Dockerfile b/Dockerfile index a2edda4903d8..6ba91bc70f27 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,8 @@ VOLUME [ "/root/.lightning" ] COPY --from=builder /opt/lightningd/cli/lightning-cli /usr/bin COPY --from=builder /opt/lightningd/lightningd/lightning* /usr/bin/ -COPY --from=builder /opt/groestlcoin /usr/bin +COPY --from=builder /opt/lightningd/plugins/pay /usr/libexec/c-lightning/plugins/ +COPY --from=builder /opt/groestlcoin/bin /usr/bin COPY tools/docker-entrypoint.sh entrypoint.sh EXPOSE 9735 9835 diff --git a/Makefile b/Makefile index 17cad95ea5aa..9bd1c650a18c 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,12 @@ VG=valgrind -q --error-exitcode=7 VG_TEST_ARGS = --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all endif +ifneq ($(ASAN),0) +SANITIZER_FLAGS=-fsanitize=address +else +SANITIZER_FLAGS= +endif + ifeq ($(DEVELOPER),1) DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1 else @@ -46,7 +52,7 @@ endif # Timeout shortly before the 600 second travis silence timeout # (method=thread to support xdist) -PYTEST_OPTS := -v --timeout=550 --timeout_method=thread +PYTEST_OPTS := -v --timeout=550 --timeout_method=thread -p no:logging # This is where we add new features as bitcoin adds them. FEATURES := @@ -90,6 +96,7 @@ CCAN_OBJS := \ ccan-str-base32.o \ ccan-str-hex.o \ ccan-str.o \ + ccan-strmap.o \ ccan-take.o \ ccan-tal-grab_file.o \ ccan-tal-link.o \ @@ -179,13 +186,13 @@ ALL_PROGRAMS = CPPFLAGS = -DBINTOPKGLIBEXECDIR='"'$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))'"' CWARNFLAGS := -Werror -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -CDEBUGFLAGS := -std=gnu11 -g -fstack-protector +CDEBUGFLAGS := -std=gnu11 -g -fstack-protector $(SANITIZER_FLAGS) CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I/usr/local/include $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) # We can get configurator to run a different compile cmd to cross-configure. CONFIGURATOR_CC := $(CC) -LDFLAGS = $(PIE_LDFLAGS) +LDFLAGS = $(PIE_LDFLAGS) $(SANITIZER_FLAGS) ifeq ($(STATIC),1) LDLIBS = -L/usr/local/lib -Wl,-dn -lgmp -lsqlite3 -lz -Wl,-dy -lm -lpthread -ldl $(COVFLAGS) else @@ -295,6 +302,8 @@ check-python: @# W503: line break before binary operator @flake8 --ignore=E501,E731,W503 --exclude=contrib/pylightning/lightning/__init__.py ${PYSRC} + PYTHONPATH=contrib/pylightning:$$PYTHONPATH $(PYTEST) contrib/pylightning/ + check-includes: @tools/check-includes.sh @@ -303,7 +312,7 @@ check-includes: @git ls-files -- "*.c" "*.h" | grep -vE '^ccan/' | xargs grep -n 'list_for_each' | sed 's/\([^:]*:.*\):.*/uninitvar:\1/' > $@ check-cppcheck: .cppcheck-suppress - @trap 'rm -f .cppcheck-suppress' 0; git ls-files -- "*.c" "*.h" | grep -vE '^ccan/' | xargs cppcheck -q --language=c --std=c11 --error-exitcode=1 --suppressions-list=.cppcheck-suppress + @trap 'rm -f .cppcheck-suppress' 0; git ls-files -- "*.c" "*.h" | grep -vE '^ccan/' | xargs cppcheck -q --language=c --std=c11 --error-exitcode=1 --suppressions-list=.cppcheck-suppress --inline-suppr check-shellcheck: @git ls-files -- "*.sh" | xargs shellcheck @@ -317,7 +326,13 @@ check-tmpctx: check-discouraged-functions: @if git grep -E "[^a-z_/](fgets|fputs|gets|scanf|sprintf)\(" -- "*.c" "*.h" ":(exclude)ccan/"; then exit 1; fi -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 +# Don't access amount_msat and amount_sat members directly without a good reason +# since it risks overflow. +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 full-check: check check-source @@ -421,13 +436,12 @@ unittest/%: % $(VG) $(VG_TEST_ARGS) $* > /dev/null # Installation directories -prefix = /usr/local -exec_prefix = $(prefix) +exec_prefix = $(PREFIX) bindir = $(exec_prefix)/bin libexecdir = $(exec_prefix)/libexec pkglibexecdir = $(libexecdir)/$(PKGNAME) plugindir = $(pkglibexecdir)/plugins -datadir = $(prefix)/share +datadir = $(PREFIX)/share docdir = $(datadir)/doc/$(PKGNAME) mandir = $(datadir)/man man1dir = $(mandir)/man1 @@ -475,7 +489,7 @@ PKGLIBEXEC_PROGRAMS = \ lightningd/lightning_openingd PLUGINS=plugins/pay -install-program: installdirs $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) +install-program: installdirs $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) @$(NORMAL_INSTALL) $(INSTALL_PROGRAM) $(BIN_PROGRAMS) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) $(PKGLIBEXEC_PROGRAMS) $(DESTDIR)$(pkglibexecdir) diff --git a/README.md b/README.md index c73fda98fff0..788bc037adf6 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ http://lightning.network. [![Build Status][travis-ci]][travis-ci-link] [![Pull Requests Welcome][prs]][prs-link] [![Irc][IRC]][IRC-link] +[![Documentation Status](https://readthedocs.org/projects/lightning/badge/?version=docs)](https://lightning.readthedocs.io/) [travis-ci]: https://travis-ci.org/ElementsProject/lightning.svg?branch=master [travis-ci-link]: https://travis-ci.org/ElementsProject/lightning @@ -58,7 +59,7 @@ For the impatient here's the gist of it for Ubuntu and Debian: sudo apt-get update sudo apt-get install -y \ autoconf automake build-essential git libtool libgmp-dev \ - libsqlite3-dev python python3 net-tools zlib1g-dev + libsqlite3-dev python python3 net-tools zlib1g-dev libsodium-dev git clone https://github.com/Groestlcoin/lightning.git cd lightning ./configure @@ -99,18 +100,6 @@ open a channel: `lightningd` will register the funds once the transaction is confirmed. -If you don't have any testcoins you can get a few from a faucet such as -[Kiwi's testnet faucet][kiw]. -You can send it directly to the `lightningd` address. - -You may need to generate a p2sh-segwit address if the faucet does not support -bech32: - - # Return a p2sh-segwit address - cli/lightning-cli newaddr p2sh-segwit - -[kiw]: https://testnet.manu.backend.hamburg/faucet - Confirm `lightningd` got funds by: # Returns an array of on-chain funds. @@ -225,7 +214,7 @@ JSON-RPC interface is documented in the following manual pages: * [getroute](doc/lightning-getroute.7.txt) * [sendpay](doc/lightning-sendpay.7.txt) * [pay](doc/lightning-pay.7.txt) -* [listpayments](doc/lightning-listpayments.7.txt) +* [listpays](doc/lightning-listpays.7.txt) * [decodepay](doc/lightning-decodepay.7.txt) For simple access to the JSON-RPC interface you can use the diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 95633a88bd04..a7734a0773da 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -10,16 +10,16 @@ const struct chainparams networks[] = { .rpc_port = 1441, .cli = "groestlcoin-cli", .cli_args = NULL, - .dust_limit = 546, + .dust_limit = { 546 }, /* BOLT #2: * * The sending node: *... * - MUST set `funding_satoshis` to less than 2^24 satoshi. */ - .max_funding_satoshi = (1 << 24) - 1, - .max_payment_msat = 0xFFFFFFFFULL, - /* "Lightning Charge Powers Developers" */ + .max_funding = AMOUNT_SAT_INIT((1 << 24) - 1), + .max_payment = AMOUNT_MSAT_INIT(0xFFFFFFFFULL), + /* "Lightning Charge Powers Developers & Blockstream Store" */ .when_lightning_became_cool = 504500, .testnet = false}, {.network_name = "regtest", @@ -28,9 +28,9 @@ const struct chainparams networks[] = { .rpc_port = 18443, .cli = "groestlcoin-cli", .cli_args = "-regtest", - .dust_limit = 546, - .max_funding_satoshi = (1 << 24) - 1, - .max_payment_msat = 0xFFFFFFFFULL, + .dust_limit = { 546 }, + .max_funding = AMOUNT_SAT_INIT((1 << 24) - 1), + .max_payment = AMOUNT_MSAT_INIT(0xFFFFFFFFULL), .when_lightning_became_cool = 1, .testnet = true}, {.network_name = "testnet", @@ -39,11 +39,11 @@ const struct chainparams networks[] = { .rpc_port = 17766, .cli = "groestlcoin-cli", .cli_args = "-testnet", - .dust_limit = 546, - .max_funding_satoshi = (1 << 24) - 1, - .max_payment_msat = 0xFFFFFFFFULL, + .dust_limit = { 546 }, + .max_funding = AMOUNT_SAT_INIT((1 << 24) - 1), + .max_payment = AMOUNT_MSAT_INIT(0xFFFFFFFFULL), .when_lightning_became_cool = 1, - .testnet = true} + .testnet = true} }; const struct chainparams *chainparams_for_network(const char *network_name) diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index 44e762101747..715e37b527d8 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -4,6 +4,7 @@ #include "config.h" #include #include +#include #include struct chainparams { @@ -13,9 +14,9 @@ struct chainparams { const int rpc_port; const char *cli; const char *cli_args; - const u64 dust_limit; - const u64 max_funding_satoshi; - const u64 max_payment_msat; + const struct amount_sat dust_limit; + const struct amount_sat max_funding; + const struct amount_msat max_payment; const u32 when_lightning_became_cool; /* Whether this is a test network or not */ diff --git a/bitcoin/pullpush.c b/bitcoin/pullpush.c index c5e652ae757e..baa94d501fa3 100644 --- a/bitcoin/pullpush.c +++ b/bitcoin/pullpush.c @@ -26,6 +26,12 @@ void push_le64(u64 v, push(&l, sizeof(l), pushp); } +void push_amount_sat(struct amount_sat v, + void (*push)(const void *, size_t, void *), void *pushp) +{ + push_le64(v.satoshis, push, pushp); /* Raw: low-level helper */ +} + void push_varint_blob(const tal_t *blob, void (*push)(const void *, size_t, void *), void *pushp) diff --git a/bitcoin/pullpush.h b/bitcoin/pullpush.h index 9e9fa22a2a7f..268125abac71 100644 --- a/bitcoin/pullpush.h +++ b/bitcoin/pullpush.h @@ -3,11 +3,14 @@ #include "config.h" #include "bitcoin/varint.h" #include +#include void push_varint(varint_t v, void (*push)(const void *, size_t, void *), void *pushp); void push_le32(u32 v, void (*push)(const void *, size_t, void *), void *pushp); void push_le64(u64 v, void (*push)(const void *, size_t, void *), void *pushp); +void push_amount_sat(struct amount_sat v, + void (*push)(const void *, size_t, void *), void *pushp); void push_varint_blob(const tal_t *blob, void (*push)(const void *, size_t, void *), void *pushp); diff --git a/bitcoin/short_channel_id.c b/bitcoin/short_channel_id.c index 90432afa150f..ee3d2e833fea 100644 --- a/bitcoin/short_channel_id.c +++ b/bitcoin/short_channel_id.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -36,7 +37,8 @@ bool mk_short_channel_id(struct short_channel_id *scid, } bool short_channel_id_from_str(const char *str, size_t strlen, - struct short_channel_id *dst) + struct short_channel_id *dst, + bool may_be_deprecated_form) { u32 blocknum, txnum; u16 outnum; @@ -48,7 +50,7 @@ bool short_channel_id_from_str(const char *str, size_t strlen, #ifdef COMPAT_V062 /* Pre-adelaide format vs. post-adelaide format */ - if (strchr(buf, ':')) + if (may_be_deprecated_form && strchr(buf, ':')) matches = sscanf(buf, "%u:%u:%hu", &blocknum, &txnum, &outnum); else matches = sscanf(buf, "%ux%ux%hu", &blocknum, &txnum, &outnum); @@ -68,12 +70,14 @@ char *short_channel_id_to_str(const tal_t *ctx, const struct short_channel_id *s } bool short_channel_id_dir_from_str(const char *str, size_t strlen, - struct short_channel_id_dir *scidd) + struct short_channel_id_dir *scidd, + bool may_be_deprecated_form) { const char *slash = memchr(str, '/', strlen); if (!slash || slash + 2 != str + strlen) return false; - if (!short_channel_id_from_str(str, slash - str, &scidd->scid)) + if (!short_channel_id_from_str(str, slash - str, &scidd->scid, + may_be_deprecated_form)) return false; if (slash[1] == '0') scidd->dir = 0; @@ -92,3 +96,7 @@ char *short_channel_id_dir_to_str(const tal_t *ctx, tal_free(scidstr); return str; } + +REGISTER_TYPE_TO_STRING(short_channel_id, short_channel_id_to_str); +REGISTER_TYPE_TO_STRING(short_channel_id_dir, short_channel_id_dir_to_str); + diff --git a/bitcoin/short_channel_id.h b/bitcoin/short_channel_id.h index bcf2ef38d009..4e0a25441570 100644 --- a/bitcoin/short_channel_id.h +++ b/bitcoin/short_channel_id.h @@ -51,13 +51,16 @@ static inline u16 short_channel_id_outnum(const struct short_channel_id *scid) bool WARN_UNUSED_RESULT mk_short_channel_id(struct short_channel_id *scid, u64 blocknum, u64 txnum, u64 outnum); +/* may_be_deprecated_form allows : separators if COMPAT defined */ bool WARN_UNUSED_RESULT short_channel_id_from_str(const char *str, size_t strlen, - struct short_channel_id *dst); + struct short_channel_id *dst, + bool may_be_deprecated_form); char *short_channel_id_to_str(const tal_t *ctx, const struct short_channel_id *scid); bool WARN_UNUSED_RESULT short_channel_id_dir_from_str(const char *str, size_t strlen, - struct short_channel_id_dir *scidd); + struct short_channel_id_dir *scidd, + bool may_be_deprecated_form); char *short_channel_id_dir_to_str(const tal_t *ctx, const struct short_channel_id_dir *scidd); diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 00a6b97991e8..2139c9fd6f4d 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -28,7 +28,7 @@ static void push_tx_input(const struct bitcoin_tx_input *input, static void push_tx_output(const struct bitcoin_tx_output *output, void (*push)(const void *, size_t, void *), void *pushp) { - push_le64(output->amount, push, pushp); + push_amount_sat(output->amount, push, pushp); push_varint_blob(output->script, push, pushp); } @@ -204,7 +204,7 @@ static void hash_outputs(struct sha256_double *h, const struct bitcoin_tx *tx, if (sighash_single(sighash_type) && i != input_num) continue; - push_le64(tx->output[i].amount, push_sha, &ctx); + push_amount_sat(tx->output[i].amount, push_sha, &ctx); push_varint_blob(tx->output[i].script, push_sha, &ctx); } @@ -243,7 +243,7 @@ static void hash_for_segwit(struct sha256_ctx *ctx, push_varint_blob(witness_script, push_sha, ctx); /* 6. value of the output spent by this input (8-byte little end) */ - push_le64(*tx->input[input_num].amount, push_sha, ctx); + push_amount_sat(*tx->input[input_num].amount, push_sha, ctx); /* 7. nSequence of the input (4-byte little endian) */ push_le32(tx->input[input_num].sequence_number, push_sha, ctx); @@ -362,6 +362,14 @@ static u64 pull_value(const u8 **cursor, size_t *max) return amount; } +static struct amount_sat pull_amount_sat(const u8 **cursor, size_t *max) +{ + struct amount_sat sat; + + sat.satoshis = pull_value(cursor, max); /* Raw: low-level helper */ + return sat; +} + /* Pulls a varint which specifies n items of mult size: ensures basic * sanity to avoid trivial OOM */ static u64 pull_length(const u8 **cursor, size_t *max, size_t mult) @@ -393,7 +401,7 @@ static void pull_input(const tal_t *ctx, const u8 **cursor, size_t *max, static void pull_output(const tal_t *ctx, const u8 **cursor, size_t *max, struct bitcoin_tx_output *output) { - output->amount = pull_value(cursor, max); + output->amount = pull_amount_sat(cursor, max); output->script = tal_arr(ctx, u8, pull_length(cursor, max, 1)); pull(cursor, max, output->script, tal_count(output->script)); } diff --git a/bitcoin/tx.h b/bitcoin/tx.h index d00b7e850d3d..65383de95f50 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -7,6 +7,7 @@ #include #include #include +#include struct bitcoin_txid { struct sha256_double shad; @@ -22,7 +23,7 @@ struct bitcoin_tx { }; struct bitcoin_tx_output { - u64 amount; + struct amount_sat amount; u8 *script; }; @@ -33,7 +34,7 @@ struct bitcoin_tx_input { u32 sequence_number; /* Value of the output we're spending (NULL if unknown). */ - u64 *amount; + struct amount_sat *amount; /* Only if BIP141 used. */ u8 **witness; diff --git a/ccan/README b/ccan/README index a5fc504def15..cc1742c3faf3 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2456-g9d2d2c49 +CCAN version: init-2458-g8cc0749a diff --git a/ccan/ccan/pipecmd/pipecmd.c b/ccan/ccan/pipecmd/pipecmd.c index c2b514df5e97..d45713b6f801 100644 --- a/ccan/ccan/pipecmd/pipecmd.c +++ b/ccan/ccan/pipecmd/pipecmd.c @@ -47,68 +47,76 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, char *const *arr) { int tochild[2], fromchild[2], errfromchild[2], execfail[2]; + /* fds for parent to close */ + int par_close[4], num_par_close = 0; + /* fds for child to close */ + int child_close[4], num_child_close = 0; pid_t childpid; int err; if (fd_tochild) { if (fd_tochild == &pipecmd_preserve) { tochild[0] = STDIN_FILENO; - fd_tochild = NULL; - } else if (pipe(tochild) != 0) + } else if (pipe(tochild) == 0) { + par_close[num_par_close++] = tochild[0]; + child_close[num_child_close++] = tochild[1]; + } else goto fail; } else { tochild[0] = open("/dev/null", O_RDONLY); if (tochild[0] < 0) goto fail; + par_close[num_par_close++] = tochild[0]; } if (fd_fromchild) { if (fd_fromchild == &pipecmd_preserve) { fromchild[1] = STDOUT_FILENO; - fd_fromchild = NULL; - } else if (pipe(fromchild) != 0) - goto close_tochild_fail; + } else if (pipe(fromchild) == 0) { + par_close[num_par_close++] = fromchild[1]; + child_close[num_child_close++] = fromchild[0]; + } else + goto fail; } else { fromchild[1] = open("/dev/null", O_WRONLY); if (fromchild[1] < 0) - goto close_tochild_fail; + goto fail; + par_close[num_par_close++] = fromchild[1]; } if (fd_errfromchild) { if (fd_errfromchild == &pipecmd_preserve) { errfromchild[1] = STDERR_FILENO; - fd_errfromchild = NULL; } else if (fd_errfromchild == fd_fromchild) { errfromchild[0] = fromchild[0]; errfromchild[1] = fromchild[1]; - } else { - if (pipe(errfromchild) != 0) - goto close_fromchild_fail; - } + } else if (pipe(errfromchild) == 0) { + par_close[num_par_close++] = errfromchild[1]; + child_close[num_child_close++] = errfromchild[0]; + } else + goto fail; } else { errfromchild[1] = open("/dev/null", O_WRONLY); if (errfromchild[1] < 0) - goto close_fromchild_fail; + goto fail; + par_close[num_par_close++] = errfromchild[1]; } if (pipe(execfail) != 0) - goto close_errfromchild_fail; + goto fail; + + par_close[num_par_close++] = execfail[1]; + child_close[num_child_close++] = execfail[0]; if (fcntl(execfail[1], F_SETFD, fcntl(execfail[1], F_GETFD) | FD_CLOEXEC) < 0) - goto close_execfail_fail; + goto fail; childpid = fork(); if (childpid < 0) - goto close_execfail_fail; + goto fail; if (childpid == 0) { - if (fd_tochild) - close(tochild[1]); - if (fd_fromchild) - close(fromchild[0]); - if (fd_errfromchild && fd_errfromchild != fd_fromchild) - close(errfromchild[0]); - - close(execfail[0]); + for (int i = 0; i < num_child_close; i++) + close(child_close[i]); // Child runs command. if (tochild[0] != STDIN_FILENO) { @@ -140,10 +148,9 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, exit(127); } - close(tochild[0]); - close(fromchild[1]); - close(errfromchild[1]); - close(execfail[1]); + for (int i = 0; i < num_par_close; i++) + close(par_close[i]); + /* Child will close this without writing on successful exec. */ if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) { close(execfail[0]); @@ -152,30 +159,17 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, return -1; } close(execfail[0]); - if (fd_tochild) + if (fd_tochild && fd_tochild != &pipecmd_preserve) *fd_tochild = tochild[1]; - if (fd_fromchild) + if (fd_fromchild && fd_fromchild != &pipecmd_preserve) *fd_fromchild = fromchild[0]; - if (fd_errfromchild) + if (fd_errfromchild && fd_errfromchild != &pipecmd_preserve) *fd_errfromchild = errfromchild[0]; return childpid; -close_execfail_fail: - close_noerr(execfail[0]); - close_noerr(execfail[1]); -close_errfromchild_fail: - if (fd_errfromchild) - close_noerr(errfromchild[0]); - close_noerr(errfromchild[1]); -close_fromchild_fail: - if (fd_fromchild) - close_noerr(fromchild[0]); - close_noerr(fromchild[1]); -close_tochild_fail: - close_noerr(tochild[0]); - if (fd_tochild) - close_noerr(tochild[1]); fail: + for (int i = 0; i < num_par_close; i++) + close_noerr(par_close[i]); return -1; } diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 1386fc90c8d7..f15f34483c36 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -888,7 +888,8 @@ static void read_tests(size_t num_tests) { while (read_test(tests + num_tests)) { num_tests++; - tests = realloc(tests, num_tests * sizeof(tests[0])); + tests = realloc(tests, (num_tests + 1) * sizeof(tests[0])); + tests[num_tests].name = NULL; } } diff --git a/channeld/Makefile b/channeld/Makefile index f15bca3af85b..f96c23d17608 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -33,8 +33,9 @@ ALL_GEN_HEADERS += $(LIGHTNINGD_CHANNEL_HEADERS_GEN) # Common source we use. CHANNELD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ - common/bip32.o \ + common/bip32.o \ common/channel_config.o \ common/crypto_state.o \ common/crypto_sync.o \ diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index 8c0fdb80b142..2cc52657502f 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -7,7 +7,7 @@ channel_init,1000 channel_init,,chain_hash,struct bitcoin_blkid channel_init,,funding_txid,struct bitcoin_txid channel_init,,funding_txout,u16 -channel_init,,funding_satoshi,u64 +channel_init,,funding_satoshi,struct amount_sat channel_init,,our_config,struct channel_config channel_init,,their_config,struct channel_config # FIXME: Fix generate-wire.py to allow NUM_SIDES*u32 here. @@ -23,7 +23,7 @@ channel_init,,old_remote_per_commit,struct pubkey channel_init,,funder,enum side channel_init,,fee_base,u32 channel_init,,fee_proportional,u32 -channel_init,,local_msatoshi,u64 +channel_init,,local_msatoshi,struct amount_msat channel_init,,our_basepoints,struct basepoints channel_init,,our_funding_pubkey,struct pubkey channel_init,,local_node_id,struct pubkey @@ -69,7 +69,7 @@ channel_funding_locked,,depth,u32 # Tell channel to offer this htlc channel_offer_htlc,1004 -channel_offer_htlc,,amount_msat,u64 +channel_offer_htlc,,amount_msat,struct amount_msat channel_offer_htlc,,cltv_expiry,u32 channel_offer_htlc,,payment_hash,struct sha256 channel_offer_htlc,,onion_routing_packet,1366*u8 diff --git a/channeld/channeld.c b/channeld/channeld.c index 03e26ce12724..160588e8c096 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -232,22 +232,45 @@ static const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) * capacity minus the cumulative reserve. * FIXME: does this need fuzz? */ -static u64 advertised_htlc_max(const struct channel *channel) +static struct amount_msat advertised_htlc_max(const struct channel *channel) { - u64 lower_bound; - u64 cumulative_reserve_msat; + struct amount_sat cumulative_reserve, lower_bound; + struct amount_msat lower_bound_msat; - cumulative_reserve_msat = - (channel->config[LOCAL].channel_reserve_satoshis + - channel->config[REMOTE].channel_reserve_satoshis) * 1000; + /* This shouldn't fail */ + if (!amount_sat_add(&cumulative_reserve, + channel->config[LOCAL].channel_reserve, + channel->config[REMOTE].channel_reserve)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "reserve overflow: local %s + remote %s", + type_to_string(tmpctx, struct amount_sat, + &channel->config[LOCAL].channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &channel->config[REMOTE].channel_reserve)); + } + + /* This shouldn't fail either */ + if (!amount_sat_sub(&lower_bound, channel->funding, cumulative_reserve)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "funding %s - cumulative_reserve %s?", + type_to_string(tmpctx, struct amount_sat, + &channel->funding), + type_to_string(tmpctx, struct amount_sat, + &cumulative_reserve)); + } - lower_bound = channel->config[REMOTE].max_htlc_value_in_flight_msat; - if (channel->funding_msat - cumulative_reserve_msat < lower_bound) - lower_bound = channel->funding_msat - cumulative_reserve_msat; + if (!amount_sat_to_msat(&lower_bound_msat, lower_bound)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "lower_bound %s invalid?", + type_to_string(tmpctx, struct amount_sat, + &lower_bound)); + } /* FIXME BOLT QUOTE: https://github.com/lightningnetwork/lightning-rfc/pull/512 once merged */ - if (channel->chainparams->max_payment_msat < lower_bound) - lower_bound = channel->chainparams->max_payment_msat; - return lower_bound; + if (amount_msat_greater(lower_bound_msat, + channel->chainparams->max_payment)) + lower_bound_msat = channel->chainparams->max_payment; + + return lower_bound_msat; } /* Create and send channel_update to gossipd (and maybe peer) */ @@ -268,7 +291,7 @@ static void send_channel_update(struct peer *peer, int disable_flag) disable_flag == ROUTING_FLAGS_DISABLED, peer->cltv_delta, - peer->channel->config[REMOTE].htlc_minimum_msat, + peer->channel->config[REMOTE].htlc_minimum, peer->fee_base, peer->fee_per_satoshi, advertised_htlc_max(peer->channel)); @@ -293,7 +316,7 @@ static void make_channel_local_active(struct peer *peer) msg = towire_gossipd_local_add_channel(NULL, &peer->short_channel_ids[LOCAL], &peer->node_ids[REMOTE], - peer->channel->funding_msat / 1000); + peer->channel->funding); wire_sync_write(GOSSIP_FD, take(msg)); /* Tell gossipd and the other side what parameters we expect should @@ -566,21 +589,21 @@ static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) { struct channel_id channel_id; u64 id; - u64 amount_msat; + struct amount_msat amount; u32 cltv_expiry; struct sha256 payment_hash; u8 onion_routing_packet[TOTAL_PACKET_SIZE]; enum channel_add_err add_err; struct htlc *htlc; - if (!fromwire_update_add_htlc(msg, &channel_id, &id, &amount_msat, + if (!fromwire_update_add_htlc(msg, &channel_id, &id, &amount, &payment_hash, &cltv_expiry, onion_routing_packet)) peer_failed(&peer->cs, &peer->channel_id, "Bad peer_add_htlc %s", tal_hex(msg, msg)); - add_err = channel_add_htlc(peer->channel, REMOTE, id, amount_msat, + add_err = channel_add_htlc(peer->channel, REMOTE, id, amount, cltv_expiry, &payment_hash, onion_routing_packet, &htlc); if (add_err != CHANNEL_ERR_ADD_OK) @@ -843,12 +866,12 @@ static u8 *make_failmsg(const tal_t *ctx, goto done; case WIRE_AMOUNT_BELOW_MINIMUM: channel_update = foreign_channel_update(ctx, peer, scid); - msg = towire_amount_below_minimum(ctx, htlc->msatoshi, + msg = towire_amount_below_minimum(ctx, htlc->amount, channel_update); goto done; case WIRE_FEE_INSUFFICIENT: channel_update = foreign_channel_update(ctx, peer, scid); - msg = towire_fee_insufficient(ctx, htlc->msatoshi, + msg = towire_fee_insufficient(ctx, htlc->amount, channel_update); goto done; case WIRE_INCORRECT_CLTV_EXPIRY: @@ -865,7 +888,7 @@ static u8 *make_failmsg(const tal_t *ctx, goto done; case WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS: msg = towire_incorrect_or_unknown_payment_details( - ctx, htlc->msatoshi); + ctx, htlc->amount); goto done; case WIRE_FINAL_EXPIRY_TOO_SOON: msg = towire_final_expiry_too_soon(ctx); @@ -874,7 +897,7 @@ static u8 *make_failmsg(const tal_t *ctx, msg = towire_final_incorrect_cltv_expiry(ctx, cltv_expiry); goto done; case WIRE_FINAL_INCORRECT_HTLC_AMOUNT: - msg = towire_final_incorrect_htlc_amount(ctx, htlc->msatoshi); + msg = towire_final_incorrect_htlc_amount(ctx, htlc->amount); goto done; case WIRE_INVALID_ONION_VERSION: msg = towire_invalid_onion_version(ctx, sha256); @@ -1228,7 +1251,7 @@ static u8 *got_commitsig_msg(const tal_t *ctx, struct secret s; a.id = htlc->id; - a.amount_msat = htlc->msatoshi; + a.amount = htlc->amount; a.payment_hash = htlc->rhash; a.cltv_expiry = abs_locktime_to_blocks(&htlc->expiry); memcpy(a.onion_routing_packet, @@ -1876,7 +1899,7 @@ static void resend_commitment(struct peer *peer, const struct changed_htlc *last if (h->state == SENT_ADD_COMMIT) { u8 *msg = towire_update_add_htlc(NULL, &peer->channel_id, - h->id, h->msatoshi, + h->id, h->amount, &h->rhash, abs_locktime_to_blocks( &h->expiry), @@ -2087,6 +2110,7 @@ static void peer_reconnect(struct peer *peer, remote_current_per_commitment_point; struct secret last_local_per_commitment_secret; bool dataloss_protect; + const u8 *premature_funding_locked = NULL; dataloss_protect = local_feature_negotiated(peer->localfeatures, LOCAL_DATA_LOSS_PROTECT); @@ -2143,8 +2167,16 @@ static void peer_reconnect(struct peer *peer, do { clean_tmpctx(); msg = sync_crypto_read(tmpctx, &peer->cs, PEER_FD); - } while (handle_peer_gossip_or_error(PEER_FD, GOSSIP_FD, &peer->cs, - &peer->channel_id, msg)); + + /* Older LND sometimes sends funding_locked before reestablish! */ + if (fromwire_peektype(msg) == WIRE_FUNDING_LOCKED + && !premature_funding_locked) { + status_trace("Stashing early funding_locked msg!"); + premature_funding_locked = tal_steal(peer, msg); + msg = NULL; + } + } while (!msg || handle_peer_gossip_or_error(PEER_FD, GOSSIP_FD, &peer->cs, + &peer->channel_id, msg)); if (dataloss_protect) { if (!fromwire_channel_reestablish_option_data_loss_protect(msg, @@ -2342,6 +2374,11 @@ static void peer_reconnect(struct peer *peer, peer->channel->changes_pending[LOCAL] = true; peer_billboard(true, "Reconnected, and reestablished."); + + if (premature_funding_locked) { + handle_peer_funding_locked(peer, premature_funding_locked); + tal_free(premature_funding_locked); + } } /* Funding has locked in, and reached depth. */ @@ -2382,7 +2419,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) { u8 *msg; u32 cltv_expiry; - u64 amount_msat; + struct amount_msat amount; struct sha256 payment_hash; u8 onion_routing_packet[TOTAL_PACKET_SIZE]; enum channel_add_err e; @@ -2394,23 +2431,25 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) status_failed(STATUS_FAIL_MASTER_IO, "funding not locked for offer_htlc"); - if (!fromwire_channel_offer_htlc(inmsg, &amount_msat, + if (!fromwire_channel_offer_htlc(inmsg, &amount, &cltv_expiry, &payment_hash, onion_routing_packet)) master_badmsg(WIRE_CHANNEL_OFFER_HTLC, inmsg); e = channel_add_htlc(peer->channel, LOCAL, peer->htlc_id, - amount_msat, cltv_expiry, &payment_hash, + amount, cltv_expiry, &payment_hash, onion_routing_packet, NULL); - status_trace("Adding HTLC %"PRIu64" msat=%"PRIu64" cltv=%u gave %s", - peer->htlc_id, amount_msat, cltv_expiry, + status_trace("Adding HTLC %"PRIu64" amount=%s cltv=%u gave %s", + peer->htlc_id, + type_to_string(tmpctx, struct amount_msat, &amount), + cltv_expiry, channel_add_err_name(e)); switch (e) { case CHANNEL_ERR_ADD_OK: /* Tell the peer. */ msg = towire_update_add_htlc(NULL, &peer->channel_id, - peer->htlc_id, amount_msat, + peer->htlc_id, amount, &payment_hash, cltv_expiry, onion_routing_packet); sync_crypto_write(&peer->cs, PEER_FD, take(msg)); @@ -2441,8 +2480,10 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) goto failed; case CHANNEL_ERR_HTLC_BELOW_MINIMUM: failcode = WIRE_AMOUNT_BELOW_MINIMUM; - failmsg = tal_fmt(inmsg, "HTLC too small (%"PRIu64" minimum)", - peer->channel->config[REMOTE].htlc_minimum_msat); + failmsg = tal_fmt(inmsg, "HTLC too small (%s minimum)", + type_to_string(tmpctx, + struct amount_msat, + &peer->channel->config[REMOTE].htlc_minimum)); goto failed; case CHANNEL_ERR_TOO_MANY_HTLCS: failcode = WIRE_TEMPORARY_CHANNEL_FAILURE; @@ -2662,9 +2703,9 @@ static void init_shared_secrets(struct channel *channel, static void init_channel(struct peer *peer) { struct basepoints points[NUM_SIDES]; - u64 funding_satoshi; + struct amount_sat funding; u16 funding_txout; - u64 local_msatoshi; + struct amount_msat local_msat; struct pubkey funding_pubkey[NUM_SIDES]; struct channel_config conf[NUM_SIDES]; struct bitcoin_txid funding_txid; @@ -2689,7 +2730,7 @@ static void init_channel(struct peer *peer) if (!fromwire_channel_init(peer, msg, &peer->chain_hash, &funding_txid, &funding_txout, - &funding_satoshi, + &funding, &conf[LOCAL], &conf[REMOTE], feerate_per_kw, &peer->feerate_min, &peer->feerate_max, @@ -2702,7 +2743,7 @@ static void init_channel(struct peer *peer) &funder, &peer->fee_base, &peer->fee_per_satoshi, - &local_msatoshi, + &local_msat, &points[LOCAL], &funding_pubkey[LOCAL], &peer->node_ids[LOCAL], @@ -2764,8 +2805,8 @@ static void init_channel(struct peer *peer) peer->channel = new_full_channel(peer, &peer->chain_hash, &funding_txid, funding_txout, - funding_satoshi, - local_msatoshi, + funding, + local_msat, feerate_per_kw, &conf[LOCAL], &conf[REMOTE], &points[LOCAL], &points[REMOTE], @@ -2928,6 +2969,10 @@ int main(int argc, char *argv[]) "Can't read command: %s", strerror(errno)); req_in(peer, msg); + } else if (FD_ISSET(PEER_FD, &rfds)) { + /* This could take forever, but who cares? */ + msg = sync_crypto_read(tmpctx, &peer->cs, PEER_FD); + peer_in(peer, msg); } else if (FD_ISSET(GOSSIP_FD, &rfds)) { msg = wire_sync_read(tmpctx, GOSSIP_FD); /* Gossipd hangs up on us to kill us when a new @@ -2935,10 +2980,6 @@ int main(int argc, char *argv[]) if (!msg) peer_failed_connection_lost(); handle_gossip_msg(PEER_FD, &peer->cs, take(msg)); - } else if (FD_ISSET(PEER_FD, &rfds)) { - /* This could take forever, but who cares? */ - msg = sync_crypto_read(tmpctx, &peer->cs, PEER_FD); - peer_in(peer, msg); } } diff --git a/channeld/channeld_htlc.h b/channeld/channeld_htlc.h index 2e6ef2cc1720..37b06ffade96 100644 --- a/channeld/channeld_htlc.h +++ b/channeld/channeld_htlc.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -13,7 +14,7 @@ struct htlc { /* The unique ID for this peer and this direction (LOCAL or REMOTE) */ u64 id; /* The amount in millisatoshi. */ - u64 msatoshi; + struct amount_msat amount; /* When the HTLC can no longer be redeemed. */ struct abs_locktime expiry; /* The hash of the preimage which can redeem this HTLC */ diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 9c38291bfa8c..a8c359a0bd2a 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -12,10 +12,11 @@ #endif static bool trim(const struct htlc *htlc, - u32 feerate_per_kw, u64 dust_limit_satoshis, + u32 feerate_per_kw, + struct amount_sat dust_limit, enum side side) { - u64 htlc_fee; + struct amount_sat htlc_fee, htlc_min; /* BOLT #3: * @@ -41,17 +42,21 @@ static bool trim(const struct htlc *htlc, else htlc_fee = htlc_success_fee(feerate_per_kw); - return htlc->msatoshi / 1000 < dust_limit_satoshis + htlc_fee; + /* If these overflow, it implies htlc must be less. */ + if (!amount_sat_add(&htlc_min, dust_limit, htlc_fee)) + return true; + return amount_msat_less_sat(htlc->amount, htlc_min); } size_t commit_tx_num_untrimmed(const struct htlc **htlcs, - u32 feerate_per_kw, u64 dust_limit_satoshis, + u32 feerate_per_kw, + struct amount_sat dust_limit, 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_satoshis, side); + n += !trim(htlcs[i], feerate_per_kw, dust_limit, side); return n; } @@ -65,10 +70,13 @@ 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->output, &ripemd, keyset); - tx->output[n].amount = htlc->msatoshi / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(htlc->amount); tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); - SUPERVERBOSE("# HTLC %"PRIu64" offered amount %"PRIu64" wscript %s\n", - htlc->id, tx->output[n].amount, tal_hex(wscript, wscript)); + SUPERVERBOSE("# HTLC %"PRIu64" offered %s wscript %s\n", + htlc->id, + type_to_string(tmpctx, struct amount_sat, + &tx->output[n].amount), + tal_hex(wscript, wscript)); tal_free(wscript); } @@ -81,35 +89,41 @@ 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); - tx->output[n].amount = htlc->msatoshi / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(htlc->amount); tx->output[n].script = scriptpubkey_p2wsh(tx->output, wscript); - SUPERVERBOSE("# HTLC %"PRIu64" received amount %"PRIu64" wscript %s\n", - htlc->id, tx->output[n].amount, tal_hex(wscript, wscript)); + SUPERVERBOSE("# HTLC %"PRIu64" received %s wscript %s\n", + htlc->id, + type_to_string(tmpctx, struct amount_sat, + &tx->output[n].amount), + tal_hex(wscript, wscript)); tal_free(wscript); } struct bitcoin_tx *commit_tx(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, + struct amount_sat funding, enum side funder, u16 to_self_delay, const struct keyset *keyset, u32 feerate_per_kw, - u64 dust_limit_satoshis, - u64 self_pay_msat, - u64 other_pay_msat, + struct amount_sat dust_limit, + struct amount_msat self_pay, + struct amount_msat other_pay, const struct htlc **htlcs, const struct htlc ***htlcmap, u64 obscured_commitment_number, enum side side) { - u64 base_fee_msat; + struct amount_sat base_fee; + struct amount_msat total_pay; struct bitcoin_tx *tx; size_t i, n, untrimmed; u32 *cltvs; - assert(self_pay_msat + other_pay_msat <= funding_satoshis * 1000); + if (!amount_msat_add(&total_pay, self_pay, other_pay)) + abort(); + assert(!amount_msat_greater_sat(total_pay, funding)); /* BOLT #3: * @@ -118,40 +132,40 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, */ untrimmed = commit_tx_num_untrimmed(htlcs, feerate_per_kw, - dust_limit_satoshis, side); + dust_limit, side); /* BOLT #3: * * 2. Calculate the base [commitment transaction * fee](#fee-calculation). */ - base_fee_msat = commit_tx_base_fee_msat(feerate_per_kw, untrimmed); + base_fee = commit_tx_base_fee(feerate_per_kw, untrimmed); - SUPERVERBOSE("# base commitment transaction fee = %"PRIu64"\n", - base_fee_msat / 1000); + SUPERVERBOSE("# base commitment transaction fee = %s\n", + type_to_string(tmpctx, struct amount_sat, &base_fee)); /* BOLT #3: * * 3. Subtract this base fee from the funder (either `to_local` or * `to_remote`), with a floor of 0 (see [Fee Payment](#fee-payment)). */ - try_subtract_fee(funder, side, base_fee_msat, - &self_pay_msat, &other_pay_msat); + try_subtract_fee(funder, side, base_fee, &self_pay, &other_pay); #ifdef PRINT_ACTUAL_FEE { - u64 satoshis_out = 0; + struct amount_sat out = AMOUNT_SAT(0); + bool ok = true; for (i = 0; i < tal_count(htlcs); i++) { - if (!trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, - side)) - satoshis_out += htlcs[i]->msatoshi / 1000; + if (!trim(htlcs[i], feerate_per_kw, dust_limit, side)) + ok &= amount_sat_add(&out, out, amount_msat_to_sat_round_down(htlcs[i]->amount)); } - if (self_pay_msat / 1000 >= dust_limit_satoshis) - satoshis_out += self_pay_msat / 1000; - if (other_pay_msat / 1000 >= dust_limit_satoshis) - satoshis_out += other_pay_msat / 1000; + if (amount_msat_greater_sat(self_pay, dust_limit)) + ok &= amount_sat_add(&out, out, amount_msat_to_sat_round_down(self_pay)); + if (amount_msat_greater_sat(other_pay, dust_limit)) + ok &= amount_sat_add(&out, out, amount_msat_to_sat_round_down(other_pay)); + assert(ok); SUPERVERBOSE("# actual commitment transaction fee = %"PRIu64"\n", - funding_satoshis - satoshis_out); + funding.satoshis - out.satoshis); /* Raw: test output */ } #endif @@ -177,7 +191,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, for (i = 0; i < tal_count(htlcs); i++) { if (htlc_owner(htlcs[i]) != side) continue; - if (trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, side)) + if (trim(htlcs[i], feerate_per_kw, dust_limit, side)) continue; add_offered_htlc_out(tx, n, htlcs[i], keyset); (*htlcmap)[n] = htlcs[i]; @@ -193,7 +207,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, for (i = 0; i < tal_count(htlcs); i++) { if (htlc_owner(htlcs[i]) == side) continue; - if (trim(htlcs[i], feerate_per_kw, dust_limit_satoshis, side)) + if (trim(htlcs[i], feerate_per_kw, dust_limit, side)) continue; add_received_htlc_out(tx, n, htlcs[i], keyset); (*htlcmap)[n] = htlcs[i]; @@ -207,15 +221,16 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * `dust_limit_satoshis`, add a [`to_local` * output](#to_local-output). */ - if (self_pay_msat / 1000 >= dust_limit_satoshis) { + if (amount_msat_greater_eq_sat(self_pay, dust_limit)) { u8 *wscript = to_self_wscript(tmpctx, to_self_delay,keyset); - tx->output[n].amount = self_pay_msat / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(self_pay); tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); (*htlcmap)[n] = NULL; /* We don't assign cltvs[n]: if we use it, order doesn't matter. * However, valgrind will warn us something wierd is happening */ - SUPERVERBOSE("# to-local amount %"PRIu64" wscript %s\n", - tx->output[n].amount, + SUPERVERBOSE("# to-local amount %s wscript %s\n", + type_to_string(tmpctx, struct amount_sat, + &tx->output[n].amount), tal_hex(tmpctx, wscript)); n++; } @@ -226,7 +241,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * `dust_limit_satoshis`, add a [`to_remote` * output](#to_remote-output). */ - if (other_pay_msat / 1000 >= dust_limit_satoshis) { + if (amount_msat_greater_eq_sat(other_pay, dust_limit)) { /* BOLT #3: * * #### `to_remote` Output @@ -234,14 +249,15 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * This output sends funds to the other peer and thus is a simple * P2WPKH to `remotepubkey`. */ - tx->output[n].amount = other_pay_msat / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(other_pay); tx->output[n].script = scriptpubkey_p2wpkh(tx, &keyset->other_payment_key); (*htlcmap)[n] = NULL; /* We don't assign cltvs[n]: if we use it, order doesn't matter. * However, valgrind will warn us something wierd is happening */ - SUPERVERBOSE("# to-remote amount %"PRIu64" P2WPKH(%s)\n", - tx->output[n].amount, + SUPERVERBOSE("# to-remote amount %s P2WPKH(%s)\n", + type_to_string(tmpctx, struct amount_sat, + &tx->output[n].amount), type_to_string(tmpctx, struct pubkey, &keyset->other_payment_key)); n++; @@ -290,7 +306,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, = (0x80000000 | ((obscured_commitment_number>>24) & 0xFFFFFF)); /* Input amount needed for signature code. */ - tx->input[0].amount = tal_dup(tx->input, u64, &funding_satoshis); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); return tx; } diff --git a/channeld/commit_tx.h b/channeld/commit_tx.h index 6f8decd6ddd2..748a81ae736d 100644 --- a/channeld/commit_tx.h +++ b/channeld/commit_tx.h @@ -12,26 +12,27 @@ struct keyset; * commit_tx_num_untrimmed: how many of these htlc outputs will commit tx have? * @htlcs: tal_arr of HTLCs * @feerate_per_kw: feerate to use - * @dust_limit_satoshis: dust limit below which to trim outputs. + * @dust_limit: dust limit below which to trim outputs. * @side: from which side's point of view * * We need @side because HTLC fees are different for offered and * received HTLCs. */ size_t commit_tx_num_untrimmed(const struct htlc **htlcs, - u32 feerate_per_kw, u64 dust_limit_satoshis, + u32 feerate_per_kw, + struct amount_sat dust_limit, enum side side); /** * commit_tx: create (unsigned) commitment tx to spend the funding tx output * @ctx: context to allocate transaction and @htlc_map from. - * @funding_txid, @funding_out, @funding_satoshis: funding outpoint. + * @funding_txid, @funding_out, @funding: funding outpoint. * @funder: is the LOCAL or REMOTE paying the fee? * @keyset: keys derived for this commit tx. * @feerate_per_kw: feerate to use - * @dust_limit_satoshis: dust limit below which to trim outputs. - * @self_pay_msat: amount to pay directly to self - * @other_pay_msat: amount to pay directly to the other side + * @dust_limit: dust limit below which to trim outputs. + * @self_pay: amount to pay directly to self + * @other_pay: amount to pay directly to the other side * @htlcs: tal_arr of htlcs committed by transaction (some may be trimmed) * @htlc_map: outputed map of outnum->HTLC (NULL for direct outputs). * @obscured_commitment_number: number to encode in commitment transaction @@ -44,14 +45,14 @@ size_t commit_tx_num_untrimmed(const struct htlc **htlcs, struct bitcoin_tx *commit_tx(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, + struct amount_sat funding, enum side funder, u16 to_self_delay, const struct keyset *keyset, u32 feerate_per_kw, - u64 dust_limit_satoshis, - u64 self_pay_msat, - u64 other_pay_msat, + struct amount_sat dust_limit, + struct amount_msat self_pay, + struct amount_msat other_pay, const struct htlc **htlcs, const struct htlc ***htlcmap, u64 obscured_commitment_number, diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 5bd6338f6673..0726d5c50f41 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -26,8 +26,8 @@ struct channel *new_full_channel(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, - u64 local_msatoshi, + struct amount_sat funding, + struct amount_msat local_msat, const u32 feerate_per_kw[NUM_SIDES], const struct channel_config *local, const struct channel_config *remote, @@ -41,8 +41,8 @@ struct channel *new_full_channel(const tal_t *ctx, chain_hash, funding_txid, funding_txout, - funding_satoshis, - local_msatoshi, + funding, + local_msat, feerate_per_kw[LOCAL], local, remote, local_basepoints, @@ -68,16 +68,20 @@ static void htlc_arr_append(const struct htlc ***arr, const struct htlc *htlc) tal_arr_expand(arr, htlc); } -/* What does adding the HTLC do to the balance for this side */ -static s64 balance_adding_htlc(const struct htlc *htlc, enum side side) +/* What does adding the HTLC do to the balance for this side (subtracts) */ +static bool WARN_UNUSED_RESULT balance_add_htlc(struct amount_msat *msat, + const struct htlc *htlc, + enum side side) { if (htlc_owner(htlc) == side) - return -(s64)htlc->msatoshi; - return 0; + return amount_msat_sub(msat, *msat, htlc->amount); + return true; } -/* What does removing the HTLC do to the balance for this side */ -static s64 balance_removing_htlc(const struct htlc *htlc, enum side side) +/* What does removing the HTLC do to the balance for this side (adds) */ +static bool WARN_UNUSED_RESULT balance_remove_htlc(struct amount_msat *msat, + const struct htlc *htlc, + enum side side) { enum side paid_to; @@ -88,8 +92,8 @@ static s64 balance_removing_htlc(const struct htlc *htlc, enum side side) paid_to = htlc_owner(htlc); if (side == paid_to) - return htlc->msatoshi; - return 0; + return amount_msat_add(msat, *msat, htlc->amount); + return true; } static void dump_htlc(const struct htlc *htlc, const char *prefix) @@ -164,16 +168,20 @@ static void gather_htlcs(const tal_t *ctx, } } -static u64 total_offered_msatoshis(const struct htlc **htlcs, enum side side) +static bool sum_offered_msatoshis(struct amount_msat *total, + const struct htlc **htlcs, + enum side side) { size_t i; - u64 total = 0; + *total = AMOUNT_MSAT(0); for (i = 0; i < tal_count(htlcs); i++) { - if (htlc_owner(htlcs[i]) == side) - total += htlcs[i]->msatoshi; + if (htlc_owner(htlcs[i]) == side) { + if (!amount_msat_add(total, *total, htlcs[i]->amount)) + return false; + } } - return total; + return true; } static void add_htlcs(struct bitcoin_tx ***txs, @@ -200,7 +208,7 @@ static void add_htlcs(struct bitcoin_tx ***txs, if (htlc_owner(htlc) == side) { tx = htlc_timeout_tx(*txs, &txid, i, - htlc->msatoshi, + htlc->amount, htlc->expiry.locktime, channel->config[!side].to_self_delay, feerate_per_kw, @@ -212,7 +220,7 @@ static void add_htlcs(struct bitcoin_tx ***txs, &keyset->self_revocation_key); } else { tx = htlc_success_tx(*txs, &txid, i, - htlc->msatoshi, + htlc->amount, channel->config[!side].to_self_delay, feerate_per_kw, keyset); @@ -257,14 +265,14 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, txs = tal_arr(ctx, struct bitcoin_tx *, 1); txs[0] = commit_tx(ctx, &channel->funding_txid, channel->funding_txout, - channel->funding_msat / 1000, + channel->funding, channel->funder, channel->config[!side].to_self_delay, &keyset, channel->view[side].feerate_per_kw, - channel->config[side].dust_limit_satoshis, - channel->view[side].owed_msat[side], - channel->view[side].owed_msat[!side], + channel->config[side].dust_limit, + channel->view[side].owed[side], + channel->view[side].owed[!side], committed, htlcmap, commitment_number ^ channel->commitment_number_obscurer, @@ -283,23 +291,27 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, static enum channel_add_err add_htlc(struct channel *channel, enum htlc_state state, - u64 id, u64 msatoshi, u32 cltv_expiry, + u64 id, + struct amount_msat amount, + u32 cltv_expiry, const struct sha256 *payment_hash, const u8 routing[TOTAL_PACKET_SIZE], struct htlc **htlcp, bool enforce_aggregate_limits) { struct htlc *htlc, *old; - s64 msat_in_htlcs, fee_msat, balance_msat; + struct amount_msat msat_in_htlcs, committed_msat, adding_msat, removing_msat; + struct amount_sat fee; enum side sender = htlc_state_owner(state), recipient = !sender; const struct htlc **committed, **adding, **removing; const struct channel_view *view; + bool ok; size_t i; htlc = tal(tmpctx, struct htlc); htlc->id = id; - htlc->msatoshi = msatoshi; + htlc->amount = amount; htlc->state = state; htlc->shared_secret = NULL; @@ -327,7 +339,7 @@ static enum channel_add_err add_htlc(struct channel *channel, old = htlc_get(channel->htlcs, htlc->id, htlc_owner(htlc)); if (old) { if (old->state != htlc->state - || old->msatoshi != htlc->msatoshi + || !amount_msat_eq(old->amount, htlc->amount) || old->expiry.locktime != htlc->expiry.locktime || !sha256_eq(&old->rhash, &htlc->rhash)) return CHANNEL_ERR_DUPLICATE_ID_DIFFERENT; @@ -345,10 +357,10 @@ static enum channel_add_err add_htlc(struct channel *channel, * `htlc_minimum_msat`: * - SHOULD fail the channel. */ - if (htlc->msatoshi == 0) { + if (amount_msat_eq(htlc->amount, AMOUNT_MSAT(0))) { return CHANNEL_ERR_HTLC_BELOW_MINIMUM; } - if (htlc->msatoshi < channel->config[recipient].htlc_minimum_msat) { + if (amount_msat_less(htlc->amount, channel->config[recipient].htlc_minimum)) { return CHANNEL_ERR_HTLC_BELOW_MINIMUM; } @@ -357,7 +369,7 @@ static enum channel_add_err add_htlc(struct channel *channel, * - for channels with `chain_hash` identifying the Groestlcoin blockchain: * - MUST set the four most significant bytes of `amount_msat` to 0. */ - if (htlc->msatoshi > channel->chainparams->max_payment_msat) { + if (amount_msat_greater(htlc->amount, channel->chainparams->max_payment)) { return CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED; } @@ -371,15 +383,26 @@ static enum channel_add_err add_htlc(struct channel *channel, * its local commitment transaction... * - SHOULD fail the channel. */ - if (enforce_aggregate_limits - && tal_count(committed) - tal_count(removing) + tal_count(adding) + if (tal_count(committed) - tal_count(removing) + tal_count(adding) > channel->config[recipient].max_accepted_htlcs) { return CHANNEL_ERR_TOO_MANY_HTLCS; } - msat_in_htlcs = total_offered_msatoshis(committed, htlc_owner(htlc)) - - total_offered_msatoshis(removing, htlc_owner(htlc)) - + total_offered_msatoshis(adding, htlc_owner(htlc)); + /* These cannot overflow with HTLC amount limitations, but + * maybe adding could later if they try to add a maximal HTLC. */ + if (!sum_offered_msatoshis(&committed_msat, + committed, htlc_owner(htlc)) + || !sum_offered_msatoshis(&removing_msat, + removing, htlc_owner(htlc)) + || !sum_offered_msatoshis(&adding_msat, + adding, htlc_owner(htlc))) { + return CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED; + } + + if (!amount_msat_add(&msat_in_htlcs, committed_msat, adding_msat) + || !amount_msat_sub(&msat_in_htlcs, msat_in_htlcs, removing_msat)) { + return CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED; + } /* BOLT #2: * @@ -388,8 +411,12 @@ static enum channel_add_err add_htlc(struct channel *channel, * local commitment transaction: * - SHOULD fail the channel. */ + + /* We don't enforce this for channel_force_htlcs: some might already + * be fulfilled/failed */ if (enforce_aggregate_limits - && msat_in_htlcs > channel->config[recipient].max_htlc_value_in_flight_msat) { + && amount_msat_greater(msat_in_htlcs, + channel->config[recipient].max_htlc_value_in_flight)) { return CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED; } @@ -404,47 +431,69 @@ static enum channel_add_err add_htlc(struct channel *channel, */ if (channel->funder == htlc_owner(htlc)) { u32 feerate = view->feerate_per_kw; - u64 dust = channel->config[recipient].dust_limit_satoshis; + struct amount_sat dust_limit = channel->config[recipient].dust_limit; size_t untrimmed; - untrimmed = commit_tx_num_untrimmed(committed, feerate, dust, + untrimmed = commit_tx_num_untrimmed(committed, feerate, dust_limit, recipient) - + commit_tx_num_untrimmed(adding, feerate, dust, + + commit_tx_num_untrimmed(adding, feerate, dust_limit, recipient) - - commit_tx_num_untrimmed(removing, feerate, dust, + - commit_tx_num_untrimmed(removing, feerate, dust_limit, recipient); - fee_msat = commit_tx_base_fee_msat(feerate, untrimmed); + fee = commit_tx_base_fee(feerate, untrimmed); } else - fee_msat = 0; - - assert(fee_msat >= 0); - - /* Figure out what balance sender would have after applying all - * pending changes. */ - balance_msat = view->owed_msat[sender]; - - assert(balance_msat >= 0); - for (i = 0; i < tal_count(removing); i++) - balance_msat += balance_removing_htlc(removing[i], sender); - assert(balance_msat >= 0); - for (i = 0; i < tal_count(adding); i++) - balance_msat += balance_adding_htlc(adding[i], sender); + fee = AMOUNT_SAT(0); + + assert((s64)fee.satoshis >= 0); /* Raw: explicit signedness test */ + + if (enforce_aggregate_limits) { + /* Figure out what balance sender would have after applying all + * pending changes. */ + struct amount_msat balance = view->owed[sender]; + /* This is a little subtle: + * + * The change is being applied to the receiver but it will + * come back to the sender after revoke_and_ack. So the check + * here is that the balance to the sender doesn't go below the + * sender's reserve. */ + const struct amount_sat reserve + = channel->config[!sender].channel_reserve; + + assert(amount_msat_greater_eq(balance, AMOUNT_MSAT(0))); + ok = true; + for (i = 0; i < tal_count(removing); i++) + ok &= balance_remove_htlc(&balance, removing[i], sender); + assert(amount_msat_greater_eq(balance, AMOUNT_MSAT(0))); + for (i = 0; i < tal_count(adding); i++) + ok &= balance_add_htlc(&balance, adding[i], sender); + + /* Overflow shouldn't happen, but if it does, complain */ + if (!ok) { + status_broken("Failed to add %zu remove %zu htlcs", + tal_count(adding), tal_count(removing)); + return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; + } - /* This is a little subtle: - * - * The change is being applied to the receiver but it will - * come back to the sender after revoke_and_ack. So the check - * here is that the balance to the sender doesn't go below the - * sender's reserve. */ - if (enforce_aggregate_limits - && balance_msat - fee_msat < (s64)channel_reserve_msat(channel, sender)) { - status_trace("balance = %"PRIu64 - ", fee is %"PRIu64 - ", reserve is %"PRIu64, - balance_msat, fee_msat, - channel_reserve_msat(channel, sender)); - return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; + if (!amount_msat_sub_sat(&balance, balance, fee)) { + status_trace("Cannot afford fee %s with balance %s", + type_to_string(tmpctx, struct amount_sat, + &fee), + type_to_string(tmpctx, struct amount_msat, + &balance)); + return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; + } + if (!amount_msat_greater_eq_sat(balance, reserve)) { + status_trace("Cannot afford fee %s: would make balance %s" + " below reserve %s", + type_to_string(tmpctx, struct amount_sat, + &fee), + type_to_string(tmpctx, struct amount_msat, + &balance), + type_to_string(tmpctx, struct amount_sat, + &reserve)); + return CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED; + } } dump_htlc(htlc, "NEW:"); @@ -465,7 +514,7 @@ static enum channel_add_err add_htlc(struct channel *channel, enum channel_add_err channel_add_htlc(struct channel *channel, enum side sender, u64 id, - u64 msatoshi, + struct amount_msat amount, u32 cltv_expiry, const struct sha256 *payment_hash, const u8 routing[TOTAL_PACKET_SIZE], @@ -478,8 +527,7 @@ enum channel_add_err channel_add_htlc(struct channel *channel, else state = RCVD_ADD_HTLC; - /* FIXME: check expiry etc. against config. */ - return add_htlc(channel, state, id, msatoshi, cltv_expiry, + return add_htlc(channel, state, id, amount, cltv_expiry, payment_hash, routing, htlcp, true); } @@ -623,23 +671,61 @@ static void htlc_incstate(struct channel *channel, /* If we've added or removed, adjust balances. */ if (!(preflags & committed_f) && (postflags & committed_f)) { - status_trace("htlc added %s: local %+"PRIi64" remote %+"PRIi64, + status_trace("htlc added %s: local %s remote %s", side_to_str(sidechanged), - balance_adding_htlc(htlc, LOCAL), - balance_adding_htlc(htlc, REMOTE)); - channel->view[sidechanged].owed_msat[LOCAL] - += balance_adding_htlc(htlc, LOCAL); - channel->view[sidechanged].owed_msat[REMOTE] - += balance_adding_htlc(htlc, REMOTE); + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[LOCAL]), + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[REMOTE])); + if (!balance_add_htlc(&channel->view[sidechanged].owed[LOCAL], + htlc, LOCAL)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot add htlc #%"PRIu64" %s" + " to LOCAL", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + if (!balance_add_htlc(&channel->view[sidechanged].owed[REMOTE], + htlc, REMOTE)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot add htlc #%"PRIu64" %s" + " to REMOTE", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + status_trace("-> local %s remote %s", + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[LOCAL]), + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[REMOTE])); } else if ((preflags & committed_f) && !(postflags & committed_f)) { - status_trace("htlc removed %s: local %+"PRIi64" remote %+"PRIi64, + status_trace("htlc added %s: local %s remote %s", side_to_str(sidechanged), - balance_removing_htlc(htlc, LOCAL), - balance_removing_htlc(htlc, REMOTE)); - channel->view[sidechanged].owed_msat[LOCAL] - += balance_removing_htlc(htlc, LOCAL); - channel->view[sidechanged].owed_msat[REMOTE] - += balance_removing_htlc(htlc, REMOTE); + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[LOCAL]), + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[REMOTE])); + if (!balance_remove_htlc(&channel->view[sidechanged].owed[LOCAL], + htlc, LOCAL)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot remove htlc #%"PRIu64" %s" + " from LOCAL", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + if (!balance_remove_htlc(&channel->view[sidechanged].owed[REMOTE], + htlc, REMOTE)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot remove htlc #%"PRIu64" %s" + " from REMOTE", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + status_trace("-> local %s remote %s", + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[LOCAL]), + type_to_string(tmpctx, struct amount_msat, + &channel->view[sidechanged].owed[REMOTE])); } } @@ -684,7 +770,8 @@ static int change_htlcs(struct channel *channel, u32 approx_max_feerate(const struct channel *channel) { size_t num; - u64 weight, avail_msat; + u64 weight; + struct amount_sat avail; const struct htlc **committed, **adding, **removing; gather_htlcs(tmpctx, channel, !channel->funder, @@ -696,29 +783,31 @@ u32 approx_max_feerate(const struct channel *channel) weight = 724 + 172 * num; /* We should never go below reserve. */ - avail_msat = channel->view[!channel->funder].owed_msat[channel->funder] - - channel_reserve_msat(channel, channel->funder); + if (!amount_sat_sub(&avail, + amount_msat_to_sat_round_down(channel->view[!channel->funder].owed[channel->funder]), + channel->config[!channel->funder].channel_reserve)) + avail = AMOUNT_SAT(0); - /* We have to pay fee from onchain funds, so it's in satoshi. */ - return avail_msat / 1000 / weight * 1000; + return avail.satoshis / weight * 1000; /* Raw: once-off reverse feerate*/ } bool can_funder_afford_feerate(const struct channel *channel, u32 feerate_per_kw) { - u64 fee_msat, dust = channel->config[!channel->funder].dust_limit_satoshis; + struct amount_sat needed, fee; + struct amount_sat dust_limit = channel->config[!channel->funder].dust_limit; size_t untrimmed; const struct htlc **committed, **adding, **removing; gather_htlcs(tmpctx, channel, !channel->funder, &committed, &removing, &adding); - untrimmed = commit_tx_num_untrimmed(committed, feerate_per_kw, dust, + untrimmed = commit_tx_num_untrimmed(committed, feerate_per_kw, dust_limit, !channel->funder) - + commit_tx_num_untrimmed(adding, feerate_per_kw, dust, + + commit_tx_num_untrimmed(adding, feerate_per_kw, dust_limit, !channel->funder) - - commit_tx_num_untrimmed(removing, feerate_per_kw, dust, + - commit_tx_num_untrimmed(removing, feerate_per_kw, dust_limit, !channel->funder); - fee_msat = commit_tx_base_fee_msat(feerate_per_kw, untrimmed); + fee = commit_tx_base_fee(feerate_per_kw, untrimmed); /* BOLT #2: * @@ -729,8 +818,17 @@ bool can_funder_afford_feerate(const struct channel *channel, u32 feerate_per_kw /* Note: sender == funder */ /* How much does it think it has? Must be >= reserve + fee */ - return channel->view[!channel->funder].owed_msat[channel->funder] - >= channel_reserve_msat(channel, channel->funder) + fee_msat; + if (!amount_sat_add(&needed, fee, + channel->config[!channel->funder].channel_reserve)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot add fee %s and reserve %s", + type_to_string(tmpctx, struct amount_sat, + &fee), + type_to_string(tmpctx, struct amount_sat, + &channel->config[!channel->funder].channel_reserve)); + + return amount_msat_greater_eq_sat(channel->view[!channel->funder].owed[channel->funder], + needed); } bool channel_update_feerate(struct channel *channel, u32 feerate_per_kw) @@ -881,10 +979,24 @@ static bool adjust_balance(struct channel *channel, struct htlc *htlc) continue; /* Add it. */ - channel->view[side].owed_msat[LOCAL] - += balance_adding_htlc(htlc, LOCAL); - channel->view[side].owed_msat[REMOTE] - += balance_adding_htlc(htlc, REMOTE); + if (!balance_add_htlc(&channel->view[side].owed[LOCAL], + htlc, LOCAL)) { + status_broken("Cannot add htlc #%"PRIu64" %s" + " to LOCAL", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + return false; + } + if (!balance_add_htlc(&channel->view[side].owed[REMOTE], + htlc, REMOTE)) { + status_broken("Cannot add htlc #%"PRIu64" %s" + " to REMOTE", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + return false; + } /* If it is no longer committed, remove it (depending * on fail || fulfill). */ @@ -892,18 +1004,32 @@ static bool adjust_balance(struct channel *channel, struct htlc *htlc) continue; if (!htlc->fail && !htlc->failcode && !htlc->r) { - status_trace("%s HTLC %"PRIu64 - " %s neither fail nor fulfill?", - htlc_state_owner(htlc->state) == LOCAL - ? "out" : "in", - htlc->id, - htlc_state_name(htlc->state)); + status_broken("%s HTLC %"PRIu64 + " %s neither fail nor fulfill?", + htlc_state_owner(htlc->state) == LOCAL + ? "out" : "in", + htlc->id, + htlc_state_name(htlc->state)); + return false; + } + if (!balance_remove_htlc(&channel->view[side].owed[LOCAL], + htlc, LOCAL)) { + status_broken("Cannot remove htlc #%"PRIu64" %s" + " from LOCAL", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); + return false; + } + if (!balance_remove_htlc(&channel->view[side].owed[REMOTE], + htlc, REMOTE)) { + status_broken("Cannot remove htlc #%"PRIu64" %s" + " from REMOTE", + htlc->id, + type_to_string(tmpctx, struct amount_msat, + &htlc->amount)); return false; } - channel->view[side].owed_msat[LOCAL] - += balance_removing_htlc(htlc, LOCAL); - channel->view[side].owed_msat[REMOTE] - += balance_removing_htlc(htlc, REMOTE); } return true; } @@ -917,6 +1043,8 @@ bool channel_force_htlcs(struct channel *channel, const enum side *failed_sides) { size_t i; + struct htlc *htlc; + struct htlc_map_iter it; if (tal_count(hstates) != tal_count(htlcs)) { status_trace("#hstates %zu != #htlcs %zu", @@ -940,16 +1068,18 @@ bool channel_force_htlcs(struct channel *channel, struct htlc *htlc; status_trace("Restoring HTLC %zu/%zu:" - " id=%"PRIu64" msat=%"PRIu64" cltv=%u" + " id=%"PRIu64" amount=%s cltv=%u" " payment_hash=%s", i, tal_count(htlcs), - htlcs[i].id, htlcs[i].amount_msat, + htlcs[i].id, + type_to_string(tmpctx, struct amount_msat, + &htlcs[i].amount), htlcs[i].cltv_expiry, type_to_string(tmpctx, struct sha256, &htlcs[i].payment_hash)); e = add_htlc(channel, hstates[i], - htlcs[i].id, htlcs[i].amount_msat, + htlcs[i].id, htlcs[i].amount, htlcs[i].cltv_expiry, &htlcs[i].payment_hash, htlcs[i].onion_routing_packet, &htlc, false); @@ -1051,12 +1181,11 @@ bool channel_force_htlcs(struct channel *channel, htlc->failed_scid = NULL; } - for (i = 0; i < tal_count(htlcs); i++) { - struct htlc *htlc; - htlc = channel_get_htlc(channel, - htlc_state_owner(hstates[i]), - htlcs[i].id); - + /* Now adjust balances. The balance never goes negative, because + * we do them in id order. */ + for (htlc = htlc_map_first(channel->htlcs, &it); + htlc; + htlc = htlc_map_next(channel->htlcs, &it)) { if (!adjust_balance(channel, htlc)) return false; } diff --git a/channeld/full_channel.h b/channeld/full_channel.h index 103de0a8f145..8a217d176028 100644 --- a/channeld/full_channel.h +++ b/channeld/full_channel.h @@ -12,8 +12,8 @@ * @ctx: tal context to allocate return value from. * @funding_txid: The commitment transaction id. * @funding_txout: The commitment transaction output number. - * @funding_satoshis: The commitment transaction amount. - * @local_msatoshi: The amount for the local side (remainder goes to remote) + * @funding: The commitment transaction amount. + * @local_msat: The amount for the local side (remainder goes to remote) * @feerate_per_kw: feerate per kiloweight (satoshis) for the commitment * transaction and HTLCS for each side. * @local: local channel configuration @@ -30,8 +30,8 @@ struct channel *new_full_channel(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, - u64 local_msatoshi, + struct amount_sat funding, + struct amount_msat local_msat, const u32 feerate_per_kw[NUM_SIDES], const struct channel_config *local, const struct channel_config *remote, @@ -84,7 +84,7 @@ u32 actual_feerate(const struct channel *channel, * @channel: The channel * @offerer: the side offering the HTLC (to the other side). * @id: unique HTLC id. - * @msatoshi: amount in millisatoshi. + * @amount: amount in millisatoshi. * @cltv_expiry: block number when HTLC can no longer be redeemed. * @payment_hash: hash whose preimage can redeem HTLC. * @routing: routing information (copied) @@ -97,7 +97,7 @@ u32 actual_feerate(const struct channel *channel, enum channel_add_err channel_add_htlc(struct channel *channel, enum side sender, u64 id, - u64 msatoshi, + struct amount_msat msatoshi, u32 cltv_expiry, const struct sha256 *payment_hash, const u8 routing[TOTAL_PACKET_SIZE], diff --git a/channeld/test/Makefile b/channeld/test/Makefile index 2a5598cba7c2..623aa6403387 100644 --- a/channeld/test/Makefile +++ b/channeld/test/Makefile @@ -8,6 +8,7 @@ ALL_TEST_PROGRAMS += $(CHANNELD_TEST_PROGRAMS) ALL_OBJS += $(CHANNELD_TEST_OBJS) CHANNELD_TEST_COMMON_OBJS := \ + common/amount.o \ common/daemon_conn.o \ common/htlc_state.o \ common/htlc_tx.o \ diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index d66f5e3fa528..cf3c03acabf3 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -12,6 +12,7 @@ static bool print_superverbose; #include #include #include +#include #include #include @@ -122,23 +123,23 @@ static const struct htlc **setup_htlcs(const tal_t *ctx) switch (i) { case 0: htlc->state = RCVD_ADD_ACK_REVOCATION; - htlc->msatoshi = 1000000; + htlc->amount = AMOUNT_MSAT(1000000); break; case 1: htlc->state = RCVD_ADD_ACK_REVOCATION; - htlc->msatoshi = 2000000; + htlc->amount = AMOUNT_MSAT(2000000); break; case 2: htlc->state = SENT_ADD_ACK_REVOCATION; - htlc->msatoshi = 2000000; + htlc->amount = AMOUNT_MSAT(2000000); break; case 3: htlc->state = SENT_ADD_ACK_REVOCATION; - htlc->msatoshi = 3000000; + htlc->amount = AMOUNT_MSAT(3000000); break; case 4: htlc->state = RCVD_ADD_ACK_REVOCATION; - htlc->msatoshi = 4000000; + htlc->amount = AMOUNT_MSAT(4000000); break; } @@ -222,7 +223,7 @@ static void report_htlcs(const struct bitcoin_tx *tx, if (htlc_owner(htlc) == LOCAL) { htlc_tx[i] = htlc_timeout_tx(htlc_tx, &txid, i, - htlc->msatoshi, + htlc->amount, htlc->expiry.locktime, to_self_delay, feerate_per_kw, @@ -234,7 +235,7 @@ static void report_htlcs(const struct bitcoin_tx *tx, remote_revocation_key); } else { htlc_tx[i] = htlc_success_tx(htlc_tx, &txid, i, - htlc->msatoshi, + htlc->amount, to_self_delay, feerate_per_kw, &keyset); @@ -353,15 +354,17 @@ static void report(struct bitcoin_tx *tx, } #ifdef DEBUG -static u64 calc_fee(const struct bitcoin_tx *tx, u64 input_satoshi) +static struct amount_sat calc_fee(const struct bitcoin_tx *tx, struct amount_sat input) { size_t i; - u64 output_satoshi = 0; + struct amount_sat output = AMOUNT_SAT(0), fee; for (i = 0; i < tal_count(tx->output); i++) - output_satoshi += tx->output[i].amount; + output.satoshis += tx->output[i].amount; - return input_satoshi - output_satoshi; + if (!amount_sub_sat(&fee, input, output)) + abort(); + return fee; } /* For debugging, we do brute-force increase to find thresholds */ @@ -423,7 +426,7 @@ int main(void) setup_locale(); struct bitcoin_txid funding_txid; - u64 funding_amount_satoshi, dust_limit_satoshi; + struct amount_sat funding_amount, dust_limit; u32 feerate_per_kw; u16 to_self_delay; /* x_ prefix means internal vars we used to derive spec */ @@ -449,7 +452,8 @@ int main(void) struct keyset keyset; u8 *wscript; unsigned int funding_output_index; - u64 commitment_number, cn_obscurer, to_local_msat, to_remote_msat; + u64 commitment_number, cn_obscurer; + struct amount_msat to_local, to_remote; const struct htlc **htlcs, **htlc_map, **htlc_map2, **inv_htlcs; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY @@ -486,10 +490,10 @@ int main(void) */ funding_txid = txid_from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be"); funding_output_index = 0; - funding_amount_satoshi = 10000000; + funding_amount.satoshis = 10000000; commitment_number = 42; to_self_delay = 144; - dust_limit_satoshi = 546; + dust_limit.satoshis = 546; #ifdef DEBUG print_superverbose = true; @@ -688,15 +692,15 @@ int main(void) * to_remote_msat: 3000000000 * local_feerate_per_kw: 15000 */ - to_local_msat = 7000000000; - to_remote_msat = 3000000000; + to_local.millisatoshis = 7000000000; + to_remote.millisatoshis = 3000000000; feerate_per_kw = 15000; printf("\n" "name: simple commitment tx with no HTLCs\n" "to_local_msat: %"PRIu64"\n" "to_remote_msat: %"PRIu64"\n" "local_feerate_per_kw: %u\n", - to_local_msat, to_remote_msat, feerate_per_kw); + to_local.millisatoshis, to_remote.millisatoshis, feerate_per_kw); keyset.self_revocation_key = remote_revocation_key; keyset.self_delayed_payment_key = local_delayedkey; @@ -707,24 +711,24 @@ int main(void) print_superverbose = true; tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, NULL, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); print_superverbose = false; tx2 = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, REMOTE, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, NULL, &htlc_map2, commitment_number ^ cn_obscurer, REMOTE); tx_must_be_eq(tx, tx2); @@ -749,36 +753,36 @@ int main(void) * to_remote_msat: 3000000000 * local_feerate_per_kw: 0 */ - to_local_msat = 6988000000; - to_remote_msat = 3000000000; + to_local.millisatoshis = 6988000000; + to_remote.millisatoshis = 3000000000; feerate_per_kw = 0; printf("\n" "name: commitment tx with all 5 htlcs untrimmed (minimum feerate)\n" "to_local_msat: %"PRIu64"\n" "to_remote_msat: %"PRIu64"\n" "local_feerate_per_kw: %u\n", - to_local_msat, to_remote_msat, feerate_per_kw); + to_local.millisatoshis, to_remote.millisatoshis, feerate_per_kw); print_superverbose = true; tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, htlcs, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); print_superverbose = false; tx2 = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, REMOTE, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, inv_htlcs, &htlc_map2, commitment_number ^ cn_obscurer, REMOTE); @@ -803,25 +807,25 @@ int main(void) feerate_per_kw = increase(feerate_per_kw); print_superverbose = false; newtx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, htlcs, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); /* This is what it would look like for peer generating it! */ tx2 = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, REMOTE, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, inv_htlcs, &htlc_map2, commitment_number ^ cn_obscurer, REMOTE); @@ -829,7 +833,7 @@ int main(void) #ifdef DEBUG if (feerate_per_kw % 100000 == 0) printf("feerate_per_kw = %u, fees = %"PRIu64"\n", - feerate_per_kw, calc_fee(newtx, funding_amount_satoshi)); + feerate_per_kw, calc_fee(newtx, funding_amount)); if (tal_count(newtx->output) == tal_count(tx->output)) { tal_free(newtx); continue; @@ -842,17 +846,17 @@ int main(void) "local_feerate_per_kw: %u\n", tal_count(tx->output), tal_count(tx->output) > 1 ? "s" : "", - to_local_msat, to_remote_msat, feerate_per_kw-1); + to_local.millisatoshis, to_remote.millisatoshis, feerate_per_kw-1); /* Recalc with verbosity on */ print_superverbose = true; tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw-1, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, htlcs, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); @@ -878,17 +882,17 @@ int main(void) "local_feerate_per_kw: %u\n", tal_count(newtx->output), tal_count(newtx->output) > 1 ? "s" : "", - to_local_msat, to_remote_msat, feerate_per_kw); + to_local.millisatoshis, to_remote.millisatoshis, feerate_per_kw); /* Recalc with verbosity on */ print_superverbose = true; newtx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, htlcs, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); @@ -916,9 +920,10 @@ int main(void) /* Now make sure we cover case where funder can't afford the fee; * its output cannot go negative! */ for (;;) { - u64 base_fee_msat = commit_tx_base_fee_msat(feerate_per_kw, 0); + struct amount_sat base_fee + = commit_tx_base_fee(feerate_per_kw, 0); - if (base_fee_msat <= to_local_msat) { + if (amount_msat_greater_eq_sat(to_local, base_fee)) { feerate_per_kw++; continue; } @@ -937,15 +942,15 @@ int main(void) "to_local_msat: %"PRIu64"\n" "to_remote_msat: %"PRIu64"\n" "local_feerate_per_kw: %u\n", - to_local_msat, to_remote_msat, feerate_per_kw); + to_local.millisatoshis, to_remote.millisatoshis, feerate_per_kw); tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, to_self_delay, &keyset, feerate_per_kw, - dust_limit_satoshi, - to_local_msat, - to_remote_msat, + dust_limit, + to_local, + to_remote, htlcs, &htlc_map, commitment_number ^ cn_obscurer, LOCAL); diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 894b1f560242..d09fe43166c5 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -8,11 +8,19 @@ #include #include #include +#include #include #include #include #include +/* AUTOGENERATED MOCKS START */ +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + void status_fmt(enum log_level level UNUSED, const char *fmt, ...) { va_list ap; @@ -114,31 +122,31 @@ static const struct htlc **include_htlcs(struct channel *channel, enum side side struct sha256 hash; enum channel_add_err e; enum side sender; - u64 msatoshi = 0; + struct amount_msat msatoshi = AMOUNT_MSAT(0); switch (i) { case 0: sender = !side; - msatoshi = 1000000; + msatoshi = AMOUNT_MSAT(1000000); break; case 1: sender = !side; - msatoshi = 2000000; + msatoshi = AMOUNT_MSAT(2000000); break; case 2: sender = side; - msatoshi = 2000000; + msatoshi = AMOUNT_MSAT(2000000); break; case 3: sender = side; - msatoshi = 3000000; + msatoshi = AMOUNT_MSAT(3000000); break; case 4: sender = !side; - msatoshi = 4000000; + msatoshi = AMOUNT_MSAT(4000000); break; } - assert(msatoshi != 0); + assert(msatoshi.millisatoshis != 0); memset(&preimage, i, sizeof(preimage)); sha256(&hash, &preimage, sizeof(preimage)); @@ -223,7 +231,7 @@ static void txs_must_be_eq(struct bitcoin_tx **a, struct bitcoin_tx **b) static void send_and_fulfill_htlc(struct channel *channel, enum side sender, - u64 msatoshi) + struct amount_msat msatoshi) { struct preimage r; struct sha256 rhash; @@ -321,7 +329,7 @@ int main(void) struct bitcoin_txid funding_txid; /* We test from both sides. */ struct channel *lchannel, *rchannel; - u64 funding_amount_satoshi; + struct amount_sat funding_amount; u32 *feerate_per_kw; unsigned int funding_output_index; struct keyset keyset; @@ -331,7 +339,7 @@ int main(void) struct pubkey *unknown; struct bitcoin_tx *raw_tx, **txs, **txs2; struct channel_config *local_config, *remote_config; - u64 to_local_msat, to_remote_msat; + struct amount_msat to_local, to_remote; const struct htlc **htlc_map, **htlcs; const u8 *funding_wscript, **wscripts; size_t i; @@ -373,20 +381,20 @@ int main(void) */ funding_txid = txid_from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be"); funding_output_index = 0; - funding_amount_satoshi = 10000000; + funding_amount = AMOUNT_SAT(10000000); remote_config->to_self_delay = 144; - local_config->dust_limit_satoshis = 546; + local_config->dust_limit = AMOUNT_SAT(546); /* This matters only because we check if added HTLC will create new * output, for fee considerations. */ - remote_config->dust_limit_satoshis = 546; - - local_config->max_htlc_value_in_flight_msat = -1ULL; - remote_config->max_htlc_value_in_flight_msat = -1ULL; - local_config->channel_reserve_satoshis = 0; - remote_config->channel_reserve_satoshis = 0; - local_config->htlc_minimum_msat = 0; - remote_config->htlc_minimum_msat = 0; + remote_config->dust_limit = AMOUNT_SAT(546); + + local_config->max_htlc_value_in_flight = AMOUNT_MSAT(-1ULL); + remote_config->max_htlc_value_in_flight = AMOUNT_MSAT(-1ULL); + local_config->channel_reserve = AMOUNT_SAT(0); + remote_config->channel_reserve = AMOUNT_SAT(0); + local_config->htlc_minimum = AMOUNT_MSAT(0); + remote_config->htlc_minimum = AMOUNT_MSAT(0); local_config->max_accepted_htlcs = 0xFFFF; remote_config->max_accepted_htlcs = 0xFFFF; @@ -441,31 +449,31 @@ int main(void) * local_feerate_per_kw: 15000 */ - to_local_msat = 7000000000; - to_remote_msat = 3000000000; + to_local = AMOUNT_MSAT(7000000000); + to_remote = AMOUNT_MSAT(3000000000); feerate_per_kw[LOCAL] = feerate_per_kw[REMOTE] = 15000; lchannel = new_full_channel(tmpctx, - &chainparams->genesis_blockhash, - &funding_txid, funding_output_index, - funding_amount_satoshi, to_local_msat, - feerate_per_kw, - local_config, - remote_config, - &localbase, &remotebase, - &local_funding_pubkey, - &remote_funding_pubkey, - LOCAL); + &chainparams->genesis_blockhash, + &funding_txid, funding_output_index, + funding_amount, to_local, + feerate_per_kw, + local_config, + remote_config, + &localbase, &remotebase, + &local_funding_pubkey, + &remote_funding_pubkey, + LOCAL); rchannel = new_full_channel(tmpctx, - &chainparams->genesis_blockhash, - &funding_txid, funding_output_index, - funding_amount_satoshi, to_remote_msat, - feerate_per_kw, - remote_config, - local_config, - &remotebase, &localbase, - &remote_funding_pubkey, - &local_funding_pubkey, - REMOTE); + &chainparams->genesis_blockhash, + &funding_txid, funding_output_index, + funding_amount, to_remote, + feerate_per_kw, + remote_config, + local_config, + &remotebase, &localbase, + &remote_funding_pubkey, + &local_funding_pubkey, + REMOTE); /* BOLT #3: * @@ -488,13 +496,13 @@ int main(void) keyset.other_htlc_key = keyset.other_payment_key; raw_tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, remote_config->to_self_delay, &keyset, feerate_per_kw[LOCAL], - local_config->dust_limit_satoshis, - to_local_msat, - to_remote_msat, + local_config->dust_limit, + to_local, + to_remote, NULL, &htlc_map, 0x2bb038521914 ^ 42, LOCAL); txs = channel_txs(tmpctx, &htlc_map, &wscripts, lchannel, &local_per_commitment_point, 42, LOCAL); @@ -515,21 +523,21 @@ int main(void) * to_remote_msat: 3000000000 * local_feerate_per_kw: 0 */ - to_local_msat = 6988000000; - to_remote_msat = 3000000000; + to_local = AMOUNT_MSAT(6988000000); + to_remote = AMOUNT_MSAT(3000000000); feerate_per_kw[LOCAL] = feerate_per_kw[REMOTE] = 0; /* Now, BOLT doesn't adjust owed amounts the same way we do * here: it's as if local side paid for all the HTLCs. We can * fix this by having local side offer an HTLC, and having * remote side accept it */ - send_and_fulfill_htlc(lchannel, LOCAL, 7000000); - send_and_fulfill_htlc(rchannel, REMOTE, 7000000); + send_and_fulfill_htlc(lchannel, LOCAL, AMOUNT_MSAT(7000000)); + send_and_fulfill_htlc(rchannel, REMOTE, AMOUNT_MSAT(7000000)); - assert(lchannel->view[LOCAL].owed_msat[LOCAL] - == rchannel->view[REMOTE].owed_msat[REMOTE]); - assert(lchannel->view[REMOTE].owed_msat[REMOTE] - == rchannel->view[LOCAL].owed_msat[LOCAL]); + assert(lchannel->view[LOCAL].owed[LOCAL].millisatoshis + == rchannel->view[REMOTE].owed[REMOTE].millisatoshis); + assert(lchannel->view[REMOTE].owed[REMOTE].millisatoshis + == rchannel->view[LOCAL].owed[LOCAL].millisatoshis); txs = channel_txs(tmpctx, &htlc_map, &wscripts, lchannel, &local_per_commitment_point, 42, LOCAL); @@ -544,10 +552,10 @@ int main(void) htlcs = include_htlcs(lchannel, LOCAL); include_htlcs(rchannel, REMOTE); - assert(lchannel->view[LOCAL].owed_msat[LOCAL] - == rchannel->view[REMOTE].owed_msat[REMOTE]); - assert(lchannel->view[REMOTE].owed_msat[REMOTE] - == rchannel->view[LOCAL].owed_msat[LOCAL]); + assert(lchannel->view[LOCAL].owed[LOCAL].millisatoshis + == rchannel->view[REMOTE].owed[REMOTE].millisatoshis); + assert(lchannel->view[REMOTE].owed[REMOTE].millisatoshis + == rchannel->view[LOCAL].owed[LOCAL].millisatoshis); txs = channel_txs(tmpctx, &htlc_map, &wscripts, lchannel, &local_per_commitment_point, 42, LOCAL); @@ -612,13 +620,13 @@ int main(void) rchannel->view[REMOTE].feerate_per_kw = feerate_per_kw[REMOTE]; raw_tx = commit_tx(tmpctx, &funding_txid, funding_output_index, - funding_amount_satoshi, + funding_amount, LOCAL, remote_config->to_self_delay, &keyset, feerate_per_kw[LOCAL], - local_config->dust_limit_satoshis, - to_local_msat, - to_remote_msat, + local_config->dust_limit, + to_local, + to_remote, htlcs, &htlc_map, 0x2bb038521914 ^ 42, LOCAL); diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index cd85dbf68ce1..594644223d07 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -24,6 +25,12 @@ int test_printf(const char *format, ...); #undef main /* AUTOGENERATED MOCKS START */ +/* 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_less */ +bool amount_sat_less(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_less 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(); } diff --git a/closingd/Makefile b/closingd/Makefile index 639225f8a5fa..d9e5768ef1ab 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -42,6 +42,7 @@ $(LIGHTNINGD_CLOSING_OBJS): $(LIGHTNINGD_HEADERS) # Common source we use. CLOSINGD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bip32.o \ common/close_tx.o \ diff --git a/closingd/closing_wire.csv b/closingd/closing_wire.csv index 43abd60810a5..623476d52daf 100644 --- a/closingd/closing_wire.csv +++ b/closingd/closing_wire.csv @@ -5,16 +5,16 @@ closing_init,2001 closing_init,,crypto_state,struct crypto_state closing_init,,funding_txid,struct bitcoin_txid closing_init,,funding_txout,u16 -closing_init,,funding_satoshi,u64 +closing_init,,funding_satoshi,struct amount_sat closing_init,,local_fundingkey,struct pubkey closing_init,,remote_fundingkey,struct pubkey closing_init,,funder,enum side -closing_init,,local_msatoshi,u64 -closing_init,,remote_msatoshi,u64 -closing_init,,our_dust_limit,u64 -closing_init,,min_fee_satoshi,u64 -closing_init,,fee_limit_satoshi,u64 -closing_init,,initial_fee_satoshi,u64 +closing_init,,local_sat,struct amount_sat +closing_init,,remote_sat,struct amount_sat +closing_init,,our_dust_limit,struct amount_sat +closing_init,,min_fee_satoshi,struct amount_sat +closing_init,,fee_limit_satoshi,struct amount_sat +closing_init,,initial_fee_satoshi,struct amount_sat closing_init,,local_scriptpubkey_len,u16 closing_init,,local_scriptpubkey,local_scriptpubkey_len*u8 closing_init,,remote_scriptpubkey_len,u16 diff --git a/closingd/closingd.c b/closingd/closingd.c index 28d0449e1fb5..750c34d5d95f 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -35,46 +35,53 @@ static struct bitcoin_tx *close_tx(const tal_t *ctx, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshi, - const u64 satoshi_out[NUM_SIDES], + struct amount_sat funding, + const struct amount_sat out[NUM_SIDES], enum side funder, - uint64_t fee, - uint64_t dust_limit) + struct amount_sat fee, + struct amount_sat dust_limit) { struct bitcoin_tx *tx; + struct amount_sat out_minus_fee[NUM_SIDES]; - if (satoshi_out[funder] < fee) + out_minus_fee[LOCAL] = out[LOCAL]; + out_minus_fee[REMOTE] = out[REMOTE]; + if (!amount_sat_sub(&out_minus_fee[funder], out[funder], fee)) peer_failed(cs, channel_id, - "Funder cannot afford fee %"PRIu64 - " (%"PRIu64" and %"PRIu64")", - fee, satoshi_out[LOCAL], - satoshi_out[REMOTE]); - - status_trace("Making close tx at = %"PRIu64"/%"PRIu64" fee %"PRIu64, - satoshi_out[LOCAL], satoshi_out[REMOTE], fee); + "Funder cannot afford fee %s (%s and %s)", + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct amount_sat, + &out[LOCAL]), + type_to_string(tmpctx, struct amount_sat, + &out[REMOTE])); + + status_trace("Making close tx at = %s/%s fee %s", + type_to_string(tmpctx, struct amount_sat, &out[LOCAL]), + type_to_string(tmpctx, struct amount_sat, &out[REMOTE]), + type_to_string(tmpctx, struct amount_sat, &fee)); /* FIXME: We need to allow this! */ tx = create_close_tx(ctx, scriptpubkey[LOCAL], scriptpubkey[REMOTE], funding_txid, funding_txout, - funding_satoshi, - satoshi_out[LOCAL] - (funder == LOCAL ? fee : 0), - satoshi_out[REMOTE] - (funder == REMOTE ? fee : 0), + funding, + out_minus_fee[LOCAL], + out_minus_fee[REMOTE], dust_limit); if (!tx) peer_failed(cs, channel_id, "Both outputs below dust limit:" - " funding = %"PRIu64 - " fee = %"PRIu64 - " dust_limit = %"PRIu64 - " LOCAL = %"PRIu64 - " REMOTE = %"PRIu64, - funding_satoshi, - fee, - dust_limit, - satoshi_out[LOCAL], - satoshi_out[REMOTE]); + " funding = %s" + " fee = %s" + " dust_limit = %s" + " LOCAL = %s" + " REMOTE = %s", + type_to_string(tmpctx, struct amount_sat, &funding), + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct amount_sat, &dust_limit), + type_to_string(tmpctx, struct amount_sat, &out[LOCAL]), + type_to_string(tmpctx, struct amount_sat, &out[REMOTE])); return tx; } @@ -170,11 +177,11 @@ static void send_offer(struct crypto_state *cs, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshi, - const u64 satoshi_out[NUM_SIDES], + struct amount_sat funding, + const struct amount_sat out[NUM_SIDES], enum side funder, - uint64_t our_dust_limit, - uint64_t fee_to_offer) + struct amount_sat our_dust_limit, + struct amount_sat fee_to_offer) { struct bitcoin_tx *tx; struct bitcoin_signature our_sig; @@ -190,8 +197,8 @@ static void send_offer(struct crypto_state *cs, scriptpubkey, funding_txid, funding_txout, - funding_satoshi, - satoshi_out, + funding, + out, funder, fee_to_offer, our_dust_limit); /* BOLT #3: @@ -206,14 +213,15 @@ static void send_offer(struct crypto_state *cs, take(towire_hsm_sign_mutual_close_tx(NULL, tx, &funding_pubkey[REMOTE], - funding_satoshi))); + funding))); msg = wire_sync_read(tmpctx, HSM_FD); if (!fromwire_hsm_sign_tx_reply(msg, &our_sig)) status_failed(STATUS_FAIL_HSM_IO, "Bad hsm_sign_mutual_close_tx reply %s", tal_hex(tmpctx, msg)); - status_trace("sending fee offer %"PRIu64, fee_to_offer); + status_trace("sending fee offer %s", + type_to_string(tmpctx, struct amount_sat, &fee_to_offer)); assert(our_sig.sighash_type == SIGHASH_ALL); msg = towire_closing_signed(NULL, channel_id, fee_to_offer, &our_sig.s); @@ -237,22 +245,23 @@ static void tell_master_their_offer(const struct bitcoin_signature *their_sig, } /* Returns fee they offered. */ -static uint64_t receive_offer(struct crypto_state *cs, - const struct channel_id *channel_id, - const struct pubkey funding_pubkey[NUM_SIDES], - const u8 *funding_wscript, - u8 *scriptpubkey[NUM_SIDES], - const struct bitcoin_txid *funding_txid, - unsigned int funding_txout, - u64 funding_satoshi, - const u64 satoshi_out[NUM_SIDES], - enum side funder, - uint64_t our_dust_limit, - u64 min_fee_to_accept) +static struct amount_sat +receive_offer(struct crypto_state *cs, + const struct channel_id *channel_id, + const struct pubkey funding_pubkey[NUM_SIDES], + const u8 *funding_wscript, + u8 *scriptpubkey[NUM_SIDES], + const struct bitcoin_txid *funding_txid, + unsigned int funding_txout, + struct amount_sat funding, + const struct amount_sat out[NUM_SIDES], + enum side funder, + struct amount_sat our_dust_limit, + struct amount_sat min_fee_to_accept) { u8 *msg; struct channel_id their_channel_id; - u64 received_fee; + struct amount_sat received_fee; struct bitcoin_signature their_sig; struct bitcoin_tx *tx; @@ -295,20 +304,20 @@ static uint64_t receive_offer(struct crypto_state *cs, scriptpubkey, funding_txid, funding_txout, - funding_satoshi, - satoshi_out, funder, received_fee, our_dust_limit); + funding, + out, funder, received_fee, our_dust_limit); if (!check_tx_sig(tx, 0, NULL, funding_wscript, &funding_pubkey[REMOTE], &their_sig)) { /* Trim it by reducing their output to minimum */ struct bitcoin_tx *trimmed; - u64 trimming_satoshi_out[NUM_SIDES]; + struct amount_sat trimming_out[NUM_SIDES]; if (funder == REMOTE) - trimming_satoshi_out[REMOTE] = received_fee; + trimming_out[REMOTE] = received_fee; else - trimming_satoshi_out[REMOTE] = 0; - trimming_satoshi_out[LOCAL] = satoshi_out[LOCAL]; + trimming_out[REMOTE] = AMOUNT_SAT(0); + trimming_out[LOCAL] = out[LOCAL]; /* BOLT #3: * @@ -324,8 +333,8 @@ static uint64_t receive_offer(struct crypto_state *cs, scriptpubkey, funding_txid, funding_txout, - funding_satoshi, - trimming_satoshi_out, + funding, + trimming_out, funder, received_fee, our_dust_limit); if (!trimmed || !check_tx_sig(trimmed, 0, NULL, funding_wscript, @@ -345,10 +354,11 @@ static uint64_t receive_offer(struct crypto_state *cs, tx = trimmed; } - status_trace("Received fee offer %"PRIu64, received_fee); + status_trace("Received fee offer %s", + type_to_string(tmpctx, struct amount_sat, &received_fee)); /* Master sorts out what is best offer, we just tell it any above min */ - if (received_fee >= min_fee_to_accept) { + if (amount_sat_greater_eq(received_fee, min_fee_to_accept)) { status_trace("...offer is reasonable"); tell_master_their_offer(&their_sig, tx); } @@ -358,14 +368,14 @@ static uint64_t receive_offer(struct crypto_state *cs, struct feerange { enum side higher_side; - u64 min, max; + struct amount_sat min, max; }; static void init_feerange(struct feerange *feerange, - u64 commitment_fee, - const u64 offer[NUM_SIDES]) + struct amount_sat commitment_fee, + const struct amount_sat offer[NUM_SIDES]) { - feerange->min = 0; + feerange->min = AMOUNT_SAT(0); /* BOLT #2: * @@ -375,58 +385,86 @@ static void init_feerange(struct feerange *feerange, */ feerange->max = commitment_fee; - if (offer[LOCAL] > offer[REMOTE]) + if (amount_sat_greater(offer[LOCAL], offer[REMOTE])) feerange->higher_side = LOCAL; else feerange->higher_side = REMOTE; - status_trace("Feerange init %"PRIu64"-%"PRIu64", %s higher", - feerange->min, feerange->max, + status_trace("Feerange init %s-%s, %s higher", + type_to_string(tmpctx, struct amount_sat, &feerange->min), + type_to_string(tmpctx, struct amount_sat, &feerange->max), feerange->higher_side == LOCAL ? "local" : "remote"); } static void adjust_feerange(struct feerange *feerange, - u64 offer, enum side side) + struct amount_sat offer, enum side side) { + bool ok; + /* BOLT #2: * * - MUST propose a value "strictly between" the received * `fee_satoshis` and its previously-sent `fee_satoshis`. */ if (side == feerange->higher_side) - feerange->max = offer - 1; + ok = amount_sat_sub(&feerange->max, offer, AMOUNT_SAT(1)); else - feerange->min = offer + 1; + ok = amount_sat_add(&feerange->min, offer, AMOUNT_SAT(1)); - status_trace("Feerange %s update %"PRIu64": now %"PRIu64"-%"PRIu64, + status_trace("Feerange %s update %s: now %s-%s", side == LOCAL ? "local" : "remote", - offer, feerange->min, feerange->max); + type_to_string(tmpctx, struct amount_sat, &offer), + type_to_string(tmpctx, struct amount_sat, &feerange->min), + type_to_string(tmpctx, struct amount_sat, &feerange->max)); + + if (!ok) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Overflow in updating fee range"); } /* Figure out what we should offer now. */ -static u64 adjust_offer(struct crypto_state *cs, - const struct channel_id *channel_id, - const struct feerange *feerange, - u64 remote_offer, - u64 min_fee_to_accept) +static struct amount_sat adjust_offer(struct crypto_state *cs, + const struct channel_id *channel_id, + const struct feerange *feerange, + struct amount_sat remote_offer, + struct amount_sat min_fee_to_accept) { + struct amount_sat min_plus_one, avg; + /* Within 1 satoshi? Agree. */ - if (feerange->min + 1 >= feerange->max) + if (!amount_sat_add(&min_plus_one, feerange->min, AMOUNT_SAT(1))) + peer_failed(cs, channel_id, + "Fee offer %s min too large", + type_to_string(tmpctx, struct amount_sat, + &feerange->min)); + + if (amount_sat_greater_eq(min_plus_one, feerange->max)) return remote_offer; /* Max is below our minimum acceptable? */ - if (feerange->max < min_fee_to_accept) + if (amount_sat_less(feerange->max, min_fee_to_accept)) peer_failed(cs, channel_id, - "Feerange %"PRIu64"-%"PRIu64 - " below minimum acceptable %"PRIu64, - feerange->min, feerange->max, - min_fee_to_accept); + "Feerange %s-%s" + " below minimum acceptable %s", + type_to_string(tmpctx, struct amount_sat, + &feerange->min), + type_to_string(tmpctx, struct amount_sat, + &feerange->max), + type_to_string(tmpctx, struct amount_sat, + &min_fee_to_accept)); /* Bisect between our minimum and max. */ - if (feerange->min > min_fee_to_accept) + if (amount_sat_greater(feerange->min, min_fee_to_accept)) min_fee_to_accept = feerange->min; - return (feerange->max + min_fee_to_accept)/2; + if (!amount_sat_add(&avg, feerange->max, min_fee_to_accept)) + peer_failed(cs, channel_id, + "Fee offer %s max too large", + type_to_string(tmpctx, struct amount_sat, + &feerange->max)); + + avg.satoshis /= 2; /* Raw: average calculation */ + return avg; } #if DEVELOPER @@ -460,9 +498,9 @@ int main(int argc, char *argv[]) struct pubkey funding_pubkey[NUM_SIDES]; struct bitcoin_txid funding_txid; u16 funding_txout; - u64 funding_satoshi, satoshi_out[NUM_SIDES]; - u64 our_dust_limit; - u64 min_fee_to_accept, commitment_fee, offer[NUM_SIDES]; + struct amount_sat funding, out[NUM_SIDES]; + struct amount_sat our_dust_limit; + struct amount_sat min_fee_to_accept, commitment_fee, offer[NUM_SIDES]; struct feerange feerange; enum side funder; u8 *scriptpubkey[NUM_SIDES], *funding_wscript, *final_scriptpubkey; @@ -480,12 +518,12 @@ int main(int argc, char *argv[]) if (!fromwire_closing_init(ctx, msg, &cs, &funding_txid, &funding_txout, - &funding_satoshi, + &funding, &funding_pubkey[LOCAL], &funding_pubkey[REMOTE], &funder, - &satoshi_out[LOCAL], - &satoshi_out[REMOTE], + &out[LOCAL], + &out[REMOTE], &our_dust_limit, &min_fee_to_accept, &commitment_fee, &offer[LOCAL], @@ -499,10 +537,13 @@ int main(int argc, char *argv[]) &final_scriptpubkey)) master_badmsg(WIRE_CLOSING_INIT, msg); - status_trace("satoshi_out = %"PRIu64"/%"PRIu64, - satoshi_out[LOCAL], satoshi_out[REMOTE]); - status_trace("dustlimit = %"PRIu64, our_dust_limit); - status_trace("fee = %"PRIu64, offer[LOCAL]); + status_trace("out = %s/%s", + type_to_string(tmpctx, struct amount_sat, &out[LOCAL]), + type_to_string(tmpctx, struct amount_sat, &out[REMOTE])); + status_trace("dustlimit = %s", + type_to_string(tmpctx, struct amount_sat, &our_dust_limit)); + status_trace("fee = %s", + type_to_string(tmpctx, struct amount_sat, &offer[LOCAL])); derive_channel_id(&channel_id, &funding_txid, funding_txout); funding_wscript = bitcoin_redeem_2of2(ctx, @@ -517,9 +558,13 @@ int main(int argc, char *argv[]) /* We don't need this any more */ tal_free(final_scriptpubkey); - peer_billboard(true, "Negotiating closing fee between %"PRIu64 - " and %"PRIu64" satoshi (ideal %"PRIu64")", - min_fee_to_accept, commitment_fee, offer[LOCAL]); + peer_billboard(true, "Negotiating closing fee between %s" + " and %s satoshi (ideal %s)", + type_to_string(tmpctx, struct amount_sat, + &min_fee_to_accept), + type_to_string(tmpctx, struct amount_sat, + &commitment_fee), + type_to_string(tmpctx, struct amount_sat, &offer[LOCAL])); /* BOLT #2: * @@ -534,7 +579,7 @@ int main(int argc, char *argv[]) send_offer(&cs, &channel_id, funding_pubkey, scriptpubkey, &funding_txid, funding_txout, - funding_satoshi, satoshi_out, funder, + funding, out, funder, our_dust_limit, offer[LOCAL]); } else { @@ -544,15 +589,17 @@ int main(int argc, char *argv[]) else peer_billboard(false, "Waiting for their initial" " closing fee offer:" - " ours was %"PRIu64" satoshi", - offer[LOCAL]); + " ours was %s", + type_to_string(tmpctx, + struct amount_sat, + &offer[LOCAL])); offer[REMOTE] = receive_offer(&cs, &channel_id, funding_pubkey, funding_wscript, scriptpubkey, &funding_txid, - funding_txout, funding_satoshi, - satoshi_out, funder, + funding_txout, funding, + out, funder, our_dust_limit, min_fee_to_accept); } @@ -565,7 +612,7 @@ int main(int argc, char *argv[]) adjust_feerange(&feerange, offer[funder], funder); /* Now any extra rounds required. */ - while (offer[LOCAL] != offer[REMOTE]) { + while (!amount_sat_eq(offer[LOCAL], offer[REMOTE])) { /* Still don't agree: adjust feerange based on previous offer */ adjust_feerange(&feerange, offer[!whose_turn], !whose_turn); @@ -578,7 +625,7 @@ int main(int argc, char *argv[]) send_offer(&cs, &channel_id, funding_pubkey, scriptpubkey, &funding_txid, funding_txout, - funding_satoshi, satoshi_out, funder, + funding, out, funder, our_dust_limit, offer[LOCAL]); } else { @@ -592,8 +639,8 @@ int main(int argc, char *argv[]) funding_pubkey, funding_wscript, scriptpubkey, &funding_txid, - funding_txout, funding_satoshi, - satoshi_out, funder, + funding_txout, funding, + out, funder, our_dust_limit, min_fee_to_accept); } diff --git a/common/Makefile b/common/Makefile index a8a6822106e4..cb4b4133c3bd 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,4 +1,5 @@ COMMON_SRC_NOGEN := \ + common/amount.c \ common/base32.c \ common/bech32.c \ common/bech32_util.c \ diff --git a/common/amount.c b/common/amount.c new file mode 100644 index 000000000000..81f474eb0c58 --- /dev/null +++ b/common/amount.c @@ -0,0 +1,408 @@ +#include +#include +#include +#include +#include +#include +#include + +bool amount_sat_to_msat(struct amount_msat *msat, + struct amount_sat sat) +{ + if (mul_overflows_u64(sat.satoshis, MSAT_PER_SAT)) + return false; + msat->millisatoshis = sat.satoshis * MSAT_PER_SAT; + return true; +} + +/* You can always truncate millisatoshis->satoshis. */ +struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat) +{ + struct amount_sat sat; + + sat.satoshis = msat.millisatoshis / MSAT_PER_SAT; + return sat; +} + +/* Different formatting by amounts: grs, sat and msat */ +const char *fmt_amount_msat_btc(const tal_t *ctx, + const struct amount_msat *msat, + bool append_unit) +{ + return tal_fmt(ctx, "%"PRIu64".%011"PRIu64"%s", + msat->millisatoshis / MSAT_PER_BTC, + msat->millisatoshis % MSAT_PER_BTC, + append_unit ? "grs" : ""); +} + +const char *fmt_amount_msat(const tal_t *ctx, const struct amount_msat *msat) +{ + return tal_fmt(ctx, "%"PRIu64"msat", msat->millisatoshis); +} +REGISTER_TYPE_TO_STRING(amount_msat, fmt_amount_msat); + +const char *fmt_amount_sat_btc(const tal_t *ctx, + const struct amount_sat *sat, + bool append_unit) +{ + return tal_fmt(ctx, "%"PRIu64".%08"PRIu64"%s", + sat->satoshis / SAT_PER_BTC, + sat->satoshis % SAT_PER_BTC, + append_unit ? "grs" : ""); +} + +const char *fmt_amount_sat(const tal_t *ctx, const struct amount_sat *sat) +{ + return tal_fmt(ctx, "%"PRIu64"sat", sat->satoshis); +} +REGISTER_TYPE_TO_STRING(amount_sat, fmt_amount_sat); + +static bool breakup(const char *str, size_t slen, + /* Length of first numeric part. */ + size_t *whole_number_len, + /* Pointer to post-decimal part, or NULL */ + const char **post_decimal_ptr, + size_t *post_decimal_len, + /* Pointer to suffix, or NULL */ + const char **suffix_ptr, + size_t *suffix_len) +{ + size_t i; + + *whole_number_len = 0; + *post_decimal_len = 0; + *post_decimal_ptr = NULL; + *suffix_ptr = NULL; + *suffix_len = 0; + + for (i = 0;; i++) { + if (i >= slen) + return i != 0; + if (cisdigit(str[i])) + (*whole_number_len)++; + else + break; + } + + if (str[i] == '.') { + i++; + *post_decimal_ptr = str + i; + for (;; i++) { + /* True if > 0 decimals. */ + if (i >= slen) + return str + i != *post_decimal_ptr; + if (cisdigit(str[i])) + (*post_decimal_len)++; + else + break; + } + } + + *suffix_ptr = str + i; + *suffix_len = slen - i; + return true; +} + +static bool from_number(u64 *res, const char *s, size_t len, int tens_factor) +{ + if (len == 0) + return false; + + *res = 0; + for (size_t i = 0; i < len; i++) { + if (mul_overflows_u64(*res, 10)) + return false; + *res *= 10; + assert(cisdigit(s[i])); + if (add_overflows_u64(*res, s[i] - '0')) + return false; + *res += s[i] - '0'; + } + while (tens_factor > 0) { + if (mul_overflows_u64(*res, 10)) + return false; + *res *= 10; + tens_factor--; + } + return true; +} + +static bool from_numbers(u64 *res, + const char *s1, size_t len1, int tens_factor, + const char *s2, size_t len2) +{ + u64 p1, p2; + if (len2 > tens_factor) + return false; + + if (!from_number(&p1, s1, len1, tens_factor) + || !from_number(&p2, s2, len2, tens_factor - len2)) + return false; + + if (add_overflows_u64(p1, p2)) + return false; + + *res = p1 + p2; + return true; +} + +/* Valid strings: + * [0-9]+ => millisatoshi. + * [0-9]+msat => millisatoshi. + * [0-9]+sat => *1000 -> millisatopshi. + * [0-9]+.[0-9]{1,11}btc => millisatoshi. + */ +bool parse_amount_msat(struct amount_msat *msat, const char *s, size_t slen) +{ + size_t whole_number_len, post_decimal_len, suffix_len; + const char *post_decimal_ptr, *suffix_ptr; + + if (!breakup(s, slen, &whole_number_len, + &post_decimal_ptr, &post_decimal_len, + &suffix_ptr, &suffix_len)) + return false; + + if (!post_decimal_ptr && !suffix_ptr) + return from_number(&msat->millisatoshis, s, whole_number_len, 0); + if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "msat")) + return from_number(&msat->millisatoshis, s, whole_number_len, 0); + if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "sat")) + return from_number(&msat->millisatoshis, s, whole_number_len, 3); + if (post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "grs")) + return from_numbers(&msat->millisatoshis, + s, whole_number_len, 11, + post_decimal_ptr, post_decimal_len); + return false; +} + +/* Valid strings: + * [0-9]+ => satoshi. + * [0-9]+sat => satoshi. + * [0-9]+000msat => satoshi. + * [0-9]+.[0-9]{1,8}btc => satoshi. + */ +bool parse_amount_sat(struct amount_sat *sat, const char *s, size_t slen) +{ + size_t whole_number_len, post_decimal_len, suffix_len; + const char *post_decimal_ptr, *suffix_ptr; + + if (!breakup(s, slen, &whole_number_len, + &post_decimal_ptr, &post_decimal_len, + &suffix_ptr, &suffix_len)) + return false; + + if (!post_decimal_ptr && !suffix_ptr) + return from_number(&sat->satoshis, s, whole_number_len, 0); + if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "sat")) + return from_number(&sat->satoshis, s, whole_number_len, 0); + if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "msat")) { + if (!memends(s, whole_number_len, "000", strlen("000"))) + return false; + return from_number(&sat->satoshis, s, whole_number_len - 3, 0); + } + if (post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "grs")) + return from_numbers(&sat->satoshis, + s, whole_number_len, 8, + post_decimal_ptr, post_decimal_len); + + return false; +} + +WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val, + struct amount_msat a, + struct amount_msat b) +{ + if (add_overflows_u64(a.millisatoshis, b.millisatoshis)) + return false; + + val->millisatoshis = a.millisatoshis + b.millisatoshis; + return true; +} + +WARN_UNUSED_RESULT bool amount_msat_sub(struct amount_msat *val, + struct amount_msat a, + struct amount_msat b) +{ + if (a.millisatoshis < b.millisatoshis) + return false; + + val->millisatoshis = a.millisatoshis - b.millisatoshis; + return true; +} + +WARN_UNUSED_RESULT bool amount_sat_add(struct amount_sat *val, + struct amount_sat a, + struct amount_sat b) +{ + if (add_overflows_u64(a.satoshis, b.satoshis)) + return false; + + val->satoshis = a.satoshis + b.satoshis; + return true; +} + +WARN_UNUSED_RESULT bool amount_sat_sub(struct amount_sat *val, + struct amount_sat a, + struct amount_sat b) +{ + if (a.satoshis < b.satoshis) + return false; + + val->satoshis = a.satoshis - b.satoshis; + return true; +} + +WARN_UNUSED_RESULT bool amount_msat_sub_sat(struct amount_msat *val, + struct amount_msat a, + struct amount_sat b) +{ + struct amount_msat msatb; + + if (!amount_sat_to_msat(&msatb, b)) + return false; + + return amount_msat_sub(val, a, msatb); +} + +WARN_UNUSED_RESULT bool amount_sat_sub_msat(struct amount_msat *val, + struct amount_sat a, + struct amount_msat b) +{ + struct amount_msat msata; + + if (!amount_sat_to_msat(&msata, a)) + return false; + + return amount_msat_sub(val, msata, b); +} + +bool amount_sat_eq(struct amount_sat a, struct amount_sat b) +{ + return a.satoshis == b.satoshis; +} + +bool amount_msat_eq(struct amount_msat a, struct amount_msat b) +{ + return a.millisatoshis == b.millisatoshis; +} + +bool amount_sat_greater(struct amount_sat a, struct amount_sat b) +{ + return a.satoshis > b.satoshis; +} + +bool amount_msat_greater(struct amount_msat a, struct amount_msat b) +{ + return a.millisatoshis > b.millisatoshis; +} + +bool amount_sat_greater_eq(struct amount_sat a, struct amount_sat b) +{ + return a.satoshis >= b.satoshis; +} + +bool amount_msat_greater_eq(struct amount_msat a, struct amount_msat b) +{ + return a.millisatoshis >= b.millisatoshis; +} + +bool amount_sat_less(struct amount_sat a, struct amount_sat b) +{ + return a.satoshis < b.satoshis; +} + +bool amount_msat_less(struct amount_msat a, struct amount_msat b) +{ + return a.millisatoshis < b.millisatoshis; +} + +bool amount_sat_less_eq(struct amount_sat a, struct amount_sat b) +{ + return a.satoshis <= b.satoshis; +} + +bool amount_msat_less_eq(struct amount_msat a, struct amount_msat b) +{ + return a.millisatoshis <= b.millisatoshis; +} + +bool amount_msat_greater_sat(struct amount_msat msat, struct amount_sat sat) +{ + struct amount_msat msat_from_sat; + + if (!amount_sat_to_msat(&msat_from_sat, sat)) + return false; + return msat.millisatoshis > msat_from_sat.millisatoshis; +} + +bool amount_msat_greater_eq_sat(struct amount_msat msat, struct amount_sat sat) +{ + struct amount_msat msat_from_sat; + + if (!amount_sat_to_msat(&msat_from_sat, sat)) + return false; + return msat.millisatoshis >= msat_from_sat.millisatoshis; +} + +bool amount_msat_less_sat(struct amount_msat msat, struct amount_sat sat) +{ + struct amount_msat msat_from_sat; + + if (!amount_sat_to_msat(&msat_from_sat, sat)) + return false; + return msat.millisatoshis < msat_from_sat.millisatoshis; +} + +bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat) +{ + struct amount_msat msat_from_sat; + + if (!amount_sat_to_msat(&msat_from_sat, sat)) + return false; + return msat.millisatoshis <= msat_from_sat.millisatoshis; +} + +bool amount_msat_fee(struct amount_msat *fee, + struct amount_msat amt, + u32 fee_base_msat, + u32 fee_proportional_millionths) +{ + struct amount_msat fee_base, fee_prop; + + /* BOLT #7: + * + * - SHOULD accept HTLCs that pay a fee equal to or greater than: + * - fee_base_msat + ( amount_to_forward * fee_proportional_millionths / 1000000 ) + */ + fee_base.millisatoshis = fee_base_msat; + + if (mul_overflows_u64(amt.millisatoshis, fee_proportional_millionths)) + return false; + fee_prop.millisatoshis = amt.millisatoshis * fee_proportional_millionths + / 1000000; + + return amount_msat_add(fee, fee_base, fee_prop); +} + +bool amount_msat_add_fee(struct amount_msat *amt, + u32 fee_base_msat, + u32 fee_proportional_millionths) +{ + struct amount_msat fee; + + if (!amount_msat_fee(&fee, *amt, + fee_base_msat, fee_proportional_millionths)) + return false; + return amount_msat_add(amt, *amt, fee); +} + +struct amount_sat amount_tx_fee(u32 fee_per_kw, size_t weight) +{ + struct amount_sat fee; + + /* If this overflows, weight must be > 2^32, which is not a real tx */ + assert(!mul_overflows_u64(fee_per_kw, weight)); + fee.satoshis = fee_per_kw * weight / 1000; + + return fee; +} diff --git a/common/amount.h b/common/amount.h new file mode 100644 index 000000000000..6df3e7c646f3 --- /dev/null +++ b/common/amount.h @@ -0,0 +1,146 @@ +#ifndef LIGHTNING_COMMON_AMOUNT_H +#define LIGHTNING_COMMON_AMOUNT_H +#include "config.h" +#include +#include +#include +#include + +#define MSAT_PER_SAT ((u64)1000) +#define SAT_PER_BTC ((u64)100000000) +#define MSAT_PER_BTC (MSAT_PER_SAT * SAT_PER_BTC) + +/* Use these to wrap amounts, for typesafety. Please use ops where possible, + * rather than accessing the members directly. */ +struct amount_sat { + /* Amount in satoshis. */ + u64 satoshis; +}; + +struct amount_msat { + /* Amount in millisatoshis. */ + u64 millisatoshis; +}; + +/* For constants only: others must be built from primitives! */ +#if HAVE_BUILTIN_CONSTANT_P +#define AMOUNT_MUST_BE_CONST(c) BUILD_ASSERT_OR_ZERO(IS_COMPILE_CONSTANT(c)) +#else +#define AMOUNT_MUST_BE_CONST(c) 0 +#endif + +/* GCC 4.8.5 (Centos 7.6!) thinks struct casts are not constants, so we + * need to not use a cast for static initializations. */ +#define AMOUNT_MSAT_INIT(msat) \ + { .millisatoshis = (msat) } +#define AMOUNT_SAT_INIT(sat) \ + { .satoshis = (sat) } + +#define AMOUNT_MSAT(constant) \ + ((struct amount_msat){(constant) + AMOUNT_MUST_BE_CONST(constant)}) + +#define AMOUNT_SAT(constant) \ + ((struct amount_sat){(constant) + AMOUNT_MUST_BE_CONST(constant)}) + +/* You may not always be able to convert satoshis->millisatoshis. */ +WARN_UNUSED_RESULT bool amount_sat_to_msat(struct amount_msat *msat, + struct amount_sat sat); + +/* You can always truncate millisatoshis->satoshis. */ +struct amount_sat amount_msat_to_sat_round_down(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, + struct amount_msat b); +WARN_UNUSED_RESULT bool amount_msat_sub(struct amount_msat *val, + struct amount_msat a, + struct amount_msat b); +WARN_UNUSED_RESULT bool amount_sat_add(struct amount_sat *val, + struct amount_sat a, + struct amount_sat b); +WARN_UNUSED_RESULT bool amount_sat_sub(struct amount_sat *val, + struct amount_sat a, + struct amount_sat b); +WARN_UNUSED_RESULT bool amount_msat_sub_sat(struct amount_msat *val, + struct amount_msat a, + struct amount_sat b); +WARN_UNUSED_RESULT bool amount_sat_sub_msat(struct amount_msat *val, + struct amount_sat a, + struct amount_msat b); + +/* Is a == b? */ +bool amount_sat_eq(struct amount_sat a, struct amount_sat b); +bool amount_msat_eq(struct amount_msat a, struct amount_msat b); + +/* Is a > b? */ +bool amount_sat_greater(struct amount_sat a, struct amount_sat b); +bool amount_msat_greater(struct amount_msat a, struct amount_msat b); + +/* Is a >= b */ +bool amount_sat_greater_eq(struct amount_sat a, struct amount_sat b); +bool amount_msat_greater_eq(struct amount_msat a, struct amount_msat b); + +/* Is a < b? */ +bool amount_sat_less(struct amount_sat a, struct amount_sat b); +bool amount_msat_less(struct amount_msat a, struct amount_msat b); + +/* Is a <= b? */ +bool amount_sat_less_eq(struct amount_sat a, struct amount_sat b); +bool amount_msat_less_eq(struct amount_msat a, struct amount_msat b); + +/* Is msat > sat? */ +bool amount_msat_greater_sat(struct amount_msat msat, struct amount_sat sat); +/* Is msat >= sat? */ +bool amount_msat_greater_eq_sat(struct amount_msat msat, struct amount_sat sat); +/* Is msat < sat? */ +bool amount_msat_less_sat(struct amount_msat msat, struct amount_sat sat); +/* Is msat <= sat? */ +bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat); + +/* Common operation: what is the HTLC fee for given feerate? Can overflow! */ +WARN_UNUSED_RESULT bool amount_msat_fee(struct amount_msat *fee, + struct amount_msat amt, + u32 fee_base_msat, + u32 fee_proportional_millionths); + +/* Same, but add into amt. */ +WARN_UNUSED_RESULT bool amount_msat_add_fee(struct amount_msat *amt, + u32 fee_base_msat, + u32 fee_proportional_millionths); + +/* What is the fee for this tx weight? */ +struct amount_sat amount_tx_fee(u32 fee_per_kw, size_t weight); + +/* Different formatting by amounts: btc, sat and msat */ +/* => 1.23456789012btc (11 decimals!) */ +const char *fmt_amount_msat_btc(const tal_t *ctx, + const struct amount_msat *msat, + bool append_unit); +/* => 1234msat */ +const char *fmt_amount_msat(const tal_t *ctx, const struct amount_msat *msat); + +/* => 1.23456789btc (8 decimals!) */ +const char *fmt_amount_sat_btc(const tal_t *ctx, + const struct amount_sat *sat, + bool append_unit); +/* => 1234sat */ +const char *fmt_amount_sat(const tal_t *ctx, const struct amount_sat *sat); + +/* Valid strings: + * [0-9]+ => millisatoshi. + * [0-9]+msat => millisatoshi. + * [0-9]+sat => *1000 -> millisatopshi. + * [0-9]+.[0-9]{1,11}btc => millisatoshi. + */ +bool parse_amount_msat(struct amount_msat *msat, const char *s, size_t slen); + +/* Valid strings: + * [0-9]+ => satoshi. + * [0-9]+sat => satoshi. + * [0-9]+000msat => satoshi. + * [0-9]+.[0-9]{1,8}btc => satoshi. + */ +bool parse_amount_sat(struct amount_sat *sat, const char *s, size_t slen); + +#endif /* LIGHTNING_COMMON_AMOUNT_H */ diff --git a/common/bolt11.c b/common/bolt11.c index 01f88c111e26..30acdd500d42 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -22,9 +22,6 @@ #include #include -/* 1000 * 10^8 millisatoshi == 1 groestlcoin */ -#define MSAT_PER_BTC 100000000000ULL - struct multiplier { const char letter; /* We can't represent p postfix to msat, so we multiply this by 10 */ @@ -436,7 +433,8 @@ static char *decode_r(struct bolt11 *b11, return NULL; } -struct bolt11 *new_bolt11(const tal_t *ctx, u64 *msatoshi) +struct bolt11 *new_bolt11(const tal_t *ctx, + const struct amount_msat *msat TAKES) { struct bolt11 *b11 = tal(ctx, struct bolt11); @@ -445,12 +443,12 @@ struct bolt11 *new_bolt11(const tal_t *ctx, u64 *msatoshi) b11->description_hash = NULL; b11->fallbacks = NULL; b11->routes = NULL; - b11->msatoshi = NULL; + b11->msat = NULL; b11->expiry = DEFAULT_X; b11->min_final_cltv_expiry = DEFAULT_C; - if (msatoshi) - b11->msatoshi = tal_dup(b11, u64, msatoshi); + if (msat) + b11->msat = tal_dup(b11, struct amount_msat, msat); return b11; } @@ -523,7 +521,7 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, * * - SHOULD indicate to the payer that amount is unspecified. */ - b11->msatoshi = NULL; + b11->msat = NULL; } else { u64 m10 = 10; u64 amount; @@ -559,8 +557,8 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, * `amount` by the `multiplier` value to derive the * amount required for payment. */ - b11->msatoshi = tal(b11, u64); - *b11->msatoshi = amount * m10 / 10; + b11->msat = tal(b11, struct amount_msat); + b11->msat->millisatoshis = amount * m10 / 10; /* Raw: raw amount multiplier calculation */ } /* BOLT #11: @@ -889,19 +887,20 @@ char *bolt11_encode_(const tal_t *ctx, * - MUST encode `amount` as a positive decimal integer with no leading 0s. * - SHOULD use the shortest representation possible, by using the largest multiplier or omitting the multiplier. */ - if (b11->msatoshi) { + if (b11->msat) { char postfix; - if (*b11->msatoshi % MSAT_PER_BTC == 0) { + u64 msat = b11->msat->millisatoshis; /* Raw: best-multiplier calc */ + if (msat % MSAT_PER_BTC == 0) { postfix = '\0'; - amount = *b11->msatoshi / MSAT_PER_BTC; + amount = msat / MSAT_PER_BTC; } else { size_t i; for (i = 0; i < ARRAY_SIZE(multipliers)-1; i++) { - if (!(*b11->msatoshi * 10 % multipliers[i].m10)) + if (!(msat * 10 % multipliers[i].m10)) break; } postfix = multipliers[i].letter; - amount = *b11->msatoshi * 10 / multipliers[i].m10; + amount = msat * 10 / multipliers[i].m10; } hrp = tal_fmt(tmpctx, "ln%s%"PRIu64"%c", b11->chain->bip173_name, amount, postfix); diff --git a/common/bolt11.h b/common/bolt11.h index 6435e6733b10..7b5aac15746a 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,7 @@ struct route_info { struct bolt11 { const struct chainparams *chain; u64 timestamp; - u64 *msatoshi; /* NULL if not specified. */ + struct amount_msat *msat; /* NULL if not specified. */ struct sha256 payment_hash; struct pubkey receiver_id; @@ -70,7 +71,8 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, const char *description, char **fail); /* Initialize an empty bolt11 struct with optional amount */ -struct bolt11 *new_bolt11(const tal_t *ctx, u64 *msatoshi); +struct bolt11 *new_bolt11(const tal_t *ctx, + const struct amount_msat *msat TAKES); /* Encodes and signs, even if it's nonsense. */ char *bolt11_encode_(const tal_t *ctx, diff --git a/common/channel_config.c b/common/channel_config.c index f4de14357636..5b8627c76639 100644 --- a/common/channel_config.c +++ b/common/channel_config.c @@ -3,10 +3,10 @@ void towire_channel_config(u8 **pptr, const struct channel_config *config) { - towire_u64(pptr, config->dust_limit_satoshis); - towire_u64(pptr, config->max_htlc_value_in_flight_msat); - towire_u64(pptr, config->channel_reserve_satoshis); - towire_u32(pptr, config->htlc_minimum_msat); + towire_amount_sat(pptr, config->dust_limit); + towire_amount_msat(pptr, config->max_htlc_value_in_flight); + towire_amount_sat(pptr, config->channel_reserve); + towire_amount_msat(pptr, config->htlc_minimum); towire_u16(pptr, config->to_self_delay); towire_u16(pptr, config->max_accepted_htlcs); } @@ -14,10 +14,10 @@ void towire_channel_config(u8 **pptr, const struct channel_config *config) void fromwire_channel_config(const u8 **ptr, size_t *max, struct channel_config *config) { - config->dust_limit_satoshis = fromwire_u64(ptr, max); - config->max_htlc_value_in_flight_msat = fromwire_u64(ptr, max); - config->channel_reserve_satoshis = fromwire_u64(ptr, max); - config->htlc_minimum_msat = fromwire_u32(ptr, max); + config->dust_limit = fromwire_amount_sat(ptr, max); + config->max_htlc_value_in_flight = fromwire_amount_msat(ptr, max); + config->channel_reserve = fromwire_amount_sat(ptr, max); + config->htlc_minimum = fromwire_amount_msat(ptr, max); config->to_self_delay = fromwire_u16(ptr, max); config->max_accepted_htlcs = fromwire_u16(ptr, max); } diff --git a/common/channel_config.h b/common/channel_config.h index 3e7f2587ec42..5daeeb6de6f0 100644 --- a/common/channel_config.h +++ b/common/channel_config.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include /* BOLT #2: * @@ -38,27 +39,27 @@ struct channel_config { * * `dust_limit_satoshis` is the threshold below which outputs should * not be generated for this node's commitment or HTLC transaction */ - u64 dust_limit_satoshis; + struct amount_sat dust_limit; /* BOLT #2: * * `max_htlc_value_in_flight_msat` is a cap on total value of * outstanding HTLCs, which allows a node to limit its exposure to * HTLCs */ - u64 max_htlc_value_in_flight_msat; + struct amount_msat max_htlc_value_in_flight; /* BOLT #2: * * `channel_reserve_satoshis` is the minimum amount that the other * node is to keep as a direct payment. */ - u64 channel_reserve_satoshis; + struct amount_sat channel_reserve; /* BOLT #2: * * `htlc_minimum_msat` indicates the smallest value HTLC this node * will accept. */ - u64 htlc_minimum_msat; + struct amount_msat htlc_minimum; /* BOLT #2: * diff --git a/common/close_tx.c b/common/close_tx.c index 6525c9b88ef4..a4318059ac64 100644 --- a/common/close_tx.c +++ b/common/close_tx.c @@ -9,14 +9,17 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, const u8 *their_script, const struct bitcoin_txid *anchor_txid, unsigned int anchor_index, - u64 anchor_satoshis, - uint64_t to_us, uint64_t to_them, - uint64_t dust_limit) + struct amount_sat funding, + struct amount_sat to_us, + struct amount_sat to_them, + struct amount_sat dust_limit) { struct bitcoin_tx *tx; size_t num_outputs = 0; + struct amount_sat total_out; - assert(to_us + to_them <= anchor_satoshis); + assert(amount_sat_add(&total_out, to_us, to_them)); + assert(amount_sat_less_eq(total_out, funding)); /* BOLT #3: * @@ -34,9 +37,9 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, /* Our input spends the anchor tx output. */ tx->input[0].txid = *anchor_txid; tx->input[0].index = anchor_index; - tx->input[0].amount = tal_dup(tx->input, u64, &anchor_satoshis); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); - if (to_us >= dust_limit) { + if (amount_sat_greater_eq(to_us, dust_limit)) { /* One output is to us. */ tx->output[num_outputs].amount = to_us; tx->output[num_outputs].script = tal_dup_arr(tx, u8, @@ -44,7 +47,7 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, num_outputs++; } - if (to_them >= dust_limit) { + if (amount_sat_greater_eq(to_them, dust_limit)) { /* Other output is to them. */ tx->output[num_outputs].amount = to_them; tx->output[num_outputs].script = tal_dup_arr(tx, u8, diff --git a/common/close_tx.h b/common/close_tx.h index 078fe4e4dc9a..9db1eed19bc6 100644 --- a/common/close_tx.h +++ b/common/close_tx.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include struct pubkey; @@ -13,7 +14,8 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, const u8 *their_script, const struct bitcoin_txid *anchor_txid, unsigned int anchor_index, - u64 anchor_satoshis, - uint64_t to_us, uint64_t to_them, - uint64_t dust_limit); + struct amount_sat funding, + struct amount_sat to_us, + struct amount_sat to_them, + struct amount_sat dust_limit); #endif /* LIGHTNING_COMMON_CLOSE_TX_H */ diff --git a/common/funding_tx.c b/common/funding_tx.c index d46b5cab74ba..f4385aff4791 100644 --- a/common/funding_tx.c +++ b/common/funding_tx.c @@ -13,31 +13,32 @@ struct bitcoin_tx *funding_tx(const tal_t *ctx, u16 *outnum, const struct utxo **utxomap, - u64 funding_satoshis, + struct amount_sat funding, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, - u64 change_satoshis, + struct amount_sat change, const struct pubkey *changekey, const struct ext_key *bip32_base) { u8 *wscript; struct bitcoin_tx *tx; - tx = tx_spending_utxos(ctx, utxomap, bip32_base, change_satoshis != 0); + tx = tx_spending_utxos(ctx, utxomap, bip32_base, + !amount_sat_eq(change, AMOUNT_SAT(0))); - tx->output[0].amount = funding_satoshis; + tx->output[0].amount = funding; wscript = bitcoin_redeem_2of2(tx, local_fundingkey, remote_fundingkey); SUPERVERBOSE("# funding witness script = %s\n", tal_hex(wscript, wscript)); tx->output[0].script = scriptpubkey_p2wsh(tx, wscript); tal_free(wscript); - if (change_satoshis != 0) { + if (!amount_sat_eq(change, AMOUNT_SAT(0))) { const void *map[2]; map[0] = int2ptr(0); map[1] = int2ptr(1); tx->output[1].script = scriptpubkey_p2wpkh(tx, changekey); - tx->output[1].amount = change_satoshis; + tx->output[1].amount = change; permute_outputs(tx->output, NULL, map); *outnum = (map[0] == int2ptr(0) ? 0 : 1); } else { diff --git a/common/funding_tx.h b/common/funding_tx.h index 7fd89616b72c..22e48cecb5b7 100644 --- a/common/funding_tx.h +++ b/common/funding_tx.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include struct bitcoin_tx; struct ext_key; @@ -15,10 +16,10 @@ struct utxo; * @ctx: context to tal from. * @outnum: (out) txout (0 or 1) which is the funding output. * @utxomap: (in/out) tal_arr of UTXO pointers to spend (permuted to match) - * @funding_satoshis: (in) satoshis to output. + * @funding: (in) satoshis to output. * @local_fundingkey: (in) local key for 2of2 funding output. * @remote_fundingkey: (in) remote key for 2of2 funding output. - * @change_satoshis: (in) amount to send as change. + * @change: (in) amount to send as change. * @changekey: (in) key to send change to (only used if change_satoshis != 0). * @bip32_base: (in) bip32 base for key derivation, or NULL. * @@ -34,10 +35,10 @@ struct utxo; struct bitcoin_tx *funding_tx(const tal_t *ctx, u16 *outnum, const struct utxo **utxomap, - u64 funding_satoshis, + struct amount_sat funding, const struct pubkey *local_fundingkey, const struct pubkey *remote_fundingkey, - u64 change_satoshis, + struct amount_sat change, const struct pubkey *changekey, const struct ext_key *bip32_base); #endif /* LIGHTNING_COMMON_FUNDING_TX_H */ diff --git a/common/htlc_tx.c b/common/htlc_tx.c index 4af1e535425e..2689b38a726e 100644 --- a/common/htlc_tx.c +++ b/common/htlc_tx.c @@ -7,16 +7,16 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, const struct bitcoin_txid *commit_txid, unsigned int commit_output_number, - u64 msatoshi, + struct amount_msat msat, u16 to_self_delay, const struct pubkey *revocation_pubkey, const struct pubkey *local_delayedkey, - u64 htlc_fee_satoshi, + struct amount_sat htlc_fee, u32 locktime) { struct bitcoin_tx *tx = bitcoin_tx(ctx, 1, 1); u8 *wscript; - u64 amount; + struct amount_sat amount; /* BOLT #3: * @@ -48,8 +48,8 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, tx->input[0].index = commit_output_number; /* We need amount for signing. */ - amount = msatoshi / 1000; - tx->input[0].amount = tal_dup(tx, u64, &amount); + amount = amount_msat_to_sat_round_down(msat); + tx->input[0].amount = tal_dup(tx, struct amount_sat, &amount); /* BOLT #3: * * `txin[0]` sequence: `0` @@ -63,7 +63,9 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, * * `txout[0]` script: version-0 P2WSH with witness script as shown * below */ - tx->output[0].amount = amount - htlc_fee_satoshi; + if (!amount_sat_sub(&tx->output[0].amount, amount, htlc_fee)) + abort(); + wscript = bitcoin_wscript_htlc_tx(tx, to_self_delay, revocation_pubkey, local_delayedkey); tx->output[0].script = scriptpubkey_p2wsh(tx, wscript); @@ -75,7 +77,7 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, struct bitcoin_tx *htlc_success_tx(const tal_t *ctx, const struct bitcoin_txid *commit_txid, unsigned int commit_output_number, - u64 htlc_msatoshi, + struct amount_msat htlc_msatoshi, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset) @@ -120,7 +122,7 @@ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx, const struct bitcoin_txid *commit_txid, unsigned int commit_output_number, - u64 htlc_msatoshi, + struct amount_msat htlc_msatoshi, u32 cltv_expiry, u16 to_self_delay, u32 feerate_per_kw, diff --git a/common/htlc_tx.h b/common/htlc_tx.h index d42a24ad40d3..b0cf87101007 100644 --- a/common/htlc_tx.h +++ b/common/htlc_tx.h @@ -1,13 +1,14 @@ #ifndef LIGHTNING_COMMON_HTLC_TX_H #define LIGHTNING_COMMON_HTLC_TX_H #include "config.h" +#include #include struct keyset; struct preimage; struct pubkey; -static inline u64 htlc_timeout_fee(u32 feerate_per_kw) +static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw) { /* BOLT #3: * @@ -16,10 +17,10 @@ static inline u64 htlc_timeout_fee(u32 feerate_per_kw) * 1. Multiply `feerate_per_kw` by 663 and divide by 1000 (rounding * down). */ - return feerate_per_kw * 663ULL / 1000; + return amount_tx_fee(663, feerate_per_kw); } -static inline u64 htlc_success_fee(u32 feerate_per_kw) +static inline struct amount_sat htlc_success_fee(u32 feerate_per_kw) { /* BOLT #3: * @@ -28,7 +29,7 @@ static inline u64 htlc_success_fee(u32 feerate_per_kw) * 1. Multiply `feerate_per_kw` by 703 and divide by 1000 (rounding * down). */ - return feerate_per_kw * 703ULL / 1000; + return amount_tx_fee(703, feerate_per_kw); } /* Create HTLC-success tx to spend a received HTLC commitment tx @@ -36,7 +37,7 @@ static inline u64 htlc_success_fee(u32 feerate_per_kw) struct bitcoin_tx *htlc_success_tx(const tal_t *ctx, const struct bitcoin_txid *commit_txid, unsigned int commit_output_number, - u64 htlc_msatoshi, + struct amount_msat htlc_msatoshi, u16 to_self_delay, u32 feerate_per_kw, const struct keyset *keyset); @@ -56,7 +57,7 @@ void htlc_success_tx_add_witness(struct bitcoin_tx *htlc_success, struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx, const struct bitcoin_txid *commit_txid, unsigned int commit_output_number, - u64 htlc_msatoshi, + struct amount_msat htlc_msatoshi, u32 cltv_expiry, u16 to_self_delay, u32 feerate_per_kw, diff --git a/common/htlc_wire.c b/common/htlc_wire.c index 529baf8490f3..58d0922c289a 100644 --- a/common/htlc_wire.c +++ b/common/htlc_wire.c @@ -10,7 +10,7 @@ void towire_added_htlc(u8 **pptr, const struct added_htlc *added) { towire_u64(pptr, added->id); - towire_u64(pptr, added->amount_msat); + towire_amount_msat(pptr, added->amount); towire_sha256(pptr, &added->payment_hash); towire_u32(pptr, added->cltv_expiry); towire(pptr, added->onion_routing_packet, @@ -77,7 +77,7 @@ void fromwire_added_htlc(const u8 **cursor, size_t *max, struct added_htlc *added) { added->id = fromwire_u64(cursor, max); - added->amount_msat = fromwire_u64(cursor, max); + added->amount = fromwire_amount_msat(cursor, max); fromwire_sha256(cursor, max, &added->payment_hash); added->cltv_expiry = fromwire_u32(cursor, max); fromwire(cursor, max, added->onion_routing_packet, diff --git a/common/htlc_wire.h b/common/htlc_wire.h index f0c5a0cce709..5841ea61b95a 100644 --- a/common/htlc_wire.h +++ b/common/htlc_wire.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -13,7 +14,7 @@ struct shachain; /* These are how we communicate about HTLC state to the master daemon */ struct added_htlc { u64 id; - u64 amount_msat; + struct amount_msat amount; struct sha256 payment_hash; u32 cltv_expiry; u8 onion_routing_packet[TOTAL_PACKET_SIZE]; diff --git a/common/initial_channel.c b/common/initial_channel.c index fe23469f84a8..06823f5355bd 100644 --- a/common/initial_channel.c +++ b/common/initial_channel.c @@ -12,8 +12,8 @@ struct channel *new_initial_channel(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, - u64 local_msatoshi, + struct amount_sat funding, + struct amount_msat local_msatoshi, u32 feerate_per_kw, const struct channel_config *local, const struct channel_config *remote, @@ -24,14 +24,13 @@ struct channel *new_initial_channel(const tal_t *ctx, enum side funder) { struct channel *channel = tal(ctx, struct channel); + struct amount_msat remote_msatoshi; channel->funding_txid = *funding_txid; channel->funding_txout = funding_txout; - if (funding_satoshis > UINT64_MAX / 1000) - return tal_free(channel); - - channel->funding_msat = funding_satoshis * 1000; - if (local_msatoshi > channel->funding_msat) + channel->funding = funding; + if (!amount_sat_sub_msat(&remote_msatoshi, + channel->funding, local_msatoshi)) return tal_free(channel); channel->funder = funder; @@ -47,12 +46,12 @@ struct channel *new_initial_channel(const tal_t *ctx, = channel->view[REMOTE].feerate_per_kw = feerate_per_kw; - channel->view[LOCAL].owed_msat[LOCAL] - = channel->view[REMOTE].owed_msat[LOCAL] + channel->view[LOCAL].owed[LOCAL] + = channel->view[REMOTE].owed[LOCAL] = local_msatoshi; - channel->view[REMOTE].owed_msat[REMOTE] - = channel->view[LOCAL].owed_msat[REMOTE] - = channel->funding_msat - local_msatoshi; + channel->view[REMOTE].owed[REMOTE] + = channel->view[LOCAL].owed[REMOTE] + = remote_msatoshi; channel->basepoints[LOCAL] = *local_basepoints; channel->basepoints[REMOTE] = *remote_basepoints; @@ -91,16 +90,16 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, return initial_commit_tx(ctx, &channel->funding_txid, channel->funding_txout, - channel->funding_msat / 1000, + channel->funding, channel->funder, /* They specify our to_self_delay and v.v. */ channel->config[!side].to_self_delay, &keyset, channel->view[side].feerate_per_kw, - channel->config[side].dust_limit_satoshis, - channel->view[side].owed_msat[side], - channel->view[side].owed_msat[!side], - channel_reserve_msat(channel, side), + channel->config[side].dust_limit, + channel->view[side].owed[side], + channel->view[side].owed[!side], + channel->config[!side].channel_reserve, 0 ^ channel->commitment_number_obscurer, side); } @@ -108,21 +107,24 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, static char *fmt_channel_view(const tal_t *ctx, const struct channel_view *view) { return tal_fmt(ctx, "{ feerate_per_kw=%"PRIu32"," - " owed_local=%"PRIu64"," - " owed_remote=%"PRIu64" }", + " owed_local=%s," + " owed_remote=%s }", view->feerate_per_kw, - view->owed_msat[LOCAL], - view->owed_msat[REMOTE]); + type_to_string(tmpctx, struct amount_msat, + &view->owed[LOCAL]), + type_to_string(tmpctx, struct amount_msat, + &view->owed[REMOTE])); } /* FIXME: This should reference HTLCs somehow. */ static char *fmt_channel(const tal_t *ctx, const struct channel *channel) { - return tal_fmt(ctx, "{ funding_msat=%"PRIu64"," + return tal_fmt(ctx, "{ funding=%s," " funder=%s," " local=%s," " remote=%s }", - channel->funding_msat, + type_to_string(tmpctx, struct amount_sat, + &channel->funding), side_to_str(channel->funder), fmt_channel_view(ctx, &channel->view[LOCAL]), fmt_channel_view(ctx, &channel->view[REMOTE])); diff --git a/common/initial_channel.h b/common/initial_channel.h index d395b6d19273..0966d45b4139 100644 --- a/common/initial_channel.h +++ b/common/initial_channel.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,7 @@ struct channel_view { u32 feerate_per_kw; /* How much is owed to each side (includes pending changes) */ - u64 owed_msat[NUM_SIDES]; + struct amount_msat owed[NUM_SIDES]; }; struct channel { @@ -34,8 +35,8 @@ struct channel { /* Keys used to spend funding tx. */ struct pubkey funding_pubkey[NUM_SIDES]; - /* Millisatoshis in from commitment tx */ - u64 funding_msat; + /* satoshis in from commitment tx */ + struct amount_sat funding; /* Who is paying fees. */ enum side funder; @@ -62,14 +63,6 @@ struct channel { const struct chainparams *chainparams; }; -/* This side's reserve is specified by the *other* side, and in satoshis: - * this is a convenience function to convert it. */ -static inline u64 channel_reserve_msat(const struct channel *channel, - enum side side) -{ - return channel->config[!side].channel_reserve_satoshis * 1000; -} - /** * new_initial_channel: Given initial fees and funding, what is initial state? * @ctx: tal context to allocate return value from. @@ -94,8 +87,8 @@ struct channel *new_initial_channel(const tal_t *ctx, const struct bitcoin_blkid *chain_hash, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, - u64 local_msatoshi, + struct amount_sat funding, + struct amount_msat local_msatoshi, u32 feerate_per_kw, const struct channel_config *local, const struct channel_config *remote, diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index 8d246a666abc..592ecff48f60 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -31,22 +32,22 @@ u64 commit_number_obscurer(const struct pubkey *opener_payment_basepoint, } bool try_subtract_fee(enum side funder, enum side side, - u64 base_fee_msat, u64 *self_msat, u64 *other_msat) + struct amount_sat base_fee, + struct amount_msat *self, + struct amount_msat *other) { - u64 *funder_msat; + struct amount_msat *funder_amount; if (funder == side) - funder_msat = self_msat; + funder_amount = self; else - funder_msat = other_msat; + funder_amount = other; - if (*funder_msat >= base_fee_msat) { - *funder_msat -= base_fee_msat; + if (amount_msat_sub_sat(funder_amount, *funder_amount, base_fee)) return true; - } else { - *funder_msat = 0; - return false; - } + + *funder_amount = AMOUNT_MSAT(0); + return false; } u8 *to_self_wscript(const tal_t *ctx, @@ -61,23 +62,26 @@ u8 *to_self_wscript(const tal_t *ctx, struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, + struct amount_sat funding, enum side funder, u16 to_self_delay, const struct keyset *keyset, u32 feerate_per_kw, - u64 dust_limit_satoshis, - u64 self_pay_msat, - u64 other_pay_msat, - u64 self_reserve_msat, + struct amount_sat dust_limit, + struct amount_msat self_pay, + struct amount_msat other_pay, + struct amount_sat self_reserve, u64 obscured_commitment_number, enum side side) { - u64 base_fee_msat; + struct amount_sat base_fee; struct bitcoin_tx *tx; size_t n, untrimmed; + struct amount_msat total_pay; - assert(self_pay_msat + other_pay_msat <= funding_satoshis * 1000); + if (!amount_msat_add(&total_pay, self_pay, other_pay)) + abort(); + assert(!amount_msat_greater_sat(total_pay, funding)); /* BOLT #3: * @@ -91,15 +95,14 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * 2. Calculate the base [commitment transaction * fee](#fee-calculation). */ - base_fee_msat = commit_tx_base_fee_msat(feerate_per_kw, untrimmed); + base_fee = commit_tx_base_fee(feerate_per_kw, untrimmed); /* BOLT #3: * * 3. Subtract this base fee from the funder (either `to_local` or * `to_remote`), with a floor of 0 (see [Fee Payment](#fee-payment)). */ - if (!try_subtract_fee(funder, side, base_fee_msat, - &self_pay_msat, &other_pay_msat)) { + if (!try_subtract_fee(funder, side, base_fee, &self_pay, &other_pay)) { /* BOLT #2: * * The receiving node MUST fail the channel if: @@ -120,15 +123,18 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * commitment transaction are less than or equal to * `channel_reserve_satoshis`. */ - if (self_pay_msat <= self_reserve_msat - && other_pay_msat <= self_reserve_msat) { - status_unusual("Neither self amount %"PRIu64 - " nor other amount %"PRIu64 - " exceed reserve %"PRIu64 + if (!amount_msat_greater_sat(self_pay, self_reserve) + && !amount_msat_greater_sat(other_pay, self_reserve)) { + status_unusual("Neither self amount %s" + " nor other amount %s" + " exceed reserve %s" " on initial commitment transaction", - self_pay_msat / 1000, - other_pay_msat / 1000, - self_reserve_msat / 1000); + type_to_string(tmpctx, struct amount_msat, + &self_pay), + type_to_string(tmpctx, struct amount_msat, + &other_pay), + type_to_string(tmpctx, struct amount_sat, + &self_reserve)); return NULL; } @@ -158,9 +164,9 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * `dust_limit_satoshis`, add a [`to_local` * output](#to_local-output). */ - if (self_pay_msat / 1000 >= dust_limit_satoshis) { + if (amount_msat_greater_eq_sat(self_pay, dust_limit)) { u8 *wscript = to_self_wscript(tmpctx, to_self_delay,keyset); - tx->output[n].amount = self_pay_msat / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(self_pay); tx->output[n].script = scriptpubkey_p2wsh(tx, wscript); n++; } @@ -171,7 +177,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * `dust_limit_satoshis`, add a [`to_remote` * output](#to_remote-output). */ - if (other_pay_msat / 1000 >= dust_limit_satoshis) { + if (amount_msat_greater_eq_sat(other_pay, dust_limit)) { /* BOLT #3: * * #### `to_remote` Output @@ -179,7 +185,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * This output sends funds to the other peer and thus is a simple * P2WPKH to `remotepubkey`. */ - tx->output[n].amount = other_pay_msat / 1000; + tx->output[n].amount = amount_msat_to_sat_round_down(other_pay); tx->output[n].script = scriptpubkey_p2wpkh(tx, &keyset->other_payment_key); n++; @@ -228,7 +234,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, = (0x80000000 | ((obscured_commitment_number>>24) & 0xFFFFFF)); /* Input amount needed for signature code. */ - tx->input[0].amount = tal_dup(tx->input, u64, &funding_satoshis); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); return tx; } diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index bb31afabf4d1..fd6830f61e5a 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -3,6 +3,7 @@ #define LIGHTNING_COMMON_INITIAL_COMMIT_TX_H #include "config.h" #include +#include #include struct keyset; @@ -18,8 +19,8 @@ u64 commit_number_obscurer(const struct pubkey *opener_payment_basepoint, const struct pubkey *accepter_payment_basepoint); /* Helper to calculate the base fee if we have this many htlc outputs */ -static inline u64 commit_tx_base_fee_sat(u32 feerate_per_kw, - size_t num_untrimmed_htlcs) +static inline struct amount_sat commit_tx_base_fee(u32 feerate_per_kw, + size_t num_untrimmed_htlcs) { u64 weight; @@ -44,26 +45,20 @@ static inline u64 commit_tx_base_fee_sat(u32 feerate_per_kw, * 3. Multiply `feerate_per_kw` by `weight`, divide by 1000 (rounding * down). */ - return (feerate_per_kw * weight / 1000); -} - -static inline u64 commit_tx_base_fee_msat(u32 feerate_per_kw, - size_t num_untrimmed_htlcs) -{ - return commit_tx_base_fee_sat(feerate_per_kw, num_untrimmed_htlcs) - * 1000; + return amount_tx_fee(feerate_per_kw, weight); } /** * initial_commit_tx: create (unsigned) commitment tx to spend the funding tx output * @ctx: context to allocate transaction and @htlc_map from. - * @funding_txid, @funding_out, @funding_satoshis: funding outpoint. + * @funding_txid, @funding_out, @funding: funding outpoint. * @funder: is the LOCAL or REMOTE paying the fee? * @keyset: keys derived for this commit tx. * @feerate_per_kw: feerate to use - * @dust_limit_satoshis: dust limit below which to trim outputs. - * @self_pay_msat: amount to pay directly to self - * @other_pay_msat: amount to pay directly to the other side + * @dust_limit: dust limit below which to trim outputs. + * @self_pay: amount to pay directly to self + * @other_pay: amount to pay directly to the other side + * @self_reserve: reserve the other side insisted we have * @obscured_commitment_number: number to encode in commitment transaction * @side: side to generate commitment transaction for. * @@ -74,21 +69,23 @@ static inline u64 commit_tx_base_fee_msat(u32 feerate_per_kw, struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, - u64 funding_satoshis, + struct amount_sat funding, enum side funder, u16 to_self_delay, const struct keyset *keyset, u32 feerate_per_kw, - u64 dust_limit_satoshis, - u64 self_pay_msat, - u64 other_pay_msat, - u64 self_reserve_msat, + struct amount_sat dust_limit, + struct amount_msat self_pay, + struct amount_msat other_pay, + struct amount_sat self_reserve, u64 obscured_commitment_number, enum side side); /* try_subtract_fee - take away this fee from the funder (and return true), or all if insufficient (and return false). */ bool try_subtract_fee(enum side funder, enum side side, - u64 base_fee_msat, u64 *self_msat, u64 *other_msat); + struct amount_sat base_fee, + struct amount_msat *self, + struct amount_msat *other); /* Generate the witness script for the to-self output: * scriptpubkey_p2wsh(ctx, wscript) gives the scriptpubkey */ diff --git a/common/json_helpers.c b/common/json_helpers.c index 42ace5b156b4..d9c20527c93b 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -38,9 +39,24 @@ bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, tok->end - tok->start, pubkey); } +bool json_to_msat(const char *buffer, const jsmntok_t *tok, + struct amount_msat *msat) +{ + return parse_amount_msat(msat, + buffer + tok->start, tok->end - tok->start); +} + +bool json_to_sat(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat) +{ + return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start); +} + bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, - struct short_channel_id *scid) + struct short_channel_id *scid, + bool may_be_deprecated_form) { return (short_channel_id_from_str(buffer + tok->start, - tok->end - tok->start, scid)); + tok->end - tok->start, scid, + may_be_deprecated_form)); } diff --git a/common/json_helpers.h b/common/json_helpers.h index 3a54ce387b01..5683bcdde873 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -4,6 +4,8 @@ #include "config.h" #include +struct amount_msat; +struct amount_sat; struct pubkey; struct short_channel_id; @@ -17,6 +19,15 @@ bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, /* Extract a short_channel_id from this */ bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, - struct short_channel_id *scid); + struct short_channel_id *scid, + bool may_be_deprecated_form); + +/* Extract a satoshis amount from this */ +bool json_to_sat(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat); + +/* Extract a millisatoshis amount from this */ +bool json_to_msat(const char *buffer, const jsmntok_t *tok, + struct amount_msat *msat); #endif /* LIGHTNING_COMMON_JSON_HELPERS_H */ diff --git a/common/json_tok.c b/common/json_tok.c index b0b0d1cc211b..faff0abc3fe7 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -116,26 +117,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name, name, tok->end - tok->start, buffer + tok->start); } -struct command_result *param_msat(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - u64 **msatoshi_val) -{ - if (json_tok_streq(buffer, tok, "any")) { - *msatoshi_val = NULL; - return NULL; - } - *msatoshi_val = tal(cmd, u64); - - if (json_to_u64(buffer, tok, *msatoshi_val) && *msatoshi_val != 0) - return NULL; - - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be a positive number or 'any', not '%.*s'", - name, - tok->end - tok->start, - buffer + tok->start); -} - struct command_result *param_percent(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, double **num) @@ -169,3 +150,30 @@ struct command_result *param_tok(struct command *cmd, const char *name, *out = tok; return NULL; } + +struct command_result *param_msat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_msat **msat) +{ + *msat = tal(cmd, struct amount_msat); + if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start)) + return NULL; + + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a millisatoshi amount, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); +} + +struct command_result *param_sat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_sat **sat) +{ + *sat = tal(cmd, struct amount_sat); + if (parse_amount_sat(*sat, buffer + tok->start, tok->end - tok->start)) + return NULL; + + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be a satoshi amount, not '%.*s'", + name, tok->end - tok->start, buffer + tok->start); +} + diff --git a/common/json_tok.h b/common/json_tok.h index 0a0e33d1c07b..8284c915fdc8 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -5,6 +5,8 @@ #include #include +struct amount_msat; +struct amount_sat; struct command; struct command_result; struct json_escaped; @@ -52,11 +54,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct sha256 **hash); -/* Extract positive integer, or NULL if tok is 'any'. */ -struct command_result *param_msat(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - u64 **msatoshi_val); - /* Extract double in range [0.0, 100.0] */ struct command_result *param_percent(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, @@ -67,6 +64,16 @@ struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num); +/* Extra msatoshi amount from this string */ +struct command_result *param_msat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_msat **msat); + +/* Extra satoshi amount from this string */ +struct command_result *param_sat(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct amount_sat **sat); + /* * Set the address of @out to @tok. Used as a callback by handlers that * want to unmarshal @tok themselves. diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index 5d91eaf660f0..473142e37f45 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -39,6 +39,7 @@ #define FUND_MAX_EXCEEDED 300 #define FUND_CANNOT_AFFORD 301 #define FUND_OUTPUT_IS_DUST 302 +#define FUNDING_BROADCAST_FAIL 303 /* Errors from `invoice` command */ #define INVOICE_LABEL_ALREADY_EXISTS 900 diff --git a/common/permute_tx.c b/common/permute_tx.c index e2e8138e96a2..909ffdf1804f 100644 --- a/common/permute_tx.c +++ b/common/permute_tx.c @@ -107,8 +107,8 @@ static bool output_better(const struct bitcoin_tx_output *a, size_t len, lena, lenb; int ret; - if (a->amount != b->amount) - return a->amount < b->amount; + if (!amount_sat_eq(a->amount, b->amount)) + return amount_sat_less(a->amount, b->amount); /* Lexicographical sort. */ lena = tal_count(a->script); diff --git a/common/pseudorand.c b/common/pseudorand.c index dc8f6b17b567..817a9e5bfa9a 100644 --- a/common/pseudorand.c +++ b/common/pseudorand.c @@ -37,6 +37,13 @@ uint64_t pseudorand(uint64_t max) return isaac64_next_uint(&isaac64, max); } +uint64_t pseudorand_u64(void) +{ + init_if_needed(); + + return isaac64_next_uint64(&isaac64); +} + const struct siphash_seed *siphash_seed(void) { init_if_needed(); diff --git a/common/pseudorand.h b/common/pseudorand.h index d66435ed44d5..0a46223bffb2 100644 --- a/common/pseudorand.h +++ b/common/pseudorand.h @@ -8,6 +8,11 @@ */ uint64_t pseudorand(uint64_t max); +/** + * pseudorand - pseudo (guessable!) random number between 0 and UINT64_MAX. + */ +uint64_t pseudorand_u64(void); + /** * Get the siphash seed for hash tables. */ diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index 77b06abc0f5e..3aac4990967c 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -26,18 +26,18 @@ u8 *peer_or_gossip_sync_read(const tal_t *ctx, select(peer_fd > gossip_fd ? peer_fd + 1 : gossip_fd + 1, &readfds, NULL, NULL, NULL); - if (FD_ISSET(gossip_fd, &readfds)) { - msg = wire_sync_read(ctx, gossip_fd); - if (!msg) - status_failed(STATUS_FAIL_GOSSIP_IO, - "Error reading gossip msg: %s", - strerror(errno)); - *from_gossipd = true; + if (FD_ISSET(peer_fd, &readfds)) { + msg = sync_crypto_read(ctx, cs, peer_fd); + *from_gossipd = false; return msg; } - msg = sync_crypto_read(ctx, cs, peer_fd); - *from_gossipd = false; + msg = wire_sync_read(ctx, gossip_fd); + if (!msg) + status_failed(STATUS_FAIL_GOSSIP_IO, + "Error reading gossip msg: %s", + strerror(errno)); + *from_gossipd = true; return msg; } diff --git a/common/sphinx.c b/common/sphinx.c index 62043902a6a1..61288809d832 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -307,7 +307,7 @@ static void serialize_hop_data(tal_t *ctx, u8 *dst, const struct hop_data *data) u8 *buf = tal_arr(ctx, u8, 0); towire_u8(&buf, data->realm); towire_short_channel_id(&buf, &data->channel_id); - towire_u64(&buf, data->amt_forward); + towire_amount_msat(&buf, data->amt_forward); towire_u32(&buf, data->outgoing_cltv); towire_pad(&buf, 12); towire(&buf, data->hmac, SECURITY_PARAMETER); @@ -321,7 +321,7 @@ static void deserialize_hop_data(struct hop_data *data, const u8 *src) size_t max = HOP_DATA_SIZE; data->realm = fromwire_u8(&cursor, &max); fromwire_short_channel_id(&cursor, &max, &data->channel_id); - data->amt_forward = fromwire_u64(&cursor, &max); + data->amt_forward = fromwire_amount_msat(&cursor, &max); data->outgoing_cltv = fromwire_u32(&cursor, &max); fromwire_pad(&cursor, &max, 12); fromwire(&cursor, &max, &data->hmac, SECURITY_PARAMETER); diff --git a/common/sphinx.h b/common/sphinx.h index 8bca1dff4220..e1f6fb5a5178 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -67,7 +67,7 @@ enum route_next_case { struct hop_data { u8 realm; struct short_channel_id channel_id; - u64 amt_forward; + struct amount_msat amt_forward; u32 outgoing_cltv; /* Padding omitted, will be zeroed */ u8 hmac[SECURITY_PARAMETER]; diff --git a/common/test/run-amount.c b/common/test/run-amount.c new file mode 100644 index 000000000000..d1e9d263207b --- /dev/null +++ b/common/test/run-amount.c @@ -0,0 +1,174 @@ +#include "../amount.c" +#include + +#define FAIL_MSAT(msatp, str) \ + assert(!parse_amount_msat((msatp), (str), strlen(str))) +#define PASS_MSAT(msatp, str, val) \ + do { \ + assert(parse_amount_msat((msatp), (str), strlen(str))); \ + assert((msatp)->millisatoshis == val); \ + } while (0) + +#define FAIL_SAT(satp, str) \ + assert(!parse_amount_sat((satp), (str), strlen(str))) +#define PASS_SAT(satp, str, val) \ + do { \ + assert(parse_amount_sat((satp), (str), strlen(str))); \ + assert((satp)->satoshis == val); \ + } while (0) + +int main(void) +{ + struct amount_msat msat; + struct amount_sat sat; + + setup_locale(); + setup_tmpctx(); + + /* Grossly malformed */ + FAIL_MSAT(&msat, "x"); + FAIL_MSAT(&msat, "x100"); + + PASS_MSAT(&msat, "0", 0); + PASS_MSAT(&msat, "1", 1); + PASS_MSAT(&msat, "2100000000000000000", 2100000000000000000ULL); + FAIL_MSAT(&msat, "0.0"); + FAIL_MSAT(&msat, "0.00000000"); + FAIL_MSAT(&msat, "0.00000000000"); + FAIL_MSAT(&msat, "0.00000000msat"); + FAIL_MSAT(&msat, "-1"); + + PASS_MSAT(&msat, "0msat", 0); + PASS_MSAT(&msat, "1msat", 1); + PASS_MSAT(&msat, "2100000000000000000msat", 2100000000000000000ULL); + FAIL_MSAT(&msat, "-1msat"); + + PASS_MSAT(&msat, "0sat", 0); + PASS_MSAT(&msat, "1sat", 1000); + PASS_MSAT(&msat, "2100000000000000sat", 2100000000000000000ULL); + FAIL_MSAT(&msat, "-1sat"); + + PASS_MSAT(&msat, "0.00000000grs", 0); + PASS_MSAT(&msat, "0.00000000000grs", 0); + PASS_MSAT(&msat, "0.00000001grs", 1000); + PASS_MSAT(&msat, "0.00000000001grs", 1); + PASS_MSAT(&msat, "1.2grs", 120000000000); + PASS_MSAT(&msat, "1.23grs", 123000000000); + PASS_MSAT(&msat, "1.234grs", 123400000000); + PASS_MSAT(&msat, "1.2345grs", 123450000000); + PASS_MSAT(&msat, "1.23456grs", 123456000000); + PASS_MSAT(&msat, "1.234567grs", 123456700000); + PASS_MSAT(&msat, "1.2345678grs", 123456780000); + PASS_MSAT(&msat, "1.23456789grs", 123456789000); + PASS_MSAT(&msat, "1.234567890grs", 123456789000); + PASS_MSAT(&msat, "1.2345678901grs", 123456789010); + PASS_MSAT(&msat, "1.23456789012grs", 123456789012); + FAIL_MSAT(&msat, "1grs"); + FAIL_MSAT(&msat, "1.000000000000grs"); + FAIL_MSAT(&msat, "-1.23456789grs"); + FAIL_MSAT(&msat, "-1.23456789012grs"); + + /* Overflowingly big. */ + FAIL_MSAT(&msat, "105000000000000000000000000.00000000"); + + /* Grossly malformed */ + FAIL_SAT(&sat, "x"); + FAIL_SAT(&sat, "x100"); + + PASS_SAT(&sat, "0", 0); + PASS_SAT(&sat, "1", 1); + PASS_SAT(&sat, "2100000000000000", 2100000000000000ULL); + FAIL_SAT(&sat, "0.0"); + FAIL_SAT(&sat, "0.00000000"); + FAIL_SAT(&sat, "0.00000000000"); + FAIL_SAT(&sat, "0.00000000sat"); + FAIL_SAT(&sat, "0.00000000000msat"); + FAIL_SAT(&sat, "-1"); + + PASS_SAT(&sat, "0sat", 0); + PASS_SAT(&sat, "1sat", 1); + PASS_SAT(&sat, "2100000000000000sat", 2100000000000000ULL); + FAIL_SAT(&sat, "-1sat"); + + PASS_SAT(&sat, "1000msat", 1); + PASS_SAT(&sat, "1000000msat", 1000); + PASS_SAT(&sat, "2100000000000000000msat", 2100000000000000ULL); + FAIL_SAT(&sat, "0msat"); + FAIL_SAT(&sat, "100msat"); + FAIL_SAT(&sat, "2000000000000000999msat"); + FAIL_SAT(&sat, "-1000msat"); + + PASS_SAT(&sat, "0.00000000grs", 0); + FAIL_SAT(&sat, "0.00000000000grs"); + PASS_SAT(&sat, "0.00000001grs", 1); + FAIL_SAT(&sat, "0.00000000001grs"); + PASS_SAT(&sat, "1.23456789grs", 123456789); + PASS_SAT(&sat, "1.2grs", 120000000); + PASS_SAT(&sat, "1.23grs", 123000000); + PASS_SAT(&sat, "1.234grs", 123400000); + PASS_SAT(&sat, "1.2345grs", 123450000); + PASS_SAT(&sat, "1.23456grs", 123456000); + PASS_SAT(&sat, "1.234567grs", 123456700); + PASS_SAT(&sat, "1.2345678grs", 123456780); + PASS_SAT(&sat, "1.23456789grs", 123456789); + FAIL_SAT(&sat, "1.234567890grs"); + FAIL_SAT(&sat, "1grs"); + FAIL_SAT(&sat, "-1.23456789grs"); + + /* Overflowingly big. */ + FAIL_SAT(&sat, "21000000000000000000000000.00000000grs"); + + /* Test fmt_amount_msat_btc, fmt_amount_msat */ + for (u64 i = 0; i <= UINT64_MAX / 10; i = i ? i * 10 : 1) { + const char *with, *without; + + msat.millisatoshis = i; + with = fmt_amount_msat_btc(tmpctx, &msat, true); + without = fmt_amount_msat_btc(tmpctx, &msat, false); + assert(strends(with, "grs")); + assert(strlen(with) == strlen(without) + 3); + assert(strncmp(with, without, strlen(without)) == 0); + /* Make sure it overwrites. */ + msat.millisatoshis++; + assert(parse_amount_msat(&msat, with, strlen(with))); + assert(msat.millisatoshis == i); + + with = fmt_amount_msat(tmpctx, &msat); + without = tal_fmt(tmpctx, "%"PRIu64, msat.millisatoshis); + assert(strends(with, "msat")); + assert(strlen(with) == strlen(without) + 4); + assert(strncmp(with, without, strlen(without)) == 0); + /* Make sure it overwrites. */ + msat.millisatoshis++; + assert(parse_amount_msat(&msat, with, strlen(with))); + assert(msat.millisatoshis == i); + } + + /* Test fmt_amount_sat_btc, fmt_amount_sat */ + for (u64 i = 0; i <= UINT64_MAX / 10; i = i ? i * 10 : 1) { + const char *with, *without; + + sat.satoshis = i; + with = fmt_amount_sat_btc(tmpctx, &sat, true); + without = fmt_amount_sat_btc(tmpctx, &sat, false); + assert(strends(with, "grs")); + assert(strlen(with) == strlen(without) + 3); + assert(strncmp(with, without, strlen(without)) == 0); + /* Make sure it overwrites. */ + sat.satoshis++; + assert(parse_amount_sat(&sat, with, strlen(with))); + assert(sat.satoshis == i); + + with = fmt_amount_sat(tmpctx, &sat); + without = tal_fmt(tmpctx, "%"PRIu64, sat.satoshis); + assert(strends(with, "sat")); + assert(strlen(with) == strlen(without) + 3); + assert(strncmp(with, without, strlen(without)) == 0); + /* Make sure it overwrites. */ + sat.satoshis++; + assert(parse_amount_sat(&sat, with, strlen(with))); + assert(sat.satoshis == i); + } + + tal_free(tmpctx); +} diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 4d3a78898191..5a75b1a59b79 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -1,3 +1,4 @@ +#include "../amount.c" #include "../bech32.c" #include "../bech32_util.c" #include "../bolt11.c" @@ -78,10 +79,10 @@ static void test_b11(const char *b11str, assert(b11->chain == expect_b11->chain); assert(b11->timestamp == expect_b11->timestamp); - if (!b11->msatoshi) - assert(!expect_b11->msatoshi); + if (!b11->msat) + assert(!expect_b11->msat); else - assert(*b11->msatoshi == *expect_b11->msatoshi); + assert(amount_msat_eq(*b11->msat, *expect_b11->msat)); assert(sha256_eq(&b11->payment_hash, &expect_b11->payment_hash)); if (!b11->description) assert(!expect_b11->description); @@ -119,7 +120,7 @@ int main(void) struct bolt11 *b11; struct pubkey node; - u64 msatoshi; + struct amount_msat msatoshi; const char *badstr; wally_init(0); @@ -194,7 +195,7 @@ int main(void) * * `aztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rsp`: signature * * `fj9srp`: Bech32 checksum */ - msatoshi = 2500 * (1000ULL * 100000000) / 1000000; + msatoshi = AMOUNT_MSAT(2500 * (1000ULL * 100000000) / 1000000); b11 = new_bolt11(tmpctx, &msatoshi); b11->chain = chainparams_for_network("groestlcoin"); b11->timestamp = 1496314658; @@ -226,7 +227,7 @@ int main(void) * * `cc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq`: signature * * `2yxxz7`: Bech32 checksum */ - msatoshi = 20 * (1000ULL * 100000000) / 1000; + msatoshi = AMOUNT_MSAT(20 * (1000ULL * 100000000) / 1000); b11 = new_bolt11(tmpctx, &msatoshi); b11->chain = chainparams_for_network("groestlcoin"); b11->timestamp = 1496314658; @@ -254,7 +255,7 @@ int main(void) } /* ALL UPPERCASE is allowed (useful for QR codes) */ - msatoshi = 2500 * (1000ULL * 100000000) / 1000000; + msatoshi = AMOUNT_MSAT(2500 * (1000ULL * 100000000) / 1000000); b11 = new_bolt11(tmpctx, &msatoshi); b11->chain = chainparams_for_network("groestlcoin"); b11->timestamp = 1496314658; diff --git a/common/test/run-funding_tx.c b/common/test/run-funding_tx.c index b69cf698d08d..9e7ecb3c5585 100644 --- a/common/test/run-funding_tx.c +++ b/common/test/run-funding_tx.c @@ -8,15 +8,19 @@ #include #include #include +#include "../amount.c" #define SUPERVERBOSE printf - #include "../funding_tx.c" +#include "../funding_tx.c" #undef SUPERVERBOSE - #include "../key_derive.c" - #include "../type_to_string.c" - #include "../permute_tx.c" - #include "../utxo.c" +#include "../key_derive.c" +#include "../type_to_string.c" +#include "../permute_tx.c" +#include "../utxo.c" /* AUTOGENERATED MOCKS START */ +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bitcoin_txid */ void fromwire_bitcoin_txid(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct bitcoin_txid *txid UNNEEDED) @@ -33,6 +37,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 towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bitcoin_txid */ void towire_bitcoin_txid(u8 **pptr UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_bitcoin_txid called!\n"); abort(); } @@ -81,14 +88,14 @@ int main(void) setup_locale(); struct bitcoin_tx *input, *funding; - u64 fee; + struct amount_sat fee, change; struct pubkey local_funding_pubkey, remote_funding_pubkey; struct privkey input_privkey; struct pubkey inputkey; bool testnet; struct utxo utxo; const struct utxo **utxomap; - u64 funding_satoshis; + struct amount_sat funding_sat; u16 funding_outnum; u8 *subscript; struct bitcoin_signature sig; @@ -138,29 +145,36 @@ int main(void) bitcoin_txid(input, &utxo.txid); utxo.outnum = 0; - utxo.amount = 24064000000000; + utxo.amount = AMOUNT_SAT(5000000000); utxo.is_p2sh = false; utxo.close_info = NULL; - funding_satoshis = 10000000; - fee = 13920; + funding_sat = AMOUNT_SAT(10000000); + fee = AMOUNT_SAT(13920); printf("input[0] txid: %s\n", tal_hexstr(tmpctx, &utxo.txid, sizeof(utxo.txid))); printf("input[0] input: %u\n", utxo.outnum); - printf("input[0] satoshis: %"PRIu64"\n", utxo.amount); - printf("funding satoshis: %"PRIu64"\n", funding_satoshis); + printf("input[0] satoshis: %s\n", + type_to_string(tmpctx, struct amount_sat, &utxo.amount)); + printf("funding: %s\n", + type_to_string(tmpctx, struct amount_sat, &funding_sat)); utxomap = tal_arr(tmpctx, const struct utxo *, 1); utxomap[0] = &utxo; + if (!amount_sat_sub(&change, utxo.amount, funding_sat) + || !amount_sat_sub(&change, change, fee)) + abort(); funding = funding_tx(tmpctx, &funding_outnum, utxomap, - funding_satoshis, + funding_sat, &local_funding_pubkey, &remote_funding_pubkey, - utxo.amount - fee - funding_satoshis, + change, &inputkey, NULL); - printf("# fee: %"PRIu64"\n", fee); - printf("change satoshis: %"PRIu64"\n", - funding->output[!funding_outnum].amount); + printf("# fee: %s\n", + type_to_string(tmpctx, struct amount_sat, &fee)); + printf("change: %s\n", + type_to_string(tmpctx, struct amount_sat, + &funding->output[!funding_outnum].amount)); printf("funding output: %u\n", funding_outnum); diff --git a/common/test/run-json.c b/common/test/run-json.c index e44e64c9a4ce..42a1e79d4083 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -5,6 +5,12 @@ #include /* AUTOGENERATED MOCKS START */ +/* 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(); } /* AUTOGENERATED MOCKS END */ diff --git a/common/test/run-param.c b/common/test/run-param.c index 7e67100d4d51..57c44d265c28 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -1,4 +1,5 @@ #include "config.h" +#include "../amount.c" #include "../json.c" #include "../json_escaped.c" #include "../json_tok.c" @@ -444,10 +445,10 @@ static void advanced(void) assert(param(cmd, j->buffer, j->toks, p_req("description", param_label, &label), - p_req("msat", param_msat, &msat), + p_req("msat", param_u64, &msat), p_req("tok", param_tok, &tok), - p_opt("msat_opt1", param_msat, &msat_opt1), - p_opt("msat_opt2", param_msat, &msat_opt2), + p_opt("msat_opt1", param_u64, &msat_opt1), + p_opt("msat_opt2", param_u64, &msat_opt2), NULL)); assert(label != NULL); assert(streq(label->s, "lightning")); @@ -472,8 +473,8 @@ static void advanced(void) u64 *msat2; struct json *j = json_parse(cmd, "[ 3 ]"); assert(param(cmd, j->buffer, j->toks, - p_opt_def("msat", param_msat, &msat, 23), - p_opt_def("msat2", param_msat, &msat2, 53), + p_opt_def("msat", param_u64, &msat, 23), + p_opt_def("msat2", param_u64, &msat2, 53), NULL)); assert(*msat == 3); assert(msat2); @@ -487,11 +488,10 @@ static void advanced_fail(void) struct json *j = json_parse(cmd, "[ 'anyx' ]"); u64 *msat; assert(!param(cmd, j->buffer, j->toks, - p_req("msat", param_msat, &msat), + p_req("msat", param_u64, &msat), NULL)); assert(check_fail()); - assert(strstr(fail_msg, "'msat' should be a positive" - " number or 'any', not 'anyx'")); + assert(strstr(fail_msg, "'msat' should be an unsigned 64 bit integer, not 'anyx'")); } } @@ -540,7 +540,7 @@ static void test_invoice(struct command *cmd, assert(cmd->mode == CMD_USAGE); if(!param(cmd, buffer, params, - p_req("msatoshi", param_msat, &msatoshi_val), + p_req("msatoshi", param_u64, &msatoshi_val), p_req("label", param_label, &label_val), p_req("description", param_escaped_string, &desc_val), p_opt("expiry", param_u64, &expiry), diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 72a9d7b566fb..c4d6c2cd5cf4 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -15,6 +15,9 @@ /* 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_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat 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(); } @@ -28,15 +31,15 @@ u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* 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 towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } /* Generated stub for towire_pad */ void towire_pad(u8 **pptr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_pad called!\n"); abort(); } @@ -50,9 +53,6 @@ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) /* 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(); } diff --git a/common/type_to_string.c b/common/type_to_string.c index 13afd7ee90c7..25d5c3ef3889 100644 --- a/common/type_to_string.c +++ b/common/type_to_string.c @@ -13,10 +13,10 @@ REGISTER_TYPE_TO_HEXSTR(ripemd160); /* This one in bitcoin/ but doesn't have its own C file */ REGISTER_TYPE_TO_HEXSTR(preimage); -char *type_to_string_(const tal_t *ctx, const char *typename, - union printable_types u) +const char *type_to_string_(const tal_t *ctx, const char *typename, + union printable_types u) { - char *s = NULL; + const char *s = NULL; size_t i; static size_t num_p; static struct type_to_string **t = NULL; diff --git a/common/type_to_string.h b/common/type_to_string.h index 521a1f931434..7c749f54c34a 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -30,6 +30,8 @@ union printable_types { const secp256k1_ecdsa_signature *secp256k1_ecdsa_signature; const struct bitcoin_signature *bitcoin_signature; const struct channel *channel; + const struct amount_msat *amount_msat; + const struct amount_sat *amount_sat; const char *charp_; }; @@ -38,12 +40,12 @@ union printable_types { ((void)sizeof((ptr) == (type *)NULL), \ ((union printable_types)((const type *)ptr)))) -char *type_to_string_(const tal_t *ctx, const char *typename, - union printable_types u); +const char *type_to_string_(const tal_t *ctx, const char *typename, + union printable_types u); #define REGISTER_TYPE_TO_STRING(typename, fmtfn) \ - static char *fmt_##typename##_(const tal_t *ctx, \ - union printable_types u) \ + static const char *fmt_##typename##_(const tal_t *ctx, \ + union printable_types u) \ { \ return fmtfn(ctx, u.typename); \ } \ @@ -53,8 +55,8 @@ char *type_to_string_(const tal_t *ctx, const char *typename, AUTODATA(type_to_string, &ttos_##typename) #define REGISTER_TYPE_TO_HEXSTR(typename) \ - static char *fmt_##typename##_(const tal_t *ctx, \ - union printable_types u) \ + static const char *fmt_##typename##_(const tal_t *ctx, \ + union printable_types u) \ { \ return tal_hexstr(ctx, u.typename, sizeof(*u.typename)); \ } \ @@ -65,7 +67,7 @@ char *type_to_string_(const tal_t *ctx, const char *typename, struct type_to_string { const char *typename; - char *(*fmt)(const tal_t *ctx, union printable_types u); + const char *(*fmt)(const tal_t *ctx, union printable_types u); }; AUTODATA_TYPE(type_to_string, struct type_to_string); #endif /* LIGHTNING_COMMON_TYPE_TO_STRING_H */ diff --git a/common/utxo.c b/common/utxo.c index 0132bd727824..469de6c6dc9a 100644 --- a/common/utxo.c +++ b/common/utxo.c @@ -10,7 +10,7 @@ void towire_utxo(u8 **pptr, const struct utxo *utxo) bool is_unilateral_close = utxo->close_info != NULL; towire_bitcoin_txid(pptr, &utxo->txid); towire_u32(pptr, utxo->outnum); - towire_u64(pptr, utxo->amount); + towire_amount_sat(pptr, utxo->amount); towire_u32(pptr, utxo->keyindex); towire_bool(pptr, utxo->is_p2sh); @@ -28,9 +28,14 @@ struct utxo *fromwire_utxo(const tal_t *ctx, const u8 **ptr, size_t *max) fromwire_bitcoin_txid(ptr, max, &utxo->txid); utxo->outnum = fromwire_u32(ptr, max); - utxo->amount = fromwire_u64(ptr, max); + utxo->amount = fromwire_amount_sat(ptr, max); utxo->keyindex = fromwire_u32(ptr, max); utxo->is_p2sh = fromwire_bool(ptr, max); + + /* No need to tell hsmd about the scriptPubkey, it has all the info to + * derive it from the rest. */ + utxo->scriptPubkey = NULL; + if (fromwire_bool(ptr, max)) { utxo->close_info = tal(utxo, struct unilateral_close_info); utxo->close_info->channel_id = fromwire_u64(ptr, max); @@ -53,7 +58,8 @@ struct bitcoin_tx *tx_spending_utxos(const tal_t *ctx, for (size_t i = 0; i < tal_count(utxos); i++) { tx->input[i].txid = utxos[i]->txid; tx->input[i].index = utxos[i]->outnum; - tx->input[i].amount = tal_dup(tx, u64, &utxos[i]->amount); + tx->input[i].amount = tal_dup(tx, struct amount_sat, + &utxos[i]->amount); if (utxos[i]->is_p2sh && bip32_base) { struct pubkey key; bip32_pubkey(bip32_base, &key, utxos[i]->keyindex); diff --git a/common/utxo.h b/common/utxo.h index 58a82d45446a..9ba9ec021c38 100644 --- a/common/utxo.h +++ b/common/utxo.h @@ -6,6 +6,7 @@ #include #include #include +#include #include struct ext_key; @@ -20,7 +21,7 @@ struct unilateral_close_info { struct utxo { struct bitcoin_txid txid; u32 outnum; - u64 amount; + struct amount_sat amount; u32 keyindex; bool is_p2sh; u8 status; @@ -34,6 +35,9 @@ struct utxo { /* NULL if not spent yet, otherwise, the block the spending transaction is in */ const u32 *spendheight; + + /* The scriptPubkey if it is known */ + u8 *scriptPubkey; }; void towire_utxo(u8 **pptr, const struct utxo *utxo); diff --git a/common/wallet_tx.c b/common/wallet_tx.c index 54e2b3b47ed0..9d627c0cb510 100644 --- a/common/wallet_tx.c +++ b/common/wallet_tx.c @@ -4,48 +4,80 @@ #include #include -void wtx_init(struct command *cmd, struct wallet_tx * wtx) +void wtx_init(struct command *cmd, struct wallet_tx *wtx, struct amount_sat max) { wtx->cmd = cmd; - wtx->amount = 0; - wtx->change_key_index = 0; - wtx->utxos = NULL; + wtx->amount = max; +} + +struct command_result *param_wtx(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct wallet_tx *wtx) +{ + struct amount_sat max = wtx->amount; + + if (json_tok_streq(buffer, tok, "all")) { + wtx->all_funds = true; + return NULL; + } wtx->all_funds = false; + if (!parse_amount_sat(&wtx->amount, + buffer + tok->start, tok->end - tok->start)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be satoshis or 'all', not '%.*s'", + name, + tok->end - tok->start, + buffer + tok->start); + + if (amount_sat_greater(wtx->amount, max)) + return command_fail(wtx->cmd, FUND_MAX_EXCEEDED, + "Amount exceeded %s", + type_to_string(tmpctx, struct amount_sat, + &max)); + return NULL; } -static struct command_result *check_amount(const struct wallet_tx *tx, - u64 amount) +static struct command_result *check_amount(const struct wallet_tx *wtx, + struct amount_sat amount) { - if (tal_count(tx->utxos) == 0) { - return command_fail(tx->cmd, FUND_CANNOT_AFFORD, + if (tal_count(wtx->utxos) == 0) { + return command_fail(wtx->cmd, FUND_CANNOT_AFFORD, "Cannot afford transaction"); } - if (amount < 546) { - return command_fail(tx->cmd, FUND_OUTPUT_IS_DUST, - "Output %"PRIu64" satoshis would be dust", - amount); + if (amount_sat_less(amount, get_chainparams(wtx->cmd->ld)->dust_limit)) { + return command_fail(wtx->cmd, FUND_OUTPUT_IS_DUST, + "Output %s would be dust", + type_to_string(tmpctx, struct amount_sat, + &amount)); } return NULL; } struct command_result *wtx_select_utxos(struct wallet_tx *tx, u32 fee_rate_per_kw, - size_t out_len) + size_t out_len, + u32 maxheight) { struct command_result *res; - u64 fee_estimate; + struct amount_sat fee_estimate; + if (tx->all_funds) { - u64 amount; + struct amount_sat amount; tx->utxos = wallet_select_all(tx->cmd, tx->cmd->ld->wallet, fee_rate_per_kw, out_len, + maxheight, &amount, &fee_estimate); res = check_amount(tx, amount); if (res) return res; - if (amount <= tx->amount) { - tx->change = 0; + /* tx->amount is max permissible */ + if (amount_sat_less_eq(amount, tx->amount)) { + tx->change = AMOUNT_SAT(0); + tx->change_key_index = 0; tx->amount = amount; return NULL; } @@ -58,13 +90,14 @@ struct command_result *wtx_select_utxos(struct wallet_tx *tx, tx->utxos = wallet_select_coins(tx->cmd, tx->cmd->ld->wallet, tx->amount, fee_rate_per_kw, out_len, + maxheight, &fee_estimate, &tx->change); res = check_amount(tx, tx->amount); if (res) return res; - if (tx->change < 546) { - tx->change = 0; + if (amount_sat_less(tx->change, get_chainparams(tx->cmd->ld)->dust_limit)) { + tx->change = AMOUNT_SAT(0); tx->change_key_index = 0; } else { tx->change_key_index = wallet_get_newindex(tx->cmd->ld); diff --git a/common/wallet_tx.h b/common/wallet_tx.h index 536096bbdfab..2c3ec62a67aa 100644 --- a/common/wallet_tx.h +++ b/common/wallet_tx.h @@ -2,6 +2,7 @@ #define LIGHTNING_COMMON_WALLET_TX_H #include "config.h" #include +#include #include #include #include @@ -11,16 +12,32 @@ */ struct wallet_tx { struct command *cmd; - u64 amount; - u64 change; + struct amount_sat amount, change; u32 change_key_index; const struct utxo **utxos; bool all_funds; /* In this case, amount is a maximum. */ }; -void wtx_init(struct command *cmd, struct wallet_tx *wtx); +void wtx_init(struct command *cmd, struct wallet_tx *wtx, struct amount_sat max); + +struct command_result *param_wtx(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct wallet_tx *wtx); + struct command_result *wtx_select_utxos(struct wallet_tx *tx, u32 fee_rate_per_kw, - size_t out_len); + size_t out_len, + u32 maxheight); + +static inline u32 minconf_to_maxheight(u32 minconf, struct lightningd *ld) +{ + /* No confirmations is special, we need to disable the check in the + * selection */ + if (minconf == 0) + return 0; + return ld->topology->tip->height - minconf + 1; +} #endif /* LIGHTNING_COMMON_WALLET_TX_H */ diff --git a/common/withdraw_tx.c b/common/withdraw_tx.c index e697b69d0a2b..8cd9d52bd559 100644 --- a/common/withdraw_tx.c +++ b/common/withdraw_tx.c @@ -10,24 +10,25 @@ struct bitcoin_tx *withdraw_tx(const tal_t *ctx, const struct utxo **utxos, u8 *destination, - const u64 withdraw_amount, + struct amount_sat withdraw_amount, const struct pubkey *changekey, - const u64 changesat, + struct amount_sat change, const struct ext_key *bip32_base) { struct bitcoin_tx *tx; - tx = tx_spending_utxos(ctx, utxos, bip32_base, changesat != 0); + tx = tx_spending_utxos(ctx, utxos, bip32_base, + !amount_sat_eq(change, AMOUNT_SAT(0))); tx->output[0].amount = withdraw_amount; tx->output[0].script = destination; - if (changesat != 0) { + if (!amount_sat_eq(change, AMOUNT_SAT(0))) { const void *map[2]; map[0] = int2ptr(0); map[1] = int2ptr(1); tx->output[1].script = scriptpubkey_p2wpkh(tx, changekey); - tx->output[1].amount = changesat; + tx->output[1].amount = change; permute_outputs(tx->output, NULL, map); } permute_inputs(tx->input, (const void **)utxos); diff --git a/common/withdraw_tx.h b/common/withdraw_tx.h index 5569fafe3563..608e68b14af0 100644 --- a/common/withdraw_tx.h +++ b/common/withdraw_tx.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include struct bitcoin_tx; struct ext_key; @@ -19,15 +20,15 @@ struct utxo; * @destination: (in) tal_arr of u8, scriptPubKey to send to. * @amount: (in) satoshis to send to the destination * @changekey: (in) key to send change to (only used if change_satoshis != 0). - * @changesat: (in) amount to send as change. + * @change: (in) amount to send as change. * @bip32_base: (in) bip32 base for key derivation, or NULL. */ struct bitcoin_tx *withdraw_tx(const tal_t *ctx, const struct utxo **utxos, u8 *destination, - const u64 withdraw_amount, + struct amount_sat withdraw_amount, const struct pubkey *changekey, - const u64 changesat, + struct amount_sat change, const struct ext_key *bip32_base); #endif /* LIGHTNING_COMMON_WITHDRAW_TX_H */ diff --git a/configure b/configure index c968d744e7fa..0532de67c7fe 100755 --- a/configure +++ b/configure @@ -13,6 +13,7 @@ EXPERIMENTAL_FEATURES=${EXPERIMENTAL_FEATURES:-0} COMPAT=${COMPAT:-1} STATIC=${STATIC:-0} CONFIGURATOR_CC=${CONFIGURATOR_CC:-$CC} +ASAN=${ASAN:-0} CONFIGURATOR=ccan/tools/configurator/configurator CONFIG_VAR_FILE=config.vars @@ -55,8 +56,10 @@ usage() echo " Compatibility mode, good to disable to see if your software breaks" usage_with_default "--enable/disable-valgrind" "(autodetect)" echo " Valgrind binary to use for tests" - usage_with_default "--enable/disable-static" "$STATIC" "enable" "disable" + usage_with_default "--enable/disable-static" "$STATIC" "enable" "disable" echo " Static link binary" + usage_with_default "--enable/disable-address-sanitizer" "$ASAN" "enable" "disable" + echo " Compile with address-sanitizer" exit 1 } @@ -119,6 +122,8 @@ for opt in "$@"; do --disable-valgrind) VALGRIND=0;; --enable-static) STATIC=1;; --disable-static) STATIC=0;; + --enable-address-sanitizer) ASAN=1;; + --disable-address-sanitizer) ASAN=0;; --help|-h) usage;; *) echo "Unknown option '$opt'" >&2 @@ -139,8 +144,48 @@ if [ -z "$VALGRIND" ]; then fi fi +if [ "$ASAN" = "1" ]; then + if [ "$CC" = "clang" ]; then + echo "Address sanitizer (ASAN) is currently only supported with gcc" + exit 1 + fi + if [ "$VALGRIND" = "1" ]; then + echo "Address sanitizer (ASAN) and valgrind cannot be enabled at the same time" + exit 1 + fi +fi + rm -f $CONFIG_VAR_FILE.$$ -$CONFIGURATOR --autotools-style --var-file=$CONFIG_VAR_FILE.$$ --header-file=$CONFIG_HEADER --configurator-cc="$CONFIGURATOR_CC" "$CC" $CWARNFLAGS $CDEBUGFLAGS +$CONFIGURATOR --extra-tests --autotools-style --var-file=$CONFIG_VAR_FILE.$$ --header-file=$CONFIG_HEADER --configurator-cc="$CONFIGURATOR_CC" "$CC" $CWARNFLAGS $CDEBUGFLAGS < +#include + +int main(void) +{ + printf("%p\n", crypto_aead_chacha20poly1305_ietf_encrypt); + exit(0); +} +/*END*/ +var=HAVE_SYSTEM_LIBBASE58 +desc=libbase58 +style=DEFINES_EVERYTHING|EXECUTE|MAY_NOT_COMPILE +link=-lbase58 +code= +#include +#include + +int main(void) +{ + printf("%p\n", b58check); + exit(0); +} +/*END*/ +EOF mv $CONFIG_VAR_FILE.$$ $CONFIG_VAR_FILE add_var PREFIX "$PREFIX" @@ -154,6 +199,7 @@ add_var EXPERIMENTAL_FEATURES "$EXPERIMENTAL_FEATURES" $CONFIG_HEADER add_var COMPAT "$COMPAT" $CONFIG_HEADER add_var PYTEST "$PYTEST" add_var STATIC "$STATIC" +add_var ASAN "$ASAN" # Hack to avoid sha256 name clash with libwally: will be fixed when that # becomes a standalone shared lib. diff --git a/connectd/Makefile b/connectd/Makefile index 3da35dfa2daa..de7a11fa92d0 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -37,6 +37,7 @@ LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_CONNECT_HEADERS) # Common source we use. CONNECTD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/contrib/linuxarm32v7.Dockerfile b/contrib/linuxarm32v7.Dockerfile index cc052bdd6f3a..22aed9c495f0 100644 --- a/contrib/linuxarm32v7.Dockerfile +++ b/contrib/linuxarm32v7.Dockerfile @@ -65,7 +65,10 @@ RUN wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz \ COPY --from=downloader /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static WORKDIR /opt/lightningd -COPY . . +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 RUN ./configure --enable-static && make -j3 DEVELOPER=${DEVELOPER} && cp lightningd/lightning* cli/lightning-cli /usr/bin/ @@ -83,7 +86,8 @@ VOLUME [ "/root/.lightning" ] COPY --from=builder /opt/lightningd/cli/lightning-cli /usr/bin COPY --from=builder /opt/lightningd/lightningd/lightning* /usr/bin/ -COPY --from=downloader /opt/groestlcoin /usr/bin +COPY --from=builder /opt/lightningd/plugins/pay /usr/libexec/c-lightning/plugins/ +COPY --from=downloader /opt/groestlcoin/bin /usr/bin COPY tools/docker-entrypoint.sh entrypoint.sh EXPOSE 9735 9835 diff --git a/contrib/plugins/helloworld.py b/contrib/plugins/helloworld.py index 3e0e22cb0a93..d2a4f4d85d0a 100644 --- a/contrib/plugins/helloworld.py +++ b/contrib/plugins/helloworld.py @@ -19,7 +19,7 @@ def hello(plugin, name="world"): return s -@plugin.method("init") +@plugin.init() def init(options, configuration, plugin): plugin.log("Plugin helloworld.py initialized") diff --git a/contrib/pylightning/README.md b/contrib/pylightning/README.md index fe18070d17d8..72a9a2fd1a1f 100644 --- a/contrib/pylightning/README.md +++ b/contrib/pylightning/README.md @@ -1,21 +1,38 @@ # pylightning: A python client library for lightningd -### Installation +This package implements the Unix socket based JSON-RPC protocol that +`lightningd` exposes to the rest of the world. It can be used to call +arbitrary functions on the RPC interface, and serves as a basis for plugins +written in python. -Note: With Python 2 you need to have the futures python library installed to be able to use pylightning: -``` -pip install futures -``` +## Installation -pylightning is available on pip +pylightning is available on `pip`: ``` pip install pylightning ``` -### Examples +Alternatively you can also install the development version to get access to +currently unreleased features by checking out the c-lightning source code and +installing into your python3 environment: + +```bash +git clone https://github.com/Groestlcoin/lightning.git +cd lightning/contrib/pylightning +python3 setup.py develop +``` + +This will add links to the library into your environment so changing the +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. +## Examples + + +### Using the JSON-RPC client ```py """ Generate invoice on one daemon and pay it on the other @@ -42,10 +59,43 @@ print(route) print(l1.sendpay(route['route'], invoice['payment_hash'])) ``` -Also see the included [lightning-pay](./lightning-pay) script, which uses the client library to pay invoices +### Writing a plugin + +Plugins are programs that `lightningd` can be configured to execute alongside +the main daemon. They allow advanced interactions with and customizations to +the daemon. + +```python +#!/usr/bin/env python3 +from lightning import Plugin + +plugin = Plugin() + +@plugin.method("hello") +def hello(plugin, name="world"): + """This is the documentation string for the hello-function. + + It gets reported as the description when registering the function + as a method with `lightningd`. + + """ + greeting = plugin.get_option('greeting') + s = '{} {}'.format(greeting, name) + plugin.log(s) + return s + + +@plugin.init() +def init(options, configuration, plugin): + plugin.log("Plugin helloworld.py initialized") + + +@plugin.subscribe("connect") +def on_connect(plugin, id, address): + plugin.log("Received connect event for peer {}".format(id)) + + +plugin.add_option('greeting', 'Hello', 'The greeting I should use.') +plugin.run() -```sh -lightning-pay -# or explicitly with -lightning-pay ``` diff --git a/contrib/pylightning/lightning/__init__.py b/contrib/pylightning/lightning/__init__.py index 2fe4821eb627..a5170aed4af4 100644 --- a/contrib/pylightning/lightning/__init__.py +++ b/contrib/pylightning/lightning/__init__.py @@ -1,2 +1,2 @@ -from .lightning import LightningRpc, RpcError +from .lightning import LightningRpc, RpcError, Millisatoshi from .plugin import Plugin, monkey_patch diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index bfb6a117ddef..1d087db5c35c 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -1,3 +1,4 @@ +from decimal import Decimal import json import logging import socket @@ -13,19 +14,126 @@ def __init__(self, method, payload, error): self.error = error +class Millisatoshi: + """ + A subtype to represent thousandths of a satoshi. + + Many JSON API fields are expressed in millisatoshis: these automatically get + turned into Millisatoshi types. Converts to and from int. + """ + def __init__(self, v): + """ + Takes either a string ending in 'msat', 'sat', 'grs' or an integer. + """ + if isinstance(v, str): + if v.endswith("msat"): + self.millisatoshis = int(v[0:-4]) + elif v.endswith("sat"): + self.millisatoshis = Decimal(v[0:-3]) * 1000 + elif v.endswith("grs"): + self.millisatoshis = Decimal(v[0:-3]) * 1000 * 10**8 + if self.millisatoshis != int(self.millisatoshis): + raise ValueError("Millisatoshi must be a whole number") + elif isinstance(v, Millisatoshi): + self.millisatoshis = v.millisatoshis + elif int(v) == v: + self.millisatoshis = v + else: + raise TypeError("Millisatoshi must be string with msat/sat/grs suffix or int") + + if self.millisatoshis < 0: + raise ValueError("Millisatoshi must be >= 0") + + def __repr__(self): + """ + Appends the 'msat' as expected for this type. + """ + return str(self.millisatoshis) + "msat" + + def to_satoshi(self): + """ + Return a Decimal representing the number of satoshis + """ + return Decimal(self.millisatoshis) / 1000 + + def to_btc(self): + """ + Return a Decimal representing the number of bitcoin + """ + return Decimal(self.millisatoshis) / 1000 / 10**8 + + def to_satoshi_str(self): + """ + Return a string of form 1234sat or 1234.567sat. + """ + if self.millisatoshis % 1000: + return '{:.3f}sat'.format(self.to_satoshi()) + else: + return '{:.0f}sat'.format(self.to_satoshi()) + + def to_btc_str(self): + """ + Return a string of form 12.34567890grs or 12.34567890123grs. + """ + if self.millisatoshis % 1000: + return '{:.8f}grs'.format(self.to_btc()) + else: + return '{:.11f}grs'.format(self.to_btc()) + + def to_json(self): + return self.__repr__() + + def __int__(self): + return self.millisatoshis + + def __lt__(self, other): + return self.millisatoshis < other.millisatoshis + + def __le__(self, other): + return self.millisatoshis <= other.millisatoshis + + def __eq__(self, other): + return self.millisatoshis == other.millisatoshis + + def __gt__(self, other): + return self.millisatoshis > other.millisatoshis + + def __ge__(self, other): + return self.millisatoshis >= other.millisatoshis + + def __add__(self, other): + return Millisatoshi(int(self) + int(other)) + + def __sub__(self, other): + return Millisatoshi(int(self) - int(other)) + + def __mul__(self, other): + return Millisatoshi(int(self) * other) + + def __truediv__(self, other): + return Millisatoshi(int(self) / other) + + def __floordiv__(self, other): + return Millisatoshi(int(self) // other) + + def __mod__(self, other): + return Millisatoshi(int(self) % other) + + class UnixDomainSocketRpc(object): - def __init__(self, socket_path, executor=None, logger=logging): + def __init__(self, socket_path, executor=None, logger=logging, encoder_cls=json.JSONEncoder, decoder=json.JSONDecoder()): self.socket_path = socket_path - self.decoder = json.JSONDecoder() + self.encoder_cls = encoder_cls + self.decoder = decoder self.executor = executor self.logger = logger # Do we require the compatibility mode? self._compat = True + self.next_id = 0 - @staticmethod - def _writeobj(sock, obj): - s = json.dumps(obj) + def _writeobj(self, sock, obj): + s = json.dumps(obj, cls=self.encoder_cls) sock.sendall(bytearray(s, 'UTF-8')) def _readobj_compat(self, sock, buff=b''): @@ -46,11 +154,13 @@ def _readobj_compat(self, sock, buff=b''): continue # Convert late to UTF-8 so glyphs split across recvs do not # impact us - objs, len_used = self.decoder.raw_decode(buff.decode("UTF-8")) - return objs, buff[len_used:].lstrip() + buff = buff.decode("UTF-8") + objs, len_used = self.decoder.raw_decode(buff) + buff = buff[len_used:].lstrip().encode("UTF-8") + return objs, buff except ValueError: # Probably didn't read enough - pass + buff = buff.lstrip().encode("UTF-8") def _readobj(self, sock, buff=b''): """Read a JSON object, starting with buff; returns object and any buffer left over""" @@ -99,8 +209,9 @@ def call(self, method, payload=None): self._writeobj(sock, { "method": method, "params": payload, - "id": 0 + "id": self.next_id, }) + self.next_id += 1 resp, _ = self._readobj_compat(sock) sock.close() @@ -126,6 +237,48 @@ class LightningRpc(UnixDomainSocketRpc): between calls, but it does not (yet) support concurrent calls. """ + class LightningJSONEncoder(json.JSONEncoder): + def default(self, o): + try: + return o.to_json() + except NameError: + pass + return json.JSONEncoder.default(self, o) + + class LightningJSONDecoder(json.JSONDecoder): + def __init__(self, *, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None): + self.object_hook_next = object_hook + super().__init__(object_hook=self.millisatoshi_hook, parse_float=parse_float, parse_int=parse_int, parse_constant=parse_constant, strict=strict, object_pairs_hook=object_pairs_hook) + + @staticmethod + def replace_amounts(obj): + """ + Recursively replace _msat fields with appropriate values with Millisatoshi. + """ + if isinstance(obj, dict): + for k, v in obj.items(): + if k.endswith('msat'): + if isinstance(v, str) and v.endswith('msat'): + obj[k] = Millisatoshi(v) + # Special case for array of msat values + elif isinstance(v, list) and all(isinstance(e, str) and e.endswith('msat') for e in v): + obj[k] = [Millisatoshi(e) for e in v] + else: + obj[k] = LightningRpc.LightningJSONDecoder.replace_amounts(v) + elif isinstance(obj, list): + obj = [LightningRpc.LightningJSONDecoder.replace_amounts(e) for e in obj] + + return obj + + def millisatoshi_hook(self, obj): + obj = LightningRpc.LightningJSONDecoder.replace_amounts(obj) + if self.object_hook_next: + obj = self.object_hook_next(obj) + return obj + + def __init__(self, socket_path, executor=None, logger=logging): + super().__init__(socket_path, executor, logging, self.LightningJSONEncoder, self.LightningJSONDecoder()) + def getpeer(self, peer_id, level=None): """ Show peer with {peer_id}, if {level} is set, include {log}s @@ -147,7 +300,7 @@ def listnodes(self, node_id=None): } return self.call("listnodes", payload) - def getroute(self, peer_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzpercent=None, seed=None, exclude=[]): + def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzpercent=None, seed=None, exclude=[]): """ Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). If specified search from {fromid} otherwise use @@ -156,7 +309,7 @@ def getroute(self, peer_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzperce seed. {exclude} is an optional array of scid/direction to exclude. """ payload = { - "id": peer_id, + "id": node_id, "msatoshi": msatoshi, "riskfactor": riskfactor, "cltv": cltv, @@ -241,11 +394,14 @@ def decodepay(self, bolt11, description=None): } return self.call("decodepay", payload) - def help(self): + def help(self, command=None): """ - Show available commands + Show available commands, or just {command} if supplied. """ - return self.call("help") + payload = { + "command": command, + } + return self.call("help", payload) def stop(self): """ @@ -315,19 +471,19 @@ def waitsendpay(self, payment_hash, timeout=None): } return self.call("waitsendpay", payload) - def pay(self, bolt11, msatoshi=None, description=None, riskfactor=None): + def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, description=None): """ Send payment specified by {bolt11} with {msatoshi} - (ignored if {bolt11} has an amount), - - {description} (required if {bolt11} uses description hash) + (ignored if {bolt11} has an amount), optional {label} and {riskfactor} (default 1.0) """ payload = { "bolt11": bolt11, "msatoshi": msatoshi, + "label": label, + "riskfactor": riskfactor, + # Deprecated. "description": description, - "riskfactor": riskfactor } return self.call("pay", payload) @@ -364,17 +520,19 @@ def listpeers(self, peerid=None, level=None): } return self.call("listpeers", payload) - def fundchannel(self, node_id, satoshi, feerate=None, announce=True): + def fundchannel(self, node_id, satoshi, feerate=None, announce=True, minconf=None): """ Fund channel with {id} using {satoshi} satoshis with feerate of {feerate} (uses default feerate if unset). If {announce} is False, don't send channel announcements. + Only select outputs with {minconf} confirmations """ payload = { "id": node_id, "satoshi": satoshi, "feerate": feerate, - "announce": announce + "announce": announce, + "minconf": minconf, } return self.call("fundchannel", payload) @@ -441,15 +599,17 @@ def dev_memleak(self): """ return self.call("dev-memleak") - def withdraw(self, destination, satoshi, feerate=None): + def withdraw(self, destination, satoshi, feerate=None, minconf=None): """ Send to {destination} address {satoshi} (or "all") - amount via Groestlcoin transaction + amount via Groestlcoin transaction. Only select outputs + with {minconf} confirmations """ payload = { "destination": destination, "satoshi": satoshi, - "feerate": feerate + "feerate": feerate, + "minconf": minconf, } return self.call("withdraw", payload) diff --git a/contrib/pylightning/lightning/plugin.py b/contrib/pylightning/lightning/plugin.py index 4232eb2b0861..3da175d70880 100644 --- a/contrib/pylightning/lightning/plugin.py +++ b/contrib/pylightning/lightning/plugin.py @@ -1,6 +1,7 @@ from collections import OrderedDict -from lightning import LightningRpc from enum import Enum +from lightning import LightningRpc, Millisatoshi +from threading import RLock import inspect import json @@ -15,6 +16,76 @@ class MethodType(Enum): HOOK = 1 +class RequestState(Enum): + PENDING = 'pending' + FINISHED = 'finished' + FAILED = 'failed' + + +class Method(object): + """Description of methods that are registered with the plugin. + + These can be one of the following: + + - RPC exposed by RPC passthrough + - HOOK registered to be called synchronously by lightningd + """ + def __init__(self, name, func, mtype=MethodType.RPCMETHOD): + self.name = name + self.func = func + self.mtype = mtype + self.background = False + + +class Request(dict): + """A request object that wraps params and allows async return + """ + def __init__(self, plugin, req_id, method, params, background=False): + self.method = method + self.params = params + self.background = background + self.plugin = plugin + self.state = RequestState.PENDING + self.id = req_id + + def getattr(self, key): + if key == "params": + return self.params + elif key == "id": + return self.id + elif key == "method": + return self.method + + def set_result(self, result): + if self.state != RequestState.PENDING: + raise ValueError( + "Cannot set the result of a request that is not pending, " + "current state is {state}".format(self.state)) + self.result = result + self._write_result({ + 'jsonrpc': '2.0', + 'id': self.id, + 'result': self.result + }) + + def set_exception(self, exc): + if self.state != RequestState.PENDING: + raise ValueError( + "Cannot set the exception of a request that is not pending, " + "current state is {state}".format(self.state)) + self.exc = exc + self._write_result({ + 'jsonrpc': '2.0', + 'id': self.id, + "error": "Error while processing {method}: {exc}".format( + method=self.method, exc=repr(exc) + ), + }) + + def _write_result(self, result): + self.plugin._write_locked(result) + + class Plugin(object): """Controls interactions with lightningd, and bundles functionality. @@ -25,7 +96,7 @@ class Plugin(object): """ def __init__(self, stdout=None, stdin=None, autopatch=True): - self.methods = {} + self.methods = {'init': Method('init', self._init, MethodType.RPCMETHOD)} self.options = {} # A dict from topics to handler functions @@ -39,13 +110,15 @@ def __init__(self, stdout=None, stdin=None, autopatch=True): if os.getenv('LIGHTNINGD_PLUGIN') and autopatch: monkey_patch(self, stdout=True, stderr=True) - self.add_method("getmanifest", self._getmanifest) + self.add_method("getmanifest", self._getmanifest, background=False) self.rpc_filename = None self.lightning_dir = None self.rpc = None - self.init = None + self.child_init = None + + self.write_lock = RLock() - def add_method(self, name, func): + def add_method(self, name, func, background=False): """Add a plugin method to the dispatch table. The function will be expected at call time (see `_dispatch`) @@ -65,6 +138,13 @@ def add_method(self, name, func): plugin and request argument should always be the last two arguments and have a default on None. + The `background` argument can be used to specify whether the method is + going to return a result that should be sent back to the lightning + daemon (`background=False`) or whether the method will return without + sending back a result. In the latter case the method MUST use + `request.set_result` or `result.set_exception` to return a result or + raise an exception for the call. + """ if name in self.methods: raise ValueError( @@ -72,7 +152,9 @@ def add_method(self, name, func): ) # Register the function with the name - self.methods[name] = (func, MethodType.RPCMETHOD) + method = Method(name, func, MethodType.RPCMETHOD) + method.background = background + self.methods[name] = method def add_subscription(self, topic, func): """Add a subscription to our list of subscriptions. @@ -129,24 +211,36 @@ def get_option(self, name): else: return self.options[name]['default'] - def method(self, method_name, *args, **kwargs): + def async_method(self, method_name): + """Decorator to add an async plugin method to the dispatch table. + + Internally uses add_method. + """ + def decorator(f): + self.add_method(method_name, f, background=True) + return f + return decorator + + def method(self, method_name): """Decorator to add a plugin method to the dispatch table. Internally uses add_method. """ def decorator(f): - self.add_method(method_name, f) + self.add_method(method_name, f, background=False) return f return decorator - def add_hook(self, name, func): + def add_hook(self, name, func, background=False): """Register a hook that is called synchronously by lightningd on events """ if name in self.methods: raise ValueError( "Method {} was already registered".format(name, self.methods[name]) ) - self.methods[name] = (func, MethodType.HOOK) + method = Method(name, func, MethodType.HOOK) + method.background = background + self.methods[name] = method def hook(self, method_name): """Decorator to add a plugin hook to the dispatch table. @@ -154,17 +248,37 @@ def hook(self, method_name): Internally uses add_hook. """ def decorator(f): - self.add_hook(method_name, f) + self.add_hook(method_name, f, background=False) + return f + return decorator + + def async_hook(self, method_name): + """Decorator to add an async plugin hook to the dispatch table. + + Internally uses add_hook. + """ + def decorator(f): + self.add_hook(method_name, f, background=True) + return f + return decorator + + def init(self, *args, **kwargs): + """Decorator to add a function called after plugin initialization + """ + def decorator(f): + if self.child_init is not None: + raise ValueError('The @plugin.init decorator should only be used once') + self.child_init = f return f return decorator def _exec_func(self, func, request): - params = request['params'] + params = request.params sig = inspect.signature(func) arguments = OrderedDict() for name, value in sig.parameters.items(): - arguments[name] = inspect.Signature.empty + arguments[name] = inspect._empty # Fill in any injected parameters if 'plugin' in arguments: @@ -173,72 +287,109 @@ def _exec_func(self, func, request): if 'request' in arguments: arguments['request'] = request + args = [] + kwargs = {} # Now zip the provided arguments and the prefilled a together if isinstance(params, dict): - arguments.update(params) + for k, v in params.items(): + if k in arguments: + # Explicitly (try to) interpret as Millisatoshi if annotated + if func.__annotations__.get(k) == Millisatoshi: + arguments[k] = Millisatoshi(v) + else: + arguments[k] = v + else: + kwargs[k] = v else: pos = 0 for k, v in arguments.items(): - if v is not inspect.Signature.empty: + # Skip already assigned args and special catch-all args + if v is not inspect._empty or k in ['args', 'kwargs']: continue + if pos < len(params): # Apply positional args if we have them - arguments[k] = params[pos] + if func.__annotations__.get(k) == Millisatoshi: + arguments[k] = Millisatoshi(params[pos]) + else: + arguments[k] = params[pos] + elif sig.parameters[k].default is inspect.Signature.empty: + # This is a positional arg with no value passed + raise TypeError("Missing required parameter: %s" % sig.parameters[k]) else: # For the remainder apply default args arguments[k] = sig.parameters[k].default pos += 1 + if len(arguments) < len(params): + args = params[len(arguments):] + + if 'kwargs' in arguments: + arguments['kwargs'] = kwargs + elif len(kwargs) > 0: + raise TypeError("Extra arguments given: {kwargs}".format(kwargs=kwargs)) + + if 'args' in arguments: + arguments['args'] = args + elif len(args) > 0: + raise TypeError("Extra arguments given: {args}".format(args=args)) + + missing = [k for k, v in arguments.items() if v is inspect._empty] + if missing: + raise TypeError("Missing positional arguments ({given} given, " + "expected {expected}): {missing}".format( + missing=", ".join(missing), + given=len(arguments) - len(missing), + expected=len(arguments) + )) ba = sig.bind(**arguments) ba.apply_defaults() return func(*ba.args, **ba.kwargs) def _dispatch_request(self, request): - name = request['method'] + name = request.method if name not in self.methods: raise ValueError("No method {} found.".format(name)) - func, _ = self.methods[name] + method = self.methods[name] + request.background = method.background try: - result = { - 'jsonrpc': '2.0', - 'id': request['id'], - 'result': self._exec_func(func, request) - } + result = self._exec_func(method.func, request) + if not method.background: + # Only if this is not an async (background) call do we need to + # return the result, otherwise the callee will eventually need + # to call request.set_result or request.set_exception to + # return a result or raise an exception. + request.set_result(result) except Exception as e: - result = { - 'jsonrpc': '2.0', - 'id': request['id'], - "error": "Error while processing {}: {}".format( - request['method'], repr(e) - ), - } + request.set_exception(e) self.log(traceback.format_exc()) - json.dump(result, fp=self.stdout) - self.stdout.write('\n\n') - self.stdout.flush() def _dispatch_notification(self, request): - name = request['method'] - if name not in self.subscriptions: - raise ValueError("No subscription for {} found.".format(name)) - func = self.subscriptions[name] + if request.method not in self.subscriptions: + raise ValueError("No subscription for {name} found.".format( + name=request.method)) + func = self.subscriptions[request.method] try: self._exec_func(func, request) - except Exception as _: + except Exception: self.log(traceback.format_exc()) + def _write_locked(self, obj): + s = json.dumps(obj, cls=LightningRpc.LightningJSONEncoder) + "\n\n" + with self.write_lock: + self.stdout.write(s) + self.stdout.flush() + def notify(self, method, params): payload = { 'jsonrpc': '2.0', 'method': method, 'params': params, } - json.dump(payload, self.stdout) - self.stdout.write("\n\n") - self.stdout.flush() + self._write_locked(payload) def log(self, message, level='info'): # Split the log into multiple lines and print them @@ -246,18 +397,30 @@ def log(self, message, level='info'): for line in message.split('\n'): self.notify('log', {'level': level, 'message': line}) + def _parse_request(self, jsrequest): + request = Request( + plugin=self, + req_id=jsrequest.get('id', None), + method=jsrequest['method'], + params=jsrequest['params'], + background=False, + ) + return request + def _multi_dispatch(self, msgs): """We received a couple of messages, now try to dispatch them all. Returns the last partial message that was not complete yet. """ for payload in msgs[:-1]: - request = json.loads(payload) + # Note that we use function annotations to do Millisatoshi conversions + # in _exec_func, so we don't use LightningJSONDecoder here. + request = self._parse_request(json.loads(payload)) # If this has an 'id'-field, it's a request and returns a # result. Otherwise it's a notification and it doesn't # return anything. - if 'id' in request: + if request.id is not None: self._dispatch_request(request) else: self._dispatch_notification(request) @@ -265,12 +428,6 @@ def _multi_dispatch(self, msgs): return msgs[-1] def run(self): - # Stash the init method handler, we'll handle opts first and - # then unstash this and call it. - if 'init' in self.methods: - self.init = self.methods['init'] - self.methods['init'] = (self._init, MethodType.RPCMETHOD) - partial = "" for l in self.stdin: partial += l @@ -281,30 +438,47 @@ def run(self): partial = self._multi_dispatch(msgs) - def _getmanifest(self): + def _getmanifest(self, **kwargs): methods = [] hooks = [] - for name, entry in self.methods.items(): - func, typ = entry + for method in self.methods.values(): # Skip the builtin ones, they don't get reported - if name in ['getmanifest', 'init']: + if method.name in ['getmanifest', 'init']: continue - if typ == MethodType.HOOK: - hooks.append(name) + if method.mtype == MethodType.HOOK: + hooks.append(method.name) continue - doc = inspect.getdoc(func) + doc = inspect.getdoc(method.func) if not doc: self.log( - 'RPC method \'{}\' does not have a docstring.'.format(name) + 'RPC method \'{}\' does not have a docstring.'.format(method.name) ) doc = "Undocumented RPC method from a plugin." doc = re.sub('\n+', ' ', doc) + # Handles out-of-order use of parameters like: + # def hello_obfus(arg1, arg2, plugin, thing3, request=None, thing5='at', thing6=21) + argspec = inspect.getfullargspec(method.func) + defaults = argspec.defaults + num_defaults = len(defaults) if defaults else 0 + start_kwargs_idx = len(argspec.args) - num_defaults + args = [] + for idx, arg in enumerate(argspec.args): + if arg in ('plugin', 'request'): + continue + # Positional arg + if idx < start_kwargs_idx: + args.append("%s" % arg) + # Keyword arg + else: + args.append("[%s]" % arg) + methods.append({ - 'name': name, - 'description': doc, + 'name': method.name, + 'usage': " ".join(args), + 'description': doc }) return { @@ -322,12 +496,9 @@ def _init(self, options, configuration, request): for name, value in options.items(): self.options[name]['value'] = value - # Swap the registered `init` method handler back in and - # re-dispatch - if self.init: - self.methods['init'], _ = self.init - self.init = None - return self._exec_func(self.methods['init'], request) + # Dispatch the plugin's init handler if any + if self.child_init: + return self._exec_func(self.child_init, request) return None @@ -343,7 +514,7 @@ def __init__(self, plugin, level="info"): def write(self, payload): self.buff += payload - if payload[-1] == '\n': + if len(payload) > 0 and payload[-1] == '\n': self.flush() def flush(self): diff --git a/contrib/pylightning/lightning/test_plugin.py b/contrib/pylightning/lightning/test_plugin.py deleted file mode 100644 index 07d2c8ca2f6a..000000000000 --- a/contrib/pylightning/lightning/test_plugin.py +++ /dev/null @@ -1,51 +0,0 @@ -from .plugin import Plugin -import itertools - - -def test_positional_inject(): - p = Plugin() - rdict = { - 'id': 1, - 'jsonrpc': - '2.0', - 'method': 'func', - 'params': {'a': 1, 'b': 2, 'kwa': 3, 'kwb': 4} - } - rarr = { - 'id': 1, - 'jsonrpc': '2.0', - 'method': 'func', - 'params': [1, 2, 3, 4] - } - - def pre_args(plugin, a, b, kwa=3, kwb=4): - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def in_args(a, plugin, b, kwa=3, kwb=4): - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def post_args(a, b, plugin, kwa=3, kwb=4): - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def post_kwargs(a, b, kwa=3, kwb=4, plugin=None): - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def in_multi_args(a, request, plugin, b, kwa=3, kwb=4): - assert request in [rarr, rdict] - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def in_multi_mix_args(a, plugin, b, request=None, kwa=3, kwb=4): - assert request in [rarr, rdict] - assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) - - def extra_def_arg(a, b, c, d, e=42): - """ Also uses a different name for kwa and kwb - """ - assert (a, b, c, d, e) == (1, 2, 3, 4, 42) - - funcs = [pre_args, in_args, post_args, post_kwargs, in_multi_args] - - for func, request in itertools.product(funcs, [rdict, rarr]): - p._exec_func(func, request) - - p._exec_func(extra_def_arg, rarr) diff --git a/contrib/pylightning/setup.py b/contrib/pylightning/setup.py index acb166bbabb9..f019e34ddfd0 100644 --- a/contrib/pylightning/setup.py +++ b/contrib/pylightning/setup.py @@ -1,11 +1,16 @@ from setuptools import setup +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + setup(name='pylightning', version='0.0.6', description='Client library for lightningd', - url='http://github.com/ElementsProject/lightning', - author='Christian Decker', - author_email='decker.christian@gmail.com', + long_description=long_description, + long_description_content_type='text/markdown', + url='http://github.com/Groestlcoin/lightning', + author='Groestlcoin Developers', + author_email='groestlcoin@gmail.com', license='MIT', packages=['lightning'], scripts=['lightning-pay'], diff --git a/contrib/pylightning/tests/test_plugin.py b/contrib/pylightning/tests/test_plugin.py index 60e37de2e024..60f642874e91 100644 --- a/contrib/pylightning/tests/test_plugin.py +++ b/contrib/pylightning/tests/test_plugin.py @@ -1,6 +1,6 @@ from lightning import Plugin - - +from lightning.plugin import Request +import itertools import pytest @@ -15,13 +15,13 @@ def test1(name): """Has a single positional argument.""" assert name == 'World' call_list.append(test1) - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': {'name': 'World'} - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1] @p.method("test2") @@ -29,13 +29,13 @@ def test2(name, plugin): """Also asks for the plugin instance. """ assert plugin == p call_list.append(test2) - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test2', 'params': {'name': 'World'} - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2] @p.method("test3") @@ -43,13 +43,13 @@ def test3(name, request): """Also asks for the request instance. """ assert request is not None call_list.append(test3) - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test3', 'params': {'name': 'World'} - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2, test3] @p.method("test4") @@ -57,13 +57,13 @@ def test4(name): """Try the positional arguments.""" assert name == 'World' call_list.append(test4) - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test4', 'params': ['World'] - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2, test3, test4] @p.method("test5") @@ -73,13 +73,13 @@ def test5(name, request, plugin): assert request is not None assert p == plugin call_list.append(test5) - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test5', 'params': ['World'] - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2, test3, test4, test5] answers = [] @@ -92,23 +92,23 @@ def test6(name, answer=42): call_list.append(test6) # Both calls should work (with and without the default param - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test6', 'params': ['World'] - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2, test3, test4, test5, test6] assert answers == [42] - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test6', 'params': ['World', 31337] - } - p._dispatch(request) + }) + p._dispatch_request(request) assert call_list == [test1, test2, test3, test4, test5, test6, test6] assert answers == [42, 31337] @@ -119,14 +119,14 @@ def test_methods_errors(): p = Plugin(autopatch=False) # Fails because we haven't added the method yet - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': {} - } + }) with pytest.raises(ValueError): - p._dispatch(request) + p._dispatch_request(request) assert call_list == [] @p.method("test1") @@ -138,34 +138,102 @@ def test1(name): p.add_method("test1", test1) # Fails because it is missing the 'name' argument - request = {'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': {}} + request = p._parse_request({'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': {}}) with pytest.raises(TypeError): - p._dispatch(request) + p._exec_func(test1, request) assert call_list == [] # The same with positional arguments - request = {'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': []} + request = p._parse_request({'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': []}) with pytest.raises(TypeError): - p._dispatch(request) + p._exec_func(test1, request) assert call_list == [] # Fails because we have a non-matching argument - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': {'name': 'World', 'extra': 1} - } + }) with pytest.raises(TypeError): - p._dispatch(request) + p._exec_func(test1, request) assert call_list == [] - request = { + request = p._parse_request({ 'id': 1, 'jsonrpc': '2.0', 'method': 'test1', 'params': ['World', 1] - } + }) + with pytest.raises(TypeError): - p._dispatch(request) + p._exec_func(test1, request) assert call_list == [] + + +def test_positional_inject(): + p = Plugin() + rdict = Request( + plugin=p, + req_id=1, + method='func', + params={'a': 1, 'b': 2, 'kwa': 3, 'kwb': 4} + ) + rarr = Request( + plugin=p, + req_id=1, + method='func', + params=[1, 2, 3, 4], + ) + + def pre_args(plugin, a, b, kwa=3, kwb=4): + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def in_args(a, plugin, b, kwa=3, kwb=4): + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def post_args(a, b, plugin, kwa=3, kwb=4): + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def post_kwargs(a, b, kwa=3, kwb=4, plugin=None): + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def in_multi_args(a, request, plugin, b, kwa=3, kwb=4): + assert request in [rarr, rdict] + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def in_multi_mix_args(a, plugin, b, request=None, kwa=3, kwb=4): + assert request in [rarr, rdict] + assert (plugin, a, b, kwa, kwb) == (p, 1, 2, 3, 4) + + def extra_def_arg(a, b, c, d, e=42): + """ Also uses a different name for kwa and kwb + """ + assert (a, b, c, d, e) == (1, 2, 3, 4, 42) + + def count(plugin, count, request): + assert count == 42 and plugin == p + + funcs = [pre_args, in_args, post_args, post_kwargs, in_multi_args] + + for func, request in itertools.product(funcs, [rdict, rarr]): + p._exec_func(func, request) + + p._exec_func(extra_def_arg, rarr) + + p._exec_func(count, Request( + plugin=p, + req_id=1, + method='func', + params=[42], + )) + + # This should fail since it is missing one positional argument + with pytest.raises(TypeError): + p._exec_func(count, Request( + plugin=p, + req_id=1, + method='func', + params=[]) + ) diff --git a/devtools/Makefile b/devtools/Makefile index 4ea586a75df0..8018804b8692 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -5,6 +5,7 @@ DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) DEVTOOLS_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index fb36513f2e04..49095af55389 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -104,8 +105,11 @@ int main(int argc, char *argv[]) printf("payment_hash: %s\n", tal_hexstr(ctx, &b11->payment_hash, sizeof(b11->payment_hash))); printf("min_final_cltv_expiry: %u\n", b11->min_final_cltv_expiry); - if (b11->msatoshi) - printf("msatoshi: %"PRIu64"\n", *b11->msatoshi); + if (b11->msat) { + printf("msatoshi: %"PRIu64"\n", b11->msat->millisatoshis); /* Raw: raw int for backwards compat */ + printf("amount_msat: %s\n", + type_to_string(tmpctx, struct amount_msat, b11->msat)); + } if (b11->description) printf("description: '%s'\n", b11->description); if (b11->description_hash) diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 7604ce17fce3..4e86a866f6d0 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) while (read(fd, &belen, sizeof(belen)) == sizeof(belen) && read(fd, &becsum, sizeof(becsum)) == sizeof(becsum)) { - u64 satoshis; + struct amount_sat sat; struct short_channel_id scid; u8 *gossip_msg; u32 msglen = be32_to_cpu(belen); @@ -54,9 +54,10 @@ int main(int argc, char *argv[]) if (fromwire_gossip_store_channel_announcement(msg, msg, &gossip_msg, - &satoshis)) { - printf("channel_announce for %"PRIu64" satoshis: %s\n", - satoshis, tal_hex(msg, gossip_msg)); + &sat)) { + printf("channel_announce for %s: %s\n", + type_to_string(tmpctx, struct amount_sat, &sat), + tal_hex(msg, gossip_msg)); } else if (fromwire_gossip_store_channel_update(msg, msg, &gossip_msg)) { printf("channel_update: %s\n", diff --git a/devtools/onion.c b/devtools/onion.c index 33f9e0ed0e86..7c8cf08b80d0 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -38,7 +38,7 @@ static void do_generate(int argc, char **argv) hops_data[i].realm = i; memset(&hops_data[i].channel_id, i, sizeof(hops_data[i].channel_id)); - hops_data[i].amt_forward = i; + hops_data[i].amt_forward.millisatoshis = i; /* Raw: test code */ hops_data[i].outgoing_cltv = i; fprintf(stderr, "Hopdata %d: %s\n", i, tal_hexstr(NULL, &hops_data[i], sizeof(hops_data[i]))); } diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 5463ff297313..9ec377b50fd5 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -180,4 +180,6 @@ PRINTWIRE_STRUCT_TYPE_TO_STRING(pubkey); PRINTWIRE_STRUCT_TYPE_TO_STRING(sha256); PRINTWIRE_STRUCT_TYPE_TO_STRING(secret); PRINTWIRE_STRUCT_TYPE_TO_STRING(short_channel_id); +PRINTWIRE_STRUCT_TYPE_TO_STRING(amount_sat); +PRINTWIRE_STRUCT_TYPE_TO_STRING(amount_msat); PRINTWIRE_TYPE_TO_STRING(secp256k1_ecdsa_signature, secp256k1_ecdsa_signature); diff --git a/devtools/print_wire.h b/devtools/print_wire.h index fea9c3904338..ffa386c52c23 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -14,6 +14,8 @@ void printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, void printwire_bitcoin_blkid(const char *fieldname, const struct bitcoin_blkid *bitcoin_blkid); void printwire_bitcoin_txid(const char *fieldname, const struct bitcoin_txid *bitcoin_txid); void printwire_channel_id(const char *fieldname, const struct channel_id *channel_id); +void printwire_amount_sat(const char *fieldname, const struct amount_sat *sat); +void printwire_amount_msat(const char *fieldname, const struct amount_msat *msat); void printwire_preimage(const char *fieldname, const struct preimage *preimage); void printwire_pubkey(const char *fieldname, const struct pubkey *pubkey); void printwire_secp256k1_ecdsa_signature(const char *fieldname, const secp256k1_ecdsa_signature *); diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md new file mode 120000 index 000000000000..04c99a55caae --- /dev/null +++ b/doc/CHANGELOG.md @@ -0,0 +1 @@ +../CHANGELOG.md \ No newline at end of file diff --git a/doc/HACKING.md b/doc/HACKING.md index cf015c95993d..2a88e1c532f3 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -1,3 +1,6 @@ +Hacking +======= + Welcome, fellow coder! This repository contains a code to run a lightning protocol daemon. @@ -165,7 +168,7 @@ pip3 install -r tests/requirements.txt Re-run `configure` for the python dependencies ``` -./configure +./configure --enable-developer ``` Tests are run with: `make check [flags]` where the pertinent flags are: diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 39200ee44ee2..b371b582db3e 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -1,4 +1,6 @@ -# Table of Contents +Install +======= + 1. [Library Requirements](#library-requirements) 2. [Ubuntu](#to-build-on-ubuntu) 3. [Fedora](#to-build-on-fedora) @@ -36,7 +38,8 @@ Get dependencies: sudo apt-get update sudo apt-get install -y \ autoconf automake build-essential git libtool libgmp-dev \ - libsqlite3-dev python python3 net-tools zlib1g-dev + libsqlite3-dev python python3 net-tools zlib1g-dev libsodium-dev \ + libbase58-dev If you don't have Groestlcoin installed locally you'll need to install that as well: @@ -93,7 +96,8 @@ $ sudo dnf update -y && \ net-tools \ valgrind \ wget \ - zlib-devel && \ + zlib-devel \ + libsodium-devel && \ sudo dnf clean all ``` @@ -127,8 +131,7 @@ $ lightningd --network=testnet To Build on macOS --------------------- -Assume you have Xcode and HomeBrew installed on your Mac. -Get dependencies: +Assuming you have Xcode and Homebrew installed. Install dependencies: $ brew install autoconf automake libtool python3 gmp gnu-sed @@ -204,7 +207,7 @@ 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 +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 @@ -249,9 +252,9 @@ Download and build sqlite3: Download and build gmp: wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz - tar xvf 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 + ./configure --disable-assembly --host=$target_host --prefix=$QEMU_LD_PREFIX make make install diff --git a/doc/MAKING-RELEASES.md b/doc/MAKING-RELEASES.md index 24f5d9930e79..c1b68775344c 100644 --- a/doc/MAKING-RELEASES.md +++ b/doc/MAKING-RELEASES.md @@ -1,6 +1,8 @@ +## Release checklist + Here's a checklist for the release process. -## Leading Up To The Release +### 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. @@ -9,14 +11,16 @@ Here's a checklist for the release process. 3. Ask the most significant contributor who has not already named a release to name the release. CC previous namers and team. -## Prepering for -rc1 +### Prepering for -rc1 -1. Check that CHANGELOG.md covers all signficant changes. +1. Check that CHANGELOG.md is well formatted, ordered in areas, + covers all signficant changes, and sub-ordered approximately by user impact + & coolness. 2. Update the CHANGELOG.md with [Unreleased] changed to -rc1, and add a new footnote. 3. Create a PR with the above. -## Releasing -rc1 +### Releasing -rc1 1. Merge the PR above. 2. Tag it `git pull && git tag -s vrc1 && git push --tags` @@ -24,7 +28,7 @@ Here's a checklist for the release process. 4. Prepare draft release notes, and share with team for editing. 5. Upgrade your personal nodes to the rc1, to help testing. -## Tagging the Release +### Tagging the Release 1. Update the CHANGELOG.md; remove -rc1 in both places, and move the [Unreleased] footnote URL from the previous version to the @@ -41,7 +45,7 @@ Here's a checklist for the release process. with `gpg --verify SHA256SUMS.asc` and include the file in the draft release. -## Performing the Release +### Performing the Release 1. Edit the GitHub draft and include the `SHA256SUMS.asc` file. 2. Publish the release as not a draft. @@ -49,7 +53,7 @@ Here's a checklist for the release process. 4. Send a mail to c-lightning and lightning-dev mailing lists, using the same wording as the Release Notes in github. -## Post-release +### Post-release 1. Add a new '[Unreleased]' section the CHANGELOG.md with empty headers. 2. Look through PRs which were delayed for release and merge them. diff --git a/doc/Makefile b/doc/Makefile index 78449841eea7..8f26c257250d 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -20,8 +20,9 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-listchannels.7 \ doc/lightning-listfunds.7 \ doc/lightning-listinvoices.7 \ - doc/lightning-listpayments.7 \ + doc/lightning-listpays.7 \ doc/lightning-listpeers.7 \ + doc/lightning-listsendpays.7 \ doc/lightning-newaddr.7 \ doc/lightning-pay.7 \ doc/lightning-sendpay.7 \ @@ -29,7 +30,6 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-waitanyinvoice.7 \ doc/lightning-waitsendpay.7 \ doc/lightning-withdraw.7 - doc-all: $(MANPAGES) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index d4cb5f6c9d69..cbab9562eedd 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -70,10 +70,12 @@ this example: "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." } @@ -93,9 +95,10 @@ currently only string 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` and the `description` fields +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). +set to `description` if it was not provided). `usage` should surround optional +parameter names in `[]`. Plugins are free to register any `name` for their `rpcmethod` as long as the name was not previously registered. This includes both built-in @@ -149,11 +152,13 @@ called `hello` and `gettime`: "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." } ], @@ -217,8 +222,53 @@ to a peer was lost. ``` ## Hooks -*TBD* +Hooks allow a plugin to define custom behavior for `lightningd` +without having to modify the c-lightning source code itself. A plugin +declares that it'd like to 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. +Hooks and notifications sounds 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, + however only one plugin may register for any hook topic at any + point in time (we cannot disambiguate between multiple plugins + returning contradictory results from a hook callback). + +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. + +### Hook Types + +#### `peer_connected` + +This hook is called whenever a peer has connected and successfully completed +the cryptographic handshake. The parameters have the following structure if there is a channel with the peer: + +```json +{ + "peer": { + "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", + "addr": "34.239.230.56:9735", + "globalfeatures": "", + "localfeatures": "" + } +} +``` + +The hook is sparse on purpose, since the plugin can use the JSON-RPC +`listpeers` command to get additional details should they be required. 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. [jsonrpc-spec]: https://www.jsonrpc.org/specification [jsonrpc-notification-spec]: https://www.jsonrpc.org/specification#notification diff --git a/doc/REPRODUCIBLE.md b/doc/REPRODUCIBLE.md new file mode 100644 index 000000000000..668c6b645880 --- /dev/null +++ b/doc/REPRODUCIBLE.md @@ -0,0 +1,113 @@ +# Rusty's Unreliable Guide to The Terrible World of Reproducible Builds + +1. The reproducible build system currently only supports Ubuntu 18.04.1. +2. It requires manual steps. +3. The input is a source zipfile, the output is a .tar.xz. + +## Step 1: Creating a Build Machine + +Download the Ubuntu Desktop ISO image for 18.04.1. I got it from +http://old-releases.ubuntu.com/releases/18.04.1/ubuntu-18.04.1-desktop-amd64.iso + +The `sha256sum` of this file should be +`5748706937539418ee5707bd538c4f5eabae485d17aa49fb13ce2c9b70532433`. + +Do a standard install, but make sure to *uncheck* 'Download updates +while installing Ubuntu' in the installer (or simply deprive it of a +network connection as I do below). I did the following to install under kvm: + + qemu-img create ubuntu-18.04.01.raw 10G + kvm -m 2G -cdrom ~/Downloads/ubuntu-18.04.1-desktop-amd64.iso ubuntu-18.04.01.raw -nic none + +You can choose a 'Minimal installation': it shouldn't matter. + +Once the installation is over, it'll want to restart. Then make sure you +disable updates: + +1. Left-click on the bottom left 9-dots menu +2. Type "update" +3. Click on the "Software & Up.." box icon. +4. Click on the "Updates" tab at the top of that app. +5. Uncheck "Important security updates", "Recommended updates" and + "Unsupported updates". You'll have to re-enter your password. +6. Hit "Close". +7. If asked, hit "Reload". + +If you didn't have a network connection, you'll want to add one for +the next steps; for me, this meant powering off the build machine and restarting: + + kvm -m 2G ubuntu-18.04.01.raw -nic user + +And then ran `sudo apt-get update` after I'd logged in. + +## Step 2: Create the Source Zipfile + +Create the source zip that the Build Machine will need, using + + ./tools/build-release.sh zipfile + +For testing (ie. when you're not on a proper released version), you +can use --force-version=, --force-mtime= and even --force-unclean. + +The will place a file into `release/`, eg. `clightning-v0.7.0rc2.zip`. + +### Example + +If you are on the git commit v0.7.0rc2 (1dcc4823507df177bf11ca60ab7da988205139b1): +``` +$ sha256sum release/clightning-v0.7.0rc2.zip +3c980858024b8b429333e7ee5a545c499ac6c25d0f1d11bb45fafce00c99ebba release/clightning-v0.7.0rc2.zip +``` + +## Step 3: Put the Zipfile Onto The Build Machine + +You can upload it somewhere and download it into the machine, or +various virtualization solutions or a USB stick for a physical machine. + +I simply started a server on my host, like so: + + cd release && python3 -m http.server --bind 127.0.0.1 8888 + +Inside my KVM build machine I did: + + wget http://10.0.2.2:8888/clightning-v0.7.0rc2.zip + + +## Step 4: Do the Build + +1. `unzip clightning-v0.7.0rc2.zip` +2. `cd clightning-v0.7.0rc2` +3. `tools/repro-build.sh` (use the same `--force-mtime` if testing). + It will download the packages needed to build, check they're identitcal to the + versions we expect, install them then build the binaries and create a tar.xz file. +4. The output will be in that top-level directory. + +### Example: + +If you built from our example zipfile: +``` +$ sha256sum clightning-v0.7.0rc2-Ubuntu-18.04.tar.xz +c9b4d9530b9b41456f460c58e3ffaa779cdc1c11fb9e3eaeea0f364b62de3d96 clightning-v0.7.0rc2-Ubuntu-18.04.tar.xz +``` + + +## Step 5: Get the Built Result Off the Build Machine + +Again, there are many ways, but for my KVM settings the simplest was: + +On the host: + + nc -l -p 8888 > clightning-v0.7.0rc2-Ubuntu-18.04.tar.xz + +On the guest: + + nc -q0 10.0.2.2 8888 < clightning-v0.7.0rc2-Ubuntu-18.04.tar.xz + + +## Step 5: Tell the World + +You can find my example artifacts on https://ozlabs.org/~rusty/clightning-repro +if you want to see why your build produced a different result from mine. + +Happy hacking! +Rusty. diff --git a/doc/TOR.md b/doc/TOR.md index 1d4e7f8566a4..4dc4a5bf3bd2 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -1,9 +1,8 @@ -# HOWTO USE TOR WITH C-LIGHTNING +# Setting up TOR with c-lightning to use tor you have to have tor installed an running. -i.e. -``` +```bash sudo apt install tor ``` then `/etc/init.d/tor start` or `sudo systemctl start tor` Depending diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000000..2352d1f468ad --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# c-lightning documentation build configuration file, created by +# sphinx-quickstart on Thu Feb 1 00:24:47 2018. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +from datetime import datetime +import subprocess + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.mathjax', + 'sphinx.ext.githubpages', + 'sphinx.ext.graphviz', + 'sphinx.ext.autodoc', +] + +source_parsers = { + '.md': 'recommonmark.parser.CommonMarkParser', +} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ['.rst', '.md'] + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'c-lightning' +author = 'c-lightning Developers' +copyright = datetime.now().strftime('2015 — %Y, {}'.format(author)) + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = subprocess.check_output('git describe --always --dirty=-modded --abbrev=7'.split()).decode('ASCII') +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'release-notes'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +html_title = 'c-lightning ' + version + +# A shorter title for the navigation bar. Default is the same as html_title. +# +html_short_title = html_title + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +# html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +# html_domain_indices = True + +# If false, no index is generated. +# +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'c-lightningdoc' diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 000000000000..475084763ff0 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,24 @@ +c-lightning Documentation +========================= + +.. toctree:: + :maxdepth: 1 + :caption: User Documentation + + INSTALL.md + TOR.md + +.. toctree:: + :maxdepth: 2 + :caption: Integrator Documentation + + Writing plugins + +.. toctree:: + :maxdepth: 1 + :caption: Developer Documentation + + HACKING.md + Coding Style Guideline + MAKING-RELEASES.md + CHANGELOG.md diff --git a/doc/lightning-connect.7 b/doc/lightning-connect.7 index 89191df0e868..f143563cac04 100644 --- a/doc/lightning-connect.7 +++ b/doc/lightning-connect.7 @@ -2,12 +2,12 @@ .\" Title: lightning-connect .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 04/26/2018 +.\" Date: 02/11/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-CONNECT" "7" "04/26/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-CONNECT" "7" "02/11/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,31 +31,41 @@ lightning-connect \- Command for connecting to another lightning node\&. .SH "SYNOPSIS" .sp -\fBconnect\fR \fInode_id\fR[@(\fIip_addr\fR|\fIhostname\fR)[:\*(Aqport\*(Aq]] -.sp -or -.sp -\fBconnect\fR \fInode_id\fR[ (\fIip_addr\fR|\fIhostname\fR)[:\*(Aqport\*(Aq]] +\fBconnect\fR \fIid\fR [\fIhost\fR \fIport\fR] .SH "DESCRIPTION" .sp The \fBconnect\fR RPC command establishes a new connection with another node in the Lightning Network\&. .sp -\fInode_id\fR represents the target node\(cqs public key and \fIip_addr\fR can be either IPv4 or IPv6\&. +\fIid\fR represents the target node\(cqs public key\&. As a convenience, \fIid\fR may be of the form \fIid@host\fR or \fIid@host:port\fR\&. In this case, the \fIhost\fR and \fIport\fR parameters must be omitted\&. .sp -If not specified the \fIport\fR is assumed to be 9375 (default lightning port)\&. +\fIhost\fR is the peer\(cqs hostname or IP address\&. .sp -If neither \fIip_addr\fR or \fIhostname\fR is specified, connection will be attempted to an IP belonging to \fInode_id\fR learned through gossip with other already connected peers +If not specified, the \fIport\fR defaults to 9375\&. .sp -Connecting to a node is just the first step in opening a channel with another node, once the peer is connected a channel can be opened with lightning\-fundchannel(7) +If \fIhost\fR is not specified, the connection will be attempted to an IP belonging to \fIid\fR obtained through gossip with other already connected peers\&. +.sp +Connecting to a node is just the first step in opening a channel with another node\&. Once the peer is connected a channel can be opened with lightning\-fundchannel(7)\&. .SH "RETURN VALUE" .sp -On success the response will include just the peer id (peer\(cqs public key) -.SH "ERRORS" +On success the peer \fIid\fR is returned\&. +.sp +The following error codes may occur: .sp -If \fInode_id\fR or \fIip_addr\fR are invalid or if \fIhostname\fR can\(cqt be resolved to a valid IP address an error message will be returned\&. An error will also be returned if the simplified version (only \fInode_id\fR) is used and there\(cqs no published IP for the peer\&. \fBconnect\fR will make up to 10(?) attempts to connect to the peer before giving up +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\-1\&. Catchall nonspecific error\&. This may occur if the host is not valid or there are problems communicating with the peer\&. +\fBconnect\fR +will make up to 10 attempts to connect to the peer before giving up\&. +.RE .SH "AUTHOR" .sp -Felix is mainly responsible\&. +Rusty Russell is mainly responsible\&. Felix is the original author of this manpage\&. .SH "SEE ALSO" .sp lightning\-fundchannel(7), lightning\-listpeers(7), lightning\-listchannels(7), lightning\-disconnect(7) diff --git a/doc/lightning-connect.7.txt b/doc/lightning-connect.7.txt index e82f612858ad..e987dcd60adb 100644 --- a/doc/lightning-connect.7.txt +++ b/doc/lightning-connect.7.txt @@ -9,50 +9,48 @@ lightning node. SYNOPSIS -------- -*connect* 'node_id'[@('ip_addr'|'hostname')[:'port']] - -or - -*connect* 'node_id'[ ('ip_addr'|'hostname')[:'port']] +*connect* 'id' ['host' 'port'] DESCRIPTION ----------- The *connect* RPC command establishes a new connection with another node in the Lightning Network. -'node_id' represents the target node's public key and -'ip_addr' can be either IPv4 or IPv6. +'id' represents the target node's public key. +As a convenience, 'id' may be of the form 'id@host' or 'id@host:port'. +In this case, the 'host' and 'port' parameters must be omitted. + +'host' is the peer's hostname or IP address. -If not specified the 'port' is assumed to be 9375 -(default lightning port). +If not specified, the 'port' defaults to 9375. -If neither 'ip_addr' or 'hostname' is specified, +If 'host' is not specified, the connection will be attempted to an IP belonging to -'node_id' learned through gossip with other already -connected peers +'id' obtained through gossip with other already +connected peers. Connecting to a node is just the first step in opening -a channel with another node, once the peer is connected -a channel can be opened with lightning-fundchannel(7) +a channel with another node. +Once the peer is connected a channel can be opened with +lightning-fundchannel(7). RETURN VALUE ------------ -On success the response will include just the peer id -(peer's public key) +On success the peer 'id' is returned. -ERRORS ------- -If 'node_id' or 'ip_addr' are invalid or if 'hostname' -can't be resolved to a valid IP address an error message -will be returned. An error will also be returned if the -simplified version (only 'node_id') is used and there's -no published IP for the peer. *connect* will make up to -10(?) attempts to connect to the peer before giving up +The following error codes may occur: + +* -1. Catchall nonspecific error. + This may occur if the host is not valid or there are problems + communicating with the peer. + *connect* will make up to 10 attempts to connect to the peer before + giving up. AUTHOR ------ -Felix is mainly responsible. +Rusty Russell is mainly responsible. +Felix is the original author of this manpage. SEE ALSO -------- diff --git a/doc/lightning-decodepay.7 b/doc/lightning-decodepay.7 index badec11b57d3..fce49f14ac73 100644 --- a/doc/lightning-decodepay.7 +++ b/doc/lightning-decodepay.7 @@ -2,12 +2,12 @@ .\" Title: lightning-decodepay .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 04/26/2018 +.\" Date: 02/18/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-DECODEPAY" "7" "04/26/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-DECODEPAY" "7" "02/18/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -138,6 +138,19 @@ The following fields are optional: .sp -1 .IP \(bu 2.3 .\} +\fIamount_msat\fR: the same as above, with +\fImsat\fR +appended (if any)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fIfallbacks\fR: array of fallback address object containing a \fIhex\fR string, and both diff --git a/doc/lightning-decodepay.7.txt b/doc/lightning-decodepay.7.txt index b6c338fd3b20..aede33950f6c 100644 --- a/doc/lightning-decodepay.7.txt +++ b/doc/lightning-decodepay.7.txt @@ -33,6 +33,7 @@ by BOLT11: The following fields are optional: - 'msatoshi': the number of millisatoshi requested (if any). +- 'amount_msat': the same as above, with 'msat' appended (if any). - 'fallbacks': array of fallback address object containing a 'hex' string, and both 'type' and 'addr' if it is recognized as one of 'P2PKH', 'P2SH', 'P2WPKH', or 'P2WSH'. - 'routes': an array of routes. Each route is an arrays of objects, each containing 'pubkey', 'short_channel_id', 'fee_base_msat', 'fee_proportional_millionths' and 'cltv_expiry_delta'. diff --git a/doc/lightning-fundchannel.7 b/doc/lightning-fundchannel.7 index 2bacab546cc0..6e74e7d4c0ee 100644 --- a/doc/lightning-fundchannel.7 +++ b/doc/lightning-fundchannel.7 @@ -2,12 +2,12 @@ .\" Title: lightning-fundchannel .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 12/07/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-FUNDCHANN" "7" "12/07/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-FUNDCHANN" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,9 +38,9 @@ The \fBfundchannel\fR RPC command opens a payment channel with a peer by committ .sp \fIid\fR is the peer id obtained from \fBconnect\fR\&. .sp -\fIsatoshi\fR is the amount in satoshis taken from the internal wallet to fund the channel\&. The string \fIall\fR can be used to specify all available funds (or 16777215 gro if more is available)\&. The value cannot be less than the dust limit, currently set to 546, nor more than 16777215 satoshi\&. +\fIsatoshi\fR is the amount in satoshis taken from the internal wallet to fund the channel\&. The string \fIall\fR can be used to specify all available funds (or 16777215 satoshi if more is available)\&. Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in \fIsat\fR, a whole number ending in \fI000msat\fR, or a number with 1 to 8 decimal places ending in \fIgrs\fR\&. The value cannot be less than the dust limit, currently set to 546, nor more than 16777215 satoshi\&. .sp -\fIfeerate\fR is an optional feerate to use\&. It can be one of the strings \fIurgent\fR, \fInormal\fR or \fIslow\fR to use lightningd\(cqs internal estimates: \fInormal\fR is the default\&. +\fIfeerate\fR is an optional feerate used for the opening transaction and as initial feerate for commitment and HTLC transactions\&. It can be one of the strings \fIurgent\fR, \fInormal\fR or \fIslow\fR to use lightningd\(cqs internal estimates: \fInormal\fR is the default\&. .sp \fIannounce\fR is an optional flag that triggers whether to announce this channel or not\&. Defaults to true\&. An unannounced channel is considered private\&. .sp @@ -95,6 +95,17 @@ The following error codes may occur: 302\&. The output amount is too small, and would be considered dust\&. .RE .sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +303\&. Broadcasting of the funding transaction failed, the internal call to bitcoin\-cli returned with an error\&. +.RE +.sp Failure may also occur if \fBlightningd\fR and the peer cannot agree on channel parameters (funding limits, channel reserves, fees, etc\&.)\&. .SH "SEE ALSO" .sp diff --git a/doc/lightning-fundchannel.7.txt b/doc/lightning-fundchannel.7.txt index 97f447fefb9a..02946e184ec6 100644 --- a/doc/lightning-fundchannel.7.txt +++ b/doc/lightning-fundchannel.7.txt @@ -22,12 +22,14 @@ for the channel. 'id' is the peer id obtained from *connect*. -'gro' is the amount in gro's taken from the internal wallet to fund the channel. -The string 'all' can be used to specify all available funds (or 16777215 gro if more is available). +'satoshi' is the amount in satoshis taken from the internal wallet to fund the channel. +The string 'all' can be used to specify all available funds (or 16777215 satoshi if more is available). +Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in 'sat', a whole number ending in '000msat', or a number with 1 to 8 decimal places ending in 'grs'. The value cannot be less than the dust limit, currently set to 546, nor more than 16777215 gro. -'feerate' is an optional feerate to use. It can be one of the strings +'feerate' is an optional feerate used for the opening transaction and as initial feerate for +commitment and HTLC transactions. It can be one of the strings 'urgent', 'normal' or 'slow' to use lightningd's internal estimates: 'normal' is the default. @@ -53,6 +55,8 @@ The following error codes may occur: * 301. There are not enough funds in the internal wallet (including fees) to create the transaction. * 302. The output amount is too small, and would be considered dust. +* 303. Broadcasting of the funding transaction failed, the internal call to + bitcoin-cli returned with an error. Failure may also occur if *lightningd* and the peer cannot agree on channel parameters (funding limits, channel reserves, fees, etc.). diff --git a/doc/lightning-getroute.7 b/doc/lightning-getroute.7 index 69658de21749..dea80452cc9e 100644 --- a/doc/lightning-getroute.7 +++ b/doc/lightning-getroute.7 @@ -2,12 +2,12 @@ .\" Title: lightning-getroute .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 12/17/2018 +.\" Date: 03/01/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-GETROUTE" "7" "12/17/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-GETROUTE" "7" "03/01/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,20 +31,26 @@ lightning-getroute \- Command for routing a payment (low\-level)\&. .SH "SYNOPSIS" .sp -\fBgetroute\fR \fIid\fR \fImsatoshi\fR \fIriskfactor\fR [\fIcltv\fR] [\fIfromid\fR] [\fIfuzzpercent\fR] +\fBgetroute\fR \fIid\fR \fImsatoshi\fR \fIriskfactor\fR [\fIcltv\fR] [\fIfromid\fR] [\fIfuzzpercent\fR] [\fIexclude\fR] [\fImaxhops\fR] .SH "DESCRIPTION" .sp The \fBgetroute\fR RPC command attempts to find the best route for the payment of \fImsatoshi\fR to lightning node \fIid\fR, such that the payment will arrive at \fIid\fR with \fIcltv\fR\-blocks to spare (default 9)\&. .sp -There are two considerations for how good a route is: how low the fees are, and how long your payment will get stuck if a node goes down during the process\&. The \fIriskfactor\fR floating\-point field controls this tradeoff; it is the annual cost of your funds being stuck (as a percentage), multiplied by the percentage chance of each node failing\&. +\fImsatoshi\fR is in millisatoshi precision; it can be a whole number, or a whole number ending in \fImsat\fR or \fIsat\fR, or a number with three decimal places ending in \fIsat\fR, or a number with 1 to 11 decimal places ending in \fIgrs\fR\&. .sp -For example, if you thought there was a 1% chance that a node would fail, and it would cost you 20% per annum if that happened, \fIriskfactor\fR would be 20\&. +There are two considerations for how good a route is: how low the fees are, and how long your payment will get stuck in a delayed output if a node goes down during the process\&. The \fIriskfactor\fR floating\-point field controls this tradeoff; it is the annual cost of your funds being stuck (as a percentage)\&. +.sp +For example, if you thought the convenience of keeping your funds liquid (not stuck) was worth 20% per annum interest, \fIriskfactor\fR would be 20\&. .sp If you didn\(cqt care about risk, \fIriskfactor\fR would be zero\&. .sp \fIfromid\fR is the node to start the route from: default is this node\&. .sp The \fIfuzzpercent\fR is a positive floating\-point number, representing a percentage of the actual fee\&. The \fIfuzzpercent\fR is used to distort computed fees along each channel, to provide some randomization to the route generated\&. 0\&.0 means the exact fee of that channel is used, while 100\&.0 means the fee used might be from 0 to twice the actual fee\&. The default is 5\&.0, or up to 5% fee distortion\&. +.sp +\fIexclude\fR is a JSON array of short\-channel\-id/direction (e\&.g\&. [ "564334x877x1/0", "564195x1292x0/1" ]) which should be excluded from consideration for routing\&. The default is not to exclude any channels\&. +.sp +\fImaxhops\fR is the maximum number of channels to return; default is 20\&. .SH "RISKFACTOR EFFECT ON ROUTING" .sp The risk factor is treated as if it were an additional fee on the route, for the purposes of comparing routes\&. @@ -55,15 +61,13 @@ The formula used is the following approximation: .RS 4 .\} .nf -hop\-risk = num\-hops x per\-hop\-risk -timeout\-cost = blocks\-timeout x per\-block\-cost -risk\-fee = amount x hop\-risk x timeout\-cost +risk\-fee = amount x blocks\-timeout x per\-block\-cost .fi .if n \{\ .RE .\} .sp -We are given a \fIriskfactor\fR; expressed as two multiplied percentages is the same as fractions multiplied by 10000\&. There are 52596 blocks per year, thus \fIper\-block\-cost\fR x \fIper\-hop\-risk\fR is riskfactor\*(Aq divided by 5,259,600,000\&. +We are given a \fIriskfactor\fR expressed as a percentage\&. There are 52596 blocks per year, thus \fIper\-block\-cost\fR is \fIriskfactor\fR divided by 5,259,600\&. .sp The final result is: .sp @@ -71,26 +75,26 @@ The final result is: .RS 4 .\} .nf -risk\-fee = amount x num\-hops x blocks\-timeout x riskfactor / 5259600000 +risk\-fee = amount x blocks\-timeout x riskfactor / 5259600 .fi .if n \{\ .RE .\} .sp -Here are the risk fees as a percentage of the amount sent, using various parameters\&. For comparison with actual fees, we assume nodes charge 0\&.05%: +Here are the risk fees in millisatoshis, using various parameters\&. I assume a channel charges the default of 1000 millisatoshis plus 1 part\-per\-million\&. Common to_self_delay values on the network at 14 and 144 blocks\&. .TS allbox tab(:); ltB ltB ltB ltB ltB. T{ -Riskfactor +Amount (msat) T}:T{ -Nodes +Riskfactor T}:T{ -Delay per node +Delay T}:T{ -Risk Fee % +Risk Fee T}:T{ -Route fee % +Route fee T} .T& lt lt lt lt lt @@ -101,165 +105,422 @@ lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt +lt lt lt lt lt lt lt lt lt lt. T{ .sp -0\&.001 +10,000 T}:T{ .sp -5 +1 T}:T{ .sp -6 +14 T}:T{ .sp 0 T}:T{ .sp -0\&.25 +1001 T} T{ .sp -1 +10,000 T}:T{ .sp -5 +10 T}:T{ .sp -6 +14 T}:T{ .sp 0 T}:T{ .sp -0\&.25 +1001 +T} +T{ +.sp +10,000 +T}:T{ +.sp +100 +T}:T{ +.sp +14 +T}:T{ +.sp +2 +T}:T{ +.sp +1001 T} T{ .sp +10,000 +T}:T{ +.sp 1000 T}:T{ .sp -5 +14 T}:T{ .sp -6 +26 T}:T{ .sp -0\&.0029 +1001 +T} +T{ +.sp +1,000,000 +T}:T{ +.sp +1 +T}:T{ +.sp +14 T}:T{ .sp -0\&.25 +2 +T}:T{ +.sp +1001 T} T{ .sp -0\&.001 +1,000,000 T}:T{ .sp 10 T}:T{ .sp -72 +14 T}:T{ .sp -0 +26 +T}:T{ +.sp +1001 +T} +T{ +.sp +1,000,000 +T}:T{ +.sp +100 T}:T{ .sp -0\&.5 +14 +T}:T{ +.sp +266 +T}:T{ +.sp +1001 T} T{ .sp +1,000,000 +T}:T{ +.sp +1000 +T}:T{ +.sp +14 +T}:T{ +.sp +2661 +T}:T{ +.sp +1001 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp 1 T}:T{ .sp +14 +T}:T{ +.sp +266 +T}:T{ +.sp +1100 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp 10 T}:T{ .sp -72 +14 T}:T{ .sp -0\&.0001 +2661 T}:T{ .sp -0\&.5 +1100 T} T{ .sp +100,000,000 +T}:T{ +.sp +100 +T}:T{ +.sp +14 +T}:T{ +.sp +26617 +T}:T{ +.sp +1100 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp 1000 T}:T{ .sp +14 +T}:T{ +.sp +266179 +T}:T{ +.sp +1100 +T} +T{ +.sp +10,000 +T}:T{ +.sp +1 +T}:T{ +.sp +144 +T}:T{ +.sp +0 +T}:T{ +.sp +1001 +T} +T{ +.sp +10,000 +T}:T{ +.sp 10 T}:T{ .sp -72 +144 T}:T{ .sp -0\&.1369 +2 T}:T{ .sp -0\&.5 +1001 T} T{ .sp -0\&.001 +10,000 T}:T{ .sp -20 +100 T}:T{ .sp -1008 +144 T}:T{ .sp -0 +27 +T}:T{ +.sp +1001 +T} +T{ +.sp +10,000 +T}:T{ +.sp +1000 T}:T{ .sp -1\&.0 +144 +T}:T{ +.sp +273 +T}:T{ +.sp +1001 T} T{ .sp +1,000,000 +T}:T{ +.sp 1 T}:T{ .sp -20 +144 +T}:T{ +.sp +27 +T}:T{ +.sp +1001 +T} +T{ +.sp +1,000,000 +T}:T{ +.sp +10 +T}:T{ +.sp +144 +T}:T{ +.sp +273 +T}:T{ +.sp +1001 +T} +T{ +.sp +1,000,000 +T}:T{ +.sp +100 T}:T{ .sp -1008 +144 T}:T{ .sp -0\&.0077 +2737 T}:T{ .sp -1\&.0 +1001 T} T{ .sp +1,000,000 +T}:T{ +.sp 1000 T}:T{ .sp -20 +144 +T}:T{ +.sp +27378 +T}:T{ +.sp +1001 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp +1 +T}:T{ +.sp +144 +T}:T{ +.sp +2737 +T}:T{ +.sp +1100 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp +10 T}:T{ .sp -1008 +144 T}:T{ .sp -7\&.6660 +27378 T}:T{ .sp -1\&.0 +1100 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp +100 +T}:T{ +.sp +144 +T}:T{ +.sp +273785 +T}:T{ +.sp +1100 +T} +T{ +.sp +100,000,000 +T}:T{ +.sp +1000 +T}:T{ +.sp +144 +T}:T{ +.sp +2737850 +T}:T{ +.sp +1100 T} .TE .sp 1 .SH "RECOMMENDED RISKFACTOR VALUES" .sp -0\&.001 is a value for tie\-breaking in favor of shorter routes, but not really costing in any risk\&. +The default \fIfuzz\fR factor is 5%, so as you can see from the table above, that tends to overwhelm the effect of \fIriskfactor\fR less than about 5\&. .sp 1 is a conservative value for a stable lightning network with very few failures\&. .sp 1000 is an aggressive value for trying to minimize timeouts at all costs\&. +.sp +The default for lightning\-pay(7) is 10, which starts to become a major factor for larger amounts, and is basically ignored for tiny ones\&. .SH "RETURN VALUE" .sp -On success, a "route" array is returned\&. Each array element contains +On success, a "route" array is returned\&. Each array element contains \fIid\fR (the node being routed through), \fImsatoshi\fR (the millisatoshis sent), \fIamount_msat\fR (the same, with \fImsat\fR appended), and \fIdelay\fR (the number of blocks to timeout at this node)\&. .sp -timeout for the payment failure, in blocks\&. +The final \fIid\fR will be the destination \fIid\fR given in the input\&. The difference between the first \fImsatoshi\fR minus the \fImsatoshi\fR given in the input is the fee\&. The first \fIdelay\fR is the very worst case timeout for the payment failure, in blocks\&. .SH "AUTHOR" .sp Rusty Russell is mainly responsible\&. diff --git a/doc/lightning-getroute.7.txt b/doc/lightning-getroute.7.txt index 53a3f674f7dc..9d9f104f9e1b 100644 --- a/doc/lightning-getroute.7.txt +++ b/doc/lightning-getroute.7.txt @@ -9,7 +9,7 @@ lightning-getroute - Command for routing a payment (low-level). SYNOPSIS -------- -*getroute* 'id' 'msatoshi' 'riskfactor' ['cltv'] ['fromid'] ['fuzzpercent'] +*getroute* 'id' 'msatoshi' 'riskfactor' ['cltv'] ['fromid'] ['fuzzpercent'] ['exclude'] ['maxhops'] DESCRIPTION ----------- @@ -17,15 +17,19 @@ The *getroute* RPC command attempts to find the best route for the payment of 'msatoshi' to lightning node 'id', such that the payment will arrive at 'id' with 'cltv'-blocks to spare (default 9). +'msatoshi' is in millisatoshi precision; it can be a whole number, or +a whole number ending in 'msat' or 'sat', or a number with three +decimal places ending in 'sat', or a number with 1 to 11 decimal +places ending in 'grs'. + There are two considerations for how good a route is: how low the -fees are, and how long your payment will get stuck if a node goes down +fees are, and how long your payment will get stuck in a delayed output if a node goes down during the process. The 'riskfactor' floating-point field controls this tradeoff; it is the annual cost of your funds being stuck (as a -percentage), multiplied by the percentage chance of each node failing. +percentage). -For example, if you thought there was a 1% chance that a node would -fail, and it would cost you 20% per annum if that happened, -'riskfactor' would be 20. +For example, if you thought the convenience of keeping your funds liquid +(not stuck) was worth 20% per annum interest, 'riskfactor' would be 20. If you didn't care about risk, 'riskfactor' would be zero. @@ -38,6 +42,9 @@ to provide some randomization to the route generated. while 100.0 means the fee used might be from 0 to twice the actual fee. The default is 5.0, or up to 5% fee distortion. +'exclude' is a JSON array of short-channel-id/direction (e.g. [ "564334x877x1/0", "564195x1292x0/1" ]) which should be excluded from consideration for routing. The default is not to exclude any channels. + +'maxhops' is the maximum number of channels to return; default is 20. RISKFACTOR EFFECT ON ROUTING ---------------------------- @@ -46,45 +53,59 @@ for the purposes of comparing routes. The formula used is the following approximation: ---- -hop-risk = num-hops x per-hop-risk -timeout-cost = blocks-timeout x per-block-cost -risk-fee = amount x hop-risk x timeout-cost +risk-fee = amount x blocks-timeout x per-block-cost ---- -We are given a 'riskfactor'; expressed as two multiplied percentages -is the same as fractions multiplied by 10000. There are 52596 blocks -per year, thus 'per-block-cost' x 'per-hop-risk' is riskfactor' -divided by 5,259,600,000. +We are given a 'riskfactor' expressed as a percentage. There are 52596 blocks +per year, thus 'per-block-cost' is 'riskfactor' divided by 5,259,600. The final result is: ---- -risk-fee = amount x num-hops x blocks-timeout x riskfactor / 5259600000 +risk-fee = amount x blocks-timeout x riskfactor / 5259600 ---- -Here are the risk fees as a percentage of the amount sent, using -various parameters. For comparison with actual fees, we assume nodes -charge 0.05%: +Here are the risk fees in millisatoshis, using various parameters. I +assume a channel charges the default of 1000 millisatoshis plus 1 +part-per-million. Common to_self_delay values on the network at 14 and 144 blocks. [options="header"] |======================= -|Riskfactor |Nodes | Delay per node |Risk Fee % |Route fee % -|0.001 |5 | 6 |0 |0.25 -|1 |5 | 6 |0 |0.25 -|1000 |5 | 6 |0.0029 |0.25 - -|0.001 |10 | 72 |0 |0.5 -|1 |10 | 72 |0.0001 |0.5 -|1000 |10 | 72 |0.1369 |0.5 - -|0.001 |20 | 1008 |0 |1.0 -|1 |20 | 1008 |0.0077 |1.0 -|1000 |20 | 1008 |7.6660 |1.0 +|Amount (msat) |Riskfactor | Delay |Risk Fee |Route fee +|10,000 |1 | 14 |0 |1001 +|10,000 |10 | 14 |0 |1001 +|10,000 |100 | 14 |2 |1001 +|10,000 |1000 | 14 |26 |1001 + +|1,000,000 |1 | 14 |2 |1001 +|1,000,000 |10 | 14 |26 |1001 +|1,000,000 |100 | 14 |266 |1001 +|1,000,000 |1000 | 14 |2661 |1001 + +|100,000,000 |1 | 14 |266 |1100 +|100,000,000 |10 | 14 |2661 |1100 +|100,000,000 |100 | 14 |26617 |1100 +|100,000,000 |1000 | 14 |266179 |1100 + +|10,000 |1 | 144 |0 |1001 +|10,000 |10 | 144 |2 |1001 +|10,000 |100 | 144 |27 |1001 +|10,000 |1000 | 144 |273 |1001 + +|1,000,000 |1 | 144 |27 |1001 +|1,000,000 |10 | 144 |273 |1001 +|1,000,000 |100 | 144 |2737 |1001 +|1,000,000 |1000 | 144 |27378 |1001 + +|100,000,000 |1 | 144 |2737 |1100 +|100,000,000 |10 | 144 |27378 |1100 +|100,000,000 |100 | 144 |273785 |1100 +|100,000,000 |1000 | 144 |2737850 |1100 |======================= RECOMMENDED RISKFACTOR VALUES ----------------------------- -0.001 is a value for tie-breaking in favor of shorter routes, but not really -costing in any risk. +The default 'fuzz' factor is 5%, so as you can see from the table above, +that tends to overwhelm the effect of 'riskfactor' less than about 5. 1 is a conservative value for a stable lightning network with very few failures. @@ -92,16 +113,20 @@ failures. 1000 is an aggressive value for trying to minimize timeouts at all costs. +The default for lightning-pay(7) is 10, which starts to become a major +factor for larger amounts, and is basically ignored for tiny ones. + RETURN VALUE ------------ -On success, a "route" array is returned. Each array element contains -{id} (the node being routed through), {msatoshi} (the millisatoshis -sent), and {delay} (the number of blocks to timeout at this node). +On success, a "route" array is returned. +Each array element contains 'id' (the node being routed through), 'msatoshi' +(the millisatoshis sent), 'amount_msat' (the same, with 'msat' appended), and 'delay' (the number of blocks to timeout at this +node). -The final {id} will be the destination {id} given in the input. The -difference between the first {msatoshi} minus the {msatoshi} given in -the input is the fee. The first {delay} is the very worst case +The final 'id' will be the destination 'id' given in the input. The +difference between the first 'msatoshi' minus the 'msatoshi' given in +the input is the fee. The first 'delay' is the very worst case timeout for the payment failure, in blocks. //FIXME:Enumerate errors diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index 3c77cabebceb..2954109cdf02 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -2,12 +2,12 @@ .\" Title: lightning-invoice .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 12/17/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-INVOICE" "7" "12/17/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-INVOICE" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -36,7 +36,7 @@ lightning-invoice \- Command for accepting payments\&. .sp The \fBinvoice\fR RPC command creates the expectation of a payment of a given amount of milli\-satoshi: it returns a unique token which another lightning daemon can use to pay this invoice\&. This token includes a \fIroute hint\fR description of an incoming channel with capacity to pay the invoice, if any exists\&. .sp -The \fImsatoshi\fR can be the string "any", which creates an invoice that can be paid with any amount\&. +The \fImsatoshi\fR parameter can be the string "any", which creates an invoice that can be paid with any amount\&. Otherwise it is in millisatoshi precision; it can be a whole number, or a whole number ending in \fImsat\fR or \fIsat\fR, or a number with three decimal places ending in \fIsat\fR, or a number with 1 to 11 decimal places ending in \fIgrs\fR\&. .sp The \fIlabel\fR must be a unique string or number (which is treated as a string, so "01" is different from "1"); it is never revealed to other nodes on the lightning network, but it can be used to query the status of this invoice\&. .sp @@ -48,7 +48,7 @@ The \fIfallbacks\fR array is one or more fallback addresses to include in the in .sp The \fIpreimage\fR is a 64\-digit hex string to be used as payment preimage for the created invoice\&. By default, if unspecified, lightningd will generate a secure pseudorandom preimage seeded from an appropriate entropy source on your system\&. \fBIMPORTANT\fR: if you specify the \fIpreimage\fR, you are responsible, to ensure appropriate care for generating using a secure pseudorandom generator seeded with sufficient entropy, and keeping the preimage secret\&. This parameter is an advanced feature intended for use with cutting\-edge cryptographic protocols and should not be used unless explicitly needed\&. .sp -The \fIexposeprivatechannels\fR includes unpublished channels in the selection of channels for the route hint; by default they are excluded\&. +If specified, \fIexposeprivatechannels\fR overrides the default route hint logic, which will use unpublished channels only if there are no published channels\&. If \fItrue\fR unpublished channels are always considered as a route hint candidate; if \fIfalse\fR, never\&. .SH "RETURN VALUE" .sp On success, a hash is returned as \fIpayment_hash\fR to be given to the payer, and the \fIexpiry_time\fR as a UNIX timestamp\&. It also returns a BOLT11 invoice as \fIbolt11\fR to be given to the payer\&. diff --git a/doc/lightning-invoice.7.txt b/doc/lightning-invoice.7.txt index 2c0e52856571..940e3397f8d8 100644 --- a/doc/lightning-invoice.7.txt +++ b/doc/lightning-invoice.7.txt @@ -18,8 +18,11 @@ lightning daemon can use to pay this invoice. This token includes a 'route hint' description of an incoming channel with capacity to pay the invoice, if any exists. -The 'msatoshi' can be the string "any", which creates an invoice -that can be paid with any amount. +The 'msatoshi' parameter can be the string "any", which creates an invoice +that can be paid with any amount. Otherwise it is in millisatoshi +precision; it can be a whole number, or a whole number ending in 'msat' or +'sat', or a number with three decimal places ending in 'sat', or a number +with 1 to 11 decimal places ending in 'grs'. The 'label' must be a unique string or number (which is treated as a string, so "01" is different from "1"); it is never revealed to other diff --git a/doc/lightning-listchannels.7 b/doc/lightning-listchannels.7 index 77faf33f3db3..05d631c63196 100644 --- a/doc/lightning-listchannels.7 +++ b/doc/lightning-listchannels.7 @@ -2,12 +2,12 @@ .\" Title: lightning-listchannels .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 01/09/2019 +.\" Date: 02/18/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-LISTCHANN" "7" "01/09/2019" "\ \&" "\ \&" +.TH "LIGHTNING\-LISTCHANN" "7" "02/18/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -115,6 +115,19 @@ Each object in the list contains the following data: .sp -1 .IP \(bu 2.3 .\} +\fIamount_sat\fR +: Same as above, but ending in +\fIsat\fR\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fImessage_flags\fR : Bitfield showing the presence of optional fields in the \fIchannel_update\fR diff --git a/doc/lightning-listchannels.7.txt b/doc/lightning-listchannels.7.txt index f2ada1d52efe..648edcb38c36 100644 --- a/doc/lightning-listchannels.7.txt +++ b/doc/lightning-listchannels.7.txt @@ -42,6 +42,7 @@ ever have this value set to true. Local channels are side-loaded by this node, rather than obtained through the gossip network, and so may have this value set to false. - 'satoshis' : Funds available in the channel. +- 'amount_sat' : Same as above, but ending in 'sat'. - 'message_flags' : Bitfield showing the presence of optional fields in the 'channel_update' message (BOLT #7). - 'channel_flags' : Bitfields indicating the direction of the channel and diff --git a/doc/lightning-listfunds.7 b/doc/lightning-listfunds.7 index ffb1a69e0fb7..d9a84475c6e1 100644 --- a/doc/lightning-listfunds.7 +++ b/doc/lightning-listfunds.7 @@ -2,12 +2,12 @@ .\" Title: lightning-listfunds .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 09/27/2018 +.\" Date: 02/18/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-LISTFUNDS" "7" "09/27/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-LISTFUNDS" "7" "02/18/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -93,6 +93,22 @@ Each entry in \fIoutputs\fR will include: .sp -1 .IP \(bu 2.3 .\} +\fIamount_sat\fR +(the same as +\fIvalue\fR +with +\fIsat\fR +appended) +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fIstatus\fR .RE .sp @@ -131,7 +147,33 @@ Each entry in \fIchannels\fR will include: .IP \(bu 2.3 .\} \fIchannel_sat\fR -\- available gro's on our node\(cqs end of the channel (values rounded to the nearest gro as internal storage is in milligro)\&. +\- available satoshis on our node\(cqs end of the channel (values rounded down to satoshis as internal storage is in millisatoshi)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIour_amount_sat\fR +\- same as above, but with +\fIsat\fR +appended\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIchannel_total_sat\fR +\- total channel value in satoshi .RE .sp .RS 4 @@ -142,8 +184,10 @@ Each entry in \fIchannels\fR will include: .sp -1 .IP \(bu 2.3 .\} -\fIchannel_total_gro\fR -\-channel value in gro (values rounded to the nearest gro as internal storage is in milligro)\&. +\fIamount_sat\fR +\- same as above, but with +\fIsat\fR +appended\&. .RE .sp .RS 4 diff --git a/doc/lightning-listfunds.7.txt b/doc/lightning-listfunds.7.txt index 510f941f8628..d9c840ea9df5 100644 --- a/doc/lightning-listfunds.7.txt +++ b/doc/lightning-listfunds.7.txt @@ -4,24 +4,24 @@ LIGHTNING-LISTFUNDS(7) NAME ---- -lightning-listfunds - Command showing all funds currently managed by +lightning-listfunds - Command showing all funds currently managed by the c-lightning node. SYNOPSIS -------- -*listfunds* +*listfunds* DESCRIPTION ----------- -The *listfunds* RPC command displays all funds available, either in -unspent outputs (UTXOs) in the internal wallet or funds locked in +The *listfunds* RPC command displays all funds available, either in +unspent outputs (UTXOs) in the internal wallet or funds locked in currently open channels. RETURN VALUE ------------ -On success two arrays will be returned: 'outputs' with funds -currently locked onchain in UTXOs and 'channels' with funds -readily spendable in channels. +On success two arrays will be returned: 'outputs' with funds +currently locked onchain in UTXOs and 'channels' with funds +readily spendable in channels. Each entry in 'outputs' will include: @@ -33,20 +33,25 @@ Each entry in 'outputs' will include: - 'value' +- 'amount_sat' (the same as 'value' with 'sat' appended) + - 'status' -Each entry in 'channels' will include: +Each entry in 'channels' will include: - 'peer_id' - the peer with which the channel is opened. - 'short_channel_id' - as per BOLT 7 (representing the block, transaction number and output index of the channel funding transaction). -- 'channel_sat' - available gro's on our node's end of the channel -(values rounded to the nearest gro as internal storage is in milligro). +- 'channel_sat' - available satoshis on our node's end of the channel +(values rounded down to satoshis as internal storage is in millisatoshi). + +- 'our_amount_sat' - same as above, but with 'sat' appended. + +- 'channel_total_sat' - total channel value in satoshi -- 'channel_total_gro' -channel value in gro -(values rounded to the nearest gro's as internal storage is in milligro). +- 'amount_sat' - same as above, but with 'sat' appended. - 'funding_txid' - funding transaction id. diff --git a/doc/lightning-listinvoices.7 b/doc/lightning-listinvoices.7 index 32cfc67b6600..f616c0aa3a76 100644 --- a/doc/lightning-listinvoices.7 +++ b/doc/lightning-listinvoices.7 @@ -2,12 +2,12 @@ .\" Title: lightning-listinvoices .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 08/27/2018 +.\" Date: 02/18/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-LISTINVOI" "7" "08/27/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-LISTINVOI" "7" "02/18/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -37,7 +37,7 @@ lightning-listinvoices \- Command for querying invoice status The \fBlistinvoices\fR RPC command gets the status of a specific invoice, if it exists, or the status of all invoices if given no argument\&. .SH "RETURN VALUE" .sp -On success, an array \fIinvoices\fR of objects is returned\&. Each object contains \fIlabel\fR, \fIpayment_hash\fR, \fIstatus\fR (one of \fIunpaid\fR, \fIpaid\fR or \fIexpired\fR), and \fIexpiry_time\fR (a UNIX timestamp)\&. If the \fImsatoshi\fR argument to lightning\-invoice(7) was not "any", there will be an \fImsatoshi\fR field\&. If the invoice \fIstatus\fR is \fIpaid\fR, there will be a \fIpay_index\fR field and an \fImsatoshi_received\fR field (which may be slightly greater than \fImsatoshi\fR as some overpaying is permitted to allow clients to obscure payment paths)\&. +On success, an array \fIinvoices\fR of objects is returned\&. Each object contains \fIlabel\fR, \fIpayment_hash\fR, \fIstatus\fR (one of \fIunpaid\fR, \fIpaid\fR or \fIexpired\fR), and \fIexpiry_time\fR (a UNIX timestamp)\&. If the \fImsatoshi\fR argument to lightning\-invoice(7) was not "any", there will be an \fImsatoshi\fR field as a number, and \fIamount_msat\fR as the same number ending in \fImsat\fR\&. If the invoice \fIstatus\fR is \fIpaid\fR, there will be a \fIpay_index\fR field and an \fImsatoshi_received\fR field (which may be slightly greater than \fImsatoshi\fR as some overpaying is permitted to allow clients to obscure payment paths); there will also be an \fIamount_received_msat\fR field with the same number as \fImsatoshi_received\fR but ending in \fImsat\fR\&. .SH "AUTHOR" .sp Rusty Russell is mainly responsible\&. diff --git a/doc/lightning-listinvoices.7.txt b/doc/lightning-listinvoices.7.txt index 51a0eadfbaed..51a7fe058941 100644 --- a/doc/lightning-listinvoices.7.txt +++ b/doc/lightning-listinvoices.7.txt @@ -20,10 +20,13 @@ RETURN VALUE On success, an array 'invoices' of objects is returned. Each object contains 'label', 'payment_hash', 'status' (one of 'unpaid', 'paid' or 'expired'), and 'expiry_time' (a UNIX timestamp). If the 'msatoshi' argument to -lightning-invoice(7) was not "any", there will be an 'msatoshi' field. If the -invoice 'status' is 'paid', there will be a 'pay_index' field and an +lightning-invoice(7) was not "any", there will be an 'msatoshi' field as +a number, and 'amount_msat' as the same number ending in 'msat'. If the +invoice 'status' is 'paid', there will be a 'pay_index' field and an 'msatoshi_received' field (which may be slightly greater than 'msatoshi' as -some overpaying is permitted to allow clients to obscure payment paths). +some overpaying is permitted to allow clients to obscure payment paths); +there will also be an 'amount_received_msat' field with the same number as +'msatoshi_received' but ending in 'msat'. //FIXME:Enumerate errors diff --git a/doc/lightning-listpayments.7 b/doc/lightning-listpayments.7 deleted file mode 100644 index cf63dca0d636..000000000000 --- a/doc/lightning-listpayments.7 +++ /dev/null @@ -1,49 +0,0 @@ -'\" t -.\" Title: lightning-listpayments -.\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 04/26/2018 -.\" Manual: \ \& -.\" Source: \ \& -.\" Language: English -.\" -.TH "LIGHTNING\-LISTPAYME" "7" "04/26/2018" "\ \&" "\ \&" -.\" ----------------------------------------------------------------- -.\" * Define some portability stuff -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" http://bugs.debian.org/507673 -.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' -.\" ----------------------------------------------------------------- -.\" * set default formatting -.\" ----------------------------------------------------------------- -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.\" ----------------------------------------------------------------- -.\" * MAIN CONTENT STARTS HERE * -.\" ----------------------------------------------------------------- -.SH "NAME" -lightning-listpayments \- Command for querying payment status -.SH "SYNOPSIS" -.sp -\fBlistpayments\fR -.SH "DESCRIPTION" -.sp -The \fBlistpayments\fR RPC command gets the status of all \fIpay\fR and \fIsendpay\fR commands\&. -.SH "RETURN VALUE" -.sp -On success, an array of objects is returned\&. Each object contains an \fIid\fR (unique internal value assigned at creation), \fIpayment_hash\fR, \fIdestination\fR, \fImsatoshi\fR and \fItimestamp\fR (UNIX timestamp indicating when it was initiated), and a \fIstatus\fR which is one of \fIpending\fR (in progress), \fIcomplete\fR (successfully paid) or \fIfailed\fR\&. -.SH "AUTHOR" -.sp -Christian Decker is mainly responsible\&. -.SH "SEE ALSO" -.sp -lightning\-pay(7), lightning\-sendpay(7), lightning\-listinvoice(7)\&. -.SH "RESOURCES" -.sp -Main web site: https://github\&.com/ElementsProject/lightning diff --git a/doc/lightning-listpayments.7.txt b/doc/lightning-listpayments.7.txt deleted file mode 100644 index 2bbe534701a9..000000000000 --- a/doc/lightning-listpayments.7.txt +++ /dev/null @@ -1,35 +0,0 @@ -LIGHTNING-LISTPAYMENTS(7) -======================== -:doctype: manpage - -NAME ----- -lightning-listpayments - Command for querying payment status - -SYNOPSIS --------- -*listpayments* - -DESCRIPTION ------------ - -The *listpayments* RPC command gets the status of all 'pay' and -'sendpay' commands. - -RETURN VALUE ------------- -On success, an array of objects is returned. Each object contains an 'id' (unique internal value assigned at creation), 'payment_hash', 'destination', 'msatoshi' and 'timestamp' (UNIX timestamp indicating when it was initiated), and a 'status' which is one of 'pending' (in progress), 'complete' (successfully paid) or 'failed'. - -//FIXME:Enumerate errors - -AUTHOR ------- -Christian Decker is mainly responsible. - -SEE ALSO --------- -lightning-pay(7), lightning-sendpay(7), lightning-listinvoice(7). - -RESOURCES ---------- -Main web site: https://github.com/ElementsProject/lightning diff --git a/doc/lightning-listpays.7 b/doc/lightning-listpays.7 new file mode 100644 index 000000000000..0904b68c824b --- /dev/null +++ b/doc/lightning-listpays.7 @@ -0,0 +1,108 @@ +'\" t +.\" Title: lightning-listpays +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 02/23/2019 +.\" Manual: \ \& +.\" Source: \ \& +.\" Language: English +.\" +.TH "LIGHTNING\-LISTPAYS" "7" "02/23/2019" "\ \&" "\ \&" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +lightning-listpays \- Command for querying payment status +.SH "SYNOPSIS" +.sp +\fBlistpays\fR [bolt11] +.SH "DESCRIPTION" +.sp +The \fBlistpay\fR RPC command gets the status of all \fIpay\fR commands, or a single one if \fIbolt11\fR is specified\&. +.SH "RETURN VALUE" +.sp +On success, an array of objects is returned\&. Each object contains: +.PP +\fIbolt11\fR +.RS 4 +the +\fIbolt11\fR +argument given to +\fIpay\fR +(see below for exceptions)\&. +.RE +.PP +\fIstatus\fR +.RS 4 +one of +\fIcomplete\fR, +\fIfailed\fR +or +\fIpending\fR\&. +.RE +.PP +\fIpayment_preimage\fR +.RS 4 +(if +\fIstatus\fR +is +\fIcomplete\fR) proves payment was received\&. +.RE +.PP +\fIlabel\fR +.RS 4 +optional +\fIlabel\fR, if provided to +\fIpay\fR\&. +.RE +.PP +\fIamount_sent_msat\fR +.RS 4 +total amount sent, in "NNNmsat" format\&. +.RE +.sp +For old payments (pre\-0\&.7) we didn\(cqt save the \fIbolt11\fR string, so in its place are three other fields: +.PP +\fIpayment_hash\fR +.RS 4 +the hash of the +\fIpayment_preimage\fR +which will prove payment\&. +.RE +.PP +\fIdestination\fR +.RS 4 +the final destination of the payment\&. +.RE +.PP +\fIamount_msat\fR +.RS 4 +the amount the destination received, in "NNNmsat" format\&. +.RE +.sp +These three can all be extracted from \fIbolt11\fR, hence are obsolete\&. +.SH "AUTHOR" +.sp +Rusty Russell is mainly responsible\&. +.SH "SEE ALSO" +.sp +lightning\-pay(7), lightning\-paystatus(7), lightning\-listpayments(7)\&. +.SH "RESOURCES" +.sp +Main web site: https://github\&.com/ElementsProject/lightning diff --git a/doc/lightning-listpays.7.txt b/doc/lightning-listpays.7.txt new file mode 100644 index 000000000000..1da3fa929c14 --- /dev/null +++ b/doc/lightning-listpays.7.txt @@ -0,0 +1,49 @@ +LIGHTNING-LISTPAYS(7) +===================== +:doctype: manpage + +NAME +---- +lightning-listpays - Command for querying payment status + +SYNOPSIS +-------- +*listpays* [bolt11] + +DESCRIPTION +----------- + +The *listpay* RPC command gets the status of all 'pay' commands, or a single +one if 'bolt11' is specified. + +RETURN VALUE +------------ +On success, an array of objects is returned. Each object contains: + +'bolt11':: the 'bolt11' argument given to 'pay' (see below for exceptions). +'status':: one of 'complete', 'failed' or 'pending'. +'payment_preimage':: (if 'status' is 'complete') proves payment was received. +'label':: optional 'label', if provided to 'pay'. +'amount_sent_msat':: total amount sent, in "NNNmsat" format. + +For old payments (pre-0.7) we didn't save the 'bolt11' string, so in +its place are three other fields: + +'payment_hash':: the hash of the 'payment_preimage' which will prove payment. +'destination':: the final destination of the payment. +'amount_msat':: the amount the destination received, in "NNNmsat" format. + +These three can all be extracted from 'bolt11', hence are obsolete. +//FIXME:Enumerate errors + +AUTHOR +------ +Rusty Russell is mainly responsible. + +SEE ALSO +-------- +lightning-pay(7), lightning-paystatus(7), lightning-listsendpays(7). + +RESOURCES +--------- +Main web site: https://github.com/ElementsProject/lightning diff --git a/doc/lightning-listsendpays.7 b/doc/lightning-listsendpays.7 new file mode 100644 index 000000000000..38f6e1cb2ff2 --- /dev/null +++ b/doc/lightning-listsendpays.7 @@ -0,0 +1,111 @@ +'\" t +.\" Title: lightning-listsendpays +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Date: 02/23/2019 +.\" Manual: \ \& +.\" Source: \ \& +.\" Language: English +.\" +.TH "LIGHTNING\-LISTSENDP" "7" "02/23/2019" "\ \&" "\ \&" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +lightning-listsendpays \- Low\-level command for querying sendpay status +.SH "SYNOPSIS" +.sp +\fBlistsendpays\fR [\fIbolt11\fR] [\fIpayment_hash\fR] +.SH "DESCRIPTION" +.sp +The \fBlistsendpays\fR RPC command gets the status of all \fIsendpay\fR commands (which is also used by the \fIpay\fR command), or with \fIbolt11\fR or \fIpayment_hash\fR limits results to that specific payment\&. You cannot specify both\&. +.sp +Note that in future there may be more than one concurrent \fIsendpay\fR command per \fIpay\fR, so this command should be used with caution\&. +.SH "RETURN VALUE" +.sp +On success, an array of objects is returned\&. Each object contains: +.PP +\fIid\fR +.RS 4 +unique internal value assigned at creation +.RE +.PP +\fIpayment_hash\fR +.RS 4 +the hash of the +\fIpayment_preimage\fR +which will prove payment\&. +.RE +.PP +\fIdestination\fR +.RS 4 +the final destination of the payment\&. +.RE +.PP +\fIamount_msat\fR +.RS 4 +the amount the destination received, in "NNNmsat" format\&. +.RE +.PP +\fIcreated_at\fR +.RS 4 +the UNIX timestamp showing when this payment was initiated\&. +.RE +.PP +\fIstatus\fR +.RS 4 +one of +\fIcomplete\fR, +\fIfailed\fR +or +\fIpending\fR\&. +.RE +.PP +\fIpayment_preimage\fR +.RS 4 +(if +\fIstatus\fR +is +\fIcomplete\fR) proves payment was received\&. +.RE +.PP +\fIlabel\fR +.RS 4 +optional +\fIlabel\fR, if provided to +\fIsendpay\fR\&. +.RE +.PP +\fIbolt11\fR +.RS 4 +the +\fIbolt11\fR +argument given to +\fIpay\fR +(may be missing for pre\-0\&.7 payments)\&. +.RE +.SH "AUTHOR" +.sp +Christian Decker is mainly responsible\&. +.SH "SEE ALSO" +.sp +lightning\-listpays(7), lightning\-sendpay(7), lightning\-listinvoice(7)\&. +.SH "RESOURCES" +.sp +Main web site: https://github\&.com/ElementsProject/lightning diff --git a/doc/lightning-listsendpays.7.txt b/doc/lightning-listsendpays.7.txt new file mode 100644 index 000000000000..d547a8e4ed04 --- /dev/null +++ b/doc/lightning-listsendpays.7.txt @@ -0,0 +1,50 @@ +LIGHTNING-LISTSENDPAYS(7) +======================== +:doctype: manpage + +NAME +---- +lightning-listsendpays - Low-level command for querying sendpay status + +SYNOPSIS +-------- +*listsendpays* ['bolt11'] ['payment_hash'] + +DESCRIPTION +----------- + +The *listsendpays* RPC command gets the status of all 'sendpay' +commands (which is also used by the 'pay' command), or with 'bolt11' +or 'payment_hash' limits results to that specific payment. You cannot +specify both. + +Note that in future there may be more than one concurrent 'sendpay' +command per 'pay', so this command should be used with caution. + +RETURN VALUE +------------ +On success, an array of objects is returned. Each object contains: + +'id':: unique internal value assigned at creation +'payment_hash':: the hash of the 'payment_preimage' which will prove payment. +'destination':: the final destination of the payment. +'amount_msat':: the amount the destination received, in "NNNmsat" format. +'created_at':: the UNIX timestamp showing when this payment was initiated. +'status':: one of 'complete', 'failed' or 'pending'. +'payment_preimage':: (if 'status' is 'complete') proves payment was received. +'label':: optional 'label', if provided to 'sendpay'. +'bolt11':: the 'bolt11' argument given to 'pay' (may be missing for pre-0.7 payments). + +//FIXME:Enumerate errors + +AUTHOR +------ +Christian Decker is mainly responsible. + +SEE ALSO +-------- +lightning-listpays(7), lightning-sendpay(7), lightning-listinvoice(7). + +RESOURCES +--------- +Main web site: https://github.com/ElementsProject/lightning diff --git a/doc/lightning-pay.7 b/doc/lightning-pay.7 index 1983dbab3a88..cdf1d796c7de 100644 --- a/doc/lightning-pay.7 +++ b/doc/lightning-pay.7 @@ -2,12 +2,12 @@ .\" Title: lightning-pay .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 11/26/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-PAY" "7" "11/26/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-PAY" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,14 +31,16 @@ lightning-pay \- Command for sending a payment to a BOLT11 invoice .SH "SYNOPSIS" .sp -\fBpay\fR \fIbolt11\fR [\fImsatoshi\fR] [\fIdescription\fR] [\fIriskfactor\fR] [\fImaxfeepercent\fR] [\fIretry_for\fR] [\fImaxdelay\fR] [\fIexemptfee\fR] +\fBpay\fR \fIbolt11\fR [\fImsatoshi\fR] [\fIlabel\fR] [\fIriskfactor\fR] [\fImaxfeepercent\fR] [\fIretry_for\fR] [\fImaxdelay\fR] [\fIexemptfee\fR] .SH "DESCRIPTION" .sp -The \fBpay\fR RPC command attempts to find a route to the given destination, and send the funds it asks for\&. If the \fIbolt11\fR does not contain an amount, \fImsatoshi\fR is required, otherwise if it is specified it must be \fInull\fR\&. If \fIbolt11\fR contains a description hash (\fIh\fR field) \fIdescription\fR is required, otherwise it is unused\&. The \fIriskfactor\fR is described in detail in lightning\-getroute(7), and defaults to 1\&.0\&. The \fImaxfeepercent\fR limits the money paid in fees, and defaults to 0\&.5\&. The maxfeepercent\*(Aq is a percentage of the amount that is to be paid\&. The `exemptfee option can be used for tiny payments which would be dominated by the fee leveraged by forwarding nodes\&. Setting exemptfee allows the maxfeepercent check to be skipped on fees that are smaller than exemptfee (default: 5000 millisatoshi)\&. +The \fBpay\fR RPC command attempts to find a route to the given destination, and send the funds it asks for\&. If the \fIbolt11\fR does not contain an amount, \fImsatoshi\fR is required, otherwise if it is specified it must be \fInull\fR\&. \fImsatoshi\fR is in millisatoshi precision; it can be a whole number, or a whole number with suffix \fImsat\fR or \fIsat\fR, or a three decimal point number with suffix \fIsat\fR, or an 1 to 11 decimal point number suffixed by \fIgrs\fR\&. +.sp +The \fIlabel\fR field is used to attach a label to payments, and is returned in lightning\-listpays(7) and lightning\-listsendpays(7)\&. The \fIriskfactor\fR is described in detail in lightning\-getroute(7), and defaults to 10\&. The \fImaxfeepercent\fR limits the money paid in fees, and defaults to 0\&.5\&. The maxfeepercent\*(Aq is a percentage of the amount that is to be paid\&. The `exemptfee option can be used for tiny payments which would be dominated by the fee leveraged by forwarding nodes\&. Setting exemptfee allows the maxfeepercent check to be skipped on fees that are smaller than exemptfee (default: 5000 millisatoshi)\&. .sp The response will occur when the payment fails or succeeds\&. Once a payment has succeeded, calls to \fBpay\fR with the same \fIbolt11\fR will succeed immediately\&. .sp -The command will keep finding routes and retrying the payment until it succeeds, or the given \fIretry_for\fR seconds passes\&. Note that the command may stop retrying while a pending payment is ongoing, which you need to monitor with \fBlistpayments\fR or \fBwaitsendpay\fR\&. \fIretry_for\fR defaults to 60 seconds and can only be an integer\&. +The command will keep finding routes and retrying the payment until it succeeds, or the given \fIretry_for\fR seconds passes\&. Note that the command may stop retrying while a pending payment is ongoing, which you need to monitor with \fBlistpays\fR\&. \fIretry_for\fR defaults to 60 seconds and can only be an integer\&. .sp When using \fIlightning\-cli\fR, you may skip optional parameters by using \fInull\fR\&. Alternatively, use \fB\-k\fR option to provide parameters by name\&. .SH "RANDOMIZATION" @@ -100,9 +102,7 @@ The following error codes may occur: .IP \(bu 2.3 .\} 200\&. Payment timed out while a payment is in progress\&. Monitor the status of that payment with -\fIlistpayments\fR -command, or wait for that payment to complete with -\fIpay\fR +\fIlistpays\fR command\&. .RE .sp @@ -265,7 +265,7 @@ The \fIdata\fR field of errors will include statistics \fIgetroute_tries\fR and Rusty Russell is mainly responsible\&. .SH "SEE ALSO" .sp -lightning\-listpayments(7), lightning\-decodepay(7), lightning\-listinvoice(7), lightning\-delinvoice(7), lightning\-getroute(7), lightning\-invoice(7)\&. +lightning\-listpays(7), lightning\-decodepay(7), lightning\-listinvoice(7), lightning\-delinvoice(7), lightning\-getroute(7), lightning\-invoice(7)\&. .SH "RESOURCES" .sp Main web site: https://github\&.com/ElementsProject/lightning diff --git a/doc/lightning-pay.7.txt b/doc/lightning-pay.7.txt index 04e830f6517d..a19bda810049 100644 --- a/doc/lightning-pay.7.txt +++ b/doc/lightning-pay.7.txt @@ -8,17 +8,23 @@ lightning-pay - Command for sending a payment to a BOLT11 invoice SYNOPSIS -------- -*pay* 'bolt11' ['msatoshi'] ['description'] ['riskfactor'] ['maxfeepercent'] ['retry_for'] ['maxdelay'] ['exemptfee'] +*pay* 'bolt11' ['msatoshi'] ['label'] ['riskfactor'] ['maxfeepercent'] ['retry_for'] ['maxdelay'] ['exemptfee'] DESCRIPTION ----------- The *pay* RPC command attempts to find a route to the given destination, and send the funds it asks for. If the 'bolt11' does not contain an amount, -'msatoshi' is required, otherwise if it is specified it must be 'null'. If -'bolt11' contains a description hash ('h' field) 'description' is -required, otherwise it is unused. The 'riskfactor' is described in detail -in lightning-getroute(7), and defaults to 1.0. +'msatoshi' is required, otherwise if it is specified it must be 'null'. +'msatoshi' is in millisatoshi precision; it can be a whole number, or +a whole number with suffix 'msat' or 'sat', or a three decimal +point number with suffix 'sat', or an 1 to 11 decimal point number +suffixed by 'grs'. + +The 'label' field is used to attach a label to payments, and is returned +in lightning-listpays(7) and lightning-listsendpays(7). +The 'riskfactor' is described in detail +in lightning-getroute(7), and defaults to 10. The 'maxfeepercent' limits the money paid in fees, and defaults to 0.5. The `maxfeepercent' is a percentage of the amount that is to be paid. @@ -34,7 +40,7 @@ succeed immediately. The command will keep finding routes and retrying the payment until it succeeds, or the given 'retry_for' seconds passes. Note that the command may stop retrying while a pending payment is -ongoing, which you need to monitor with *listpayments* or *waitsendpay*. +ongoing, which you need to monitor with *listpays*. 'retry_for' defaults to 60 seconds and can only be an integer. When using 'lightning-cli', you may skip optional parameters by using @@ -85,8 +91,7 @@ The following error codes may occur: * -1. Catchall nonspecific error. * 200. Payment timed out while a payment is in progress. Monitor - the status of that payment with 'listpayments' command, or - wait for that payment to complete with 'pay' command. + the status of that payment with 'listpays' command. * 201. Already paid with this 'hash' using different amount or destination. * 203. Permanent failure at destination. The 'data' field of @@ -139,7 +144,7 @@ Rusty Russell is mainly responsible. SEE ALSO -------- -lightning-listpayments(7), lightning-decodepay(7), +lightning-listpays(7), lightning-decodepay(7), lightning-listinvoice(7), lightning-delinvoice(7), lightning-getroute(7), lightning-invoice(7). diff --git a/doc/lightning-sendpay.7 b/doc/lightning-sendpay.7 index c58f84659d5b..abb71fdfe66d 100644 --- a/doc/lightning-sendpay.7 +++ b/doc/lightning-sendpay.7 @@ -2,12 +2,12 @@ .\" Title: lightning-sendpay .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 10/23/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-SENDPAY" "7" "10/23/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-SENDPAY" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -28,26 +28,26 @@ .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" -lightning-sendpay \- Command for sending a payment via a route\&. +lightning-sendpay \- Low\-level command for sending a payment via a route\&. .SH "SYNOPSIS" .sp -\fBsendpay\fR \fIroute\fR \fIpayment_hash\fR [\fIdescription\fR] [\fImsatoshi\fR] +\fBsendpay\fR \fIroute\fR \fIpayment_hash\fR [\fIlabel\fR] [\fImsatoshi\fR] [\fIbolt11\fR] .SH "DESCRIPTION" .sp -The \fBsendpay\fR RPC command attempts to send funds associated with the given payment_\(cqhash\*(Aq, along a route to the final destination in the route\&. +The \fBsendpay\fR RPC command attempts to send funds associated with the given \fIpayment_hash\fR, along a route to the final destination in the route\&. .sp Generally, a client would call lightning\-getroute(7) to resolve a route, then use \fBsendpay\fR to send it\&. If it fails, it would call lightning\-getroute(7) again to retry\&. .sp The response will occur when the payment is on its way to the destination\&. The \fBsendpay\fR RPC command does not wait for definite success or definite failure of the payment\&. Instead, use the \fBwaitsendpay\fR RPC command to poll or wait for definite success or definite failure\&. .sp -The \fIdescription\fR parameter, if provided, will be returned in \fIwaitsendpay\fR and \fIlistpayments\fR results\&. +The \fIlabel\fR and \fIbolt11\fR parameters, if provided, will be returned in \fIwaitsendpay\fR and \fIlistsendpays\fR results\&. .sp -The \fImsatoshi\fR amount, if provided, is the amount that will be recorded as the target payment value\&. If not specified, it will be the final amount to the destination\&. If specified, then the final amount at the destination must be from the specified \fImsatoshi\fR to twice the specified \fImsatoshi\fR, inclusive\&. This is intended to obscure payments by overpaying slightly at the destination; the actual target payment is what should be specified as the \fImsatoshi\fR argument\&. +The \fImsatoshi\fR amount, if provided, is the amount that will be recorded as the target payment value\&. If not specified, it will be the final amount to the destination\&. If specified, then the final amount at the destination must be from the specified \fImsatoshi\fR to twice the specified \fImsatoshi\fR, inclusive\&. This is intended to obscure payments by overpaying slightly at the destination; the actual target payment is what should be specified as the \fImsatoshi\fR argument\&. \fImsatoshi\fR is in millisatoshi precision; it can be a whole number, or a whole number ending in \fImsat\fR or \fIsat\fR, or a number with three decimal places ending in \fIsat\fR, or a number with 1 to 11 decimal places ending in \fIgrs\fR\&. .sp Once a payment has succeeded, calls to \fBsendpay\fR with the same \fIpayment_hash\fR but a different \fImsatoshi\fR or destination will fail; this prevents accidental multiple payments\&. Calls to \fBsendpay\fR with the same \fIpayment_hash\fR, \fImsatoshi\fR, and destination as a previous successful payment (even if a different route) will return immediately with success\&. .SH "RETURN VALUE" .sp -On success, an object similar to the output of \fBlistpayments\fR will be returned\&. This object will have a \fIstatus\fR field that is typically the string \fI"pending"\fR, but may be \fI"complete"\fR if the payment was already performed successfully\&. +On success, an object similar to the output of \fBlistsendpays\fR will be returned\&. This object will have a \fIstatus\fR field that is typically the string \fI"pending"\fR, but may be \fI"complete"\fR if the payment was already performed successfully\&. .sp On error, if the error occurred from a node other than the final destination, the route table will be updated so that lightning\-getroute(7) should return an alternate route (if any)\&. An error from the final destination implies the payment should not be retried\&. .sp diff --git a/doc/lightning-sendpay.7.txt b/doc/lightning-sendpay.7.txt index 91c3cdd3635d..04b4cd75301b 100644 --- a/doc/lightning-sendpay.7.txt +++ b/doc/lightning-sendpay.7.txt @@ -4,17 +4,17 @@ LIGHTNING-SENDPAY(7) NAME ---- -lightning-sendpay - Command for sending a payment via a route. +lightning-sendpay - Low-level command for sending a payment via a route. SYNOPSIS -------- -*sendpay* 'route' 'payment_hash' ['description'] ['msatoshi'] +*sendpay* 'route' 'payment_hash' ['label'] ['msatoshi'] ['bolt11'] DESCRIPTION ----------- The *sendpay* RPC command attempts to send funds associated with the -given payment_'hash', along a route to the final destination in the route. +given 'payment_hash', along a route to the final destination in the route. Generally, a client would call lightning-getroute(7) to resolve a route, then use *sendpay* to send it. If it fails, it would call lightning-getroute(7) @@ -27,8 +27,8 @@ definite failure of the payment. Instead, use the *waitsendpay* RPC command to poll or wait for definite success or definite failure. -The 'description' parameter, if provided, will be returned in -'waitsendpay' and 'listpayments' results. +The 'label' and 'bolt11' parameters, if provided, will be returned in +'waitsendpay' and 'listsendpays' results. The 'msatoshi' amount, if provided, is the amount that will be recorded as the target payment value. @@ -39,7 +39,10 @@ inclusive. This is intended to obscure payments by overpaying slightly at the destination; the actual target payment is what should be specified as the -'msatoshi' argument. +'msatoshi' argument. 'msatoshi' is in millisatoshi precision; it can +be a whole number, or a whole number ending in 'msat' or 'sat', or a +number with three decimal places ending in 'sat', or a number with 1 +to 11 decimal places ending in 'grs'. Once a payment has succeeded, calls to *sendpay* with the same 'payment_hash' but a different 'msatoshi' or destination will fail; this prevents @@ -51,7 +54,7 @@ immediately with success. RETURN VALUE ------------ -On success, an object similar to the output of *listpayments* will +On success, an object similar to the output of *listsendpays* will be returned. This object will have a 'status' field that is typically the string '"pending"', but may be '"complete"' if the payment was diff --git a/doc/lightning-waitsendpay.7 b/doc/lightning-waitsendpay.7 index ce08e83b460a..951cb5a4e0b3 100644 --- a/doc/lightning-waitsendpay.7 +++ b/doc/lightning-waitsendpay.7 @@ -2,12 +2,12 @@ .\" Title: lightning-waitsendpay .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 04/26/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-WAITSENDP" "7" "04/26/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-WAITSENDP" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -43,7 +43,7 @@ Indicating a \fItimeout\fR of 0 effectively makes this call a pollable query of If the payment completed with success, this command returns with success\&. Otherwise, if the payment completed with failure, this command returns an error\&. .SH "RETURN VALUE" .sp -On success, an object similar to the output of \fBlistpayments\fR will be returned\&. This object will have a \fIstatus\fR field that is the string \fI"complete"\fR\&. +On success, an object similar to the output of \fBlistsendpays\fR will be returned\&. This object will have a \fIstatus\fR field that is the string \fI"complete"\fR\&. .sp On error, if the error occurred from a node other than the final destination, the route table will be updated so that getroute(7) should return an alternate route (if any)\&. An error from the final destination implies the payment should not be retried\&. .sp @@ -168,9 +168,19 @@ A routing failure object has the fields below: .sp -1 .IP \(bu 2.3 .\} -\fIerring_channel\fR\&. The short channel ID of the channel that has the error, or -\fI0:0:0\fR -if the destination node raised the error\&. +\fIerring_channel\fR\&. The short channel ID of the channel that has the error (or the final channel if the destination raised the error)\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIerring_direction\fR\&. The direction of traversing the +\fIerring_channel\fR\&. .RE .sp .RS 4 @@ -192,11 +202,8 @@ if the destination node raised the error\&. .sp -1 .IP \(bu 2.3 .\} -\fIchannel_update\fR\&. The hex string of the -\fIchannel_update\fR -message received from the remote node\&. Only present if error is from the remote node and the -\fIfailcode\fR -has the UPDATE bit set, as per BOLT #4\&. +\fIfailcodename\fR\&. The human\-readable name corresponding to +\fIfailcode\fR, if known\&. .RE .SH "AUTHOR" .sp diff --git a/doc/lightning-waitsendpay.7.txt b/doc/lightning-waitsendpay.7.txt index 9888fbca52b1..0fa191c40e30 100644 --- a/doc/lightning-waitsendpay.7.txt +++ b/doc/lightning-waitsendpay.7.txt @@ -36,7 +36,7 @@ returns an error. RETURN VALUE ------------ -On success, an object similar to the output of *listpayments* will +On success, an object similar to the output of *listsendpays* will be returned. This object will have a 'status' field that is the string '"complete"'. @@ -71,13 +71,10 @@ A routing failure object has the fields below: * 'erring_node'. The hex string of the pubkey id of the node that reported the error. * 'erring_channel'. The short channel ID of the channel that - has the error, or '0:0:0' if the destination node raised - the error. + has the error (or the final channel if the destination raised the error). +* 'erring_direction'. The direction of traversing the 'erring_channel'. * 'failcode'. The failure code, as per BOLT #4. -* 'channel_update'. The hex string of the 'channel_update' - message received from the remote node. Only present if - error is from the remote node and the 'failcode' has the - UPDATE bit set, as per BOLT #4. +* 'failcodename'. The human-readable name corresponding to 'failcode', if known. AUTHOR ------ diff --git a/doc/lightning-withdraw.7 b/doc/lightning-withdraw.7 index ab4837103c45..c7e7e7920429 100644 --- a/doc/lightning-withdraw.7 +++ b/doc/lightning-withdraw.7 @@ -2,12 +2,12 @@ .\" Title: lightning-withdraw .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 08/29/2018 +.\" Date: 02/23/2019 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-WITHDRAW" "7" "08/29/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-WITHDRAW" "7" "02/23/2019" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,7 +38,7 @@ The \fBwithdraw\fR RPC command sends funds from c\-lightning\(cqs internal walle .sp The address can be of any Groestlcoin accepted type, including bech32\&. .sp -\fIsatoshi\fR is the amount to be withdrawn from the internal wallet (expressed, as name suggests, in gro)\&. The string \fIall\fR can be used to specify withdrawal of all available funds\&. +\fIsatoshi\fR is the amount to be withdrawn from the internal wallet (expressed, as name suggests, in satoshi)\&. The string \fIall\fR can be used to specify withdrawal of all available funds\&. Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in \fIsat\fR, a whole number ending in \fI000msat\fR, or a number with 1 to 8 decimal places ending in \fIgrs\fR\&. .sp \fIfeerate\fR is an optional feerate to use\&. It can be one of the strings \fIurgent\fR, \fInormal\fR or \fIslow\fR to use lightningd\(cqs internal estimates: \fInormal\fR is the default\&. .sp diff --git a/doc/lightning-withdraw.7.txt b/doc/lightning-withdraw.7.txt index b01e3d73b48f..a9b12fa71665 100644 --- a/doc/lightning-withdraw.7.txt +++ b/doc/lightning-withdraw.7.txt @@ -19,10 +19,10 @@ internal wallet to the address specified in 'destination'. The address can be of any Groestlcoin accepted type, including bech32. -'gro' is the amount to be withdrawn from the internal -wallet (expressed, as name suggests, in gro). +'satoshi' is the amount to be withdrawn from the internal +wallet (expressed, as name suggests, in satoshi). The string 'all' can be used to specify withdrawal of all -available funds. +available funds. Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in 'sat', a whole number ending in '000msat', or a number with 1 to 8 decimal places ending in 'grs'. 'feerate' is an optional feerate to use. It can be one of the strings 'urgent', 'normal' or 'slow' to use lightningd's internal estimates: diff --git a/doc/requirements.txt b/doc/requirements.txt new file mode 100644 index 000000000000..e5484f764ac7 --- /dev/null +++ b/doc/requirements.txt @@ -0,0 +1,7 @@ +sphinx-rtd-theme==0.4.2 +sphinxcontrib-websupport==1.1.0 +m2r==0.2.1 +Sphinx==1.8.4 +commonmark==0.8.1 +commonmark==0.8.1 +recommonmark==0.5.0 diff --git a/external/Makefile b/external/Makefile index 71b9d81ef73c..816b95fb1065 100644 --- a/external/Makefile +++ b/external/Makefile @@ -15,17 +15,29 @@ JSMN_HEADERS := external/jsmn/jsmn.h LIBBASE58_HEADERS := external/libbase58/libbase58.h EXTERNAL_HEADERS := $(LIBSODIUM_HEADERS) $(LIBWALLY_HEADERS) $(LIBSECP_HEADERS) $(JSMN_HEADERS) $(LIBBASE58_HEADERS) -EXTERNAL_LIBS := external/libwallycore.a external/libsecp256k1.a external/libsodium.a external/libjsmn.a external/libbase58.a external/libbacktrace.a +EXTERNAL_LIBS := external/libwallycore.a external/libsecp256k1.a external/libjsmn.a external/libbacktrace.a EXTERNAL_INCLUDE_FLAGS := \ - -I external/libsodium/src/libsodium/include \ -I external/libwally-core/include/ \ -I external/libwally-core/src/secp256k1/include/ \ -I external/jsmn/ \ - -I external/libbase58/ \ -I external/libbacktrace/ \ -I external/libbacktrace-build +ifneq ($(HAVE_GOOD_LIBSODIUM),1) +EXTERNAL_INCLUDE_FLAGS += -I external/libsodium/src/libsodium/include +EXTERNAL_LIBS += external/libsodium.a +else +LDLIBS += -lsodium +endif + +ifneq ($(HAVE_SYSTEM_LIBBASE58),1) +EXTERNAL_INCLUDE_FLAGS += -I external/libbase58/ +EXTERNAL_LIBS += external/libbase58.a +else +LDLIBS += -lbase58 +endif + EXTERNAL_LDLIBS := -Lexternal $(patsubst lib%.a,-l%,$(notdir $(EXTERNAL_LIBS))) submodcheck: FORCE diff --git a/gossipd/Makefile b/gossipd/Makefile index b8c96bcab4de..abe604f2974d 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -36,6 +36,7 @@ LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_GOSSIP_HEADERS) # Common source we use. GOSSIPD_COMMON_OBJS := \ bitcoin/chainparams.o \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/gossipd/gossip_peerd_wire.csv b/gossipd/gossip_peerd_wire.csv index d4460be7b2f3..ec3844e37989 100644 --- a/gossipd/gossip_peerd_wire.csv +++ b/gossipd/gossip_peerd_wire.csv @@ -19,14 +19,14 @@ gossipd_send_gossip,,gossip,len*u8 gossipd_local_add_channel,3503 gossipd_local_add_channel,,short_channel_id,struct short_channel_id gossipd_local_add_channel,,remote_node_id,struct pubkey -gossipd_local_add_channel,,satoshis,u64 +gossipd_local_add_channel,,satoshis,struct amount_sat # Send this channel_update. gossipd_local_channel_update,3504 gossipd_local_channel_update,,short_channel_id,struct short_channel_id gossipd_local_channel_update,,disable,bool gossipd_local_channel_update,,cltv_expiry_delta,u16 -gossipd_local_channel_update,,htlc_minimum_msat,u64 +gossipd_local_channel_update,,htlc_minimum_msat,struct amount_msat gossipd_local_channel_update,,fee_base_msat,u32 gossipd_local_channel_update,,fee_proportional_millionths,u32 -gossipd_local_channel_update,,htlc_maximum_msat,u64 +gossipd_local_channel_update,,htlc_maximum_msat,struct amount_msat diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index eba7fbcc3dfa..b791a0828e7c 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -104,10 +104,10 @@ static u8 *gossip_store_wrap_channel_announcement(const tal_t *ctx, "Error parsing channel_announcement"); struct chan *chan = get_channel(rstate, &scid); - assert(chan && chan->satoshis > 0); + assert(chan && amount_sat_greater(chan->sat, AMOUNT_SAT(0))); u8 *msg = towire_gossip_store_channel_announcement(ctx, gossip_msg, - chan->satoshis); + chan->sat); return msg; } @@ -247,7 +247,7 @@ void gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) beint32_t belen, becsum; u32 msglen, checksum; u8 *msg, *gossip_msg; - u64 satoshis; + struct amount_sat satoshis; struct short_channel_id scid; /* We set/check version byte on creation */ off_t known_good = 1; diff --git a/gossipd/gossip_store.csv b/gossipd/gossip_store.csv index f10fb09a6a97..6b8a260f4d3f 100644 --- a/gossipd/gossip_store.csv +++ b/gossipd/gossip_store.csv @@ -2,7 +2,7 @@ gossip_store_channel_announcement,4096 gossip_store_channel_announcement,,len,u16 gossip_store_channel_announcement,,announcement,len*u8 -gossip_store_channel_announcement,,satoshis,u64 +gossip_store_channel_announcement,,satoshis,struct amount_sat gossip_store_channel_update,4097 gossip_store_channel_update,,len,u16 @@ -17,4 +17,4 @@ gossip_store_channel_delete,,short_channel_id,struct short_channel_id gossip_store_local_add_channel,4100 gossip_store_local_add_channel,,len,u16 -gossip_store_local_add_channel,,local_add,len*u8 \ No newline at end of file +gossip_store_local_add_channel,,local_add,len*u8 diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 7e4709f2654c..a9b86522562f 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -28,8 +28,9 @@ gossip_getnodes_reply,,nodes,num_nodes*struct gossip_getnodes_entry gossip_getroute_request,3006 gossip_getroute_request,,source,struct pubkey gossip_getroute_request,,destination,struct pubkey -gossip_getroute_request,,msatoshi,u64 -gossip_getroute_request,,riskfactor,u16 +gossip_getroute_request,,msatoshi,struct amount_msat +# We don't pass doubles, so pass riskfactor * 1000000. +gossip_getroute_request,,riskfactor_by_million,u64 gossip_getroute_request,,final_cltv,u32 gossip_getroute_request,,fuzz,double gossip_getroute_request,,num_excluded,u16 @@ -114,22 +115,17 @@ gossip_get_txout,,short_channel_id,struct short_channel_id # master->gossipd here is the output, or empty if none. gossip_get_txout_reply,3118 gossip_get_txout_reply,,short_channel_id,struct short_channel_id -gossip_get_txout_reply,,satoshis,u64 +gossip_get_txout_reply,,satoshis,struct amount_sat gossip_get_txout_reply,,len,u16 gossip_get_txout_reply,,outscript,len*u8 -# master->gossipd a routing failure occurred -gossip_routing_failure,3021 -gossip_routing_failure,,erring_node,struct pubkey -gossip_routing_failure,,erring_channel,struct short_channel_id -gossip_routing_failure,,failcode,u16 -gossip_routing_failure,,len,u16 -gossip_routing_failure,,channel_update,len*u8 - -# master->gossipd temporarily mark a channel unroutable -# (used in case of unparseable onion reply) -gossip_mark_channel_unroutable,3022 -gossip_mark_channel_unroutable,,channel,struct short_channel_id +# master->gossipd an htlc failed with this onion error. +gossip_payment_failure,3021 +gossip_payment_failure,,erring_node,struct pubkey +gossip_payment_failure,,erring_channel,struct short_channel_id +gossip_payment_failure,,erring_channel_direction,u8 +gossip_payment_failure,,len,u16 +gossip_payment_failure,,error,len*u8 # master -> gossipd: a potential funding outpoint was spent, please forget the eventual channel gossip_outpoint_spent,3024 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index f9b906c464e1..957aadaaec53 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1180,10 +1180,10 @@ static void update_local_channel(struct daemon *daemon, int direction, bool disable, u16 cltv_expiry_delta, - u64 htlc_minimum_msat, + struct amount_msat htlc_minimum, u32 fee_base_msat, u32 fee_proportional_millionths, - u64 htlc_maximum_msat, + struct amount_msat htlc_maximum, const char *caller) { secp256k1_ecdsa_signature dummy_sig; @@ -1241,10 +1241,10 @@ static void update_local_channel(struct daemon *daemon, timestamp, message_flags, channel_flags, cltv_expiry_delta, - htlc_minimum_msat, + htlc_minimum, fee_base_msat, fee_proportional_millionths, - htlc_maximum_msat); + htlc_maximum); /* Note that we treat the hsmd as synchronous. This is simple (no * callback hell)!, but may need to change to async if we ever want @@ -1316,10 +1316,10 @@ static void maybe_update_local_channel(struct daemon *daemon, update_local_channel(daemon, chan, direction, chan->local_disabled, hc->delay, - hc->htlc_minimum_msat, + hc->htlc_minimum, hc->base_fee, hc->proportional_fee, - hc->htlc_maximum_msat, + hc->htlc_maximum, /* Note this magic C macro which expands to the * function name, for debug messages */ __func__); @@ -1402,18 +1402,18 @@ static bool handle_get_update(struct peer *peer, const u8 *msg) * currently happen if the user restarts with different fee options, but we * don't assume that. */ static bool halfchan_new_info(const struct half_chan *hc, - u16 cltv_delta, u64 htlc_minimum_msat, + u16 cltv_delta, struct amount_msat htlc_minimum, u32 fee_base_msat, u32 fee_proportional_millionths, - u64 htlc_maximum_msat) + struct amount_msat htlc_maximum) { if (!is_halfchan_defined(hc)) return true; return hc->delay != cltv_delta - || hc->htlc_minimum_msat != htlc_minimum_msat + || !amount_msat_eq(hc->htlc_minimum, htlc_minimum) || hc->base_fee != fee_base_msat || hc->proportional_fee != fee_proportional_millionths - || hc->htlc_maximum_msat != htlc_maximum_msat; + || !amount_msat_eq(hc->htlc_maximum, htlc_maximum); } /*~ channeld asks us to update the local channel. */ @@ -1423,8 +1423,7 @@ static bool handle_local_channel_update(struct peer *peer, const u8 *msg) struct short_channel_id scid; bool disable; u16 cltv_expiry_delta; - u64 htlc_minimum_msat; - u64 htlc_maximum_msat; + struct amount_msat htlc_minimum, htlc_maximum; u32 fee_base_msat; u32 fee_proportional_millionths; int direction; @@ -1436,10 +1435,10 @@ static bool handle_local_channel_update(struct peer *peer, const u8 *msg) &scid, &disable, &cltv_expiry_delta, - &htlc_minimum_msat, + &htlc_minimum, &fee_base_msat, &fee_proportional_millionths, - &htlc_maximum_msat)) { + &htlc_maximum)) { status_broken("peer %s bad local_channel_update %s", type_to_string(tmpctx, struct pubkey, &peer->id), tal_hex(tmpctx, msg)); @@ -1469,19 +1468,19 @@ static bool handle_local_channel_update(struct peer *peer, const u8 *msg) * Or, if we're *enabling* an announced-disabled channel. * Or, if it's an unannounced channel (only sending to peer). */ if (halfchan_new_info(&chan->half[direction], - cltv_expiry_delta, htlc_minimum_msat, + cltv_expiry_delta, htlc_minimum, fee_base_msat, fee_proportional_millionths, - htlc_maximum_msat) + htlc_maximum) || ((chan->half[direction].channel_flags & ROUTING_FLAGS_DISABLED) && !disable) || !is_chan_public(chan)) { update_local_channel(peer->daemon, chan, direction, disable, cltv_expiry_delta, - htlc_minimum_msat, + htlc_minimum, fee_base_msat, fee_proportional_millionths, - htlc_maximum_msat, + htlc_maximum, __func__); } @@ -1766,10 +1765,10 @@ static void gossip_send_keepalive_update(struct daemon *daemon, hc->channel_flags & ROUTING_FLAGS_DIRECTION, chan->local_disabled, hc->delay, - hc->htlc_minimum_msat, + hc->htlc_minimum, hc->base_fee, hc->proportional_fee, - hc->htlc_maximum_msat, + hc->htlc_maximum, __func__); } @@ -1892,9 +1891,9 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { struct pubkey source, destination; - u64 msatoshi; + struct amount_msat msat; u32 final_cltv; - u16 riskfactor; + u64 riskfactor_by_million; u32 max_hops; u8 *out; struct route_hop *hops; @@ -1909,20 +1908,21 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, * avoid being too predictable. */ if (!fromwire_gossip_getroute_request(msg, msg, &source, &destination, - &msatoshi, &riskfactor, + &msat, &riskfactor_by_million, &final_cltv, &fuzz, &excluded, &max_hops)) master_badmsg(WIRE_GOSSIP_GETROUTE_REQUEST, msg); - status_trace("Trying to find a route from %s to %s for %"PRIu64" msatoshi", + status_trace("Trying to find a route from %s to %s for %s", pubkey_to_hexstr(tmpctx, &source), - pubkey_to_hexstr(tmpctx, &destination), msatoshi); + pubkey_to_hexstr(tmpctx, &destination), + type_to_string(tmpctx, struct amount_msat, &msat)); /* routing.c does all the hard work; can return NULL. */ hops = get_route(tmpctx, daemon->rstate, &source, &destination, - msatoshi, riskfactor, final_cltv, - fuzz, siphash_seed(), excluded, max_hops); + msat, riskfactor_by_million / 1000000.0, final_cltv, + fuzz, pseudorand_u64(), excluded, max_hops); out = towire_gossip_getroute_reply(NULL, hops); daemon_conn_send(daemon->master, take(out)); @@ -1967,7 +1967,7 @@ static void append_half_channel(struct gossip_getchannels_entry **entries, * raw in this case. */ raw_pubkey(e.source, &chan->nodes[idx]->id); raw_pubkey(e.destination, &chan->nodes[!idx]->id); - e.satoshis = chan->satoshis; + e.sat = chan->sat; e.channel_flags = c->channel_flags; e.message_flags = c->message_flags; e.local_disabled = chan->local_disabled; @@ -2496,13 +2496,13 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn, { struct short_channel_id scid; u8 *outscript; - u64 satoshis; + struct amount_sat sat; - if (!fromwire_gossip_get_txout_reply(msg, msg, &scid, &satoshis, &outscript)) + if (!fromwire_gossip_get_txout_reply(msg, msg, &scid, &sat, &outscript)) master_badmsg(WIRE_GOSSIP_GET_TXOUT_REPLY, msg); /* Outscript is NULL if it's not an unspent output */ - handle_pending_cannouncement(daemon->rstate, &scid, satoshis, outscript); + handle_pending_cannouncement(daemon->rstate, &scid, sat, outscript); /* Anywhere we might have announced a channel, we check if it's time to * announce ourselves (ie. if we just announced our own first channel) */ @@ -2511,53 +2511,99 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } +/* Fix up the channel_update to include the type if it doesn't currently have + * one. See ElementsProject/lightning#1730 and lightningnetwork/lnd#1599 for the + * in-depth discussion on why we break message parsing here... */ +static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES) +{ + u8 *fixed; + if (channel_update != NULL && + fromwire_peektype(channel_update) != WIRE_CHANNEL_UPDATE) { + /* This should be a channel_update, prefix with the + * WIRE_CHANNEL_UPDATE type, but isn't. Let's prefix it. */ + fixed = tal_arr(ctx, u8, 0); + towire_u16(&fixed, WIRE_CHANNEL_UPDATE); + towire(&fixed, channel_update, tal_bytelen(channel_update)); + if (taken(channel_update)) + tal_free(channel_update); + return fixed; + } else { + return tal_dup_arr(ctx, u8, + channel_update, tal_count(channel_update), 0); + } +} + +/* Return NULL if the wrapped onion error message has no channel_update field, + * or return the embedded channel_update message otherwise. */ +static u8 *channel_update_from_onion_error(const tal_t *ctx, + const u8 *onion_message) +{ + u8 *channel_update = NULL; + struct amount_msat unused_msat; + u32 unused32; + + /* Identify failcodes that have some channel_update. + * + * TODO > BOLT 1.0: Add new failcodes when updating to a + * new BOLT version. */ + if (!fromwire_temporary_channel_failure(ctx, + onion_message, + &channel_update) && + !fromwire_amount_below_minimum(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_fee_insufficient(ctx, + onion_message, &unused_msat, + &channel_update) && + !fromwire_incorrect_cltv_expiry(ctx, + onion_message, &unused32, + &channel_update) && + !fromwire_expiry_too_soon(ctx, + onion_message, + &channel_update)) + /* No channel update. */ + return NULL; + + return patch_channel_update(ctx, take(channel_update)); +} + /*~ lightningd tells us when a payment has failed; we mark the channel (or - * node) unusable here (maybe temporarily), and unpack and channel_update - * contained in the error. */ -static struct io_plan *handle_routing_failure(struct io_conn *conn, + * node) unusable here if it's a permanent failure, and unpack any + * channel_update contained in the error. */ +static struct io_plan *handle_payment_failure(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { struct pubkey erring_node; struct short_channel_id erring_channel; - u16 failcode; + u8 erring_channel_direction; + u8 *error; + enum onion_type failcode; u8 *channel_update; - if (!fromwire_gossip_routing_failure(msg, - msg, + if (!fromwire_gossip_payment_failure(msg, msg, &erring_node, &erring_channel, - &failcode, - &channel_update)) - master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg); - + &erring_channel_direction, + &error)) + master_badmsg(WIRE_GOSSIP_PAYMENT_FAILURE, msg); + + failcode = fromwire_peektype(error); + channel_update = channel_update_from_onion_error(tmpctx, error); + if (channel_update) + status_debug("Extracted channel_update %s from onionreply %s", + tal_hex(tmpctx, channel_update), + tal_hex(tmpctx, error)); routing_failure(daemon->rstate, &erring_node, &erring_channel, - (enum onion_type) failcode, + erring_channel_direction, + failcode, channel_update); return daemon_conn_read_next(conn, daemon->master); } -/*~ This allows lightningd to explicitly mark a channel temporarily unroutable. - * This is used when we get an unparsable error, and we don't know who to blame; - * lightningd uses this to marking routes unroutable at random... */ -static struct io_plan * -handle_mark_channel_unroutable(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) -{ - struct short_channel_id channel; - - if (!fromwire_gossip_mark_channel_unroutable(msg, &channel)) - master_badmsg(WIRE_GOSSIP_MARK_CHANNEL_UNROUTABLE, msg); - - mark_channel_unroutable(daemon->rstate, &channel); - - return daemon_conn_read_next(conn, daemon->master); -} - /*~ This is where lightningd tells us that a channel's funding transaction has * been spent. */ static struct io_plan *handle_outpoint_spent(struct io_conn *conn, @@ -2568,7 +2614,7 @@ static struct io_plan *handle_outpoint_spent(struct io_conn *conn, struct chan *chan; struct routing_state *rstate = daemon->rstate; if (!fromwire_gossip_outpoint_spent(msg, &scid)) - master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg); + master_badmsg(WIRE_GOSSIP_OUTPOINT_SPENT, msg); chan = get_channel(rstate, &scid); if (chan) { @@ -2605,7 +2651,7 @@ static struct io_plan *handle_local_channel_close(struct io_conn *conn, struct chan *chan; struct routing_state *rstate = daemon->rstate; if (!fromwire_gossip_local_channel_close(msg, &scid)) - master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg); + master_badmsg(WIRE_GOSSIP_LOCAL_CHANNEL_CLOSE, msg); chan = get_channel(rstate, &scid); if (chan) @@ -2639,11 +2685,8 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIP_GET_TXOUT_REPLY: return handle_txout_reply(conn, daemon, msg); - case WIRE_GOSSIP_ROUTING_FAILURE: - return handle_routing_failure(conn, daemon, msg); - - case WIRE_GOSSIP_MARK_CHANNEL_UNROUTABLE: - return handle_mark_channel_unroutable(conn, daemon, msg); + case WIRE_GOSSIP_PAYMENT_FAILURE: + return handle_payment_failure(conn, daemon, msg); case WIRE_GOSSIP_OUTPOINT_SPENT: return handle_outpoint_spent(conn, daemon, msg); diff --git a/gossipd/routing.c b/gossipd/routing.c index f49ef21baa26..3d8609d55336 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -25,12 +25,6 @@ /* 365.25 * 24 * 60 Groestlcoin */ #define BLOCKS_PER_YEAR 525960 -/* For overflow avoidance, we never deal with msatoshi > 40 bits. */ -#define MAX_MSATOSHI (1ULL << 40) - -/* Proportional fee must be less than 24 bits, so never overflows. */ -#define MAX_PROPORTIONAL_FEE (1 << 24) - /* We've unpacked and checked its signatures, now we wait for master to tell * us the txout to check */ struct pending_cannouncement { @@ -265,7 +259,6 @@ static void init_half_chan(struct routing_state *rstate, struct half_chan *c = &chan->half[channel_idx]; c->channel_update = NULL; - c->unroutable_until = 0; /* Set the channel direction */ c->channel_flags = channel_idx; @@ -288,7 +281,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct pubkey *id1, const struct pubkey *id2, - u64 satoshis) + struct amount_sat satoshis) { struct chan *chan = tal(rstate, struct chan); int n1idx = pubkey_idx(id1, id2); @@ -311,7 +304,7 @@ struct chan *new_chan(struct routing_state *rstate, chan->txout_script = NULL; chan->channel_announce = NULL; chan->channel_announcement_index = 0; - chan->satoshis = satoshis; + chan->sat = satoshis; chan->local_disabled = false; tal_arr_expand(&n2->chans, chan); @@ -328,7 +321,7 @@ struct chan *new_chan(struct routing_state *rstate, } /* Too big to reach, but don't overflow if added. */ -#define INFINITE 0x3FFFFFFFFFFFFFFFULL +#define INFINITE AMOUNT_MSAT(0x3FFFFFFFFFFFFFFFULL) static void clear_bfg(struct node_map *nodes) { @@ -339,37 +332,45 @@ static void clear_bfg(struct node_map *nodes) size_t i; for (i = 0; i < ARRAY_SIZE(n->bfg); i++) { n->bfg[i].total = INFINITE; - n->bfg[i].risk = 0; + n->bfg[i].risk = AMOUNT_MSAT(0); } } } -static u64 connection_fee(const struct half_chan *c, u64 msatoshi) -{ - u64 fee; - - assert(msatoshi < MAX_MSATOSHI); - assert(c->proportional_fee < MAX_PROPORTIONAL_FEE); - - fee = (c->proportional_fee * msatoshi) / 1000000; - /* This can't overflow: c->base_fee is a u32 */ - return c->base_fee + fee; -} - /* Risk of passing through this channel. We insert a tiny constant here * in order to prefer shorter routes, all things equal. */ -static u64 risk_fee(u64 amount, u32 delay, double riskfactor) +static WARN_UNUSED_RESULT bool risk_add_fee(struct amount_msat *risk, + struct amount_msat msat, + u32 delay, double riskfactor) { - return 1 + amount * delay * riskfactor; + double r; + + /* Won't overflow on add, just lose precision */ + r = 1.0 + riskfactor * delay * msat.millisatoshis + risk->millisatoshis; /* Raw: to double */ + if (r > UINT64_MAX) + return false; + risk->millisatoshis = r; /* Raw: from double */ + return true; } /* Check that we can fit through this channel's indicated * maximum_ and minimum_msat requirements. */ -static bool hc_can_carry(const struct half_chan *hc, u64 requiredcap) +static bool hc_can_carry(const struct half_chan *hc, + struct amount_msat requiredcap) +{ + return amount_msat_greater_eq(hc->htlc_maximum, requiredcap) && + amount_msat_less_eq(hc->htlc_minimum, requiredcap); +} + +/* Theoretically, this could overflow. */ +static bool fuzz_fee(u64 *fee, double fee_scale) { - return hc->htlc_maximum_msat >= requiredcap && - hc->htlc_minimum_msat <= requiredcap; + u64 fuzzed_fee = *fee * fee_scale; + if (fee_scale > 1.0 && fuzzed_fee < *fee) + return false; + *fee = fuzzed_fee; + return true; } /* We track totals, rather than costs. That's because the fee depends @@ -398,38 +399,57 @@ static void bfg_one_edge(struct node *node, for (h = 0; h < max_hops; h++) { struct node *src; /* FIXME: Bias against smaller channels. */ - u64 fee; - u64 risk; - u64 requiredcap; + struct amount_msat fee, risk, requiredcap, + this_total, curr_total; + + if (!amount_msat_fee(&fee, node->bfg[h].total, + c->base_fee, c->proportional_fee)) + continue; + + if (!fuzz_fee(&fee.millisatoshis, fee_scale)) /* Raw: double manipulation */ + continue; - if (node->bfg[h].total == INFINITE) + if (!amount_msat_add(&requiredcap, node->bfg[h].total, fee)) continue; - fee = connection_fee(c, node->bfg[h].total) * fee_scale; - requiredcap = node->bfg[h].total + fee; - risk = node->bfg[h].risk + - risk_fee(requiredcap, c->delay, riskfactor); + risk = node->bfg[h].risk; + if (!risk_add_fee(&risk, requiredcap, c->delay, riskfactor)) + continue; if (!hc_can_carry(c, requiredcap)) { /* Skip a channel if it indicated that it won't route * the requested amount. */ continue; - } else if (requiredcap >= MAX_MSATOSHI) { - SUPERVERBOSE("...extreme %"PRIu64 - " + fee %"PRIu64 - " + risk %"PRIu64" ignored", - node->bfg[h].total, fee, risk); - continue; } + if (!amount_msat_add(&this_total, requiredcap, risk)) + continue; + /* nodes[0] is src for connections[0] */ src = chan->nodes[idx]; - if (requiredcap + risk < - src->bfg[h + 1].total + src->bfg[h + 1].risk) { - SUPERVERBOSE("...%s can reach here in hoplen %zu total %"PRIu64, + + if (!amount_msat_add(&curr_total, + src->bfg[h + 1].total, + src->bfg[h + 1].risk)) { + /* We just calculated this: shouldn't happen! */ + status_broken("Overflow: total %s + risk %s", + type_to_string(tmpctx, struct amount_msat, + &src->bfg[h + 1].total), + type_to_string(tmpctx, struct amount_msat, + &src->bfg[h + 1].risk)); + continue; + } + + if (amount_msat_less(this_total, curr_total)) { + SUPERVERBOSE("...%s can reach here hoplen %zu" + " total %s risk %s", type_to_string(tmpctx, struct pubkey, &src->id), - h, node->bfg[h].total + fee); + h, + type_to_string(tmpctx, struct amount_msat, + &requiredcap), + type_to_string(tmpctx, struct amount_msat, + &risk)); src->bfg[h+1].total = requiredcap; src->bfg[h+1].risk = risk; src->bfg[h+1].prev = chan; @@ -438,30 +458,27 @@ static void bfg_one_edge(struct node *node, } /* Determine if the given half_chan is routable */ -static bool hc_is_routable(const struct chan *chan, int idx, time_t now) +static bool hc_is_routable(const struct chan *chan, int idx) { return !chan->local_disabled - && is_halfchan_enabled(&chan->half[idx]) - && chan->half[idx].unroutable_until < now; + && is_halfchan_enabled(&chan->half[idx]); } /* riskfactor is already scaled to per-block amount */ static struct chan ** find_route(const tal_t *ctx, struct routing_state *rstate, - const struct pubkey *from, const struct pubkey *to, u64 msatoshi, + const struct pubkey *from, const struct pubkey *to, + struct amount_msat msat, double riskfactor, double fuzz, const struct siphash_seed *base_seed, size_t max_hops, - u64 *fee) + struct amount_msat *fee) { struct chan **route; struct node *n, *src, *dst; struct node_map_iter it; + struct amount_msat best_total; int runs, i, best; - /* Call time_now() once at the start, so that our tight loop - * does not keep calling into operating system for the - * current time */ - time_t now = time_now().ts.tv_sec; /* Note: we map backwards, since we know the amount of satoshi we want * at the end, and need to derive how much we need to send. */ @@ -482,12 +499,6 @@ find_route(const tal_t *ctx, struct routing_state *rstate, return NULL; } - if (msatoshi >= MAX_MSATOSHI) { - status_info("find_route: can't route huge amount %"PRIu64, - msatoshi); - return NULL; - } - if (max_hops > ROUTING_MAX_HOPS) { status_info("find_route: max_hops huge amount %zu > %u", max_hops, ROUTING_MAX_HOPS); @@ -499,8 +510,8 @@ find_route(const tal_t *ctx, struct routing_state *rstate, /* Bellman-Ford-Gibson: like Bellman-Ford, but keep values for * every path length. */ - src->bfg[0].total = msatoshi; - src->bfg[0].risk = 0; + src->bfg[0].total = msat; + src->bfg[0].risk = AMOUNT_MSAT(0); for (runs = 0; runs < max_hops; runs++) { SUPERVERBOSE("Run %i", runs); @@ -518,7 +529,7 @@ find_route(const tal_t *ctx, struct routing_state *rstate, &n->id), i, num_edges); - if (!hc_is_routable(chan, idx, now)) { + if (!hc_is_routable(chan, idx)) { SUPERVERBOSE("...unroutable (local_disabled = %i, is_halfchan_enabled = %i, unroutable_until = %i", chan->local_disabled, is_halfchan_enabled(&chan->half[idx]), @@ -534,13 +545,27 @@ find_route(const tal_t *ctx, struct routing_state *rstate, } best = 0; - for (i = 1; i <= max_hops; i++) { - if (dst->bfg[i].total < dst->bfg[best].total) + best_total = INFINITE; + for (i = 0; i <= max_hops; i++) { + struct amount_msat total; + status_trace("%i hop solution: %s + %s", + i, + type_to_string(tmpctx, struct amount_msat, + &dst->bfg[i].total), + type_to_string(tmpctx, struct amount_msat, + &dst->bfg[i].risk)); + if (!amount_msat_add(&total, + dst->bfg[i].total, dst->bfg[i].risk)) + continue; + if (amount_msat_less(total, best_total)) { best = i; + best_total = total; + } } + status_trace("=> chose %i hop solution", best); /* No route? */ - if ((dst->bfg[best].total) >= INFINITE) { + if (amount_msat_greater_eq(best_total, INFINITE)) { status_trace("find_route: No route to %s", type_to_string(tmpctx, struct pubkey, to)); return NULL; @@ -548,7 +573,13 @@ find_route(const tal_t *ctx, struct routing_state *rstate, /* We (dst) don't charge ourselves fees, so skip first hop */ n = other_node(dst, dst->bfg[best].prev); - *fee = n->bfg[best-1].total - msatoshi; + if (!amount_msat_sub(fee, n->bfg[best-1].total, msat)) { + status_broken("Could not subtract %s - %s for fee", + type_to_string(tmpctx, struct amount_msat, + &n->bfg[best-1].total), + type_to_string(tmpctx, struct amount_msat, &msat)); + return NULL; + } /* Lay out route */ route = tal_arr(ctx, struct chan *, best); @@ -734,7 +765,8 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, } bool routing_add_channel_announcement(struct routing_state *rstate, - const u8 *msg TAKES, u64 satoshis) + const u8 *msg TAKES, + struct amount_sat sat) { struct chan *chan; secp256k1_ecdsa_signature node_signature_1, node_signature_2; @@ -758,7 +790,7 @@ bool routing_add_channel_announcement(struct routing_state *rstate, * channel_announcements. See handle_channel_announcement. */ chan = get_channel(rstate, &scid); if (!chan) - chan = new_chan(rstate, &scid, &node_id_1, &node_id_2, satoshis); + chan = new_chan(rstate, &scid, &node_id_1, &node_id_2, sat); /* Channel is now public. */ chan->channel_announce = tal_dup_arr(chan, u8, msg, tal_count(msg), 0); @@ -902,6 +934,8 @@ u8 *handle_channel_announcement(struct routing_state *rstate, tal_add_destructor2(pending, destroy_pending_cannouncement, rstate); /* Success */ + // MSC: Cppcheck 1.86 gets this false positive + // cppcheck-suppress autoVariables *scid = &pending->short_channel_id; return NULL; @@ -937,7 +971,7 @@ static void process_pending_channel_update(struct routing_state *rstate, void handle_pending_cannouncement(struct routing_state *rstate, const struct short_channel_id *scid, - const u64 satoshis, + struct amount_sat sat, const u8 *outscript) { const u8 *s; @@ -985,7 +1019,7 @@ void handle_pending_cannouncement(struct routing_state *rstate, return; } - if (!routing_add_channel_announcement(rstate, pending->announce, satoshis)) + if (!routing_add_channel_announcement(rstate, pending->announce, sat)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not add channel_announcement"); @@ -1025,14 +1059,14 @@ static void set_connection_values(struct chan *chan, u8 message_flags, u8 channel_flags, u64 timestamp, - u64 htlc_minimum_msat, - u64 htlc_maximum_msat) + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum) { struct half_chan *c = &chan->half[idx]; c->delay = delay; - c->htlc_minimum_msat = htlc_minimum_msat; - c->htlc_maximum_msat = htlc_maximum_msat; + c->htlc_minimum = htlc_minimum; + c->htlc_maximum = htlc_maximum; c->base_fee = base_fee; c->proportional_fee = proportional_fee; c->message_flags = message_flags; @@ -1040,22 +1074,9 @@ static void set_connection_values(struct chan *chan, c->last_timestamp = timestamp; assert((c->channel_flags & ROUTING_FLAGS_DIRECTION) == idx); - /* If it was temporarily unroutable, re-enable */ - c->unroutable_until = 0; - SUPERVERBOSE("Channel %s/%d was updated.", type_to_string(tmpctx, struct short_channel_id, &chan->scid), idx); - - if (c->proportional_fee >= MAX_PROPORTIONAL_FEE) { - status_trace("Channel %s/%d massive proportional fee %u:" - " disabling.", - type_to_string(tmpctx, struct short_channel_id, - &chan->scid), - idx, - c->proportional_fee); - c->channel_flags |= ROUTING_FLAGS_DISABLED; - } } bool routing_add_channel_update(struct routing_state *rstate, @@ -1066,10 +1087,9 @@ bool routing_add_channel_update(struct routing_state *rstate, u32 timestamp; u8 message_flags, channel_flags; u16 expiry; - u64 htlc_minimum_msat; + struct amount_msat htlc_minimum, htlc_maximum; u32 fee_base_msat; u32 fee_proportional_millionths; - u64 htlc_maximum_msat; struct bitcoin_blkid chain_hash; struct chan *chan; u8 direction; @@ -1077,7 +1097,7 @@ bool routing_add_channel_update(struct routing_state *rstate, if (!fromwire_channel_update(update, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, - &expiry, &htlc_minimum_msat, &fee_base_msat, + &expiry, &htlc_minimum, &fee_base_msat, &fee_proportional_millionths)) return false; /* If it's flagged as containing the optional field, reparse for @@ -1087,9 +1107,9 @@ bool routing_add_channel_update(struct routing_state *rstate, update, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, - &expiry, &htlc_minimum_msat, &fee_base_msat, + &expiry, &htlc_minimum, &fee_base_msat, &fee_proportional_millionths, - &htlc_maximum_msat)) + &htlc_maximum)) return false; chan = get_channel(rstate, &short_channel_id); if (!chan) @@ -1098,25 +1118,29 @@ bool routing_add_channel_update(struct routing_state *rstate, if (message_flags & ROUTING_OPT_HTLC_MAX_MSAT) { /* Reject update if the `htlc_maximum_msat` is greater * than the total available channel satoshis */ - if (htlc_maximum_msat > chan->satoshis * 1000) + if (amount_msat_greater_sat(htlc_maximum, chan->sat)) return false; } else { /* If not indicated, set htlc_max_msat to channel capacity */ - htlc_maximum_msat = chan->satoshis * 1000; + if (!amount_sat_to_msat(&htlc_maximum, chan->sat)) { + status_broken("Channel capacity %s overflows!", + type_to_string(tmpctx, struct amount_sat, + &chan->sat)); + return false; + } } /* FIXME: https://github.com/lightningnetwork/lightning-rfc/pull/512 * says we MUST NOT exceed 2^32-1, but c-lightning did, so just trim * rather than rejecting. */ - if (htlc_maximum_msat > rstate->chainparams->max_payment_msat) - htlc_maximum_msat = rstate->chainparams->max_payment_msat; + if (amount_msat_greater(htlc_maximum, rstate->chainparams->max_payment)) + htlc_maximum = rstate->chainparams->max_payment; direction = channel_flags & 0x1; set_connection_values(chan, direction, fee_base_msat, fee_proportional_millionths, expiry, message_flags, channel_flags, - timestamp, htlc_minimum_msat, - htlc_maximum_msat); + timestamp, htlc_minimum, htlc_maximum); /* Replace any old one. */ tal_free(chan->half[direction].channel_update); @@ -1152,7 +1176,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 timestamp; u8 message_flags, channel_flags; u16 expiry; - u64 htlc_minimum_msat; + struct amount_msat htlc_minimum; u32 fee_base_msat; u32 fee_proportional_millionths; struct bitcoin_blkid chain_hash; @@ -1166,7 +1190,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, &expiry, - &htlc_minimum_msat, &fee_base_msat, + &htlc_minimum, &fee_base_msat, &fee_proportional_millionths)) { err = towire_errorfmt(rstate, NULL, "Malformed channel_update %s", @@ -1503,43 +1527,44 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann) struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, const struct pubkey *source, const struct pubkey *destination, - const u64 msatoshi, double riskfactor, + struct amount_msat msat, double riskfactor, u32 final_cltv, - double fuzz, const struct siphash_seed *base_seed, + double fuzz, u64 seed, const struct short_channel_id_dir *excluded, size_t max_hops) { struct chan **route; - u64 total_amount; + struct amount_msat total_amount; unsigned int total_delay; - u64 fee; + struct amount_msat fee; struct route_hop *hops; struct node *n; - u64 *saved_capacity; + struct amount_msat *saved_capacity; + struct siphash_seed base_seed; + + saved_capacity = tal_arr(tmpctx, struct amount_msat, tal_count(excluded)); - saved_capacity = tal_arr(tmpctx, u64, tal_count(excluded)); + base_seed.u.u64[0] = base_seed.u.u64[1] = seed; /* Temporarily set excluded channels' capacity to zero. */ for (size_t i = 0; i < tal_count(excluded); i++) { struct chan *chan = get_channel(rstate, &excluded[i].scid); if (!chan) continue; - saved_capacity[i] - = chan->half[excluded[i].dir].htlc_maximum_msat; - chan->half[excluded[i].dir].htlc_maximum_msat = 0; + saved_capacity[i] = chan->half[excluded[i].dir].htlc_maximum; + chan->half[excluded[i].dir].htlc_maximum = AMOUNT_MSAT(0); } - route = find_route(ctx, rstate, source, destination, msatoshi, - riskfactor / BLOCKS_PER_YEAR / 10000, - fuzz, base_seed, max_hops, &fee); + route = find_route(ctx, rstate, source, destination, msat, + riskfactor / BLOCKS_PER_YEAR / 100, + fuzz, &base_seed, max_hops, &fee); /* Now restore the capacity. */ for (size_t i = 0; i < tal_count(excluded); i++) { struct chan *chan = get_channel(rstate, &excluded[i].scid); if (!chan) continue; - chan->half[excluded[i].dir].htlc_maximum_msat - = saved_capacity[i]; + chan->half[excluded[i].dir].htlc_maximum = saved_capacity[i]; } if (!route) { @@ -1548,13 +1573,14 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, /* Fees, delays need to be calculated backwards along route. */ hops = tal_arr(ctx, struct route_hop, tal_count(route)); - total_amount = msatoshi; + total_amount = msat; total_delay = final_cltv; /* Start at destination node. */ n = get_node(rstate, destination); for (int i = tal_count(route) - 1; i >= 0; i--) { const struct half_chan *c; + int idx = half_chan_to(n, route[i]); c = &route[i]->half[idx]; hops[i].channel_id = route[i]->scid; @@ -1562,80 +1588,75 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, hops[i].amount = total_amount; hops[i].delay = total_delay; hops[i].direction = idx; - total_amount += connection_fee(c, total_amount); + + /* Since we calculated this route, it should not overflow! */ + if (!amount_msat_add_fee(&total_amount, + c->base_fee, c->proportional_fee)) { + status_broken("Route overflow step %i: %s + %u/%u!?", + i, type_to_string(tmpctx, struct amount_msat, + &total_amount), + c->base_fee, c->proportional_fee); + return tal_free(hops); + } total_delay += c->delay; n = other_node(n, route[i]); } assert(pubkey_eq(&n->id, source)); - /* FIXME: Shadow route! */ return hops; } -/** - * routing_failure_channel_out - Handle routing failure on a specific channel - * - * If we want to delete the channel, we reparent it to disposal_context. - */ -static void routing_failure_channel_out(const tal_t *disposal_context, - struct node *node, - enum onion_type failcode, - struct chan *chan, - time_t now) -{ - struct half_chan *hc = half_chan_from(node, chan); - - /* BOLT #4: - * - * - if the PERM bit is NOT set: - * - SHOULD restore the channels as it receives new `channel_update`s. - */ - if (!(failcode & PERM)) - /* Prevent it for 20 seconds. */ - hc->unroutable_until = now + 20; - else - /* Set it up to be pruned. */ - tal_steal(disposal_context, chan); -} - void routing_failure(struct routing_state *rstate, const struct pubkey *erring_node_pubkey, const struct short_channel_id *scid, + int erring_direction, enum onion_type failcode, const u8 *channel_update) { - struct node *node; - time_t now = time_now().ts.tv_sec; - status_trace("Received routing failure 0x%04x (%s), " "erring node %s, " - "channel %s", + "channel %s/%u", (int) failcode, onion_type_name(failcode), type_to_string(tmpctx, struct pubkey, erring_node_pubkey), - type_to_string(tmpctx, struct short_channel_id, scid)); - - node = get_node(rstate, erring_node_pubkey); - if (!node) { - status_unusual("routing_failure: Erring node %s not in map", - type_to_string(tmpctx, struct pubkey, - erring_node_pubkey)); - /* No node, so no channel, so any channel_update - * can also be ignored. */ - return; + type_to_string(tmpctx, struct short_channel_id, scid), + erring_direction); + + /* lightningd will only extract this if UPDATE is set. */ + if (channel_update) { + u8 *err = handle_channel_update(rstate, channel_update, "error"); + if (err) { + status_unusual("routing_failure: " + "bad channel_update %s", + sanitize_error(err, err, NULL)); + tal_free(err); + } + } else if (failcode & UPDATE) { + status_unusual("routing_failure: " + "UPDATE bit set, no channel_update. " + "failcode: 0x%04x", + (int) failcode); } - /* BOLT #4: - * - * - if the NODE bit is set: - * - SHOULD remove all channels connected with the erring node from - * consideration. - * - */ + /* We respond to permanent errors, ignore the rest: they're + * for the pay command to worry about. */ + if (!(failcode & PERM)) + return; + if (failcode & NODE) { - for (int i = 0; i < tal_count(node->chans); ++i) { - routing_failure_channel_out(tmpctx, node, failcode, - node->chans[i], - now); + struct node *node = get_node(rstate, erring_node_pubkey); + if (!node) { + status_unusual("routing_failure: Erring node %s not in map", + type_to_string(tmpctx, struct pubkey, + erring_node_pubkey)); + } else { + status_trace("Deleting node %s", + type_to_string(tmpctx, + struct pubkey, + &node->id)); + for (size_t i = 0; i < tal_count(node->chans); ++i) { + /* Set it up to be pruned. */ + tal_steal(tmpctx, node->chans[i]); + } } } else { struct chan *chan = get_channel(rstate, scid); @@ -1646,73 +1667,24 @@ void routing_failure(struct routing_state *rstate, type_to_string(tmpctx, struct short_channel_id, scid)); - else if (chan->nodes[0] != node && chan->nodes[1] != node) - status_unusual("routing_failure: " - "Channel %s does not connect to %s", - type_to_string(tmpctx, - struct short_channel_id, - scid), - type_to_string(tmpctx, struct pubkey, - erring_node_pubkey)); - else - routing_failure_channel_out(tmpctx, - node, failcode, chan, now); - } - - /* Update the channel if UPDATE failcode. Do - * this after deactivating, so that if the - * channel_update is newer it will be - * reactivated. */ - if (failcode & UPDATE) { - u8 *err; - if (tal_count(channel_update) == 0) { - /* Suppress UNUSUAL log if local failure */ - if (pubkey_eq(erring_node_pubkey, &rstate->local_id)) + else { + /* This error can be triggered by sendpay if caller + * uses the wrong key for dest. */ + if (failcode == WIRE_INVALID_ONION_HMAC + && !pubkey_eq(&chan->nodes[!erring_direction]->id, + erring_node_pubkey)) return; - status_unusual("routing_failure: " - "UPDATE bit set, no channel_update. " - "failcode: 0x%04x", - (int) failcode); - return; - } - err = handle_channel_update(rstate, channel_update, "error"); - if (err) { - status_unusual("routing_failure: " - "bad channel_update %s", - sanitize_error(err, err, NULL)); - tal_free(err); - return; + + status_trace("Deleting channel %s", + type_to_string(tmpctx, + struct short_channel_id, + scid)); + /* Set it up to be deleted. */ + tal_steal(tmpctx, chan); } - } else { - if (tal_count(channel_update) != 0) - status_unusual("routing_failure: " - "UPDATE bit clear, channel_update given. " - "failcode: 0x%04x", - (int) failcode); } } -void mark_channel_unroutable(struct routing_state *rstate, - const struct short_channel_id *channel) -{ - struct chan *chan; - time_t now = time_now().ts.tv_sec; - const char *scid = type_to_string(tmpctx, struct short_channel_id, - channel); - - status_trace("Received mark_channel_unroutable channel %s", - scid); - - chan = get_channel(rstate, channel); - if (!chan) { - status_unusual("mark_channel_unroutable: " - "channel %s not in routemap", - scid); - return; - } - chan->half[0].unroutable_until = now + 20; - chan->half[1].unroutable_until = now + 20; -} void route_prune(struct routing_state *rstate) { @@ -1763,10 +1735,10 @@ bool handle_local_add_channel(struct routing_state *rstate, const u8 *msg) { struct short_channel_id scid; struct pubkey remote_node_id; - u64 satoshis; + struct amount_sat sat; if (!fromwire_gossipd_local_add_channel(msg, &scid, &remote_node_id, - &satoshis)) { + &sat)) { status_broken("Unable to parse local_add_channel message: %s", tal_hex(msg, msg)); return false; @@ -1782,6 +1754,6 @@ bool handle_local_add_channel(struct routing_state *rstate, const u8 *msg) type_to_string(tmpctx, struct short_channel_id, &scid)); /* Create new (unannounced) channel */ - new_chan(rstate, &scid, &rstate->local_id, &remote_node_id, satoshis); + new_chan(rstate, &scid, &rstate->local_id, &remote_node_id, sat); return true; } diff --git a/gossipd/routing.h b/gossipd/routing.h index 73c2fd660872..2fb5d73aee30 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -26,11 +27,8 @@ struct half_chan { /* -1 if channel_update is NULL */ s64 last_timestamp; - /* Minimum number of msatoshi in an HTLC */ - u64 htlc_minimum_msat; - - /* Maximum number of msatoshis in an HTLC */ - u64 htlc_maximum_msat; + /* Minimum and maximum number of msatoshi in an HTLC */ + struct amount_msat htlc_minimum, htlc_maximum; /* Flags as specified by the `channel_update`s, among other * things indicated direction wrt the `channel_id` */ @@ -39,10 +37,6 @@ struct half_chan { /* Flags as specified by the `channel_update`s, indicates * optional fields. */ u8 message_flags; - - /* If greater than current time, this connection should not - * be used for routing. */ - time_t unroutable_until; }; struct chan { @@ -65,7 +59,7 @@ struct chan { /* Disabled locally (due to peer disconnect) */ bool local_disabled; - u64 satoshis; + struct amount_sat sat; }; /* A local channel can exist which isn't announcable. */ @@ -106,9 +100,9 @@ struct node { /* Temporary data for routefinding. */ struct { /* Total to get to here from target. */ - u64 total; + struct amount_msat total; /* Total risk premium of this route. */ - u64 risk; + struct amount_msat risk; /* Where that came from. */ struct chan *prev; } bfg[ROUTING_MAX_HOPS+1]; @@ -209,7 +203,7 @@ struct route_hop { struct short_channel_id channel_id; int direction; struct pubkey nodeid; - u64 amount; + struct amount_msat amount; u32 delay; }; @@ -228,7 +222,7 @@ struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct pubkey *id1, const struct pubkey *id2, - u64 satoshis); + struct amount_sat sat); /* Handlers for incoming messages */ @@ -248,7 +242,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, */ void handle_pending_cannouncement(struct routing_state *rstate, const struct short_channel_id *scid, - const u64 satoshis, + const struct amount_sat sat, const u8 *txscript); /* Returns NULL if all OK, otherwise an error for the peer which sent. */ @@ -265,21 +259,19 @@ struct node *get_node(struct routing_state *rstate, const struct pubkey *id); struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, const struct pubkey *source, const struct pubkey *destination, - const u64 msatoshi, double riskfactor, + const struct amount_msat msat, double riskfactor, u32 final_cltv, double fuzz, - const struct siphash_seed *base_seed, + u64 seed, const struct short_channel_id_dir *excluded, size_t max_hops); /* Disable channel(s) based on the given routing failure. */ void routing_failure(struct routing_state *rstate, const struct pubkey *erring_node, const struct short_channel_id *erring_channel, + int erring_direction, enum onion_type failcode, const u8 *channel_update); -/* Disable specific channel from routing. */ -void mark_channel_unroutable(struct routing_state *rstate, - const struct short_channel_id *channel); void route_prune(struct routing_state *rstate); @@ -291,7 +283,8 @@ void route_prune(struct routing_state *rstate); * @see{handle_channel_announcement} entrypoint to check before adding. */ bool routing_add_channel_announcement(struct routing_state *rstate, - const u8 *msg TAKES, u64 satoshis); + const u8 *msg TAKES, + struct amount_sat sat); /** * Add a channel_update without checking for errors diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index 04b14dad07c2..a3d76a9512f9 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -7,6 +7,7 @@ GOSSIPD_TEST_OBJS := $(GOSSIPD_TEST_SRC:.c=.o) GOSSIPD_TEST_PROGRAMS := $(GOSSIPD_TEST_OBJS:.o=) GOSSIPD_TEST_COMMON_OBJS := \ + common/amount.o \ common/features.o \ common/pseudorand.o \ common/type_to_string.o \ diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 418d45a657a6..02164cdf99f4 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -38,16 +38,16 @@ void broadcast_del(struct broadcast_state *bstate UNNEEDED, u64 index UNNEEDED, bool fromwire_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, secp256k1_ecdsa_signature *node_signature_1 UNNEEDED, secp256k1_ecdsa_signature *node_signature_2 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_1 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_2 UNNEEDED, u8 **features UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *node_id_1 UNNEEDED, struct pubkey *node_id_2 UNNEEDED, struct pubkey *bitcoin_key_1 UNNEEDED, struct pubkey *bitcoin_key_2 UNNEEDED) { fprintf(stderr, "fromwire_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_channel_update */ -bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) +bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) { fprintf(stderr, "fromwire_channel_update called!\n"); abort(); } /* Generated stub for fromwire_channel_update_option_channel_htlc_max */ -bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, u64 *htlc_maximum_msat UNNEEDED) +bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_channel_update_option_channel_htlc_max called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_add_channel */ -bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_announcement */ -bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_delete */ bool fromwire_gossip_store_channel_delete(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) @@ -96,7 +96,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ -u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, u64 satoshis UNNEEDED) +u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_delete */ u8 *towire_gossip_store_channel_delete(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) @@ -143,7 +143,7 @@ static void add_connection(struct routing_state *rstate, chan = get_channel(rstate, &scid); if (!chan) chan = new_chan(rstate, &scid, &nodes[from], &nodes[to], - 1000000); + AMOUNT_SAT(1000000)); c = &chan->half[idx]; c->base_fee = base_fee; @@ -152,8 +152,8 @@ static void add_connection(struct routing_state *rstate, c->channel_flags = get_channel_direction(&nodes[from], &nodes[to]); /* This must be non-NULL, otherwise we consider it disabled! */ c->channel_update = tal(chan, u8); - c->htlc_maximum_msat = -1ULL; - c->htlc_minimum_msat = 0; + c->htlc_maximum = AMOUNT_MSAT(-1ULL); + c->htlc_minimum = AMOUNT_MSAT(0); } static struct pubkey nodeid(size_t n) @@ -254,11 +254,11 @@ int main(int argc, char *argv[]) for (size_t i = 0; i < num_runs; i++) { const struct pubkey *from = &nodes[pseudorand(num_nodes)]; const struct pubkey *to = &nodes[pseudorand(num_nodes)]; - u64 fee; + struct amount_msat fee; struct chan **route; route = find_route(tmpctx, rstate, from, to, - pseudorand(100000), + (struct amount_msat){pseudorand(100000)}, riskfactor, 0.75, &base_seed, ROUTING_MAX_HOPS, diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index c9a820bac20d..b2832646a4b4 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -28,16 +28,16 @@ void broadcast_del(struct broadcast_state *bstate UNNEEDED, u64 index UNNEEDED, bool fromwire_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, secp256k1_ecdsa_signature *node_signature_1 UNNEEDED, secp256k1_ecdsa_signature *node_signature_2 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_1 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_2 UNNEEDED, u8 **features UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *node_id_1 UNNEEDED, struct pubkey *node_id_2 UNNEEDED, struct pubkey *bitcoin_key_1 UNNEEDED, struct pubkey *bitcoin_key_2 UNNEEDED) { fprintf(stderr, "fromwire_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_channel_update */ -bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) +bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) { fprintf(stderr, "fromwire_channel_update called!\n"); abort(); } /* Generated stub for fromwire_channel_update_option_channel_htlc_max */ -bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, u64 *htlc_maximum_msat UNNEEDED) +bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_channel_update_option_channel_htlc_max called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_add_channel */ -bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_announcement */ -bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_delete */ bool fromwire_gossip_store_channel_delete(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) @@ -86,7 +86,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ -u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, u64 satoshis UNNEEDED) +u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_delete */ u8 *towire_gossip_store_channel_delete(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) @@ -121,13 +121,14 @@ get_or_make_connection(struct routing_state *rstate, const struct pubkey *from_id, const struct pubkey *to_id, const char *shortid, - const u64 satoshis) + struct amount_sat satoshis) { struct short_channel_id scid; struct chan *chan; const int idx = pubkey_idx(from_id, to_id); - if (!short_channel_id_from_str(shortid, strlen(shortid), &scid)) + if (!short_channel_id_from_str(shortid, strlen(shortid), &scid, + false)) abort(); chan = get_channel(rstate, &scid); if (!chan) @@ -135,8 +136,9 @@ get_or_make_connection(struct routing_state *rstate, /* Make sure it's seen as initialized (update non-NULL). */ chan->half[idx].channel_update = (void *)chan; - chan->half[idx].htlc_minimum_msat = 0; - chan->half[idx].htlc_maximum_msat = satoshis * 1000; + chan->half[idx].htlc_minimum = AMOUNT_MSAT(0); + if (!amount_sat_to_msat(&chan->half[idx].htlc_maximum, satoshis)) + abort(); return &chan->half[idx]; } @@ -162,7 +164,7 @@ int main(void) struct half_chan *nc; struct routing_state *rstate; struct pubkey a, b, c, d; - u64 fee; + struct amount_msat fee; struct chan **route; const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; @@ -187,7 +189,7 @@ int main(void) /* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &c, &b, "6990x2x1", 1000); + nc = get_or_make_connection(rstate, &c, &b, "6990x2x1", AMOUNT_SAT(1000)); nc->base_fee = 0; nc->proportional_fee = 10; nc->delay = 5; @@ -196,7 +198,7 @@ int main(void) nc->last_timestamp = 1504064344; /* {'active': True, 'short_id': '6989:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &a, "6989x2x1", 1000); + nc = get_or_make_connection(rstate, &b, &a, "6989x2x1", AMOUNT_SAT(1000)); nc->base_fee = 0; nc->proportional_fee = 10; nc->delay = 5; @@ -205,17 +207,17 @@ int main(void) nc->last_timestamp = 1504064344; /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &b, &c, "6990x2x1", 1000); + nc = get_or_make_connection(rstate, &b, &c, "6990x2x1", AMOUNT_SAT(1000)); nc->base_fee = 0; nc->proportional_fee = 10; nc->delay = 5; nc->channel_flags = 0; nc->message_flags = 0; nc->last_timestamp = 1504064344; - nc->htlc_minimum_msat = 100; + nc->htlc_minimum = AMOUNT_MSAT(100); /* {'active': True, 'short_id': '6989:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} */ - nc = get_or_make_connection(rstate, &a, &b, "6989x2x1", 1000); + nc = get_or_make_connection(rstate, &a, &b, "6989x2x1", AMOUNT_SAT(1000)); nc->base_fee = 0; nc->proportional_fee = 10; nc->delay = 5; @@ -223,7 +225,7 @@ int main(void) nc->message_flags = 0; nc->last_timestamp = 1504064344; - route = find_route(tmpctx, rstate, &a, &c, 100000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(100000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 2); @@ -232,41 +234,41 @@ int main(void) /* We should not be able to find a route that exceeds our own capacity */ - route = find_route(tmpctx, rstate, &a, &c, 1000001, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000001), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(!route); /* Now test with a query that exceeds the channel capacity after adding * some fees */ - route = find_route(tmpctx, rstate, &a, &c, 999999, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(999999), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(!route); /* This should fail to return a route because it is smaller than these * htlc_minimum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &c, 1, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(!route); /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 1, 'htlc_maximum_msat': 500000, 'htlc_minimum_msat': 100, 'channel_flags': 0, 'destination': '02cca6c5c966fcf61d121e3a70e03a1cd9eeeea024b26ea666ce974d43b242e636', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}, */ - nc = get_or_make_connection(rstate, &a, &d, "6991x2x1", 1000); + nc = get_or_make_connection(rstate, &a, &d, "6991x2x1", AMOUNT_SAT(1000)); nc->base_fee = 0; nc->proportional_fee = 0; nc->delay = 5; nc->channel_flags = 0; nc->message_flags = 1; nc->last_timestamp = 1504064344; - nc->htlc_minimum_msat = 100; - nc->htlc_maximum_msat = 500000; /* half capacity */ + nc->htlc_minimum = AMOUNT_MSAT(100); + nc->htlc_maximum = AMOUNT_MSAT(500000); /* half capacity */ /* This should route correctly at the max_msat level */ - route = find_route(tmpctx, rstate, &a, &d, 500000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); /* This should fail to return a route because it's larger than the * htlc_maximum_msat on the last channel. */ - route = find_route(tmpctx, rstate, &a, &d, 500001, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &d, AMOUNT_MSAT(500001), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(!route); diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index ea8becbee060..750538821f52 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -25,16 +25,16 @@ void broadcast_del(struct broadcast_state *bstate UNNEEDED, u64 index UNNEEDED, bool fromwire_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, secp256k1_ecdsa_signature *node_signature_1 UNNEEDED, secp256k1_ecdsa_signature *node_signature_2 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_1 UNNEEDED, secp256k1_ecdsa_signature *bitcoin_signature_2 UNNEEDED, u8 **features UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *node_id_1 UNNEEDED, struct pubkey *node_id_2 UNNEEDED, struct pubkey *bitcoin_key_1 UNNEEDED, struct pubkey *bitcoin_key_2 UNNEEDED) { fprintf(stderr, "fromwire_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_channel_update */ -bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) +bool fromwire_channel_update(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED) { fprintf(stderr, "fromwire_channel_update called!\n"); abort(); } /* Generated stub for fromwire_channel_update_option_channel_htlc_max */ -bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, u64 *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, u64 *htlc_maximum_msat UNNEEDED) +bool fromwire_channel_update_option_channel_htlc_max(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, struct bitcoin_blkid *chain_hash UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, u32 *timestamp UNNEEDED, u8 *message_flags UNNEEDED, u8 *channel_flags UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_channel_update_option_channel_htlc_max called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_add_channel */ -bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct pubkey *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_announcement */ -bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, u64 *satoshis UNNEEDED) +bool fromwire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **announcement UNNEEDED, struct amount_sat *satoshis UNNEEDED) { fprintf(stderr, "fromwire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_gossip_store_channel_delete */ bool fromwire_gossip_store_channel_delete(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) @@ -83,7 +83,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_announcement */ -u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, u64 satoshis UNNEEDED) +u8 *towire_gossip_store_channel_announcement(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED, struct amount_sat satoshis UNNEEDED) { fprintf(stderr, "towire_gossip_store_channel_announcement called!\n"); abort(); } /* Generated stub for towire_gossip_store_channel_delete */ u8 *towire_gossip_store_channel_delete(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) @@ -121,7 +121,7 @@ static void add_connection(struct routing_state *rstate, struct short_channel_id scid; struct half_chan *c; struct chan *chan; - int satoshis = 100000; + struct amount_sat satoshis = AMOUNT_SAT(100000); /* Make a unique scid. */ memcpy(&scid, from, sizeof(scid) / 2); @@ -138,8 +138,8 @@ static void add_connection(struct routing_state *rstate, c->proportional_fee = proportional_fee; c->delay = delay; c->channel_flags = get_channel_direction(from, to); - c->htlc_minimum_msat = 0; - c->htlc_maximum_msat = satoshis * 1000; + c->htlc_minimum = AMOUNT_MSAT(0); + c->htlc_maximum = AMOUNT_MSAT(100000 * 1000); } /* Returns chan connecting from and to: *idx set to refer @@ -201,7 +201,7 @@ int main(void) struct routing_state *rstate; struct pubkey a, b, c, d; struct privkey tmp; - u64 fee; + struct amount_msat fee; struct chan **route; const double riskfactor = 1.0 / BLOCKS_PER_YEAR / 10000; @@ -222,11 +222,11 @@ int main(void) /* A<->B */ add_connection(rstate, &a, &b, 1, 1, 1); - route = find_route(tmpctx, rstate, &a, &b, 1000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &b, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 1); - assert(fee == 0); + assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); /* A<->B<->C */ memset(&tmp, 'c', sizeof(tmp)); @@ -238,11 +238,11 @@ int main(void) status_trace("C = %s", type_to_string(tmpctx, struct pubkey, &c)); add_connection(rstate, &b, &c, 1, 1, 1); - route = find_route(tmpctx, rstate, &a, &c, 1000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 2); - assert(fee == 1); + assert(amount_msat_eq(fee, AMOUNT_MSAT(1))); /* A<->D<->C: Lower base, higher percentage. */ memset(&tmp, 'd', sizeof(tmp)); @@ -254,32 +254,32 @@ int main(void) add_connection(rstate, &d, &c, 0, 2, 1); /* Will go via D for small amounts. */ - route = find_route(tmpctx, rstate, &a, &c, 1000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(1000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 2); assert(channel_is_between(route[0], &a, &d)); assert(channel_is_between(route[1], &d, &c)); - assert(fee == 0); + assert(amount_msat_eq(fee, AMOUNT_MSAT(0))); /* Will go via B for large amounts. */ - route = find_route(tmpctx, rstate, &a, &c, 3000000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 2); assert(channel_is_between(route[0], &a, &b)); assert(channel_is_between(route[1], &b, &c)); - assert(fee == 1 + 3); + assert(amount_msat_eq(fee, AMOUNT_MSAT(1 + 3))); /* Make B->C inactive, force it back via D */ get_connection(rstate, &b, &c)->channel_flags |= ROUTING_FLAGS_DISABLED; - route = find_route(tmpctx, rstate, &a, &c, 3000000, riskfactor, 0.0, NULL, + route = find_route(tmpctx, rstate, &a, &c, AMOUNT_MSAT(3000000), riskfactor, 0.0, NULL, ROUTING_MAX_HOPS, &fee); assert(route); assert(tal_count(route) == 2); assert(channel_is_between(route[0], &a, &d)); assert(channel_is_between(route[1], &d, &c)); - assert(fee == 0 + 6); + assert(amount_msat_eq(fee, AMOUNT_MSAT(0 + 6))); tal_free(tmpctx); secp256k1_context_destroy(secp256k1_ctx); diff --git a/hsmd/Makefile b/hsmd/Makefile index f13a6e5b7584..432b05e562b9 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -13,6 +13,7 @@ LIGHTNINGD_HSM_OBJS := $(LIGHTNINGD_HSM_SRC:.c=.o) # Common source we use. HSMD_COMMON_OBJS := \ + common/amount.o \ common/bip32.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/hsmd/hsm_wire.csv b/hsmd/hsm_wire.csv index 357f2ba5e692..88f523d910a2 100644 --- a/hsmd/hsm_wire.csv +++ b/hsmd/hsm_wire.csv @@ -37,8 +37,8 @@ hsm_get_channel_basepoints_reply,,funding_pubkey,struct pubkey #include # FIXME: This should also take their commit sig & details, to verify. hsm_sign_funding,4 -hsm_sign_funding,,satoshi_out,u64 -hsm_sign_funding,,change_out,u64 +hsm_sign_funding,,satoshi_out,struct amount_sat +hsm_sign_funding,,change_out,struct amount_sat hsm_sign_funding,,change_keyindex,u32 hsm_sign_funding,,our_pubkey,struct pubkey hsm_sign_funding,,their_pubkey,struct pubkey @@ -58,8 +58,8 @@ hsm_node_announcement_sig_reply,,signature,secp256k1_ecdsa_signature # Sign a withdrawal request hsm_sign_withdrawal,7 -hsm_sign_withdrawal,,satoshi_out,u64 -hsm_sign_withdrawal,,change_out,u64 +hsm_sign_withdrawal,,satoshi_out,struct amount_sat +hsm_sign_withdrawal,,change_out,struct amount_sat hsm_sign_withdrawal,,change_keyindex,u32 hsm_sign_withdrawal,,scriptpubkey_len,u16 hsm_sign_withdrawal,,scriptpubkey,scriptpubkey_len*u8 @@ -107,7 +107,7 @@ hsm_sign_commitment_tx,,peer_id,struct pubkey hsm_sign_commitment_tx,,channel_dbid,u64 hsm_sign_commitment_tx,,tx,struct bitcoin_tx hsm_sign_commitment_tx,,remote_funding_key,struct pubkey -hsm_sign_commitment_tx,,funding_amount,u64 +hsm_sign_commitment_tx,,funding_amount,struct amount_sat hsm_sign_commitment_tx_reply,105 hsm_sign_commitment_tx_reply,,sig,struct bitcoin_signature @@ -120,21 +120,21 @@ hsm_sign_delayed_payment_to_us,,commit_num,u64 hsm_sign_delayed_payment_to_us,,tx,struct bitcoin_tx hsm_sign_delayed_payment_to_us,,wscript_len,u16 hsm_sign_delayed_payment_to_us,,wscript,wscript_len*u8 -hsm_sign_delayed_payment_to_us,,input_amount,u64 +hsm_sign_delayed_payment_to_us,,input_amount,struct amount_sat hsm_sign_remote_htlc_to_us,13 hsm_sign_remote_htlc_to_us,,remote_per_commitment_point,struct pubkey hsm_sign_remote_htlc_to_us,,tx,struct bitcoin_tx hsm_sign_remote_htlc_to_us,,wscript_len,u16 hsm_sign_remote_htlc_to_us,,wscript,wscript_len*u8 -hsm_sign_remote_htlc_to_us,,input_amount,u64 +hsm_sign_remote_htlc_to_us,,input_amount,struct amount_sat hsm_sign_penalty_to_us,14 hsm_sign_penalty_to_us,,revocation_secret,struct secret hsm_sign_penalty_to_us,,tx,struct bitcoin_tx hsm_sign_penalty_to_us,,wscript_len,u16 hsm_sign_penalty_to_us,,wscript,wscript_len*u8 -hsm_sign_penalty_to_us,,input_amount,u64 +hsm_sign_penalty_to_us,,input_amount,struct amount_sat # Onchaind asks HSM to sign a local HTLC success or HTLC timeout tx. hsm_sign_local_htlc_tx,16 @@ -142,27 +142,27 @@ hsm_sign_local_htlc_tx,,commit_num,u64 hsm_sign_local_htlc_tx,,tx,struct bitcoin_tx hsm_sign_local_htlc_tx,,wscript_len,u16 hsm_sign_local_htlc_tx,,wscript,wscript_len*u8 -hsm_sign_local_htlc_tx,,input_amount,u64 +hsm_sign_local_htlc_tx,,input_amount,struct amount_sat # Openingd/channeld asks HSM to sign the other sides' commitment tx. hsm_sign_remote_commitment_tx,19 hsm_sign_remote_commitment_tx,,tx,struct bitcoin_tx hsm_sign_remote_commitment_tx,,remote_funding_key,struct pubkey -hsm_sign_remote_commitment_tx,,funding_amount,u64 +hsm_sign_remote_commitment_tx,,funding_amount,struct amount_sat # channeld asks HSM to sign remote HTLC tx. hsm_sign_remote_htlc_tx,20 hsm_sign_remote_htlc_tx,,tx,struct bitcoin_tx hsm_sign_remote_htlc_tx,,len,u16 hsm_sign_remote_htlc_tx,,wscript,len*u8 -hsm_sign_remote_htlc_tx,,amounts_satoshi,u64 +hsm_sign_remote_htlc_tx,,amounts_satoshi,struct amount_sat hsm_sign_remote_htlc_tx,,remote_per_commit_point,struct pubkey # closingd asks HSM to sign mutual close tx. hsm_sign_mutual_close_tx,21 hsm_sign_mutual_close_tx,,tx,struct bitcoin_tx hsm_sign_mutual_close_tx,,remote_funding_key,struct pubkey -hsm_sign_mutual_close_tx,,funding_amount,u64 +hsm_sign_mutual_close_tx,,funding,struct amount_sat # Reply for all the above requests. hsm_sign_tx_reply,112 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index b39f9de91e6c..1a079f6977b3 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -654,8 +654,7 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, secp256k1_ecdsa_signature sig; struct short_channel_id scid; u32 timestamp, fee_base_msat, fee_proportional_mill; - u64 htlc_minimum_msat; - u64 htlc_maximum_msat; + struct amount_msat htlc_minimum, htlc_maximum; u8 message_flags, channel_flags; u16 cltv_expiry_delta; struct bitcoin_blkid chain_hash; @@ -667,8 +666,8 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, if (!fromwire_channel_update_option_channel_htlc_max(cu, &sig, &chain_hash, &scid, ×tamp, &message_flags, &channel_flags, &cltv_expiry_delta, - &htlc_minimum_msat, &fee_base_msat, - &fee_proportional_mill, &htlc_maximum_msat)) { + &htlc_minimum, &fee_base_msat, + &fee_proportional_mill, &htlc_maximum)) { return bad_req_fmt(conn, c, msg_in, "Bad inner channel_update"); } if (tal_count(cu) < offset) @@ -682,9 +681,9 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, cu = towire_channel_update_option_channel_htlc_max(tmpctx, &sig, &chain_hash, &scid, timestamp, message_flags, channel_flags, - cltv_expiry_delta, htlc_minimum_msat, + cltv_expiry_delta, htlc_minimum, fee_base_msat, fee_proportional_mill, - htlc_maximum_msat); + htlc_maximum); return req_reply(conn, c, take(towire_hsm_cupdate_sig_reply(NULL, cu))); } @@ -728,7 +727,8 @@ static struct io_plan *handle_sign_commitment_tx(struct io_conn *conn, const u8 *msg_in) { struct pubkey peer_id, remote_funding_pubkey, local_funding_pubkey; - u64 dbid, funding_amount; + u64 dbid; + struct amount_sat funding; struct secret channel_seed; struct bitcoin_tx *tx; struct bitcoin_signature sig; @@ -739,7 +739,7 @@ static struct io_plan *handle_sign_commitment_tx(struct io_conn *conn, &peer_id, &dbid, &tx, &remote_funding_pubkey, - &funding_amount)) + &funding)) return bad_req(conn, c, msg_in); get_channel_seed(&peer_id, dbid, &channel_seed); @@ -758,7 +758,7 @@ static struct io_plan *handle_sign_commitment_tx(struct io_conn *conn, * pointer, as we don't always know it (and zero is a valid amount, so * NULL is better to mean 'unknown' and has the nice property that * you'll crash if you assume it's there and you're wrong. */ - tx->input[0].amount = tal_dup(tx->input, u64, &funding_amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); sign_tx_input(tx, 0, NULL, funding_wscript, &secrets.funding_privkey, &local_funding_pubkey, @@ -782,7 +782,7 @@ static struct io_plan *handle_sign_remote_commitment_tx(struct io_conn *conn, const u8 *msg_in) { struct pubkey remote_funding_pubkey, local_funding_pubkey; - u64 funding_amount; + struct amount_sat funding; struct secret channel_seed; struct bitcoin_tx *tx; struct bitcoin_signature sig; @@ -792,7 +792,7 @@ static struct io_plan *handle_sign_remote_commitment_tx(struct io_conn *conn, if (!fromwire_hsm_sign_remote_commitment_tx(tmpctx, msg_in, &tx, &remote_funding_pubkey, - &funding_amount)) + &funding)) bad_req(conn, c, msg_in); get_channel_seed(&c->id, c->dbid, &channel_seed); @@ -803,7 +803,7 @@ static struct io_plan *handle_sign_remote_commitment_tx(struct io_conn *conn, &local_funding_pubkey, &remote_funding_pubkey); /* Need input amount for signing */ - tx->input[0].amount = tal_dup(tx->input, u64, &funding_amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); sign_tx_input(tx, 0, NULL, funding_wscript, &secrets.funding_privkey, &local_funding_pubkey, @@ -825,7 +825,7 @@ static struct io_plan *handle_sign_remote_htlc_tx(struct io_conn *conn, struct secrets secrets; struct basepoints basepoints; struct pubkey remote_per_commit_point; - u64 amount; + struct amount_sat amount; u8 *wscript; struct privkey htlc_privkey; struct pubkey htlc_pubkey; @@ -852,7 +852,7 @@ static struct io_plan *handle_sign_remote_htlc_tx(struct io_conn *conn, "Failed deriving htlc pubkey"); /* Need input amount for signing */ - tx->input[0].amount = tal_dup(tx->input, u64, &amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &amount); sign_tx_input(tx, 0, NULL, wscript, &htlc_privkey, &htlc_pubkey, SIGHASH_ALL, &sig); @@ -868,7 +868,7 @@ static struct io_plan *handle_sign_to_us_tx(struct io_conn *conn, struct bitcoin_tx *tx, const struct privkey *privkey, const u8 *wscript, - u64 input_amount) + struct amount_sat input_sat) { struct bitcoin_signature sig; struct pubkey pubkey; @@ -879,7 +879,7 @@ static struct io_plan *handle_sign_to_us_tx(struct io_conn *conn, if (tal_count(tx->input) != 1) return bad_req_fmt(conn, c, msg_in, "bad txinput count"); - tx->input[0].amount = tal_dup(tx->input, u64, &input_amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &input_sat); sign_tx_input(tx, 0, NULL, wscript, privkey, &pubkey, SIGHASH_ALL, &sig); return req_reply(conn, c, take(towire_hsm_sign_tx_reply(NULL, &sig))); @@ -893,7 +893,8 @@ static struct io_plan *handle_sign_delayed_payment_to_us(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 commit_num, input_amount; + u64 commit_num; + struct amount_sat input_sat; struct secret channel_seed, basepoint_secret; struct pubkey basepoint; struct bitcoin_tx *tx; @@ -906,7 +907,7 @@ static struct io_plan *handle_sign_delayed_payment_to_us(struct io_conn *conn, if (!fromwire_hsm_sign_delayed_payment_to_us(tmpctx, msg_in, &commit_num, &tx, &wscript, - &input_amount)) + &input_sat)) return bad_req(conn, c, msg_in); get_channel_seed(&c->id, c->dbid, &channel_seed); @@ -939,7 +940,7 @@ static struct io_plan *handle_sign_delayed_payment_to_us(struct io_conn *conn, return bad_req_fmt(conn, c, msg_in, "failed deriving privkey"); return handle_sign_to_us_tx(conn, c, msg_in, - tx, &privkey, wscript, input_amount); + tx, &privkey, wscript, input_sat); } /*~ This is used when the a commitment transaction is onchain, and has an HTLC @@ -949,7 +950,7 @@ static struct io_plan *handle_sign_remote_htlc_to_us(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 input_amount; + struct amount_sat input_sat; struct secret channel_seed, htlc_basepoint_secret; struct pubkey htlc_basepoint; struct bitcoin_tx *tx; @@ -960,7 +961,7 @@ static struct io_plan *handle_sign_remote_htlc_to_us(struct io_conn *conn, if (!fromwire_hsm_sign_remote_htlc_to_us(tmpctx, msg_in, &remote_per_commitment_point, &tx, &wscript, - &input_amount)) + &input_sat)) return bad_req(conn, c, msg_in); get_channel_seed(&c->id, c->dbid, &channel_seed); @@ -978,7 +979,7 @@ static struct io_plan *handle_sign_remote_htlc_to_us(struct io_conn *conn, "Failed deriving htlc privkey"); return handle_sign_to_us_tx(conn, c, msg_in, - tx, &privkey, wscript, input_amount); + tx, &privkey, wscript, input_sat); } /*~ This is used when the remote peer's commitment transaction is revoked; @@ -988,7 +989,7 @@ static struct io_plan *handle_sign_penalty_to_us(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 input_amount; + struct amount_sat input_sat; struct secret channel_seed, revocation_secret, revocation_basepoint_secret; struct pubkey revocation_basepoint; struct bitcoin_tx *tx; @@ -999,7 +1000,7 @@ static struct io_plan *handle_sign_penalty_to_us(struct io_conn *conn, if (!fromwire_hsm_sign_penalty_to_us(tmpctx, msg_in, &revocation_secret, &tx, &wscript, - &input_amount)) + &input_sat)) return bad_req(conn, c, msg_in); if (!pubkey_from_secret(&revocation_secret, &point)) @@ -1021,7 +1022,7 @@ static struct io_plan *handle_sign_penalty_to_us(struct io_conn *conn, "Failed deriving revocation privkey"); return handle_sign_to_us_tx(conn, c, msg_in, - tx, &privkey, wscript, input_amount); + tx, &privkey, wscript, input_sat); } /*~ This is used when the a commitment transaction is onchain, and has an HTLC @@ -1031,7 +1032,8 @@ static struct io_plan *handle_sign_local_htlc_tx(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 commit_num, input_amount; + u64 commit_num; + struct amount_sat input_sat; struct secret channel_seed, htlc_basepoint_secret; struct sha256 shaseed; struct pubkey per_commitment_point, htlc_basepoint; @@ -1043,7 +1045,7 @@ static struct io_plan *handle_sign_local_htlc_tx(struct io_conn *conn, if (!fromwire_hsm_sign_local_htlc_tx(tmpctx, msg_in, &commit_num, &tx, &wscript, - &input_amount)) + &input_sat)) return bad_req(conn, c, msg_in); get_channel_seed(&c->id, c->dbid, &channel_seed); @@ -1076,7 +1078,7 @@ static struct io_plan *handle_sign_local_htlc_tx(struct io_conn *conn, return bad_req_fmt(conn, c, msg_in, "bad txinput count"); /* FIXME: Check that output script is correct! */ - tx->input[0].amount = tal_dup(tx->input, u64, &input_amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &input_sat); sign_tx_input(tx, 0, NULL, wscript, &htlc_privkey, &htlc_pubkey, SIGHASH_ALL, &sig); @@ -1171,13 +1173,13 @@ static struct io_plan *handle_sign_mutual_close_tx(struct io_conn *conn, struct pubkey remote_funding_pubkey, local_funding_pubkey; struct bitcoin_signature sig; struct secrets secrets; - u64 funding_amount; + struct amount_sat funding; const u8 *funding_wscript; if (!fromwire_hsm_sign_mutual_close_tx(tmpctx, msg_in, &tx, &remote_funding_pubkey, - &funding_amount)) + &funding)) return bad_req(conn, c, msg_in); /* FIXME: We should know dust level, decent fee range and @@ -1191,7 +1193,7 @@ static struct io_plan *handle_sign_mutual_close_tx(struct io_conn *conn, &local_funding_pubkey, &remote_funding_pubkey); /* Need input amount for signing */ - tx->input[0].amount = tal_dup(tx->input, u64, &funding_amount); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &funding); sign_tx_input(tx, 0, NULL, funding_wscript, &secrets.funding_privkey, &local_funding_pubkey, @@ -1363,7 +1365,7 @@ static struct io_plan *handle_sign_funding_tx(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 satoshi_out, change_out; + struct amount_sat satoshi_out, change_out; u32 change_keyindex; struct pubkey local_pubkey, remote_pubkey; struct utxo **utxos; @@ -1378,7 +1380,7 @@ static struct io_plan *handle_sign_funding_tx(struct io_conn *conn, &remote_pubkey, &utxos)) return bad_req(conn, c, msg_in); - if (change_out) { + if (amount_sat_greater(change_out, AMOUNT_SAT(0))) { changekey = tal(tmpctx, struct pubkey); bitcoin_key(NULL, changekey, change_keyindex); } else @@ -1405,7 +1407,7 @@ static struct io_plan *handle_sign_withdrawal_tx(struct io_conn *conn, struct client *c, const u8 *msg_in) { - u64 satoshi_out, change_out; + struct amount_sat satoshi_out, change_out; u32 change_keyindex; struct utxo **utxos; struct bitcoin_tx *tx; diff --git a/lightningd/Makefile b/lightningd/Makefile index a02438f8457f..16b9bed6abc7 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -14,6 +14,7 @@ default: lightningd-all # Common source we use. LIGHTNINGD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 949e22dfb1f1..abcac21ae401 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -558,7 +558,7 @@ static bool process_gettxout(struct bitcoin_cli *bcli) bcli_args(tmpctx, bcli), (int)bcli->output_bytes, bcli->output); - if (!json_to_bitcoin_amount(bcli->output, valuetok, &out.amount)) + if (!json_to_bitcoin_amount(bcli->output, valuetok, &out.amount.satoshis)) /* Raw: talking to bitcoind */ fatal("%s: had bad value (%.*s)?", bcli_args(tmpctx, bcli), (int)bcli->output_bytes, bcli->output); diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 2e69ec41edef..2a606275a4fe 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -60,7 +60,7 @@ static bool we_broadcast(const struct chain_topology *topo, static void filter_block_txs(struct chain_topology *topo, struct block *b) { size_t i; - u64 satoshi_owned; + struct amount_sat owned; /* Now we see if any of those txs are interesting. */ for (i = 0; i < tal_count(b->full_txs); i++) { @@ -83,17 +83,17 @@ static void filter_block_txs(struct chain_topology *topo, struct block *b) } } - satoshi_owned = 0; + owned = AMOUNT_SAT(0); if (txfilter_match(topo->bitcoind->ld->owned_txfilter, tx)) { wallet_extract_owned_outputs(topo->bitcoind->ld->wallet, tx, &b->height, - &satoshi_owned); + &owned); } /* We did spends first, in case that tells us to watch tx. */ bitcoin_txid(tx, &txid); if (watching_txid(topo, &txid) || we_broadcast(topo, &txid) || - satoshi_owned != 0) { + !amount_sat_eq(owned, AMOUNT_SAT(0))) { wallet_transaction_add(topo->ld->wallet, tx, b->height, i); } @@ -200,8 +200,8 @@ static void broadcast_done(struct bitcoind *bitcoind, /* No longer needs to be disconnected if channel dies. */ tal_del_destructor2(otx->channel, clear_otx_channel, otx); - if (otx->failed && exitstatus != 0) { - otx->failed(otx->channel, exitstatus, msg); + if (otx->failed_or_success) { + otx->failed_or_success(otx->channel, exitstatus, msg); tal_free(otx); } else { /* For continual rebroadcasting, until channel freed. */ @@ -213,7 +213,7 @@ static void broadcast_done(struct bitcoind *bitcoind, void broadcast_tx(struct chain_topology *topo, struct channel *channel, const struct bitcoin_tx *tx, - void (*failed)(struct channel *channel, + void (*failed_or_success)(struct channel *channel, int exitstatus, const char *err)) { /* Channel might vanish: topo owns it to start with. */ @@ -223,7 +223,7 @@ void broadcast_tx(struct chain_topology *topo, otx->channel = channel; bitcoin_txid(tx, &otx->txid); otx->hextx = tal_hex(otx, rawtx); - otx->failed = failed; + otx->failed_or_success = failed_or_success; tal_free(rawtx); tal_add_destructor2(channel, clear_otx_channel, otx); diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index a2729d0bf3ee..6cd173e2695a 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -37,7 +37,7 @@ struct outgoing_tx { struct channel *channel; const char *hextx; struct bitcoin_txid txid; - void (*failed)(struct channel *channel, int exitstatus, const char *err); + void (*failed_or_success)(struct channel *channel, int exitstatus, const char *err); }; struct block { diff --git a/lightningd/channel.c b/lightningd/channel.c index 109aac274733..d429fb1685bb 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -145,14 +145,14 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u64 next_htlc_id, const struct bitcoin_txid *funding_txid, u16 funding_outnum, - u64 funding_satoshi, - u64 push_msat, + struct amount_sat funding, + struct amount_msat push, bool remote_funding_locked, /* NULL or stolen */ struct short_channel_id *scid, - u64 our_msatoshi, - u64 msatoshi_to_us_min, - u64 msatoshi_to_us_max, + struct amount_msat our_msat, + struct amount_msat msat_to_us_min, + struct amount_msat msat_to_us_max, /* Stolen */ struct bitcoin_tx *last_tx, const struct bitcoin_signature *last_sig, @@ -195,7 +195,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, if (!log) { /* FIXME: update log prefix when we get scid */ /* FIXME: Use minimal unique pubkey prefix for logs! */ - char *idname = type_to_string(peer, struct pubkey, &peer->id); + const char *idname = type_to_string(peer, struct pubkey, &peer->id); channel->log = new_log(channel, peer->log_book, "%s chan #%"PRIu64":", idname, dbid); @@ -210,13 +210,13 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->next_htlc_id = next_htlc_id; channel->funding_txid = *funding_txid; channel->funding_outnum = funding_outnum; - channel->funding_satoshi = funding_satoshi; - channel->push_msat = push_msat; + channel->funding = funding; + channel->push = push; channel->remote_funding_locked = remote_funding_locked; channel->scid = tal_steal(channel, scid); - channel->our_msatoshi = our_msatoshi; - channel->msatoshi_to_us_min = msatoshi_to_us_min; - channel->msatoshi_to_us_max = msatoshi_to_us_max; + channel->our_msat = our_msat; + channel->msat_to_us_min = msat_to_us_min; + channel->msat_to_us_max = msat_to_us_max; channel->last_tx = tal_steal(channel, last_tx); channel->last_sig = *last_sig; channel->last_htlc_sigs = tal_steal(channel, last_htlc_sigs); diff --git a/lightningd/channel.h b/lightningd/channel.h index 09759c9dfae1..fb5b6176dc39 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -59,16 +59,17 @@ struct channel { /* Funding txid and amounts */ struct bitcoin_txid funding_txid; u16 funding_outnum; - u64 funding_satoshi, push_msat; + struct amount_sat funding; + struct amount_msat push; bool remote_funding_locked; /* Channel if locked locally. */ struct short_channel_id *scid; /* Amount going to us, not counting unfinished HTLCs; if we have one. */ - u64 our_msatoshi; + struct amount_msat our_msat; /* Statistics for min and max our_msatoshi. */ - u64 msatoshi_to_us_min; - u64 msatoshi_to_us_max; + struct amount_msat msat_to_us_min; + struct amount_msat msat_to_us_max; /* Timer we use in case they don't add an HTLC in a timely manner. */ struct oneshot *htlc_timeout; @@ -127,14 +128,14 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u64 next_htlc_id, const struct bitcoin_txid *funding_txid, u16 funding_outnum, - u64 funding_satoshi, - u64 push_msat, + struct amount_sat funding, + struct amount_msat push, bool remote_funding_locked, /* NULL or stolen */ struct short_channel_id *scid, - u64 our_msatoshi, - u64 msatoshi_to_us_min, - u64 msatoshi_to_us_max, + struct amount_msat our_msatoshi, + struct amount_msat msatoshi_to_us_min, + struct amount_msat msatoshi_to_us_max, /* Stolen */ struct bitcoin_tx *last_tx, const struct bitcoin_signature *last_sig, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 8e3e0b932635..98dcba45be14 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -339,7 +339,7 @@ void peer_start_channeld(struct channel *channel, &get_chainparams(ld)->genesis_blockhash, &channel->funding_txid, channel->funding_outnum, - channel->funding_satoshi, + channel->funding, &channel->our_config, &channel->channel_info.their_config, channel->channel_info.feerate_per_kw, @@ -354,7 +354,7 @@ void peer_start_channeld(struct channel *channel, channel->funder, cfg->fee_base, cfg->fee_per_satoshi, - channel->our_msatoshi, + channel->our_msat, &channel->local_basepoints, &channel->local_funding_pubkey, &ld->id, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index ad3a50fa24d9..54a23c342dce 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -17,6 +17,19 @@ #include #include +static struct amount_sat calc_tx_fee(struct amount_sat sat_in, + const struct bitcoin_tx *tx) +{ + struct amount_sat fee = sat_in; + for (size_t i = 0; i < tal_count(tx->output); i++) { + if (!amount_sat_sub(&fee, fee, tx->output[i].amount)) + fatal("Tx spends more than input %s? %s", + type_to_string(tmpctx, struct amount_sat, &sat_in), + type_to_string(tmpctx, struct bitcoin_tx, tx)); + } + return fee; +} + /* Is this better than the last tx we were holding? This can happen * even without closingd misbehaving, if we have multiple, * interrupted, rounds of negotiation. */ @@ -24,22 +37,19 @@ static bool better_closing_fee(struct lightningd *ld, struct channel *channel, const struct bitcoin_tx *tx) { - u64 weight, fee, last_fee, min_fee; + struct amount_sat fee, last_fee, min_fee; + u64 weight; u32 min_feerate; - size_t i; bool feerate_unknown; /* Calculate actual fee (adds in eliminated outputs) */ - fee = channel->funding_satoshi; - for (i = 0; i < tal_count(tx->output); i++) - fee -= tx->output[i].amount; + fee = calc_tx_fee(channel->funding, tx); + last_fee = calc_tx_fee(channel->funding, channel->last_tx); - last_fee = channel->funding_satoshi; - for (i = 0; i < tal_count(channel->last_tx->output); i++) - last_fee -= channel->last_tx->output[i].amount; - - log_debug(channel->log, "Their actual closing tx fee is %"PRIu64 - " vs previous %"PRIu64, fee, last_fee); + log_debug(channel->log, "Their actual closing tx fee is %s" + " vs previous %s", + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct amount_sat, &last_fee)); /* Weight once we add in sigs. */ weight = measure_tx_weight(tx) + 74 * 2; @@ -47,11 +57,12 @@ static bool better_closing_fee(struct lightningd *ld, /* If we don't have a feerate estimate, this gives feerate_floor */ min_feerate = feerate_min(ld, &feerate_unknown); - min_fee = min_feerate * weight / 1000; - if (fee < min_fee) { - log_debug(channel->log, "... That's below our min %"PRIu64 - " for weight %"PRIu64" at feerate %u", - min_fee, weight, min_feerate); + min_fee = amount_tx_fee(min_feerate, weight); + if (amount_sat_less(fee, min_fee)) { + log_debug(channel->log, "... That's below our min %s" + " for weight %"PRIu64" at feerate %u", + type_to_string(tmpctx, struct amount_sat, &fee), + weight, min_feerate); return false; } @@ -60,10 +71,10 @@ static bool better_closing_fee(struct lightningd *ld, /* If we don't know the feerate, prefer higher fee. */ if (feerate_unknown) - return fee >= last_fee; + return amount_sat_greater_eq(fee, last_fee); /* Otherwise prefer lower fee. */ - return fee <= last_fee; + return amount_sat_less_eq(fee, last_fee); } static void peer_received_closing_signature(struct channel *channel, @@ -143,9 +154,9 @@ void peer_start_closingd(struct channel *channel, { u8 *initmsg; u32 feerate; - u64 minfee, startfee, feelimit; + struct amount_sat minfee, startfee, feelimit; u64 num_revocations; - u64 funding_msatoshi, our_msatoshi, their_msatoshi; + struct amount_msat their_msat; int hsmfd; struct lightningd *ld = channel->peer->ld; @@ -184,11 +195,11 @@ void peer_start_closingd(struct channel *channel, * fee of the final commitment transaction, as calculated in * [BOLT #3](03-transactions.md#fee-calculation). */ - feelimit = commit_tx_base_fee_sat(channel->channel_info.feerate_per_kw[LOCAL], - 0); + feelimit = commit_tx_base_fee(channel->channel_info.feerate_per_kw[LOCAL], + 0); /* Pick some value above slow feerate (or min possible if unknown) */ - minfee = commit_tx_base_fee_sat(feerate_min(ld, NULL), 0); + minfee = commit_tx_base_fee(feerate_min(ld, NULL), 0); /* If we can't determine feerate, start at half unilateral feerate. */ feerate = mutual_close_feerate(ld->topology); @@ -197,11 +208,11 @@ void peer_start_closingd(struct channel *channel, if (feerate < feerate_floor()) feerate = feerate_floor(); } - startfee = commit_tx_base_fee_sat(feerate, 0); + startfee = commit_tx_base_fee(feerate, 0); - if (startfee > feelimit) + if (amount_sat_greater(startfee, feelimit)) startfee = feelimit; - if (minfee > feelimit) + if (amount_sat_greater(minfee, feelimit)) minfee = feelimit; num_revocations @@ -212,22 +223,28 @@ void peer_start_closingd(struct channel *channel, * Each node offering a signature: * - MUST round each output down to whole satoshis. */ - /* Convert unit */ - funding_msatoshi = channel->funding_satoshi * 1000; /* What is not ours is theirs */ - our_msatoshi = channel->our_msatoshi; - their_msatoshi = funding_msatoshi - our_msatoshi; + if (!amount_sat_sub_msat(&their_msat, + channel->funding, channel->our_msat)) { + log_broken(channel->log, "our_msat overflow funding %s minus %s", + type_to_string(tmpctx, struct amount_sat, + &channel->funding), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + channel_fail_permanent(channel, "our_msat overflow on closing"); + return; + } initmsg = towire_closing_init(tmpctx, cs, &channel->funding_txid, channel->funding_outnum, - channel->funding_satoshi, + channel->funding, &channel->local_funding_pubkey, &channel->channel_info.remote_fundingkey, channel->funder, - our_msatoshi / 1000, /* Rounds down */ - their_msatoshi / 1000, /* Rounds down */ - channel->our_config.dust_limit_satoshis, + amount_msat_to_sat_round_down(channel->our_msat), + amount_msat_to_sat_round_down(their_msat), + channel->our_config.dust_limit, minfee, feelimit, startfee, p2wpkh_for_keyidx(tmpctx, ld, channel->final_key_idx), diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 1ff7af914972..8e7cd5f5430f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -41,20 +42,20 @@ static void got_txout(struct bitcoind *bitcoind, struct short_channel_id *scid) { const u8 *script; - u64 satoshis; + struct amount_sat sat; /* output will be NULL if it wasn't found */ if (output) { script = output->script; - satoshis = output->amount; + sat = output->amount; } else { script = NULL; - satoshis = 0; + sat = AMOUNT_SAT(0); } subd_send_msg( bitcoind->ld->gossip, - towire_gossip_get_txout_reply(scid, scid, satoshis, script)); + towire_gossip_get_txout_reply(scid, scid, sat, script)); tal_free(scid); } @@ -77,7 +78,7 @@ static void get_txout(struct subd *gossip, const u8 *msg) if (op) { subd_send_msg(gossip, towire_gossip_get_txout_reply( - scid, scid, op->satoshis, op->scriptpubkey)); + scid, scid, op->sat, op->scriptpubkey)); tal_free(scid); } else if (blockheight >= topo->min_blockheight && blockheight <= topo->max_blockheight) { @@ -86,7 +87,7 @@ static void get_txout(struct subd *gossip, const u8 *msg) * this is either a spent outpoint or an invalid one. Return a * failure. */ subd_send_msg(gossip, take(towire_gossip_get_txout_reply( - NULL, scid, 0, NULL))); + NULL, scid, AMOUNT_SAT(0), NULL))); tal_free(scid); } else { bitcoind_getoutput(topo->bitcoind, @@ -111,8 +112,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_GET_CHANNEL_PEER: case WIRE_GOSSIP_GET_TXOUT_REPLY: case WIRE_GOSSIP_OUTPOINT_SPENT: - case WIRE_GOSSIP_ROUTING_FAILURE: - case WIRE_GOSSIP_MARK_CHANNEL_UNROUTABLE: + case WIRE_GOSSIP_PAYMENT_FAILURE: case WIRE_GOSSIP_QUERY_SCIDS: case WIRE_GOSSIP_QUERY_CHANNEL_RANGE: case WIRE_GOSSIP_SEND_TIMESTAMP_FILTER: @@ -301,7 +301,7 @@ static struct command_result *json_getroute(struct command *cmd, struct pubkey *destination; struct pubkey *source; const jsmntok_t *excludetok; - u64 *msatoshi; + struct amount_msat *msat; unsigned *cltv; double *riskfactor; struct short_channel_id_dir *excluded; @@ -316,7 +316,7 @@ static struct command_result *json_getroute(struct command *cmd, if (!param(cmd, buffer, params, p_req("id", param_pubkey, &destination), - p_req("msatoshi", param_u64, &msatoshi), + p_req("msatoshi", param_msat, &msat), p_req("riskfactor", param_double, &riskfactor), p_opt_def("cltv", param_number, &cltv, 9), p_opt_def("fromid", param_pubkey, &source, ld->id), @@ -340,7 +340,8 @@ static struct command_result *json_getroute(struct command *cmd, json_for_each_arr(i, t, excludetok) { if (!short_channel_id_dir_from_str(buffer + t->start, t->end - t->start, - &excluded[i])) { + &excluded[i], + deprecated_apis)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "%.*s is not a valid" " short_channel_id/direction", @@ -353,7 +354,8 @@ static struct command_result *json_getroute(struct command *cmd, } u8 *req = towire_gossip_getroute_request(cmd, source, destination, - *msatoshi, *riskfactor * 1000, + *msat, + *riskfactor * 1000000.0, *cltv, fuzz, excluded, *max_hops); @@ -397,7 +399,8 @@ static void json_listchannels_reply(struct subd *gossip UNUSED, const u8 *reply, type_to_string(reply, struct short_channel_id, &entries[i].short_channel_id)); json_add_bool(response, "public", entries[i].public); - json_add_u64(response, "satoshis", entries[i].satoshis); + json_add_amount_sat(response, entries[i].sat, + "satoshis", "amount_msat"); json_add_num(response, "message_flags", entries[i].message_flags); json_add_num(response, "channel_flags", entries[i].channel_flags); /* Prior to spec v0891374d47ddffa64c5a2e6ad151247e3d6b7a59, these two were a single u16 field */ @@ -497,7 +500,8 @@ static struct command_result *json_dev_query_scids(struct command *cmd, scids = tal_arr(cmd, struct short_channel_id, scidstok->size); json_for_each_arr(i, t, scidstok) { - if (!json_to_short_channel_id(buffer, t, &scids[i])) { + if (!json_to_short_channel_id(buffer, t, &scids[i], + deprecated_apis)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "scid %zu '%.*s' is not an scid", i, json_tok_full_len(t), diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index 39155fbeafa6..e9c6935cc416 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -65,7 +65,7 @@ void fromwire_route_hop(const u8 **pptr, size_t *max, struct route_hop *entry) fromwire_pubkey(pptr, max, &entry->nodeid); fromwire_short_channel_id(pptr, max, &entry->channel_id); entry->direction = fromwire_u8(pptr, max); - entry->amount = fromwire_u64(pptr, max); + entry->amount = fromwire_amount_msat(pptr, max); entry->delay = fromwire_u32(pptr, max); } @@ -74,7 +74,7 @@ void towire_route_hop(u8 **pptr, const struct route_hop *entry) towire_pubkey(pptr, &entry->nodeid); towire_short_channel_id(pptr, &entry->channel_id); towire_u8(pptr, entry->direction); - towire_u64(pptr, entry->amount); + towire_amount_msat(pptr, entry->amount); towire_u32(pptr, entry->delay); } @@ -102,7 +102,7 @@ void fromwire_gossip_getchannels_entry(const u8 **pptr, size_t *max, fromwire_short_channel_id(pptr, max, &entry->short_channel_id); fromwire(pptr, max, entry->source, sizeof(entry->source)); fromwire(pptr, max, entry->destination, sizeof(entry->destination)); - entry->satoshis = fromwire_u64(pptr, max); + entry->sat = fromwire_amount_sat(pptr, max); entry->message_flags = fromwire_u8(pptr, max); entry->channel_flags = fromwire_u8(pptr, max); entry->public = fromwire_bool(pptr, max); @@ -119,7 +119,7 @@ void towire_gossip_getchannels_entry(u8 **pptr, towire_short_channel_id(pptr, &entry->short_channel_id); towire(pptr, entry->source, sizeof(entry->source)); towire(pptr, entry->destination, sizeof(entry->destination)); - towire_u64(pptr, entry->satoshis); + towire_amount_sat(pptr, entry->sat); towire_u8(pptr, entry->message_flags); towire_u8(pptr, entry->channel_flags); towire_bool(pptr, entry->public); diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h index a9de59961c5a..35c197ecc5be 100644 --- a/lightningd/gossip_msg.h +++ b/lightningd/gossip_msg.h @@ -24,7 +24,7 @@ struct gossip_getnodes_entry { struct gossip_getchannels_entry { /* These are raw to optimize marshaling: be careful! */ u8 source[sizeof(struct pubkey)], destination[sizeof(struct pubkey)]; - u64 satoshis; + struct amount_sat sat; struct short_channel_id short_channel_id; u8 message_flags; u8 channel_flags; diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index 25499ad9625e..9bd5949146a9 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -75,7 +75,7 @@ static void *PRINTF_FMT(2,3) struct htlc_in *htlc_in_check(const struct htlc_in *hin, const char *abortstr) { - if (hin->msatoshi == 0) + if (amount_msat_eq(hin->msat, AMOUNT_MSAT(0))) return corrupt(abortstr, "zero msatoshi"); else if (htlc_state_owner(hin->hstate) != REMOTE) return corrupt(abortstr, "invalid state %s", @@ -109,7 +109,7 @@ struct htlc_in *htlc_in_check(const struct htlc_in *hin, const char *abortstr) struct htlc_in *new_htlc_in(const tal_t *ctx, struct channel *channel, u64 id, - u64 msatoshi, u32 cltv_expiry, + struct amount_msat msat, u32 cltv_expiry, const struct sha256 *payment_hash, const struct secret *shared_secret TAKES, const u8 *onion_routing_packet) @@ -119,7 +119,7 @@ struct htlc_in *new_htlc_in(const tal_t *ctx, hin->dbid = 0; hin->key.channel = channel; hin->key.id = id; - hin->msatoshi = msatoshi; + hin->msat = msat; hin->cltv_expiry = cltv_expiry; hin->payment_hash = *payment_hash; if (shared_secret) @@ -150,10 +150,13 @@ struct htlc_out *htlc_out_check(const struct htlc_out *hout, return corrupt(abortstr, "Both origin and incoming"); if (hout->in) { - if (hout->in->msatoshi < hout->msatoshi) - return corrupt(abortstr, "Input msatoshi %"PRIu64 - " less than %"PRIu64, - hout->in->msatoshi, hout->msatoshi); + if (amount_msat_less(hout->in->msat, hout->msat)) + return corrupt(abortstr, "Input amount %s" + " less than %s", + type_to_string(tmpctx, struct amount_msat, + &hout->in->msat), + type_to_string(tmpctx, struct amount_msat, + &hout->msat)); if (hout->in->cltv_expiry <= hout->cltv_expiry) return corrupt(abortstr, "Input cltv_expiry %u" " less than %u", @@ -240,7 +243,8 @@ void htlc_out_connect_htlc_in(struct htlc_out *hout, struct htlc_in *hin) /* You need to set the ID, then connect_htlc_out this! */ struct htlc_out *new_htlc_out(const tal_t *ctx, struct channel *channel, - u64 msatoshi, u32 cltv_expiry, + struct amount_msat msat, + u32 cltv_expiry, const struct sha256 *payment_hash, const u8 *onion_routing_packet, bool am_origin, @@ -253,7 +257,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, hout->key.channel = channel; hout->key.id = HTLC_INVALID_ID; - hout->msatoshi = msatoshi; + hout->msat = msat; hout->cltv_expiry = cltv_expiry; hout->payment_hash = *payment_hash; memcpy(hout->onion_routing_packet, onion_routing_packet, diff --git a/lightningd/htlc_end.h b/lightningd/htlc_end.h index 128493632a8c..558924f28304 100644 --- a/lightningd/htlc_end.h +++ b/lightningd/htlc_end.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -22,7 +23,7 @@ struct htlc_in { * database. */ u64 dbid; struct htlc_key key; - u64 msatoshi; + struct amount_msat msat; u32 cltv_expiry; struct sha256 payment_hash; @@ -55,7 +56,7 @@ struct htlc_out { u64 dbid; u64 origin_htlc_id; struct htlc_key key; - u64 msatoshi; + struct amount_msat msat; u32 cltv_expiry; struct sha256 payment_hash; @@ -122,7 +123,7 @@ struct htlc_out *find_htlc_out(const struct htlc_out_map *map, /* You still need to connect_htlc_in this! */ struct htlc_in *new_htlc_in(const tal_t *ctx, struct channel *channel, u64 id, - u64 msatoshi, u32 cltv_expiry, + struct amount_msat msat, u32 cltv_expiry, const struct sha256 *payment_hash, const struct secret *shared_secret TAKES, const u8 *onion_routing_packet); @@ -130,7 +131,8 @@ struct htlc_in *new_htlc_in(const tal_t *ctx, /* You need to set the ID, then connect_htlc_out this! */ struct htlc_out *new_htlc_out(const tal_t *ctx, struct channel *channel, - u64 msatoshi, u32 cltv_expiry, + struct amount_msat msat, + u32 cltv_expiry, const struct sha256 *payment_hash, const u8 *onion_routing_packet, bool am_origin, diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 22fd967484cb..d4ed4aa642d4 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -45,13 +46,14 @@ static void json_add_invoice(struct json_stream *response, json_add_escaped_string(response, "label", inv->label); json_add_string(response, "bolt11", inv->bolt11); json_add_hex(response, "payment_hash", &inv->rhash, sizeof(inv->rhash)); - if (inv->msatoshi) - json_add_u64(response, "msatoshi", *inv->msatoshi); + if (inv->msat) + json_add_amount_msat(response, *inv->msat, + "msatoshi", "amount_msat"); json_add_string(response, "status", invoice_status_str(inv)); if (inv->state == PAID) { json_add_u64(response, "pay_index", inv->pay_index); - json_add_u64(response, "msatoshi_received", - inv->msatoshi_received); + json_add_amount_msat(response, inv->received, + "msatoshi_received", "amount_received_msat"); json_add_u64(response, "paid_at", inv->paid_timestamp); } @@ -141,7 +143,7 @@ static struct command_result *parse_fallback(struct command *cmd, /* BOLT11 struct wants an array of arrays (can provide multiple routes) */ static struct route_info **select_inchan(const tal_t *ctx, struct lightningd *ld, - u64 capacity_needed, + struct amount_msat capacity_needed, const struct route_info *inchans, bool *any_offline) { @@ -158,7 +160,7 @@ static struct route_info **select_inchan(const tal_t *ctx, for (size_t i = 0; i < tal_count(inchans); i++) { struct peer *peer; struct channel *c; - u64 msatoshi_avail; + struct amount_msat avail, excess; /* Do we know about this peer? */ peer = peer_by_id(ld, &inchans[i].pubkey); @@ -171,15 +173,22 @@ static struct route_info **select_inchan(const tal_t *ctx, continue; /* Does it have sufficient capacity. */ - msatoshi_avail = c->funding_satoshi * 1000 - c->our_msatoshi; + if (!amount_sat_sub_msat(&avail, c->funding, c->our_msat)) { + log_broken(ld->log, + "underflow: funding %s - our_msat %s", + type_to_string(tmpctx, struct amount_sat, + &c->funding), + type_to_string(tmpctx, struct amount_msat, + &c->our_msat)); + continue; + } /* Even after reserve taken into account */ - if (c->our_config.channel_reserve_satoshis * 1000 - > msatoshi_avail) + if (!amount_msat_sub_sat(&avail, + avail, c->our_config.channel_reserve)) continue; - msatoshi_avail -= c->our_config.channel_reserve_satoshis * 1000; - if (msatoshi_avail < capacity_needed) + if (!amount_msat_sub(&excess, avail, capacity_needed)) continue; /* Is it offline? */ @@ -189,9 +198,9 @@ static struct route_info **select_inchan(const tal_t *ctx, } /* Avoid divide-by-zero corner case. */ - wsum += (msatoshi_avail - capacity_needed + 1); + wsum += excess.millisatoshis + 1; /* Raw: rand select */ if (pseudorand(1ULL << 32) - <= ((msatoshi_avail - capacity_needed + 1) << 32) / wsum) + <= ((excess.millisatoshis + 1) << 32) / wsum) /* Raw: rand select */ r = &inchans[i]; } @@ -235,7 +244,7 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, info->b11->routes = select_inchan(info->b11, info->cmd->ld, - info->b11->msatoshi ? *info->b11->msatoshi : 1, + info->b11->msat ? *info->b11->msat : AMOUNT_MSAT(1), inchans, &any_offline); @@ -254,7 +263,7 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, if (!wallet_invoice_create(wallet, &invoice, - info->b11->msatoshi, + info->b11->msat, info->label, info->b11->expiry, b11enc, @@ -280,9 +289,11 @@ static void gossipd_incoming_channels_reply(struct subd *gossipd, /* Warn if there's not sufficient incoming capacity. */ if (tal_count(info->b11->routes) == 0) { log_unusual(info->cmd->ld->log, - "invoice: insufficient incoming capacity for %"PRIu64 - " msatoshis%s", - info->b11->msatoshi ? *info->b11->msatoshi : 0, + "invoice: insufficient incoming capacity for %s%s", + info->b11->msat + ? type_to_string(tmpctx, struct amount_msat, + info->b11->msat) + : "0", any_offline ? " (among currently connected peers)" : ""); @@ -325,7 +336,8 @@ static struct route_info *unpack_route(const tal_t *ctx, if (!json_to_pubkey(buffer, pubkey, &r->pubkey) || !json_to_short_channel_id(buffer, scid, - &r->short_channel_id) + &r->short_channel_id, + deprecated_apis) || !json_to_number(buffer, fee_base, &r->fee_base_msat) || !json_to_number(buffer, fee_prop, &r->fee_proportional_millionths) @@ -356,6 +368,27 @@ static struct route_info **unpack_routes(const tal_t *ctx, } #endif /* DEVELOPER */ +static struct command_result *param_msat_or_any(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct amount_msat **msat) +{ + if (json_tok_streq(buffer, tok, "any")) { + *msat = NULL; + return NULL; + } + *msat = tal(cmd, struct amount_msat); + if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start)) + return NULL; + + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be millisatoshis or 'any', not '%.*s'", + name, + tok->end - tok->start, + buffer + tok->start); +} + static struct command_result *json_invoice(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -363,13 +396,14 @@ static struct command_result *json_invoice(struct command *cmd, { const jsmntok_t *fallbacks; const jsmntok_t *preimagetok; - u64 *msatoshi_val; + struct amount_msat *msatoshi_val; struct invoice_info *info; const char *desc_val; const u8 **fallback_scripts = NULL; u64 *expiry; struct sha256 rhash; bool *exposeprivate; + const struct chainparams *chainparams; #if DEVELOPER const jsmntok_t *routes; #endif @@ -378,7 +412,7 @@ static struct command_result *json_invoice(struct command *cmd, info->cmd = cmd; if (!param(cmd, buffer, params, - p_req("msatoshi", param_msat, &msatoshi_val), + p_req("msatoshi", param_msat_or_any, &msatoshi_val), p_req("label", param_label, &info->label), p_req("description", param_escaped_string, &desc_val), p_opt_def("expiry", param_u64, &expiry, 3600), @@ -406,6 +440,15 @@ static struct command_result *json_invoice(struct command *cmd, strlen(desc_val)); } + chainparams = get_chainparams(cmd->ld); + if (msatoshi_val + && amount_msat_greater(*msatoshi_val, chainparams->max_payment)) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "msatoshi cannot exceed %s", + type_to_string(tmpctx, struct amount_msat, + &chainparams->max_payment)); + } + if (fallbacks) { size_t i; const jsmntok_t *t; @@ -436,9 +479,8 @@ static struct command_result *json_invoice(struct command *cmd, /* Generate preimage hash. */ sha256(&rhash, &info->payment_preimage, sizeof(info->payment_preimage)); - /* Construct bolt11 string. */ info->b11 = new_bolt11(info, msatoshi_val); - info->b11->chain = get_chainparams(cmd->ld); + info->b11->chain = chainparams; info->b11->timestamp = time_now().ts.tv_sec; info->b11->payment_hash = rhash; info->b11->receiver_id = cmd->ld->id; @@ -770,8 +812,9 @@ static struct command_result *json_decodepay(struct command *cmd, json_add_u64(response, "created_at", b11->timestamp); json_add_u64(response, "expiry", b11->expiry); json_add_pubkey(response, "payee", &b11->receiver_id); - if (b11->msatoshi) - json_add_u64(response, "msatoshi", *b11->msatoshi); + if (b11->msat) + json_add_amount_msat(response, *b11->msat, + "msatoshi", "amount_msat"); if (b11->description) { struct json_escaped *esc = json_escape(NULL, b11->description); json_add_escaped_string(response, "description", take(esc)); diff --git a/lightningd/json.c b/lightningd/json.c index cee09ff01797..70fdbaf21871 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ json_add_route_hop(struct json_stream *r, char const *n, json_add_short_channel_id(r, "channel", &h->channel_id); json_add_num(r, "direction", h->direction); - json_add_u64(r, "msatoshi", h->amount); + json_add_amount_msat(r, h->amount, "msatoshi", "amount_msat"); json_add_num(r, "delay", h->delay); json_object_end(r); } @@ -49,37 +50,6 @@ json_add_route(struct json_stream *r, char const *n, json_array_end(r); } -/* Outputs fields, not a separate object*/ -void -json_add_payment_fields(struct json_stream *response, - const struct wallet_payment *t) -{ - json_add_u64(response, "id", t->id); - json_add_hex(response, "payment_hash", &t->payment_hash, sizeof(t->payment_hash)); - json_add_pubkey(response, "destination", &t->destination); - json_add_u64(response, "msatoshi", t->msatoshi); - json_add_u64(response, "msatoshi_sent", t->msatoshi_sent); - json_add_u64(response, "created_at", t->timestamp); - - switch (t->status) { - case PAYMENT_PENDING: - json_add_string(response, "status", "pending"); - break; - case PAYMENT_COMPLETE: - json_add_string(response, "status", "complete"); - break; - case PAYMENT_FAILED: - json_add_string(response, "status", "failed"); - break; - } - if (t->payment_preimage) - json_add_hex(response, "payment_preimage", - t->payment_preimage, - sizeof(*t->payment_preimage)); - if (t->description) - json_add_string(response, "description", t->description); -} - void json_add_pubkey(struct json_stream *response, const char *fieldname, const struct pubkey *key) @@ -128,7 +98,8 @@ struct command_result *param_short_channel_id(struct command *cmd, struct short_channel_id **scid) { *scid = tal(cmd, struct short_channel_id); - if (json_to_short_channel_id(buffer, tok, *scid)) + if (json_to_short_channel_id(buffer, tok, *scid, + deprecated_apis)) return NULL; return command_fail(cmd, JSONRPC2_INVALID_PARAMS, @@ -316,7 +287,7 @@ void json_add_literal(struct json_stream *result, const char *fieldname, json_add_member(result, fieldname, "%.*s", len, literal); } -void json_add_string(struct json_stream *result, const char *fieldname, const char *value) +void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES) { struct json_escaped *esc = json_partial_escape(NULL, value); @@ -329,6 +300,11 @@ void json_add_bool(struct json_stream *result, const char *fieldname, bool value json_add_member(result, fieldname, value ? "true" : "false"); } +void json_add_null(struct json_stream *stream, const char *fieldname) +{ + json_add_member(stream, fieldname, "null"); +} + void json_add_hex(struct json_stream *result, const char *fieldname, const void *data, size_t len) { @@ -353,3 +329,25 @@ void json_add_escaped_string(struct json_stream *result, const char *fieldname, if (taken(esc)) tal_free(esc); } + +void json_add_amount_msat(struct json_stream *result, + struct amount_msat msat, + const char *rawfieldname, + const char *msatfieldname) +{ + json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ + json_add_member(result, msatfieldname, "\"%s\"", + type_to_string(tmpctx, struct amount_msat, &msat)); +} + +void json_add_amount_sat(struct json_stream *result, + struct amount_sat sat, + const char *rawfieldname, + const char *msatfieldname) +{ + struct amount_msat msat; + json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ + if (amount_sat_to_msat(&msat, sat)) + json_add_member(result, msatfieldname, "\"%s\"", + type_to_string(tmpctx, struct amount_msat, &msat)); +} diff --git a/lightningd/json.h b/lightningd/json.h index 8d02a7712cf2..69f54083c7b3 100644 --- a/lightningd/json.h +++ b/lightningd/json.h @@ -7,6 +7,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -33,11 +34,6 @@ struct wireaddr_internal; void json_add_route(struct json_stream *r, char const *n, const struct route_hop *hops, size_t hops_len); -/* Output the fields of a wallet payment. - * Should be used within an object context. */ -void json_add_payment_fields(struct json_stream *response, - const struct wallet_payment *t); - /* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ void json_add_pubkey(struct json_stream *response, const char *fieldname, @@ -120,6 +116,10 @@ void json_add_u64(struct json_stream *result, const char *fieldname, /* '"fieldname" : true|false' or 'true|false' if fieldname is NULL */ void json_add_bool(struct json_stream *result, const char *fieldname, bool value); + +/* '"fieldname" : null' or 'null' if fieldname is NULL */ +void json_add_null(struct json_stream *stream, const char *fieldname); + /* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */ void json_add_hex(struct json_stream *result, const char *fieldname, const void *data, size_t len); @@ -128,6 +128,20 @@ void json_add_hex_talarr(struct json_stream *result, const char *fieldname, const tal_t *data); +/* Adds both a 'raw' number field and an 'amount_msat' field */ +void json_add_amount_msat(struct json_stream *result, + struct amount_msat msat, + const char *rawfieldname, + const char *msatfieldname) + NO_NULL_ARGS; + +/* Adds both a 'raw' number field and an 'amount_msat' field */ +void json_add_amount_sat(struct json_stream *result, + struct amount_sat sat, + const char *rawfieldname, + const char *msatfieldname) + NO_NULL_ARGS; + enum address_parse_result { /* Not recognized as an onchain address */ ADDRESS_PARSE_UNRECOGNIZED, @@ -143,9 +157,4 @@ enum address_parse_result json_tok_address_scriptpubkey(const tal_t *ctx, const struct chainparams *chainparams, const char *buffer, const jsmntok_t *tok, const u8 **scriptpubkey); - -/* Parse the satoshi token in wallet_tx. */ -struct command_result *param_wtx(struct wallet_tx * tx, const char * buffer, - const jsmntok_t * sattok, u64 max); - #endif /* LIGHTNING_LIGHTNINGD_JSON_H */ diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c index b90723d9100e..a26f77bae7cb 100644 --- a/lightningd/json_stream.c +++ b/lightningd/json_stream.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,9 @@ struct json_stream { void *reader_arg; size_t len_read; + /* Where to log I/O */ + struct log *log; + /* Current command's output. */ MEMBUF(char) outbuf; }; @@ -43,7 +47,9 @@ static void *membuf_tal_realloc(struct membuf *mb, return p; } -struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer) +struct json_stream *new_json_stream(const tal_t *ctx, + struct command *writer, + struct log *log) { struct json_stream *js = tal(ctx, struct json_stream); @@ -56,6 +62,7 @@ struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer) #endif js->indent = 0; js->empty = true; + js->log = log; return js; } @@ -109,6 +116,8 @@ static void js_written_some(struct json_stream *js) void json_stream_append_part(struct json_stream *js, const char *str, size_t len) { mkroom(js, len); + if (js->log) + log_io(js->log, LOG_IO_OUT, "", str, len); memcpy(membuf_add(&js->outbuf, len), str, len); js_written_some(js); } @@ -139,6 +148,9 @@ static void json_stream_append_vfmt(struct json_stream *js, mkroom(js, fmtlen + 1); vsprintf(membuf_space(&js->outbuf), fmt, ap2); } + if (js->log) + log_io(js->log, LOG_IO_OUT, "", + membuf_space(&js->outbuf), fmtlen); membuf_added(&js->outbuf, fmtlen); js_written_some(js); va_end(ap2); diff --git a/lightningd/json_stream.h b/lightningd/json_stream.h index 3e7e4f4949e2..f30f23f98006 100644 --- a/lightningd/json_stream.h +++ b/lightningd/json_stream.h @@ -15,13 +15,16 @@ struct command; struct io_conn; struct json_stream; +struct log; /** * new_json_stream - create a new JSON stream. * @ctx: tal context for allocation. * @writer: object responsible for writing to this stream. + * @log: where to log the IO */ -struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer); +struct json_stream *new_json_stream(const tal_t *ctx, struct command *writer, + struct log *log); /** * Duplicate an existing stream. @@ -82,8 +85,8 @@ void json_stream_append_part(struct json_stream *js, const char *str, * @js: the json_stream. * @fmt...: the printf-style format */ -void PRINTF_FMT(2,3) -json_stream_append_fmt(struct json_stream *js, const char *fmt, ...); +PRINTF_FMT(2,3) +void json_stream_append_fmt(struct json_stream *js, const char *fmt, ...); /** * json_add_member - add a generic member. @@ -91,9 +94,9 @@ json_stream_append_fmt(struct json_stream *js, const char *fmt, ...); * @fieldname: optional fieldname. * @fmt...: the printf-style format */ -void PRINTF_FMT(3,4) -json_add_member(struct json_stream *js, const char *fieldname, - const char *fmt, ...); +PRINTF_FMT(3,4) +void json_add_member(struct json_stream *js, const char *fieldname, + const char *fmt, ...); /** * json_stream_output - start writing out a json_stream to this conn. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index c6facb6420fd..88157e50e977 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,10 @@ struct jsonrpc { struct io_listener *rpc_listener; struct json_command **commands; struct log *log; + + /* Map from json command names to usage strings: we don't put this inside + * struct json_command as it's good practice to have those const. */ + STRMAP(const char *) usagemap; }; /* The command itself usually owns the stream, because jcon may get closed. @@ -114,7 +119,7 @@ static struct json_stream *jcon_new_json_stream(const tal_t *ctx, struct json_connection *jcon, struct command *writer) { - struct json_stream *js = new_json_stream(ctx, writer); + struct json_stream *js = new_json_stream(ctx, writer, jcon->log); /* Wake writer to start streaming, in case it's not already. */ io_wake(jcon); @@ -304,10 +309,11 @@ static void json_add_help_command(struct command *cmd, struct json_command *json_command) { char *usage; - cmd->mode = CMD_USAGE; - json_command->dispatch(cmd, NULL, NULL, NULL); - usage = tal_fmt(cmd, "%s %s", json_command->name, cmd->usage); + usage = tal_fmt(cmd, "%s %s", + json_command->name, + strmap_get(&cmd->ld->jsonrpc->usagemap, + json_command->name)); json_object_start(response, NULL); json_add_string(response, "command", usage); @@ -329,6 +335,17 @@ static void json_add_help_command(struct command *cmd, } +static const struct json_command *find_command(struct json_command **commands, + const char *buffer, + const jsmntok_t *cmdtok) +{ + for (size_t i = 0; i < tal_count(commands); i++) { + if (json_tok_streq(buffer, cmdtok, commands[i]->name)) + return commands[i]; + } + return NULL; +} + static struct command_result *json_help(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -336,37 +353,35 @@ static struct command_result *json_help(struct command *cmd, { struct json_stream *response; const jsmntok_t *cmdtok; - struct json_command **commands = cmd->ld->jsonrpc->commands; + struct json_command **commands; + const struct json_command *one_cmd; if (!param(cmd, buffer, params, p_opt("command", param_tok, &cmdtok), NULL)) return command_param_failed(); + commands = cmd->ld->jsonrpc->commands; if (cmdtok) { - for (size_t i = 0; i < tal_count(commands); i++) { - if (json_tok_streq(buffer, cmdtok, commands[i]->name)) { - response = json_stream_success(cmd); - json_add_help_command(cmd, response, commands[i]); - goto done; - } - } - return command_fail(cmd, JSONRPC2_METHOD_NOT_FOUND, - "Unknown command '%.*s'", - json_tok_full_len(cmdtok), - json_tok_full(buffer, cmdtok)); - } + one_cmd = find_command(commands, buffer, cmdtok); + if (!one_cmd) + return command_fail(cmd, JSONRPC2_METHOD_NOT_FOUND, + "Unknown command '%.*s'", + cmdtok->end - cmdtok->start, + buffer + cmdtok->start); + } else + one_cmd = NULL; response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "help"); - for (size_t i=0; ijcon) js = jcon_new_json_stream(cmd, cmd->jcon, cmd); else - js = new_json_stream(cmd, cmd); + js = new_json_stream(cmd, cmd, NULL); assert(!cmd->have_json_stream); cmd->have_json_stream = true; @@ -584,12 +599,11 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) "Expected string for method"); } - c->json_cmd = find_cmd(jcon->ld->jsonrpc, jcon->buffer, method); - if (!c->json_cmd) { - return command_fail(c, JSONRPC2_METHOD_NOT_FOUND, - "Unknown command '%.*s'", - json_tok_full_len(method), - json_tok_full(jcon->buffer, method)); + c->json_cmd = find_cmd(jcon->ld->jsonrpc, jcon->buffer, method); + if (!c->json_cmd) { + return command_fail( + c, JSONRPC2_METHOD_NOT_FOUND, "Unknown command '%.*s'", + method->end - method->start, jcon->buffer + method->start); } if (c->json_cmd->deprecated && !deprecated_apis) { return command_fail(c, JSONRPC2_METHOD_NOT_FOUND, @@ -762,7 +776,19 @@ static struct io_plan *incoming_jcon_connected(struct io_conn *conn, return jcon_connected(notleak(conn), ld); } -bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command) +static void destroy_json_command(struct json_command *command, struct jsonrpc *rpc) +{ + strmap_del(&rpc->usagemap, command->name, NULL); + for (size_t i = 0; i < tal_count(rpc->commands); i++) { + if (rpc->commands[i] == command) { + tal_arr_remove(&rpc->commands, i); + return; + } + } + abort(); +} + +static bool command_add(struct jsonrpc *rpc, struct json_command *command) { size_t count = tal_count(rpc->commands); @@ -775,30 +801,58 @@ bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command) return true; } -void jsonrpc_command_remove(struct jsonrpc *rpc, const char *method) +/* Built-in commands get called to construct usage string via param() */ +static void setup_command_usage(struct lightningd *ld, + struct json_command *command) { - for (size_t i=0; icommands); i++) { - struct json_command *cmd = rpc->commands[i]; - if (streq(cmd->name, method)) { - tal_arr_remove(&rpc->commands, i); - tal_free(cmd); - break; - } - } + const struct command_result *res; + struct command *dummy; + + /* Call it with minimal cmd, to fill out usagemap */ + dummy = tal(tmpctx, struct command); + dummy->mode = CMD_USAGE; + dummy->ld = ld; + dummy->json_cmd = command; + res = command->dispatch(dummy, NULL, NULL, NULL); + assert(res == ¶m_failed); + assert(strmap_get(&ld->jsonrpc->usagemap, command->name)); +} + +bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command, + const char *usage TAKES) +{ + if (!command_add(rpc, command)) + return false; + usage = tal_strdup(command, usage); + strmap_add(&rpc->usagemap, command->name, usage); + tal_add_destructor2(command, destroy_json_command, rpc); + return true; +} + +static bool jsonrpc_command_add_perm(struct lightningd *ld, + struct jsonrpc *rpc, + struct json_command *command) +{ + if (!command_add(rpc, command)) + return false; + setup_command_usage(ld, command); + return true; } -struct jsonrpc *jsonrpc_new(const tal_t *ctx, struct lightningd *ld) +void jsonrpc_setup(struct lightningd *ld) { - struct jsonrpc *jsonrpc = tal(ctx, struct jsonrpc); struct json_command **commands = get_cmdlist(); - jsonrpc->commands = tal_arr(jsonrpc, struct json_command *, 0); - jsonrpc->log = new_log(jsonrpc, ld->log_book, "jsonrpc"); + ld->jsonrpc = tal(ld, struct jsonrpc); + strmap_init(&ld->jsonrpc->usagemap); + ld->jsonrpc->commands = tal_arr(ld->jsonrpc, struct json_command *, 0); + ld->jsonrpc->log = new_log(ld->jsonrpc, ld->log_book, "jsonrpc"); for (size_t i=0; ijsonrpc, commands[i])) + fatal("Cannot add duplicate command %s", + commands[i]->name); } - jsonrpc->rpc_listener = NULL; - return jsonrpc; + ld->jsonrpc->rpc_listener = NULL; } bool command_usage_only(const struct command *cmd) @@ -806,9 +860,11 @@ bool command_usage_only(const struct command *cmd) return cmd->mode == CMD_USAGE; } -void command_set_usage(struct command *cmd, const char *usage) +void command_set_usage(struct command *cmd, const char *usage TAKES) { - cmd->usage = usage; + usage = tal_strdup(cmd->ld, usage); + if (!strmap_add(&cmd->ld->jsonrpc->usagemap, cmd->json_cmd->name, usage)) + fatal("Two usages for command %s?", cmd->json_cmd->name); } bool command_check_only(const struct command *cmd) @@ -974,22 +1030,6 @@ json_tok_address_scriptpubkey(const tal_t *cxt, return ADDRESS_PARSE_UNRECOGNIZED; } -struct command_result *param_wtx(struct wallet_tx * tx, const char * buffer, - const jsmntok_t *sattok, u64 max) -{ - if (json_tok_streq(buffer, sattok, "all")) { - tx->all_funds = true; - tx->amount = max; - } else if (!json_to_u64(buffer, sattok, &tx->amount)) { - return command_fail(tx->cmd, JSONRPC2_INVALID_PARAMS, - "Invalid satoshis"); - } else if (tx->amount > max) { - return command_fail(tx->cmd, FUND_MAX_EXCEEDED, - "Amount exceeded %"PRIu64, max); - } - return NULL; -} - static struct command_result *param_command(struct command *cmd, const char *name, const char *buffer, @@ -1011,7 +1051,7 @@ struct jsonrpc_notification *jsonrpc_notification_start(const tal_t *ctx, const { struct jsonrpc_notification *n = tal(ctx, struct jsonrpc_notification); n->method = tal_strdup(n, method); - n->stream = new_json_stream(n, NULL); + n->stream = new_json_stream(n, NULL, NULL); json_object_start(n->stream, NULL); json_add_string(n->stream, "jsonrpc", "2.0"); json_add_string(n->stream, "method", method); @@ -1028,7 +1068,7 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n) } struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx, const char *method, + const tal_t *ctx, const char *method, struct log *log, void (*response_cb)(const char *buffer, const jsmntok_t *toks, const jsmntok_t *idtok, void *), void *response_cb_arg) @@ -1039,7 +1079,7 @@ struct jsonrpc_request *jsonrpc_request_start_( r->response_cb = response_cb; r->response_cb_arg = response_cb_arg; r->method = NULL; - r->stream = new_json_stream(r, NULL); + r->stream = new_json_stream(r, NULL, log); /* If no method is specified we don't prefill the JSON-RPC * request with the header. This serves as an escape hatch to @@ -1124,3 +1164,11 @@ static const struct json_command check_command = { }; AUTODATA(json_command, &check_command); + +#if DEVELOPER +void jsonrpc_remove_memleak(struct htable *memtable, + const struct jsonrpc *jsonrpc) +{ + memleak_remove_strmap(memtable, &jsonrpc->usagemap); +} +#endif /* DEVELOPER */ diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index b248d54bb08a..76a09a16c2ee 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -8,6 +8,8 @@ #include #include +struct jsonrpc; + /* The command mode tells param() how to process. */ enum command_mode { /* Normal command processing */ @@ -35,8 +37,6 @@ struct command { bool pending; /* Tell param() how to process the command */ enum command_mode mode; - /* This is created if mode is CMD_USAGE */ - const char *usage; /* Have we started a json stream already? For debugging. */ bool have_json_stream; }; @@ -152,8 +152,10 @@ struct command_result *command_its_complicated(const char *why); * This doesn't setup the listener yet, see `jsonrpc_listen` for * that. This just creates the container for all jsonrpc-related * information so we can start gathering it before actually starting. + * + * It initializes ld->jsonrpc. */ -struct jsonrpc *jsonrpc_new(const tal_t *ctx, struct lightningd *ld); +void jsonrpc_setup(struct lightningd *ld); /** @@ -168,16 +170,11 @@ void jsonrpc_listen(struct jsonrpc *rpc, struct lightningd *ld); * * Returns true if the command was added correctly, false if adding * this would clobber a command name. - */ -bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command); - -/** - * Remove a command/method from the JSON-RPC. * - * Used to dynamically remove a `struct json_command` from the - * JSON-RPC dispatch table by its name. + * Free @command to remove it. */ -void jsonrpc_command_remove(struct jsonrpc *rpc, const char *method); +bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command, + const char *usage TAKES); /** * Begin a JSON-RPC notification with the specified topic. @@ -192,9 +189,9 @@ struct jsonrpc_notification *jsonrpc_notification_start(const tal_t *ctx, const */ void jsonrpc_notification_end(struct jsonrpc_notification *n); -#define jsonrpc_request_start(ctx, method, response_cb, response_cb_arg) \ +#define jsonrpc_request_start(ctx, method, log, response_cb, response_cb_arg) \ jsonrpc_request_start_( \ - (ctx), (method), \ + (ctx), (method), (log), \ typesafe_cb_preargs(void, void *, (response_cb), (response_cb_arg), \ const char *buffer, \ const jsmntok_t *toks, \ @@ -202,7 +199,7 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); (response_cb_arg)) struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx, const char *method, + const tal_t *ctx, const char *method, struct log *log, void (*response_cb)(const char *buffer, const jsmntok_t *toks, const jsmntok_t *idtok, void *), void *response_cb_arg); @@ -210,4 +207,13 @@ struct jsonrpc_request *jsonrpc_request_start_( void jsonrpc_request_end(struct jsonrpc_request *request); AUTODATA_TYPE(json_command, struct json_command); + +#if DEVELOPER +struct htable; +struct jsonrpc; + +void jsonrpc_remove_memleak(struct htable *memtable, + const struct jsonrpc *jsonrpc); +#endif /* DEVELOPER */ + #endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 57a0cb72e6e2..cc1581228764 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -207,7 +207,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * lightningd needs to have something to put those in. This * is that :-) */ - ld->jsonrpc = jsonrpc_new(ld, ld); + jsonrpc_setup(ld); /*~ We run a number of plugins (subprocesses that we talk JSON-RPC with) *alongside this process. This allows us to have an easy way for users @@ -215,7 +215,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) *code. Here we initialize the context that will keep track and control *the plugins. */ - ld->plugins = plugins_new(ld, ld->log_book, ld->jsonrpc, ld); + ld->plugins = plugins_new(ld, ld->log_book, ld); return ld; } @@ -764,6 +764,11 @@ int main(int argc, char *argv[]) * can start talking to us. */ plugins_config(ld->plugins); + /*~ Setting this (global) activates the crash log: we don't usually need + * a backtrace if we fail during startup. We do this before daemonize, + * in case that runs into trouble. */ + crashlog = ld->log; + /*~ We defer --daemon until we've completed most initialization: that * way we'll exit with an error rather than silently exiting 0, then * realizing we can't start and forcing the confused user to read the @@ -794,6 +799,7 @@ int main(int argc, char *argv[]) * log. And tal_hex() is a helper from utils which returns a hex string; * it's assumed that the argument was allocated with tal or tal_arr * so it can use tal_bytelen() to get the length. */ + log_info(ld->log, "--------------------------------------------------"); log_info(ld->log, "Server started with public key %s, alias %s (color #%s) and lightningd %s", type_to_string(tmpctx, struct pubkey, &ld->id), json_escape(tmpctx, (const char *)ld->alias)->s, @@ -808,10 +814,6 @@ int main(int argc, char *argv[]) * can start the poll loop which queries groestlcoind for new blocks. */ begin_topology(ld->topology); - /*~ Setting this (global) activates the crash log: we don't usually need - * a backtrace if we fail during startup. */ - crashlog = ld->log; - /*~ The root of every backtrace (almost). This is our main event * loop. */ for (;;) { diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 4c1ebb28639e..680e45dcd80d 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -40,7 +40,7 @@ struct config { /* Fee rates. */ u32 fee_base; - s32 fee_per_satoshi; + u32 fee_per_satoshi; /* How long between changing commit and sending COMMIT message. */ u32 commit_time_ms; diff --git a/lightningd/log.c b/lightningd/log.c index 787cf4ce8d7a..ef02ced2c77d 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -504,6 +504,7 @@ char *arg_log_to_file(const char *arg, struct lightningd *ld) { const struct log_entry *i; FILE *logf; + int size; if (ld->logfile) { fclose(ld->log->lr->print_arg); @@ -517,6 +518,11 @@ char *arg_log_to_file(const char *arg, struct lightningd *ld) return tal_fmt(NULL, "Failed to open: %s", strerror(errno)); set_log_outfn(ld->log->lr, log_to_file, logf); + /* For convenience make a block of empty lines just like Bitcoin Core */ + size = ftell(logf); + if (size > 0) + fprintf(logf, "\n\n\n\n"); + /* Catch up */ list_for_each(&ld->log->lr->log, i, list) maybe_print(ld->log, i, 0); diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 258f84c90018..9c6642ae4c63 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -2,6 +2,7 @@ #include "memdump.h" #if DEVELOPER #include +#include #include #include #include @@ -120,6 +121,22 @@ static void json_add_backtrace(struct json_stream *response, json_array_end(response); } +static bool handle_strmap(const char *member, void *p, void *memtable_) +{ + struct htable *memtable = memtable_; + + memleak_scan_region(memtable, p, tal_bytelen(p)); + + /* Keep going */ + return true; +} + +/* FIXME: If strmap used tal, this wouldn't be necessary! */ +void memleak_remove_strmap_(struct htable *memtable, const struct strmap *m) +{ + strmap_iterate_(m, handle_strmap, memtable); +} + static void scan_mem(struct command *cmd, struct json_stream *response, struct lightningd *ld, @@ -137,6 +154,7 @@ static void scan_mem(struct command *cmd, memleak_remove_htable(memtable, &ld->topology->txowatches.raw); memleak_remove_htable(memtable, &ld->htlcs_in.raw); memleak_remove_htable(memtable, &ld->htlcs_out.raw); + jsonrpc_remove_memleak(memtable, ld->jsonrpc); /* Now delete ld and those which it has pointers to. */ memleak_remove_referenced(memtable, ld); diff --git a/lightningd/memdump.h b/lightningd/memdump.h index b7837d1b3160..5526099872b6 100644 --- a/lightningd/memdump.h +++ b/lightningd/memdump.h @@ -5,8 +5,16 @@ #include struct command; +struct htable; +struct strmap; struct subd; void opening_memleak_done(struct command *cmd, struct subd *leaker); void peer_memleak_done(struct command *cmd, struct subd *leaker); + +/* Remove any pointers inside this strmap (which is opaque to memleak). */ +#define memleak_remove_strmap(memtable, strmap) \ + memleak_remove_strmap_((memtable), tcon_unwrap(strmap)) +void memleak_remove_strmap_(struct htable *memtable, const struct strmap *m); + #endif /* LIGHTNING_LIGHTNINGD_MEMDUMP_H */ diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 4f70a1577b33..b643c89efb2c 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -273,9 +273,9 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) u->close_info->peer_id = channel->peer->id; u->spendheight = NULL; - if (!fromwire_onchain_add_utxo(msg, &u->txid, &u->outnum, - &u->close_info->commitment_point, - &u->amount, &blockheight)) { + if (!fromwire_onchain_add_utxo( + u, msg, &u->txid, &u->outnum, &u->close_info->commitment_point, + &u->amount, &blockheight, &u->scriptPubkey)) { fatal("onchaind gave invalid add_utxo message: %s", tal_hex(msg, msg)); } u->blockheight = blockheight>0?&blockheight:NULL; @@ -449,18 +449,29 @@ enum watch_result onchaind_funding_spent(struct channel *channel, feerate = try_get_feerate(ld->topology, FEERATE_NORMAL); if (!feerate) { /* We have at least one data point: the last tx's feerate. */ - u64 fee = channel->funding_satoshi; + struct amount_sat fee = channel->funding; for (size_t i = 0; i < tal_count(channel->last_tx->output); i++) - fee -= channel->last_tx->output[i].amount; + if (!amount_sat_sub(&fee, fee, + channel->last_tx->output[i].amount)) { + log_broken(channel->log, "Could not get fee" + " funding %s tx %s", + type_to_string(tmpctx, + struct amount_sat, + &channel->funding), + type_to_string(tmpctx, + struct bitcoin_tx, + channel->last_tx)); + return KEEP_WATCHING; + } - feerate = fee / measure_tx_weight(tx); + feerate = fee.satoshis / measure_tx_weight(tx); /* Raw: reverse feerate extraction */ if (feerate < feerate_floor()) feerate = feerate_floor(); } msg = towire_onchain_init(channel, &channel->their_shachain.chain, - channel->funding_satoshi, + channel->funding, &channel->channel_info.old_remote_per_commit, &channel->channel_info.remote_per_commit, /* BOLT #2: @@ -472,7 +483,7 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel->channel_info.their_config.to_self_delay, channel->our_config.to_self_delay, feerate, - channel->our_config.dust_limit_satoshis, + channel->our_config.dust_limit, &our_last_txid, p2wpkh_for_keyidx(tmpctx, ld, channel->final_key_idx), diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 60dd261de5cb..9db54b1224b4 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -66,15 +66,20 @@ struct uncommitted_channel { struct funding_channel { - struct command *cmd; /* Which also owns us. */ + struct command *cmd; /* Which initially owns us until openingd request */ + struct wallet_tx wtx; - u64 push_msat; + struct amount_msat push; u8 channel_flags; + /* Variables we need to compose fields in cmd's response */ + const char *hextx; + struct channel_id cid; + /* Peer we're trying to reach. */ struct pubkey peerid; - /* Channel. */ + /* Channel, subsequent owner of us */ struct uncommitted_channel *uc; }; @@ -105,7 +110,7 @@ void kill_uncommitted_channel(struct uncommitted_channel *uc, void json_add_uncommitted_channel(struct json_stream *response, const struct uncommitted_channel *uc) { - u64 msatoshi_total, our_msatoshi; + struct amount_msat total, ours; if (!uc) return; @@ -123,10 +128,14 @@ void json_add_uncommitted_channel(struct json_stream *response, json_array_end(response); } - msatoshi_total = uc->fc->wtx.amount * 1000; - our_msatoshi = msatoshi_total - uc->fc->push_msat; - json_add_u64(response, "msatoshi_to_us", our_msatoshi); - json_add_u64(response, "msatoshi_total", msatoshi_total); + /* These should never fail. */ + if (amount_sat_to_msat(&total, uc->fc->wtx.amount) + && amount_msat_sub(&ours, total, uc->fc->push)) { + json_add_amount_msat(response, ours, + "msatoshi_to_us", "to_us_msat"); + json_add_amount_msat(response, total, + "msatoshi_total", "total_msat"); + } json_object_end(response); } @@ -139,14 +148,14 @@ wallet_commit_channel(struct lightningd *ld, struct bitcoin_signature *remote_commit_sig, const struct bitcoin_txid *funding_txid, u16 funding_outnum, - u64 funding_satoshi, - u64 push_msat, + struct amount_sat funding, + struct amount_msat push, u8 channel_flags, struct channel_info *channel_info, u32 feerate) { struct channel *channel; - u64 our_msatoshi; + struct amount_msat our_msat; s64 final_key_idx; /* Get a key to use for closing outputs from this tx */ @@ -156,10 +165,17 @@ wallet_commit_channel(struct lightningd *ld, return NULL; } - if (uc->fc) - our_msatoshi = funding_satoshi * 1000 - push_msat; - else - our_msatoshi = push_msat; + if (uc->fc) { + if (!amount_sat_sub_msat(&our_msat, funding, push)) { + log_broken(uc->log, "push %s exceeds funding %s", + type_to_string(tmpctx, struct amount_msat, + &push), + type_to_string(tmpctx, struct amount_sat, + &funding)); + return NULL; + } + } else + our_msat = push; /* Feerates begin identical. */ channel_info->feerate_per_kw[LOCAL] @@ -181,17 +197,17 @@ wallet_commit_channel(struct lightningd *ld, 1, 1, 0, funding_txid, funding_outnum, - funding_satoshi, - push_msat, + funding, + push, false, /* !remote_funding_locked */ NULL, /* no scid yet */ /* The three arguments below are msatoshi_to_us, * msatoshi_to_us_min, and msatoshi_to_us_max. * Because, this is a newly-funded channel, * all three are same value. */ - our_msatoshi, - our_msatoshi, /* msatoshi_to_us_min */ - our_msatoshi, /* msatoshi_to_us_max */ + our_msat, + our_msat, /* msat_to_us_min */ + our_msat, /* msat_to_us_max */ remote_commit, remote_commit_sig, NULL, /* No HTLC sigs yet */ @@ -216,18 +232,56 @@ wallet_commit_channel(struct lightningd *ld, } static void funding_broadcast_failed(struct channel *channel, - int exitstatus, const char *err) + int exitstatus, const char *msg) { - channel_internal_error(channel, - "Funding broadcast exited with %i: %s", - exitstatus, err); + struct funding_channel *fc = channel->peer->uncommitted_channel->fc; + struct command *cmd = fc->cmd; + + /* Massage output into shape so it doesn't kill the JSON serialization */ + char *output = tal_strjoin(cmd, tal_strsplit(cmd, msg, "\n", STR_NO_EMPTY), " ", STR_NO_TRAIL); + was_pending(command_fail(cmd, FUNDING_BROADCAST_FAIL, + "Error broadcasting funding transaction: %s", output)); + + /* Frees fc too */ + tal_free(fc->uc); + + /* Keep in state CHANNELD_AWAITING_LOCKIN until (manual) broadcast */ +} + +static void funding_broadcast_success(struct channel *channel) +{ + struct json_stream *response; + struct funding_channel *fc = channel->peer->uncommitted_channel->fc; + struct command *cmd = fc->cmd; + + response = json_stream_success(cmd); + json_object_start(response, NULL); + json_add_string(response, "tx", fc->hextx); + json_add_txid(response, "txid", &channel->funding_txid); + json_add_string(response, "channel_id", + type_to_string(tmpctx, struct channel_id, &fc->cid)); + json_object_end(response); + was_pending(command_success(cmd, response)); + + /* Frees fc too */ + tal_free(fc->uc); +} + +static void funding_broadcast_failed_or_success(struct channel *channel, + int exitstatus, const char *msg) +{ + if (exitstatus == 0) { + funding_broadcast_success(channel); + } else { + funding_broadcast_failed(channel, exitstatus, msg); + } } static void opening_funder_finished(struct subd *openingd, const u8 *resp, const int *fds, struct funding_channel *fc) { - u8 *msg, *linear; + u8 *msg; struct channel_info channel_info; struct bitcoin_tx *fundingtx; struct bitcoin_txid funding_txid, expected_txid; @@ -237,11 +291,9 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, struct bitcoin_tx *remote_commit; u16 funding_outnum; u32 feerate; - u64 change_satoshi; + struct amount_sat change; struct channel *channel; - struct json_stream *response; struct lightningd *ld = openingd->ld; - struct channel_id cid; assert(tal_count(fds) == 2); @@ -262,7 +314,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, &channel_info.remote_fundingkey, &expected_txid, &feerate, - &fc->uc->our_config.channel_reserve_satoshis)) { + &fc->uc->our_config.channel_reserve)) { log_broken(fc->uc->log, "bad OPENING_FUNDER_REPLY %s", tal_hex(resp, resp)); @@ -276,7 +328,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, &channel_info.remote_per_commit)); /* Generate the funding tx. */ - if (fc->wtx.change + if (!amount_sat_eq(fc->wtx.change, AMOUNT_SAT(0)) && !bip32_pubkey(ld->wallet->bip32_base, &changekey, fc->wtx.change_key_index)) fatal("Error deriving change key %u", fc->wtx.change_key_index); @@ -293,8 +345,10 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, tal_count(fundingtx->output)); for (size_t i = 0; i < tal_count(fundingtx->input); i++) { - log_debug(fc->uc->log, "%zi: %"PRIu64" satoshi (%s) %s\n", - i, fc->wtx.utxos[i]->amount, + log_debug(fc->uc->log, "%zi: %s (%s) %s\n", + i, + type_to_string(tmpctx, struct amount_sat, + &fc->wtx.utxos[i]->amount), fc->wtx.utxos[i]->is_p2sh ? "P2SH" : "SEGWIT", type_to_string(tmpctx, struct bitcoin_txid, &fundingtx->input[i].txid)); @@ -305,22 +359,29 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, if (!bitcoin_txid_eq(&funding_txid, &expected_txid)) { log_broken(fc->uc->log, "Funding txid mismatch:" - " satoshi %"PRIu64" change %"PRIu64 + " amount %s change %s" " changeidx %u" " localkey %s remotekey %s", - fc->wtx.amount, - fc->wtx.change, fc->wtx.change_key_index, + type_to_string(tmpctx, struct amount_sat, + &fc->wtx.amount), + type_to_string(tmpctx, struct amount_sat, + &fc->wtx.change), + fc->wtx.change_key_index, type_to_string(fc, struct pubkey, &fc->uc->local_funding_pubkey), type_to_string(fc, struct pubkey, &channel_info.remote_fundingkey)); was_pending(command_fail(fc->cmd, JSONRPC2_INVALID_PARAMS, "Funding txid mismatch:" - " satoshi %"PRIu64" change %"PRIu64 + " amount %s change %s" " changeidx %u" " localkey %s remotekey %s", - fc->wtx.amount, - fc->wtx.change, + type_to_string(tmpctx, + struct amount_sat, + &fc->wtx.amount), + type_to_string(tmpctx, + struct amount_sat, + &fc->wtx.change), fc->wtx.change_key_index, type_to_string(fc, struct pubkey, &fc->uc->local_funding_pubkey), @@ -336,7 +397,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, &funding_txid, funding_outnum, fc->wtx.amount, - fc->push_msat, + fc->push, fc->channel_flags, &channel_info, feerate); @@ -349,8 +410,9 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, /* Get HSM to sign the funding tx. */ log_debug(channel->log, "Getting HSM to sign funding tx"); - msg = towire_hsm_sign_funding(tmpctx, channel->funding_satoshi, - fc->wtx.change, fc->wtx.change_key_index, + msg = towire_hsm_sign_funding(tmpctx, channel->funding, + fc->wtx.change, + fc->wtx.change_key_index, &fc->uc->local_funding_pubkey, &channel_info.remote_fundingkey, fc->wtx.utxos); @@ -364,7 +426,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, tal_hex(msg, resp)); /* Extract the change output and add it to the DB */ - wallet_extract_owned_outputs(ld->wallet, fundingtx, NULL, &change_satoshi); + wallet_extract_owned_outputs(ld->wallet, fundingtx, NULL, &change); /* Make sure we recognize our change output by its scriptpubkey in * future. This assumes that we have only two outputs, may not be true @@ -372,32 +434,23 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, if (tal_count(fundingtx->output) == 2) txfilter_add_scriptpubkey(ld->owned_txfilter, fundingtx->output[!funding_outnum].script); - /* Send it out and watch for confirms. */ - broadcast_tx(ld->topology, channel, fundingtx, funding_broadcast_failed); + /* We need these to compose cmd's response in funding_broadcast_success */ + fc->hextx = tal_hex(fc, linearize_tx(fc->cmd, fundingtx)); + derive_channel_id(&fc->cid, &channel->funding_txid, funding_outnum); + /* Send it out and watch for confirms. */ + broadcast_tx(ld->topology, channel, fundingtx, funding_broadcast_failed_or_success); channel_watch_funding(ld, channel); - /* Start normal channel daemon. */ - peer_start_channeld(channel, &cs, fds[0], fds[1], NULL, false); - + /* Mark consumed outputs as spent */ wallet_confirm_utxos(ld->wallet, fc->wtx.utxos); - response = json_stream_success(fc->cmd); - json_object_start(response, NULL); - linear = linearize_tx(response, fundingtx); - json_add_hex_talarr(response, "tx", linear); - json_add_txid(response, "txid", &channel->funding_txid); - derive_channel_id(&cid, &channel->funding_txid, funding_outnum); - json_add_string(response, "channel_id", - type_to_string(tmpctx, struct channel_id, &cid)); - json_object_end(response); - was_pending(command_success(fc->cmd, response)); + /* Start normal channel daemon. */ + peer_start_channeld(channel, &cs, fds[0], fds[1], NULL, false); subd_release_channel(openingd, fc->uc); fc->uc->openingd = NULL; - /* Frees fc too, and tmpctx */ - tal_free(fc->uc); return; failed: @@ -422,7 +475,8 @@ static void opening_fundee_finished(struct subd *openingd, struct lightningd *ld = openingd->ld; struct bitcoin_txid funding_txid; u16 funding_outnum; - u64 funding_satoshi, push_msat; + struct amount_sat funding; + struct amount_msat push; u32 feerate; u8 channel_flags; struct channel *channel; @@ -446,12 +500,12 @@ static void opening_fundee_finished(struct subd *openingd, &channel_info.remote_fundingkey, &funding_txid, &funding_outnum, - &funding_satoshi, - &push_msat, + &funding, + &push, &channel_flags, &feerate, &funding_signed, - &uc->our_config.channel_reserve_satoshis)) { + &uc->our_config.channel_reserve)) { log_broken(uc->log, "bad OPENING_FUNDEE_REPLY %s", tal_hex(reply, reply)); uncommitted_channel_disconnect(uc, "bad OPENING_FUNDEE_REPLY"); @@ -471,8 +525,8 @@ static void opening_fundee_finished(struct subd *openingd, &remote_commit_sig, &funding_txid, funding_outnum, - funding_satoshi, - push_msat, + funding, + push, channel_flags, &channel_info, feerate); @@ -572,7 +626,7 @@ new_uncommitted_channel(struct peer *peer) { struct lightningd *ld = peer->ld; struct uncommitted_channel *uc = tal(ld, struct uncommitted_channel); - char *idname; + const char *idname; uc->peer = peer; assert(!peer->uncommitted_channel); @@ -600,12 +654,12 @@ new_uncommitted_channel(struct peer *peer) static void channel_config(struct lightningd *ld, struct channel_config *ours, u32 *max_to_self_delay, - u64 *min_effective_htlc_capacity_msat) + struct amount_msat *min_effective_htlc_capacity) { /* FIXME: depend on feerate. */ *max_to_self_delay = ld->config.locktime_max; /* This is 1c at $1000/BTC */ - *min_effective_htlc_capacity_msat = 1000000; + *min_effective_htlc_capacity = AMOUNT_MSAT(1000000); /* BOLT #2: * @@ -614,11 +668,11 @@ static void channel_config(struct lightningd *ld, * - set `dust_limit_satoshis` to a sufficient value to allow * commitment transactions to propagate through the Bitcoin network. */ - ours->dust_limit_satoshis = 546; - ours->max_htlc_value_in_flight_msat = UINT64_MAX; + ours->dust_limit = get_chainparams(ld)->dust_limit; + ours->max_htlc_value_in_flight = AMOUNT_MSAT(UINT64_MAX); /* Don't care */ - ours->htlc_minimum_msat = 0; + ours->htlc_minimum = AMOUNT_MSAT(0); /* BOLT #2: * @@ -638,7 +692,7 @@ static void channel_config(struct lightningd *ld, ours->max_accepted_htlcs = 483; /* This is filled in by lightning_openingd, for consistency. */ - ours->channel_reserve_satoshis = -1; + ours->channel_reserve = AMOUNT_SAT(UINT64_MAX); } static unsigned int openingd_msg(struct subd *openingd, @@ -698,7 +752,7 @@ void peer_start_openingd(struct peer *peer, { int hsmfd; u32 max_to_self_delay; - u64 min_effective_htlc_capacity_msat; + struct amount_msat min_effective_htlc_capacity; struct uncommitted_channel *uc; const u8 *msg; @@ -729,7 +783,7 @@ void peer_start_openingd(struct peer *peer, channel_config(peer->ld, &uc->our_config, &max_to_self_delay, - &min_effective_htlc_capacity_msat); + &min_effective_htlc_capacity); /* BOLT #2: * @@ -743,7 +797,7 @@ void peer_start_openingd(struct peer *peer, &get_chainparams(peer->ld)->genesis_blockhash, &uc->our_config, max_to_self_delay, - min_effective_htlc_capacity_msat, + min_effective_htlc_capacity, cs, &uc->local_basepoints, &uc->local_funding_pubkey, uc->minimum_depth, @@ -772,31 +826,29 @@ static struct command_result *json_fund_channel(struct command *cmd, const jsmntok_t *params) { struct command_result *res; - const jsmntok_t *sattok; struct funding_channel * fc = tal(cmd, struct funding_channel); struct pubkey *id; struct peer *peer; struct channel *channel; - u32 *feerate_per_kw; + u32 *feerate_per_kw, *minconf, maxheight; bool *announce_channel; u8 *msg; - u64 max_funding_satoshi = get_chainparams(cmd->ld)->max_funding_satoshi; + struct amount_sat max_funding_satoshi; + + max_funding_satoshi = get_chainparams(cmd->ld)->max_funding; fc->cmd = cmd; fc->uc = NULL; - wtx_init(cmd, &fc->wtx); + wtx_init(cmd, &fc->wtx, max_funding_satoshi); if (!param(fc->cmd, buffer, params, p_req("id", param_pubkey, &id), - p_req("satoshi", param_tok, &sattok), + p_req("satoshi", param_wtx, &fc->wtx), p_opt("feerate", param_feerate, &feerate_per_kw), p_opt_def("announce", param_bool, &announce_channel, true), + p_opt_def("minconf", param_number, &minconf, 1), NULL)) return command_param_failed(); - res = param_wtx(&fc->wtx, buffer, sattok, max_funding_satoshi); - if (res) - return res; - if (!feerate_per_kw) { feerate_per_kw = tal(cmd, u32); *feerate_per_kw = opening_feerate(cmd->ld->topology); @@ -831,7 +883,7 @@ static struct command_result *json_fund_channel(struct command *cmd, } /* FIXME: Support push_msat? */ - fc->push_msat = 0; + fc->push = AMOUNT_MSAT(0); fc->channel_flags = OUR_CHANNEL_FLAGS; if (!*announce_channel) { fc->channel_flags &= ~CHANNEL_FLAGS_ANNOUNCE_CHANNEL; @@ -839,19 +891,20 @@ static struct command_result *json_fund_channel(struct command *cmd, type_to_string(fc, struct pubkey, id)); } + maxheight = minconf_to_maxheight(*minconf, cmd->ld); res = wtx_select_utxos(&fc->wtx, *feerate_per_kw, - BITCOIN_SCRIPTPUBKEY_P2WSH_LEN); + BITCOIN_SCRIPTPUBKEY_P2WSH_LEN, maxheight); if (res) return res; - assert(fc->wtx.amount <= max_funding_satoshi); + assert(!amount_sat_greater(fc->wtx.amount, max_funding_satoshi)); peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc); fc->uc = peer->uncommitted_channel; msg = towire_opening_funder(NULL, fc->wtx.amount, - fc->push_msat, + fc->push, *feerate_per_kw, fc->wtx.change, fc->wtx.change_key_index, @@ -866,9 +919,9 @@ static struct command_result *json_fund_channel(struct command *cmd, } static const struct json_command fund_channel_command = { - "fundchannel", - json_fund_channel, - "Fund channel with {id} using {satoshi} (or 'all') satoshis, at optional {feerate}" + "fundchannel", json_fund_channel, + "Fund channel with {id} using {satoshi} (or 'all') satoshis, at optional " + "{feerate}. Only use outputs that have {minconf} confirmations." }; AUTODATA(json_command, &fund_channel_command); diff --git a/lightningd/options.c b/lightningd/options.c index 08befd78dcf2..fa9521f5caa0 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -373,7 +373,7 @@ static void config_register_opts(struct lightningd *ld) &ld->config.rescan, "Number of blocks to rescan from the current head, or " "absolute blockheight if negative"); - opt_register_arg("--fee-per-satoshi", opt_set_s32, opt_show_s32, + opt_register_arg("--fee-per-satoshi", opt_set_u32, opt_show_u32, &ld->config.fee_per_satoshi, "Microgro fee for every gro in HTLC"); opt_register_arg("--addr", opt_add_addr, NULL, @@ -1031,7 +1031,8 @@ static void add_config(struct lightningd *ld, } else if (opt->cb_arg == (void *)opt_add_plugin) { json_add_opt_plugins(response, ld->plugins); } else if (opt->cb_arg == (void *)opt_add_plugin_dir - || opt->cb_arg == (void *)opt_disable_plugin) { + || opt->cb_arg == (void *)opt_disable_plugin + || opt->cb_arg == (void *)plugin_opt_set) { /* FIXME: We actually treat it as if they specified * --plugin for each one, so ignore these */ #if DEVELOPER diff --git a/lightningd/pay.c b/lightningd/pay.c index 9d07a2c5e69c..e35c3f4a63bc 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -18,18 +18,21 @@ #include #include -/*----------------------------------------------------------------------------- -Internal sendpay interface ------------------------------------------------------------------------------*/ +/* Routing failure object */ +struct routing_failure { + unsigned int erring_index; + enum onion_type failcode; + struct pubkey erring_node; + struct short_channel_id erring_channel; + int channel_dir; +}; /* sendpay command */ struct sendpay_command { struct list_node list; struct sha256 payment_hash; - - void (*cb)(const struct sendpay_result *, void*); - void *cbarg; + struct command *cmd; }; static void destroy_sendpay_command(struct sendpay_command *pc) @@ -37,212 +40,204 @@ static void destroy_sendpay_command(struct sendpay_command *pc) list_del(&pc->list); } -/* Owned by cxt, if cxt is deleted, then cb will +/* Owned by cmd, if cmd is deleted, then sendpay_success/sendpay_fail will * no longer be called. */ static void -add_sendpay_waiter(const tal_t *cxt, - const struct sha256 *payment_hash, - struct lightningd *ld, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg) +add_sendpay_waiter(struct lightningd *ld, + struct command *cmd, + const struct sha256 *payment_hash) { - struct sendpay_command *pc = tal(cxt, struct sendpay_command); + struct sendpay_command *pc = tal(cmd, struct sendpay_command); pc->payment_hash = *payment_hash; - pc->cb = cb; - pc->cbarg = cbarg; + pc->cmd = cmd; list_add(&ld->sendpay_commands, &pc->list); tal_add_destructor(pc, destroy_sendpay_command); } -/* Owned by cxt; if cxt is deleted, then cb will +/* Owned by cmd, if cmd is deleted, then sendpay_success/sendpay_fail will * no longer be called. */ static void -add_waitsendpay_waiter(const tal_t *cxt, - const struct sha256 *payment_hash, - struct lightningd *ld, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg) +add_waitsendpay_waiter(struct lightningd *ld, + struct command *cmd, + const struct sha256 *payment_hash) { - struct sendpay_command *pc = tal(cxt, struct sendpay_command); + struct sendpay_command *pc = tal(cmd, struct sendpay_command); pc->payment_hash = *payment_hash; - pc->cb = cb; - pc->cbarg = cbarg; + pc->cmd = cmd; list_add(&ld->waitsendpay_commands, &pc->list); tal_add_destructor(pc, destroy_sendpay_command); } -/* Caller responsible for freeing ctx. */ -static void waitsendpay_resolve(const tal_t *ctx, - struct lightningd *ld, - const struct sha256 *payment_hash, - const struct sendpay_result *result) +/* Outputs fields, not a separate object*/ +static void +json_add_payment_fields(struct json_stream *response, + const struct wallet_payment *t) { - struct sendpay_command *pc; - struct sendpay_command *next; - list_for_each_safe(&ld->waitsendpay_commands, pc, next, list) { - if (!sha256_eq(payment_hash, &pc->payment_hash)) - continue; - - /* Delete later (in our own caller) if callback did - * not delete. */ - tal_steal(ctx, pc); - pc->cb(result, pc->cbarg); + json_add_u64(response, "id", t->id); + json_add_hex(response, "payment_hash", &t->payment_hash, sizeof(t->payment_hash)); + json_add_pubkey(response, "destination", &t->destination); + json_add_amount_msat(response, t->msatoshi, + "msatoshi", "amount_msat"); + json_add_amount_msat(response, t->msatoshi_sent, + "msatoshi_sent", "amount_sent_msat"); + json_add_u64(response, "created_at", t->timestamp); + + switch (t->status) { + case PAYMENT_PENDING: + json_add_string(response, "status", "pending"); + break; + case PAYMENT_COMPLETE: + json_add_string(response, "status", "complete"); + break; + case PAYMENT_FAILED: + json_add_string(response, "status", "failed"); + break; } + if (t->payment_preimage) + json_add_hex(response, "payment_preimage", + t->payment_preimage, + sizeof(*t->payment_preimage)); + if (t->label) { + if (deprecated_apis) + json_add_string(response, "description", t->label); + json_add_string(response, "label", t->label); + } + if (t->bolt11) + json_add_string(response, "bolt11", t->bolt11); } -static struct sendpay_result* -sendpay_result_success(const tal_t *ctx, - const struct preimage *payment_preimage, - const struct wallet_payment *payment) +static struct command_result *sendpay_success(struct command *cmd, + const struct wallet_payment *payment) { - struct sendpay_result *result = tal(ctx, struct sendpay_result); - result->succeeded = true; - result->preimage = *payment_preimage; - result->payment = payment; - return result; + struct json_stream *response; + + assert(payment->status == PAYMENT_COMPLETE); + + response = json_stream_success(cmd); + json_object_start(response, NULL); + json_add_payment_fields(response, payment); + json_object_end(response); + return command_success(cmd, response); } -static void payment_trigger_success(struct lightningd *ld, - const struct sha256 *payment_hash) +static void +json_add_routefail_info(struct json_stream *js, + unsigned int erring_index, + enum onion_type failcode, + const struct pubkey *erring_node, + const struct short_channel_id *erring_channel, + int channel_dir) { - struct sendpay_result *result; - struct wallet_payment *payment; - - payment = wallet_payment_by_hash(tmpctx, ld->wallet, payment_hash); - assert(payment); + const char *failcodename = onion_type_name(failcode); + + json_object_start(js, NULL); + json_add_num(js, "erring_index", erring_index); + json_add_num(js, "failcode", failcode); + /* FIXME: Better way to detect this? */ + if (!strstarts(failcodename, "INVALID ")) + json_add_string(js, "failcodename", failcodename); + json_add_pubkey(js, "erring_node", erring_node); + json_add_short_channel_id(js, "erring_channel", erring_channel); + json_add_num(js, "erring_direction", channel_dir); + json_object_end(js); +} - result = sendpay_result_success(tmpctx, payment->payment_preimage, payment); +/* onionreply used if pay_errcode == PAY_UNPARSEABLE_ONION */ +static struct command_result * +sendpay_fail(struct command *cmd, + int pay_errcode, + const u8 *onionreply, + const struct routing_failure *fail, + const char *details) +{ + struct json_stream *data; + + if (pay_errcode == PAY_UNPARSEABLE_ONION) { + data = json_stream_fail(cmd, PAY_UNPARSEABLE_ONION, + "Malformed error reply"); + json_object_start(data, NULL); + json_add_hex_talarr(data, "onionreply", onionreply); + json_object_end(data); + return command_failed(cmd, data); + } - waitsendpay_resolve(tmpctx, ld, payment_hash, result); + assert(fail); + data = json_stream_fail(cmd, pay_errcode, + tal_fmt(tmpctx, "failed: %s (%s)", + onion_type_name(fail->failcode), + details)); + json_add_routefail_info(data, + fail->erring_index, + fail->failcode, + &fail->erring_node, + &fail->erring_channel, + fail->channel_dir); + return command_failed(cmd, data); } -static struct sendpay_result* -sendpay_result_route_failure(const tal_t *ctx, - bool retry_plausible, - struct routing_failure *fail, - const u8 *onionreply, - const char *details) +/* We defer sendpay "success" until we know it's pending; consumes cmd */ +static struct command_result * +json_sendpay_in_progress(struct command *cmd, + const struct wallet_payment *payment) { - struct sendpay_result *result = tal(ctx, struct sendpay_result); - result->succeeded = false; - result->errorcode = - (!fail) ? PAY_UNPARSEABLE_ONION : - (!retry_plausible) ? PAY_DESTINATION_PERM_FAIL : - /*otherwise*/ PAY_TRY_OTHER_ROUTE ; - result->onionreply = onionreply; - result->routing_failure = fail; - result->details = details; - return result; + struct json_stream *response = json_stream_success(cmd); + json_object_start(response, NULL); + json_add_string(response, "message", + "Monitor status with listpayments or waitsendpay"); + json_add_payment_fields(response, payment); + json_object_end(response); + return command_success(cmd, response); } -static void payment_route_failure(struct lightningd *ld, - const struct sha256 *payment_hash, - bool retry_plausible, - struct routing_failure *fail, - const u8 *onionreply, - const char *details) +static void tell_waiters_failed(struct lightningd *ld, + const struct sha256 *payment_hash, + int pay_errcode, + const u8 *onionreply, + const struct routing_failure *fail, + const char *details) { - struct sendpay_result *result; + struct sendpay_command *pc; + struct sendpay_command *next; - result = sendpay_result_route_failure(tmpctx, - retry_plausible, - fail, - onionreply, - details); + /* Careful: sendpay_fail deletes cmd */ + list_for_each_safe(&ld->waitsendpay_commands, pc, next, list) { + if (!sha256_eq(payment_hash, &pc->payment_hash)) + continue; - waitsendpay_resolve(tmpctx, ld, payment_hash, result); + sendpay_fail(pc->cmd, pay_errcode, onionreply, fail, details); + } } -static struct sendpay_result * -sendpay_result_simple_fail(const tal_t *ctx, - int errorcode, - char const *details) +static void tell_waiters_success(struct lightningd *ld, + const struct sha256 *payment_hash, + struct wallet_payment *payment) { - struct sendpay_result *result = tal(ctx, struct sendpay_result); - result->succeeded = false; - result->errorcode = errorcode; - result->details = details; - return result; -} + struct sendpay_command *pc; + struct sendpay_command *next; -static struct sendpay_result * -sendpay_result_in_progress(const tal_t *ctx, - const struct wallet_payment* payment, - char const *details) -{ - struct sendpay_result *result = tal(ctx, struct sendpay_result); - result->succeeded = false; - result->errorcode = PAY_IN_PROGRESS; - result->payment = payment; - result->details = details; - return result; + /* Careful: sendpay_success deletes cmd */ + list_for_each_safe(&ld->waitsendpay_commands, pc, next, list) { + if (!sha256_eq(payment_hash, &pc->payment_hash)) + continue; + + sendpay_success(pc->cmd, payment); + } } void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, const struct preimage *rval) { + struct wallet_payment *payment; + wallet_payment_set_status(ld->wallet, &hout->payment_hash, PAYMENT_COMPLETE, rval); - payment_trigger_success(ld, &hout->payment_hash); -} - -/* Fix up the channel_update to include the type if it doesn't currently have - * one. See ElementsProject/lightning#1730 and lightningnetwork/lnd#1599 for the - * in-depth discussion on why we break message parsing here... */ -static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES) -{ - u8 *fixed; - if (channel_update != NULL && - fromwire_peektype(channel_update) != WIRE_CHANNEL_UPDATE) { - /* This should be a channel_update, prefix with the - * WIRE_CHANNEL_UPDATE type, but isn't. Let's prefix it. */ - fixed = tal_arr(ctx, u8, 0); - towire_u16(&fixed, WIRE_CHANNEL_UPDATE); - towire(&fixed, channel_update, tal_bytelen(channel_update)); - if (taken(channel_update)) - tal_free(channel_update); - return fixed; - } else { - return tal_dup_arr(ctx, u8, - channel_update, tal_count(channel_update), 0); - } -} - -/* Return NULL if the wrapped onion error message has no - * channel_update field, or return the embedded - * channel_update message otherwise. */ -static u8 *channel_update_from_onion_error(const tal_t *ctx, - const u8 *onion_message) -{ - u8 *channel_update = NULL; - u64 unused64; - u32 unused32; - - /* Identify failcodes that have some channel_update. - * - * TODO > BOLT 1.0: Add new failcodes when updating to a - * new BOLT version. */ - if (!fromwire_temporary_channel_failure(ctx, - onion_message, - &channel_update) && - !fromwire_amount_below_minimum(ctx, - onion_message, &unused64, - &channel_update) && - !fromwire_fee_insufficient(ctx, - onion_message, &unused64, - &channel_update) && - !fromwire_incorrect_cltv_expiry(ctx, - onion_message, &unused32, - &channel_update) && - !fromwire_expiry_too_soon(ctx, - onion_message, - &channel_update)) - /* No channel update. */ - return NULL; + payment = wallet_payment_by_hash(tmpctx, ld->wallet, + &hout->payment_hash); + assert(payment); - return patch_channel_update(ctx, take(channel_update)); + tell_waiters_success(ld, &hout->payment_hash, payment); } /* Return a struct routing_failure for an immediate failure @@ -264,12 +259,7 @@ immediate_routing_failure(const tal_t *ctx, routing_failure->failcode = failcode; routing_failure->erring_node = ld->id; routing_failure->erring_channel = *channel0; - if (dstid) - routing_failure->channel_dir = pubkey_idx(&ld->id, dstid); - /* FIXME: Don't set at all unless we know. */ - else - routing_failure->channel_dir = 0; - routing_failure->channel_update = NULL; + routing_failure->channel_dir = pubkey_idx(&ld->id, dstid); return routing_failure; } @@ -293,58 +283,48 @@ local_routing_failure(const tal_t *ctx, routing_failure->erring_channel = payment->route_channels[0]; routing_failure->channel_dir = pubkey_idx(&ld->id, &payment->route_nodes[0]); - routing_failure->channel_update = NULL; + log_debug(hout->key.channel->log, "local_routing_failure: %u (%s)", + hout->failcode, onion_type_name(hout->failcode)); return routing_failure; } -/* Return false if permanent failure at the destination, true if - * retrying is plausible. Fill *routing_failure with NULL if - * we cannot report the remote failure, or with the routing - * failure to report (allocated from ctx) otherwise. */ +/* Fills in *pay_errcode with PAY_TRY_OTHER_ROUTE or PAY_DESTINATION_PERM_FAIL */ static struct routing_failure* remote_routing_failure(const tal_t *ctx, - bool *p_retry_plausible, - bool *p_report_to_gossipd, + struct lightningd *ld, const struct wallet_payment *payment, const struct onionreply *failure, - struct log *log) + struct log *log, + int *pay_errcode) { enum onion_type failcode = fromwire_peektype(failure->msg); - u8 *channel_update; struct routing_failure *routing_failure; const struct pubkey *route_nodes; const struct pubkey *erring_node; const struct short_channel_id *route_channels; const struct short_channel_id *erring_channel; - static const struct short_channel_id dummy_channel = { 0 }; int origin_index; - bool retry_plausible; - bool report_to_gossipd; int dir; routing_failure = tal(ctx, struct routing_failure); route_nodes = payment->route_nodes; route_channels = payment->route_channels; origin_index = failure->origin_index; - channel_update - = channel_update_from_onion_error(routing_failure, - failure->msg); - if (channel_update) - log_debug(log, "Extracted channel_update %s from onionreply %s", - tal_hex(tmpctx, channel_update), - tal_hex(tmpctx, failure->msg)); - - retry_plausible = true; - report_to_gossipd = true; assert(origin_index < tal_count(route_nodes)); /* Check if at destination. */ if (origin_index == tal_count(route_nodes) - 1) { - /* FIXME: Don't set erring_channel or dir in this case! */ - erring_channel = &dummy_channel; - dir = 0; + /* If any channel is to blame, it's the last one. */ + erring_channel = &route_channels[origin_index]; + /* Single hop? */ + if (origin_index == 0) + dir = pubkey_idx(&ld->id, + &route_nodes[origin_index]); + else + dir = pubkey_idx(&route_nodes[origin_index - 1], + &route_nodes[origin_index]); /* BOLT #4: * @@ -353,18 +333,16 @@ remote_routing_failure(const tal_t *ctx, * - SHOULD fail the payment. * */ if (failcode & PERM) - retry_plausible = false; - else - retry_plausible = true; - /* Only send message to gossipd if NODE error; - * there is no "next" channel to report as - * failing if this is the last node. */ - if (failcode & NODE) - report_to_gossipd = true; + *pay_errcode = PAY_DESTINATION_PERM_FAIL; else - report_to_gossipd = false; + /* FIXME: not right for WIRE_FINAL_EXPIRY_TOO_SOON */ + *pay_errcode = PAY_TRY_OTHER_ROUTE; erring_node = &route_nodes[origin_index]; } else { + u8 *gossip_msg; + + *pay_errcode = PAY_TRY_OTHER_ROUTE; + /* Report the *next* channel as failing. */ erring_channel = &route_channels[origin_index + 1]; @@ -379,95 +357,44 @@ remote_routing_failure(const tal_t *ctx, erring_node = &route_nodes[origin_index + 1]; } else erring_node = &route_nodes[origin_index]; + + /* Tell gossipd: it may want to remove channels or even nodes + * in response to this, and there may be a channel_update + * embedded too */ + gossip_msg = towire_gossip_payment_failure(NULL, + erring_node, + erring_channel, + dir, + failure->msg); + subd_send_msg(ld->gossip, take(gossip_msg)); } routing_failure->erring_index = (unsigned int) (origin_index + 1); routing_failure->failcode = failcode; routing_failure->erring_node = *erring_node; routing_failure->erring_channel = *erring_channel; - routing_failure->channel_update = channel_update; routing_failure->channel_dir = dir; - *p_retry_plausible = retry_plausible; - *p_report_to_gossipd = report_to_gossipd; - return routing_failure; } -static void random_mark_channel_unroutable(struct log *log, - struct subd *gossip, - struct short_channel_id *route_channels) -{ - size_t num_channels = tal_count(route_channels); - size_t i; - const struct short_channel_id *channel; - u8 *msg; - assert(num_channels != 0); - - /* Select one channel by random. */ - randombytes_buf(&i, sizeof(i)); - i = i % num_channels; - channel = &route_channels[i]; - - log_debug(log, - "Disable randomly %dth channel (%s) along route " - "(guessing due to bad reply)", - (int) i, - type_to_string(tmpctx, struct short_channel_id, - channel)); - msg = towire_gossip_mark_channel_unroutable(tmpctx, channel); - subd_send_msg(gossip, msg); -} - -static void report_routing_failure(struct log *log, - struct subd *gossip, - struct routing_failure *fail) -{ - u8 *gossip_msg; - assert(fail); - - log_debug(log, - "Reporting route failure to gossipd: 0x%04x (%s) " - "node %s channel %s update %s", - fail->failcode, onion_type_name(fail->failcode), - type_to_string(tmpctx, struct pubkey, - &fail->erring_node), - type_to_string(tmpctx, struct short_channel_id, - &fail->erring_channel), - tal_hex(tmpctx, fail->channel_update)); - - gossip_msg = towire_gossip_routing_failure(tmpctx, - &fail->erring_node, - &fail->erring_channel, - (u16) fail->failcode, - fail->channel_update); - subd_send_msg(gossip, gossip_msg); -} - -void payment_store(struct lightningd *ld, - const struct sha256 *payment_hash) +void payment_store(struct lightningd *ld, const struct sha256 *payment_hash) { struct sendpay_command *pc; struct sendpay_command *next; - struct sendpay_result *result; const struct wallet_payment *payment; wallet_payment_store(ld->wallet, payment_hash); payment = wallet_payment_by_hash(tmpctx, ld->wallet, payment_hash); assert(payment); - /* Invent a sendpay result with PAY_IN_PROGRESS. */ - result = sendpay_result_in_progress(tmpctx, payment, - "Payment is still in progress"); - /* Trigger any sendpay commands waiting for the store to occur. */ list_for_each_safe(&ld->sendpay_commands, pc, next, list) { if (!sha256_eq(payment_hash, &pc->payment_hash)) continue; - /* Delete later if callback did not delete. */ - tal_steal(tmpctx, pc); - pc->cb(result, pc->cbarg); + /* Deletes from list, frees pc */ + json_sendpay_in_progress(pc->cmd, payment); } } @@ -477,8 +404,7 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, struct wallet_payment *payment; struct routing_failure* fail = NULL; const char *failmsg; - bool retry_plausible; - bool report_to_gossipd; + int pay_errcode; payment = wallet_payment_by_hash(tmpctx, ld->wallet, &hout->payment_hash); @@ -517,15 +443,16 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, if (localfail) { fail = local_routing_failure(tmpctx, ld, hout, payment); failmsg = localfail; - retry_plausible = true; - report_to_gossipd = true; + pay_errcode = PAY_TRY_OTHER_ROUTE; } else { /* Must be remote fail. */ assert(!hout->failcode); failmsg = "reply from remote"; /* Try to parse reply. */ struct secret *path_secrets = payment->path_secrets; - struct onionreply *reply = unwrap_onionreply(tmpctx, path_secrets, + struct onionreply *reply; + + reply = unwrap_onionreply(tmpctx, path_secrets, tal_count(path_secrets), hout->failuremsg); if (!reply) { @@ -533,18 +460,9 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, "htlc %"PRIu64" failed with bad reply (%s)", hout->key.id, tal_hex(tmpctx, hout->failuremsg)); - /* Cannot report failure. */ + /* Cannot record failure. */ fail = NULL; - /* Select a channel to mark unroutable by random */ - random_mark_channel_unroutable(hout->key.channel->log, - ld->gossip, - payment->route_channels); - /* Can now retry; we selected a channel to mark - * unroutable by random */ - retry_plausible = true; - /* Already reported something to gossipd, do not - * report anything else */ - report_to_gossipd = false; + pay_errcode = PAY_UNPARSEABLE_ONION; } else { enum onion_type failcode = fromwire_peektype(reply->msg); log_info(hout->key.channel->log, @@ -554,11 +472,10 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, hout->key.id, reply->origin_index, failcode, onion_type_name(failcode)); - fail = remote_routing_failure(tmpctx, - &retry_plausible, - &report_to_gossipd, + fail = remote_routing_failure(tmpctx, ld, payment, reply, - hout->key.channel->log); + hout->key.channel->log, + &pay_errcode); } } @@ -569,40 +486,27 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, wallet_payment_set_failinfo(ld->wallet, &hout->payment_hash, fail ? NULL : hout->failuremsg, - (fail && !retry_plausible), + pay_errcode == PAY_DESTINATION_PERM_FAIL, fail ? fail->erring_index : -1, fail ? fail->failcode : 0, fail ? &fail->erring_node : NULL, fail ? &fail->erring_channel : NULL, - fail ? fail->channel_update : NULL, + NULL, failmsg, fail ? fail->channel_dir : 0); - /* Report to gossipd if we decided we should. */ - if (report_to_gossipd) - report_routing_failure(ld->log, ld->gossip, fail); - - - /* Report to client. */ - payment_route_failure(ld, &hout->payment_hash, - retry_plausible, fail, hout->failuremsg, - failmsg); + tell_waiters_failed(ld, &hout->payment_hash, pay_errcode, + hout->failuremsg, fail, failmsg); } -/* Wait for a payment. If cxt is deleted, then cb will +/* Wait for a payment. If cmd is deleted, then json_waitsendpay_on_resolve * no longer be called. - * Return false if we called callback already, true if - * callback is scheduled for later. */ -bool wait_payment(const tal_t *cxt, - struct lightningd *ld, - const struct sha256 *payment_hash, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg) + * Return callback if we called already, otherwise NULL. */ +static struct command_result *wait_payment(struct lightningd *ld, + struct command *cmd, + const struct sha256 *payment_hash) { struct wallet_payment *payment; - struct sendpay_result *result; - char const *details; - bool cb_not_called; u8 *failonionreply; bool faildestperm; int failindex; @@ -616,31 +520,19 @@ bool wait_payment(const tal_t *cxt, payment = wallet_payment_by_hash(tmpctx, ld->wallet, payment_hash); if (!payment) { - details = tal_fmt(tmpctx, - "Never attempted payment for '%s'", - type_to_string(tmpctx, struct sha256, - payment_hash)); - result = sendpay_result_simple_fail(tmpctx, - PAY_NO_SUCH_PAYMENT, - details); - cb(result, cbarg); - cb_not_called = false; - goto end; + return command_fail(cmd, PAY_NO_SUCH_PAYMENT, + "Never attempted payment for '%s'", + type_to_string(tmpctx, struct sha256, + payment_hash)); } switch (payment->status) { case PAYMENT_PENDING: - add_waitsendpay_waiter(cxt, payment_hash, ld, cb, cbarg); - cb_not_called = true; - goto end; + add_waitsendpay_waiter(ld, cmd, payment_hash); + return NULL; case PAYMENT_COMPLETE: - result = sendpay_result_success(tmpctx, - payment->payment_preimage, - payment); - cb(result, cbarg); - cb_not_called = false; - goto end; + return sendpay_success(cmd, payment); case PAYMENT_FAILED: /* Get error from DB */ @@ -655,13 +547,14 @@ bool wait_payment(const tal_t *cxt, &faildetail, &faildirection); /* Old DB might not save failure information */ - if (!failonionreply && !failnode) - result = sendpay_result_simple_fail(tmpctx, - PAY_UNSPECIFIED_ERROR, - "Payment failure reason unknown"); - else if (failonionreply) { + if (!failonionreply && !failnode) { + return command_fail(cmd, PAY_UNSPECIFIED_ERROR, + "Payment failure reason unknown"); + } else if (failonionreply) { /* failed to parse returned onion error */ - result = sendpay_result_route_failure(tmpctx, true, NULL, failonionreply, faildetail); + return sendpay_fail(cmd, PAY_UNPARSEABLE_ONION, + failonionreply, + NULL, faildetail); } else { /* Parsed onion error, get its details */ assert(failnode); @@ -671,33 +564,29 @@ bool wait_payment(const tal_t *cxt, fail->failcode = failcode; fail->erring_node = *failnode; fail->erring_channel = *failchannel; - fail->channel_update = failupdate; fail->channel_dir = faildirection; - result = sendpay_result_route_failure(tmpctx, !faildestperm, fail, NULL, faildetail); + return sendpay_fail(cmd, + faildestperm + ? PAY_DESTINATION_PERM_FAIL + : PAY_TRY_OTHER_ROUTE, + NULL, + fail, faildetail); } - - cb(result, cbarg); - cb_not_called = false; - goto end; } /* Impossible. */ abort(); - -end: - return cb_not_called; } -/* Returns false if cb was called, true if cb not yet called. */ -bool -send_payment(const tal_t *ctx, - struct lightningd* ld, +/* Returns command_result if cmd was resolved, NULL if not yet called. */ +static struct command_result * +send_payment(struct lightningd *ld, + struct command *cmd, const struct sha256 *rhash, const struct route_hop *route, - u64 msatoshi, - const char *description TAKES, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg) + struct amount_msat msat, + const char *label TAKES, + const char *b11str TAKES) { const u8 *onion; u8 sessionkey[32]; @@ -713,7 +602,6 @@ send_payment(const tal_t *ctx, struct short_channel_id *channels; struct routing_failure *fail; struct channel *channel; - struct sendpay_result *result; /* Expiry for HTLCs is absolute. And add one to give some margin. */ base_expiry = get_block_height(ld->topology) + 1; @@ -744,62 +632,42 @@ send_payment(const tal_t *ctx, log_debug(ld->log, "send_payment: found previous"); if (payment->status == PAYMENT_PENDING) { log_add(ld->log, "Payment is still in progress"); - result = sendpay_result_in_progress(tmpctx, - payment, - "Payment is still in progress"); - cb(result, cbarg); - return false; + return json_sendpay_in_progress(cmd, payment); } if (payment->status == PAYMENT_COMPLETE) { log_add(ld->log, "... succeeded"); /* Must match successful payment parameters. */ - if (payment->msatoshi != msatoshi) { - char *msg = tal_fmt(tmpctx, + if (!amount_msat_eq(payment->msatoshi, msat)) { + return command_fail(cmd, PAY_RHASH_ALREADY_USED, "Already succeeded " - "with amount %"PRIu64, - payment->msatoshi); - result = sendpay_result_simple_fail(tmpctx, - PAY_RHASH_ALREADY_USED, - msg); - cb(result, cbarg); - return false; + "with amount %s", + type_to_string(tmpctx, + struct amount_msat, + &payment->msatoshi)); } if (!pubkey_eq(&payment->destination, &ids[n_hops-1])) { - char *msg = tal_fmt(tmpctx, + return command_fail(cmd, PAY_RHASH_ALREADY_USED, "Already succeeded to %s", type_to_string(tmpctx, struct pubkey, &payment->destination)); - result = sendpay_result_simple_fail(tmpctx, - PAY_RHASH_ALREADY_USED, - msg); - cb(result, cbarg); - return false; } - result = sendpay_result_success(tmpctx, - payment->payment_preimage, - payment); - cb(result, cbarg); - return false; + return sendpay_success(cmd, payment); } log_add(ld->log, "... retrying"); } channel = active_channel_by_id(ld, &ids[0], NULL); if (!channel) { - /* Report routing failure to gossipd */ - fail = immediate_routing_failure(ctx, ld, - WIRE_UNKNOWN_NEXT_PEER, - &route[0].channel_id, - 0); - report_routing_failure(ld->log, ld->gossip, fail); - - /* Report routing failure to caller */ - result = sendpay_result_route_failure(tmpctx, true, fail, NULL, - "No connection to first " - "peer found"); - cb(result, cbarg); - return false; + struct json_stream *data + = json_stream_fail(cmd, PAY_TRY_OTHER_ROUTE, + "No connection to first " + "peer found"); + + json_add_routefail_info(data, 0, WIRE_UNKNOWN_NEXT_PEER, + &ld->id, &route[0].channel_id, + pubkey_idx(&ld->id, &route[0].nodeid)); + return command_failed(cmd, data); } randombytes_buf(&sessionkey, sizeof(sessionkey)); @@ -809,25 +677,21 @@ send_payment(const tal_t *ctx, sizeof(struct sha256), &path_secrets); onion = serialize_onionpacket(tmpctx, packet); - log_info(ld->log, "Sending %"PRIu64" over %zu hops to deliver %"PRIu64"", - route[0].amount, n_hops, msatoshi); + log_info(ld->log, "Sending %s over %zu hops to deliver %s", + type_to_string(tmpctx, struct amount_msat, &route[0].amount), + n_hops, type_to_string(tmpctx, struct amount_msat, &msat)); failcode = send_htlc_out(channel, route[0].amount, base_expiry + route[0].delay, rhash, onion, NULL, &hout); if (failcode) { - /* Report routing failure to gossipd */ - fail = immediate_routing_failure(ctx, ld, + fail = immediate_routing_failure(cmd, ld, failcode, &route[0].channel_id, &channel->peer->id); - report_routing_failure(ld->log, ld->gossip, fail); - /* Report routing failure to caller */ - result = sendpay_result_route_failure(tmpctx, true, fail, NULL, - "First peer not ready"); - cb(result, cbarg); - return false; + return sendpay_fail(cmd, PAY_TRY_OTHER_ROUTE, NULL, + fail, "First peer not ready"); } /* Copy channels used along the route. */ @@ -851,130 +715,33 @@ send_payment(const tal_t *ctx, payment->payment_hash = *rhash; payment->destination = ids[n_hops - 1]; payment->status = PAYMENT_PENDING; - payment->msatoshi = msatoshi; + payment->msatoshi = msat; payment->msatoshi_sent = route[0].amount; payment->timestamp = time_now().ts.tv_sec; payment->payment_preimage = NULL; payment->path_secrets = tal_steal(payment, path_secrets); payment->route_nodes = tal_steal(payment, ids); payment->route_channels = tal_steal(payment, channels); - if (description != NULL) - payment->description = tal_strdup(payment, description); + if (label != NULL) + payment->label = tal_strdup(payment, label); else - payment->description = NULL; + payment->label = NULL; + if (b11str != NULL) + payment->bolt11 = tal_strdup(payment, b11str); + else + payment->bolt11 = NULL; /* We write this into db when HTLC is actually sent. */ wallet_payment_setup(ld->wallet, payment); - add_sendpay_waiter(ctx, rhash, ld, cb, cbarg); - - return true; + add_sendpay_waiter(ld, cmd, rhash); + return NULL; } /*----------------------------------------------------------------------------- JSON-RPC sendpay interface -----------------------------------------------------------------------------*/ -static void -json_sendpay_success(struct command *cmd, - const struct sendpay_result *r) -{ - struct json_stream *response; - - assert(r->payment->status == PAYMENT_COMPLETE); - - response = json_stream_success(cmd); - json_object_start(response, NULL); - json_add_payment_fields(response, r->payment); - json_object_end(response); - was_pending(command_success(cmd, response)); -} - -static void json_waitsendpay_on_resolve(const struct sendpay_result *r, - void *vcmd) -{ - struct command *cmd = (struct command*) vcmd; - - const char *msg = NULL; - struct routing_failure *fail; - - if (r->succeeded) - json_sendpay_success(cmd, r); - else { - struct json_stream *data; - switch (r->errorcode) { - /* We will never handle this case */ - case PAY_IN_PROGRESS: - abort(); - - case PAY_RHASH_ALREADY_USED: - case PAY_UNSPECIFIED_ERROR: - case PAY_NO_SUCH_PAYMENT: - was_pending(command_fail(cmd, r->errorcode, "%s", - r->details)); - return; - - case PAY_UNPARSEABLE_ONION: - msg = tal_fmt(tmpctx, - "failed: WIRE_PERMANENT_NODE_FAILURE " - "(%s)", - r->details); - - data = json_stream_fail(cmd, r->errorcode, msg); - json_object_start(data, NULL); - json_add_hex_talarr(data, "onionreply", r->onionreply); - json_object_end(data); - was_pending(command_failed(cmd, data)); - return; - - case PAY_DESTINATION_PERM_FAIL: - case PAY_TRY_OTHER_ROUTE: - fail = r->routing_failure; - msg = tal_fmt(cmd, - "failed: %s (%s)", - onion_type_name(fail->failcode), - r->details); - data = json_stream_fail(cmd, r->errorcode, msg); - - json_object_start(data, NULL); - json_add_num(data, "erring_index", - fail->erring_index); - json_add_num(data, "failcode", - (unsigned) fail->failcode); - json_add_pubkey(data, "erring_node", &fail->erring_node); - json_add_short_channel_id(data, "erring_channel", - &fail->erring_channel); - json_add_num(data, "erring_direction", - fail->channel_dir); - if (fail->channel_update) - json_add_hex_talarr(data, "channel_update", - fail->channel_update); - json_object_end(data); - was_pending(command_failed(cmd, data)); - return; - } - abort(); - } -} - -static void json_sendpay_on_resolve(const struct sendpay_result* r, - void *vcmd) -{ - struct command *cmd = (struct command*) vcmd; - - if (!r->succeeded && r->errorcode == PAY_IN_PROGRESS) { - /* This is normal for sendpay. Succeed. */ - struct json_stream *response = json_stream_success(cmd); - json_object_start(response, NULL); - json_add_string(response, "message", - "Monitor status with listpayments or waitsendpay"); - json_add_payment_fields(response, r->payment); - json_object_end(response); - was_pending(command_success(cmd, response)); - } else - json_waitsendpay_on_resolve(r, cmd); -} - static struct command_result *json_sendpay(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -985,37 +752,91 @@ static struct command_result *json_sendpay(struct command *cmd, size_t i; struct sha256 *rhash; struct route_hop *route; - u64 *msatoshi; - const char *description; + struct amount_msat *msat; + const char *b11str, *label; + struct command_result *res; + + /* If by array, or 'check' command, use 'label' as param name */ + if (!params || params->type == JSMN_ARRAY) { + if (!param(cmd, buffer, params, + p_req("route", param_array, &routetok), + p_req("payment_hash", param_sha256, &rhash), + p_opt("label", param_escaped_string, &label), + p_opt("msatoshi", param_msat, &msat), + p_opt("bolt11", param_string, &b11str), + NULL)) + return command_param_failed(); + } else { + const char *description_deprecated; + + /* If by keyword, treat description and label as + * separate parameters. */ + if (!param(cmd, buffer, params, + p_req("route", param_array, &routetok), + p_req("payment_hash", param_sha256, &rhash), + p_opt("label", param_escaped_string, &label), + p_opt("description", param_escaped_string, + &description_deprecated), + p_opt("msatoshi", param_msat, &msat), + p_opt("bolt11", param_string, &b11str), + NULL)) + return command_param_failed(); - if (!param(cmd, buffer, params, - p_req("route", param_array, &routetok), - p_req("payment_hash", param_sha256, &rhash), - p_opt("description", param_escaped_string, &description), - p_opt("msatoshi", param_u64, &msatoshi), - NULL)) - return command_param_failed(); + if (description_deprecated) { + if (!deprecated_apis) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Deprecated parameter description, use label"); + if (label) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Cannot specify both description and label"); + label = description_deprecated; + } + } if (routetok->size == 0) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty route"); route = tal_arr(cmd, struct route_hop, routetok->size); json_for_each_arr(i, t, routetok) { - u64 *amount; + struct amount_msat *msat, *amount_msat; struct pubkey *id; struct short_channel_id *channel; unsigned *delay, *direction; if (!param(cmd, buffer, t, - p_req("msatoshi", param_u64, &amount), - p_req("id", param_pubkey, &id), - p_req("delay", param_number, &delay), - p_req("channel", param_short_channel_id, &channel), + /* Only *one* of these is required */ + p_opt("msatoshi", param_msat, &msat), + p_opt("amount_msat", param_msat, &amount_msat), + /* These three actually required */ + p_opt("id", param_pubkey, &id), + p_opt("delay", param_number, &delay), + p_opt("channel", param_short_channel_id, &channel), p_opt("direction", param_number, &direction), NULL)) return command_param_failed(); - route[i].amount = *amount; + if (!msat && !amount_msat) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "route[%zi]: must have msatoshi" + " or amount_msat", i); + if (!id || !channel || !delay) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "route[%zi]: must have id, channel" + " and delay", i); + if (msat && amount_msat && !amount_msat_eq(*msat, *amount_msat)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "route[%zi]: msatoshi %s != amount_msat %s", + i, + type_to_string(tmpctx, + struct amount_msat, + msat), + type_to_string(tmpctx, + struct amount_msat, + amount_msat)); + if (!msat) + msat = amount_msat; + + route[i].amount = *msat; route[i].nodeid = *id; route[i].delay = *delay; route[i].channel_id = *channel; @@ -1028,22 +849,36 @@ static struct command_result *json_sendpay(struct command *cmd, * be from the msatoshi to twice msatoshi. */ /* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, fail. */ - if (msatoshi) { - if (!(*msatoshi <= route[routetok->size-1].amount && - route[routetok->size-1].amount <= 2 * *msatoshi)) { + if (msat) { + struct amount_msat limit = route[routetok->size-1].amount; + + if (amount_msat_less(*msat, limit)) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "msatoshi %"PRIu64" out of range", - *msatoshi); - } + "msatoshi %s less than final %s", + type_to_string(tmpctx, + struct amount_msat, + msat), + type_to_string(tmpctx, + struct amount_msat, + &route[routetok->size-1].amount)); + limit.millisatoshis *= 2; /* Raw: sanity check */ + if (amount_msat_greater(*msat, limit)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "msatoshi %s more than twice final %s", + type_to_string(tmpctx, + struct amount_msat, + msat), + type_to_string(tmpctx, + struct amount_msat, + &route[routetok->size-1].amount)); } - if (send_payment(cmd, cmd->ld, rhash, route, - msatoshi ? *msatoshi : route[routetok->size-1].amount, - description, - &json_sendpay_on_resolve, cmd)) - return command_still_pending(cmd); - return command_its_complicated("send_payment is called in multiple paths," - " patching return value through is hard"); + res = send_payment(cmd->ld, cmd, rhash, route, + msat ? *msat : route[routetok->size-1].amount, + label, b11str); + if (res) + return res; + return command_still_pending(cmd); } static const struct json_command sendpay_command = { @@ -1066,6 +901,7 @@ static struct command_result *json_waitsendpay(struct command *cmd, { struct sha256 *rhash; unsigned int *timeout; + struct command_result *res; if (!param(cmd, buffer, params, p_req("payment_hash", param_sha256, &rhash), @@ -1073,10 +909,9 @@ static struct command_result *json_waitsendpay(struct command *cmd, NULL)) return command_param_failed(); - if (!wait_payment(cmd, cmd->ld, rhash, &json_waitsendpay_on_resolve, cmd)) - return command_its_complicated("wait_payment called in multiple" - " paths, patching return value" - " through is hard"); + res = wait_payment(cmd->ld, cmd, rhash); + if (res) + return res; if (timeout) new_reltimer(&cmd->ld->timers, cmd, time_from_sec(*timeout), @@ -1092,7 +927,7 @@ static const struct json_command waitsendpay_command = { }; AUTODATA(json_command, &waitsendpay_command); -static struct command_result *json_listpayments(struct command *cmd, +static struct command_result *json_listsendpays(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) @@ -1145,7 +980,15 @@ static struct command_result *json_listpayments(struct command *cmd, static const struct json_command listpayments_command = { "listpayments", - json_listpayments, - "Show outgoing payments" + json_listsendpays, + "Show outgoing payments", + true /* deprecated, use new name */ }; AUTODATA(json_command, &listpayments_command); + +static const struct json_command listsendpays_command = { + "listsendpays", + json_listsendpays, + "Show sendpay, old and current, optionally limiting to {bolt11} or {payment_hash}." +}; +AUTODATA(json_command, &listsendpays_command); diff --git a/lightningd/pay.h b/lightningd/pay.h index bb586ee2cd0e..a3edef862d15 100644 --- a/lightningd/pay.h +++ b/lightningd/pay.h @@ -1,88 +1,11 @@ #ifndef LIGHTNING_LIGHTNINGD_PAY_H #define LIGHTNING_LIGHTNINGD_PAY_H #include "config.h" -#include -#include -#include -#include struct htlc_out; -struct json_stream; struct lightningd; -struct route_hop; +struct preimage; struct sha256; -struct wallet_payment; - -/* Routing failure object */ -struct routing_failure { - unsigned int erring_index; - enum onion_type failcode; - struct pubkey erring_node; - struct short_channel_id erring_channel; - int channel_dir; - u8 *channel_update; -}; - -/* Result of send_payment */ -struct sendpay_result { - /* Did the payment succeed? */ - bool succeeded; - /* Preimage. Only loaded if payment succeeded. */ - struct preimage preimage; - /* Error code, one of the PAY_* macro in jsonrpc_errors.h. - * Only loaded if payment failed. */ - int errorcode; - /* Pointer to the payment. Only loaded if payment - * succeeded or if error is PAY_IN_PROGRESS */ - const struct wallet_payment *payment; - /* Unparseable onion reply. Only loaded if payment failed, - * and errorcode == PAY_UNPARSEABLE_ONION. */ - const u8* onionreply; - /* Routing failure object. Only loaded if payment failed, - * and errorcode == PAY_DESTINATION_PERM_FAIL or - * errorcode == PAY_TRY_OTHER_ROUTE */ - struct routing_failure* routing_failure; - /* Error message. Only loaded if payment failed. */ - const char *details; -}; - -/* Initiate a payment. Return true if the callback will be - * scheduled for later, or false if the callback has already - * been called. If the given context is freed before the - * callback is called, then the callback will no longer be - * called. - * - * This will call the callback "soon" in 10ms or less. - * - * Typically the callback will be called with a failed - * sendpay_result indicating an error code of PAY_IN_PROGRESS. - * It will only call the callback with successful sendpay_result - * if the payment has already completed with the same amount - * and destination before. - * - * The msatoshi given is what is recorded in the payment. */ -bool send_payment(const tal_t *ctx, - struct lightningd* ld, - const struct sha256 *rhash, - const struct route_hop *route, - u64 msatoshi, - const char *description TAKES, - void (*cb)(const struct sendpay_result *, void*), - void *cbarg); - -/* Wait for a previous send_payment to complete in definite - * success or failure. If the given context is freed before - * the callback is called, then the callback will no longer - * be called. - * - * Return true if the payment is still pending on return, or - * false if the callback was already invoked before this - * function returned. */ -bool wait_payment(const tal_t *ctx, - struct lightningd* ld, - const struct sha256 *payment_hash, - void (*cb)(const struct sendpay_result *, void *cbarg), - void *cbarg); void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, const struct preimage *rval); @@ -93,10 +16,4 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, /* Inform payment system to save the payment. */ void payment_store(struct lightningd *ld, const struct sha256 *payment_hash); -/* Output the fields of a payment. Caller should have put the - * response within a JSON object and is responsible for - * closing the object. */ -void json_add_payment_fields(struct json_stream *response, - const struct wallet_payment *t); - #endif /* LIGHTNING_LIGHTNINGD_PAY_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index b97e3d4c83ee..3aa0d16df176 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -138,10 +139,16 @@ static void delete_peer(struct peer *peer) /* Last one out deletes peer. */ void maybe_delete_peer(struct peer *peer) { - if (peer->uncommitted_channel) - return; if (!list_empty(&peer->channels)) return; + if (peer->uncommitted_channel) { + /* This isn't sufficient to keep it in db! */ + if (peer->dbid != 0) { + wallet_peer_delete(peer->ld->wallet, peer->dbid); + peer->dbid = 0; + } + return; + } delete_peer(peer); } @@ -201,7 +208,7 @@ static void sign_last_tx(struct channel *channel) channel->last_tx, &channel->channel_info .remote_fundingkey, - channel->funding_satoshi); + channel->funding); if (!wire_sync_write(ld->hsm_fd, take(msg))) fatal("Could not write to HSM: %s", strerror(errno)); @@ -411,39 +418,320 @@ void channel_errmsg(struct channel *channel, err_for_them ? "sent" : "received", desc); } -/* Connectd tells us a peer has connected: it never hands us duplicates, since - * it holds them until we say peer_died. */ -void peer_connected(struct lightningd *ld, const u8 *msg, - int peer_fd, int gossip_fd) -{ - struct pubkey id; - struct crypto_state cs; - u8 *globalfeatures, *localfeatures; - u8 *error; +/* Possible outcomes returned by the `connected` hook. */ +enum peer_connected_hook_result { + PEER_CONNECTED_CONTINUE, + PEER_CONNECTED_DISCONNECT, +}; + +struct peer_connected_hook_payload { + struct lightningd *ld; + struct crypto_state crypto_state; struct channel *channel; struct wireaddr_internal addr; struct peer *peer; + int peer_fd; + int gossip_fd; +}; - if (!fromwire_connect_peer_connected(msg, msg, - &id, &addr, &cs, - &globalfeatures, &localfeatures)) - fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", - tal_hex(msg, msg)); +struct peer_connected_hook_response { + enum peer_connected_hook_result result; +}; - /* Complete any outstanding connect commands. */ - connect_succeeded(ld, &id); +static void json_add_htlcs(struct lightningd *ld, + struct json_stream *response, + const struct channel *channel) +{ + /* FIXME: make per-channel htlc maps! */ + const struct htlc_in *hin; + struct htlc_in_map_iter ini; + const struct htlc_out *hout; + struct htlc_out_map_iter outi; - /* If we're already dealing with this peer, hand off to correct - * subdaemon. Otherwise, we'll hand to openingd to wait there. */ - peer = peer_by_id(ld, &id); - if (!peer) - peer = new_peer(ld, 0, &id, &addr); + /* FIXME: Add more fields. */ + json_array_start(response, "htlcs"); + for (hin = htlc_in_map_first(&ld->htlcs_in, &ini); + hin; + hin = htlc_in_map_next(&ld->htlcs_in, &ini)) { + if (hin->key.channel != channel) + continue; - peer_update_features(peer, globalfeatures, localfeatures); + json_object_start(response, NULL); + json_add_string(response, "direction", "in"); + json_add_u64(response, "id", hin->key.id); + json_add_amount_msat(response, hin->msat, + "msatoshi", "amount_msat"); + json_add_u64(response, "expiry", hin->cltv_expiry); + json_add_hex(response, "payment_hash", + &hin->payment_hash, sizeof(hin->payment_hash)); + json_add_string(response, "state", + htlc_state_name(hin->hstate)); + json_object_end(response); + } - /* Can't be opening, since we wouldn't have sent peer_disconnected. */ - assert(!peer->uncommitted_channel); - channel = peer_active_channel(peer); + for (hout = htlc_out_map_first(&ld->htlcs_out, &outi); + hout; + hout = htlc_out_map_next(&ld->htlcs_out, &outi)) { + if (hout->key.channel != channel) + continue; + + json_object_start(response, NULL); + json_add_string(response, "direction", "out"); + json_add_u64(response, "id", hout->key.id); + json_add_amount_msat(response, hout->msat, + "msatoshi", "amount_msat"); + json_add_u64(response, "expiry", hout->cltv_expiry); + json_add_hex(response, "payment_hash", + &hout->payment_hash, sizeof(hout->payment_hash)); + json_add_string(response, "state", + htlc_state_name(hout->hstate)); + json_object_end(response); + } + json_array_end(response); +} + +/* We do this replication manually because it's an array. */ +static void json_add_sat_only(struct json_stream *result, + const char *fieldname, + struct amount_sat sat) +{ + struct amount_msat msat; + + if (amount_sat_to_msat(&msat, sat)) + json_add_member(result, fieldname, "\"%s\"", + type_to_string(tmpctx, struct amount_msat, &msat)); +} + +static void json_add_channel(struct lightningd *ld, + struct json_stream *response, const char *key, + const struct channel *channel) +{ + struct channel_id cid; + struct channel_stats channel_stats; + struct amount_msat spendable, funding_msat; + struct peer *p = channel->peer; + + json_object_start(response, key); + json_add_string(response, "state", channel_state_name(channel)); + if (channel->last_tx) { + struct bitcoin_txid txid; + bitcoin_txid(channel->last_tx, &txid); + + json_add_txid(response, "scratch_txid", &txid); + } + if (channel->owner) + json_add_string(response, "owner", channel->owner->name); + + if (channel->scid) { + json_add_short_channel_id(response, "short_channel_id", + channel->scid); + json_add_num(response, "direction", + pubkey_idx(&ld->id, &channel->peer->id)); + } + + derive_channel_id(&cid, &channel->funding_txid, + channel->funding_outnum); + json_add_string(response, "channel_id", + type_to_string(tmpctx, struct channel_id, &cid)); + json_add_txid(response, "funding_txid", &channel->funding_txid); + json_add_bool( + response, "private", + !(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)); + + // FIXME @conscott : Modify this when dual-funded channels + // are implemented + json_object_start(response, "funding_allocation_msat"); + if (channel->funder == LOCAL) { + json_add_u64(response, pubkey_to_hexstr(tmpctx, &p->id), 0); + json_add_u64(response, pubkey_to_hexstr(tmpctx, &ld->id), + channel->funding.satoshis * 1000); /* Raw: raw JSON field */ + } else { + json_add_u64(response, pubkey_to_hexstr(tmpctx, &ld->id), 0); + json_add_u64(response, pubkey_to_hexstr(tmpctx, &p->id), + channel->funding.satoshis * 1000); /* Raw: raw JSON field */ + } + json_object_end(response); + + json_object_start(response, "funding_msat"); + if (channel->funder == LOCAL) { + json_add_sat_only(response, + pubkey_to_hexstr(tmpctx, &p->id), + AMOUNT_SAT(0)); + json_add_sat_only(response, + pubkey_to_hexstr(tmpctx, &ld->id), + channel->funding); + } else { + json_add_sat_only(response, + pubkey_to_hexstr(tmpctx, &ld->id), + AMOUNT_SAT(0)); + json_add_sat_only(response, + pubkey_to_hexstr(tmpctx, &p->id), + channel->funding); + } + json_object_end(response); + + if (!amount_sat_to_msat(&funding_msat, channel->funding)) { + log_broken(channel->log, + "Overflow converting funding %s", + type_to_string(tmpctx, struct amount_sat, + &channel->funding)); + funding_msat = AMOUNT_MSAT(0); + } + json_add_amount_msat(response, channel->our_msat, + "msatoshi_to_us", "to_us_msat"); + json_add_amount_msat(response, channel->msat_to_us_min, + "msatoshi_to_us_min", "min_to_us_msat"); + json_add_amount_msat(response, channel->msat_to_us_max, + "msatoshi_to_us_max", "max_to_us_msat"); + json_add_amount_msat(response, funding_msat, + "msatoshi_total", "total_msat"); + + /* channel config */ + json_add_amount_sat(response, + channel->our_config.dust_limit, + "dust_limit_satoshis", "dust_limit_msat"); + json_add_amount_msat(response, + channel->our_config.max_htlc_value_in_flight, + "max_htlc_value_in_flight_msat", + "max_total_htlc_in_msat"); + + /* The `channel_reserve_satoshis` is imposed on + * the *other* side (see `channel_reserve_msat` + * function in, it uses `!side` to flip sides). + * So our configuration `channel_reserve_satoshis` + * is imposed on their side, while their + * configuration `channel_reserve_satoshis` is + * imposed on ours. */ + json_add_amount_sat(response, + channel->our_config.channel_reserve, + "their_channel_reserve_satoshis", + "their_reserve_msat"); + json_add_amount_sat(response, + channel->channel_info.their_config.channel_reserve, + "our_channel_reserve_satoshis", + "our_reserve_msat"); + /* Compute how much we can send via this channel. */ + if (!amount_msat_sub_sat(&spendable, + channel->our_msat, + channel->channel_info.their_config.channel_reserve)) + spendable = AMOUNT_MSAT(0); + + json_add_amount_msat(response, spendable, + "spendable_msatoshi", "spendable_msat"); + json_add_amount_msat(response, + channel->our_config.htlc_minimum, + "htlc_minimum_msat", + "minimum_htlc_in_msat"); + + /* The `to_self_delay` is imposed on the *other* + * side, so our configuration `to_self_delay` is + * imposed on their side, while their configuration + * `to_self_delay` is imposed on ours. */ + json_add_num(response, "their_to_self_delay", + channel->our_config.to_self_delay); + json_add_num(response, "our_to_self_delay", + channel->channel_info.their_config.to_self_delay); + json_add_num(response, "max_accepted_htlcs", + channel->our_config.max_accepted_htlcs); + + json_array_start(response, "status"); + for (size_t i = 0; i < ARRAY_SIZE(channel->billboard.permanent); i++) { + if (!channel->billboard.permanent[i]) + continue; + json_add_string(response, NULL, + channel->billboard.permanent[i]); + } + if (channel->billboard.transient) + json_add_string(response, NULL, channel->billboard.transient); + json_array_end(response); + + /* Provide channel statistics */ + wallet_channel_stats_load(ld->wallet, channel->dbid, &channel_stats); + json_add_u64(response, "in_payments_offered", + channel_stats.in_payments_offered); + json_add_amount_msat(response, + channel_stats.in_msatoshi_offered, + "in_msatoshi_offered", + "in_offered_msat"); + json_add_u64(response, "in_payments_fulfilled", + channel_stats.in_payments_fulfilled); + json_add_amount_msat(response, + channel_stats.in_msatoshi_fulfilled, + "in_msatoshi_fulfilled", + "in_fulfilled_msat"); + json_add_u64(response, "out_payments_offered", + channel_stats.out_payments_offered); + json_add_amount_msat(response, + channel_stats.out_msatoshi_offered, + "out_msatoshi_offered", + "out_offered_msat"); + json_add_u64(response, "out_payments_fulfilled", + channel_stats.out_payments_fulfilled); + json_add_amount_msat(response, + channel_stats.out_msatoshi_fulfilled, + "out_msatoshi_fulfilled", + "out_fulfilled_msat"); + + json_add_htlcs(ld, response, channel); + json_object_end(response); +} + +static void +peer_connected_serialize(struct peer_connected_hook_payload *payload, + struct json_stream *stream) +{ + const struct peer *p = payload->peer; + json_object_start(stream, "peer"); + json_add_pubkey(stream, "id", &p->id); + json_add_string( + stream, "addr", + type_to_string(stream, struct wireaddr_internal, &payload->addr)); + json_add_hex_talarr(stream, "globalfeatures", p->globalfeatures); + json_add_hex_talarr(stream, "localfeatures", p->localfeatures); + json_object_end(stream); /* .peer */ +} + +static struct peer_connected_hook_response * +peer_connected_deserialize(const tal_t *ctx, const char *buffer, + const jsmntok_t *toks) +{ + const jsmntok_t *resulttok; + struct peer_connected_hook_response *resp; + resulttok = json_get_member(buffer, toks, "result"); + if (!resulttok) { + return NULL; + } + + resp = tal(ctx, struct peer_connected_hook_response); + if (json_tok_streq(buffer, resulttok, "continue")) + resp->result = PEER_CONNECTED_CONTINUE; + else if (json_tok_streq(buffer, resulttok, "disconnect")) + resp->result = PEER_CONNECTED_DISCONNECT; + else + fatal("Plugin returned an invalid response to the connected " + "hook: %s", buffer); + + return resp; +} + +static void +peer_connected_hook_cb(struct peer_connected_hook_payload *payload, + struct peer_connected_hook_response *response) +{ + struct lightningd *ld = payload->ld; + struct crypto_state *cs = &payload->crypto_state; + struct channel *channel = payload->channel; + struct wireaddr_internal addr = payload->addr; + struct peer *peer = payload->peer; + int gossip_fd = payload->gossip_fd; + int peer_fd = payload->peer_fd; + u8 *error; + + if (response && response->result == PEER_CONNECTED_DISCONNECT) { + close(peer_fd); + tal_free(payload); + return; + } if (channel) { log_debug(channel->log, "Peer has reconnected, state %s", @@ -490,30 +778,82 @@ void peer_connected(struct lightningd *ld, const u8 *msg, assert(!channel->owner); channel->peer->addr = addr; - peer_start_channeld(channel, &cs, + peer_start_channeld(channel, cs, peer_fd, gossip_fd, NULL, true); + tal_free(payload); return; case CLOSINGD_SIGEXCHANGE: assert(!channel->owner); channel->peer->addr = addr; - peer_start_closingd(channel, &cs, + peer_start_closingd(channel, cs, peer_fd, gossip_fd, true, NULL); + tal_free(payload); return; } abort(); } - notify_connect(ld, &id, &addr); + notify_connect(ld, &peer->id, &addr); /* No err, all good. */ error = NULL; send_error: - peer_start_openingd(peer, &cs, peer_fd, gossip_fd, error); + peer_start_openingd(peer, cs, peer_fd, gossip_fd, error); + tal_free(payload); +} + +REGISTER_PLUGIN_HOOK(peer_connected, peer_connected_hook_cb, + struct peer_connected_hook_payload *, + peer_connected_serialize, + struct peer_connected_hook_payload *, + peer_connected_deserialize, + struct peer_connected_hook_response *); + +/* Connectd tells us a peer has connected: it never hands us duplicates, since + * it holds them until we say peer_died. */ +void peer_connected(struct lightningd *ld, const u8 *msg, + int peer_fd, int gossip_fd) +{ + struct pubkey id; + u8 *globalfeatures, *localfeatures; + struct peer *peer; + struct peer_connected_hook_payload *hook_payload; + + hook_payload = tal(NULL, struct peer_connected_hook_payload); + hook_payload->ld = ld; + hook_payload->gossip_fd = gossip_fd; + hook_payload->peer_fd = peer_fd; + + if (!fromwire_connect_peer_connected(msg, msg, + &id, &hook_payload->addr, &hook_payload->crypto_state, + &globalfeatures, &localfeatures)) + fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", + tal_hex(msg, msg)); + + /* Complete any outstanding connect commands. */ + connect_succeeded(ld, &id); + + /* If we're already dealing with this peer, hand off to correct + * subdaemon. Otherwise, we'll hand to openingd to wait there. */ + peer = peer_by_id(ld, &id); + if (!peer) + peer = new_peer(ld, 0, &id, &hook_payload->addr); + + tal_steal(peer, hook_payload); + hook_payload->peer = peer; + + peer_update_features(peer, globalfeatures, localfeatures); + + /* Can't be opening, since we wouldn't have sent peer_disconnected. */ + assert(!peer->uncommitted_channel); + hook_payload->channel = peer_active_channel(peer); + + plugin_hook_call_peer_connected(ld, hook_payload, hook_payload); } static enum watch_result funding_lockin_cb(struct lightningd *ld, @@ -598,56 +938,6 @@ void channel_watch_funding(struct lightningd *ld, struct channel *channel) funding_spent); } -static void json_add_htlcs(struct lightningd *ld, - struct json_stream *response, - const struct channel *channel) -{ - /* FIXME: make per-channel htlc maps! */ - const struct htlc_in *hin; - struct htlc_in_map_iter ini; - const struct htlc_out *hout; - struct htlc_out_map_iter outi; - - /* FIXME: Add more fields. */ - json_array_start(response, "htlcs"); - for (hin = htlc_in_map_first(&ld->htlcs_in, &ini); - hin; - hin = htlc_in_map_next(&ld->htlcs_in, &ini)) { - if (hin->key.channel != channel) - continue; - - json_object_start(response, NULL); - json_add_string(response, "direction", "in"); - json_add_u64(response, "id", hin->key.id); - json_add_u64(response, "msatoshi", hin->msatoshi); - json_add_u64(response, "expiry", hin->cltv_expiry); - json_add_hex(response, "payment_hash", - &hin->payment_hash, sizeof(hin->payment_hash)); - json_add_string(response, "state", - htlc_state_name(hin->hstate)); - json_object_end(response); - } - - for (hout = htlc_out_map_first(&ld->htlcs_out, &outi); - hout; - hout = htlc_out_map_next(&ld->htlcs_out, &outi)) { - if (hout->key.channel != channel) - continue; - - json_object_start(response, NULL); - json_add_string(response, "direction", "out"); - json_add_u64(response, "id", hout->key.id); - json_add_u64(response, "msatoshi", hout->msatoshi); - json_add_u64(response, "expiry", hout->cltv_expiry); - json_add_hex(response, "payment_hash", - &hout->payment_hash, sizeof(hout->payment_hash)); - json_add_string(response, "state", - htlc_state_name(hout->hstate)); - json_object_end(response); - } - json_array_end(response); -} - static void json_add_peer(struct lightningd *ld, struct json_stream *response, struct peer *p, @@ -693,138 +983,8 @@ static void json_add_peer(struct lightningd *ld, json_array_start(response, "channels"); json_add_uncommitted_channel(response, p->uncommitted_channel); - list_for_each(&p->channels, channel, list) { - struct channel_id cid; - struct channel_stats channel_stats; - u64 our_reserve_msat = channel->channel_info.their_config.channel_reserve_satoshis * 1000; - json_object_start(response, NULL); - json_add_string(response, "state", - channel_state_name(channel)); - if (channel->last_tx) { - struct bitcoin_txid txid; - bitcoin_txid(channel->last_tx, &txid); - - json_add_txid(response, "scratch_txid", &txid); - } - if (channel->owner) - json_add_string(response, "owner", - channel->owner->name); - if (channel->scid) { - json_add_short_channel_id(response, - "short_channel_id", - channel->scid); - json_add_num(response, "direction", - pubkey_idx(&ld->id, &p->id)); - } - derive_channel_id(&cid, - &channel->funding_txid, - channel->funding_outnum); - json_add_string(response, "channel_id", - type_to_string(tmpctx, struct channel_id, &cid)); - json_add_txid(response, - "funding_txid", - &channel->funding_txid); - json_add_bool(response, "private", - !(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)); - - // FIXME @conscott : Modify this when dual-funded channels - // are implemented - json_object_start(response, "funding_allocation_msat"); - if (channel->funder == LOCAL) { - json_add_u64(response, pubkey_to_hexstr(tmpctx, &p->id), 0); - json_add_u64(response, pubkey_to_hexstr(tmpctx, &ld->id), - channel->funding_satoshi * 1000); - } else { - json_add_u64(response, pubkey_to_hexstr(tmpctx, &ld->id), 0); - json_add_u64(response, pubkey_to_hexstr(tmpctx, &p->id), - channel->funding_satoshi * 1000); - } - json_object_end(response); - - json_add_u64(response, "msatoshi_to_us", - channel->our_msatoshi); - json_add_u64(response, "msatoshi_to_us_min", - channel->msatoshi_to_us_min); - json_add_u64(response, "msatoshi_to_us_max", - channel->msatoshi_to_us_max); - json_add_u64(response, "msatoshi_total", - channel->funding_satoshi * 1000); - - /* channel config */ - json_add_u64(response, "dust_limit_satoshis", - channel->our_config.dust_limit_satoshis); - json_add_u64(response, "max_htlc_value_in_flight_msat", - channel->our_config.max_htlc_value_in_flight_msat); - - /* The `channel_reserve_satoshis` is imposed on - * the *other* side (see `channel_reserve_msat` - * function in, it uses `!side` to flip sides). - * So our configuration `channel_reserve_satoshis` - * is imposed on their side, while their - * configuration `channel_reserve_satoshis` is - * imposed on ours. */ - json_add_u64(response, "their_channel_reserve_satoshis", - channel->our_config.channel_reserve_satoshis); - json_add_u64(response, "our_channel_reserve_satoshis", - channel->channel_info.their_config.channel_reserve_satoshis); - /* Compute how much we can send via this channel. */ - if (channel->our_msatoshi <= our_reserve_msat) - json_add_u64(response, "spendable_msatoshi", 0); - else - json_add_u64(response, "spendable_msatoshi", - channel->our_msatoshi - our_reserve_msat); - json_add_u64(response, "htlc_minimum_msat", - channel->our_config.htlc_minimum_msat); - - /* The `to_self_delay` is imposed on the *other* - * side, so our configuration `to_self_delay` is - * imposed on their side, while their configuration - * `to_self_delay` is imposed on ours. */ - json_add_num(response, "their_to_self_delay", - channel->our_config.to_self_delay); - json_add_num(response, "our_to_self_delay", - channel->channel_info.their_config.to_self_delay); - json_add_num(response, "max_accepted_htlcs", - channel->our_config.max_accepted_htlcs); - - json_array_start(response, "status"); - for (size_t i = 0; - i < ARRAY_SIZE(channel->billboard.permanent); - i++) { - if (!channel->billboard.permanent[i]) - continue; - json_add_string(response, NULL, - channel->billboard.permanent[i]); - } - if (channel->billboard.transient) - json_add_string(response, NULL, - channel->billboard.transient); - json_array_end(response); - - /* Provide channel statistics */ - wallet_channel_stats_load(ld->wallet, - channel->dbid, - &channel_stats); - json_add_u64(response, "in_payments_offered", - channel_stats.in_payments_offered); - json_add_u64(response, "in_msatoshi_offered", - channel_stats.in_msatoshi_offered); - json_add_u64(response, "in_payments_fulfilled", - channel_stats.in_payments_fulfilled); - json_add_u64(response, "in_msatoshi_offered_fulfilled", - channel_stats.in_msatoshi_fulfilled); - json_add_u64(response, "out_payments_offered", - channel_stats.out_payments_offered); - json_add_u64(response, "out_msatoshi_offered", - channel_stats.out_msatoshi_offered); - json_add_u64(response, "out_payments_fulfilled", - channel_stats.out_payments_fulfilled); - json_add_u64(response, "out_msatoshi_offered_fulfilled", - channel_stats.out_msatoshi_fulfilled); - - json_add_htlcs(ld, response, channel); - json_object_end(response); - } + list_for_each(&p->channels, channel, list) + json_add_channel(ld, response, NULL, channel); json_array_end(response); if (ll) @@ -897,7 +1057,8 @@ command_find_channel(struct command *cmd, "Channel ID not found: '%.*s'", tok->end - tok->start, buffer + tok->start); - } else if (json_to_short_channel_id(buffer, tok, &scid)) { + } else if (json_to_short_channel_id(buffer, tok, &scid, + deprecated_apis)) { list_for_each(&ld->peers, peer, list) { *channel = peer_active_channel(peer); if (!*channel) @@ -1168,8 +1329,9 @@ static struct command_result *json_getinfo(struct command *cmd, json_add_string(response, "version", version()); json_add_num(response, "blockheight", get_block_height(cmd->ld->topology)); json_add_string(response, "network", get_chainparams(cmd->ld)->network_name); - json_add_u64(response, "msatoshi_fees_collected", - wallet_total_forward_fees(cmd->ld->wallet)); + json_add_amount_msat(response, + wallet_total_forward_fees(cmd->ld->wallet), + "msatoshi_fees_collected", "fees_collected_msat"); json_object_end(response); return command_success(cmd, response); } @@ -1406,7 +1568,8 @@ static struct command_result *json_dev_forget_channel(struct command *cmd, } if (!forget->channel) { return command_fail(cmd, LIGHTNINGD, - "No channels matching that short_channel_id"); + "No channels matching that peer_id%s", + scid ? " and that short_channel_id" : ""); } if (channel_has_htlc_out(forget->channel) || diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index ae33608934f5..acc2e6a2873b 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -167,13 +167,22 @@ static void fail_out_htlc(struct htlc_out *hout, const char *localfail) * the final node. */ static bool check_amount(struct htlc_in *hin, - u64 amt_to_forward, u64 amt_in_htlc, u64 fee) + struct amount_msat amt_to_forward, + struct amount_msat amt_in_htlc, + struct amount_msat fee) { - if (amt_in_htlc - fee >= amt_to_forward) + struct amount_msat fwd; + + if (amount_msat_sub(&fwd, amt_in_htlc, fee) + && amount_msat_greater_eq(fwd, amt_to_forward)) return true; + log_debug(hin->key.channel->log, "HTLC %"PRIu64" incorrect amount:" - " %"PRIu64" in, %"PRIu64" out, fee reqd %"PRIu64, - hin->key.id, amt_in_htlc, amt_to_forward, fee); + " %s in, %s out, fee reqd %s", + hin->key.id, + type_to_string(tmpctx, struct amount_msat, &amt_in_htlc), + type_to_string(tmpctx, struct amount_msat, &amt_to_forward), + type_to_string(tmpctx, struct amount_msat, &fee)); return false; } @@ -223,7 +232,7 @@ static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) /* Update channel stats */ wallet_channel_stats_incr_in_fulfilled(wallet, channel->dbid, - hin->msatoshi); + hin->msat); /* No owner? We'll either send to channeld in peer_htlcs, or * onchaind in onchaind_tell_fulfill. */ @@ -246,7 +255,7 @@ static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) static void handle_localpay(struct htlc_in *hin, u32 cltv_expiry, const struct sha256 *payment_hash, - u64 amt_to_forward, + struct amount_msat amt_to_forward, u32 outgoing_cltv_value) { enum onion_type failcode; @@ -262,7 +271,7 @@ static void handle_localpay(struct htlc_in *hin, * * The amount in the HTLC doesn't match the value in the onion. */ - if (!check_amount(hin, amt_to_forward, hin->msatoshi, 0)) { + if (!check_amount(hin, amt_to_forward, hin->msat, AMOUNT_MSAT(0))) { failcode = WIRE_FINAL_INCORRECT_HTLC_AMOUNT; goto fail; } @@ -293,19 +302,26 @@ static void handle_localpay(struct htlc_in *hin, * - if the amount paid is less than the amount expected: * - MUST fail the HTLC. */ - if (details->msatoshi != NULL && hin->msatoshi < *details->msatoshi) { - failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; - goto fail; - } else if (details->msatoshi != NULL && hin->msatoshi > *details->msatoshi * 2) { - /* FIXME: bolt update fixes this quote! */ - /* BOLT #4: - * - * - if the amount paid is more than twice the amount expected: - * - SHOULD fail the HTLC. - * - SHOULD return an `incorrect_payment_amount` error. - */ - failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; - goto fail; + if (details->msat != NULL) { + struct amount_msat twice; + + if (amount_msat_less(hin->msat, *details->msat)) { + failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; + goto fail; + } + + if (amount_msat_add(&twice, *details->msat, *details->msat) + && amount_msat_greater(hin->msat, twice)) { + /* FIXME: bolt update fixes this quote! */ + /* BOLT #4: + * + * - if the amount paid is more than twice the amount expected: + * - SHOULD fail the HTLC. + * - SHOULD return an `incorrect_payment_amount` error. + */ + failcode = WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS; + goto fail; + } } /* BOLT #4: @@ -327,10 +343,12 @@ static void handle_localpay(struct htlc_in *hin, log_info(ld->log, "Resolving invoice '%s' with HTLC %"PRIu64, details->label->s, hin->key.id); - log_debug(ld->log, "%s: Actual amount %"PRIu64"msat, HTLC expiry %u", - details->label->s, hin->msatoshi, cltv_expiry); + log_debug(ld->log, "%s: Actual amount %s, HTLC expiry %u", + details->label->s, + type_to_string(tmpctx, struct amount_msat, &hin->msat), + cltv_expiry); fulfill_htlc(hin, &details->r); - wallet_invoice_resolve(ld->wallet, invoice, hin->msatoshi); + wallet_invoice_resolve(ld->wallet, invoice, hin->msat); return; @@ -429,7 +447,8 @@ static void htlc_offer_timeout(struct channel *channel) "Adding HTLC timed out: killed channel"); } -enum onion_type send_htlc_out(struct channel *out, u64 amount, u32 cltv, +enum onion_type send_htlc_out(struct channel *out, + struct amount_msat amount, u32 cltv, const struct sha256 *payment_hash, const u8 *onion_routing_packet, struct htlc_in *in, @@ -472,13 +491,13 @@ enum onion_type send_htlc_out(struct channel *out, u64 amount, u32 cltv, static void forward_htlc(struct htlc_in *hin, u32 cltv_expiry, - u64 amt_to_forward, + struct amount_msat amt_to_forward, u32 outgoing_cltv_value, const struct pubkey *next_hop, const u8 next_onion[TOTAL_PACKET_SIZE]) { enum onion_type failcode; - u64 fee; + struct amount_msat fee; struct lightningd *ld = hin->key.channel->peer->ld; struct channel *next = active_channel_by_id(ld, next_hop, NULL); @@ -494,14 +513,16 @@ static void forward_htlc(struct htlc_in *hin, * - SHOULD accept HTLCs that pay a fee equal to or greater than: * - fee_base_msat + ( amount_to_forward * fee_proportional_millionths / 1000000 ) */ - if (mul_overflows_u64(amt_to_forward, - ld->config.fee_per_satoshi)) { + if (!amount_msat_fee(&fee, amt_to_forward, + ld->config.fee_base, + ld->config.fee_per_satoshi)) { + log_broken(ld->log, "Fee overflow forwarding %s!", + type_to_string(tmpctx, struct amount_msat, + &amt_to_forward)); failcode = WIRE_FEE_INSUFFICIENT; goto fail; } - fee = ld->config.fee_base - + amt_to_forward * ld->config.fee_per_satoshi / 1000000; - if (!check_amount(hin, amt_to_forward, hin->msatoshi, fee)) { + if (!check_amount(hin, amt_to_forward, hin->msat, fee)) { failcode = WIRE_FEE_INSUFFICIENT; goto fail; } @@ -559,7 +580,7 @@ static void forward_htlc(struct htlc_in *hin, /* Temporary information, while we resolve the next hop */ struct gossip_resolve { struct short_channel_id next_channel; - u64 amt_to_forward; + struct amount_msat amt_to_forward; u32 outgoing_cltv_value; u8 *next_onion; struct htlc_in *hin; @@ -721,7 +742,7 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, /* Update channel stats */ wallet_channel_stats_incr_out_fulfilled(ld->wallet, channel->dbid, - hout->msatoshi); + hout->msat); if (hout->am_origin) payment_succeeded(ld, hout, preimage); @@ -874,12 +895,25 @@ static void remove_htlc_in(struct channel *channel, struct htlc_in *hin) /* If we fulfilled their HTLC, credit us. */ if (hin->preimage) { - log_debug(channel->log, "Balance %"PRIu64" -> %"PRIu64, - channel->our_msatoshi, - channel->our_msatoshi + hin->msatoshi); - channel->our_msatoshi += hin->msatoshi; - if (channel->our_msatoshi > channel->msatoshi_to_us_max) - channel->msatoshi_to_us_max = channel->our_msatoshi; + struct amount_msat oldamt = channel->our_msat; + if (!amount_msat_add(&channel->our_msat, channel->our_msat, + hin->msat)) { + channel_internal_error(channel, + "Overflow our_msat %s + HTLC %s", + type_to_string(tmpctx, + struct amount_msat, + &channel->our_msat), + type_to_string(tmpctx, + struct amount_msat, + &hin->msat)); + } + log_debug(channel->log, "Balance %s -> %s", + type_to_string(tmpctx, struct amount_msat, &oldamt), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + if (amount_msat_greater(channel->our_msat, + channel->msat_to_us_max)) + channel->msat_to_us_max = channel->our_msat; } tal_free(hin); @@ -899,13 +933,26 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout) if (!hout->preimage) { fail_out_htlc(hout, NULL); } else { + struct amount_msat oldamt = channel->our_msat; /* We paid for this HTLC, so deduct balance. */ - log_debug(channel->log, "Balance %"PRIu64" -> %"PRIu64, - channel->our_msatoshi, - channel->our_msatoshi - hout->msatoshi); - channel->our_msatoshi -= hout->msatoshi; - if (channel->our_msatoshi < channel->msatoshi_to_us_min) - channel->msatoshi_to_us_min = channel->our_msatoshi; + if (!amount_msat_sub(&channel->our_msat, channel->our_msat, + hout->msat)) { + channel_internal_error(channel, + "Underflow our_msat %s - HTLC %s", + type_to_string(tmpctx, + struct amount_msat, + &channel->our_msat), + type_to_string(tmpctx, + struct amount_msat, + &hout->msat)); + } + + log_debug(channel->log, "Balance %s -> %s", + type_to_string(tmpctx, struct amount_msat, &oldamt), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + if (amount_msat_less(channel->our_msat, channel->msat_to_us_min)) + channel->msat_to_us_min = channel->our_msat; } tal_free(hout); @@ -950,7 +997,7 @@ static bool update_out_htlc(struct channel *channel, /* Update channel stats */ wallet_channel_stats_incr_out_offered(ld->wallet, channel->dbid, - hout->msatoshi); + hout->msat); if (hout->in) wallet_forwarded_payment_add(ld->wallet, hout->in, hout, @@ -1107,13 +1154,17 @@ static bool channel_added_their_htlc(struct channel *channel, * - receiving an `amount_msat` equal to 0, OR less than its own `htlc_minimum_msat`: * - SHOULD fail the channel. */ - if (added->amount_msat == 0 - || added->amount_msat < channel->our_config.htlc_minimum_msat) { + if (amount_msat_eq(added->amount, AMOUNT_MSAT(0)) + || amount_msat_less(added->amount, channel->our_config.htlc_minimum)) { channel_internal_error(channel, - "trying to add HTLC msat %"PRIu64 - " but minimum is %"PRIu64, - added->amount_msat, - channel->our_config.htlc_minimum_msat); + "trying to add HTLC amount %s" + " but minimum is %s", + type_to_string(tmpctx, + struct amount_msat, + &added->amount), + type_to_string(tmpctx, + struct amount_msat, + &channel->our_config.htlc_minimum)); return false; } @@ -1124,7 +1175,7 @@ static bool channel_added_their_htlc(struct channel *channel, /* This stays around even if we fail it immediately: it *is* * part of the current commitment. */ - hin = new_htlc_in(channel, channel, added->id, added->amount_msat, + hin = new_htlc_in(channel, channel, added->id, added->amount, added->cltv_expiry, &added->payment_hash, shared_secret, added->onion_routing_packet); @@ -1132,7 +1183,7 @@ static bool channel_added_their_htlc(struct channel *channel, wallet_htlc_save_in(ld->wallet, channel, hin); /* Update channel stats */ wallet_channel_stats_incr_in_offered(ld->wallet, channel->dbid, - added->amount_msat); + added->amount); log_debug(channel->log, "Adding their HTLC %"PRIu64, added->id); connect_htlc_in(&channel->peer->ld->htlcs_in, hin); @@ -1384,7 +1435,7 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) static void add_htlc(struct added_htlc **htlcs, enum htlc_state **htlc_states, u64 id, - u64 amount_msat, + struct amount_msat amount, const struct sha256 *payment_hash, u32 cltv_expiry, const u8 onion_routing_packet[TOTAL_PACKET_SIZE], @@ -1393,7 +1444,7 @@ static void add_htlc(struct added_htlc **htlcs, struct added_htlc a; a.id = id; - a.amount_msat = amount_msat; + a.amount = amount; a.payment_hash = *payment_hash; a.cltv_expiry = cltv_expiry; memcpy(a.onion_routing_packet, onion_routing_packet, @@ -1476,7 +1527,7 @@ void peer_htlcs(const tal_t *ctx, continue; add_htlc(htlcs, htlc_states, - hin->key.id, hin->msatoshi, &hin->payment_hash, + hin->key.id, hin->msat, &hin->payment_hash, hin->cltv_expiry, hin->onion_routing_packet, hin->hstate); @@ -1496,7 +1547,7 @@ void peer_htlcs(const tal_t *ctx, continue; add_htlc(htlcs, htlc_states, - hout->key.id, hout->msatoshi, &hout->payment_hash, + hout->key.id, hout->msat, &hout->payment_hash, hout->cltv_expiry, hout->onion_routing_packet, hout->hstate); @@ -1699,11 +1750,11 @@ static void fixup_hout(struct lightningd *ld, struct htlc_out *hout) } log_broken(ld->log, "HTLC #%"PRIu64" (%s) " - " for amount %"PRIu64 + " for amount %s" " to %s" " is missing a resolution: %s.", hout->key.id, htlc_state_name(hout->hstate), - hout->msatoshi, + type_to_string(tmpctx, struct amount_msat, &hout->msat), type_to_string(tmpctx, struct pubkey, &hout->key.channel->peer->id), fix); @@ -1841,9 +1892,15 @@ static void listforwardings_add_forwardings(struct json_stream *response, struct json_add_short_channel_id(response, "in_channel", &cur->channel_in); json_add_short_channel_id(response, "out_channel", &cur->channel_out); - json_add_num(response, "in_msatoshi", cur->msatoshi_in); - json_add_num(response, "out_msatoshi", cur->msatoshi_out); - json_add_num(response, "fee", cur->fee); + json_add_amount_msat(response, + cur->msat_in, + "in_msatoshi", "in_msat"); + json_add_amount_msat(response, + cur->msat_out, + "out_msatoshi", "out_msat"); + json_add_amount_msat(response, + cur->fee, + "fee", "fee_msat"); json_add_string(response, "status", forward_status_name(cur->status)); json_object_end(response); } diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index 6f3ad3646e2b..27f601b864ac 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -44,7 +44,8 @@ void peer_got_revoke(struct channel *channel, const u8 *msg); void update_per_commit_point(struct channel *channel, const struct pubkey *per_commitment_point); -enum onion_type send_htlc_out(struct channel *out, u64 amount, u32 cltv, +enum onion_type send_htlc_out(struct channel *out, + struct amount_msat amount, u32 cltv, const struct sha256 *payment_hash, const u8 *onion_routing_packet, struct htlc_in *in, diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 7f4d7c88aa9b..91e39cbe3cef 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -76,15 +77,10 @@ struct plugins { struct log *log; struct log_book *log_book; - /* RPC interface to bind JSON-RPC methods to */ - struct jsonrpc *rpc; - struct timers timers; struct lightningd *ld; }; -/* Simple storage for plugin options inbetween registering them on the - * command line and passing them off to the plugin */ struct plugin_opt { struct list_node list; const char *name; @@ -93,14 +89,13 @@ struct plugin_opt { }; struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, - struct jsonrpc *rpc, struct lightningd *ld) + struct lightningd *ld) { struct plugins *p; p = tal(ctx, struct plugins); list_head_init(&p->plugins); p->log_book = log_book; p->log = new_log(p, log_book, "plugin-manager"); - p->rpc = rpc; timers_init(&p->timers, time_mono()); p->ld = ld; return p; @@ -466,9 +461,7 @@ static struct io_plan *plugin_stdout_conn_init(struct io_conn *conn, plugin_read_json, plugin); } -/* Callback called when parsing options. It just stores the value in - * the plugin_opt */ -static char *plugin_opt_set(const char *arg, struct plugin_opt *popt) +char *plugin_opt_set(const char *arg, struct plugin_opt *popt) { tal_free(popt->value); popt->value = tal_strdup(popt, arg); @@ -547,12 +540,6 @@ static bool plugin_opts_add(struct plugin *plugin, return true; } -static void plugin_rpcmethod_destroy(struct json_command *cmd, - struct jsonrpc *rpc) -{ - jsonrpc_command_remove(rpc, cmd->name); -} - static void json_stream_forward_change_id(struct json_stream *stream, const char *buffer, const jsmntok_t *toks, @@ -620,11 +607,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, struct jsonrpc_request *req; char id[STR_MAX_CHARS(u64)]; - if (cmd->mode == CMD_USAGE || cmd->mode == CMD_CHECK) { - /* FIXME! */ - cmd->usage = "[params]"; + if (cmd->mode == CMD_CHECK) return command_param_failed(); - } plugin = find_plugin_for_command(cmd); @@ -632,7 +616,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, idtok = json_get_member(buffer, toks, "id"); assert(idtok != NULL); - req = jsonrpc_request_start(plugin, NULL, plugin_rpcmethod_cb, cmd); + req = jsonrpc_request_start(plugin, NULL, plugin->log, + plugin_rpcmethod_cb, cmd); snprintf(id, ARRAY_SIZE(id), "%"PRIu64, req->id); json_stream_forward_change_id(req->stream, buffer, toks, idtok, id); @@ -646,12 +631,14 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, const char *buffer, const jsmntok_t *meth) { - const jsmntok_t *nametok, *desctok, *longdesctok; + const jsmntok_t *nametok, *desctok, *longdesctok, *usagetok; struct json_command *cmd; + const char *usage; nametok = json_get_member(buffer, meth, "name"); desctok = json_get_member(buffer, meth, "description"); longdesctok = json_get_member(buffer, meth, "long_description"); + usagetok = json_get_member(buffer, meth, "usage"); if (!nametok || nametok->type != JSMN_STRING) { plugin_kill(plugin, @@ -675,6 +662,13 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, return false; } + if (usagetok && usagetok->type != JSMN_STRING) { + plugin_kill(plugin, + "\"usage\" is not a string: %.*s", + meth->end - meth->start, buffer + meth->start); + return false; + } + cmd = notleak(tal(plugin, struct json_command)); cmd->name = json_strdup(cmd, buffer, nametok); cmd->description = json_strdup(cmd, buffer, desctok); @@ -682,11 +676,18 @@ static bool plugin_rpcmethod_add(struct plugin *plugin, cmd->verbose = json_strdup(cmd, buffer, longdesctok); else cmd->verbose = cmd->description; + if (usagetok) + usage = json_strdup(tmpctx, buffer, usagetok); + else if (!deprecated_apis) { + plugin_kill(plugin, + "\"usage\" not provided by plugin"); + return false; + } else + usage = "[params]"; cmd->deprecated = false; cmd->dispatch = plugin_rpcmethod_dispatch; - tal_add_destructor2(cmd, plugin_rpcmethod_destroy, plugin->plugins->rpc); - if (!jsonrpc_command_add(plugin->plugins->rpc, cmd)) { + if (!jsonrpc_command_add(plugin->plugins->ld->jsonrpc, cmd, usage)) { log_broken(plugin->log, "Could not register method \"%s\", a method with " "that name is already registered", @@ -809,7 +810,9 @@ static void plugin_manifest_cb(const char *buffer, resulttok = json_get_member(buffer, toks, "result"); if (!resulttok || resulttok->type != JSMN_OBJECT) { plugin_kill(plugin, - "\"getmanifest\" result is not an object"); + "\"getmanifest\" result is not an object: %.*s", + toks[0].end - toks[0].start, + buffer + toks[0].start); return; } @@ -928,7 +931,8 @@ void plugins_init(struct plugins *plugins, const char *dev_plugin_debug) * write-only on p->stdout */ io_new_conn(p, stdout, plugin_stdout_conn_init, p); io_new_conn(p, stdin, plugin_stdin_conn_init, p); - req = jsonrpc_request_start(p, "getmanifest", plugin_manifest_cb, p); + req = jsonrpc_request_start(p, "getmanifest", p->log, + plugin_manifest_cb, p); jsonrpc_request_end(req); plugin_request_send(p, req); @@ -971,7 +975,8 @@ static void plugin_config(struct plugin *plugin) const char *name; struct jsonrpc_request *req; struct lightningd *ld = plugin->plugins->ld; - req = jsonrpc_request_start(plugin, "init", plugin_config_cb, plugin); + req = jsonrpc_request_start(plugin, "init", plugin->log, + plugin_config_cb, plugin); /* Add .params.options */ json_object_start(req->stream, "options"); diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 408a5c95a639..47b1187863d2 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -18,11 +18,17 @@ struct plugins; */ struct plugin; +/** + * Simple storage for plugin options inbetween registering them on the + * command line and passing them off to the plugin + */ +struct plugin_opt; + /** * Create a new plugins context. */ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, - struct jsonrpc *rpc, struct lightningd *ld); + struct lightningd *ld); /** * Initialize the registered plugins. @@ -93,4 +99,10 @@ void plugins_notify(struct plugins *plugins, void plugin_request_send(struct plugin *plugin, struct jsonrpc_request *req TAKES); +/** + * Callback called when parsing options. It just stores the value in + * the plugin_opt + */ +char *plugin_opt_set(const char *arg, struct plugin_opt *popt); + #endif /* LIGHTNING_LIGHTNINGD_PLUGIN_H */ diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index cefea2365b6c..0a5975386b5a 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -71,7 +71,8 @@ void plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, * currently have a list to store these. We might want * to eventually to inspect in-flight requests. */ ph_req = notleak(tal(hook->plugin, struct plugin_hook_request)); - req = jsonrpc_request_start(NULL, hook->name, + /* FIXME: do IO logging for these! */ + req = jsonrpc_request_start(NULL, hook->name, NULL, plugin_hook_callback, ph_req); ph_req->hook = hook; ph_req->cb_arg = cb_arg; diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index f0b7c3504d84..17e2fc5392b6 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -8,6 +8,7 @@ ALL_TEST_PROGRAMS += $(LIGHTNINGD_TEST_PROGRAMS) ALL_OBJS += $(LIGHTNINGD_TEST_OBJS) LIGHTNINGD_TEST_COMMON_OBJS := \ + common/amount.o \ common/bech32.o \ common/daemon_conn.o \ common/htlc_state.o \ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 47c5e7ed4086..0a09c6452e02 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -4,6 +4,7 @@ int unused_main(int argc, char *argv[]); #include "../../common/wireaddr.c" #include "../lightningd.c" #include "../subd.c" +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for activate_peers */ @@ -94,9 +95,9 @@ void htlcs_notify_new_block(struct lightningd *ld UNNEEDED, u32 height UNNEEDED) /* Generated stub for jsonrpc_listen */ void jsonrpc_listen(struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED) { fprintf(stderr, "jsonrpc_listen called!\n"); abort(); } -/* Generated stub for jsonrpc_new */ -struct jsonrpc *jsonrpc_new(const tal_t *ctx UNNEEDED, struct lightningd *ld UNNEEDED) -{ fprintf(stderr, "jsonrpc_new called!\n"); abort(); } +/* Generated stub for jsonrpc_setup */ +void jsonrpc_setup(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "jsonrpc_setup called!\n"); abort(); } /* Generated stub for load_channels_from_wallet */ void load_channels_from_wallet(struct lightningd *ld UNNEEDED) { fprintf(stderr, "load_channels_from_wallet called!\n"); abort(); } @@ -137,7 +138,7 @@ void plugins_init(struct plugins *plugins UNNEEDED, const char *dev_plugin_debug { fprintf(stderr, "plugins_init called!\n"); abort(); } /* Generated stub for plugins_new */ struct plugins *plugins_new(const tal_t *ctx UNNEEDED, struct log_book *log_book UNNEEDED, - struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED) + struct lightningd *ld UNNEEDED) { fprintf(stderr, "plugins_new called!\n"); abort(); } /* Generated stub for register_opts */ void register_opts(struct lightningd *ld UNNEEDED) diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 3a128469caca..8bea981bd41d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -120,6 +120,20 @@ void json_add_address_internal(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "json_add_address_internal called!\n"); abort(); } +/* Generated stub for json_add_amount_msat */ +void json_add_amount_msat(struct json_stream *result UNNEEDED, + struct amount_msat msat UNNEEDED, + const char *rawfieldname UNNEEDED, + const char *msatfieldname) + +{ fprintf(stderr, "json_add_amount_msat called!\n"); abort(); } +/* Generated stub for json_add_amount_sat */ +void json_add_amount_sat(struct json_stream *result UNNEEDED, + struct amount_sat sat UNNEEDED, + const char *rawfieldname UNNEEDED, + const char *satfieldname) + +{ fprintf(stderr, "json_add_amount_sat called!\n"); abort(); } /* Generated stub for json_add_bool */ void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, bool value UNNEEDED) @@ -142,6 +156,10 @@ void json_add_hex_talarr(struct json_stream *result UNNEEDED, void json_add_log(struct json_stream *result UNNEEDED, const struct log_book *lr UNNEEDED, enum log_level minlevel UNNEEDED) { fprintf(stderr, "json_add_log called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } /* Generated stub for json_add_num */ void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, unsigned int value UNNEEDED) @@ -207,7 +225,8 @@ bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, { fprintf(stderr, "json_to_pubkey called!\n"); abort(); } /* Generated stub for json_to_short_channel_id */ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) + struct short_channel_id *scid UNNEEDED, + bool may_be_deprecated_form UNNEEDED) { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, @@ -225,7 +244,8 @@ void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *c const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "log_io called!\n"); abort(); } /* Generated stub for new_bolt11 */ -struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, u64 *msatoshi UNNEEDED) +struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, + const struct amount_msat *msat TAKES UNNEEDED) { fprintf(stderr, "new_bolt11 called!\n"); abort(); } /* Generated stub for new_log */ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) @@ -291,11 +311,6 @@ struct command_result *param_loglevel(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, enum log_level **level UNNEEDED) { fprintf(stderr, "param_loglevel called!\n"); abort(); } -/* Generated stub for param_msat */ -struct command_result *param_msat(struct command *cmd UNNEEDED, const char *name UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED, - u64 **msatoshi_val UNNEEDED) -{ fprintf(stderr, "param_msat called!\n"); abort(); } /* Generated stub for param_number */ struct command_result *param_number(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -351,6 +366,10 @@ void peer_start_openingd(struct peer *peer UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } +/* Generated stub for plugin_hook_call_ */ +void plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, + void *payload UNNEEDED, void *cb_arg UNNEEDED) +{ fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } /* Generated stub for set_log_outfn_ */ void set_log_outfn_(struct log_book *lr UNNEEDED, void (*print)(const char *prefix UNNEEDED, @@ -403,7 +422,7 @@ u8 *towire_gossip_get_incoming_channels(const tal_t *ctx UNNEEDED, const bool *p u8 *towire_hsm_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct pubkey *peerid UNNEEDED, u64 dbid UNNEEDED) { fprintf(stderr, "towire_hsm_get_channel_basepoints called!\n"); abort(); } /* Generated stub for towire_hsm_sign_commitment_tx */ -u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct pubkey *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, u64 funding_amount UNNEEDED) +u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct pubkey *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, struct amount_sat funding_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_commitment_tx called!\n"); abort(); } /* Generated stub for towire_hsm_sign_invoice */ u8 *towire_hsm_sign_invoice(const tal_t *ctx UNNEEDED, const u8 *u5bytes UNNEEDED, const u8 *hrp UNNEEDED) @@ -448,7 +467,7 @@ void wallet_invoice_autoclean(struct wallet * wallet UNNEEDED, /* Generated stub for wallet_invoice_create */ bool wallet_invoice_create(struct wallet *wallet UNNEEDED, struct invoice *pinvoice UNNEEDED, - u64 *msatoshi TAKES UNNEEDED, + const struct amount_msat *msat TAKES UNNEEDED, const struct json_escaped *label TAKES UNNEEDED, u64 expiry UNNEEDED, const char *b11enc UNNEEDED, @@ -506,7 +525,7 @@ void wallet_invoice_waitone(const tal_t *ctx UNNEEDED, void wallet_peer_delete(struct wallet *w UNNEEDED, u64 peer_dbid UNNEEDED) { fprintf(stderr, "wallet_peer_delete called!\n"); abort(); } /* Generated stub for wallet_total_forward_fees */ -u64 wallet_total_forward_fees(struct wallet *w UNNEEDED) +struct amount_msat wallet_total_forward_fees(struct wallet *w UNNEEDED) { fprintf(stderr, "wallet_total_forward_fees called!\n"); abort(); } /* Generated stub for wallet_transaction_locate */ struct txlocator *wallet_transaction_locate(const tal_t *ctx UNNEEDED, struct wallet *w UNNEEDED, @@ -564,9 +583,9 @@ static void add_peer(struct lightningd *ld, int n, enum channel_state state, c->state = state; c->owner = connected ? (void *)peer : NULL; /* Channel has incoming capacity n*1000 - 1 millisatoshi */ - c->funding_satoshi = n+1; - c->our_msatoshi = 1; - c->our_config.channel_reserve_satoshis = 1; + c->funding.satoshis = n+1; + c->our_msat = AMOUNT_MSAT(1); + c->our_config.channel_reserve = AMOUNT_SAT(1); list_add_tail(&peer->channels, &c->list); } @@ -596,47 +615,47 @@ int main(void) inchans = tal_arr(tmpctx, struct route_info, 0); /* Nothing to choose from -> NULL result. */ - assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL); + assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline) == NULL); assert(any_offline == false); /* inchan but no peer -> NULL result. */ add_inchan(&inchans, 0); - assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL); + assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline) == NULL); assert(any_offline == false); /* connected peer but no inchan -> NULL result. */ add_peer(ld, 1, CHANNELD_NORMAL, false); - assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL); + assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline) == NULL); assert(any_offline == false); /* inchan but peer awaiting lockin -> NULL result. */ add_peer(ld, 0, CHANNELD_AWAITING_LOCKIN, true); - assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL); + assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline) == NULL); assert(any_offline == false); /* inchan but peer not connected -> NULL result. */ add_inchan(&inchans, 1); - assert(select_inchan(tmpctx, ld, 0, inchans, &any_offline) == NULL); + assert(select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline) == NULL); assert(any_offline == true); /* Finally, a correct peer! */ add_inchan(&inchans, 2); add_peer(ld, 2, CHANNELD_NORMAL, true); - ret = select_inchan(tmpctx, ld, 0, inchans, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(0), inchans, &any_offline); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); assert(any_offline == true); assert(route_info_eq(ret[0], &inchans[2])); /* Not if we ask for too much! Reserve is 1 satoshi */ - ret = select_inchan(tmpctx, ld, 1999, inchans, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1999), inchans, &any_offline); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); assert(any_offline == false); /* Other candidate insufficient funds. */ assert(route_info_eq(ret[0], &inchans[2])); - ret = select_inchan(tmpctx, ld, 2000, inchans, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(2000), inchans, &any_offline); assert(ret == NULL); assert(any_offline == false); /* Other candidate insufficient funds. */ @@ -645,7 +664,7 @@ int main(void) add_peer(ld, 3, CHANNELD_NORMAL, true); for (size_t i = n = 0; i < 1000; i++) { - ret = select_inchan(tmpctx, ld, 1000, inchans, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1000), inchans, &any_offline); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); assert(any_offline == false); /* Other candidate insufficient funds. */ @@ -661,7 +680,7 @@ int main(void) n, 1000 - n); for (size_t i = n = 0; i < 1000; i++) { - ret = select_inchan(tmpctx, ld, 1499, inchans, &any_offline); + ret = select_inchan(tmpctx, ld, AMOUNT_MSAT(1499), inchans, &any_offline); assert(tal_count(ret) == 1); assert(tal_count(ret[0]) == 1); assert(any_offline == false); /* Other candidate insufficient funds. */ diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 1d35b022bce8..6ebe3e8e4951 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -27,7 +27,8 @@ bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, { fprintf(stderr, "json_to_pubkey called!\n"); abort(); } /* Generated stub for json_to_short_channel_id */ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) + struct short_channel_id *scid UNNEEDED, + bool may_be_deprecated_form UNNEEDED) { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } /* Generated stub for log_ */ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) @@ -40,6 +41,9 @@ void log_io(struct log *log UNNEEDED, enum log_level dir UNNEEDED, const char *c /* Generated stub for log_prefix */ const char *log_prefix(const struct log *log UNNEEDED) { fprintf(stderr, "log_prefix called!\n"); abort(); } +/* Generated stub for memleak_remove_strmap_ */ +void memleak_remove_strmap_(struct htable *memtable UNNEEDED, const struct strmap *m UNNEEDED) +{ fprintf(stderr, "memleak_remove_strmap_ called!\n"); abort(); } /* Generated stub for new_log */ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "new_log called!\n"); abort(); } @@ -79,7 +83,7 @@ bool deprecated_apis; static int test_json_filter(void) { - struct json_stream *result = new_json_stream(NULL, NULL); + struct json_stream *result = new_json_stream(NULL, NULL, NULL); jsmntok_t *toks; const jsmntok_t *x; bool valid; @@ -125,7 +129,7 @@ static void test_json_escape(void) for (i = 1; i < 256; i++) { char badstr[2]; - struct json_stream *result = new_json_stream(NULL, NULL); + struct json_stream *result = new_json_stream(NULL, NULL, NULL); struct json_escaped *esc; badstr[0] = i; diff --git a/onchaind/Makefile b/onchaind/Makefile index d9146f394d8b..aa102f1d5ee2 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -47,6 +47,7 @@ $(LIGHTNINGD_ONCHAIN_OBJS): $(LIGHTNINGD_HEADERS) # Common source we use. ONCHAIND_COMMON_OBJS := \ + common/amount.o \ common/bip32.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/onchaind/onchain_wire.csv b/onchaind/onchain_wire.csv index ac97561cd9d7..cec4f03eb0f8 100644 --- a/onchaind/onchain_wire.csv +++ b/onchaind/onchain_wire.csv @@ -4,7 +4,7 @@ # Begin! Here's the onchain tx which spends funding tx, followed by all HTLCs. onchain_init,5001 onchain_init,,shachain,struct shachain -onchain_init,,funding_amount_satoshi,u64 +onchain_init,,funding_amount_satoshi,struct amount_sat # Remote per commit point for committed tx. onchain_init,,old_remote_per_commitment_point,struct pubkey # Remote per commit point for current tx (needed if we haven't got revoke_and_ack yet). @@ -12,7 +12,7 @@ onchain_init,,remote_per_commitment_point,struct pubkey onchain_init,,local_to_self_delay,u32 onchain_init,,remote_to_self_delay,u32 onchain_init,,feerate_per_kw,u32 -onchain_init,,local_dust_limit_satoshi,u64 +onchain_init,,local_dust_limit_satoshi,struct amount_sat # Gives an easy way to tell if it's our unilateral close or theirs... onchain_init,,our_broadcast_txid,struct bitcoin_txid onchain_init,,local_scriptpubkey_len,u16 @@ -88,8 +88,10 @@ onchain_add_utxo,5012 onchain_add_utxo,,prev_out_tx,struct bitcoin_txid onchain_add_utxo,,prev_out_index,u32 onchain_add_utxo,,per_commit_point,struct pubkey -onchain_add_utxo,,value,u64 +onchain_add_utxo,,value,struct amount_sat onchain_add_utxo,,blockheight,u32 +onchain_add_utxo,,len,u16 +onchain_add_utxo,,scriptpubkey,len*u8 # master -> onchaind: do you have a memleak? onchain_dev_memleak,5033 diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 5ef5c773c18d..3fc432d8ae4f 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -46,7 +46,7 @@ static u32 feerate_per_kw; static u32 min_possible_feerate, max_possible_feerate; /* The dust limit to use when we generate transactions. */ -static u64 dust_limit_satoshis; +static struct amount_sat dust_limit; /* The CSV delays for each side. */ static u32 to_self_delay[NUM_SIDES]; @@ -89,7 +89,7 @@ struct tracked_output { /* FIXME: Convert all depths to blocknums, then just get new blk msgs */ u32 depth; u32 outnum; - u64 satoshi; + struct amount_sat sat; enum output_type output_type; /* If it is an HTLC, this is set, wscript is non-NULL. */ @@ -107,13 +107,13 @@ struct tracked_output { }; /* We vary feerate until signature they offered matches. */ -static u64 grind_htlc_tx_fee(struct bitcoin_tx *tx, - const struct bitcoin_signature *remotesig, - const u8 *wscript, - u64 multiplier) +static bool grind_htlc_tx_fee(struct amount_sat *fee, + struct bitcoin_tx *tx, + const struct bitcoin_signature *remotesig, + const u8 *wscript, + u64 weight) { - u64 prev_fee = UINT64_MAX; - u64 input_amount = *tx->input[0].amount; + struct amount_sat prev_fee = AMOUNT_SAT(UINT64_MAX); for (u64 i = min_possible_feerate; i <= max_possible_feerate; i++) { /* BOLT #3: @@ -128,31 +128,35 @@ static u64 grind_htlc_tx_fee(struct bitcoin_tx *tx, * 1. Multiply `feerate_per_kw` by 703 and divide by 1000 * (rounding down). */ - u64 fee = i * multiplier / 1000; + struct amount_sat out; - if (fee > input_amount) - break; + *fee = amount_tx_fee(i, weight); /* Minor optimization: don't check same fee twice */ - if (fee == prev_fee) + if (amount_sat_eq(*fee, prev_fee)) continue; - prev_fee = fee; - tx->output[0].amount = input_amount - fee; + prev_fee = *fee; + if (!amount_sat_sub(&out, *tx->input[0].amount, *fee)) + break; + + tx->output[0].amount = out; if (!check_tx_sig(tx, 0, NULL, wscript, &keyset->other_htlc_key, remotesig)) continue; - return fee; + status_trace("grind feerate_per_kw for %"PRIu64" = %"PRIu64, + weight, i); + return true; } - return UINT64_MAX; + return false; } static bool set_htlc_timeout_fee(struct bitcoin_tx *tx, const struct bitcoin_signature *remotesig, const u8 *wscript) { - static u64 fee = UINT64_MAX; + static struct amount_sat fee = AMOUNT_SAT_INIT(UINT64_MAX); /* BOLT #3: * @@ -161,12 +165,14 @@ static bool set_htlc_timeout_fee(struct bitcoin_tx *tx, * 1. Multiply `feerate_per_kw` by 663 and divide by 1000 (rounding * down). */ - if (fee == UINT64_MAX) { - fee = grind_htlc_tx_fee(tx, remotesig, wscript, 663); - return fee != UINT64_MAX; - } + if (amount_sat_eq(fee, AMOUNT_SAT(UINT64_MAX))) + return grind_htlc_tx_fee(&fee, tx, remotesig, wscript, 663); - tx->output[0].amount = *tx->input[0].amount - fee; + if (!amount_sat_sub(&tx->output[0].amount, tx->output[0].amount, fee)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot deduct htlc-timeout fee %s from tx %s", + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct bitcoin_tx, tx)); return check_tx_sig(tx, 0, NULL, wscript, &keyset->other_htlc_key, remotesig); } @@ -175,7 +181,7 @@ static void set_htlc_success_fee(struct bitcoin_tx *tx, const struct bitcoin_signature *remotesig, const u8 *wscript) { - static u64 fee = UINT64_MAX; + static struct amount_sat fee = AMOUNT_SAT_INIT(UINT64_MAX); /* BOLT #3: * @@ -184,20 +190,34 @@ static void set_htlc_success_fee(struct bitcoin_tx *tx, * 1. Multiply `feerate_per_kw` by 703 and divide by 1000 * (rounding down). */ - if (fee == UINT64_MAX) { - fee = grind_htlc_tx_fee(tx, remotesig, wscript, 703); + if (amount_sat_eq(fee, AMOUNT_SAT(UINT64_MAX))) { + if (!grind_htlc_tx_fee(&fee, tx, remotesig, wscript, 703)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "htlc_success_fee can't be found " + " for tx %s, signature %s, wscript %s", + type_to_string(tmpctx, struct bitcoin_tx, + tx), + type_to_string(tmpctx, + struct bitcoin_signature, + remotesig), + tal_hex(tmpctx, wscript)); return; } - tx->output[0].amount = *tx->input[0].amount - fee; + if (!amount_sat_sub(&tx->output[0].amount, tx->output[0].amount, fee)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot deduct htlc-success fee %s from tx %s", + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct bitcoin_tx, tx)); + if (check_tx_sig(tx, 0, NULL, wscript, &keyset->other_htlc_key, remotesig)) return; status_failed(STATUS_FAIL_INTERNAL_ERROR, - "htlc_success_fee %"PRIu64" failed sigcheck " + "htlc_success_fee %s failed sigcheck " " for tx %s, signature %s, wscript %s", - fee, + type_to_string(tmpctx, struct amount_sat, &fee), type_to_string(tmpctx, struct bitcoin_tx, tx), type_to_string(tmpctx, struct bitcoin_signature, remotesig), tal_hex(tmpctx, wscript)); @@ -272,8 +292,9 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx, enum tx_type *tx_type) { struct bitcoin_tx *tx; - u64 fee; + struct amount_sat fee, min_out; struct bitcoin_signature sig; + size_t weight; u8 *msg; tx = bitcoin_tx(ctx, 1, 1); @@ -281,42 +302,45 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx, tx->input[0].sequence_number = to_self_delay; tx->input[0].txid = out->txid; tx->input[0].index = out->outnum; - tx->input[0].amount = tal_dup(tx->input, u64, &out->satoshi); + tx->input[0].amount = tal_dup(tx->input, struct amount_sat, &out->sat); - tx->output[0].amount = out->satoshi; + tx->output[0].amount = out->sat; tx->output[0].script = scriptpubkey_p2wpkh(tx->output, &our_wallet_pubkey); /* Worst-case sig is 73 bytes */ - fee = feerate_per_kw * (measure_tx_weight(tx) - + 1 + 3 + 73 + 0 + tal_count(wscript)) - / 1000; + weight = measure_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); + fee = amount_tx_fee(feerate_per_kw, weight); /* Result is trivial? Spend with small feerate, but don't wait * around for it as it might not confirm. */ - if (tx->output[0].amount < dust_limit_satoshis + fee) { - /* FIXME: We should use SIGHASH_NONE so others can take it */ - fee = feerate_floor() * (measure_tx_weight(tx) - + 1 + 3 + 73 + 0 + tal_count(wscript)) - / 1000; - /* This shouldn't happen (we don't set feerate below floor!), - * but just in case. */ - if (tx->output[0].amount < dust_limit_satoshis + fee) { - fee = tx->output[0].amount - dust_limit_satoshis; - status_broken("TX %s can't afford minimal feerate" - "; setting fee to %"PRIu64, - tx_type_name(*tx_type), - fee); - } else - status_unusual("TX %s amount %"PRIu64" too small to" - " pay reasonable fee, using minimal fee" - " and ignoring", - tx_type_name(*tx_type), - out->satoshi); + if (!amount_sat_add(&min_out, dust_limit, fee)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Cannot add dust_limit %s and fee %s", + type_to_string(tmpctx, struct amount_sat, &dust_limit), + type_to_string(tmpctx, struct amount_sat, &fee)); + if (amount_sat_less(out->sat, min_out)) { + /* FIXME: We should use SIGHASH_NONE so others can take it */ + fee = amount_tx_fee(feerate_floor(), weight); + status_unusual("TX %s amount %s too small to" + " pay reasonable fee, using minimal fee" + " and ignoring", + tx_type_name(*tx_type), + type_to_string(tmpctx, struct amount_sat, &out->sat)); *tx_type = IGNORING_TINY_PAYMENT; } - tx->output[0].amount -= fee; + + /* This can only happen if feerate_floor() is still too high; shouldn't + * happen! */ + if (!amount_sat_sub(&tx->output[0].amount, out->sat, fee)) { + tx->output[0].amount = dust_limit; + status_broken("TX %s can't afford minimal feerate" + "; setting output to %s", + tx_type_name(*tx_type), + type_to_string(tmpctx, struct amount_sat, + &tx->output[0].amount)); + } if (!wire_sync_write(HSM_FD, take(hsm_sign_msg(NULL, tx, wscript)))) status_failed(STATUS_FAIL_HSM_IO, "Writing sign request to hsm"); @@ -375,7 +399,7 @@ static struct tracked_output * u32 tx_blockheight, enum tx_type tx_type, u32 outnum, - u64 satoshi, + struct amount_sat sat, enum output_type output_type, const struct htlc_stub *htlc, const u8 *wscript, @@ -394,7 +418,7 @@ static struct tracked_output * out->tx_blockheight = tx_blockheight; out->depth = 0; out->outnum = outnum; - out->satoshi = satoshi; + out->sat = sat; out->output_type = output_type; out->proposal = NULL; out->resolved = NULL; @@ -1150,9 +1174,17 @@ static void handle_preimage(struct tracked_output **outs, * HTLC-success transaction. */ if (outs[i]->remote_htlc_sig) { + struct amount_msat htlc_amount; + if (!amount_sat_to_msat(&htlc_amount, outs[i]->sat)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Overflow in output %zu %s", + i, + type_to_string(tmpctx, + struct amount_sat, + &outs[i]->sat)); tx = htlc_success_tx(outs[i], &outs[i]->txid, outs[i]->outnum, - outs[i]->satoshi * 1000, + htlc_amount, to_self_delay[LOCAL], 0, keyset); @@ -1342,6 +1374,13 @@ static size_t resolve_our_htlc_ourcommit(struct tracked_output *out, struct bitcoin_tx *tx = NULL; struct bitcoin_signature localsig; size_t i; + struct amount_msat htlc_amount; + + if (!amount_sat_to_msat(&htlc_amount, out->sat)) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Overflow in our_htlc output %s", + type_to_string(tmpctx, struct amount_sat, + &out->sat)); assert(tal_count(matches)); @@ -1358,7 +1397,7 @@ static size_t resolve_our_htlc_ourcommit(struct tracked_output *out, * HTLC-timeout transaction. */ tx = htlc_timeout_tx(tmpctx, &out->txid, out->outnum, - out->satoshi * 1000, + htlc_amount, htlcs[matches[i]].cltv_expiry, to_self_delay[LOCAL], 0, keyset); @@ -1385,12 +1424,13 @@ static size_t resolve_our_htlc_ourcommit(struct tracked_output *out, status_failed(STATUS_FAIL_INTERNAL_ERROR, "No valid signature found for %zu htlc_timeout_txs" " feerate %u-%u," - " last tx %s, inputamount %"PRIu64", signature %s," + " last tx %s, input %s, signature %s," " cltvs %s wscripts %s", tal_count(matches), min_possible_feerate, max_possible_feerate, type_to_string(tmpctx, struct bitcoin_tx, tx), - out->satoshi, + type_to_string(tmpctx, struct amount_sat, + &out->sat), type_to_string(tmpctx, struct bitcoin_signature, out->remote_htlc_sig), cltvs, wscripts); @@ -1974,7 +2014,6 @@ static void handle_their_cheat(const struct bitcoin_tx *tx, i, tx->output[i].amount, OUTPUT_TO_US, NULL, NULL, NULL); ignore_output(out); - script[LOCAL] = NULL; /* Tell the master that it will want to add * this UTXO to its outputs */ @@ -1982,7 +2021,9 @@ static void handle_their_cheat(const struct bitcoin_tx *tx, tmpctx, txid, i, remote_per_commitment_point, tx->output[i].amount, - tx_blockheight)); + tx_blockheight, + script[LOCAL])); + script[LOCAL] = NULL; continue; } if (script[REMOTE] @@ -2186,7 +2227,6 @@ static void handle_their_unilateral(const struct bitcoin_tx *tx, i, tx->output[i].amount, OUTPUT_TO_US, NULL, NULL, NULL); ignore_output(out); - script[LOCAL] = NULL; /* Tell the master that it will want to add * this UTXO to its outputs */ @@ -2194,7 +2234,9 @@ static void handle_their_unilateral(const struct bitcoin_tx *tx, tmpctx, txid, i, remote_per_commitment_point, tx->output[i].amount, - tx_blockheight)); + tx_blockheight, + script[LOCAL])); + script[LOCAL] = NULL; continue; } if (script[REMOTE] @@ -2315,7 +2357,6 @@ static void handle_unknown_commitment(const struct bitcoin_tx *tx, i, tx->output[i].amount, OUTPUT_TO_US, NULL, NULL, NULL); ignore_output(out); - local_script = NULL; /* Tell the master that it will want to add * this UTXO to its outputs */ @@ -2323,7 +2364,9 @@ static void handle_unknown_commitment(const struct bitcoin_tx *tx, tmpctx, txid, i, possible_remote_per_commitment_point, tx->output[i].amount, - tx_blockheight)); + tx_blockheight, + local_script)); + local_script = NULL; to_us_output = i; } } @@ -2368,7 +2411,8 @@ int main(int argc, char *argv[]) struct tracked_output **outs; struct bitcoin_txid our_broadcast_txid, txid; secp256k1_ecdsa_signature *remote_htlc_sigs; - u64 funding_amount_satoshi, num_htlcs; + struct amount_sat funding; + u64 num_htlcs; u8 *scriptpubkey[NUM_SIDES]; struct htlc_stub *htlcs; bool *tell_if_missing, *tell_immediately; @@ -2384,13 +2428,13 @@ int main(int argc, char *argv[]) msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_onchain_init(tmpctx, msg, &shachain, - &funding_amount_satoshi, + &funding, &old_remote_per_commit_point, &remote_per_commit_point, &to_self_delay[LOCAL], &to_self_delay[REMOTE], &feerate_per_kw, - &dust_limit_satoshis, + &dust_limit, &our_broadcast_txid, &scriptpubkey[LOCAL], &scriptpubkey[REMOTE], @@ -2409,6 +2453,7 @@ int main(int argc, char *argv[]) master_badmsg(WIRE_ONCHAIN_INIT, msg); } + status_trace("feerate_per_kw = %u", feerate_per_kw); bitcoin_txid(tx, &txid); /* We need to keep tx around, but there's only one: not really a leak */ tal_steal(ctx, notleak(tx)); @@ -2434,7 +2479,7 @@ int main(int argc, char *argv[]) 0, /* We don't care about funding blockheight */ FUNDING_TRANSACTION, tx->input[0].index, - funding_amount_satoshi, + funding, FUNDING_OUTPUT, NULL, NULL, NULL); status_trace("Remote per-commit point: %s", diff --git a/onchaind/test/Makefile b/onchaind/test/Makefile index 86b4e71d9d39..c487946df1cc 100644 --- a/onchaind/test/Makefile +++ b/onchaind/test/Makefile @@ -7,6 +7,7 @@ ONCHAIND_TEST_OBJS := $(ONCHAIND_TEST_SRC:.c=.o) ONCHAIND_TEST_PROGRAMS := $(ONCHAIND_TEST_OBJS:.o=) ONCHAIND_TEST_COMMON_OBJS := \ + common/amount.o \ common/features.o \ common/pseudorand.o \ common/type_to_string.o \ diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index b50a466dd137..2484541dd05f 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -40,7 +40,7 @@ bool fromwire_onchain_dev_memleak(const void *p UNNEEDED) bool fromwire_onchain_htlc(const void *p UNNEEDED, struct htlc_stub *htlc UNNEEDED, bool *tell_if_missing UNNEEDED, bool *tell_immediately UNNEEDED) { fprintf(stderr, "fromwire_onchain_htlc called!\n"); abort(); } /* Generated stub for fromwire_onchain_init */ -bool fromwire_onchain_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, u64 *funding_amount_satoshi UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *feerate_per_kw UNNEEDED, u64 *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *funder UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct bitcoin_tx **tx UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, secp256k1_ecdsa_signature **htlc_signature UNNEEDED, u64 *num_htlcs UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED) +bool fromwire_onchain_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *feerate_per_kw UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *funder UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct bitcoin_tx **tx UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, secp256k1_ecdsa_signature **htlc_signature UNNEEDED, u64 *num_htlcs UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED) { fprintf(stderr, "fromwire_onchain_init called!\n"); abort(); } /* Generated stub for fromwire_onchain_known_preimage */ bool fromwire_onchain_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED) @@ -63,7 +63,7 @@ u8 *htlc_received_wscript(const tal_t *ctx UNNEEDED, struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *commit_txid UNNEEDED, unsigned int commit_output_number UNNEEDED, - u64 htlc_msatoshi UNNEEDED, + struct amount_msat htlc_msatoshi UNNEEDED, u16 to_self_delay UNNEEDED, u32 feerate_per_kw UNNEEDED, const struct keyset *keyset UNNEEDED) @@ -72,7 +72,7 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED, struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *commit_txid UNNEEDED, unsigned int commit_output_number UNNEEDED, - u64 htlc_msatoshi UNNEEDED, + struct amount_msat htlc_msatoshi UNNEEDED, u32 cltv_expiry UNNEEDED, u16 to_self_delay UNNEEDED, u32 feerate_per_kw UNNEEDED, @@ -112,19 +112,19 @@ u8 *to_self_wscript(const tal_t *ctx UNNEEDED, u8 *towire_hsm_get_per_commitment_point(const tal_t *ctx UNNEEDED, u64 n UNNEEDED) { fprintf(stderr, "towire_hsm_get_per_commitment_point called!\n"); abort(); } /* Generated stub for towire_hsm_sign_delayed_payment_to_us */ -u8 *towire_hsm_sign_delayed_payment_to_us(const tal_t *ctx UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, u64 input_amount UNNEEDED) +u8 *towire_hsm_sign_delayed_payment_to_us(const tal_t *ctx UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, struct amount_sat input_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_delayed_payment_to_us called!\n"); abort(); } /* Generated stub for towire_hsm_sign_local_htlc_tx */ -u8 *towire_hsm_sign_local_htlc_tx(const tal_t *ctx UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, u64 input_amount UNNEEDED) +u8 *towire_hsm_sign_local_htlc_tx(const tal_t *ctx UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, struct amount_sat input_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_local_htlc_tx called!\n"); abort(); } /* Generated stub for towire_hsm_sign_penalty_to_us */ -u8 *towire_hsm_sign_penalty_to_us(const tal_t *ctx UNNEEDED, const struct secret *revocation_secret UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, u64 input_amount UNNEEDED) +u8 *towire_hsm_sign_penalty_to_us(const tal_t *ctx UNNEEDED, const struct secret *revocation_secret UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, struct amount_sat input_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_penalty_to_us called!\n"); abort(); } /* Generated stub for towire_hsm_sign_remote_htlc_to_us */ -u8 *towire_hsm_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, u64 input_amount UNNEEDED) +u8 *towire_hsm_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, struct amount_sat input_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_remote_htlc_to_us called!\n"); abort(); } /* Generated stub for towire_onchain_add_utxo */ -u8 *towire_onchain_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, u64 value UNNEEDED, u32 blockheight UNNEEDED) +u8 *towire_onchain_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED) { fprintf(stderr, "towire_onchain_add_utxo called!\n"); abort(); } /* Generated stub for towire_onchain_all_irrevocably_resolved */ u8 *towire_onchain_all_irrevocably_resolved(const tal_t *ctx UNNEEDED) @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) struct bitcoin_tx *tx; struct bitcoin_signature sig; u8 *der, *wscript; - u64 fee; + struct amount_sat fee; struct pubkey htlc_key; struct keyset *keys; struct timeabs start, end; @@ -197,8 +197,8 @@ int main(int argc, char *argv[]) setup_tmpctx(); tx = bitcoin_tx_from_hex(tmpctx, "0200000001e1ebca08cf1c301ac563580a1126d5c8fcb0e5e2043230b852c726553caf1e1d0000000000000000000160ae0a000000000022002082e03c5a9cb79c82cd5a0572dc175290bc044609aabe9cc852d61927436041796d000000", strlen("0200000001e1ebca08cf1c301ac563580a1126d5c8fcb0e5e2043230b852c726553caf1e1d0000000000000000000160ae0a000000000022002082e03c5a9cb79c82cd5a0572dc175290bc044609aabe9cc852d61927436041796d000000")); - tx->input[0].amount = tal(tx, u64); - *tx->input[0].amount = 700000; + tx->input[0].amount = tal(tx, struct amount_sat); + *tx->input[0].amount = AMOUNT_SAT(700000); der = tal_hexdata(tmpctx, "30450221009b2e0eef267b94c3899fb0dc7375012e2cee4c10348a068fe78d1b82b4b14036022077c3fad3adac2ddf33f415e45f0daf6658b7a0b09647de4443938ae2dbafe2b9" "01", strlen("30450221009b2e0eef267b94c3899fb0dc7375012e2cee4c10348a068fe78d1b82b4b14036022077c3fad3adac2ddf33f415e45f0daf6658b7a0b09647de4443938ae2dbafe2b9" "01")); if (!signature_from_der(der, tal_count(der), &sig)) @@ -222,11 +222,10 @@ int main(int argc, char *argv[]) min_possible_feerate = max_possible_feerate + 1 - iterations; start = time_now(); - - fee = grind_htlc_tx_fee(tx, &sig, wscript, 663); + if (!grind_htlc_tx_fee(&fee, tx, &sig, wscript, 663)) + abort(); end = time_now(); - - assert(fee == 703); + assert(amount_sat_eq(fee, AMOUNT_SAT(165750))); printf("%u iterations in %"PRIu64" msec = %"PRIu64" nsec each\n", iterations, time_to_msec(time_between(end, start)), diff --git a/openingd/Makefile b/openingd/Makefile index 20b69c52fc55..01847d90ab07 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -35,6 +35,7 @@ LIGHTNINGD_HEADERS_NOGEN += $(LIGHTNINGD_OPENING_HEADERS_NOGEN) # Common source we use. OPENINGD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bip32.o \ common/channel_config.o \ diff --git a/openingd/opening_wire.csv b/openingd/opening_wire.csv index c9e05d74749b..6e8a30fb3218 100644 --- a/openingd/opening_wire.csv +++ b/openingd/opening_wire.csv @@ -9,7 +9,7 @@ opening_init,,chain_hash,struct bitcoin_blkid opening_init,,our_config,struct channel_config # Minimum/maximum configuration values we'll accept opening_init,,max_to_self_delay,u32 -opening_init,,min_effective_htlc_capacity_msat,u64 +opening_init,,min_effective_htlc_capacity_msat,struct amount_msat opening_init,,crypto_state,struct crypto_state opening_init,,our_basepoints,struct basepoints opening_init,,our_funding_pubkey,struct pubkey @@ -29,10 +29,10 @@ opening_can_accept_channel,6002 #include # Master->openingd: please fund a channel. opening_funder,6001 -opening_funder,,funding_satoshis,u64 -opening_funder,,push_msat,u64 +opening_funder,,funding_satoshis,struct amount_sat +opening_funder,,push_msat,struct amount_msat opening_funder,,feerate_per_kw,u32 -opening_funder,,change_satoshis,u64 +opening_funder,,change_satoshis,struct amount_sat opening_funder,,change_keyindex,u32 opening_funder,,channel_flags,u8 #include @@ -56,7 +56,7 @@ opening_funder_reply,,minimum_depth,u32 opening_funder_reply,,remote_fundingkey,struct pubkey opening_funder_reply,,funding_txid,struct bitcoin_txid opening_funder_reply,,feerate_per_kw,u32 -opening_funder_reply,,our_channel_reserve_satoshis,u64 +opening_funder_reply,,our_channel_reserve_satoshis,struct amount_sat # Openingd->master: we failed to negotiation channel opening_funder_failed,6004 @@ -77,14 +77,14 @@ opening_fundee,,their_per_commit_point,struct pubkey opening_fundee,,remote_fundingkey,struct pubkey opening_fundee,,funding_txid,struct bitcoin_txid opening_fundee,,funding_txout,u16 -opening_fundee,,funding_satoshis,u64 -opening_fundee,,push_msat,u64 +opening_fundee,,funding_satoshis,struct amount_sat +opening_fundee,,push_msat,struct amount_msat opening_fundee,,channel_flags,u8 opening_fundee,,feerate_per_kw,u32 # The funding signed message: send this and we're committed. opening_fundee,,msglen,u16 opening_fundee,,funding_signed_msg,msglen*u8 -opening_fundee,,our_channel_reserve_satoshis,u64 +opening_fundee,,our_channel_reserve_satoshis,struct amount_sat # master -> openingd: do you have a memleak? opening_dev_memleak,6033 diff --git a/openingd/openingd.c b/openingd/openingd.c index 67ecc1bdc41b..1c263f9063f8 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -61,7 +61,7 @@ struct state { /* Constraints on a channel they open. */ u32 minimum_depth; u32 min_feerate, max_feerate; - u64 min_effective_htlc_capacity_msat; + struct amount_msat min_effective_htlc_capacity; /* Limits on what remote config we accept. */ u32 max_to_self_delay; @@ -79,7 +79,8 @@ struct state { struct channel_id channel_id; /* Funding and feerate: set by opening peer. */ - u64 funding_satoshis, push_msat; + struct amount_sat funding; + struct amount_msat push_msat; u32 feerate_per_kw; struct bitcoin_txid funding_txid; u16 funding_txout; @@ -155,8 +156,8 @@ static bool check_config_bounds(struct state *state, const struct channel_config *remoteconf, bool am_funder) { - u64 capacity_msat; - u64 reserve_msat; + struct amount_sat capacity; + struct amount_sat reserve; /* BOLT #2: * @@ -185,67 +186,74 @@ static bool check_config_bounds(struct state *state, /* We accumulate this into an effective bandwidth minimum. */ /* Add both reserves to deduct from capacity. */ - if (mul_overflows_u64(remoteconf->channel_reserve_satoshis, 1000) - || add_overflows_u64(remoteconf->channel_reserve_satoshis * 1000, - state->localconf.channel_reserve_satoshis * 1000)) { + if (!amount_sat_add(&reserve, + remoteconf->channel_reserve, + state->localconf.channel_reserve)) { negotiation_failed(state, am_funder, - "channel_reserve_satoshis %"PRIu64 + "channel_reserve_satoshis %s" " too large", - remoteconf->channel_reserve_satoshis); + type_to_string(tmpctx, struct amount_sat, + &remoteconf->channel_reserve)); return false; } - reserve_msat = remoteconf->channel_reserve_satoshis * 1000 - + state->localconf.channel_reserve_satoshis * 1000; - /* We checked this before, or it's ours. */ - assert(!mul_overflows_u64(state->funding_satoshis, 1000)); - - /* If reserves are larger than total msat, we fail. */ - if (reserve_msat > state->funding_satoshis * 1000) { + /* If reserves are larger than total sat, we fail. */ + if (!amount_sat_sub(&capacity, state->funding, reserve)) { negotiation_failed(state, am_funder, - "channel_reserve_satoshis %"PRIu64 - " and %"PRIu64" too large for funding_satoshis %"PRIu64, - remoteconf->channel_reserve_satoshis, - state->localconf.channel_reserve_satoshis, - state->funding_satoshis); + "channel_reserve_satoshis %s" + " and %s too large for funding %s", + type_to_string(tmpctx, struct amount_sat, + &remoteconf->channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &state->localconf.channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &state->funding)); return false; } - capacity_msat = state->funding_satoshis * 1000 - reserve_msat; - /* If they set the max HTLC value to less than that number, it caps * the channel capacity. */ - if (remoteconf->max_htlc_value_in_flight_msat < capacity_msat) - capacity_msat = remoteconf->max_htlc_value_in_flight_msat; + if (amount_sat_greater(capacity, + amount_msat_to_sat_round_down(remoteconf->max_htlc_value_in_flight))) + capacity = amount_msat_to_sat_round_down(remoteconf->max_htlc_value_in_flight); /* If the minimum htlc is greater than the capacity, the channel is * useless. */ - if (mul_overflows_u64(remoteconf->htlc_minimum_msat, 1000) - || remoteconf->htlc_minimum_msat * (u64)1000 > capacity_msat) { + if (amount_msat_greater_sat(remoteconf->htlc_minimum, capacity)) { negotiation_failed(state, am_funder, - "htlc_minimum_msat %"PRIu64 - " too large for funding_satoshis %"PRIu64 - " capacity_msat %"PRIu64, - remoteconf->htlc_minimum_msat, - state->funding_satoshis, - capacity_msat); + "htlc_minimum_msat %s" + " too large for funding %s" + " capacity_msat %s", + type_to_string(tmpctx, struct amount_msat, + &remoteconf->htlc_minimum), + type_to_string(tmpctx, struct amount_sat, + &state->funding), + type_to_string(tmpctx, struct amount_sat, + &capacity)); return false; } /* If the resulting channel doesn't meet our minimum "effective capacity" * set by lightningd, don't bother opening it. */ - if (capacity_msat < state->min_effective_htlc_capacity_msat) { + if (amount_msat_greater_sat(state->min_effective_htlc_capacity, + capacity)) { negotiation_failed(state, am_funder, - "channel capacity with funding %"PRIu64" msat," - " reserves %"PRIu64"/%"PRIu64" msat," - " max_htlc_value_in_flight_msat %"PRIu64 - " is %"PRIu64" msat, which is below %"PRIu64" msat", - state->funding_satoshis * 1000, - remoteconf->channel_reserve_satoshis * 1000, - state->localconf.channel_reserve_satoshis * 1000, - remoteconf->max_htlc_value_in_flight_msat, - capacity_msat, - state->min_effective_htlc_capacity_msat); + "channel capacity with funding %s," + " reserves %s/%s," + " max_htlc_value_in_flight_msat %s" + " is %s msat, which is below %s msat", + type_to_string(tmpctx, struct amount_sat, + &state->funding), + type_to_string(tmpctx, struct amount_sat, + &remoteconf->channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &state->localconf.channel_reserve), + type_to_string(tmpctx, struct amount_msat, + &remoteconf->max_htlc_value_in_flight), + type_to_string(tmpctx, struct amount_sat, + &capacity), + type_to_string(tmpctx, struct amount_msat, + &state->min_effective_htlc_capacity)); return false; } @@ -276,14 +284,15 @@ static bool check_config_bounds(struct state *state, *... * - `dust_limit_satoshis` is greater than `channel_reserve_satoshis`. */ - if (remoteconf->dust_limit_satoshis - > remoteconf->channel_reserve_satoshis) { + if (amount_sat_greater(remoteconf->dust_limit, + remoteconf->channel_reserve)) { negotiation_failed(state, am_funder, - "dust_limit_satoshis %"PRIu64 - " too large for channel_reserve_satoshis %" - PRIu64, - remoteconf->dust_limit_satoshis, - remoteconf->channel_reserve_satoshis); + "dust_limit_satoshis %s" + " too large for channel_reserve_satoshis %s", + type_to_string(tmpctx, struct amount_sat, + &remoteconf->dust_limit), + type_to_string(tmpctx, struct amount_sat, + &remoteconf->channel_reserve)); return false; } @@ -293,8 +302,8 @@ static bool check_config_bounds(struct state *state, /* We always set channel_reserve_satoshis to 1%, rounded up. */ static void set_reserve(struct state *state) { - state->localconf.channel_reserve_satoshis - = (state->funding_satoshis + 99) / 100; + state->localconf.channel_reserve.satoshis /* Raw: rounding. */ + = (state->funding.satoshis + 99) / 100; /* Raw: rounding. */ /* BOLT #2: * @@ -303,10 +312,10 @@ static void set_reserve(struct state *state) * - MUST set `channel_reserve_satoshis` greater than or equal to * `dust_limit_satoshis`. */ - if (state->localconf.channel_reserve_satoshis - < state->localconf.dust_limit_satoshis) - state->localconf.channel_reserve_satoshis - = state->localconf.dust_limit_satoshis; + if (amount_sat_greater(state->localconf.dust_limit, + state->localconf.channel_reserve)) + state->localconf.channel_reserve + = state->localconf.dust_limit; } /* BOLT #2: @@ -419,7 +428,8 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, /*~ OK, let's fund a channel! Returns the reply for lightningd on success, * or NULL if something goes wrong. */ static u8 *funder_channel(struct state *state, - u64 change_satoshis, u32 change_keyindex, + struct amount_sat change, + u32 change_keyindex, u8 channel_flags, struct utxo **utxos TAKES, const struct ext_key *bip32_base) @@ -434,6 +444,7 @@ static u8 *funder_channel(struct state *state, u32 minimum_depth; const u8 *wscript; struct bitcoin_tx *funding; + struct amount_msat local_msat; /*~ For symmetry, we calculate our own reserve even though lightningd * could do it for the we-are-funding case. */ @@ -449,11 +460,13 @@ static u8 *funder_channel(struct state *state, *... * - MUST set `funding_satoshis` to less than 2^24 satoshi. */ - if (state->funding_satoshis > state->chainparams->max_funding_satoshi) + if (amount_sat_greater(state->funding, state->chainparams->max_funding)) status_failed(STATUS_FAIL_MASTER_IO, - "funding_satoshis must be < %"PRIu64", not %"PRIu64, - state->chainparams->max_funding_satoshi, - state->funding_satoshis); + "funding_satoshis must be < %s, not %s", + type_to_string(tmpctx, struct amount_sat, + &state->chainparams->max_funding), + type_to_string(tmpctx, struct amount_sat, + &state->funding)); /* BOLT #2: * @@ -462,19 +475,21 @@ static u8 *funder_channel(struct state *state, * - MUST set `push_msat` to equal or less than 1000 * * `funding_satoshis`. */ - if (state->push_msat > 1000 * state->funding_satoshis) + if (!amount_sat_sub_msat(&local_msat, state->funding, state->push_msat)) status_failed(STATUS_FAIL_MASTER_IO, - "push-msat must be < %"PRIu64, - 1000 * state->funding_satoshis); + "push-msat must be < %s", + type_to_string(tmpctx, struct amount_sat, + &state->funding)); msg = towire_open_channel(NULL, &state->chainparams->genesis_blockhash, &state->channel_id, - state->funding_satoshis, state->push_msat, - state->localconf.dust_limit_satoshis, - state->localconf.max_htlc_value_in_flight_msat, - state->localconf.channel_reserve_satoshis, - state->localconf.htlc_minimum_msat, + state->funding, + state->push_msat, + state->localconf.dust_limit, + state->localconf.max_htlc_value_in_flight, + state->localconf.channel_reserve, + state->localconf.htlc_minimum, state->feerate_per_kw, state->localconf.to_self_delay, state->localconf.max_accepted_htlcs, @@ -504,12 +519,10 @@ static u8 *funder_channel(struct state *state, * valid DER-encoded compressed secp256k1 pubkeys. */ if (!fromwire_accept_channel(msg, &id_in, - &state->remoteconf.dust_limit_satoshis, - &state->remoteconf - .max_htlc_value_in_flight_msat, - &state->remoteconf - .channel_reserve_satoshis, - &state->remoteconf.htlc_minimum_msat, + &state->remoteconf.dust_limit, + &state->remoteconf.max_htlc_value_in_flight, + &state->remoteconf.channel_reserve, + &state->remoteconf.htlc_minimum, &minimum_depth, &state->remoteconf.to_self_delay, &state->remoteconf.max_accepted_htlcs, @@ -563,22 +576,26 @@ static u8 *funder_channel(struct state *state, * less than `dust_limit_satoshis`: * - MUST reject the channel. */ - if (state->remoteconf.channel_reserve_satoshis - < state->localconf.dust_limit_satoshis) { + if (amount_sat_greater(state->localconf.dust_limit, + state->remoteconf.channel_reserve)) { negotiation_failed(state, true, - "channel reserve %"PRIu64 - " would be below our dust %"PRIu64, - state->remoteconf.channel_reserve_satoshis, - state->localconf.dust_limit_satoshis); + "channel reserve %s" + " would be below our dust %s", + type_to_string(tmpctx, struct amount_sat, + &state->remoteconf.channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &state->localconf.dust_limit)); goto fail; } - if (state->localconf.channel_reserve_satoshis - < state->remoteconf.dust_limit_satoshis) { + if (amount_sat_greater(state->remoteconf.dust_limit, + state->localconf.channel_reserve)) { negotiation_failed(state, true, - "dust limit %"PRIu64 - " would be above our reserve %"PRIu64, - state->remoteconf.dust_limit_satoshis, - state->localconf.channel_reserve_satoshis); + "dust limit %s" + " would be above our reserve %s", + type_to_string(tmpctx, struct amount_sat, + &state->remoteconf.dust_limit), + type_to_string(tmpctx, struct amount_sat, + &state->localconf.channel_reserve)); goto fail; } @@ -587,7 +604,7 @@ static u8 *funder_channel(struct state *state, /*~ If lightningd told us to create change, use change index to do * that. */ - if (change_satoshis) { + if (!amount_sat_eq(change, AMOUNT_SAT(0))) { changekey = tal(tmpctx, struct pubkey); if (!bip32_pubkey(bip32_base, changekey, change_keyindex)) status_failed(STATUS_FAIL_MASTER_IO, @@ -602,10 +619,10 @@ static u8 *funder_channel(struct state *state, */ funding = funding_tx(state, &state->funding_txout, cast_const2(const struct utxo **, utxos), - state->funding_satoshis, + state->funding, &state->our_funding_pubkey, &their_funding_pubkey, - change_satoshis, changekey, + change, changekey, bip32_base); bitcoin_txid(funding, &state->funding_txid); @@ -621,9 +638,8 @@ static u8 *funder_channel(struct state *state, &state->chainparams->genesis_blockhash, &state->funding_txid, state->funding_txout, - state->funding_satoshis, - state->funding_satoshis * 1000 - - state->push_msat, + state->funding, + local_msat, state->feerate_per_kw, &state->localconf, &state->remoteconf, @@ -668,7 +684,7 @@ static u8 *funder_channel(struct state *state, msg = towire_hsm_sign_remote_commitment_tx(NULL, tx, &state->channel->funding_pubkey[REMOTE], - state->channel->funding_msat / 1000); + state->channel->funding); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); @@ -804,7 +820,7 @@ static u8 *funder_channel(struct state *state, &their_funding_pubkey, &state->funding_txid, state->feerate_per_kw, - state->localconf.channel_reserve_satoshis); + state->localconf.channel_reserve); fail: if (taken(utxos)) @@ -835,11 +851,12 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) */ if (!fromwire_open_channel(open_channel_msg, &chain_hash, &state->channel_id, - &state->funding_satoshis, &state->push_msat, - &state->remoteconf.dust_limit_satoshis, - &state->remoteconf.max_htlc_value_in_flight_msat, - &state->remoteconf.channel_reserve_satoshis, - &state->remoteconf.htlc_minimum_msat, + &state->funding, + &state->push_msat, + &state->remoteconf.dust_limit, + &state->remoteconf.max_htlc_value_in_flight, + &state->remoteconf.channel_reserve, + &state->remoteconf.htlc_minimum, &state->feerate_per_kw, &state->remoteconf.to_self_delay, &state->remoteconf.max_accepted_htlcs, @@ -884,10 +901,11 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * * The receiving node ... MUST fail the channel if `funding-satoshis` * is greater than or equal to 2^24 */ - if (state->funding_satoshis > state->chainparams->max_funding_satoshi) { + if (amount_sat_greater(state->funding, state->chainparams->max_funding)) { negotiation_failed(state, false, - "funding_satoshis %"PRIu64" too large", - state->funding_satoshis); + "funding_satoshis %s too large", + type_to_string(tmpctx, struct amount_sat, + &state->funding)); return NULL; } @@ -897,12 +915,15 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * ... * - `push_msat` is greater than `funding_satoshis` * 1000. */ - if (state->push_msat > state->funding_satoshis * 1000) { + if (amount_msat_greater_sat(state->push_msat, state->funding)) { peer_failed(&state->cs, &state->channel_id, - "Our push_msat %"PRIu64 - " would be too large for funding_satoshis %"PRIu64, - state->push_msat, state->funding_satoshis); + "Our push_msat %s" + " would be too large for funding_satoshis %s", + type_to_string(tmpctx, struct amount_msat, + &state->push_msat), + type_to_string(tmpctx, struct amount_sat, + &state->funding)); return NULL; } @@ -939,22 +960,26 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * - MUST set `dust_limit_satoshis` less than or equal to * `channel_reserve_satoshis` from the `open_channel` message. */ - if (state->localconf.channel_reserve_satoshis - < state->remoteconf.dust_limit_satoshis) { + if (amount_sat_greater(state->remoteconf.dust_limit, + state->localconf.channel_reserve)) { negotiation_failed(state, false, - "Our channel reserve %"PRIu64 - " would be below their dust %"PRIu64, - state->localconf.channel_reserve_satoshis, - state->remoteconf.dust_limit_satoshis); + "Our channel reserve %s" + " would be below their dust %s", + type_to_string(tmpctx, struct amount_sat, + &state->localconf.channel_reserve), + type_to_string(tmpctx, struct amount_sat, + &state->remoteconf.dust_limit)); return NULL; } - if (state->localconf.dust_limit_satoshis - > state->remoteconf.channel_reserve_satoshis) { + if (amount_sat_greater(state->localconf.dust_limit, + state->remoteconf.channel_reserve)) { negotiation_failed(state, false, - "Our dust limit %"PRIu64 - " would be above their reserve %"PRIu64, - state->localconf.dust_limit_satoshis, - state->remoteconf.channel_reserve_satoshis); + "Our dust limit %s" + " would be above their reserve %s", + type_to_string(tmpctx, struct amount_sat, + &state->localconf.dust_limit), + type_to_string(tmpctx, struct amount_sat, + &state->remoteconf.channel_reserve)); return NULL; } @@ -964,11 +989,10 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* OK, we accept! */ msg = towire_accept_channel(NULL, &state->channel_id, - state->localconf.dust_limit_satoshis, - state->localconf - .max_htlc_value_in_flight_msat, - state->localconf.channel_reserve_satoshis, - state->localconf.htlc_minimum_msat, + state->localconf.dust_limit, + state->localconf.max_htlc_value_in_flight, + state->localconf.channel_reserve, + state->localconf.htlc_minimum, state->minimum_depth, state->localconf.to_self_delay, state->localconf.max_accepted_htlcs, @@ -1017,7 +1041,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &chain_hash, &state->funding_txid, state->funding_txout, - state->funding_satoshis, + state->funding, state->push_msat, state->feerate_per_kw, &state->localconf, @@ -1112,7 +1136,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) msg = towire_hsm_sign_remote_commitment_tx(NULL, remote_commit, &state->channel->funding_pubkey[REMOTE], - state->channel->funding_msat / 1000); + state->channel->funding); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); @@ -1138,12 +1162,12 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &their_funding_pubkey, &state->funding_txid, state->funding_txout, - state->funding_satoshis, + state->funding, state->push_msat, channel_flags, state->feerate_per_kw, msg, - state->localconf.channel_reserve_satoshis); + state->localconf.channel_reserve); } /*~ Standard "peer sent a message, handle it" demuxer. Though it really only @@ -1271,7 +1295,7 @@ static u8 *handle_master_in(struct state *state) { u8 *msg = wire_sync_read(tmpctx, REQ_FD); enum opening_wire_type t = fromwire_peektype(msg); - u64 change_satoshis; + struct amount_sat change; u32 change_keyindex; u8 channel_flags; struct utxo **utxos; @@ -1280,16 +1304,17 @@ static u8 *handle_master_in(struct state *state) switch (t) { case WIRE_OPENING_FUNDER: if (!fromwire_opening_funder(state, msg, - &state->funding_satoshis, + &state->funding, &state->push_msat, &state->feerate_per_kw, - &change_satoshis, &change_keyindex, + &change, + &change_keyindex, &channel_flags, &utxos, &bip32_base)) master_badmsg(WIRE_OPENING_FUNDER, msg); msg = funder_channel(state, - change_satoshis, + change, change_keyindex, channel_flags, take(utxos), &bip32_base); return msg; @@ -1339,7 +1364,7 @@ int main(int argc, char *argv[]) &chain_hash, &state->localconf, &state->max_to_self_delay, - &state->min_effective_htlc_capacity_msat, + &state->min_effective_htlc_capacity, &state->cs, &state->our_points, &state->our_funding_pubkey, diff --git a/plugins/Makefile b/plugins/Makefile index 06ffe2e6539d..9a2e2294e09e 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -17,6 +17,7 @@ PLUGIN_COMMON_OBJS := \ bitcoin/signature.o \ bitcoin/tx.o \ bitcoin/varint.o \ + common/amount.o \ common/bech32.o \ common/bech32_util.o \ common/bolt11.o \ diff --git a/plugins/libplugin.c b/plugins/libplugin.c index c1edd49dfcf5..d47f18ef761f 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,12 @@ static UINTMAP(struct out_req *) out_reqs; static u64 next_outreq_id; +/* Map from json command names to usage strings: we don't put this inside + * struct json_command as it's good practice to have those const. */ +static STRMAP(const char *) usagemap; + +bool deprecated_apis; + struct plugin_conn { int fd; MEMBUF(char) mb; @@ -27,6 +34,7 @@ struct plugin_conn { struct command { u64 id; + const char *methodname; struct plugin_conn *rpc; }; @@ -110,11 +118,10 @@ static int read_json(struct plugin_conn *conn) static struct command *read_json_request(const tal_t *ctx, struct plugin_conn *conn, struct plugin_conn *rpc, - const jsmntok_t **method, const jsmntok_t **params, int *reqlen) { - const jsmntok_t *toks, *id; + const jsmntok_t *toks, *id, *method; bool valid; struct command *cmd = tal(ctx, struct command); @@ -128,7 +135,7 @@ static struct command *read_json_request(const tal_t *ctx, plugin_err("Malformed JSON command '%*.s' is not an object", *reqlen, membuf_elems(&conn->mb)); - *method = json_get_member(membuf_elems(&conn->mb), toks, "method"); + method = json_get_member(membuf_elems(&conn->mb), toks, "method"); *params = json_get_member(membuf_elems(&conn->mb), toks, "params"); /* FIXME: Notifications don't have id! */ id = json_get_member(membuf_elems(&conn->mb), toks, "id"); @@ -138,6 +145,7 @@ static struct command *read_json_request(const tal_t *ctx, membuf_elems(&conn->mb) + id->start); /* Putting this in cmd avoids a global, or direct exposure to users */ cmd->rpc = rpc; + cmd->methodname = json_strdup(cmd, membuf_elems(&conn->mb), method); return cmd; } @@ -259,20 +267,23 @@ struct command_result *command_fail(struct command *cmd, return res; } -/* We never invoke param for usage. */ +/* We invoke param for usage at registration time. */ bool command_usage_only(const struct command *cmd) { - return false; + return cmd->rpc == NULL; } +/* FIXME: would be good to support this! */ bool command_check_only(const struct command *cmd) { return false; } -void command_set_usage(struct command *cmd, const char *usage) +void command_set_usage(struct command *cmd, const char *usage TAKES) { - abort(); + usage = tal_strdup(NULL, usage); + if (!strmap_add(&usagemap, cmd->methodname, usage)) + plugin_err("Two usages for command %s?", cmd->methodname); } /* Reads rpc reply and returns tokens, setting contents to 'error' or @@ -417,8 +428,10 @@ handle_getmanifest(struct command *getmanifest_cmd, "'rpcmethods': [ "); for (size_t i = 0; i < num_commands; i++) { tal_append_fmt(¶ms, "{ 'name': '%s'," + " 'usage': '%s'," " 'description': '%s'", commands[i].name, + strmap_get(&usagemap, commands[i].name), commands[i].description); if (commands[i].long_description) tal_append_fmt(¶ms, @@ -461,6 +474,12 @@ static struct command_result *handle_init(struct command *init_cmd, rpctok->end - rpctok->start, buf + rpctok->start, strerror(errno)); + deprecated_apis = streq(rpc_delve(tmpctx, "listconfigs", + "'config': 'allow-deprecated-apis'", + init_cmd->rpc, + ".allow-deprecated-apis"), + "true"); + if (init) init(init_cmd->rpc); @@ -474,14 +493,12 @@ static void handle_new_command(const tal_t *ctx, size_t num_commands) { struct command *cmd; - const jsmntok_t *method, *params; + const jsmntok_t *params; int reqlen; - cmd = read_json_request(ctx, request_conn, rpc_conn, - &method, ¶ms, &reqlen); + cmd = read_json_request(ctx, request_conn, rpc_conn, ¶ms, &reqlen); for (size_t i = 0; i < num_commands; i++) { - if (json_tok_streq(membuf_elems(&request_conn->mb), method, - commands[i].name)) { + if (streq(cmd->methodname, commands[i].name)) { commands[i].handle(cmd, membuf_elems(&request_conn->mb), params); membuf_consume(&request_conn->mb, reqlen); @@ -489,9 +506,24 @@ static void handle_new_command(const tal_t *ctx, } } - plugin_err("Unknown command '%.*s'", - method->end - method->start, - membuf_elems(&request_conn->mb) + method->start); + plugin_err("Unknown command '%s'", cmd->methodname); +} + +static void setup_command_usage(const struct plugin_command *commands, + size_t num_commands) +{ + struct command *usage_cmd = tal(tmpctx, struct command); + + /* This is how common/param can tell it's just a usage request */ + usage_cmd->rpc = NULL; + for (size_t i = 0; i < num_commands; i++) { + struct command_result *res; + + usage_cmd->methodname = commands[i].name; + res = commands[i].handle(usage_cmd, NULL, NULL); + assert(res == NULL); + assert(strmap_get(&usagemap, commands[i].name)); + } } void plugin_main(char *argv[], @@ -502,7 +534,7 @@ void plugin_main(char *argv[], struct plugin_conn request_conn, rpc_conn; const tal_t *ctx = tal(NULL, char); struct command *cmd; - const jsmntok_t *method, *params; + const jsmntok_t *params; int reqlen; struct pollfd fds[2]; @@ -513,6 +545,8 @@ void plugin_main(char *argv[], /* Note this already prints to stderr, which is enough for now */ daemon_setup(argv[0], NULL, NULL); + setup_command_usage(commands, num_commands); + membuf_init(&rpc_conn.mb, tal_arr(ctx, char, READ_CHUNKSIZE), READ_CHUNKSIZE, membuf_tal_realloc); @@ -523,23 +557,18 @@ void plugin_main(char *argv[], uintmap_init(&out_reqs); cmd = read_json_request(tmpctx, &request_conn, NULL, - &method, ¶ms, &reqlen); - if (!json_tok_streq(membuf_elems(&request_conn.mb), method, - "getmanifest")) { - plugin_err("Expected getmanifest not '%.*s'", - method->end - method->start, - membuf_elems(&request_conn.mb) + method->start); - } + ¶ms, &reqlen); + if (!streq(cmd->methodname, "getmanifest")) + plugin_err("Expected getmanifest not %s", cmd->methodname); + membuf_consume(&request_conn.mb, reqlen); handle_getmanifest(cmd, commands, num_commands); cmd = read_json_request(tmpctx, &request_conn, &rpc_conn, - &method, ¶ms, &reqlen); - if (!json_tok_streq(membuf_elems(&request_conn.mb), method, "init")) { - plugin_err("Expected init not '%.*s'", - method->end - method->start, - membuf_elems(&request_conn.mb) + method->start); - } + ¶ms, &reqlen); + if (!streq(cmd->methodname, "init")) + plugin_err("Expected init not %s", cmd->methodname); + handle_init(cmd, membuf_elems(&request_conn.mb), params, init); membuf_consume(&request_conn.mb, reqlen); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index b38c7aa1fa2c..036e18ca714a 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -12,6 +12,8 @@ struct command; struct plugin_conn; +extern bool deprecated_apis; + /* Create an array of these, one for each command you support. */ struct plugin_command { const char *name; diff --git a/plugins/pay.c b/plugins/pay.c index 19905d9216cf..f85dbc6f26ea 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1,7 +1,9 @@ #include +#include #include #include #include +#include #include #include #include @@ -39,9 +41,9 @@ struct pay_status { struct list_node list; /* Description user provided (if any) */ - const char *desc; + const char *label; /* Amount they wanted to pay. */ - u64 msatoshi; + struct amount_msat msat; /* CLTV delay required by destination. */ u32 final_cltv; /* Bolt11 invoice. */ @@ -65,20 +67,20 @@ struct pay_command { const char *dest; /* How much we're paying, and what riskfactor for routing. */ - u64 msatoshi; + struct amount_msat msat; double riskfactor; unsigned int final_cltv; /* Limits on what routes we'll accept. */ double maxfeepercent; unsigned int maxdelay; - u64 exemptfee; + struct amount_msat exemptfee; /* Payment hash, as text. */ const char *payment_hash; /* Description, if any. */ - const char *desc; + const char *label; /* Chatty description of attempts. */ struct pay_status *ps; @@ -92,7 +94,10 @@ struct pay_command { /* Channels which have failed us. */ const char **excludes; - /* Any routehints to use. */ + /* Current routehint, if any. */ + struct route_info *current_routehint; + + /* Any remaining routehints to try. */ struct route_info **routehints; /* Current node during shadow route calculation. */ @@ -145,7 +150,7 @@ static bool channel_in_routehint(const struct route_info *routehint, { struct short_channel_id scid; - if (!json_to_short_channel_id(buf, scidtok, &scid)) + if (!json_to_short_channel_id(buf, scidtok, &scid, false)) plugin_err("bad erring_channel '%.*s'", scidtok->end - scidtok->start, buf + scidtok->start); @@ -179,12 +184,23 @@ static struct command_result *waitsendpay_expired(struct command *cmd, return command_done_err(cmd, PAY_STOPPED_RETRYING, errmsg, data); } -/* Try again with the next routehint (or none if that was the last) */ static struct command_result *next_routehint(struct command *cmd, struct pay_command *pc) { - tal_arr_remove(&pc->routehints, 0); - return start_pay_attempt(cmd, pc, "Removed route hint"); + if (tal_count(pc->routehints) > 0) { + pc->current_routehint = pc->routehints[0]; + tal_arr_remove(&pc->routehints, 0); + return start_pay_attempt(cmd, pc, "Trying route hint"); + } + + /* No (more) routehints; we're out of routes. */ + /* If we eliminated one because it was too pricy, return that. */ + if (pc->expensive_route) + return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE, + "%s", pc->expensive_route); + + return command_fail(cmd, PAY_ROUTE_NOT_FOUND, + "Could not find a route"); } static struct command_result *waitsendpay_error(struct command *cmd, @@ -222,9 +238,8 @@ static struct command_result *waitsendpay_error(struct command *cmd, return waitsendpay_expired(cmd, pc); } - /* If failure is in routehint part, eliminate that */ - if (tal_count(pc->routehints) != 0 - && channel_in_routehint(pc->routehints[0], buf, scidtok)) + /* If failure is in routehint part, try next one */ + if (channel_in_routehint(pc->current_routehint, buf, scidtok)) return next_routehint(cmd, pc); /* Otherwise, add erring channel to exclusion list. */ @@ -264,17 +279,18 @@ static struct command_result *sendpay_done(struct command *cmd, /* Calculate how many millisatoshi we need at the start of this route * to get msatoshi to the end. */ -static u64 route_msatoshi(u64 msatoshi, - const struct route_info *route, size_t num_route) +static bool route_msatoshi(struct amount_msat *total, + const struct amount_msat msat, + const struct route_info *route, size_t num_route) { + *total = msat; for (ssize_t i = num_route - 1; i >= 0; i--) { - u64 fee; - - fee = route[i].fee_base_msat; - fee += (route[i].fee_proportional_millionths * msatoshi) / 1000000; - msatoshi += fee; + if (!amount_msat_add_fee(total, + route[i].fee_base_msat, + route[i].fee_proportional_millionths)) + return false; } - return msatoshi; + return true; } /* Calculate cltv we need at the start of this route to get cltv at the end. */ @@ -308,18 +324,25 @@ static const char *join_routehint(const tal_t *ctx, /* Truncate closing ] from route */ ret = tal_strndup(ctx, buf + route->start, route->end - route->start - 1); for (size_t i = 0; i < tal_count(routehint); i++) { + /* amount to be received by *destination* */ + struct amount_msat dest_amount; + + if (!route_msatoshi(&dest_amount, pc->msat, + routehint + i + 1, + tal_count(routehint) - i - 1)) + return tal_free(ret); + tal_append_fmt(&ret, ", {" " 'id': '%s'," " 'channel': '%s'," - " 'msatoshi': %"PRIu64"," + " 'msatoshi': '%s'," " 'delay': %u }", /* pubkey of *destination* */ route_pubkey(tmpctx, pc, routehint, i + 1), type_to_string(tmpctx, struct short_channel_id, &routehint[i].short_channel_id), - /* amount to be received by *destination* */ - route_msatoshi(pc->msatoshi, routehint + i + 1, - tal_count(routehint) - i - 1), + type_to_string(tmpctx, struct amount_msat, + &dest_amount), /* cltv for *destination* */ route_cltv(pc->final_cltv, routehint + i + 1, tal_count(routehint) - i - 1)); @@ -370,8 +393,7 @@ static bool maybe_exclude(struct pay_command *pc, scid = json_get_member(buf, route, "channel"); - if (tal_count(pc->routehints) != 0 - && channel_in_routehint(pc->routehints[0], buf, scid)) + if (channel_in_routehint(pc->current_routehint, buf, scid)) return false; dir = json_get_member(buf, route, "direction"); @@ -391,7 +413,7 @@ static struct command_result *getroute_done(struct command *cmd, struct pay_attempt *attempt = current_attempt(pc); const jsmntok_t *t = json_get_member(buf, result, "route"); char *json_desc; - u64 fee; + struct amount_msat fee; u32 delay; double feepercent; @@ -399,53 +421,62 @@ static struct command_result *getroute_done(struct command *cmd, plugin_err("getroute gave no 'route'? '%.*s'", result->end - result->start, buf); - if (tal_count(pc->routehints)) + if (pc->current_routehint) { attempt->route = join_routehint(pc->ps->attempts, buf, t, - pc, pc->routehints[0]); - else + pc, pc->current_routehint); + if (!attempt->route) { + attempt_failed_fmt(pc, + "{ 'message': 'Joining routehint gave absurd fee' }"); + return next_routehint(cmd, pc); + } + } else attempt->route = json_strdup(pc->ps->attempts, buf, t); - if (!json_to_u64(buf, json_delve(buf, t, "[0].msatoshi"), &fee)) - plugin_err("getroute with invalid msatoshi? '%.*s'", + if (!json_to_msat(buf, json_delve(buf, t, "[0].msatoshi"), &fee)) + plugin_err("getroute with invalid msatoshi? %.*s", result->end - result->start, buf); - fee -= pc->msatoshi; + if (!amount_msat_sub(&fee, fee, pc->msat)) + plugin_err("final amount %s less than paid %s", + type_to_string(tmpctx, struct amount_msat, &fee), + type_to_string(tmpctx, struct amount_msat, &pc->msat)); if (!json_to_number(buf, json_delve(buf, t, "[0].delay"), &delay)) - plugin_err("getroute with invalid delay? '%.*s'", + plugin_err("getroute with invalid delay? %.*s", result->end - result->start, buf); /* Casting u64 to double will lose some precision. The loss of precision * in feepercent will be like 3.0000..(some dots)..1 % - 3.0 %. * That loss will not be representable in double. So, it's Okay to * cast u64 to double for feepercent calculation. */ - feepercent = ((double)fee) * 100.0 / ((double) pc->msatoshi); + feepercent = ((double)fee.millisatoshis) * 100.0 / ((double) pc->msat.millisatoshis); /* Raw: fee double manipulation */ - if (fee > pc->exemptfee && feepercent > pc->maxfeepercent) { + if (amount_msat_greater(fee, pc->exemptfee) + && feepercent > pc->maxfeepercent) { const jsmntok_t *charger; - attempt_failed_fmt(pc, "{ 'message': 'Route wanted fee of %"PRIu64" msatoshis' }", fee); + attempt_failed_fmt(pc, "{ 'message': 'Route wanted fee of %s' }", + type_to_string(tmpctx, struct amount_msat, + &fee)); /* Remember this if we eliminating this causes us to have no * routes at all! */ if (!pc->expensive_route) pc->expensive_route - = tal_fmt(pc, "Route wanted fee of %"PRIu64 - " msatoshis", fee); + = tal_fmt(pc, "Route wanted fee of %s", + type_to_string(tmpctx, + struct amount_msat, + &fee)); /* Try excluding most fee-charging channel (unless it's in * routeboost). */ - charger = find_worst_channel(buf, t, "msatoshi", pc->msatoshi); + charger = find_worst_channel(buf, t, "msatoshi", pc->msat.millisatoshis); /* Raw: shared function needs u64 */ if (maybe_exclude(pc, buf, charger)) { return start_pay_attempt(cmd, pc, "Excluded expensive channel %s", pc->excludes[tal_count(pc->excludes)-1]); } - if (tal_count(pc->routehints) != 0) - return next_routehint(cmd, pc); - - return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE, - "%s", pc->expensive_route); + return next_routehint(cmd, pc); } if (delay > pc->maxdelay) { @@ -472,22 +503,19 @@ static struct command_result *getroute_done(struct command *cmd, pc->excludes[tal_count(pc->excludes)-1]); } - if (tal_count(pc->routehints) != 0) - return next_routehint(cmd, pc); - - return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE, - "%s", pc->expensive_route); + return next_routehint(cmd, pc); } - if (pc->desc) - json_desc = tal_fmt(pc, ", 'description': '%s'", pc->desc); + if (pc->label) + json_desc = tal_fmt(pc, ", 'label': '%s'", pc->label); else json_desc = ""; return send_outreq(cmd, "sendpay", sendpay_done, sendpay_error, pc, - "'route': %s, 'payment_hash': '%s'%s", + "'route': %s, 'payment_hash': '%s', 'bolt11': '%s'%s", attempt->route, pc->payment_hash, + pc->ps->bolt11, json_desc); } @@ -497,18 +525,21 @@ static struct command_result *getroute_error(struct command *cmd, const jsmntok_t *error, struct pay_command *pc) { + int code; + const jsmntok_t *codetok; + attempt_failed_tok(pc, "getroute", buf, error); - /* If we were trying to use a routehint, remove and try again. */ - if (tal_count(pc->routehints) != 0) - return next_routehint(cmd, pc); + codetok = json_get_member(buf, error, "code"); + if (!json_to_int(buf, codetok, &code)) + plugin_err("getroute error gave no 'code'? '%.*s'", + error->end - error->start, buf + error->start); - /* If we've run out of routes, there might be a good reason. */ - if (pc->expensive_route) - return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE, - "%s", pc->expensive_route); + /* Strange errors from getroute should be forwarded. */ + if (code != PAY_ROUTE_NOT_FOUND) + return forward_error(cmd, buf, error, pc); - return forward_error(cmd, buf, error, pc); + return next_routehint(cmd, pc); } /* Deep copy of excludes array. */ @@ -526,22 +557,27 @@ static struct command_result *start_pay_attempt(struct command *cmd, const char *fmt, ...) { char *exclude; - u64 amount; + struct amount_msat msat; const char *dest; size_t max_hops = ROUTING_MAX_HOPS; u32 cltv; - struct pay_attempt attempt; + struct pay_attempt *attempt; va_list ap; + size_t n; + + n = tal_count(pc->ps->attempts); + tal_resize(&pc->ps->attempts, n+1); + attempt = &pc->ps->attempts[n]; va_start(ap, fmt); - attempt.start = time_now(); + attempt->start = time_now(); /* Mark it unfinished */ - attempt.end.ts.tv_sec = -1; - attempt.excludes = dup_excludes(pc->ps, pc->excludes); - attempt.route = NULL; - attempt.failure = NULL; - attempt.result = NULL; - attempt.why = tal_vfmt(pc->ps, fmt, ap); + attempt->end.ts.tv_sec = -1; + attempt->excludes = dup_excludes(pc->ps, pc->excludes); + attempt->route = NULL; + attempt->failure = NULL; + attempt->result = NULL; + attempt->why = tal_vfmt(pc->ps, fmt, ap); va_end(ap); /* routehint set below. */ @@ -559,34 +595,38 @@ static struct command_result *start_pay_attempt(struct command *cmd, /* If we have a routehint, try that first; we need to do extra * checks that it meets our criteria though. */ - if (tal_count(pc->routehints)) { - amount = route_msatoshi(pc->msatoshi, - pc->routehints[0], - tal_count(pc->routehints[0])); + if (pc->current_routehint) { + attempt->routehint = tal_steal(pc->ps, pc->current_routehint); + if (!route_msatoshi(&msat, pc->msat, + attempt->routehint, + tal_count(attempt->routehint))) { + attempt_failed_fmt(pc, + "{ 'message': 'Routehint absurd fee' }"); + return next_routehint(cmd, pc); + } dest = type_to_string(tmpctx, struct pubkey, - &pc->routehints[0][0].pubkey); - max_hops -= tal_count(pc->routehints[0]); + &attempt->routehint[0].pubkey); + max_hops -= tal_count(attempt->routehint); cltv = route_cltv(pc->final_cltv, - pc->routehints[0], - tal_count(pc->routehints[0])); - attempt.routehint = tal_steal(pc->ps, pc->routehints[0]); + attempt->routehint, + tal_count(attempt->routehint)); } else { - amount = pc->msatoshi; + msat = pc->msat; dest = pc->dest; cltv = pc->final_cltv; - attempt.routehint = NULL; + attempt->routehint = NULL; } - tal_arr_expand(&pc->ps->attempts, attempt); - /* OK, ask for route to destination */ return send_outreq(cmd, "getroute", getroute_done, getroute_error, pc, "'id': '%s'," - "'msatoshi': %"PRIu64"," + "'msatoshi': '%s'," "'cltv': %u," "'maxhops': %zu," "'riskfactor': %f%s", - dest, amount, cltv, max_hops, pc->riskfactor, exclude); + dest, + type_to_string(tmpctx, struct amount_msat, &msat), + cltv, max_hops, pc->riskfactor, exclude); } /* BOLT #7: @@ -622,10 +662,11 @@ static struct command_result *add_shadow_route(struct command *cmd, u32 cltv, best_cltv; json_for_each_arr(i, chan, channels) { - u64 sats, v; + struct amount_sat sat; + u64 v; - json_to_u64(buf, json_get_member(buf, chan, "satoshis"), &sats); - if (sats * 1000 < pc->msatoshi) + json_to_sat(buf, json_get_member(buf, chan, "satoshis"), &sat); + if (amount_msat_greater_sat(pc->msat, sat)) continue; /* Don't use if total would exceed 1/4 of our time allowance. */ @@ -695,7 +736,7 @@ static struct command_result *listpeers_done(struct command *cmd, chans = json_get_member(buf, peer, "channels"); json_for_each_arr(j, chan, chans) { const jsmntok_t *state, *scid, *dir; - u64 spendable; + struct amount_msat spendable; /* gossipd will only consider things in state NORMAL * anyway; we don't need to exclude others. */ @@ -703,12 +744,13 @@ static struct command_result *listpeers_done(struct command *cmd, if (!json_tok_streq(buf, state, "CHANNELD_NORMAL")) continue; - json_to_u64(buf, + json_to_msat(buf, json_get_member(buf, chan, "spendable_msatoshi"), &spendable); - if (connected && spendable >= pc->msatoshi) + if (connected + && amount_msat_greater_eq(spendable, pc->msat)) continue; /* Exclude this disconnected or low-capacity channel */ @@ -721,9 +763,10 @@ static struct command_result *listpeers_done(struct command *cmd, buf[dir->start])); tal_append_fmt(&mods, - "Excluded channel %s (%"PRIu64" msat, %s). ", + "Excluded channel %s (%s, %s). ", pc->excludes[tal_count(pc->excludes)-1], - spendable, + type_to_string(tmpctx, struct amount_msat, + &spendable), connected ? "connected" : "disconnected"); } } @@ -792,8 +835,8 @@ static struct pay_status *add_pay_status(struct pay_command *pc, /* The pay_status outlives the pc, so it simply takes field ownership */ ps->dest = tal_steal(ps, pc->dest); - ps->desc = tal_steal(ps, pc->desc); - ps->msatoshi = pc->msatoshi; + ps->label = tal_steal(ps, pc->label); + ps->msat = pc->msat; ps->final_cltv = pc->final_cltv; ps->bolt11 = tal_steal(ps, b11str); ps->routehint_modifications = NULL; @@ -805,37 +848,69 @@ static struct pay_status *add_pay_status(struct pay_command *pc, return ps; } -static struct command_result *handle_pay(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *json_pay(struct command *cmd, + const char *buf, + const jsmntok_t *params) { - u64 *msatoshi; + struct amount_msat *msat; struct bolt11 *b11; - const char *b11str; + const char *b11str, *description_deprecated; char *fail; double *riskfactor; unsigned int *retryfor; struct pay_command *pc = tal(cmd, struct pay_command); double *maxfeepercent; unsigned int *maxdelay; - u64 *exemptfee; - - setup_locale(); - - if (!param(cmd, buf, params, - p_req("bolt11", param_string, &b11str), - p_opt("msatoshi", param_u64, &msatoshi), - p_opt("description", param_string, &pc->desc), - p_opt_def("riskfactor", param_double, &riskfactor, 1.0), - p_opt_def("maxfeepercent", param_percent, &maxfeepercent, 0.5), - p_opt_def("retry_for", param_number, &retryfor, 60), - p_opt_def("maxdelay", param_number, &maxdelay, - maxdelay_default), - p_opt_def("exemptfee", param_u64, &exemptfee, 5000), - NULL)) + struct amount_msat *exemptfee; + + /* If params is array, label takes place of description. For + * keywords, its a separate parameter. */ + if (!params || params->type == JSMN_ARRAY) { + if (!param(cmd, buf, params, + p_req("bolt11", param_string, &b11str), + p_opt("msatoshi", param_msat, &msat), + p_opt("label", param_string, &pc->label), + p_opt_def("riskfactor", param_double, &riskfactor, 10), + p_opt_def("maxfeepercent", param_percent, &maxfeepercent, 0.5), + p_opt_def("retry_for", param_number, &retryfor, 60), + p_opt_def("maxdelay", param_number, &maxdelay, + maxdelay_default), + p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), + NULL)) + return NULL; + + /* This works because bolt11_decode ignores unneeded descriptions */ + if (deprecated_apis) + description_deprecated = pc->label; + else + description_deprecated = NULL; + } else { + /* If by keyword, treat description and label as + * separate parameters. */ + if (!param(cmd, buf, params, + p_req("bolt11", param_string, &b11str), + p_opt("msatoshi", param_msat, &msat), + p_opt("description", param_string, + &description_deprecated), + p_opt_def("riskfactor", param_double, &riskfactor, 10), + p_opt_def("maxfeepercent", param_percent, &maxfeepercent, 0.5), + p_opt_def("retry_for", param_number, &retryfor, 60), + p_opt_def("maxdelay", param_number, &maxdelay, + maxdelay_default), + p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), + p_opt("label", param_string, &pc->label), + NULL)) return NULL; - b11 = bolt11_decode(cmd, b11str, pc->desc, &fail); + if (description_deprecated && !deprecated_apis) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Deprecated parameter description, use label"); + if (description_deprecated && pc->label) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Cannot specify both description and label"); + } + + b11 = bolt11_decode(cmd, b11str, description_deprecated, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); @@ -845,18 +920,18 @@ static struct command_result *handle_pay(struct command *cmd, return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired"); } - if (b11->msatoshi) { - if (msatoshi) { + if (b11->msat) { + if (msat) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "msatoshi parameter unnecessary"); } - pc->msatoshi = *b11->msatoshi; + pc->msat = *b11->msat; } else { - if (!msatoshi) { + if (!msat) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "msatoshi parameter required"); } - pc->msatoshi = *msatoshi; + pc->msat = *msat; } pc->maxfeepercent = *maxfeepercent; @@ -871,6 +946,8 @@ static struct command_result *handle_pay(struct command *cmd, pc->stoptime = timeabs_add(time_now(), time_from_sec(*retryfor)); pc->excludes = tal_arr(cmd, const char *, 0); pc->ps = add_pay_status(pc, b11str); + /* We try first without using routehint */ + pc->current_routehint = NULL; pc->routehints = filter_routehints(pc, b11->routes); pc->expensive_route = NULL; @@ -920,20 +997,18 @@ static void add_attempt(char **ret, tal_append_fmt(ret, "%s{" " 'id': '%s'," " 'channel': '%s'," - " 'msatoshi': %"PRIu64"," - " 'delay': %u }", + " 'fee_base_msat': %u," + " 'fee_proportional_millionths': %u," + " 'cltv_expiry_delta': %u }", i == 0 ? "" : ", ", type_to_string(tmpctx, struct pubkey, &attempt->routehint[i].pubkey), type_to_string(tmpctx, struct short_channel_id, &attempt->routehint[i].short_channel_id), - route_msatoshi(ps->msatoshi, - attempt->routehint + i, - tal_count(attempt->routehint) - i), - route_cltv(ps->final_cltv, - attempt->routehint + i, - tal_count(attempt->routehint) - i)); + attempt->routehint[i].fee_base_msat, + attempt->routehint[i].fee_proportional_millionths, + attempt->routehint[i].cltv_expiry_delta); } tal_append_fmt(ret, "]"); } @@ -960,9 +1035,9 @@ static void add_attempt(char **ret, tal_append_fmt(ret, "}"); } -static struct command_result *handle_paystatus(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *json_paystatus(struct command *cmd, + const char *buf, + const jsmntok_t *params) { struct pay_status *ps; const char *b11str; @@ -986,10 +1061,14 @@ static struct command_result *handle_paystatus(struct command *cmd, tal_append_fmt(&ret, "{ 'bolt11': '%s'," " 'msatoshi': %"PRIu64", " + " 'amount_msat': '%s', " " 'destination': '%s'", - ps->bolt11, ps->msatoshi, ps->dest); - if (ps->desc) - tal_append_fmt(&ret, ", 'description': '%s'", ps->desc); + ps->bolt11, + ps->msat.millisatoshis, /* Raw: JSON */ + type_to_string(tmpctx, struct amount_msat, + &ps->msat), ps->dest); + if (ps->label) + tal_append_fmt(&ret, ", 'label': '%s'", ps->label); if (ps->routehint_modifications) tal_append_fmt(&ret, ", 'routehint_modifications': '%s'", ps->routehint_modifications); @@ -1015,6 +1094,90 @@ static struct command_result *handle_paystatus(struct command *cmd, return command_success(cmd, ret); } +static const jsmntok_t *copy_member(char **ret, + const char *buf, + const jsmntok_t *result, + const char *membername, + const char *term) +{ + const jsmntok_t *m = json_get_member(buf, result, membername); + if (m) + tal_append_fmt(ret, "'%s': %.*s%s", + membername, + json_tok_full_len(m), json_tok_full(buf, m), + term); + return m; +} + +static struct command_result *listsendpays_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + char *b11str) +{ + size_t i; + const jsmntok_t *t, *arr; + char *ret; + bool some = false; + + arr = json_get_member(buf, result, "payments"); + if (!arr || arr->type != JSMN_ARRAY) + return command_fail(cmd, LIGHTNINGD, + "Unexpected non-array result from listsendpays"); + + ret = tal_fmt(cmd, "{ 'pays': ["); + json_for_each_arr(i, t, arr) { + const jsmntok_t *status; + + if (some) + tal_append_fmt(&ret, ",\n"); + some = true; + + tal_append_fmt(&ret, "{"); + /* Old payments didn't have bolt11 field */ + if (!copy_member(&ret, buf, t, "bolt11", ",")) { + if (b11str) { + /* If it's a single query, we can fake it */ + tal_append_fmt(&ret, "'bolt11': '%s',", b11str); + } else { + copy_member(&ret, buf, t, "payment_hash", ","); + copy_member(&ret, buf, t, "destination", ","); + copy_member(&ret, buf, t, "amount_msat", ","); + } + } + + status = copy_member(&ret, buf, t, "status", ","); + if (status && json_tok_streq(buf, status, "complete")) + copy_member(&ret, buf, t, "payment_preimage", ","); + copy_member(&ret, buf, t, "label", ","); + copy_member(&ret, buf, t, "amount_sent_msat", ""); + tal_append_fmt(&ret, "}"); + } + tal_append_fmt(&ret, "] }"); + return command_success(cmd, ret); +} + +static struct command_result *json_listpays(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *b11str, *paramstr; + + /* FIXME: would be nice to parse as a bolt11 so check worked in future */ + if (!param(cmd, buf, params, + p_opt("bolt11", param_string, &b11str), + NULL)) + return NULL; + + if (b11str) + paramstr = tal_fmt(tmpctx, "'bolt11' : '%s'", b11str); + else + paramstr = ""; + return send_outreq(cmd, "listsendpays", + listsendpays_done, forward_error, + cast_const(char *, b11str), + "%s", paramstr); +} + static void init(struct plugin_conn *rpc) { const char *field; @@ -1031,18 +1194,24 @@ static void init(struct plugin_conn *rpc) static const struct plugin_command commands[] = { { "pay", - "Send payment specified by {bolt11} with {msatoshi}", + "Send payment specified by {bolt11} with {amount}", "Try to send a payment, retrying {retry_for} seconds before giving up", - handle_pay + json_pay }, { "paystatus", "Detail status of attempts to pay {bolt11}, or all", "Covers both old payments and current ones.", - handle_paystatus + json_paystatus + }, { + "listpays", + "List result of payment {bolt11}, or all", + "Covers old payments (failed and succeeded) and current ones.", + json_listpays } }; int main(int argc, char *argv[]) { + setup_locale(); plugin_main(argv, init, commands, ARRAY_SIZE(commands)); } diff --git a/tests/btcproxy.py b/tests/btcproxy.py index 8dd6d1d389a3..b43a4dbaab80 100755 --- a/tests/btcproxy.py +++ b/tests/btcproxy.py @@ -56,6 +56,7 @@ def _handle_request(self, r): except JSONRPCError as e: reply = { "error": e.error, + "code": -32603, "id": r['id'] } self.request_count += 1 diff --git a/tests/data/dangling-peer.sqlite3.xz b/tests/data/dangling-peer.sqlite3.xz new file mode 100644 index 000000000000..df05a4b4f932 Binary files /dev/null and b/tests/data/dangling-peer.sqlite3.xz differ diff --git a/tests/fixtures.py b/tests/fixtures.py index a4eb913edfe1..a59a09fd8e24 100755 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -98,7 +98,7 @@ def bitcoind(directory): yield bitcoind try: - bitcoind.rpc.stop() + bitcoind.stop() except Exception: bitcoind.proc.kill() bitcoind.proc.wait() diff --git a/tests/plugins/asynctest.py b/tests/plugins/asynctest.py new file mode 100755 index 000000000000..97dd4df61db9 --- /dev/null +++ b/tests/plugins/asynctest.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +"""This plugin is used to check that async method calls are working correctly. + +The plugin registers a method `callme` with an argument. All calls are +stashed away, and are only resolved on the fifth invocation. All calls +will then return the argument of the fifth call. + +""" +from lightning import Plugin + +plugin = Plugin() + + +@plugin.init() +def init(configuration, options, plugin): + plugin.requests = [] + + +@plugin.async_method('asyncqueue') +def async_queue(request, plugin): + plugin.requests.append(request) + + +@plugin.method('asyncflush') +def async_flush(res, plugin): + for r in plugin.requests: + r.set_result(res) + + +plugin.run() diff --git a/tests/plugins/millisatoshis.py b/tests/plugins/millisatoshis.py new file mode 100755 index 000000000000..5404aa8e086d --- /dev/null +++ b/tests/plugins/millisatoshis.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +from lightning import Plugin, Millisatoshi + + +plugin = Plugin(autopatch=True) + + +@plugin.method("echo") +def echo(plugin, msat: Millisatoshi, not_an_msat): + plugin.log("got echo for {} {} (types {} and {})" + .format(msat, not_an_msat, type(msat), type(not_an_msat))) + if not isinstance(msat, Millisatoshi): + raise TypeError("msat must be Millisatoshi not {}".format(type(msat))) + if isinstance(not_an_msat, Millisatoshi): + raise TypeError("not_an_msat must not be Millisatoshi") + plugin.log("got echo for {} (type {})".format(msat, type(msat))) + if not isinstance(msat, Millisatoshi): + raise TypeError("msat must be Millisatoshi not {}".format(type(msat))) + plugin.log("Returning {}".format(msat)) + return {'echo_msat': msat} + + +plugin.run() diff --git a/tests/plugins/reject.py b/tests/plugins/reject.py new file mode 100755 index 000000000000..6850578562aa --- /dev/null +++ b/tests/plugins/reject.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Simple plugin to test the connected_hook. + +It can mark some node_ids as rejects and it'll check for each +connection if it should be disconnected immediately or if it can +continue. + +""" + +from lightning import Plugin + +plugin = Plugin() + + +@plugin.hook('peer_connected') +def on_connected(peer, plugin): + if peer['id'] in plugin.reject_ids: + print("{} is in reject list, disconnecting".format(peer['id'])) + return {'result': 'disconnect'} + + print("{} is allowed".format(peer['id'])) + return {'result': 'continue'} + + +@plugin.init() +def init(configuration, options, plugin): + plugin.reject_ids = [] + + +@plugin.method('reject') +def reject(node_id, plugin): + """Mark a given node_id as reject for future connections. + """ + print("Rejecting connections from {}".format(node_id)) + plugin.reject_ids.append(node_id) + + +plugin.run() diff --git a/tests/requirements.txt b/tests/requirements.txt index 7037dac72c36..f323b311fd74 100755 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,13 +1,13 @@ -flake8==3.5.0 -pytest==3.8.1 -CherryPy==17.3.0 +flake8==3.6.0 +pytest==4.1.1 +CherryPy==18.1.0 Flask==1.0.2 -cheroot==6.5.2 +cheroot==6.5.4 ephemeral-port-reserve==1.1.0 -flaky==3.4.0 -pytest-benchmark==3.1.1 -pytest-forked==0.2 -pytest-xdist==1.22.2 -python-bitcoinlib==0.7.0 -tqdm==4.26.0 +flaky==3.5.3 +pytest-benchmark==3.2.2 +pytest-forked==1.0.1 +pytest-xdist==1.26.0 +python-bitcoinlib==0.10.1 +tqdm==4.29.1 pytest-timeout==1.3.3 diff --git a/tests/test_closing.py b/tests/test_closing.py index 992231559925..44df77aaf9fc 100755 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -215,7 +215,7 @@ def test_closing_different_fees(node_factory, bitcoind, executor): peers.append(p) for p in peers: - p.channel = l1.rpc.fundchannel(p.info['id'], 10**6)['channel_id'] + p.channel = l1.rpc.fundchannel(p.info['id'], 10**6, minconf=0)['channel_id'] # Technically, this is async to fundchannel returning. l1.daemon.wait_for_log('sendrawtx exit 0') @@ -1469,6 +1469,12 @@ def check_billboard(): # generated some more blocks. assert (closetxid, "confirmed") in set([(o['txid'], o['status']) for o in l1.rpc.listfunds()['outputs']]) + # Check that the all the addresses match what we generated ourselves: + for o in l1.rpc.listfunds()['outputs']: + txout = bitcoind.rpc.gettxout(o['txid'], o['output']) + addr = txout['scriptPubKey']['addresses'][0] + assert(addr == o['address']) + addr = l1.bitcoin.rpc.getnewaddress() l1.rpc.withdraw(addr, "all") diff --git a/tests/test_connection.py b/tests/test_connection.py index 7b68334f8a5b..d1833e7dd34a 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -801,6 +801,7 @@ def test_channel_persistence(node_factory, bitcoind, executor): l1.daemon.wait_for_log(' to ONCHAIN') +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") def test_private_channel(node_factory): l1, l2 = node_factory.line_graph(2, announce_channels=False, wait_for_announce=False) l3, l4 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True) @@ -1136,7 +1137,7 @@ def test_fundee_forget_funding_tx_unconfirmed(node_factory, bitcoind): time.sleep(1) def mock_sendrawtransaction(r): - return {'error': 'sendrawtransaction disabled'} + return {'id': r['id'], 'error': {'code': 100, 'message': 'sendrawtransaction disabled'}} # Prevent funder from broadcasting funding tx (any tx really). l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_sendrawtransaction) @@ -1144,7 +1145,8 @@ def mock_sendrawtransaction(r): # Fund the channel. # The process will complete, but funder will be unable # to broadcast and confirm funding tx. - l1.rpc.fundchannel(l2.info['id'], 10**6) + with pytest.raises(RpcError, match=r'sendrawtransaction disabled'): + l1.rpc.fundchannel(l2.info['id'], 10**6) # Generate blocks until unconfirmed. bitcoind.generate_block(blocks) @@ -1191,7 +1193,7 @@ def test_no_fee_estimate(node_factory, bitcoind, executor): # Can with manual feerate. l1.rpc.withdraw(l2.rpc.newaddr()['address'], 10000, '1500perkb') - l1.rpc.fundchannel(l2.info['id'], 10**6, '2000perkw') + l1.rpc.fundchannel(l2.info['id'], 10**6, '2000perkw', minconf=0) # Make sure we clean up cahnnel for later attempt. l1.daemon.wait_for_log('sendrawtx exit 0') @@ -1393,9 +1395,9 @@ def test_restart_multi_htlc_rexmit(node_factory, bitcoind, executor): del l1.daemon.opts['dev-disconnect'] l1.start() - # Payments will fail due to restart, but we can see results in listpayments. - print(l1.rpc.listpayments()) - wait_for(lambda: [p['status'] for p in l1.rpc.listpayments()['payments']] == ['complete', 'complete']) + # Payments will fail due to restart, but we can see results in listsendpays. + print(l1.rpc.listsendpays()) + wait_for(lambda: [p['status'] for p in l1.rpc.listsendpays()['payments']] == ['complete', 'complete']) @unittest.skipIf(not DEVELOPER, "needs dev-disconnect") @@ -1441,6 +1443,7 @@ def test_fulfill_incoming_first(node_factory, bitcoind): l3.daemon.wait_for_log('onchaind complete, forgetting peer') +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") def test_restart_many_payments(node_factory): l1 = node_factory.get_node(may_reconnect=True) @@ -1528,4 +1531,75 @@ def test_restart_many_payments(node_factory): # Wait for them to finish. for n in innodes: - wait_for(lambda: 'pending' not in [p['status'] for p in n.rpc.listpayments()['payments']]) + wait_for(lambda: 'pending' not in [p['status'] for p in n.rpc.listsendpays()['payments']]) + + +@unittest.skipIf(not DEVELOPER, "need dev-disconnect") +def test_fail_unconfirmed(node_factory, bitcoind, executor): + """Test that if we crash with an unconfirmed connection to a known + peer, we don't have a dangling peer in db""" + # = is a NOOP disconnect, but sets up file. + l1 = node_factory.get_node(disconnect=['=WIRE_OPEN_CHANNEL']) + l2 = node_factory.get_node() + + # First one, we close by mutual agreement. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fund_channel(l2, 200000, wait_for_active=True) + l1.rpc.close(l2.info['id']) + + # Make sure it's closed + l1.wait_for_channel_onchain(l2.info['id']) + bitcoind.generate_block(1) + l1.daemon.wait_for_log('State changed from CLOSINGD_COMPLETE to FUNDING_SPEND_SEEN') + + l1.stop() + # Mangle disconnect file so this time it blackholes.... + with open(l1.daemon.disconnect_file, "w") as f: + f.write("0WIRE_OPEN_CHANNEL\n") + l1.start() + + # Now we establish a new channel, which gets stuck. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fundwallet(10**7) + executor.submit(l1.rpc.fundchannel, l2.info['id'], 100000) + + l1.daemon.wait_for_log("dev_disconnect") + + # Now complete old channel. + bitcoind.generate_block(100) + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + + # And crash l1, which is stuck. + l1.daemon.kill() + + # Now, restart and see if it can connect OK. + del l1.daemon.opts['dev-disconnect'] + l1.start() + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fund_channel(l2, 200000, wait_for_active=True) + + +def test_change_chaining(node_factory, bitcoind): + """Test change chaining of unconfirmed fundings + + Change chaining is the case where one transaction is broadcast but not + confirmed yet and we already build a followup on top of the change. If the + first transaction doesn't confirm we may end up creating a series of + unconfirmable transactions. This is why we generally disallow chaining. + + """ + l1, l2, l3 = node_factory.get_nodes(3) + l1.fundwallet(10**8) # This will create an output with 1 confirmation + + # Now fund a channel from l1 to l2, that should succeed, with minconf=1 but not before + l1.connect(l2) + with pytest.raises(RpcError): + l1.rpc.fundchannel(l2.info['id'], 10**7, minconf=2) + l1.rpc.fundchannel(l2.info['id'], 10**7) # Defaults to minconf=1 + + # We don't have confirmed outputs anymore, so this should fail without minconf=0 + l1.connect(l3) + with pytest.raises(RpcError): + l1.rpc.fundchannel(l3.info['id'], 10**7) # Defaults to minconf=1 + l1.rpc.fundchannel(l3.info['id'], 10**7, minconf=0) diff --git a/tests/test_db.py b/tests/test_db.py new file mode 100644 index 000000000000..692581a734a9 --- /dev/null +++ b/tests/test_db.py @@ -0,0 +1,17 @@ +from fixtures import * # noqa: F401,F403 + + +def test_db_dangling_peer_fix(node_factory): + # This was taken from test_fail_unconfirmed() node. + l1 = node_factory.get_node(dbfile='dangling-peer.sqlite3.xz') + l2 = node_factory.get_node() + + # Must match entry in db + assert l2.info['id'] == '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59' + + # This time it should work! (Connect *in* since l1 thinks it has UTXOs + # it doesn't have). + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # Make sure l2 has register connection + l2.daemon.wait_for_log('Handed peer, entering loop') + l2.fund_channel(l1, 200000, wait_for_active=True) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 123be7f7eb16..cd123ab3de3a 100755 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -151,7 +151,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind): chan12 = l1.fund_channel(l2, 10**5) bitcoind.generate_block(5) - l3.wait_for_routes([chan12]) + l3.wait_for_channel_updates([chan12]) after_12 = int(time.time()) # Full IO logging for l1's channeld subprocess.run(['kill', '-USR1', l1.subd_pid('channeld')]) @@ -160,7 +160,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind): chan23 = l2.fund_channel(l3, 10**5) bitcoind.generate_block(5) - l1.wait_for_routes([chan23]) + l1.wait_for_channel_updates([chan23]) after_23 = int(time.time()) # Make sure l1 has received all the gossip. @@ -750,7 +750,7 @@ def fund_from_to_payer(lsrc, ldst, lpayer): lsrc.rpc.connect(ldst.info['id'], 'localhost', ldst.port) c = lsrc.fund_channel(ldst, 10000000) bitcoind.generate_block(5) - lpayer.wait_for_routes([c]) + lpayer.wait_for_channel_updates([c]) # Setup # Construct lightningd @@ -984,6 +984,7 @@ def test_gossip_notices_close(node_factory, bitcoind): assert(l1.rpc.listnodes()['nodes'] == []) +@unittest.skipIf(not DEVELOPER, "gossip propagation is slow without DEVELOPER=1") def test_getroute_exclude(node_factory, bitcoind): """Test getroute's exclude argument""" l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 4f91aa9de09a..40f76e44d8ab 100755 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -52,6 +52,11 @@ def test_invoice(node_factory): assert 'routes' not in b11 assert 'warning_capacity' in inv + # Make sure no wumbo invoices + with pytest.raises(RpcError, match=r'msatoshi cannot exceed 4294967295msat'): + l2.rpc.invoice(4294967295 + 1, 'inv3', '?') + l2.rpc.invoice(4294967295, 'inv3', '?') + def test_invoice_weirdstring(node_factory): l1 = node_factory.get_node() @@ -167,6 +172,7 @@ def test_invoice_routeboost(node_factory, bitcoind): assert 'warning_offline' not in inv +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") def test_invoice_routeboost_private(node_factory, bitcoind): """Test routeboost 'r' hint in bolt11 invoice for private channels """ diff --git a/tests/test_misc.py b/tests/test_misc.py index 9fe6635fe9c1..9e531f865782 100755 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -198,7 +198,7 @@ def test_htlc_sig_persistence(node_factory, bitcoind, executor): time.sleep(3) bitcoind.generate_block(1) l1.daemon.wait_for_logs([ - r'Owning output . (\d+) .SEGWIT. txid', + r'Owning output . (\d+)sat .SEGWIT. txid', ]) # We should now have a) the change from funding, b) the @@ -471,11 +471,11 @@ def test_withdraw(node_factory, bitcoind): assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 6 # Test withdrawal to self. - l1.rpc.withdraw(l1.rpc.newaddr('bech32')['address'], 'all') + l1.rpc.withdraw(l1.rpc.newaddr('bech32')['address'], 'all', minconf=0) bitcoind.generate_block(1) assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 1 - l1.rpc.withdraw(waddr, 'all') + l1.rpc.withdraw(waddr, 'all', minconf=0) assert l1.db_query('SELECT COUNT(*) as c FROM outputs WHERE status=0')[0]['c'] == 0 # This should fail, can't even afford fee. @@ -731,7 +731,7 @@ def test_cli(node_factory): '-J', 'help', 'command=help']).decode('utf-8') j, _ = json.JSONDecoder().raw_decode(out) - assert 'help [command]' in j['verbose'] + assert 'help [command]' in j['help'][0]['verbose'] # Test keyword input (forced) out = subprocess.check_output(['cli/lightning-cli', @@ -740,7 +740,7 @@ def test_cli(node_factory): '-J', '-k', 'help', 'command=help']).decode('utf-8') j, _ = json.JSONDecoder().raw_decode(out) - assert 'help [command]' in j['verbose'] + assert 'help [command]' in j['help'][0]['verbose'] # Test ordered input (autodetect) out = subprocess.check_output(['cli/lightning-cli', @@ -749,7 +749,7 @@ def test_cli(node_factory): '-J', 'help', 'help']).decode('utf-8') j, _ = json.JSONDecoder().raw_decode(out) - assert 'help [command]' in j['verbose'] + assert 'help [command]' in j['help'][0]['verbose'] # Test ordered input (forced) out = subprocess.check_output(['cli/lightning-cli', @@ -758,7 +758,7 @@ def test_cli(node_factory): '-J', '-o', 'help', 'help']).decode('utf-8') j, _ = json.JSONDecoder().raw_decode(out) - assert 'help [command]' in j['verbose'] + assert 'help [command]' in j['help'][0]['verbose'] # Test missing parameters. try: @@ -773,6 +773,31 @@ def test_cli(node_factory): pass +def test_daemon_option(node_factory): + """ + Make sure --daemon at least vaguely works! + """ + # Lazy way to set up command line and env, plus do VALGRIND checks + l1 = node_factory.get_node() + l1.stop() + + os.unlink(l1.rpc.socket_path) + subprocess.run(l1.daemon.cmd_line + ['--daemon', '--log-file={}/log-daemon'.format(l1.daemon.lightning_dir)], env=l1.daemon.env, + check=True) + + # Test some known output (wait for rpc to be ready) + wait_for(lambda: os.path.exists(l1.rpc.socket_path)) + out = subprocess.check_output(['cli/lightning-cli', + '--lightning-dir={}' + .format(l1.daemon.lightning_dir), + 'help']).decode('utf-8') + assert 'help [command]\n List available commands, or give verbose help on one {command}' in out + + subprocess.run(['cli/lightning-cli', + '--lightning-dir={}'.format(l1.daemon.lightning_dir), + 'stop'], check=True) + + @flaky @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_blockchaintrack(node_factory, bitcoind): diff --git a/tests/test_pay.py b/tests/test_pay.py old mode 100755 new mode 100644 index fa04375fc3ac..8ddafc2c17f3 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1,6 +1,6 @@ from fixtures import * # noqa: F401,F403 -from lightning import RpcError -from utils import DEVELOPER, wait_for, only_one, sync_blockheight +from lightning import RpcError, Millisatoshi +from utils import DEVELOPER, wait_for, only_one, sync_blockheight, SLOW_MACHINE import copy @@ -52,13 +52,29 @@ def test_pay(node_factory): l1.rpc.pay(inv2, random.randint(1000, 999999)) # Should see 6 completed payments - assert len(l1.rpc.listpayments()['payments']) == 6 + assert len(l1.rpc.listsendpays()['payments']) == 6 - # Test listpayments indexed by bolt11. - payments = l1.rpc.listpayments(inv)['payments'] + # Test listsendpays indexed by bolt11. + payments = l1.rpc.listsendpays(inv)['payments'] assert len(payments) == 1 and payments[0]['payment_preimage'] == preimage +def test_pay_amounts(node_factory): + l1, l2 = node_factory.line_graph(2) + inv = l2.rpc.invoice(Millisatoshi("123sat"), 'test_pay_amounts', 'description')['bolt11'] + + invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) + + assert isinstance(invoice['amount_msat'], Millisatoshi) + assert invoice['amount_msat'] == Millisatoshi(123000) + + l1.rpc.pay(inv) + + invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) + assert isinstance(invoice['amount_received_msat'], Millisatoshi) + assert invoice['amount_received_msat'] >= Millisatoshi(123000) + + def test_pay_limits(node_factory): """Test that we enforce fee max percentage and max delay""" l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) @@ -69,17 +85,21 @@ def test_pay_limits(node_factory): inv = l3.rpc.invoice("any", "any", 'description') # Fee too high. - with pytest.raises(RpcError, match=r'Route wanted fee of .* msatoshis') as err: + with pytest.raises(RpcError, match=r'Route wanted fee of .*msat') as err: l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'msatoshi': 100000, 'maxfeepercent': 0.0001, 'exemptfee': 0}) assert err.value.error['code'] == PAY_ROUTE_TOO_EXPENSIVE # It should have retried (once without routehint, too) status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][0]['attempts'] - assert len(status) == 3 + + # Hits weird corner case: it excludes channel, then uses routehint + # which reintroduces it, so then it excludes other channel. + assert len(status) == 4 assert status[0]['strategy'] == "Initial attempt" assert status[1]['strategy'].startswith("Excluded expensive channel ") - assert status[2]['strategy'] == "Removed route hint" + assert status[2]['strategy'] == "Trying route hint" + assert status[3]['strategy'].startswith("Excluded expensive channel ") # Delay too high. with pytest.raises(RpcError, match=r'Route wanted delay of .* blocks') as err: @@ -88,10 +108,11 @@ def test_pay_limits(node_factory): assert err.value.error['code'] == PAY_ROUTE_TOO_EXPENSIVE # Should also have retried. status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][1]['attempts'] - assert len(status) == 3 + assert len(status) == 4 assert status[0]['strategy'] == "Initial attempt" assert status[1]['strategy'].startswith("Excluded delaying channel ") - assert status[2]['strategy'] == "Removed route hint" + assert status[2]['strategy'] == "Trying route hint" + assert status[3]['strategy'].startswith("Excluded delaying channel ") # This works, because fee is less than exemptfee. l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'msatoshi': 100000, 'maxfeepercent': 0.0001, 'exemptfee': 2000}) @@ -199,24 +220,24 @@ def test_pay_optional_args(node_factory): l1, l2 = node_factory.line_graph(2) inv1 = l2.rpc.invoice(123000, 'test_pay', 'desc')['bolt11'] - l1.rpc.pay(inv1, description='desc') - payment1 = l1.rpc.listpayments(inv1)['payments'] + l1.rpc.pay(inv1, label='desc') + payment1 = l1.rpc.listsendpays(inv1)['payments'] assert len(payment1) and payment1[0]['msatoshi'] == 123000 - assert payment1[0]['description'] == 'desc' + assert payment1[0]['label'] == 'desc' inv2 = l2.rpc.invoice(321000, 'test_pay2', 'description')['bolt11'] l1.rpc.pay(inv2, riskfactor=5.0) - payment2 = l1.rpc.listpayments(inv2)['payments'] + payment2 = l1.rpc.listsendpays(inv2)['payments'] assert len(payment2) == 1 and payment2[0]['msatoshi'] == 321000 anyinv = l2.rpc.invoice('any', 'any_pay', 'desc')['bolt11'] - l1.rpc.pay(anyinv, description='desc', msatoshi='500') - payment3 = l1.rpc.listpayments(anyinv)['payments'] + l1.rpc.pay(anyinv, label='desc', msatoshi='500') + payment3 = l1.rpc.listsendpays(anyinv)['payments'] assert len(payment3) == 1 and payment3[0]['msatoshi'] == 500 - assert payment3[0]['description'] == 'desc' + assert payment3[0]['label'] == 'desc' # Should see 3 completed transactions - assert len(l1.rpc.listpayments()['payments']) == 3 + assert len(l1.rpc.listsendpays()['payments']) == 3 @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") @@ -249,9 +270,9 @@ def test_payment_success_persistence(node_factory, bitcoind, executor): # Should reconnect, and sort the payment out. l1.start() - wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending') + wait_for(lambda: l1.rpc.listsendpays()['payments'][0]['status'] != 'pending') - payments = l1.rpc.listpayments()['payments'] + payments = l1.rpc.listsendpays()['payments'] invoices = l2.rpc.listinvoices('inv1')['invoices'] assert len(payments) == 1 and payments[0]['status'] == 'complete' assert len(invoices) == 1 and invoices[0]['status'] == 'paid' @@ -299,9 +320,9 @@ def test_payment_failed_persistence(node_factory, executor): # Should reconnect, and fail the payment l1.start() - wait_for(lambda: l1.rpc.listpayments()['payments'][0]['status'] != 'pending') + wait_for(lambda: l1.rpc.listsendpays()['payments'][0]['status'] != 'pending') - payments = l1.rpc.listpayments()['payments'] + payments = l1.rpc.listsendpays()['payments'] invoices = l2.rpc.listinvoices('inv1')['invoices'] assert len(invoices) == 1 and invoices[0]['status'] == 'expired' assert len(payments) == 1 and payments[0]['status'] == 'failed' @@ -329,8 +350,8 @@ def test_payment_duplicate_uncommitted(node_factory, executor): # Make sure that's started... l1.daemon.wait_for_log('dev_disconnect: =WIRE_UPDATE_ADD_HTLC-nocommit') - # We should see it in listpayments - payments = l1.rpc.listpayments()['payments'] + # We should see it in listsendpays + payments = l1.rpc.listsendpays()['payments'] assert len(payments) == 1 assert payments[0]['status'] == 'pending' and payments[0]['payment_hash'] == inv1['payment_hash'] @@ -452,19 +473,19 @@ def check_balances(): assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['msatoshi_received'] == amt * 2 - # Test listpayments - payments = l1.rpc.listpayments()['payments'] + # Test listsendpays + payments = l1.rpc.listsendpays()['payments'] assert len(payments) == 2 invoice2 = only_one(l2.rpc.listinvoices('testpayment2')['invoices']) - payments = l1.rpc.listpayments(payment_hash=invoice2['payment_hash'])['payments'] + payments = l1.rpc.listsendpays(payment_hash=invoice2['payment_hash'])['payments'] assert len(payments) == 1 assert payments[0]['status'] == 'complete' assert payments[0]['payment_preimage'] == preimage2 invoice3 = only_one(l2.rpc.listinvoices('testpayment3')['invoices']) - payments = l1.rpc.listpayments(payment_hash=invoice3['payment_hash'])['payments'] + payments = l1.rpc.listsendpays(payment_hash=invoice3['payment_hash'])['payments'] assert len(payments) == 1 assert payments[0]['status'] == 'complete' @@ -749,9 +770,9 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind): # We add one to the blockcount for a bit of fuzz (FIXME: Shadowroute would fix this!) shadow_route = 1 - l1.daemon.wait_for_log("Adding HTLC 0 msat=5010198 cltv={} gave CHANNEL_ERR_ADD_OK" + l1.daemon.wait_for_log("Adding HTLC 0 amount=5010198msat cltv={} gave CHANNEL_ERR_ADD_OK" .format(bitcoind.rpc.getblockcount() + 20 + 9 + shadow_route)) - l2.daemon.wait_for_log("Adding HTLC 0 msat=4999999 cltv={} gave CHANNEL_ERR_ADD_OK" + l2.daemon.wait_for_log("Adding HTLC 0 amount=4999999msat cltv={} gave CHANNEL_ERR_ADD_OK" .format(bitcoind.rpc.getblockcount() + 9 + shadow_route)) l3.daemon.wait_for_log("test_forward_different_fees_and_cltv: Actual amount 4999999msat, HTLC expiry {}" .format(bitcoind.rpc.getblockcount() + 9 + shadow_route)) @@ -807,8 +828,10 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): # Modify so we overpay, overdo the cltv. route[0]['msatoshi'] += 2000 + route[0]['amount_msat'] = Millisatoshi(route[0]['msatoshi']) route[0]['delay'] += 20 route[1]['msatoshi'] += 1000 + route[1]['amount_msat'] = Millisatoshi(route[1]['msatoshi']) route[1]['delay'] += 10 # This should work. @@ -893,7 +916,7 @@ def test_forward_stats(node_factory, bitcoind): assert l3.rpc.getinfo()['msatoshi_fees_collected'] == 0 -@unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1 for dev_ignore_htlcs") +@unittest.skipIf(not DEVELOPER or SLOW_MACHINE, "needs DEVELOPER=1 for dev_ignore_htlcs, and temporarily disabled on Travis") def test_htlcs_cltv_only_difference(node_factory, bitcoind): # l1 -> l2 -> l3 -> l4 # l4 ignores htlcs, so they stay. @@ -936,9 +959,9 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): l4.restart() l3.rpc.connect(l4.info['id'], 'localhost', l4.port) - wait_for(lambda: only_one(l1.rpc.listpayments()['payments'])['status'] == 'failed') - wait_for(lambda: only_one(l2.rpc.listpayments()['payments'])['status'] == 'failed') - wait_for(lambda: only_one(l3.rpc.listpayments()['payments'])['status'] == 'failed') + wait_for(lambda: only_one(l1.rpc.listsendpays()['payments'])['status'] == 'failed') + wait_for(lambda: only_one(l2.rpc.listsendpays()['payments'])['status'] == 'failed') + wait_for(lambda: only_one(l3.rpc.listsendpays()['payments'])['status'] == 'failed') # Should all still be connected. assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] @@ -965,6 +988,7 @@ def test_pay_variants(node_factory): l1.rpc.pay(b11) +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") def test_pay_retry(node_factory, bitcoind): """Make sure pay command retries properly. """ def exhaust_channel(funder, fundee, scid, already_spent=0): @@ -1058,21 +1082,32 @@ def test_pay_routeboost(node_factory, bitcoind): status = l1.rpc.call('paystatus', [inv['bolt11']]) assert only_one(status['pay'])['bolt11'] == inv['bolt11'] assert only_one(status['pay'])['msatoshi'] == 10**5 + assert only_one(status['pay'])['amount_msat'] == Millisatoshi(10**5) assert only_one(status['pay'])['destination'] == l4.info['id'] - assert 'description' not in only_one(status['pay']) + assert 'label' not in only_one(status['pay']) assert 'routehint_modifications' not in only_one(status['pay']) assert 'local_exclusions' not in only_one(status['pay']) - attempt = only_one(only_one(status['pay'])['attempts']) - assert attempt['age_in_seconds'] <= time.time() - start - assert attempt['duration_in_seconds'] <= end - start - assert only_one(attempt['routehint']) - assert only_one(attempt['routehint'])['id'] == l3.info['id'] - assert only_one(attempt['routehint'])['msatoshi'] == 10**5 + 1 + 10**5 // 100000 - assert only_one(attempt['routehint'])['delay'] == 5 + 6 + # First attempt will fail, then it will try route hint + attempts = only_one(status['pay'])['attempts'] + assert len(attempts) == 2 + assert attempts[0]['strategy'] == "Initial attempt" + # FIXME! + PAY_ROUTE_NOT_FOUND = 205 + assert attempts[0]['failure']['code'] == PAY_ROUTE_NOT_FOUND + assert attempts[1]['strategy'] == "Trying route hint" + assert 'success' in attempts[1] + assert attempts[1]['age_in_seconds'] <= time.time() - start + assert attempts[1]['duration_in_seconds'] <= end - start + assert only_one(attempts[1]['routehint']) + assert only_one(attempts[1]['routehint'])['id'] == l3.info['id'] + scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['short_channel_id'] + assert only_one(attempts[1]['routehint'])['channel'] == scid34 + assert only_one(attempts[1]['routehint'])['fee_base_msat'] == 1 + assert only_one(attempts[1]['routehint'])['fee_proportional_millionths'] == 10 + assert only_one(attempts[1]['routehint'])['cltv_expiry_delta'] == 6 # With dev-route option we can test longer routehints. if DEVELOPER: - scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['short_channel_id'] scid45 = only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'][0]['short_channel_id'] routel3l4l5 = [{'id': l3.info['id'], 'short_channel_id': scid34, @@ -1090,43 +1125,11 @@ def test_pay_routeboost(node_factory, bitcoind): 'dev-routes': [routel3l4l5]}) l1.rpc.pay(inv['bolt11']) status = l1.rpc.call('paystatus', [inv['bolt11']]) - assert len(only_one(status['pay'])['attempts']) == 1 - assert 'failure' not in only_one(status['pay'])['attempts'][0] - assert 'success' in only_one(status['pay'])['attempts'][0] - - # Now test that it falls back correctly to not using routeboost - # if it can't route to the node mentioned - routel4l3 = [{'id': l4.info['id'], - 'short_channel_id': scid34, - 'fee_base_msat': 1000, - 'fee_proportional_millionths': 10, - 'cltv_expiry_delta': 6}] - inv = l3.rpc.call('invoice', {'msatoshi': 10**5, - 'label': 'test_pay_routeboost3', - 'description': 'test_pay_routeboost3', - 'dev-routes': [routel4l3]}) - l1.rpc.pay(inv['bolt11']) - status = l1.rpc.call('paystatus', [inv['bolt11']]) assert len(only_one(status['pay'])['attempts']) == 2 assert 'failure' in only_one(status['pay'])['attempts'][0] assert 'success' not in only_one(status['pay'])['attempts'][0] - routehint = only_one(status['pay'])['attempts'][0]['routehint'] - assert [h['channel'] for h in routehint] == [r['short_channel_id'] for r in routel4l3] assert 'failure' not in only_one(status['pay'])['attempts'][1] assert 'success' in only_one(status['pay'])['attempts'][1] - assert 'routehint' not in only_one(status['pay'])['attempts'][1] - - # Similarly if it can route, but payment fails. - routel2bad = [{'id': l2.info['id'], - 'short_channel_id': scid34, # Invalid scid - 'fee_base_msat': 1000, - 'fee_proportional_millionths': 10, - 'cltv_expiry_delta': 6}] - inv = l3.rpc.call('invoice', {'msatoshi': 10**5, - 'label': 'test_pay_routeboost4', - 'description': 'test_pay_routeboost4', - 'dev-routes': [routel2bad]}) - l1.rpc.pay(inv['bolt11']) # Finally, it should fall back to second routehint if first fails. # (Note, this is not public because it's not 6 deep) @@ -1142,21 +1145,79 @@ def test_pay_routeboost(node_factory, bitcoind): 'label': 'test_pay_routeboost5', 'description': 'test_pay_routeboost5', 'dev-routes': [routel3l4l5, routel3l5]}) - l1.rpc.pay(inv['bolt11'], description="paying test_pay_routeboost5") + l1.rpc.pay(inv['bolt11'], label="paying test_pay_routeboost5") status = l1.rpc.call('paystatus', [inv['bolt11']]) assert only_one(status['pay'])['bolt11'] == inv['bolt11'] assert only_one(status['pay'])['msatoshi'] == 10**5 assert only_one(status['pay'])['destination'] == l5.info['id'] - assert only_one(status['pay'])['description'] == "paying test_pay_routeboost5" + assert only_one(status['pay'])['label'] == "paying test_pay_routeboost5" assert 'routehint_modifications' not in only_one(status['pay']) assert 'local_exclusions' not in only_one(status['pay']) attempts = only_one(status['pay'])['attempts'] - # First failed, second succeeded. - assert len(attempts) == 2 + # First two failed (w/o routehint and w bad hint), third succeeded. + assert len(attempts) == 3 assert 'success' not in attempts[0] - assert 'success' in attempts[1] + assert 'success' not in attempts[1] + assert 'success' in attempts[2] + + assert [h['channel'] for h in attempts[1]['routehint']] == [r['short_channel_id'] for r in routel3l4l5] + assert [h['channel'] for h in attempts[2]['routehint']] == [r['short_channel_id'] for r in routel3l5] + + +@unittest.skipIf(not DEVELOPER, "gossip without DEVELOPER=1 is slow") +def test_pay_direct(node_factory, bitcoind): + """Check that we prefer the direct route. + """ + # l2->l3 is really cheap by comparison. + l0, l1, l2, l3 = node_factory.get_nodes(4, opts=[{'fee-base': 1000, + 'cltv-delta': 14}, + {'fee-base': 1000, + 'cltv-delta': 14}, + {'fee-base': 0, + 'cltv-delta': 14}, + {'fee-base': 1000, + 'cltv-delta': 14}]) + + # Direct channel l0->l1->l3 + l0.rpc.connect(l1.info['id'], 'localhost', l1.port) + # Waiting takes a *long* time if !DEVELOPER. + c0 = l0.fund_channel(l1, 10**7, wait_for_active=False) + + l1.rpc.connect(l3.info['id'], 'localhost', l3.port) + c1 = l1.fund_channel(l3, 10**7, wait_for_active=False) + + # Indirect route l0->l1->l2->l3 + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + c2 = l1.fund_channel(l2, 10**7, wait_for_active=False) + + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + c3 = l2.fund_channel(l3, 10**7, wait_for_active=False) + + # Let channels lock in. + bitcoind.generate_block(5) + + # Make l1 sees it, so it doesn't produce bad CLTVs. + sync_blockheight(bitcoind, [l1]) + + # Make sure l0 knows the l2->l3 channel. + # Without DEVELOPER, channel lockin can take 30 seconds to detect, + # and gossip 2 minutes to propagate + l0.wait_for_channel_updates([c0, c1, c2, c3]) + + # Find out how much msatoshi l1 owns on l1->l2 channel. + l1l2msatreference = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['msatoshi_to_us'] + + # Try multiple times to ensure that route randomization + # will not override our preference for direct route. + for i in range(8): + inv = l3.rpc.invoice(20000000, 'pay{}'.format(i), 'desc')['bolt11'] + + l0.rpc.pay(inv) - assert [h['channel'] for h in attempts[0]['routehint']] == [r['short_channel_id'] for r in routel3l4l5] - assert [h['channel'] for h in attempts[1]['routehint']] == [r['short_channel_id'] for r in routel3l5] + # We should have gone the direct route, so + # l1->l2 channel msatoshi_to_us should not + # have changed. + l1l2msat = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['msatoshi_to_us'] + assert l1l2msat == l1l2msatreference diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3e19fcfb4a7b..5a5331dbf58d 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1,9 +1,11 @@ from collections import OrderedDict from fixtures import * # noqa: F401,F403 -from lightning import RpcError +from lightning import RpcError, Millisatoshi +from utils import only_one import pytest import subprocess +import time def test_option_passthrough(node_factory): @@ -32,6 +34,23 @@ def test_option_passthrough(node_factory): n.stop() +def test_millisatoshi_passthrough(node_factory): + """ Ensure that Millisatoshi arguments and return work. + """ + plugin_path = 'tests/plugins/millisatoshis.py' + n = node_factory.get_node(options={'plugin': plugin_path, 'log-level': 'io'}) + + # By keyword + ret = n.rpc.call('echo', {'msat': Millisatoshi(17), 'not_an_msat': '22msat'})['echo_msat'] + assert type(ret) == Millisatoshi + assert ret == Millisatoshi(17) + + # By position + ret = n.rpc.call('echo', [Millisatoshi(18), '22msat'])['echo_msat'] + assert type(ret) == Millisatoshi + assert ret == Millisatoshi(18) + + def test_rpc_passthrough(node_factory): """Starting with a plugin exposes its RPC methods. @@ -47,6 +66,8 @@ def test_rpc_passthrough(node_factory): cmd = [hlp for hlp in n.rpc.help()['help'] if 'hello' in hlp['command']] assert(len(cmd) == 1) + # Make sure usage message is present. + assert only_one(n.rpc.help('hello')['help'])['command'] == 'hello [name]' # While we're at it, let's check that helloworld.py is logging # correctly via the notifications plugin->lightningd assert n.daemon.is_in_log('Plugin helloworld.py initialized') @@ -119,3 +140,51 @@ def test_pay_plugin(node_factory): with pytest.raises(RpcError, match=r'missing required parameter'): l1.rpc.call('pay') + + # Make sure usage messages are present. + assert only_one(l1.rpc.help('pay')['help'])['command'] == 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] [retry_for] [maxdelay] [exemptfee]' + assert only_one(l1.rpc.help('paystatus')['help'])['command'] == 'paystatus [bolt11]' + + +def test_plugin_connected_hook(node_factory): + """ l1 uses the reject plugin to reject connections. + + l1 is configured to accept connections from l2, but not from l3. + """ + opts = [{'plugin': 'tests/plugins/reject.py'}, {}, {}] + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + l1.rpc.reject(l3.info['id']) + + l2.connect(l1) + l1.daemon.wait_for_log(r"{} is allowed".format(l2.info['id'])) + assert len(l1.rpc.listpeers(l2.info['id'])['peers']) == 1 + + l3.connect(l1) + l1.daemon.wait_for_log(r"{} is in reject list".format(l3.info['id'])) + + peer = l1.rpc.listpeers(l3.info['id'])['peers'] + assert(peer == [] or not peer[0]['connected']) + + +def test_async_rpcmethod(node_factory, executor): + """This tests the async rpcmethods. + + It works in conjunction with the `asynctest` plugin which stashes + requests and then resolves all of them on the fifth call. + """ + l1 = node_factory.get_node(options={'plugin': 'tests/plugins/asynctest.py'}) + + results = [] + for i in range(10): + results.append(executor.submit(l1.rpc.asyncqueue)) + + time.sleep(3) + + # None of these should have returned yet + assert len([r for r in results if r.done()]) == 0 + + # This last one triggers the release and all results should be 42, + # since the last number is returned for all + l1.rpc.asyncflush(42) + + assert [r.result() for r in results] == [42] * len(results) diff --git a/tests/utils.py b/tests/utils.py index 1d2745188e9d..1ed67437ed3f 100755 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,6 +7,7 @@ import json import logging +import lzma import os import random import re @@ -298,6 +299,7 @@ def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None): btc_conf_file = os.path.join(bitcoin_dir, 'groestlcoin.conf') write_config(btc_conf_file, BITCOIND_CONFIG, BITCOIND_REGTEST) self.rpc = SimpleBitcoinProxy(btc_conf_file=btc_conf_file) + self.proxies = [] def start(self): TailableProc.start(self) @@ -305,13 +307,24 @@ def start(self): logging.info("GroestlcoinD started") + def stop(self): + for p in self.proxies: + p.stop() + self.rpc.stop() + return TailableProc.stop(self) + + def get_proxy(self): + proxy = BitcoinRpcProxy(self) + self.proxies.append(proxy) + return proxy + def generate_block(self, numblocks=1): # As of 0.16, generate() is removed; use generatetoaddress. return self.rpc.generatetoaddress(numblocks, self.rpc.getnewaddress()) class LightningD(TailableProc): - def __init__(self, lightning_dir, bitcoind, port=9735, random_hsm=False, node_id=0): + def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, node_id=0): TailableProc.__init__(self, lightning_dir) self.executable = 'lightningd/lightningd' self.lightning_dir = lightning_dir @@ -319,7 +332,7 @@ def __init__(self, lightning_dir, bitcoind, port=9735, random_hsm=False, node_id self.cmd_prefix = [] self.disconnect_file = None - self.rpcproxy = BitcoinRpcProxy(bitcoind) + self.rpcproxy = bitcoindproxy self.opts = LIGHTNINGD_CONFIG.copy() opts = { @@ -384,7 +397,6 @@ def wait(self, timeout=10): not return before the timeout triggers. """ self.proc.wait(timeout) - self.rpcproxy.stop() return self.proc.returncode @@ -595,12 +607,25 @@ def wait_channel_active(self, chanid): # This waits until gossipd sees channel_update in both directions # (or for local channels, at least a local announcement) - def wait_for_routes(self, channel_ids): + def wait_for_channel_updates(self, scids): # Could happen in any order... self.daemon.wait_for_logs(['Received channel_update for channel {}/0'.format(c) - for c in channel_ids] + for c in scids] + ['Received channel_update for channel {}/1'.format(c) - for c in channel_ids]) + for c in scids]) + + def wait_for_route(self, destination, timeout=30): + """ Wait for a route to the destination to become available. + """ + start_time = time.time() + while time.time() < start_time + timeout: + try: + self.rpc.getroute(destination.info['id'], 1, 1) + return True + except Exception: + time.sleep(1) + if time.time() > start_time + timeout: + raise ValueError("Error waiting for a route to destination {}".format(destination)) def pay(self, dst, amt, label=None): if not label: @@ -729,7 +754,8 @@ def get_nodes(self, num_nodes, opts=None): def get_node(self, disconnect=None, options=None, may_fail=False, may_reconnect=False, random_hsm=False, - feerates=(15000, 7500, 3750), start=True, log_all_io=False): + feerates=(15000, 7500, 3750), start=True, log_all_io=False, + dbfile=None): with self.lock: node_id = self.next_id self.next_id += 1 @@ -743,7 +769,7 @@ def get_node(self, disconnect=None, options=None, may_fail=False, socket_path = os.path.join(lightning_dir, "lightning-rpc").format(node_id) daemon = LightningD( - lightning_dir, self.bitcoind, + lightning_dir, bitcoindproxy=self.bitcoind.get_proxy(), port=port, random_hsm=random_hsm, node_id=node_id ) # If we have a disconnect string, dump it to a file for daemon. @@ -788,6 +814,11 @@ def get_node(self, disconnect=None, options=None, may_fail=False, '--log-file={}/valgrind-errors.%p'.format(node.daemon.lightning_dir) ] + if dbfile: + out = open(os.path.join(node.daemon.lightning_dir, 'lightningd.sqlite3'), 'xb') + with lzma.open(os.path.join('tests/data', dbfile), 'rb') as f: + out.write(f.read()) + if start: try: node.start() diff --git a/tools/build-release.sh b/tools/build-release.sh index 4e29d35f3caa..23d902b53437 100644 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -9,23 +9,76 @@ if [ x"$1" = x"--inside-docker" ]; then ./configure make -j3 make install DESTDIR=/"$VER" - tar cvfz /release/clightning-"$VER".tar.gz -- * + cd /"$VER" && tar cvfz /release/clightning-"$VER".tar.gz -- * exit 0 fi -if [ "$(git status --porcelain -u no)" != "" ]; then +ALL_TARGETS="bin-Fedora-28-amd64 bin-Ubuntu-16.04-amd64 bin-Ubuntu-16.04-i386 zipfile sign" + +FORCE_VERSION= +FORCE_UNCLEAN=false + +for arg; do + case "$arg" in + --force-mtime=*) + FORCE_MTIME=${arg#*=} + ;; + --force-version=*) + FORCE_VERSION=${arg#*=} + ;; + --force-unclean) + FORCE_UNCLEAN=true + ;; + --help) + echo "Usage: [--force-version=] [--force-unclean] [--force-mtime=YYYY-MM-DD] [TARGETS]" + echo Known targets: "$ALL_TARGETS" + exit 0 + ;; + -*) + echo "Unknown arg $arg" >&2 + exit 1 + ;; + *) + break + ;; + esac + shift +done + +if [ "$#" = 0 ]; then + TARGETS=" $ALL_TARGETS " +else + TARGETS=" $* " +fi + +# `status --porcelain -u no` suppressed modified! Bug reported... +if [ "$(git diff --name-only)" != "" ] && ! $FORCE_UNCLEAN; then echo "Not a clean git directory" >&2 exit 1 fi VERSION=$(git tag --points-at HEAD) +VERSION=${VERSION:-$FORCE_VERSION} if [ "$VERSION" = "" ]; then echo "No tagged version at HEAD?" >&2 exit 1 fi +# Skip 'v' here in $VERSION +MTIME=${FORCE_MTIME:-$(sed -n "s/^## \\[${VERSION#v}\\] - \\([-0-9]*\\).*/\\1/p" < CHANGELOG.md)} +if [ -z "$MTIME" ]; then + echo "No date found for $VERSION in CHANGELOG.md" >&2 + exit 1 +fi + +# If it's a completely clean directory, we need submodules! +make submodcheck + +rm -rf release mkdir -p release -for platform in Fedora-28-amd64 Ubuntu-16.04-amd64 Ubuntu-16.04-i386; do +for target in $TARGETS; do + platform=${target#bin-} + [ "$platform" != "$target" ] || continue case $platform in Fedora-28-amd64) DOCKERFILE=contrib/Dockerfile.builder.fedora @@ -49,13 +102,26 @@ for platform in Fedora-28-amd64 Ubuntu-16.04-amd64 Ubuntu-16.04-i386; do docker run --rm=true -w /build $TAG rm -rf /"$VERSION-$platform" /build done -# git archive won't go into submodules :( -ln -sf .. "release/clightning-$VERSION" -FILES=$(git ls-files --recurse-submodules | sed "s,^,clightning-$VERSION/,") -# shellcheck disable=SC2086 -(cd release && zip "clightning-$VERSION.zip" $FILES) -rm "release/clightning-$VERSION" -exit 0 +if [ -z "${TARGETS##* zipfile *}" ]; then + mkdir "release/clightning-$VERSION" + # git archive won't go into submodules :(; We use tar to copy + git ls-files -z --recurse-submodules | tar --null --files-from=- -c -f - | (cd "release/clightning-$VERSION" && tar xf -) + # tar can set dates on files, but zip cares about dates in directories! + # We set to local time (not "$MTIME 00:00Z") because zip uses local time! + find "release/clightning-$VERSION" -print0 | xargs -0r touch --no-dereference --date="$MTIME" + # Seriously, we can have differing permissions, too. Normalize. + # Directories become drwxr-xr-x + find "release/clightning-$VERSION" -type d -print0 | xargs -0r chmod 755 + # Executables become -rwxr-xr-x + find "release/clightning-$VERSION" -type f -perm -100 -print0 | xargs -0r chmod 755 + # Non-executables become -rw-r--r-- + find "release/clightning-$VERSION" -type f ! -perm -100 -print0 | xargs -0r chmod 644 + # zip -r doesn't have a deterministic order, and git ls-files does. + LANG=C git ls-files --recurse-submodules | sed "s@^@clightning-$VERSION/@" | (cd release && zip -@ -X "clightning-$VERSION.zip") + rm -r "release/clightning-$VERSION" +fi -sha256sum release/clightning-"$VERSION"* > release/SHA256SUMS -gpg -sb --armor -o release/SHA256SUMS.asc-"$(gpgconf --list-options gpg | awk -F: '$1 == "default-key" {print $10}' | tr -d '"')" release/SHA256SUMS +if [ -z "${TARGETS##* sign *}" ]; then + sha256sum release/clightning-"$VERSION"* > release/SHA256SUMS + gpg -sb --armor -o release/SHA256SUMS.asc-"$(gpgconf --list-options gpg | awk -F: '$1 == "default-key" {print $10}' | tr -d '"')" release/SHA256SUMS +fi diff --git a/tools/check-setup_locale.sh b/tools/check-setup_locale.sh index c89a564dd9a7..39d87b33f9aa 100755 --- a/tools/check-setup_locale.sh +++ b/tools/check-setup_locale.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash EXIT_CODE=0 -for FILE in $(git grep -lE 'int main\(' | grep -vE '^ccan/'); do +for FILE in $(git grep -lE 'int main\(' | grep -vE '^ccan/' | grep '.c$'); do if ! grep -q 'setup_locale();' "${FILE}"; then echo "main(...) in ${FILE} does not call setup_locale() (see common/utils.h)" EXIT_CODE=1 diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 46a519e4d8bc..3d03bcce7aa3 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -22,6 +22,8 @@ 'struct bitcoin_blkid': 32, 'struct bitcoin_txid': 32, 'struct secret': 32, + 'struct amount_msat': 8, + 'struct amount_sat': 8, 'u64': 8, 'u32': 4, 'u16': 2, @@ -45,7 +47,7 @@ def __init__(self, name): self.name = name def is_assignable(self): - return self.name in ['u8', 'u16', 'u32', 'u64', 'bool'] or self.name.startswith('enum ') + return self.name in ['u8', 'u16', 'u32', 'u64', 'bool', 'struct amount_msat', 'struct amount_sat'] or self.name.startswith('enum ') # We only accelerate the u8 case: it's common and trivial. def has_array_helper(self): @@ -79,7 +81,9 @@ def _typesize(typename): ('channel_announcement', 'short_channel_id'): FieldType('struct short_channel_id'), ('channel_update', 'short_channel_id'): FieldType('struct short_channel_id'), ('revoke_and_ack', 'per_commitment_secret'): FieldType('struct secret'), - ('channel_reestablish_option_data_loss_protect', 'your_last_per_commitment_secret'): FieldType('struct secret') + ('channel_reestablish_option_data_loss_protect', 'your_last_per_commitment_secret'): FieldType('struct secret'), + ('channel_update', 'fee_base_msat'): FieldType('u32'), + ('final_incorrect_htlc_amount', 'incoming_htlc_amt'): FieldType('struct amount_msat'), } # Partial names that map to a datatype @@ -90,6 +94,8 @@ def _typesize(typename): 'chain_hash': FieldType('struct bitcoin_blkid'), 'funding_txid': FieldType('struct bitcoin_txid'), 'pad': FieldType('pad'), + 'msat': FieldType('struct amount_msat'), + 'satoshis': FieldType('struct amount_sat'), } # Size to typename match diff --git a/tools/mockup.sh b/tools/mockup.sh index 66192a59887e..87817667206a 100755 --- a/tools/mockup.sh +++ b/tools/mockup.sh @@ -33,5 +33,5 @@ for SYMBOL; do END=$(tail -n "+${LINE}" < "$FILE" | grep -n ';$'); NUM=${END%%:*} - tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NORETURN//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/\s*$//' + tail -n "+${LINE}" < "$FILE" | head -n "$NUM" | sed 's/^extern *//' | sed 's/PRINTF_FMT([^)]*)//' | sed 's/NON_NULL_ARGS([^)]*)//' | sed 's/NO_NULL_ARGS//g' | sed 's/NORETURN//g' | sed 's/LAST_ARG_NULL//g' | sed 's/WARN_UNUSED_RESULT//g' | sed 's/,/ UNNEEDED,/g' | sed 's/\([a-z0-9A-Z*_]* [a-z0-9A-Z*_]*\));/\1 UNNEEDED);/' | sed "s/;\$/$STUB/" | sed 's/\s*$//' done diff --git a/tools/repro-build.sh b/tools/repro-build.sh new file mode 100755 index 000000000000..37bb3b774331 --- /dev/null +++ b/tools/repro-build.sh @@ -0,0 +1,133 @@ +#! /bin/sh + +set -e + +LANG=C +LC_ALL=C +export LANG LC_ALL + +for arg; do + case "$arg" in + --force-mtime=*) + FORCE_MTIME=${arg#*=} + ;; + --help) + echo "Usage: [--force-mtime=YYYY-MM-DD]" + exit 0 + ;; + *) + echo "Unknown arg $arg" >&2 + exit 1 + ;; + esac + shift +done + +# Taken from https://unix.stackexchange.com/questions/6345/how-can-i-get-distribution-name-and-version-number-in-a-simple-shell-script +if [ -f /etc/os-release ]; then + # freedesktop.org and systemd + # shellcheck disable=SC1091 + . /etc/os-release + OS=$NAME + VER=$VERSION_ID +elif type lsb_release >/dev/null 2>&1; then + # linuxbase.org + OS=$(lsb_release -si) + VER=$(lsb_release -sr) +elif [ -f /etc/lsb-release ]; then + # For some versions of Debian/Ubuntu without lsb_release command + # shellcheck disable=SC1091 + . /etc/lsb-release + OS=$DISTRIB_ID + VER=$DISTRIB_RELEASE +elif [ -f /etc/debian_version ]; then + # Older Debian/Ubuntu/etc. + OS=Debian + VER=$(cat /etc/debian_version) +else + # Fall back to uname, e.g. "Linux ", also works for BSD, etc. + OS=$(uname -s) + VER=$(uname -r) +fi + +PLATFORM="$OS"-"$VER" +VERSION=$(git describe --always --dirty=-modded --abbrev=7 2>/dev/null || pwd | sed -n 's,.*/clightning-\(v[0-9.rc]*\)$,\1,p') + +# eg. ## [0.6.3] - 2019-01-09: "The Smallblock Conspiracy" +# Skip 'v' here in $VERSION +MTIME=${FORCE_MTIME:-$(sed -n "s/^## \\[${VERSION#v}\\] - \\([-0-9]*\\).*/\\1/p" < CHANGELOG.md)} +if [ -z "$MTIME" ]; then + echo "No date found for $VERSION in CHANGELOG.md" >&2 + exit 1 +fi + +case "$PLATFORM" in + Ubuntu-18.04) + # Use an ISO base of 5748706937539418ee5707bd538c4f5eabae485d17aa49fb13ce2c9b70532433 /home/rusty/Downloads/ubuntu-18.04.1-desktop-amd64.iso + # Check they've turned off updates and security updates + if grep ^deb /etc/apt/sources.list | grep -- '-\(updates\|security\)'; then + echo Please disable security and updates in /etc/apt/sources.list >&2 + exit 1 + fi + DOWNLOAD='sudo apt --no-install-recommends --reinstall -d install' + PKGS='autoconf automake libtool make gcc libgmp-dev libsqlite3-dev zlib1g-dev libsodium-dev libbase58-dev' + INST='sudo dpkg -i' + cat > /tmp/SHASUMS <&2 + exit 1 + ;; +esac + +# Download the packages +# shellcheck disable=SC2086 +$DOWNLOAD $PKGS + +# Make sure versions match, and exactly. +sha256sum -c /tmp/SHASUMS + +# Install them +# shellcheck disable=SC2046 +$INST $(cut -c66- < /tmp/SHASUMS) + +# Build ready for packaging. +# Once everyone has gcc8, we can use CC="gcc -ffile-prefix-map=$(pwd)=/home/clightning" +./configure --prefix=/usr CC="gcc -fdebug-prefix-map=$(pwd)=/home/clightning" +# libwally wants "python". Seems to work to force it here. +make PYTHON_VERSION=3 +make install DESTDIR=inst/ + +cd inst && tar --sort=name \ + --mtime="$MTIME 00:00Z" \ + --owner=0 --group=0 --numeric-owner -cvaf ../clightning-"$VERSION-$PLATFORM".tar.xz . diff --git a/wallet/db.c b/wallet/db.c index a57f4df41c69..b9addde3ea01 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -334,6 +334,7 @@ char *dbmigrations[] = { "DELETE FROM blocks WHERE height IS NULL;", /* -- End of PR #1398 -- */ "ALTER TABLE invoices ADD description TEXT;", + /* FIXME: payments table 'description' is really a 'label' */ "ALTER TABLE payments ADD description TEXT;", /* future_per_commitment_point if other side proves we're out of date -- */ "ALTER TABLE channels ADD future_per_commitment_point BLOB;", @@ -356,6 +357,11 @@ char *dbmigrations[] = { ");", /* Add a direction for failed payments. */ "ALTER TABLE payments ADD faildirection INTEGER;", /* erring_direction */ + /* Fix dangling peers with no channels. */ + "DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);", + "ALTER TABLE outputs ADD scriptpubkey BLOB;", + /* Keep bolt11 string for payments. */ + "ALTER TABLE payments ADD bolt11 TEXT;", NULL, }; @@ -557,7 +563,7 @@ static struct db *db_open(const tal_t *ctx, char *filename) } db = tal(ctx, struct db); - db->filename = tal_dup_arr(db, char, filename, strlen(filename), 0); + db->filename = tal_strdup(db, filename); db->sql = sql; tal_add_destructor(db, destroy_db); db->in_transaction = NULL; @@ -742,7 +748,7 @@ bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, { const char *source = sqlite3_column_blob(stmt, col); size_t sourcelen = sqlite3_column_bytes(stmt, col); - return short_channel_id_from_str(source, sourcelen, dest); + return short_channel_id_from_str(source, sourcelen, dest, true); } bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, const struct short_channel_id *id) @@ -943,3 +949,31 @@ bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col, int err = sqlite3_bind_text(stmt, col, esc->s, strlen(esc->s), SQLITE_TRANSIENT); return err == SQLITE_OK; } + +struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col) +{ + struct amount_msat msat; + + msat.millisatoshis = sqlite3_column_int64(stmt, col); /* Raw: low level function */ + return msat; +} + +struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col) +{ + struct amount_sat sat; + + sat.satoshis = sqlite3_column_int64(stmt, col); /* Raw: low level function */ + return sat; +} + +void sqlite3_bind_amount_msat(sqlite3_stmt *stmt, int col, + struct amount_msat msat) +{ + sqlite3_bind_int64(stmt, col, msat.millisatoshis); /* Raw: low level function */ +} + +void sqlite3_bind_amount_sat(sqlite3_stmt *stmt, int col, + struct amount_sat sat) +{ + sqlite3_bind_int64(stmt, col, sat.satoshis); /* Raw: low level function */ +} diff --git a/wallet/db.h b/wallet/db.h index 2ec3b692e4f2..3883940668ec 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -170,4 +171,11 @@ struct json_escaped *sqlite3_column_json_escaped(const tal_t *ctx, sqlite3_stmt *stmt, int col); bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col, const struct json_escaped *esc); + +struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col); +struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col); +void sqlite3_bind_amount_msat(sqlite3_stmt *stmt, int col, + struct amount_msat msat); +void sqlite3_bind_amount_sat(sqlite3_stmt *stmt, int col, + struct amount_sat sat); #endif /* LIGHTNING_WALLET_DB_H */ diff --git a/wallet/invoices.c b/wallet/invoices.c index ba0f71ab49ba..c318c81410eb 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -102,17 +103,17 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, dtl->label = sqlite3_column_json_escaped(dtl, stmt, 3); if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) { - dtl->msatoshi = tal(dtl, u64); - *dtl->msatoshi = sqlite3_column_int64(stmt, 4); + dtl->msat = tal(dtl, struct amount_msat); + *dtl->msat = sqlite3_column_amount_msat(stmt, 4); } else { - dtl->msatoshi = NULL; + dtl->msat = NULL; } dtl->expiry_time = sqlite3_column_int64(stmt, 5); if (dtl->state == PAID) { dtl->pay_index = sqlite3_column_int64(stmt, 6); - dtl->msatoshi_received = sqlite3_column_int64(stmt, 7); + dtl->received = sqlite3_column_amount_msat(stmt, 7); dtl->paid_timestamp = sqlite3_column_int64(stmt, 8); } @@ -259,7 +260,7 @@ static void install_expiration_timer(struct invoices *invoices) bool invoices_create(struct invoices *invoices, struct invoice *pinvoice, - u64 *msatoshi TAKES, + const struct amount_msat *msat TAKES, const struct json_escaped *label TAKES, u64 expiry, const char *b11enc, @@ -273,8 +274,8 @@ bool invoices_create(struct invoices *invoices, u64 now = time_now().ts.tv_sec; if (invoices_find_by_label(invoices, &dummy, label)) { - if (taken(msatoshi)) - tal_free(msatoshi); + if (taken(msat)) + tal_free(msat); if (taken(label)) tal_free(label); return false; @@ -301,8 +302,8 @@ bool invoices_create(struct invoices *invoices, sqlite3_bind_blob(stmt, 1, rhash, sizeof(struct sha256), SQLITE_TRANSIENT); sqlite3_bind_blob(stmt, 2, r, sizeof(struct preimage), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 3, UNPAID); - if (msatoshi) - sqlite3_bind_int64(stmt, 4, *msatoshi); + if (msat) + sqlite3_bind_amount_msat(stmt, 4, *msat); else sqlite3_bind_null(stmt, 4); sqlite3_bind_json_escaped(stmt, 5, label); @@ -322,8 +323,8 @@ bool invoices_create(struct invoices *invoices, install_expiration_timer(invoices); } - if (taken(msatoshi)) - tal_free(msatoshi); + if (taken(msat)) + tal_free(msat); if (taken(label)) tal_free(label); return true; @@ -507,7 +508,7 @@ static s64 get_next_pay_index(struct db *db) void invoices_resolve(struct invoices *invoices, struct invoice invoice, - u64 msatoshi_received) + struct amount_msat received) { sqlite3_stmt *stmt; s64 pay_index; @@ -527,7 +528,7 @@ void invoices_resolve(struct invoices *invoices, " WHERE id=?;"); sqlite3_bind_int(stmt, 1, PAID); sqlite3_bind_int64(stmt, 2, pay_index); - sqlite3_bind_int64(stmt, 3, msatoshi_received); + sqlite3_bind_amount_msat(stmt, 3, received); sqlite3_bind_int64(stmt, 4, paid_timestamp); sqlite3_bind_int64(stmt, 5, invoice.id); db_exec_prepared(invoices->db, stmt); diff --git a/wallet/invoices.h b/wallet/invoices.h index de825b8ab529..41a250f261a0 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -6,6 +6,7 @@ #include #include +struct amount_msat; struct db; struct json_escaped; struct invoice; @@ -34,7 +35,7 @@ struct invoices *invoices_new(const tal_t *ctx, * * @invoices - the invoice handler. * @pinvoice - pointer to location to load new invoice in. - * @msatoshi - the amount the invoice should have, or + * @msat - the amount the invoice should have, or * NULL for any-amount invoices. * @label - the unique label for this invoice. Must be * non-NULL. @@ -47,7 +48,7 @@ struct invoices *invoices_new(const tal_t *ctx, */ bool invoices_create(struct invoices *invoices, struct invoice *pinvoice, - u64 *msatoshi TAKES, + const struct amount_msat *msat TAKES, const struct json_escaped *label TAKES, u64 expiry, const char *b11enc, @@ -173,14 +174,14 @@ const struct invoice_details *invoices_iterator_deref( * * @invoices - the invoice handler. * @invoice - the invoice to mark as paid. - * @msatoshi_received - the actual amount received. + * @received - the actual amount received. * * Precondition: the invoice must not yet be expired (invoices * does not check). */ void invoices_resolve(struct invoices *invoices, struct invoice invoice, - u64 msatoshi_received); + struct amount_msat received); /** * invoices_waitany - Wait for any invoice to be paid. diff --git a/wallet/test/Makefile b/wallet/test/Makefile index e33af34824bd..ba17897ec2cb 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -3,6 +3,7 @@ WALLET_TEST_OBJS := $(WALLET_TEST_SRC:.c=.o) WALLET_TEST_PROGRAMS := $(WALLET_TEST_OBJS:.o=) WALLET_TEST_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/derive_basepoints.o \ common/htlc_state.o \ diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 86ece1beea9f..605c0348c70b 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -12,6 +12,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const c #include "test_utils.h" +#include #include #include #include diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index b00f65f121cb..29ec197c13a0 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -19,6 +19,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const c #include #include +#include #include #include #include @@ -119,7 +120,7 @@ void invoices_autoclean_set(struct invoices *invoices UNNEEDED, /* Generated stub for invoices_create */ bool invoices_create(struct invoices *invoices UNNEEDED, struct invoice *pinvoice UNNEEDED, - u64 *msatoshi TAKES UNNEEDED, + const struct amount_msat *msat TAKES UNNEEDED, const struct json_escaped *label TAKES UNNEEDED, u64 expiry UNNEEDED, const char *b11enc UNNEEDED, @@ -173,7 +174,7 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED, /* Generated stub for invoices_resolve */ void invoices_resolve(struct invoices *invoices UNNEEDED, struct invoice invoice UNNEEDED, - u64 msatoshi_received UNNEEDED) + struct amount_msat received UNNEEDED) { fprintf(stderr, "invoices_resolve called!\n"); abort(); } /* Generated stub for invoices_waitany */ void invoices_waitany(const tal_t *ctx UNNEEDED, @@ -198,6 +199,20 @@ void json_add_address_internal(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "json_add_address_internal called!\n"); abort(); } +/* Generated stub for json_add_amount_msat */ +void json_add_amount_msat(struct json_stream *result UNNEEDED, + struct amount_msat msat UNNEEDED, + const char *rawfieldname UNNEEDED, + const char *msatfieldname) + +{ fprintf(stderr, "json_add_amount_msat called!\n"); abort(); } +/* Generated stub for json_add_amount_sat */ +void json_add_amount_sat(struct json_stream *result UNNEEDED, + struct amount_sat sat UNNEEDED, + const char *rawfieldname UNNEEDED, + const char *satfieldname) + +{ fprintf(stderr, "json_add_amount_sat called!\n"); abort(); } /* Generated stub for json_add_bool */ void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, bool value UNNEEDED) @@ -215,6 +230,10 @@ void json_add_hex_talarr(struct json_stream *result UNNEEDED, void json_add_log(struct json_stream *result UNNEEDED, const struct log_book *lr UNNEEDED, enum log_level minlevel UNNEEDED) { fprintf(stderr, "json_add_log called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } /* Generated stub for json_add_num */ void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, unsigned int value UNNEEDED) @@ -254,6 +273,10 @@ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNN struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED, const void *bytes UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "json_escaped_string_ called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } @@ -273,13 +296,17 @@ const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEED /* Generated stub for json_tok_full_len */ int json_tok_full_len(const jsmntok_t *t UNNEEDED) { fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } /* Generated stub for json_to_pubkey */ bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct pubkey *pubkey UNNEEDED) { fprintf(stderr, "json_to_pubkey called!\n"); abort(); } /* Generated stub for json_to_short_channel_id */ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) + struct short_channel_id *scid UNNEEDED, + bool may_be_deprecated_form UNNEEDED) { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } /* Generated stub for kill_uncommitted_channel */ void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, @@ -406,6 +433,10 @@ void peer_start_openingd(struct peer *peer UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } +/* Generated stub for plugin_hook_call_ */ +void plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, + void *payload UNNEEDED, void *cb_arg UNNEEDED) +{ fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } /* Generated stub for process_onionpacket */ struct route_step *process_onionpacket( const tal_t * ctx UNNEEDED, @@ -453,7 +484,7 @@ u8 *towire_channel_got_commitsig_reply(const tal_t *ctx UNNEEDED) u8 *towire_channel_got_revoke_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_got_revoke_reply called!\n"); abort(); } /* Generated stub for towire_channel_offer_htlc */ -u8 *towire_channel_offer_htlc(const tal_t *ctx UNNEEDED, u64 amount_msat UNNEEDED, u32 cltv_expiry UNNEEDED, const struct sha256 *payment_hash UNNEEDED, const u8 onion_routing_packet[1366]) +u8 *towire_channel_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amount_msat UNNEEDED, u32 cltv_expiry UNNEEDED, const struct sha256 *payment_hash UNNEEDED, const u8 onion_routing_packet[1366]) { fprintf(stderr, "towire_channel_offer_htlc called!\n"); abort(); } /* Generated stub for towire_channel_sending_commitsig_reply */ u8 *towire_channel_sending_commitsig_reply(const tal_t *ctx UNNEEDED) @@ -476,7 +507,7 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, u8 *towire_gossip_get_channel_peer(const tal_t *ctx UNNEEDED, const struct short_channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_gossip_get_channel_peer called!\n"); abort(); } /* Generated stub for towire_hsm_sign_commitment_tx */ -u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct pubkey *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, u64 funding_amount UNNEEDED) +u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct pubkey *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, struct amount_sat funding_amount UNNEEDED) { fprintf(stderr, "towire_hsm_sign_commitment_tx called!\n"); abort(); } /* Generated stub for towire_onchain_dev_memleak */ u8 *towire_onchain_dev_memleak(const tal_t *ctx UNNEEDED) @@ -652,12 +683,12 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx) struct wallet *w = create_test_wallet(ld, ctx); struct utxo u; struct pubkey pk; - u64 fee_estimate, change_satoshis; + struct amount_sat fee_estimate, change_satoshis; const struct utxo **utxos; CHECK(w); memset(&u, 0, sizeof(u)); - u.amount = 1; + u.amount = AMOUNT_SAT(1); pubkey_from_der(tal_hexdata(w, "02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66), 33, &pk); db_begin_transaction(w->db); @@ -680,7 +711,9 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx) "wallet_add_utxo with close_info"); /* Now select them */ - utxos = wallet_select_coins(w, w, 2, 0, 21, &fee_estimate, &change_satoshis); + utxos = wallet_select_coins(w, w, AMOUNT_SAT(2), 0, 21, + 0 /* no confirmations required */, + &fee_estimate, &change_satoshis); CHECK(utxos && tal_count(utxos) == 2); u = *utxos[1]; @@ -781,7 +814,7 @@ static bool channelseq(struct channel *c1, struct channel *c2) CHECK_MSG(pubkey_eq(&p1->id, &p2->id), "NodeIDs do not match"); CHECK((c1->scid == NULL && c2->scid == NULL) || short_channel_id_eq(c1->scid, c2->scid)); - CHECK(c1->our_msatoshi == c2->our_msatoshi); + CHECK(amount_msat_eq(c1->our_msat, c2->our_msat)); CHECK((c1->remote_shutdown_scriptpubkey == NULL && c2->remote_shutdown_scriptpubkey == NULL) || memeq( c1->remote_shutdown_scriptpubkey, tal_count(c1->remote_shutdown_scriptpubkey), @@ -958,10 +991,10 @@ static bool test_channel_config_crud(struct lightningd *ld, const tal_t *ctx) struct wallet *w = create_test_wallet(ld, ctx); CHECK(w); - cc1->dust_limit_satoshis = 1; - cc1->max_htlc_value_in_flight_msat = 2; - cc1->channel_reserve_satoshis = 3; - cc1->htlc_minimum_msat = 4; + cc1->dust_limit.satoshis = 1; + cc1->max_htlc_value_in_flight.millisatoshis = 2; + cc1->channel_reserve.satoshis = 3; + cc1->htlc_minimum.millisatoshis = 4; cc1->to_self_delay = 5; cc1->max_accepted_htlcs = 6; @@ -1000,12 +1033,12 @@ static bool test_htlc_crud(struct lightningd *ld, const tal_t *ctx) memset(&payment_key, 'B', sizeof(payment_key)); in.key.id = 42; in.key.channel = chan; - in.msatoshi = 42; + in.msat = AMOUNT_MSAT(42); out.in = ∈ out.key.id = 1337; out.key.channel = chan; - out.msatoshi = 41; + out.msat = AMOUNT_MSAT(41); /* Store the htlc_in */ CHECK_MSG(transaction_wrap(w->db, wallet_htlc_save_in(w, chan, &in)), @@ -1075,8 +1108,8 @@ static bool test_payment_crud(struct lightningd *ld, const tal_t *ctx) memset(&t->destination, 1, sizeof(t->destination)); t->id = 0; - t->msatoshi = 100; - t->msatoshi_sent = 101; + t->msatoshi = AMOUNT_MSAT(100); + t->msatoshi_sent = AMOUNT_MSAT(101); t->status = PAYMENT_PENDING; t->payment_preimage = NULL; memset(&t->payment_hash, 1, sizeof(t->payment_hash)); @@ -1088,8 +1121,8 @@ static bool test_payment_crud(struct lightningd *ld, const tal_t *ctx) CHECK(t2 != NULL); CHECK(t2->status == t->status); CHECK(pubkey_cmp(&t2->destination, &t->destination) == 0); - CHECK(t2->msatoshi == t->msatoshi); - CHECK(t2->msatoshi_sent == t->msatoshi_sent); + CHECK(amount_msat_eq(t2->msatoshi, t->msatoshi)); + CHECK(amount_msat_eq(t2->msatoshi_sent, t->msatoshi_sent)); CHECK(!t2->payment_preimage); t->status = PAYMENT_COMPLETE; @@ -1101,8 +1134,8 @@ static bool test_payment_crud(struct lightningd *ld, const tal_t *ctx) CHECK(t2 != NULL); CHECK(t2->status == t->status); CHECK(pubkey_cmp(&t2->destination, &t->destination) == 0); - CHECK(t2->msatoshi == t->msatoshi); - CHECK(t2->msatoshi_sent == t->msatoshi_sent); + CHECK(amount_msat_eq(t2->msatoshi, t->msatoshi)); + CHECK(amount_msat_eq(t2->msatoshi_sent, t->msatoshi_sent)); CHECK(preimage_eq(t->payment_preimage, t2->payment_preimage)); db_commit_transaction(w->db); diff --git a/wallet/wallet.c b/wallet/wallet.c index 37b2df6dc266..12481a5e2592 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -65,7 +65,7 @@ struct wallet *wallet_new(struct lightningd *ld, #define UTXO_FIELDS \ "prev_out_tx, prev_out_index, value, type, status, keyindex, " \ "channel_id, peer_id, commitment_point, confirmation_height, " \ - "spend_height" + "spend_height, scriptpubkey" /* We actually use the db constraints to uniquify, so OK if this fails. */ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, @@ -75,10 +75,10 @@ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, stmt = db_prepare(w->db, "INSERT INTO outputs (" UTXO_FIELDS - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); sqlite3_bind_blob(stmt, 1, &utxo->txid, sizeof(utxo->txid), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 2, utxo->outnum); - sqlite3_bind_int64(stmt, 3, utxo->amount); + sqlite3_bind_amount_sat(stmt, 3, utxo->amount); sqlite3_bind_int(stmt, 4, wallet_output_type_in_db(type)); sqlite3_bind_int(stmt, 5, output_state_available); sqlite3_bind_int(stmt, 6, utxo->keyindex); @@ -102,6 +102,13 @@ bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, else sqlite3_bind_null(stmt, 11); + if (utxo->scriptPubkey) + sqlite3_bind_blob(stmt, 12, utxo->scriptPubkey, + tal_bytelen(utxo->scriptPubkey), + SQLITE_TRANSIENT); + else + sqlite3_bind_null(stmt, 12); + /* May fail if we already know about the tx, e.g., because * it's change or some internal tx. */ return db_exec_prepared_mayfail(w->db, stmt); @@ -116,7 +123,7 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, sqlite3_stmt *stmt) u32 *blockheight, *spendheight; sqlite3_column_sha256_double(stmt, 0, &utxo->txid.shad); utxo->outnum = sqlite3_column_int(stmt, 1); - utxo->amount = sqlite3_column_int64(stmt, 2); + utxo->amount = sqlite3_column_amount_sat(stmt, 2); utxo->is_p2sh = sqlite3_column_int(stmt, 3) == p2sh_wpkh; utxo->status = sqlite3_column_int(stmt, 4); utxo->keyindex = sqlite3_column_int(stmt, 5); @@ -131,6 +138,7 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, sqlite3_stmt *stmt) utxo->blockheight = NULL; utxo->spendheight = NULL; + utxo->scriptPubkey = NULL; if (sqlite3_column_type(stmt, 9) != SQLITE_NULL) { blockheight = tal(utxo, u32); @@ -144,6 +152,12 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, sqlite3_stmt *stmt) utxo->spendheight = spendheight; } + if (sqlite3_column_type(stmt, 11) != SQLITE_NULL) { + utxo->scriptPubkey = + tal_dup_arr(utxo, u8, sqlite3_column_blob(stmt, 11), + sqlite3_column_bytes(stmt, 11), 0); + } + return utxo; } @@ -250,12 +264,13 @@ void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos) } static const struct utxo **wallet_select(const tal_t *ctx, struct wallet *w, - const u64 value, + struct amount_sat sat, const u32 feerate_per_kw, size_t outscriptlen, bool may_have_change, - u64 *satoshi_in, - u64 *fee_estimate) + u32 maxheight, + struct amount_sat *satoshi_in, + struct amount_sat *fee_estimate) { size_t i = 0; struct utxo **available; @@ -273,15 +288,23 @@ static const struct utxo **wallet_select(const tal_t *ctx, struct wallet *w, if (may_have_change) weight += (8 + 1 + BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN) * 4; - *fee_estimate = 0; - *satoshi_in = 0; + *fee_estimate = AMOUNT_SAT(0); + *satoshi_in = AMOUNT_SAT(0); available = wallet_get_utxos(ctx, w, output_state_available); for (i = 0; i < tal_count(available); i++) { size_t input_weight; + struct amount_sat needed; struct utxo *u = tal_steal(utxos, available[i]); + /* If we require confirmations check that we have a + * confirmation height and that it is below the required + * maxheight (current_height - minconf */ + if (maxheight != 0 && + (!u->blockheight || *u->blockheight > maxheight)) + continue; + tal_arr_expand(&utxos, u); if (!wallet_update_output_status( @@ -304,9 +327,22 @@ static const struct utxo **wallet_select(const tal_t *ctx, struct wallet *w, weight += input_weight; - *fee_estimate = weight * feerate_per_kw / 1000; - *satoshi_in += utxos[i]->amount; - if (*satoshi_in >= *fee_estimate + value) + if (!amount_sat_add(satoshi_in, *satoshi_in, utxos[i]->amount)) + fatal("Overflow in available satoshis %zu/%zu %s + %s", + i, tal_count(available), + type_to_string(tmpctx, struct amount_sat, + satoshi_in), + type_to_string(tmpctx, struct amount_sat, + &utxos[i]->amount)); + + *fee_estimate = amount_tx_fee(feerate_per_kw, weight); + if (!amount_sat_add(&needed, sat, *fee_estimate)) + fatal("Overflow in fee estimate %zu/%zu %s + %s", + i, tal_count(available), + type_to_string(tmpctx, struct amount_sat, &sat), + type_to_string(tmpctx, struct amount_sat, + fee_estimate)); + if (amount_sat_greater_eq(*satoshi_in, needed)) break; } tal_free(available); @@ -315,45 +351,47 @@ static const struct utxo **wallet_select(const tal_t *ctx, struct wallet *w, } const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, - const u64 value, + struct amount_sat sat, const u32 feerate_per_kw, size_t outscriptlen, - u64 *fee_estimate, u64 *changesatoshi) + u32 maxheight, + struct amount_sat *fee_estimate, + struct amount_sat *change) { - u64 satoshi_in; + struct amount_sat satoshi_in; const struct utxo **utxo; - utxo = wallet_select(ctx, w, value, feerate_per_kw, - outscriptlen, true, + utxo = wallet_select(ctx, w, sat, feerate_per_kw, + outscriptlen, true, maxheight, &satoshi_in, fee_estimate); /* Couldn't afford it? */ - if (satoshi_in < *fee_estimate + value) + if (!amount_sat_sub(change, satoshi_in, sat) + || !amount_sat_sub(change, *change, *fee_estimate)) return tal_free(utxo); - *changesatoshi = satoshi_in - value - *fee_estimate; return utxo; } const struct utxo **wallet_select_all(const tal_t *ctx, struct wallet *w, const u32 feerate_per_kw, size_t outscriptlen, - u64 *value, - u64 *fee_estimate) + u32 maxheight, + struct amount_sat *value, + struct amount_sat *fee_estimate) { - u64 satoshi_in; + struct amount_sat satoshi_in; const struct utxo **utxo; /* Huge value, but won't overflow on addition */ - utxo = wallet_select(ctx, w, (1ULL << 56), feerate_per_kw, - outscriptlen, false, + utxo = wallet_select(ctx, w, AMOUNT_SAT(1ULL << 56), feerate_per_kw, + outscriptlen, false, maxheight, &satoshi_in, fee_estimate); /* Can't afford fees? */ - if (*fee_estimate > satoshi_in) + if (!amount_sat_sub(value, satoshi_in, *fee_estimate)) return tal_free(utxo); - *value = satoshi_in - *fee_estimate; return utxo; } @@ -680,13 +718,13 @@ static struct channel *wallet_stmt2channel(const tal_t *ctx, struct wallet *w, s sqlite3_column_int64(stmt, 11), &funding_txid, sqlite3_column_int(stmt, 13), - sqlite3_column_int64(stmt, 14), - sqlite3_column_int64(stmt, 16), + sqlite3_column_amount_sat(stmt, 14), + sqlite3_column_amount_msat(stmt, 16), sqlite3_column_int(stmt, 15) != 0, scid, - sqlite3_column_int64(stmt, 17), - sqlite3_column_int64(stmt, 38), /* msatoshi_to_us_min */ - sqlite3_column_int64(stmt, 39), /* msatoshi_to_us_max */ + sqlite3_column_amount_msat(stmt, 17), + sqlite3_column_amount_msat(stmt, 38), /* msatoshi_to_us_min */ + sqlite3_column_amount_msat(stmt, 39), /* msatoshi_to_us_max */ sqlite3_column_tx(tmpctx, stmt, 32), &last_sig, wallet_htlc_sigs_load(tmpctx, w, @@ -761,7 +799,7 @@ void wallet_channel_stats_incr_x(struct wallet *w, char const *dir, char const *typ, u64 cdbid, - u64 msatoshi) + struct amount_msat msat) { char const *payments_stat = tal_fmt(tmpctx, "%s_payments_%s", dir, typ); @@ -773,24 +811,28 @@ void wallet_channel_stats_incr_x(struct wallet *w, " , %s = COALESCE(%s, 0) + %"PRIu64"" " WHERE id = %"PRIu64";", payments_stat, payments_stat, - msatoshi_stat, msatoshi_stat, msatoshi, + msatoshi_stat, msatoshi_stat, msat.millisatoshis, /* Raw: db access */ cdbid); sqlite3_stmt *stmt = db_prepare(w->db, qry); db_exec_prepared(w->db, stmt); } -void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 id, u64 m) +void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 id, + struct amount_msat m) { wallet_channel_stats_incr_x(w, "in", "offered", id, m); } -void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 id, u64 m) +void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 id, + struct amount_msat m) { wallet_channel_stats_incr_x(w, "in", "fulfilled", id, m); } -void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 id, u64 m) +void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 id, + struct amount_msat m) { wallet_channel_stats_incr_x(w, "out", "offered", id, m); } -void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 id, u64 m) +void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 id, + struct amount_msat m) { wallet_channel_stats_incr_x(w, "out", "fulfilled", id, m); } @@ -817,12 +859,12 @@ void wallet_channel_stats_load(struct wallet *w, stats->in_payments_offered = sqlite3_column_int64(stmt, 0); stats->in_payments_fulfilled = sqlite3_column_int64(stmt, 1); - stats->in_msatoshi_offered = sqlite3_column_int64(stmt, 2); - stats->in_msatoshi_fulfilled = sqlite3_column_int64(stmt, 3); + stats->in_msatoshi_offered = sqlite3_column_amount_msat(stmt, 2); + stats->in_msatoshi_fulfilled = sqlite3_column_amount_msat(stmt, 3); stats->out_payments_offered = sqlite3_column_int64(stmt, 4); stats->out_payments_fulfilled = sqlite3_column_int64(stmt, 5); - stats->out_msatoshi_offered = sqlite3_column_int64(stmt, 6); - stats->out_msatoshi_fulfilled = sqlite3_column_int64(stmt, 7); + stats->out_msatoshi_offered = sqlite3_column_amount_msat(stmt, 6); + stats->out_msatoshi_fulfilled = sqlite3_column_amount_msat(stmt, 7); db_stmt_done(stmt); } @@ -868,10 +910,10 @@ static void wallet_channel_config_save(struct wallet *w, " to_self_delay=?," " max_accepted_htlcs=?" " WHERE id=?;"); - sqlite3_bind_int64(stmt, 1, cc->dust_limit_satoshis); - sqlite3_bind_int64(stmt, 2, cc->max_htlc_value_in_flight_msat); - sqlite3_bind_int64(stmt, 3, cc->channel_reserve_satoshis); - sqlite3_bind_int64(stmt, 4, cc->htlc_minimum_msat); + sqlite3_bind_amount_sat(stmt, 1, cc->dust_limit); + sqlite3_bind_amount_msat(stmt, 2, cc->max_htlc_value_in_flight); + sqlite3_bind_amount_sat(stmt, 3, cc->channel_reserve); + sqlite3_bind_amount_msat(stmt, 4, cc->htlc_minimum); sqlite3_bind_int(stmt, 5, cc->to_self_delay); sqlite3_bind_int(stmt, 6, cc->max_accepted_htlcs); sqlite3_bind_int64(stmt, 7, cc->id); @@ -893,10 +935,10 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, return false; } cc->id = id; - cc->dust_limit_satoshis = sqlite3_column_int64(stmt, col++); - cc->max_htlc_value_in_flight_msat = sqlite3_column_int64(stmt, col++); - cc->channel_reserve_satoshis = sqlite3_column_int64(stmt, col++); - cc->htlc_minimum_msat = sqlite3_column_int64(stmt, col++); + cc->dust_limit = sqlite3_column_amount_sat(stmt, col++); + cc->max_htlc_value_in_flight = sqlite3_column_amount_msat(stmt, col++); + cc->channel_reserve = sqlite3_column_amount_sat(stmt, col++); + cc->htlc_minimum = sqlite3_column_amount_msat(stmt, col++); cc->to_self_delay = sqlite3_column_int(stmt, col++); cc->max_accepted_htlcs = sqlite3_column_int(stmt, col++); assert(col == 7); @@ -960,10 +1002,10 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) sqlite3_bind_sha256_double(stmt, 10, &chan->funding_txid.shad); sqlite3_bind_int(stmt, 11, chan->funding_outnum); - sqlite3_bind_int64(stmt, 12, chan->funding_satoshi); + sqlite3_bind_amount_sat(stmt, 12, chan->funding); sqlite3_bind_int(stmt, 13, chan->remote_funding_locked); - sqlite3_bind_int64(stmt, 14, chan->push_msat); - sqlite3_bind_int64(stmt, 15, chan->our_msatoshi); + sqlite3_bind_amount_msat(stmt, 14, chan->push); + sqlite3_bind_amount_msat(stmt, 15, chan->our_msat); if (chan->remote_shutdown_scriptpubkey) sqlite3_bind_blob(stmt, 16, chan->remote_shutdown_scriptpubkey, @@ -979,8 +1021,8 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) sqlite3_bind_int(stmt, 21, chan->last_was_revoke); sqlite3_bind_int(stmt, 22, chan->min_possible_feerate); sqlite3_bind_int(stmt, 23, chan->max_possible_feerate); - sqlite3_bind_int64(stmt, 24, chan->msatoshi_to_us_min); - sqlite3_bind_int64(stmt, 25, chan->msatoshi_to_us_max); + sqlite3_bind_amount_msat(stmt, 24, chan->msat_to_us_min); + sqlite3_bind_amount_msat(stmt, 25, chan->msat_to_us_max); sqlite3_bind_int64(stmt, 26, chan->dbid); db_exec_prepared(w->db, stmt); @@ -1108,9 +1150,12 @@ void wallet_confirm_tx(struct wallet *w, } int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, - const u32 *blockheight, u64 *total_satoshi) + const u32 *blockheight, + struct amount_sat *total) { int num_utxos = 0; + + *total = AMOUNT_SAT(0); for (size_t output = 0; output < tal_count(tx->output); output++) { struct utxo *utxo; u32 index; @@ -1129,14 +1174,17 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, utxo->outnum = output; utxo->close_info = NULL; - utxo->blockheight = blockheight?blockheight:NULL; + utxo->blockheight = blockheight ? blockheight : NULL; utxo->spendheight = NULL; + utxo->scriptPubkey = tx->output[output].script; - log_debug(w->log, "Owning output %zu %"PRIu64" (%s) txid %s", - output, tx->output[output].amount, + log_debug(w->log, "Owning output %zu %s (%s) txid %s%s", + output, + type_to_string(tmpctx, struct amount_sat, + &tx->output[output].amount), is_p2sh ? "P2SH" : "SEGWIT", type_to_string(tmpctx, struct bitcoin_txid, - &utxo->txid)); + &utxo->txid), blockheight ? " CONFIRMED" : ""); if (!wallet_add_utxo(w, utxo, is_p2sh ? p2sh_wpkh : our_change)) { /* In case we already know the output, make @@ -1151,7 +1199,12 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, } outpointfilter_add(w->owned_outpoints, &utxo->txid, utxo->outnum); - *total_satoshi += utxo->amount; + if (!amount_sat_add(total, *total, utxo->amount)) + fatal("Cannot add utxo output %zu/%zu %s + %s", + output, tal_count(tx->output), + type_to_string(tmpctx, struct amount_sat, total), + type_to_string(tmpctx, struct amount_sat, + &utxo->amount)); tal_free(utxo); num_utxos++; } @@ -1181,7 +1234,7 @@ void wallet_htlc_save_in(struct wallet *wallet, sqlite3_bind_int64(stmt, 1, chan->dbid); sqlite3_bind_int64(stmt, 2, in->key.id); sqlite3_bind_int(stmt, 3, DIRECTION_INCOMING); - sqlite3_bind_int64(stmt, 4, in->msatoshi); + sqlite3_bind_amount_msat(stmt, 4, in->msat); sqlite3_bind_int(stmt, 5, in->cltv_expiry); sqlite3_bind_sha256(stmt, 6, &in->payment_hash); @@ -1236,7 +1289,7 @@ void wallet_htlc_save_out(struct wallet *wallet, sqlite3_bind_int64(stmt, 4, out->in->dbid); else sqlite3_bind_null(stmt, 4); - sqlite3_bind_int64(stmt, 5, out->msatoshi); + sqlite3_bind_amount_msat(stmt, 5, out->msat); sqlite3_bind_int(stmt, 6, out->cltv_expiry); sqlite3_bind_sha256(stmt, 7, &out->payment_hash); @@ -1302,7 +1355,7 @@ static bool wallet_stmt2htlc_in(struct channel *channel, in->dbid = sqlite3_column_int64(stmt, 0); in->key.id = sqlite3_column_int64(stmt, 1); in->key.channel = channel; - in->msatoshi = sqlite3_column_int64(stmt, 2); + in->msat = sqlite3_column_amount_msat(stmt, 2); in->cltv_expiry = sqlite3_column_int(stmt, 3); in->hstate = sqlite3_column_int(stmt, 4); @@ -1345,7 +1398,7 @@ static bool wallet_stmt2htlc_out(struct channel *channel, out->dbid = sqlite3_column_int64(stmt, 0); out->key.id = sqlite3_column_int64(stmt, 1); out->key.channel = channel; - out->msatoshi = sqlite3_column_int64(stmt, 2); + out->msat = sqlite3_column_amount_msat(stmt, 2); out->cltv_expiry = sqlite3_column_int(stmt, 3); out->hstate = sqlite3_column_int(stmt, 4); sqlite3_column_sha256(stmt, 5, &out->payment_hash); @@ -1405,12 +1458,12 @@ static void fixup_hin(struct wallet *wallet, struct htlc_in *hin) hin->failcode = WIRE_TEMPORARY_NODE_FAILURE; log_broken(wallet->log, "HTLC #%"PRIu64" (%s) " - " for amount %"PRIu64 + " for amount %s" " from %s" " is missing a resolution:" " subsituting temporary node failure", hin->key.id, htlc_state_name(hin->hstate), - hin->msatoshi, + type_to_string(tmpctx, struct amount_msat, &hin->msat), type_to_string(tmpctx, struct pubkey, &hin->key.channel->peer->id)); #endif @@ -1473,7 +1526,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, bool wallet_invoice_create(struct wallet *wallet, struct invoice *pinvoice, - u64 *msatoshi TAKES, + const struct amount_msat *msat TAKES, const struct json_escaped *label TAKES, u64 expiry, const char *b11enc, @@ -1481,7 +1534,7 @@ bool wallet_invoice_create(struct wallet *wallet, const struct preimage *r, const struct sha256 *rhash) { - return invoices_create(wallet->invoices, pinvoice, msatoshi, label, expiry, b11enc, description, r, rhash); + return invoices_create(wallet->invoices, pinvoice, msat, label, expiry, b11enc, description, r, rhash); } bool wallet_invoice_find_by_label(struct wallet *wallet, struct invoice *pinvoice, @@ -1527,7 +1580,7 @@ wallet_invoice_iterator_deref(const tal_t *ctx, struct wallet *wallet, } void wallet_invoice_resolve(struct wallet *wallet, struct invoice invoice, - u64 msatoshi_received) + struct amount_msat msatoshi_received) { invoices_resolve(wallet->invoices, invoice, msatoshi_received); } @@ -1669,13 +1722,14 @@ void wallet_payment_store(struct wallet *wallet, " route_nodes," " route_channels," " msatoshi_sent," - " description" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + " description," + " bolt11" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); sqlite3_bind_int(stmt, 1, payment->status); sqlite3_bind_sha256(stmt, 2, &payment->payment_hash); sqlite3_bind_pubkey(stmt, 3, &payment->destination); - sqlite3_bind_int64(stmt, 4, payment->msatoshi); + sqlite3_bind_amount_msat(stmt, 4, payment->msatoshi); sqlite3_bind_int(stmt, 5, payment->timestamp); sqlite3_bind_blob(stmt, 6, payment->path_secrets, tal_bytelen(payment->path_secrets), @@ -1683,15 +1737,22 @@ void wallet_payment_store(struct wallet *wallet, sqlite3_bind_pubkey_array(stmt, 7, payment->route_nodes); sqlite3_bind_short_channel_id_array(stmt, 8, payment->route_channels); - sqlite3_bind_int64(stmt, 9, payment->msatoshi_sent); + sqlite3_bind_amount_msat(stmt, 9, payment->msatoshi_sent); - if (payment->description != NULL) - sqlite3_bind_text(stmt, 10, payment->description, - strlen(payment->description), + if (payment->label != NULL) + sqlite3_bind_text(stmt, 10, payment->label, + strlen(payment->label), SQLITE_TRANSIENT); else sqlite3_bind_null(stmt, 10); + if (payment->bolt11 != NULL) + sqlite3_bind_text(stmt, 11, payment->bolt11, + strlen(payment->bolt11), + SQLITE_TRANSIENT); + else + sqlite3_bind_null(stmt, 11); + db_exec_prepared(wallet->db, stmt); tal_free(payment); @@ -1726,7 +1787,7 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, payment->status = sqlite3_column_int(stmt, 1); sqlite3_column_pubkey(stmt, 2, &payment->destination); - payment->msatoshi = sqlite3_column_int64(stmt, 3); + payment->msatoshi = sqlite3_column_amount_msat(stmt, 3); sqlite3_column_sha256(stmt, 4, &payment->payment_hash); payment->timestamp = sqlite3_column_int(stmt, 5); @@ -1743,13 +1804,19 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, payment->route_channels = sqlite3_column_short_channel_id_array(payment, stmt, 9); - payment->msatoshi_sent = sqlite3_column_int64(stmt, 10); + payment->msatoshi_sent = sqlite3_column_amount_msat(stmt, 10); if (sqlite3_column_type(stmt, 11) != SQLITE_NULL) - payment->description = tal_strdup( + payment->label = tal_strdup( payment, (const char *)sqlite3_column_text(stmt, 11)); else - payment->description = NULL; + payment->label = NULL; + + if (sqlite3_column_type(stmt, 12) != SQLITE_NULL) + payment->bolt11 = tal_strdup(payment, + (const char *)sqlite3_column_text(stmt, 12)); + else + payment->bolt11 = NULL; return payment; } @@ -1758,7 +1825,7 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, #define PAYMENT_FIELDS \ "id, status, destination, msatoshi, payment_hash, timestamp, " \ "payment_preimage, path_secrets, route_nodes, route_channels, " \ - "msatoshi_sent, description " + "msatoshi_sent, description, bolt11 " struct wallet_payment * wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, @@ -2179,7 +2246,7 @@ wallet_outpoint_spend(struct wallet *w, const tal_t *ctx, const u32 blockheight, void wallet_utxoset_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 outnum, const u32 blockheight, const u32 txindex, const u8 *scriptpubkey, - const u64 satoshis) + struct amount_sat sat) { sqlite3_stmt *stmt; struct bitcoin_txid txid; @@ -2200,7 +2267,7 @@ void wallet_utxoset_add(struct wallet *w, const struct bitcoin_tx *tx, sqlite3_bind_null(stmt, 4); sqlite3_bind_int(stmt, 5, txindex); sqlite3_bind_blob(stmt, 6, scriptpubkey, tal_count(scriptpubkey), SQLITE_TRANSIENT); - sqlite3_bind_int64(stmt, 7, satoshis); + sqlite3_bind_amount_sat(stmt, 7, sat); db_exec_prepared(w->db, stmt); outpointfilter_add(w->utxoset_outpoints, &txid, outnum); @@ -2239,7 +2306,7 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, op->spendheight = sqlite3_column_int(stmt, 1); op->scriptpubkey = tal_arr(op, u8, sqlite3_column_bytes(stmt, 2)); memcpy(op->scriptpubkey, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2)); - op->satoshis = sqlite3_column_int64(stmt, 3); + op->sat = sqlite3_column_amount_sat(stmt, 3); db_stmt_done(stmt); return op; @@ -2449,16 +2516,16 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, sqlite3_bind_int64(stmt, 2, out->dbid); sqlite3_bind_int64(stmt, 3, in->key.channel->scid->u64); sqlite3_bind_int64(stmt, 4, out->key.channel->scid->u64); - sqlite3_bind_int64(stmt, 5, in->msatoshi); - sqlite3_bind_int64(stmt, 6, out->msatoshi); + sqlite3_bind_amount_msat(stmt, 5, in->msat); + sqlite3_bind_amount_msat(stmt, 6, out->msat); sqlite3_bind_int(stmt, 7, wallet_forward_status_in_db(state)); db_exec_prepared(w->db, stmt); } -u64 wallet_total_forward_fees(struct wallet *w) +struct amount_msat wallet_total_forward_fees(struct wallet *w) { sqlite3_stmt *stmt; - u64 total; + struct amount_msat total; int res; stmt = db_prepare(w->db, @@ -2472,7 +2539,7 @@ u64 wallet_total_forward_fees(struct wallet *w) res = sqlite3_step(stmt); assert(res == SQLITE_ROW); - total = sqlite3_column_int64(stmt, 0); + total = sqlite3_column_amount_msat(stmt, 0); db_stmt_done(stmt); return total; @@ -2499,9 +2566,16 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, tal_resize(&results, count+1); struct forwarding *cur = &results[count]; cur->status = sqlite3_column_int(stmt, 0); - cur->msatoshi_in = sqlite3_column_int64(stmt, 1); - cur->msatoshi_out = sqlite3_column_int64(stmt, 2); - cur->fee = cur->msatoshi_in - cur->msatoshi_out; + cur->msat_in = sqlite3_column_amount_msat(stmt, 1); + cur->msat_out = sqlite3_column_amount_msat(stmt, 2); + if (!amount_msat_sub(&cur->fee, cur->msat_in, cur->msat_out)) { + log_broken(w->log, "Forwarded in %s less than out %s!", + type_to_string(tmpctx, struct amount_msat, + &cur->msat_in), + type_to_string(tmpctx, struct amount_msat, + &cur->msat_out)); + cur->fee = AMOUNT_MSAT(0); + } if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) { cur->payment_hash = tal(ctx, struct sha256_double); diff --git a/wallet/wallet.h b/wallet/wallet.h index f5b749936a9e..671a185f25a2 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -19,6 +19,7 @@ #include enum onion_type; +struct amount_msat; struct invoices; struct channel; struct lightningd; @@ -161,7 +162,7 @@ static inline const char* forward_status_name(enum forward_status status) struct forwarding { struct short_channel_id channel_in, channel_out; - u64 msatoshi_in, msatoshi_out, fee; + struct amount_msat msat_in, msat_out, fee; struct sha256_double *payment_hash; enum forward_status status; }; @@ -215,17 +216,19 @@ struct wallet_payment { struct sha256 payment_hash; enum wallet_payment_status status; struct pubkey destination; - u64 msatoshi; - u64 msatoshi_sent; + struct amount_msat msatoshi; + struct amount_msat msatoshi_sent; /* If and only if PAYMENT_COMPLETE */ struct preimage *payment_preimage; /* Needed for recovering from routing failures. */ struct secret *path_secrets; struct pubkey *route_nodes; struct short_channel_id *route_channels; + /* bolt11 string; NULL for old payments. */ + const char *bolt11; - /* The description of the payment. Must support `tal_len` */ - const char *description; + /* The label of the payment. Must support `tal_len` */ + const char *label; }; struct outpoint { @@ -233,7 +236,7 @@ struct outpoint { u32 blockheight; u32 txindex; u32 outnum; - u64 satoshis; + struct amount_sat sat; u8 *scriptpubkey; u32 spendheight; }; @@ -241,9 +244,9 @@ struct outpoint { /* Statistics for a channel */ struct channel_stats { u64 in_payments_offered, in_payments_fulfilled; - u64 in_msatoshi_offered, in_msatoshi_fulfilled; + struct amount_msat in_msatoshi_offered, in_msatoshi_fulfilled; u64 out_payments_offered, out_payments_fulfilled; - u64 out_msatoshi_offered, out_msatoshi_fulfilled; + struct amount_msat out_msatoshi_offered, out_msatoshi_fulfilled; }; struct channeltx { @@ -315,17 +318,19 @@ struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, struct wallet *w); const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w, - const u64 value, + struct amount_sat value, const u32 feerate_per_kw, size_t outscriptlen, - u64 *fee_estimate, - u64 *change_satoshi); + u32 maxheight, + struct amount_sat *fee_estimate, + struct amount_sat *change_satoshi); const struct utxo **wallet_select_all(const tal_t *ctx, struct wallet *w, - const u32 feerate_per_kw, - size_t outscriptlen, - u64 *value, - u64 *fee_estimate); + const u32 feerate_per_kw, + size_t outscriptlen, + u32 maxheight, + struct amount_sat *sat, + struct amount_sat *fee_estimate); /** * wallet_confirm_utxos - Once we've spent a set of utxos, mark them confirmed. @@ -434,10 +439,10 @@ bool wallet_channels_load_active(const tal_t *ctx, struct wallet *w); * @cdbid: channel database id * @msatoshi: amount in msatoshi being transferred */ -void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 cdbid, u64 msatoshi); -void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 cdbid, u64 msatoshi); -void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 cdbid, u64 msatoshi); -void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 cdbid, u64 msatoshi); +void wallet_channel_stats_incr_in_offered(struct wallet *w, u64 cdbid, struct amount_msat msatoshi); +void wallet_channel_stats_incr_in_fulfilled(struct wallet *w, u64 cdbid, struct amount_msat msatoshi); +void wallet_channel_stats_incr_out_offered(struct wallet *w, u64 cdbid, struct amount_msat msatoshi); +void wallet_channel_stats_incr_out_fulfilled(struct wallet *w, u64 cdbid, struct amount_msat msatoshi); /** * wallet_channel_stats_load - Load channel statistics @@ -465,7 +470,8 @@ void wallet_blocks_heights(struct wallet *w, u32 def, u32 *min, u32 *max); * wallet_extract_owned_outputs - given a tx, extract all of our outputs */ int wallet_extract_owned_outputs(struct wallet *w, const struct bitcoin_tx *tx, - const u32 *blockheight, u64 *total_satoshi); + const u32 *blockheight, + struct amount_sat *total); /** * wallet_htlc_save_in - store an htlc_in in the database @@ -575,13 +581,13 @@ struct invoice_details { /* Label assigned by user */ const struct json_escaped *label; /* NULL if they specified "any" */ - u64 *msatoshi; + struct amount_msat *msat; /* Absolute UNIX epoch time this will expire */ u64 expiry_time; /* Set if state == PAID; order to be returned by waitanyinvoice */ u64 pay_index; /* Set if state == PAID; amount received */ - u64 msatoshi_received; + struct amount_msat received; /* Set if state == PAID; time paid */ u64 paid_timestamp; /* BOLT11 encoding for this invoice */ @@ -611,7 +617,7 @@ struct invoice { * * @wallet - the wallet to create the invoice in. * @pinvoice - pointer to location to load new invoice in. - * @msatoshi - the amount the invoice should have, or + * @msat - the amount the invoice should have, or * NULL for any-amount invoices. * @label - the unique label for this invoice. Must be * non-NULL. @@ -624,7 +630,7 @@ struct invoice { */ bool wallet_invoice_create(struct wallet *wallet, struct invoice *pinvoice, - u64 *msatoshi TAKES, + const struct amount_msat *msat TAKES, const struct json_escaped *label TAKES, u64 expiry, const char *b11enc, @@ -743,14 +749,14 @@ const struct invoice_details *wallet_invoice_iterator_deref(const tal_t *ctx, * * @wallet - the wallet containing the invoice. * @invoice - the invoice to mark as paid. - * @msatoshi_received - the actual amount received. + * @received - the actual amount received. * * Precondition: the invoice must not yet be expired (wallet * does not check!). */ void wallet_invoice_resolve(struct wallet *wallet, struct invoice invoice, - u64 msatoshi_received); + struct amount_msat received); /** * wallet_invoice_waitany - Wait for any invoice to be paid. @@ -974,7 +980,7 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, void wallet_utxoset_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 outnum, const u32 blockheight, const u32 txindex, const u8 *scriptpubkey, - const u64 satoshis); + struct amount_sat sat); void wallet_transaction_add(struct wallet *w, const struct bitcoin_tx *tx, const u32 blockheight, const u32 txindex); @@ -1028,7 +1034,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, /** * Retrieve summary of successful forwarded payments' fees */ -u64 wallet_total_forward_fees(struct wallet *w); +struct amount_msat wallet_total_forward_fees(struct wallet *w); /** * Retrieve a list of all forwarded_payments diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 2b3dc9745da2..87f0467ebc16 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -48,7 +48,7 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, { struct command *cmd = withdraw->cmd; struct lightningd *ld = withdraw->cmd->ld; - u64 change_satoshi = 0; + struct amount_sat change = AMOUNT_SAT(0); /* Massage output into shape so it doesn't kill the JSON serialization */ char *output = tal_strjoin(cmd, tal_strsplit(cmd, msg, "\n", STR_NO_EMPTY), " ", STR_NO_TRAIL); @@ -60,11 +60,13 @@ static void wallet_withdrawal_broadcast(struct bitcoind *bitcoind UNUSED, * generated the hex tx, so this should always work */ struct bitcoin_tx *tx = bitcoin_tx_from_hex(withdraw, withdraw->hextx, strlen(withdraw->hextx)); assert(tx != NULL); - wallet_extract_owned_outputs(ld->wallet, tx, NULL, &change_satoshi); + + /* Extract the change output and add it to the DB */ + wallet_extract_owned_outputs(ld->wallet, tx, NULL, &change); /* Note normally, change_satoshi == withdraw->wtx.change, but * not if we're actually making a payment to ourselves! */ - assert(change_satoshi >= withdraw->wtx.change); + assert(amount_sat_greater_eq(change, withdraw->wtx.change)); struct json_stream *response = json_stream_success(cmd); json_object_start(response, NULL); @@ -91,27 +93,27 @@ static struct command_result *json_withdraw(struct command *cmd, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - const jsmntok_t *desttok, *sattok; + const jsmntok_t *desttok; struct withdrawal *withdraw = tal(cmd, struct withdrawal); u32 *feerate_per_kw; struct bitcoin_tx *tx; + struct ext_key ext; + struct pubkey pubkey; enum address_parse_result addr_parse; struct command_result *res; + u32 *minconf, maxheight; withdraw->cmd = cmd; - wtx_init(cmd, &withdraw->wtx); + wtx_init(cmd, &withdraw->wtx, AMOUNT_SAT(-1ULL)); if (!param(cmd, buffer, params, p_req("destination", param_tok, &desttok), - p_req("satoshi", param_tok, &sattok), + p_req("satoshi", param_wtx, &withdraw->wtx), p_opt("feerate", param_feerate, &feerate_per_kw), + p_opt_def("minconf", param_number, &minconf, 1), NULL)) return command_param_failed(); - res = param_wtx(&withdraw->wtx, buffer, sattok, -1ULL); - if (res) - return res; - if (!feerate_per_kw) { res = param_feerate_estimate(cmd, &feerate_per_kw, FEERATE_NORMAL); @@ -138,11 +140,24 @@ static struct command_result *json_withdraw(struct command *cmd, get_chainparams(cmd->ld)->network_name); } + maxheight = minconf_to_maxheight(*minconf, cmd->ld); res = wtx_select_utxos(&withdraw->wtx, *feerate_per_kw, - tal_count(withdraw->destination)); + tal_count(withdraw->destination), maxheight); if (res) return res; + if (bip32_key_from_parent(cmd->ld->wallet->bip32_base, withdraw->wtx.change_key_index, + BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) { + return command_fail(cmd, LIGHTNINGD, "Keys generation failure"); + } + + if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &pubkey.pubkey, + ext.pub_key, sizeof(ext.pub_key))) { + return command_fail(cmd, LIGHTNINGD, "Key parsing failure"); + } + + txfilter_add_derkey(cmd->ld->owned_txfilter, ext.pub_key); + u8 *msg = towire_hsm_sign_withdrawal(cmd, withdraw->wtx.amount, withdraw->wtx.change, @@ -168,10 +183,14 @@ static struct command_result *json_withdraw(struct command *cmd, } static const struct json_command withdraw_command = { - "withdraw", - json_withdraw, - "Send to {destination} address {satoshi} (or 'all') amount via Groestlcoin transaction, at optional {feerate}", - false, "Send funds from the internal wallet to the specified address. Either specify a number of satoshis to send or 'all' to sweep all funds in the internal wallet to the address." + "withdraw", json_withdraw, + "Send to {destination} address {satoshi} (or 'all') amount via Groestlcoin " + "transaction, at optional {feerate}", + false, + "Send funds from the internal wallet to the specified address. Either " + "specify a number of satoshis to send or 'all' to sweep all funds in the " + "internal wallet to the address. Only use outputs that have at least " + "{minconf} confirmations." }; AUTODATA(json_command, &withdraw_command); @@ -227,6 +246,26 @@ encode_pubkey_to_addr(const tal_t *ctx, return out; } +/* Returns NULL if the script is not a P2WPKH */ +static char * +encode_scriptpubkey_to_addr(const tal_t *ctx, + const struct lightningd *ld, + const u8 *scriptPubkey) +{ + char *out; + const char *hrp; + size_t scriptLen = tal_bytelen(scriptPubkey); + bool ok; + if (scriptLen != 22 || scriptPubkey[0] != 0x00 || scriptPubkey[1] != 0x14) + return NULL; + hrp = get_chainparams(ld)->bip173_name; + out = tal_arr(ctx, char, 73 + strlen(hrp)); + ok = segwit_addr_encode(out, hrp, 0, scriptPubkey + 2, scriptLen - 2); + if (!ok) + return tal_free(out); + return out; +} + /* Extract a bool indicating "p2sh-segwit" or "bech32" */ static struct command_result *param_newaddr(struct command *cmd, const char *name, @@ -316,12 +355,15 @@ static struct command_result *json_listaddrs(struct command *cmd, u64 *bip32_max_index; if (!param(cmd, buffer, params, - p_opt_def("bip32_max_index", param_u64, &bip32_max_index, - db_get_intvar(cmd->ld->wallet->db, - "bip32_max_index", 0)), + p_opt("bip32_max_index", param_u64, &bip32_max_index), NULL)) return command_param_failed(); + if (!bip32_max_index) { + bip32_max_index = tal(cmd, u64); + *bip32_max_index = db_get_intvar(cmd->ld->wallet->db, + "bip32_max_index", 0); + } response = json_stream_success(cmd); json_object_start(response, NULL); json_array_start(response, "addresses"); @@ -407,7 +449,8 @@ static struct command_result *json_listfunds(struct command *cmd, json_object_start(response, NULL); json_add_txid(response, "txid", &utxos[i]->txid); json_add_num(response, "output", utxos[i]->outnum); - json_add_u64(response, "value", utxos[i]->amount); + json_add_amount_sat(response, utxos[i]->amount, + "value", "amount_msat"); /* @close_info is for outputs that are not yet claimable */ if (utxos[i]->close_info == NULL) { @@ -422,7 +465,13 @@ static struct command_result *json_listfunds(struct command *cmd, "p2wpkh address encoding failure."); } json_add_string(response, "address", out); + } else if (utxos[i]->scriptPubkey != NULL) { + out = encode_scriptpubkey_to_addr( + cmd, cmd->ld, utxos[i]->scriptPubkey); + if (out) + json_add_string(response, "address", out); } + if (utxos[i]->spendheight) json_add_string(response, "status", "spent"); else if (utxos[i]->blockheight) @@ -446,11 +495,11 @@ static struct command_result *json_listfunds(struct command *cmd, "short_channel_id", c->scid); - /* Poor man's rounding to satoshis to match the unit for outputs */ - json_add_u64(response, "channel_sat", - (c->our_msatoshi + 500)/1000); - json_add_u64(response, "channel_total_sat", - c->funding_satoshi); + json_add_amount_sat(response, + amount_msat_to_sat_round_down(c->our_msat), + "channel_sat", "our_amount_msat"); + json_add_amount_sat(response, c->funding, + "channel_total_sat", "amount_msat"); json_add_txid(response, "funding_txid", &c->funding_txid); json_object_end(response); diff --git a/wire/fromwire.c b/wire/fromwire.c index 28c6ac70a433..4490fe11df38 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -241,8 +242,6 @@ char *fromwire_wirestring(const tal_t *ctx, const u8 **cursor, size_t *max) return NULL; } -REGISTER_TYPE_TO_STRING(short_channel_id, short_channel_id_to_str); -REGISTER_TYPE_TO_STRING(short_channel_id_dir, short_channel_id_dir_to_str); REGISTER_TYPE_TO_HEXSTR(channel_id); /* BOLT #2: @@ -253,7 +252,7 @@ REGISTER_TYPE_TO_HEXSTR(channel_id); * (i.e. `funding_output_index` alters the last 2 bytes). */ void derive_channel_id(struct channel_id *channel_id, - struct bitcoin_txid *txid, u16 txout) + const struct bitcoin_txid *txid, u16 txout) { BUILD_ASSERT(sizeof(*channel_id) == sizeof(*txid)); memcpy(channel_id, txid, sizeof(*channel_id)); @@ -272,3 +271,20 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max, { fromwire(cursor, max, seed, sizeof(*seed)); } + +struct amount_msat fromwire_amount_msat(const u8 **cursor, size_t *max) +{ + struct amount_msat msat; + + msat.millisatoshis = fromwire_u64(cursor, max); /* Raw: primitive */ + return msat; +} + +struct amount_sat fromwire_amount_sat(const u8 **cursor, size_t *max) +{ + struct amount_sat sat; + + sat.satoshis = fromwire_u64(cursor, max); /* Raw: primitive */ + return sat; +} + diff --git a/wire/peer_wire.c b/wire/peer_wire.c index db88fe4a134c..f79bf8f00332 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -83,6 +83,8 @@ bool is_unknown_msg_discardable(const u8 *cursor) /* Extract channel_id from various packets, return true if possible. */ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) { + struct amount_sat ignored_sat; + struct amount_msat ignored_msat; u64 ignored_u64; u32 ignored_u32; u16 ignored_u16; @@ -94,10 +96,10 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) &ignored_u64, &ignored_u64)) return true; if (fromwire_open_channel(in_pkt, &ignored_chainhash, - channel_id, &ignored_u64, - &ignored_u64, &ignored_u64, - &ignored_u64, &ignored_u64, - &ignored_u64, &ignored_u32, + channel_id, &ignored_sat, + &ignored_msat, &ignored_sat, + &ignored_msat, &ignored_sat, + &ignored_msat, &ignored_u32, &ignored_u16, &ignored_u16, &ignored_pubkey, &ignored_pubkey, &ignored_pubkey, &ignored_pubkey, diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 5e156ed1f145..c55506fb8d02 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -13,10 +13,14 @@ void fromwire_pad_orig(const u8 **cursor, size_t *max, size_t num); #include #include +#include #include secp256k1_context *secp256k1_ctx; +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + /* We allow non-zero padding for testing. */ static const void *towire_pad_arr; void towire_pad(u8 **pptr, size_t num) @@ -77,7 +81,7 @@ struct msg_error { }; struct msg_closing_signed { struct channel_id channel_id; - u64 fee_satoshis; + struct amount_sat fee_satoshis; secp256k1_ecdsa_signature signature; }; struct msg_funding_created { @@ -88,10 +92,10 @@ struct msg_funding_created { }; struct msg_accept_channel { struct channel_id temporary_channel_id; - u64 dust_limit_satoshis; - u64 max_htlc_value_in_flight_msat; - u64 channel_reserve_satoshis; - u64 htlc_minimum_msat; + struct amount_sat dust_limit_satoshis; + struct amount_msat max_htlc_value_in_flight_msat; + struct amount_sat channel_reserve_satoshis; + struct amount_msat htlc_minimum_msat; u32 minimum_depth; u16 to_self_delay; u16 max_accepted_htlcs; @@ -126,7 +130,7 @@ struct msg_channel_update { u8 message_flags; u8 channel_flags; u16 expiry; - u64 htlc_minimum_msat; + struct amount_msat htlc_minimum_msat; u32 fee_base_msat; u32 fee_proportional_millionths; struct bitcoin_blkid chain_hash; @@ -138,10 +142,10 @@ struct msg_channel_update_opt_htlc_max { u8 message_flags; u8 channel_flags; u16 expiry; - u64 htlc_minimum_msat; + struct amount_msat htlc_minimum_msat; u32 fee_base_msat; u32 fee_proportional_millionths; - u64 htlc_maximum_msat; + struct amount_msat htlc_maximum_msat; struct bitcoin_blkid chain_hash; struct short_channel_id short_channel_id; }; @@ -172,12 +176,12 @@ struct msg_node_announcement { struct msg_open_channel { struct bitcoin_blkid chain_hash; struct channel_id temporary_channel_id; - u64 funding_satoshis; - u64 push_msat; - u64 dust_limit_satoshis; - u64 max_htlc_value_in_flight_msat; - u64 channel_reserve_satoshis; - u64 htlc_minimum_msat; + struct amount_sat funding_satoshis; + struct amount_msat push_msat; + struct amount_sat dust_limit_satoshis; + struct amount_msat max_htlc_value_in_flight_msat; + struct amount_sat channel_reserve_satoshis; + struct amount_msat htlc_minimum_msat; u32 feerate_per_kw; u16 to_self_delay; u16 max_accepted_htlcs; @@ -214,7 +218,7 @@ struct msg_init { struct msg_update_add_htlc { struct channel_id channel_id; u64 id; - u64 amount_msat; + struct amount_msat amount_msat; u32 expiry; struct sha256 payment_hash; u8 onion_routing_packet[TOTAL_PACKET_SIZE]; diff --git a/wire/towire.c b/wire/towire.c index bec14906b6a7..9b71d1193675 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -8,6 +8,7 @@ #include #include #include +#include #include void towire(u8 **pptr, const void *data, size_t len) @@ -182,3 +183,13 @@ void towire_siphash_seed(u8 **pptr, const struct siphash_seed *seed) { towire(pptr, seed, sizeof(*seed)); } + +void towire_amount_msat(u8 **pptr, const struct amount_msat msat) +{ + towire_u64(pptr, msat.millisatoshis); /* Raw: primitive */ +} + +void towire_amount_sat(u8 **pptr, const struct amount_sat sat) +{ + towire_u64(pptr, sat.satoshis); /* Raw: primitive */ +} diff --git a/wire/wire.h b/wire/wire.h index c3eaed7d8968..c615a866033c 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,7 @@ struct siphash_seed; typedef char wirestring; void derive_channel_id(struct channel_id *channel_id, - struct bitcoin_txid *txid, u16 txout); + const struct bitcoin_txid *txid, u16 txout); /* Read the type; returns -1 if not long enough. cursor is a tal ptr. */ int fromwire_peektype(const u8 *cursor); @@ -56,6 +57,8 @@ void towire_bitcoin_signature(u8 **pptr, const struct bitcoin_signature *sig); void towire_bitcoin_blkid(u8 **pptr, const struct bitcoin_blkid *blkid); void towire_preimage(u8 **pptr, const struct preimage *preimage); void towire_ripemd160(u8 **pptr, const struct ripemd160 *ripemd); +void towire_amount_msat(u8 **pptr, const struct amount_msat msat); +void towire_amount_sat(u8 **pptr, const struct amount_sat sat); void towire_u8(u8 **pptr, u8 v); void towire_u16(u8 **pptr, u16 v); void towire_u32(u8 **pptr, u32 v); @@ -102,6 +105,8 @@ void fromwire_bitcoin_blkid(const u8 **cursor, size_t *max, struct bitcoin_blkid *blkid); void fromwire_preimage(const u8 **cursor, size_t *max, struct preimage *preimage); void fromwire_ripemd160(const u8 **cursor, size_t *max, struct ripemd160 *ripemd); +struct amount_msat fromwire_amount_msat(const u8 **cursor, size_t *max); +struct amount_sat fromwire_amount_sat(const u8 **cursor, size_t *max); void fromwire_pad(const u8 **cursor, size_t *max, size_t num); void fromwire_u8_array(const u8 **cursor, size_t *max, u8 *arr, size_t num);