From 648415771e76224d9aca82c0d85954136612d264 Mon Sep 17 00:00:00 2001 From: araspitzu Date: Mon, 29 Jul 2019 10:32:05 +0200 Subject: [PATCH 1/3] Update list of commands in eclair-cli help (#1091) * Add missing API endpoints to eclair-cli help --- eclair-core/eclair-cli | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/eclair-core/eclair-cli b/eclair-core/eclair-cli index 08984c7ab4..89a05cdde7 100755 --- a/eclair-core/eclair-cli +++ b/eclair-core/eclair-cli @@ -27,11 +27,12 @@ where OPTIONS can be: -s Some commands can print a trimmed JSON and COMMAND is one of: - getinfo, connect, open, close, forceclose, updaterelayfee, + getinfo, connect, disconnect, open, close, forceclose, updaterelayfee, peers, channels, channel, allnodes, allchannels, allupdates findroute, findroutetonode, parseinvoice, payinvoice, sendtonode, - getsentinfo, createinvoice, getinvoice, listinvoices, - listpendinginvoices, getreceivedinfo, audit, networkfees, channelstats + sendtoroute, getsentinfo, createinvoice, getinvoice, listinvoices, + listpendinginvoices, getreceivedinfo, audit, networkfees, + channelstats, usablebalances Examples -------- From b9698f22305e386744a2b8980ab5ec9dbe7541bb Mon Sep 17 00:00:00 2001 From: Bastien Teinturier <31281497+t-bast@users.noreply.github.com> Date: Wed, 31 Jul 2019 13:35:26 +0000 Subject: [PATCH 2/3] Documentation update (#1092) * Fix README style warnings * Add documentation links to the wiki * Update build instructions. --- BUILD.md | 52 +++++++++++++++++++++++++------------ README.md | 77 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 84 insertions(+), 45 deletions(-) diff --git a/BUILD.md b/BUILD.md index 0c050418eb..054c10e8db 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,39 +1,57 @@ # Building Eclair ## Requirements -- [OpenJDK 11](https://jdk.java.net/11/). + +- [OpenJDK 11](https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot). - [Maven](https://maven.apache.org/download.cgi) 3.6.0 or newer - [Docker](https://www.docker.com/) 18.03 or newer (optional) if you want to run all tests -:warning: You can also use [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) to build and run eclair, but we recommend you use Open JDK11. +:warning: You can also use [Oracle JDK 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) to build and run eclair, but we recommend you use OpenJDK 11. ## Build -To build the project, simply run: + +To build the project and run the tests, simply run: + ```shell -$ mvn install +mvn install ``` -#### Other build options +### Other build options To skip all tests, run: + +```shell +mvn install -DskipTests +``` + +To only build the `eclair-node` module, run: + ```shell -$ mvn install -DskipTests +mvn install -pl eclair-node -am -DskipTests ``` -To only build the `eclair-node` module + +To run the tests, run: + +```shell +mvn test +``` + +To run tests for a specific class, run: + ```shell -$ mvn install -pl eclair-node -am -DskipTests +mvn test -Dsuites=* ``` -# Building the API documentation +## Build the API documentation -## Slate +### Slate The API doc is generated via slate and hosted on github pages. To make a change and update the doc follow the steps: -1. git checkout slate-doc -2. Install your local dependencies for slate, more info [here](https://github.com/lord/slate#getting-started-with-slate) -3. Edit `source/index.html.md` and save your changes. -4. Commit all the changes to git, before deploying the repo should be clean. -5. Push your commit to remote. -6. Run `./deploy.sh` -7. Wait a few minutes and the doc should be updated at https://acinq.github.io/eclair \ No newline at end of file +1. `git checkout slate-doc` +2. Install your local dependencies for slate, more info [here](https://github.com/lord/slate#getting-started-with-slate) +3. Edit `source/index.html.md` and save your changes. +4. Commit all the changes to git, before deploying the repo should be clean. +5. Push your commit to remote. +6. Run `./deploy.sh` +7. Wait a few minutes and the doc should be updated at [https://acinq.github.io/eclair](https://acinq.github.io/eclair) diff --git a/README.md b/README.md index cf308835b6..ae9abf156f 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,20 @@ **Eclair** (French for Lightning) is a Scala implementation of the Lightning Network. It can run with or without a GUI, and a JSON API is also available. This software follows the [Lightning Network Specifications (BOLTs)](https://github.com/lightningnetwork/lightning-rfc). Other implementations include [c-lightning](https://github.com/ElementsProject/lightning) and [lnd](https://github.com/LightningNetwork/lnd). - - --- - - :construction: Both the BOLTs and Eclair itself are still a work in progress. Expect things to break/change! - - :rotating_light: If you run Eclair on mainnet (which is the default setting): - - Keep in mind that it is beta-quality software and **don't put too much money** in it - - Eclair's JSON API should **NOT** be accessible from the outside world (similarly to Bitcoin Core API) - + +--- + +:construction: Both the BOLTs and Eclair itself are still a work in progress. Expect things to break/change! + +:rotating_light: If you run Eclair on mainnet (which is the default setting): + +* Keep in mind that it is beta-quality software and **don't put too much money** in it +* Eclair's JSON API should **NOT** be accessible from the outside world (similarly to Bitcoin Core API) + --- ## Lightning Network Specification Compliance + Please see the latest [release note](https://github.com/ACINQ/eclair/releases) for detailed information on BOLT compliance. ## Overview @@ -32,18 +34,26 @@ Eclair offers a feature rich HTTP API that enables application developers to eas For more information please visit the [API documentation website](https://acinq.github.io/eclair). +## Documentation + +Please visit our [wiki](https://github.com/acinq/eclair/wiki) to find detailed instructions on how to configure your +node, connect to other nodes, open channels, send and receive payments and more advanced scenario. + +You will find detailed guides and frequently asked questions there. + ## Installation ### Configuring Bitcoin Core :warning: Eclair requires Bitcoin Core 0.17.1 or higher. If you are upgrading an existing wallet, you need to create a new address and send all your funds to that address. -Eclair needs a _synchronized_, _segwit-ready_, **_zeromq-enabled_**, _wallet-enabled_, _non-pruning_, _tx-indexing_ [Bitcoin Core](https://github.com/bitcoin/bitcoin) node. +Eclair needs a _synchronized_, _segwit-ready_, **_zeromq-enabled_**, _wallet-enabled_, _non-pruning_, _tx-indexing_ [Bitcoin Core](https://github.com/bitcoin/bitcoin) node. Eclair will use any BTC it finds in the Bitcoin Core wallet to fund any channels you choose to open. Eclair will return BTC from closed channels to this wallet. You can configure your Bitcoin Node to use either `p2sh-segwit` addresses or `bech32` addresses, Eclair is compatible with both modes. Run bitcoind with the following minimal `bitcoin.conf`: -``` + +```conf server=1 rpcuser=foo rpcpassword=bar @@ -55,17 +65,22 @@ zmqpubrawtx=tcp://127.0.0.1:29000 ### Installing Eclair Eclair is developed in [Scala](https://www.scala-lang.org/), a powerful functional language that runs on the JVM, and is packaged as a JAR (Java Archive) file. We provide 2 different packages, which internally use the same core libraries: + * eclair-node, which is a headless application that you can run on servers and desktops, and control from the command line * eclair-node-gui, which also includes a JavaFX GUI To run Eclair, you first need to install Java, we recommend that you use [OpenJDK 11](https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot). Eclair will also run on Oracle JDK 1.8, Oracle JDK 11, and other versions of OpenJDK but we don't recommend using them. Then download our latest [release](https://github.com/ACINQ/eclair/releases) and depending on whether or not you want a GUI run the following command: + * with GUI: + ```shell java -jar eclair-node-gui--.jar ``` + * without GUI: + ```shell java -jar eclair-node--.jar ``` @@ -78,7 +93,7 @@ Eclair reads its configuration file, and write its logs, to `~/.eclair` by defau To change your node's configuration, create a file named `eclair.conf` in `~/.eclair`. Here's an example configuration file: -``` +```conf eclair.node-alias=eclair eclair.node-color=49daaa ``` @@ -111,10 +126,11 @@ Some advanced parameters can be changed with java environment variables. Most us name | description | default value ----------------------|--------------------------------------------|-------------- eclair.datadir | Path to the data directory | ~/.eclair -eclair.headless | Run eclair without a GUI | +eclair.headless | Run eclair without a GUI | eclair.printToConsole | Log to stdout (in addition to eclair.log) | For example, to specify a different data directory you would run the following command: + ```shell java -Declair.datadir=/tmp/node1 -jar eclair-node-gui--.jar ``` @@ -130,41 +146,44 @@ java -Dlogback.configurationFile=/path/to/logback-custom.xml -jar eclair-node-gu #### Backup The files that you need to backup are located in your data directory. You must backup: -- your seed (`seed.dat`) -- your channel database (`eclair.sqlite.bak` under directory `mainnet`, `testnet` or `regtest` depending on which chain you're running on) + +* your seed (`seed.dat`) +* your channel database (`eclair.sqlite.bak` under directory `mainnet`, `testnet` or `regtest` depending on which chain you're running on) Your seed never changes once it has been created, but your channels will change whenever you receive or send payments. Eclair will -create and maintain a snapshot of its database, named `eclair.sqlite.bak`, in your data directory, and update it when needed. This file is +create and maintain a snapshot of its database, named `eclair.sqlite.bak`, in your data directory, and update it when needed. This file is always consistent and safe to use even when Eclair is running, and this is what you should backup regularly. For example you could configure a `cron` task for your backup job. Or you could configure an optional notification script to be called by eclair once a new database snapshot has been created, using the following option: -``` + +```conf eclair.backup-notify-script = "/absolute/path/to/script.sh" ``` + Make sure that your script is executable and uses an absolute path name for `eclair.sqlite.bak`. -Note that depending on your filesystem, in your backup process we recommend first moving `eclair.sqlite.bak` to some temporary file +Note that depending on your filesystem, in your backup process we recommend first moving `eclair.sqlite.bak` to some temporary file before copying that file to your final backup location. - ## Docker A [Dockerfile](Dockerfile) image is built on each commit on [docker hub](https://hub.docker.com/r/acinq/eclair) for running a dockerized eclair-node. You can use the `JAVA_OPTS` environment variable to set arguments to `eclair-node`. -``` +```shell docker run -ti --rm -e "JAVA_OPTS=-Xmx512m -Declair.api.binding-ip=0.0.0.0 -Declair.node-alias=node-pm -Declair.printToConsole" acinq/eclair ``` If you want to persist the data directory, you can make the volume to your host with the `-v` argument, as the following example: -``` +```shell docker run -ti --rm -v "/path_on_host:/data" -e "JAVA_OPTS=-Declair.printToConsole" acinq/eclair ``` If you enabled the API you can check the status of eclair using the command line tool: -``` + +```shell docker exec eclair-cli -p foobar getinfo ``` @@ -175,6 +194,7 @@ For advanced usage, Eclair supports plugins written in Scala, Java, or any JVM-c A valid plugin is a jar that contains an implementation of the [Plugin](eclair-node/src/main/scala/fr/acinq/eclair/Plugin.scala) interface. Here is how to run Eclair with plugins: + ```shell java -jar eclair-node--.jar <...> ``` @@ -184,15 +204,15 @@ java -jar eclair-node--.jar <... Eclair is configured to run on mainnet by default, but you can still run it on testnet (or regtest): start your Bitcoin Node in testnet mode (add `testnet=1` in `bitcoin.conf` or start with `-testnet`), and change Eclair's chain parameter and Bitcoin RPC port: -``` +```conf eclair.chain=testnet eclair.bitcoind.rpcport=18332 ``` -You may also want to take advantage of the new configuration sections in `bitcoin.conf` to manage parameters that are network specific, +You may also want to take advantage of the new configuration sections in `bitcoin.conf` to manage parameters that are network specific, so you can easily run your bitcoin node on both mainnet and testnet. For example you could use: -``` +```conf server=1 txindex=1 [main] @@ -208,6 +228,7 @@ zmqpubrawtx=tcp://127.0.0.1:29001 ``` ## Resources -- [1] [The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments](https://lightning.network/lightning-network-paper.pdf) by Joseph Poon and Thaddeus Dryja -- [2] [Reaching The Ground With Lightning](https://github.com/ElementsProject/lightning/raw/master/doc/deployable-lightning.pdf) by Rusty Russell -- [3] [Lightning Network Explorer](https://explorer.acinq.co) - Explore testnet LN nodes you can connect to + +* [1] [The Bitcoin Lightning Network: Scalable Off-Chain Instant Payments](https://lightning.network/lightning-network-paper.pdf) by Joseph Poon and Thaddeus Dryja +* [2] [Reaching The Ground With Lightning](https://github.com/ElementsProject/lightning/raw/master/doc/deployable-lightning.pdf) by Rusty Russell +* [3] [Lightning Network Explorer](https://explorer.acinq.co) - Explore testnet LN nodes you can connect to From 4929febbd11ebad6ae20caed4b161db44dfd263d Mon Sep 17 00:00:00 2001 From: araspitzu Date: Wed, 7 Aug 2019 17:37:38 +0200 Subject: [PATCH 3/3] Typed amounts (#1088) * Type all amounts used in eclair * Add eclair.MilliSatoshi class * Use bitcoin-lib 0.14 * Add specialized codecs for Satoshi/MilliSatoshi * Rename 'toSatoshi' to 'truncateToSatoshi' to highlight it's a precision-losing conversion --- eclair-core/eclair-cli | 2 +- .../scala/fr/acinq/eclair/CoinUtils.scala | 16 +- .../main/scala/fr/acinq/eclair/Eclair.scala | 42 +- .../scala/fr/acinq/eclair/NodeParams.scala | 25 +- .../fr/acinq/eclair/api/ExtraDirectives.scala | 5 +- .../eclair/api/FormParamExtractors.scala | 14 +- .../fr/acinq/eclair/api/JsonSerializers.scala | 9 +- .../scala/fr/acinq/eclair/api/Service.scala | 16 +- .../fr/acinq/eclair/channel/Channel.scala | 68 +-- .../acinq/eclair/channel/ChannelEvents.scala | 5 +- .../eclair/channel/ChannelExceptions.scala | 28 +- .../acinq/eclair/channel/ChannelTypes.scala | 25 +- .../fr/acinq/eclair/channel/Commitments.scala | 82 ++-- .../fr/acinq/eclair/channel/Helpers.scala | 70 +-- .../scala/fr/acinq/eclair/db/AuditDb.scala | 8 +- .../scala/fr/acinq/eclair/db/PaymentsDb.scala | 10 +- .../eclair/db/sqlite/SqliteAuditDb.scala | 17 +- .../eclair/db/sqlite/SqlitePaymentsDb.scala | 15 +- .../main/scala/fr/acinq/eclair/io/Peer.scala | 27 +- .../main/scala/fr/acinq/eclair/package.scala | 52 ++- .../fr/acinq/eclair/payment/Auditor.scala | 12 +- .../fr/acinq/eclair/payment/Autoprobe.scala | 6 +- .../eclair/payment/LocalPaymentHandler.scala | 14 +- .../acinq/eclair/payment/PaymentEvents.scala | 5 +- .../eclair/payment/PaymentLifecycle.scala | 50 +- .../acinq/eclair/payment/PaymentRequest.scala | 5 +- .../fr/acinq/eclair/payment/Relayer.scala | 24 +- .../acinq/eclair/router/Announcements.scala | 6 +- .../scala/fr/acinq/eclair/router/Graph.scala | 36 +- .../scala/fr/acinq/eclair/router/Router.scala | 22 +- .../eclair/transactions/CommitmentSpec.scala | 17 +- .../eclair/transactions/Transactions.scala | 31 +- .../fr/acinq/eclair/wire/ChannelCodecs.scala | 20 +- .../fr/acinq/eclair/wire/CommonCodecs.scala | 8 +- .../fr/acinq/eclair/wire/FailureMessage.scala | 19 +- .../eclair/wire/LightningMessageCodecs.scala | 30 +- .../eclair/wire/LightningMessageTypes.scala | 30 +- .../scala/fr/acinq/eclair/wire/Onion.scala | 6 +- .../src/test/resources/api/usablebalances | 2 +- .../scenarii/01-offer1.script.expected | 24 +- .../scenarii/02-offer2.script.expected | 24 +- .../scenarii/03-fulfill1.script.expected | 16 +- .../04-two-commits-onedir.script.expected | 16 +- .../10-offers-crossover.script.expected | 32 +- .../11-commits-crossover.script.expected | 32 +- .../scala/fr/acinq/eclair/CoinUtilsSpec.scala | 2 +- .../fr/acinq/eclair/EclairImplSpec.scala | 52 +-- .../scala/fr/acinq/eclair/TestConstants.scala | 30 +- .../fr/acinq/eclair/api/ApiServiceSpec.scala | 17 +- .../eclair/api/JsonSerializersSpec.scala | 2 +- .../bitcoind/BitcoinCoreWalletSpec.scala | 2 +- .../fr/acinq/eclair/channel/FuzzySpec.scala | 4 +- .../acinq/eclair/channel/ThroughputSpec.scala | 2 +- .../states/StateTestsHelperMethods.scala | 10 +- .../a/WaitForAcceptChannelStateSpec.scala | 10 +- .../a/WaitForOpenChannelStateSpec.scala | 28 +- .../b/WaitForFundingCreatedStateSpec.scala | 14 +- .../channel/states/e/NormalStateSpec.scala | 303 +++++++------ .../channel/states/e/OfflineStateSpec.scala | 26 +- .../channel/states/f/ShutdownStateSpec.scala | 10 +- .../states/g/NegotiatingStateSpec.scala | 14 +- .../channel/states/h/ClosingStateSpec.scala | 22 +- .../acinq/eclair/db/SqliteAuditDbSpec.scala | 14 +- .../acinq/eclair/db/SqliteNetworkDbSpec.scala | 10 +- .../eclair/db/SqlitePaymentsDbSpec.scala | 28 +- .../eclair/integration/IntegrationSpec.scala | 53 +-- .../interop/rustytests/RustyTestsSpec.scala | 6 +- .../rustytests/SynchronizationPipe.scala | 12 +- .../fr/acinq/eclair/io/HtlcReaperSpec.scala | 7 +- .../scala/fr/acinq/eclair/io/PeerSpec.scala | 2 +- .../eclair/payment/ChannelSelectionSpec.scala | 40 +- .../eclair/payment/HtlcGenerationSpec.scala | 29 +- .../eclair/payment/PaymentHandlerSpec.scala | 12 +- .../eclair/payment/PaymentLifecycleSpec.scala | 18 +- .../eclair/payment/PaymentRequestSpec.scala | 21 +- .../fr/acinq/eclair/payment/RelayerSpec.scala | 72 +-- .../AnnouncementsBatchValidationSpec.scala | 5 +- .../eclair/router/AnnouncementsSpec.scala | 12 +- .../acinq/eclair/router/BaseRouterSpec.scala | 16 +- .../fr/acinq/eclair/router/GraphSpec.scala | 66 +-- .../eclair/router/RouteCalculationSpec.scala | 428 +++++++++--------- .../fr/acinq/eclair/router/RouterSpec.scala | 16 +- .../acinq/eclair/router/RoutingSyncSpec.scala | 6 +- .../transactions/CommitmentSpecSpec.scala | 30 +- .../eclair/transactions/TestVectorsSpec.scala | 49 +- .../transactions/TransactionsSpec.scala | 43 +- .../acinq/eclair/wire/ChannelCodecsSpec.scala | 84 ++-- .../wire/FailureMessageCodecsSpec.scala | 14 +- .../wire/LightningMessageCodecsSpec.scala | 14 +- .../acinq/eclair/wire/OnionCodecsSpec.scala | 4 +- .../fr/acinq/eclair/gui/GUIUpdater.scala | 8 +- .../scala/fr/acinq/eclair/gui/Handlers.scala | 6 +- .../controllers/ChannelPaneController.scala | 8 +- .../gui/controllers/MainController.scala | 4 +- .../controllers/OpenChannelController.scala | 15 +- .../ReceivePaymentController.scala | 2 +- pom.xml | 2 +- 97 files changed, 1420 insertions(+), 1307 deletions(-) diff --git a/eclair-core/eclair-cli b/eclair-core/eclair-cli index 89a05cdde7..be2102d71c 100755 --- a/eclair-core/eclair-cli +++ b/eclair-core/eclair-cli @@ -89,7 +89,7 @@ jq_filter='if type=="object" and .error != null then .error else .'; # apply special jq filter if we are in "short" ouput mode -- only for specific commands such as 'channels' if [ "$short" = true ]; then - jq_channel_filter="{ nodeId, shortChannelId: .data.shortChannelId, channelId, state, balanceSat: (try (.data.commitments.localCommit.spec.toLocalMsat / 1000 | floor) catch null), capacitySat: .data.commitments.commitInput.amountSatoshis, channelPoint: .data.commitments.commitInput.outPoint }"; + jq_channel_filter="{ nodeId, shortChannelId: .data.shortChannelId, channelId, state, balanceSat: (try (.data.commitments.localCommit.spec.toLocal / 1000 | floor) catch null), capacitySat: .data.commitments.commitInput.amountSatoshis, channelPoint: .data.commitments.commitInput.outPoint }"; case $api_endpoint in "channels") jq_filter="$jq_filter | map( $jq_channel_filter )" ;; "channel") jq_filter="$jq_filter | $jq_channel_filter" ;; diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala b/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala index e4453ab8aa..2e58bef4dd 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/CoinUtils.scala @@ -17,10 +17,8 @@ package fr.acinq.eclair import java.text.{DecimalFormat, NumberFormat} - -import fr.acinq.bitcoin.{Btc, BtcAmount, MilliBtc, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Btc, BtcAmount, MilliBtc, Satoshi} import grizzled.slf4j.Logging - import scala.util.{Failure, Success, Try} /** @@ -154,7 +152,7 @@ object CoinUtils extends Logging { } def convertStringAmountToSat(amount: String, unit: String): Satoshi = - fr.acinq.bitcoin.millisatoshi2satoshi(CoinUtils.convertStringAmountToMsat(amount, unit)) + CoinUtils.convertStringAmountToMsat(amount, unit).truncateToSatoshi /** * Only BtcUnit, MBtcUnit, BitUnit, SatUnit and MSatUnit codes or label are supported. @@ -223,6 +221,11 @@ object CoinUtils extends Logging { if (withUnit) s"$formatted ${unit.shortLabel}" else formatted } + def formatAmountInUnit(amount: MilliSatoshi, unit: CoinUnit, withUnit: Boolean): String = { + val formatted = COIN_FORMAT.format(rawAmountInUnit(amount, unit)) + if (withUnit) s"$formatted ${unit.shortLabel}" else formatted + } + /** * Converts the amount to the user preferred unit and returns the BigDecimal value. * This method is useful to feed numeric text input without formatting. @@ -237,7 +240,10 @@ object CoinUtils extends Logging { case a => throw new IllegalArgumentException(s"unhandled unit $a") }) match { case Success(b) => b - case Failure(t) => logger.error("can not convert amount to user unit", t) + case Failure(t) => + logger.error("can not convert amount to user unit", t) -1 } + + def rawAmountInUnit(msat: MilliSatoshi, unit: CoinUnit): BigDecimal = BigDecimal(msat.amount) / unit.factorToMsat } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala index e423806715..e22b621d35 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Eclair.scala @@ -22,7 +22,7 @@ import akka.actor.ActorRef import akka.pattern._ import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.channel.Register.{Forward, ForwardShortId} import fr.acinq.eclair.channel._ import fr.acinq.eclair.db.{IncomingPayment, NetworkFee, OutgoingPayment, Stats} @@ -60,13 +60,13 @@ trait Eclair { def disconnect(nodeId: PublicKey)(implicit timeout: Timeout): Future[String] - def open(nodeId: PublicKey, fundingSatoshis: Long, pushMsat_opt: Option[Long], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] + def open(nodeId: PublicKey, fundingAmount: Satoshi, pushAmount_opt: Option[MilliSatoshi], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] def close(channelIdentifier: Either[ByteVector32, ShortChannelId], scriptPubKey_opt: Option[ByteVector])(implicit timeout: Timeout): Future[String] def forceClose(channelIdentifier: Either[ByteVector32, ShortChannelId])(implicit timeout: Timeout): Future[String] - def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: Long, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] + def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBase: MilliSatoshi, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] def channelsInfo(toRemoteNode_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[RES_GETINFO]] @@ -74,17 +74,17 @@ trait Eclair { def peersInfo()(implicit timeout: Timeout): Future[Iterable[PeerInfo]] - def receive(description: String, amountMsat_opt: Option[Long], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] + def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] def receivedInfo(paymentHash: ByteVector32)(implicit timeout: Timeout): Future[Option[IncomingPayment]] - def send(recipientNodeId: PublicKey, amountMsat: Long, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long] = None, maxAttempts_opt: Option[Int] = None, feeThresholdSat_opt: Option[Long] = None, maxFeePct_opt: Option[Double] = None)(implicit timeout: Timeout): Future[UUID] + def send(recipientNodeId: PublicKey, amount: MilliSatoshi, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long] = None, maxAttempts_opt: Option[Int] = None, feeThresholdSat_opt: Option[Satoshi] = None, maxFeePct_opt: Option[Double] = None)(implicit timeout: Timeout): Future[UUID] def sentInfo(id: Either[UUID, ByteVector32])(implicit timeout: Timeout): Future[Seq[OutgoingPayment]] - def findRoute(targetNodeId: PublicKey, amountMsat: Long, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] + def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] - def sendToRoute(route: Seq[PublicKey], amountMsat: Long, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] + def sendToRoute(route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] def audit(from_opt: Option[Long], to_opt: Option[Long])(implicit timeout: Timeout): Future[AuditResponse] @@ -122,13 +122,13 @@ class EclairImpl(appKit: Kit) extends Eclair { (appKit.switchboard ? Peer.Disconnect(nodeId)).mapTo[String] } - override def open(nodeId: PublicKey, fundingSatoshis: Long, pushMsat_opt: Option[Long], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] = { + override def open(nodeId: PublicKey, fundingAmount: Satoshi, pushAmount_opt: Option[MilliSatoshi], fundingFeerateSatByte_opt: Option[Long], flags_opt: Option[Int], openTimeout_opt: Option[Timeout])(implicit timeout: Timeout): Future[String] = { // we want the open timeout to expire *before* the default ask timeout, otherwise user won't get a generic response val openTimeout = openTimeout_opt.getOrElse(Timeout(10 seconds)) (appKit.switchboard ? Peer.OpenChannel( remoteNodeId = nodeId, - fundingSatoshis = Satoshi(fundingSatoshis), - pushMsat = pushMsat_opt.map(MilliSatoshi).getOrElse(MilliSatoshi(0)), + fundingSatoshis = fundingAmount, + pushMsat = pushAmount_opt.getOrElse(MilliSatoshi(0)), fundingTxFeeratePerKw_opt = fundingFeerateSatByte_opt.map(feerateByte2Kw), channelFlags = flags_opt.map(_.toByte), timeout_opt = Some(openTimeout))).mapTo[String] @@ -142,7 +142,7 @@ class EclairImpl(appKit: Kit) extends Eclair { sendToChannel(channelIdentifier, CMD_FORCECLOSE).mapTo[String] } - override def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: Long, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] = { + override def updateRelayFee(channelIdentifier: Either[ByteVector32, ShortChannelId], feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long)(implicit timeout: Timeout): Future[String] = { sendToChannel(channelIdentifier, CMD_UPDATE_RELAY_FEE(feeBaseMsat, feeProportionalMillionths)).mapTo[String] } @@ -177,31 +177,31 @@ class EclairImpl(appKit: Kit) extends Eclair { case Some(pk) => (appKit.router ? 'updatesMap).mapTo[Map[ChannelDesc, ChannelUpdate]].map(_.filter(e => e._1.a == pk || e._1.b == pk).values) } - override def receive(description: String, amountMsat_opt: Option[Long], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] = { + override def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest] = { fallbackAddress_opt.map { fa => fr.acinq.eclair.addressToPublicKeyScript(fa, appKit.nodeParams.chainHash) } // if it's not a bitcoin address throws an exception - (appKit.paymentHandler ? ReceivePayment(description = description, amountMsat_opt = amountMsat_opt.map(MilliSatoshi), expirySeconds_opt = expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt)).mapTo[PaymentRequest] + (appKit.paymentHandler ? ReceivePayment(description = description, amount_opt = amount_opt, expirySeconds_opt = expire_opt, fallbackAddress = fallbackAddress_opt, paymentPreimage = paymentPreimage_opt)).mapTo[PaymentRequest] } - override def findRoute(targetNodeId: PublicKey, amountMsat: Long, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] = { - (appKit.router ? RouteRequest(appKit.nodeParams.nodeId, targetNodeId, amountMsat, assistedRoutes)).mapTo[RouteResponse] + override def findRoute(targetNodeId: PublicKey, amount: MilliSatoshi, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty)(implicit timeout: Timeout): Future[RouteResponse] = { + (appKit.router ? RouteRequest(appKit.nodeParams.nodeId, targetNodeId, amount, assistedRoutes)).mapTo[RouteResponse] } - override def sendToRoute(route: Seq[PublicKey], amountMsat: Long, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] = { - (appKit.paymentInitiator ? SendPaymentToRoute(amountMsat, paymentHash, route, finalCltvExpiry)).mapTo[UUID] + override def sendToRoute(route: Seq[PublicKey], amount: MilliSatoshi, paymentHash: ByteVector32, finalCltvExpiry: Long)(implicit timeout: Timeout): Future[UUID] = { + (appKit.paymentInitiator ? SendPaymentToRoute(amount, paymentHash, route, finalCltvExpiry)).mapTo[UUID] } - override def send(recipientNodeId: PublicKey, amountMsat: Long, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long], maxAttempts_opt: Option[Int], feeThresholdSat_opt: Option[Long], maxFeePct_opt: Option[Double])(implicit timeout: Timeout): Future[UUID] = { + override def send(recipientNodeId: PublicKey, amount: MilliSatoshi, paymentHash: ByteVector32, assistedRoutes: Seq[Seq[PaymentRequest.ExtraHop]] = Seq.empty, minFinalCltvExpiry_opt: Option[Long], maxAttempts_opt: Option[Int], feeThreshold_opt: Option[Satoshi], maxFeePct_opt: Option[Double])(implicit timeout: Timeout): Future[UUID] = { val maxAttempts = maxAttempts_opt.getOrElse(appKit.nodeParams.maxPaymentAttempts) val defaultRouteParams = Router.getDefaultRouteParams(appKit.nodeParams.routerConf) val routeParams = defaultRouteParams.copy( maxFeePct = maxFeePct_opt.getOrElse(defaultRouteParams.maxFeePct), - maxFeeBaseMsat = feeThresholdSat_opt.map(_ * 1000).getOrElse(defaultRouteParams.maxFeeBaseMsat) + maxFeeBase = feeThreshold_opt.map(_.toMilliSatoshi).getOrElse(defaultRouteParams.maxFeeBase) ) val sendPayment = minFinalCltvExpiry_opt match { - case Some(minCltv) => SendPayment(amountMsat, paymentHash, recipientNodeId, assistedRoutes, finalCltvExpiry = minCltv, maxAttempts = maxAttempts, routeParams = Some(routeParams)) - case None => SendPayment(amountMsat, paymentHash, recipientNodeId, assistedRoutes, maxAttempts = maxAttempts, routeParams = Some(routeParams)) + case Some(minCltv) => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, finalCltvExpiry = minCltv, maxAttempts = maxAttempts, routeParams = Some(routeParams)) + case None => SendPayment(amount, paymentHash, recipientNodeId, assistedRoutes, maxAttempts = maxAttempts, routeParams = Some(routeParams)) } (appKit.paymentInitiator ? sendPayment).mapTo[UUID] } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala index 55f5fb2859..2daa66510f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/NodeParams.scala @@ -23,8 +23,8 @@ import java.util.concurrent.TimeUnit import com.typesafe.config.{Config, ConfigFactory} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{Block, ByteVector32} -import fr.acinq.eclair.NodeParams.{WatcherType} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi} +import fr.acinq.eclair.NodeParams.WatcherType import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, OnChainFeeConf} import fr.acinq.eclair.channel.Channel import fr.acinq.eclair.crypto.KeyManager @@ -33,7 +33,6 @@ import fr.acinq.eclair.router.RouterConf import fr.acinq.eclair.tor.Socks5ProxyParams import fr.acinq.eclair.wire.{Color, NodeAddress} import scodec.bits.ByteVector - import scala.collection.JavaConversions._ import scala.concurrent.duration.FiniteDuration @@ -47,17 +46,17 @@ case class NodeParams(keyManager: KeyManager, globalFeatures: ByteVector, localFeatures: ByteVector, overrideFeatures: Map[PublicKey, (ByteVector, ByteVector)], - dustLimitSatoshis: Long, + dustLimit: Satoshi, onChainFeeConf: OnChainFeeConf, maxHtlcValueInFlightMsat: UInt64, maxAcceptedHtlcs: Int, expiryDeltaBlocks: Int, fulfillSafetyBeforeTimeoutBlocks: Int, - htlcMinimumMsat: Int, + htlcMinimum: MilliSatoshi, toRemoteDelayBlocks: Int, maxToLocalDelayBlocks: Int, minDepthBlocks: Int, - feeBaseMsat: Int, + feeBase: MilliSatoshi, feeProportionalMillionth: Int, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double, @@ -73,7 +72,7 @@ case class NodeParams(keyManager: KeyManager, channelFlags: Byte, watcherType: WatcherType, paymentRequestExpiry: FiniteDuration, - minFundingSatoshis: Long, + minFundingSatoshis: Satoshi, routerConf: RouterConf, socksProxy_opt: Option[Socks5ProxyParams], maxPaymentAttempts: Int) { @@ -137,7 +136,7 @@ object NodeParams { case _ => BITCOIND } - val dustLimitSatoshis = config.getLong("dust-limit-satoshis") + val dustLimitSatoshis = Satoshi(config.getLong("dust-limit-satoshis")) if (chainHash == Block.LivenetGenesisBlock.hash) { require(dustLimitSatoshis >= Channel.MIN_DUSTLIMIT, s"dust limit must be greater than ${Channel.MIN_DUSTLIMIT}") } @@ -195,7 +194,7 @@ object NodeParams { globalFeatures = ByteVector.fromValidHex(config.getString("global-features")), localFeatures = ByteVector.fromValidHex(config.getString("local-features")), overrideFeatures = overrideFeatures, - dustLimitSatoshis = dustLimitSatoshis, + dustLimit = dustLimitSatoshis, onChainFeeConf = OnChainFeeConf( feeTargets = feeTargets, feeEstimator = feeEstimator, @@ -206,11 +205,11 @@ object NodeParams { maxAcceptedHtlcs = maxAcceptedHtlcs, expiryDeltaBlocks = expiryDeltaBlocks, fulfillSafetyBeforeTimeoutBlocks = fulfillSafetyBeforeTimeoutBlocks, - htlcMinimumMsat = config.getInt("htlc-minimum-msat"), + htlcMinimum = MilliSatoshi(config.getInt("htlc-minimum-msat")), toRemoteDelayBlocks = config.getInt("to-remote-delay-blocks"), maxToLocalDelayBlocks = config.getInt("max-to-local-delay-blocks"), minDepthBlocks = config.getInt("mindepth-blocks"), - feeBaseMsat = config.getInt("fee-base-msat"), + feeBase = MilliSatoshi(config.getInt("fee-base-msat")), feeProportionalMillionth = config.getInt("fee-proportional-millionths"), reserveToFundingRatio = config.getDouble("reserve-to-funding-ratio"), maxReserveToFundingRatio = config.getDouble("max-reserve-to-funding-ratio"), @@ -226,14 +225,14 @@ object NodeParams { channelFlags = config.getInt("channel-flags").toByte, watcherType = watcherType, paymentRequestExpiry = FiniteDuration(config.getDuration("payment-request-expiry").getSeconds, TimeUnit.SECONDS), - minFundingSatoshis = config.getLong("min-funding-satoshis"), + minFundingSatoshis = Satoshi(config.getLong("min-funding-satoshis")), routerConf = RouterConf( channelExcludeDuration = FiniteDuration(config.getDuration("router.channel-exclude-duration").getSeconds, TimeUnit.SECONDS), routerBroadcastInterval = FiniteDuration(config.getDuration("router.broadcast-interval").getSeconds, TimeUnit.SECONDS), randomizeRouteSelection = config.getBoolean("router.randomize-route-selection"), searchMaxRouteLength = config.getInt("router.path-finding.max-route-length"), searchMaxCltv = config.getInt("router.path-finding.max-cltv"), - searchMaxFeeBaseSat = config.getLong("router.path-finding.fee-threshold-sat"), + searchMaxFeeBase = Satoshi(config.getLong("router.path-finding.fee-threshold-sat")), searchMaxFeePct = config.getDouble("router.path-finding.max-fee-pct"), searchHeuristicsEnabled = config.getBoolean("router.path-finding.heuristics-enable"), searchRatioCltv = config.getDouble("router.path-finding.ratio-cltv"), diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala index 9d5e1414f0..2973b53ec6 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/ExtraDirectives.scala @@ -22,10 +22,11 @@ import akka.http.scaladsl.model.StatusCodes._ import akka.http.scaladsl.server.{Directive1, Directives, MalformedFormFieldRejection, Route} import fr.acinq.bitcoin.ByteVector32 import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.api.FormParamExtractors.{sha256HashUnmarshaller, shortChannelIdUnmarshaller} import fr.acinq.eclair.api.JsonSupport._ import fr.acinq.eclair.payment.PaymentRequest + import scala.concurrent.Future import scala.util.{Failure, Success} @@ -38,7 +39,7 @@ trait ExtraDirectives extends Directives { val paymentHashFormParam = "paymentHash".as[ByteVector32](sha256HashUnmarshaller) val fromFormParam = "from".as[Long] val toFormParam = "to".as[Long] - val amountMsatFormParam = "amountMsat".as[Long] + val amountMsatFormParam = "amountMsat".as[MilliSatoshi] val invoiceFormParam = "invoice".as[PaymentRequest] // custom directive to fail with HTTP 404 (and JSON response) if the element was not found diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala index 17425cf368..cbe045faa8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/FormParamExtractors.scala @@ -21,12 +21,13 @@ import java.util.UUID import JsonSupport._ import akka.http.scaladsl.unmarshalling.Unmarshaller import akka.util.Timeout -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.io.NodeURI import fr.acinq.eclair.payment.PaymentRequest import scodec.bits.ByteVector + import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} @@ -75,4 +76,13 @@ object FormParamExtractors { } } + implicit val satoshiUnmarshaller: Unmarshaller[String, Satoshi] = Unmarshaller.strict { str => + Satoshi(str.toLong) + } + + implicit val millisatoshiUnmarshaller: Unmarshaller[String, MilliSatoshi] = Unmarshaller.strict { str => + MilliSatoshi(str.toLong) + } + + } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala index a1a586941f..203c6c7f35 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/JsonSerializers.scala @@ -23,7 +23,7 @@ import com.google.common.net.HostAndPort import de.heikoseeberger.akkahttpjson4s.Json4sSupport import de.heikoseeberger.akkahttpjson4s.Json4sSupport.ShouldWritePretty import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64, MilliSatoshi, OutPoint, Transaction} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, OutPoint, Satoshi, Transaction} import fr.acinq.eclair.channel.{ChannelVersion, State} import fr.acinq.eclair.crypto.ShaChain import fr.acinq.eclair.db.OutgoingPaymentStatus @@ -32,7 +32,7 @@ import fr.acinq.eclair.router.RouteResponse import fr.acinq.eclair.transactions.Direction import fr.acinq.eclair.transactions.Transactions.{InputInfo, TransactionWithInputInfo} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import org.json4s.JsonAST._ import org.json4s.{CustomKeySerializer, CustomSerializer, TypeHints, jackson} import scodec.bits.ByteVector @@ -57,6 +57,10 @@ class UInt64Serializer extends CustomSerializer[UInt64](format => ({ null }, { case x: UInt64 => JInt(x.toBigInt) })) +class SatoshiSerializer extends CustomSerializer[Satoshi](format => ({ null }, { + case x: Satoshi => JInt(x.amount) +})) + class MilliSatoshiSerializer extends CustomSerializer[MilliSatoshi](format => ({ null }, { case x: MilliSatoshi => JInt(x.amount) })) @@ -187,6 +191,7 @@ object JsonSupport extends Json4sSupport { new ByteVector32Serializer + new ByteVector64Serializer + new UInt64Serializer + + new SatoshiSerializer + new MilliSatoshiSerializer + new ShortChannelIdSerializer + new StateSerializer + diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala index 3d781cadc5..7d50d75217 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/api/Service.scala @@ -31,14 +31,14 @@ import akka.stream.scaladsl.{BroadcastHub, Flow, Keep, Source} import akka.stream.{ActorMaterializer, OverflowStrategy} import akka.util.Timeout import com.google.common.net.HostAndPort -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.api.FormParamExtractors._ import fr.acinq.eclair.api.JsonSupport.CustomTypeHints import fr.acinq.eclair.io.NodeURI import fr.acinq.eclair.payment.PaymentLifecycle.PaymentFailed import fr.acinq.eclair.payment.{PaymentReceived, PaymentRequest, _} -import fr.acinq.eclair.{Eclair, ShortChannelId} +import fr.acinq.eclair.{Eclair, MilliSatoshi, ShortChannelId} import grizzled.slf4j.Logging import org.json4s.jackson.Serialization import scodec.bits.ByteVector @@ -156,14 +156,14 @@ trait Service extends ExtraDirectives with Logging { } } ~ path("open") { - formFields(nodeIdFormParam, "fundingSatoshis".as[Long], "pushMsat".as[Long].?, "fundingFeerateSatByte".as[Long].?, "channelFlags".as[Int].?, "openTimeoutSeconds".as[Timeout].?) { + formFields(nodeIdFormParam, "fundingSatoshis".as[Satoshi], "pushMsat".as[MilliSatoshi].?, "fundingFeerateSatByte".as[Long].?, "channelFlags".as[Int].?, "openTimeoutSeconds".as[Timeout].?) { (nodeId, fundingSatoshis, pushMsat, fundingFeerateSatByte, channelFlags, openTimeout_opt) => complete(eclairApi.open(nodeId, fundingSatoshis, pushMsat, fundingFeerateSatByte, channelFlags, openTimeout_opt)) } } ~ path("updaterelayfee") { withChannelIdentifier { channelIdentifier => - formFields("feeBaseMsat".as[Long], "feeProportionalMillionths".as[Long]) { (feeBase, feeProportional) => + formFields("feeBaseMsat".as[MilliSatoshi], "feeProportionalMillionths".as[Long]) { (feeBase, feeProportional) => complete(eclairApi.updateRelayFee(channelIdentifier, feeBase, feeProportional)) } } @@ -206,7 +206,7 @@ trait Service extends ExtraDirectives with Logging { } ~ path("findroute") { formFields(invoiceFormParam, amountMsatFormParam.?) { - case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None) => complete(eclairApi.findRoute(nodeId, amount.toLong, invoice.routingInfo)) + case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None) => complete(eclairApi.findRoute(nodeId, amount, invoice.routingInfo)) case (invoice, Some(overrideAmount)) => complete(eclairApi.findRoute(invoice.nodeId, overrideAmount, invoice.routingInfo)) case _ => reject(MalformedFormFieldRejection("invoice", "The invoice must have an amount or you need to specify one using 'amountMsat'")) } @@ -222,16 +222,16 @@ trait Service extends ExtraDirectives with Logging { } } ~ path("payinvoice") { - formFields(invoiceFormParam, amountMsatFormParam.?, "maxAttempts".as[Int].?, "feeThresholdSat".as[Long].?, "maxFeePct".as[Double].?) { + formFields(invoiceFormParam, amountMsatFormParam.?, "maxAttempts".as[Int].?, "feeThresholdSat".as[Satoshi].?, "maxFeePct".as[Double].?) { case (invoice@PaymentRequest(_, Some(amount), _, nodeId, _, _), None, maxAttempts, feeThresholdSat_opt, maxFeePct_opt) => - complete(eclairApi.send(nodeId, amount.toLong, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) + complete(eclairApi.send(nodeId, amount, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) case (invoice, Some(overrideAmount), maxAttempts, feeThresholdSat_opt, maxFeePct_opt) => complete(eclairApi.send(invoice.nodeId, overrideAmount, invoice.paymentHash, invoice.routingInfo, invoice.minFinalCltvExpiry, maxAttempts, feeThresholdSat_opt, maxFeePct_opt)) case _ => reject(MalformedFormFieldRejection("invoice", "The invoice must have an amount or you need to specify one using the field 'amountMsat'")) } } ~ path("sendtonode") { - formFields(amountMsatFormParam, paymentHashFormParam, nodeIdFormParam, "maxAttempts".as[Int].?, "feeThresholdSat".as[Long].?, "maxFeePct".as[Double].?) { (amountMsat, paymentHash, nodeId, maxAttempts_opt, feeThresholdSat_opt, maxFeePct_opt) => + formFields(amountMsatFormParam, paymentHashFormParam, nodeIdFormParam, "maxAttempts".as[Int].?, "feeThresholdSat".as[Satoshi].?, "maxFeePct".as[Double].?) { (amountMsat, paymentHash, nodeId, maxAttempts_opt, feeThresholdSat_opt, maxFeePct_opt) => complete(eclairApi.send(nodeId, amountMsat, paymentHash, maxAttempts_opt = maxAttempts_opt, feeThresholdSat_opt = feeThresholdSat_opt, maxFeePct_opt = maxFeePct_opt)) } } ~ diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala index 72585ee2fd..d095f595fb 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Channel.scala @@ -20,7 +20,7 @@ import akka.actor.{ActorRef, FSM, OneForOneStrategy, Props, Status, SupervisorSt import akka.event.Logging.MDC import akka.pattern.pipe import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256} -import fr.acinq.bitcoin._ +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Satoshi, Script, ScriptFlags, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.channel.Helpers.{Closing, Funding} @@ -48,11 +48,11 @@ object Channel { val ANNOUNCEMENTS_MINCONF = 6 // https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md#requirements - val MAX_FUNDING_SATOSHIS = 16777216L // = 2^24 + val MAX_FUNDING = Satoshi(16777216L) // = 2^24 val MAX_ACCEPTED_HTLCS = 483 // we don't want the counterparty to use a dust limit lower than that, because they wouldn't only hurt themselves we may need them to publish their commit tx in certain cases (backup/restore) - val MIN_DUSTLIMIT = 546 + val MIN_DUSTLIMIT = Satoshi(546) // we won't exchange more than this many signatures when negotiating the closing fee val MAX_NEGOTIATION_ITERATIONS = 20 @@ -152,10 +152,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId temporaryChannelId = temporaryChannelId, fundingSatoshis = fundingSatoshis, pushMsat = pushMsat, - dustLimitSatoshis = localParams.dustLimitSatoshis, + dustLimitSatoshis = localParams.dustLimit, maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = localParams.channelReserveSatoshis, - htlcMinimumMsat = localParams.htlcMinimumMsat, + channelReserveSatoshis = localParams.channelReserve, + htlcMinimumMsat = localParams.htlcMinimum, feeratePerKw = initialFeeratePerKw, toSelfDelay = localParams.toSelfDelay, maxAcceptedHtlcs = localParams.maxAcceptedHtlcs, @@ -227,7 +227,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // we rebuild a new channel_update with values from the configuration because they may have changed while eclair was down val candidateChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, normal.channelUpdate.shortChannelId, nodeParams.expiryDeltaBlocks, - normal.commitments.remoteParams.htlcMinimumMsat, normal.channelUpdate.feeBaseMsat, normal.channelUpdate.feeProportionalMillionths, normal.commitments.localCommit.spec.totalFunds, enable = Announcements.isEnabled(normal.channelUpdate.channelFlags)) + normal.commitments.remoteParams.htlcMinimum, normal.channelUpdate.feeBaseMsat, normal.channelUpdate.feeProportionalMillionths, normal.commitments.localCommit.spec.totalFunds, enable = Announcements.isEnabled(normal.channelUpdate.channelFlags)) val channelUpdate1 = if (Announcements.areSame(candidateChannelUpdate, normal.channelUpdate)) { // if there was no configuration change we keep the existing channel update normal.channelUpdate @@ -274,11 +274,11 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // TODO: maybe also check uniqueness of temporary channel id val minimumDepth = nodeParams.minDepthBlocks val accept = AcceptChannel(temporaryChannelId = open.temporaryChannelId, - dustLimitSatoshis = localParams.dustLimitSatoshis, + dustLimitSatoshis = localParams.dustLimit, maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = localParams.channelReserveSatoshis, + channelReserveSatoshis = localParams.channelReserve, minimumDepth = minimumDepth, - htlcMinimumMsat = localParams.htlcMinimumMsat, + htlcMinimumMsat = localParams.htlcMinimum, toSelfDelay = localParams.toSelfDelay, maxAcceptedHtlcs = localParams.maxAcceptedHtlcs, fundingPubkey = keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, @@ -289,10 +289,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId firstPerCommitmentPoint = keyManager.commitmentPoint(localParams.channelKeyPath, 0)) val remoteParams = RemoteParams( nodeId = remoteNodeId, - dustLimitSatoshis = open.dustLimitSatoshis, + dustLimit = open.dustLimitSatoshis, maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat, - channelReserveSatoshis = open.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment - htlcMinimumMsat = open.htlcMinimumMsat, + channelReserve = open.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment + htlcMinimum = open.htlcMinimumMsat, toSelfDelay = open.toSelfDelay, maxAcceptedHtlcs = open.maxAcceptedHtlcs, fundingPubKey = open.fundingPubkey, @@ -322,10 +322,10 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId // TODO: check equality of temporaryChannelId? or should be done upstream val remoteParams = RemoteParams( nodeId = remoteNodeId, - dustLimitSatoshis = accept.dustLimitSatoshis, + dustLimit = accept.dustLimitSatoshis, maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat, - channelReserveSatoshis = accept.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment - htlcMinimumMsat = accept.htlcMinimumMsat, + channelReserve = accept.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment + htlcMinimum = accept.htlcMinimumMsat, toSelfDelay = accept.toSelfDelay, maxAcceptedHtlcs = accept.maxAcceptedHtlcs, fundingPubKey = accept.fundingPubkey, @@ -338,7 +338,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId log.debug(s"remote params: $remoteParams") val localFundingPubkey = keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey val fundingPubkeyScript = Script.write(Script.pay2wsh(Scripts.multiSig2of2(localFundingPubkey, remoteParams.fundingPubKey))) - wallet.makeFundingTx(fundingPubkeyScript, Satoshi(fundingSatoshis), fundingTxFeeratePerKw).pipeTo(self) + wallet.makeFundingTx(fundingPubkeyScript, fundingSatoshis, fundingTxFeeratePerKw).pipeTo(self) goto(WAIT_FOR_FUNDING_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, accept.firstPerCommitmentPoint, open) } @@ -360,9 +360,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId }) when(WAIT_FOR_FUNDING_INTERNAL)(handleExceptions { - case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, open)) => + case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, open)) => // let's create the first commitment tx that spends the yet uncommitted funding tx - val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) + val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) require(fundingTx.txOut(fundingTxOutputIndex).publicKeyScript == localCommitTx.input.txOut.publicKeyScript, s"pubkey script mismatch!") val localSigOfRemoteTx = keyManager.sign(remoteCommitTx, keyManager.fundingPublicKey(localParams.channelKeyPath)) // signature of their initial commitment tx that pays remote pushMsat @@ -401,9 +401,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId }) when(WAIT_FOR_FUNDING_CREATED)(handleExceptions { - case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelFlags, _)) => + case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelFlags, _)) => // they fund the channel with their funding tx, so the money is theirs (but we are paid pushMsat) - val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingSatoshis: Long, pushMsat, initialFeeratePerKw, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) + val (localSpec, localCommitTx, remoteSpec, remoteCommitTx) = Funding.makeFirstCommitTxs(keyManager, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint, nodeParams.onChainFeeConf.maxFeerateMismatch) // check remote signature validity val localSigOfLocalTx = keyManager.sign(localCommitTx, keyManager.fundingPublicKey(localParams.channelKeyPath)) @@ -561,7 +561,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId blockchain ! WatchConfirmed(self, commitments.commitInput.outPoint.txid, commitments.commitInput.txOut.publicKeyScript, ANNOUNCEMENTS_MINCONF, BITCOIN_FUNDING_DEEPLYBURIED) context.system.eventStream.publish(ShortChannelIdAssigned(self, commitments.channelId, shortChannelId)) // we create a channel_update early so that we can use it to send payments through this channel, but it won't be propagated to other nodes since the channel is not yet announced - val initialChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, shortChannelId, nodeParams.expiryDeltaBlocks, d.commitments.remoteParams.htlcMinimumMsat, nodeParams.feeBaseMsat, nodeParams.feeProportionalMillionth, commitments.localCommit.spec.totalFunds, enable = Helpers.aboveReserve(d.commitments)) + val initialChannelUpdate = Announcements.makeChannelUpdate(nodeParams.chainHash, nodeParams.privateKey, remoteNodeId, shortChannelId, nodeParams.expiryDeltaBlocks, d.commitments.remoteParams.htlcMinimum, nodeParams.feeBase, nodeParams.feeProportionalMillionth, commitments.localCommit.spec.totalFunds, enable = Helpers.aboveReserve(d.commitments)) // we need to periodically re-send channel updates, otherwise channel will be considered stale and get pruned by network context.system.scheduler.schedule(initialDelay = REFRESH_CHANNEL_UPDATE_INTERVAL, interval = REFRESH_CHANNEL_UPDATE_INTERVAL, receiver = self, message = BroadcastChannelUpdate(PeriodicRefresh)) goto(NORMAL) using DATA_NORMAL(commitments.copy(remoteNextCommitInfo = Right(nextPerCommitmentPoint)), shortChannelId, buried = false, None, initialChannelUpdate, None, None) storing() @@ -704,7 +704,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId val nextCommitNumber = nextRemoteCommit.index // we persist htlc data in order to be able to claim htlc outputs in case a revoked tx is published by our // counterparty, so only htlcs above remote's dust_limit matter - val trimmedHtlcs = Transactions.trimOfferedHtlcs(Satoshi(d.commitments.remoteParams.dustLimitSatoshis), nextRemoteCommit.spec) ++ Transactions.trimReceivedHtlcs(Satoshi(d.commitments.remoteParams.dustLimitSatoshis), nextRemoteCommit.spec) + val trimmedHtlcs = Transactions.trimOfferedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec) ++ Transactions.trimReceivedHtlcs(d.commitments.remoteParams.dustLimit, nextRemoteCommit.spec) trimmedHtlcs collect { case DirectedHtlc(_, u) => log.info(s"adding paymentHash=${u.paymentHash} cltvExpiry=${u.cltvExpiry} to htlcs db for commitNumber=$nextCommitNumber") @@ -716,9 +716,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId self ! BroadcastChannelUpdate(AboveReserve) } context.system.eventStream.publish(ChannelSignatureSent(self, commitments1)) - if (nextRemoteCommit.spec.toRemoteMsat != d.commitments.remoteCommit.spec.toRemoteMsat) { + if (nextRemoteCommit.spec.toRemote != d.commitments.remoteCommit.spec.toRemote) { // we send this event only when our balance changes (note that remoteCommit.toRemote == toLocal) - context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortChannelId, nextRemoteCommit.spec.toRemoteMsat, commitments1)) + context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortChannelId, nextRemoteCommit.spec.toRemote, commitments1)) } // we expect a quick response from our peer setTimer(RevocationTimeout.toString, RevocationTimeout(commitments1.remoteCommit.index, peer = context.parent), timeout = nodeParams.revocationTimeout, repeat = false) @@ -1159,21 +1159,21 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId when(NEGOTIATING)(handleExceptions { case Event(c@ClosingSigned(_, remoteClosingFee, remoteSig), d: DATA_NEGOTIATING) => log.info(s"received closingFeeSatoshis=$remoteClosingFee") - Closing.checkClosingSignature(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(remoteClosingFee), remoteSig) match { + Closing.checkClosingSignature(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, remoteClosingFee, remoteSig) match { case Success(signedClosingTx) if d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis).contains(remoteClosingFee) || d.closingTxProposed.flatten.size >= MAX_NEGOTIATION_ITERATIONS => // we close when we converge or when there were too many iterations handleMutualClose(signedClosingTx, Left(d.copy(bestUnpublishedClosingTx_opt = Some(signedClosingTx)))) case Success(signedClosingTx) => // if we are fundee and we were waiting for them to send their first closing_signed, we don't have a lastLocalClosingFee, so we compute a firstClosingFee - val lastLocalClosingFee = d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis).map(Satoshi) + val lastLocalClosingFee = d.closingTxProposed.last.lastOption.map(_.localClosingSigned.feeSatoshis) val nextClosingFee = Closing.nextClosingFee( localClosingFee = lastLocalClosingFee.getOrElse(Closing.firstClosingFee(d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.onChainFeeConf.feeEstimator, nodeParams.onChainFeeConf.feeTargets)), - remoteClosingFee = Satoshi(remoteClosingFee)) + remoteClosingFee = remoteClosingFee) val (closingTx, closingSigned) = Closing.makeClosingTx(keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nextClosingFee) if (lastLocalClosingFee.contains(nextClosingFee)) { // next computed fee is the same than the one we previously sent (probably because of rounding), let's close now handleMutualClose(signedClosingTx, Left(d.copy(bestUnpublishedClosingTx_opt = Some(signedClosingTx)))) - } else if (nextClosingFee == Satoshi(remoteClosingFee)) { + } else if (nextClosingFee == remoteClosingFee) { // we have converged! val closingTxProposed1 = d.closingTxProposed match { case previousNegotiations :+ currentNegotiation => previousNegotiations :+ (currentNegotiation :+ ClosingTxProposed(closingTx.tx, closingSigned)) @@ -1310,9 +1310,9 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId } // we may need to fail some htlcs in case a commitment tx was published and they have reached the timeout threshold val timedoutHtlcs = - Closing.timedoutHtlcs(d.commitments.localCommit, Satoshi(d.commitments.localParams.dustLimitSatoshis), tx) ++ - Closing.timedoutHtlcs(d.commitments.remoteCommit, Satoshi(d.commitments.remoteParams.dustLimitSatoshis), tx) ++ - d.commitments.remoteNextCommitInfo.left.toSeq.flatMap(r => Closing.timedoutHtlcs(r.nextRemoteCommit, Satoshi(d.commitments.remoteParams.dustLimitSatoshis), tx)) + Closing.timedoutHtlcs(d.commitments.localCommit, d.commitments.localParams.dustLimit, tx) ++ + Closing.timedoutHtlcs(d.commitments.remoteCommit, d.commitments.remoteParams.dustLimit, tx) ++ + d.commitments.remoteNextCommitInfo.left.toSeq.flatMap(r => Closing.timedoutHtlcs(r.nextRemoteCommit, d.commitments.remoteParams.dustLimit, tx)) timedoutHtlcs.foreach { add => d.commitments.originChannels.get(add.id) match { case Some(origin) => @@ -1339,7 +1339,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId Closing .onchainOutgoingHtlcs(d.commitments.localCommit, d.commitments.remoteCommit, d.commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit), tx) .map(add => (add, d.commitments.originChannels.get(add.id).collect { case Local(id, _) => id })) // we resolve the payment id if this was a local payment - .collect { case (add, Some(id)) => context.system.eventStream.publish(PaymentSettlingOnChain(id, amount = MilliSatoshi(add.amountMsat), add.paymentHash)) } + .collect { case (add, Some(id)) => context.system.eventStream.publish(PaymentSettlingOnChain(id, amount = add.amountMsat, add.paymentHash)) } // we update the channel data val d1 = d.copy(localCommitPublished = localCommitPublished1, remoteCommitPublished = remoteCommitPublished1, nextRemoteCommitPublished = nextRemoteCommitPublished1, futureRemoteCommitPublished = futureRemoteCommitPublished1, revokedCommitPublished = revokedCommitPublished1) // and we also send events related to fee @@ -2207,7 +2207,7 @@ class Channel(val nodeParams: NodeParams, val wallet: EclairWallet, remoteNodeId def origin(c: CMD_ADD_HTLC): Origin = c.upstream match { case Left(id) => Local(id, Some(sender)) // we were the origin of the payment - case Right(u) => Relayed(u.channelId, u.id, u.amountMsat, c.amountMsat) // this is a relayed payment + case Right(u) => Relayed(u.channelId, u.id, u.amountMsat, c.amount) // this is a relayed payment } def feePaid(fee: Satoshi, tx: Transaction, desc: String, channelId: ByteVector32): Unit = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala index e9104a7db4..e80ff51eca 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelEvents.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.channel import akka.actor.ActorRef import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} +import fr.acinq.eclair.api.MilliSatoshiSerializer import fr.acinq.eclair.channel.Channel.ChannelError import fr.acinq.eclair.channel.Helpers.Closing.ClosingType import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate} @@ -53,7 +54,7 @@ case class ChannelErrorOccured(channel: ActorRef, channelId: ByteVector32, remot case class NetworkFeePaid(channel: ActorRef, remoteNodeId: PublicKey, channelId: ByteVector32, tx: Transaction, fee: Satoshi, txType: String) extends ChannelEvent // NB: this event is only sent when the channel is available -case class AvailableBalanceChanged(channel: ActorRef, channelId: ByteVector32, shortChannelId: ShortChannelId, localBalanceMsat: Long, commitments: Commitments) extends ChannelEvent +case class AvailableBalanceChanged(channel: ActorRef, channelId: ByteVector32, shortChannelId: ShortChannelId, localBalance: MilliSatoshi, commitments: Commitments) extends ChannelEvent case class ChannelPersisted(channel: ActorRef, remoteNodeId: PublicKey, channelId: ByteVector32, data: Data) extends ChannelEvent diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala index 3546f22af6..56fc890eb5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelExceptions.scala @@ -17,8 +17,8 @@ package fr.acinq.eclair.channel import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{ByteVector32, Transaction} -import fr.acinq.eclair.UInt64 +import fr.acinq.bitcoin.{ByteVector32, Satoshi, Transaction} +import fr.acinq.eclair.{MilliSatoshi, UInt64} import fr.acinq.eclair.payment.Origin import fr.acinq.eclair.wire.{ChannelUpdate, UpdateAddHtlc} @@ -31,16 +31,16 @@ class ChannelException(val channelId: ByteVector32, message: String) extends Run // @formatter:off case class DebugTriggeredException (override val channelId: ByteVector32) extends ChannelException(channelId, "debug-mode triggered failure") case class InvalidChainHash (override val channelId: ByteVector32, local: ByteVector32, remote: ByteVector32) extends ChannelException(channelId, s"invalid chainHash (local=$local remote=$remote)") -case class InvalidFundingAmount (override val channelId: ByteVector32, fundingSatoshis: Long, min: Long, max: Long) extends ChannelException(channelId, s"invalid funding_satoshis=$fundingSatoshis (min=$min max=$max)") -case class InvalidPushAmount (override val channelId: ByteVector32, pushMsat: Long, max: Long) extends ChannelException(channelId, s"invalid pushMsat=$pushMsat (max=$max)") +case class InvalidFundingAmount (override val channelId: ByteVector32, fundingAmount: Satoshi, min: Satoshi, max: Satoshi) extends ChannelException(channelId, s"invalid funding_satoshis=$fundingAmount (min=$min max=$max)") +case class InvalidPushAmount (override val channelId: ByteVector32, pushAmount: MilliSatoshi, max: MilliSatoshi) extends ChannelException(channelId, s"invalid pushAmount=$pushAmount (max=$max)") case class InvalidMaxAcceptedHtlcs (override val channelId: ByteVector32, maxAcceptedHtlcs: Int, max: Int) extends ChannelException(channelId, s"invalid max_accepted_htlcs=$maxAcceptedHtlcs (max=$max)") -case class DustLimitTooSmall (override val channelId: ByteVector32, dustLimitSatoshis: Long, min: Long) extends ChannelException(channelId, s"dustLimitSatoshis=$dustLimitSatoshis is too small (min=$min)") -case class DustLimitTooLarge (override val channelId: ByteVector32, dustLimitSatoshis: Long, max: Long) extends ChannelException(channelId, s"dustLimitSatoshis=$dustLimitSatoshis is too large (max=$max)") -case class DustLimitAboveOurChannelReserve (override val channelId: ByteVector32, dustLimitSatoshis: Long, channelReserveSatoshis: Long) extends ChannelException(channelId, s"dustLimitSatoshis dustLimitSatoshis=$dustLimitSatoshis is above our channelReserveSatoshis=$channelReserveSatoshis") +case class DustLimitTooSmall (override val channelId: ByteVector32, dustLimit: Satoshi, min: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is too small (min=$min)") +case class DustLimitTooLarge (override val channelId: ByteVector32, dustLimit: Satoshi, max: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is too large (max=$max)") +case class DustLimitAboveOurChannelReserve (override val channelId: ByteVector32, dustLimit: Satoshi, channelReserve: Satoshi) extends ChannelException(channelId, s"dustLimit=$dustLimit is above our channelReserve=$channelReserve") case class ToSelfDelayTooHigh (override val channelId: ByteVector32, toSelfDelay: Int, max: Int) extends ChannelException(channelId, s"unreasonable to_self_delay=$toSelfDelay (max=$max)") -case class ChannelReserveTooHigh (override val channelId: ByteVector32, channelReserveSatoshis: Long, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double) extends ChannelException(channelId, s"channelReserveSatoshis too high: reserve=$channelReserveSatoshis fundingRatio=$reserveToFundingRatio maxFundingRatio=$maxReserveToFundingRatio") -case class ChannelReserveBelowOurDustLimit (override val channelId: ByteVector32, channelReserveSatoshis: Long, dustLimitSatoshis: Long) extends ChannelException(channelId, s"their channelReserveSatoshis=$channelReserveSatoshis is below our dustLimitSatoshis=$dustLimitSatoshis") -case class ChannelReserveNotMet (override val channelId: ByteVector32, toLocalMsat: Long, toRemoteMsat: Long, reserveSatoshis: Long) extends ChannelException(channelId, s"channel reserve is not met toLocalMsat=$toLocalMsat toRemoteMsat=$toRemoteMsat reserveSat=$reserveSatoshis") +case class ChannelReserveTooHigh (override val channelId: ByteVector32, channelReserve: Satoshi, reserveToFundingRatio: Double, maxReserveToFundingRatio: Double) extends ChannelException(channelId, s"channelReserve too high: reserve=$channelReserve fundingRatio=$reserveToFundingRatio maxFundingRatio=$maxReserveToFundingRatio") +case class ChannelReserveBelowOurDustLimit (override val channelId: ByteVector32, channelReserve: Satoshi, dustLimit: Satoshi) extends ChannelException(channelId, s"their channelReserve=$channelReserve is below our dustLimit=$dustLimit") +case class ChannelReserveNotMet (override val channelId: ByteVector32, toLocal: MilliSatoshi, toRemote: MilliSatoshi, reserve: Satoshi) extends ChannelException(channelId, s"channel reserve is not met toLocal=$toLocal toRemote=$toRemote reserve=$reserve") case class ChannelFundingError (override val channelId: ByteVector32) extends ChannelException(channelId, "channel funding error") case class NoMoreHtlcsClosingInProgress (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot send new htlcs, closing in progress") case class ClosingAlreadyInProgress (override val channelId: ByteVector32) extends ChannelException(channelId, "closing already in progress") @@ -57,21 +57,21 @@ case class FeerateTooDifferent (override val channelId: ByteVect case class InvalidCommitmentSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid commitment signature: tx=$tx") case class InvalidHtlcSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid htlc signature: tx=$tx") case class InvalidCloseSignature (override val channelId: ByteVector32, tx: Transaction) extends ChannelException(channelId, s"invalid close signature: tx=$tx") -case class InvalidCloseFee (override val channelId: ByteVector32, feeSatoshi: Long) extends ChannelException(channelId, s"invalid close fee: fee_satoshis=$feeSatoshi") +case class InvalidCloseFee (override val channelId: ByteVector32, fee: Satoshi) extends ChannelException(channelId, s"invalid close fee: fee_satoshis=$fee") case class HtlcSigCountMismatch (override val channelId: ByteVector32, expected: Int, actual: Int) extends ChannelException(channelId, s"htlc sig count mismatch: expected=$expected actual: $actual") case class ForcedLocalCommit (override val channelId: ByteVector32) extends ChannelException(channelId, s"forced local commit") case class UnexpectedHtlcId (override val channelId: ByteVector32, expected: Long, actual: Long) extends ChannelException(channelId, s"unexpected htlc id: expected=$expected actual=$actual") case class ExpiryTooSmall (override val channelId: ByteVector32, minimum: Long, actual: Long, blockCount: Long) extends ChannelException(channelId, s"expiry too small: minimum=$minimum actual=$actual blockCount=$blockCount") case class ExpiryTooBig (override val channelId: ByteVector32, maximum: Long, actual: Long, blockCount: Long) extends ChannelException(channelId, s"expiry too big: maximum=$maximum actual=$actual blockCount=$blockCount") -case class HtlcValueTooSmall (override val channelId: ByteVector32, minimum: Long, actual: Long) extends ChannelException(channelId, s"htlc value too small: minimum=$minimum actual=$actual") +case class HtlcValueTooSmall (override val channelId: ByteVector32, minimum: MilliSatoshi, actual: MilliSatoshi) extends ChannelException(channelId, s"htlc value too small: minimum=$minimum actual=$actual") case class HtlcValueTooHighInFlight (override val channelId: ByteVector32, maximum: UInt64, actual: UInt64) extends ChannelException(channelId, s"in-flight htlcs hold too much value: maximum=$maximum actual=$actual") case class TooManyAcceptedHtlcs (override val channelId: ByteVector32, maximum: Long) extends ChannelException(channelId, s"too many accepted htlcs: maximum=$maximum") -case class InsufficientFunds (override val channelId: ByteVector32, amountMsat: Long, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"insufficient funds: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis fees=$feesSatoshis") +case class InsufficientFunds (override val channelId: ByteVector32, amount: MilliSatoshi, missing: Satoshi, reserve: Satoshi, fees: Satoshi) extends ChannelException(channelId, s"insufficient funds: missing=$missing reserve=$reserve fees=$fees") case class InvalidHtlcPreimage (override val channelId: ByteVector32, id: Long) extends ChannelException(channelId, s"invalid htlc preimage for htlc id=$id") case class UnknownHtlcId (override val channelId: ByteVector32, id: Long) extends ChannelException(channelId, s"unknown htlc id=$id") case class CannotExtractSharedSecret (override val channelId: ByteVector32, htlc: UpdateAddHtlc) extends ChannelException(channelId, s"can't extract shared secret: paymentHash=${htlc.paymentHash} onion=${htlc.onionRoutingPacket}") case class FundeeCannotSendUpdateFee (override val channelId: ByteVector32) extends ChannelException(channelId, s"only the funder should send update_fee messages") -case class CannotAffordFees (override val channelId: ByteVector32, missingSatoshis: Long, reserveSatoshis: Long, feesSatoshis: Long) extends ChannelException(channelId, s"can't pay the fee: missingSatoshis=$missingSatoshis reserveSatoshis=$reserveSatoshis feesSatoshis=$feesSatoshis") +case class CannotAffordFees (override val channelId: ByteVector32, missing: Satoshi, reserve: Satoshi, fees: Satoshi) extends ChannelException(channelId, s"can't pay the fee: missing=$missing reserve=$reserve fees=$fees") case class CannotSignWithoutChanges (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot sign when there are no changes") case class CannotSignBeforeRevocation (override val channelId: ByteVector32) extends ChannelException(channelId, "cannot sign until next revocation hash is received") case class UnexpectedRevocation (override val channelId: ByteVector32) extends ChannelException(channelId, "received unexpected RevokeAndAck message") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala index 48e6d6b57f..151cf582d5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelTypes.scala @@ -21,10 +21,11 @@ import java.util.UUID import akka.actor.ActorRef import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, Transaction} +import fr.acinq.eclair.api.MilliSatoshiSerializer import fr.acinq.eclair.transactions.CommitmentSpec import fr.acinq.eclair.transactions.Transactions.CommitTx import fr.acinq.eclair.wire.{AcceptChannel, ChannelAnnouncement, ChannelReestablish, ChannelUpdate, ClosingSigned, FailureMessage, FundingCreated, FundingLocked, FundingSigned, Init, OnionRoutingPacket, OpenChannel, Shutdown, UpdateAddHtlc} -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import scodec.bits.{BitVector, ByteVector} @@ -75,7 +76,7 @@ case object ERR_INFORMATION_LEAK extends State 8888888888 Y8P 8888888888 888 Y888 888 "Y8888P" */ -case class INPUT_INIT_FUNDER(temporaryChannelId: ByteVector32, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, fundingTxFeeratePerKw: Long, localParams: LocalParams, remote: ActorRef, remoteInit: Init, channelFlags: Byte) +case class INPUT_INIT_FUNDER(temporaryChannelId: ByteVector32, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, fundingTxFeeratePerKw: Long, localParams: LocalParams, remote: ActorRef, remoteInit: Init, channelFlags: Byte) case class INPUT_INIT_FUNDEE(temporaryChannelId: ByteVector32, localParams: LocalParams, remote: ActorRef, remoteInit: Init) case object INPUT_CLOSE_COMPLETE_TIMEOUT // when requesting a mutual close, we wait for as much as this timeout, then unilateral close case object INPUT_DISCONNECTED @@ -106,14 +107,14 @@ case class BITCOIN_PARENT_TX_CONFIRMED(childTx: Transaction) extends BitcoinEven */ sealed trait Command -final case class CMD_ADD_HTLC(amountMsat: Long, paymentHash: ByteVector32, cltvExpiry: Long, onion: OnionRoutingPacket, upstream: Either[UUID, UpdateAddHtlc], commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command +final case class CMD_ADD_HTLC(amount: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: Long, onion: OnionRoutingPacket, upstream: Either[UUID, UpdateAddHtlc], commit: Boolean = false, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends Command final case class CMD_FULFILL_HTLC(id: Long, r: ByteVector32, commit: Boolean = false) extends Command final case class CMD_FAIL_HTLC(id: Long, reason: Either[ByteVector, FailureMessage], commit: Boolean = false) extends Command final case class CMD_FAIL_MALFORMED_HTLC(id: Long, onionHash: ByteVector32, failureCode: Int, commit: Boolean = false) extends Command final case class CMD_UPDATE_FEE(feeratePerKw: Long, commit: Boolean = false) extends Command final case object CMD_SIGN extends Command final case class CMD_CLOSE(scriptPubKey: Option[ByteVector]) extends Command -final case class CMD_UPDATE_RELAY_FEE(feeBaseMsat: Long, feeProportionalMillionths: Long) extends Command +final case class CMD_UPDATE_RELAY_FEE(feeBase: MilliSatoshi, feeProportionalMillionths: Long) extends Command final case object CMD_FORCECLOSE extends Command final case object CMD_GETSTATE extends Command final case object CMD_GETSTATEDATA extends Command @@ -148,8 +149,8 @@ case class RevokedCommitPublished(commitTx: Transaction, claimMainOutputTx: Opti final case class DATA_WAIT_FOR_OPEN_CHANNEL(initFundee: INPUT_INIT_FUNDEE) extends Data final case class DATA_WAIT_FOR_ACCEPT_CHANNEL(initFunder: INPUT_INIT_FUNDER, lastSent: OpenChannel) extends Data -final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, lastSent: OpenChannel) extends Data -final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, channelFlags: Byte, lastSent: AcceptChannel) extends Data +final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, lastSent: OpenChannel) extends Data +final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushAmount: MilliSatoshi, initialFeeratePerKw: Long, remoteFirstPerCommitmentPoint: PublicKey, channelFlags: Byte, lastSent: AcceptChannel) extends Data final case class DATA_WAIT_FOR_FUNDING_SIGNED(channelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingTx: Transaction, fundingTxFee: Satoshi, localSpec: CommitmentSpec, localCommitTx: CommitTx, remoteCommit: RemoteCommit, channelFlags: Byte, lastSent: FundingCreated) extends Data final case class DATA_WAIT_FOR_FUNDING_CONFIRMED(commitments: Commitments, fundingTx: Option[Transaction], @@ -191,10 +192,10 @@ final case class DATA_WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT(commitments: Com final case class LocalParams(nodeId: PublicKey, channelKeyPath: DeterministicWallet.KeyPath, - dustLimitSatoshis: Long, + dustLimit: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserve: Satoshi, + htlcMinimum: MilliSatoshi, toSelfDelay: Int, maxAcceptedHtlcs: Int, isFunder: Boolean, @@ -203,10 +204,10 @@ final case class LocalParams(nodeId: PublicKey, localFeatures: ByteVector) final case class RemoteParams(nodeId: PublicKey, - dustLimitSatoshis: Long, + dustLimit: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserve: Satoshi, + htlcMinimum: MilliSatoshi, toSelfDelay: Int, maxAcceptedHtlcs: Int, fundingPubKey: PublicKey, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala index 7c1bace272..21533f0dba 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -19,13 +19,15 @@ package fr.acinq.eclair.channel import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256} import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi} +import fr.acinq.eclair +import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets} import fr.acinq.eclair.crypto.{Generators, KeyManager, ShaChain, Sphinx} import fr.acinq.eclair.payment._ import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.transactions._ import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, UInt64} +import fr.acinq.eclair.{Globals, MilliSatoshi, UInt64} // @formatter:off case class LocalChanges(proposed: List[UpdateMessage], signed: List[UpdateMessage], acked: List[UpdateMessage]) { @@ -84,16 +86,16 @@ case class Commitments(channelVersion: ChannelVersion, val announceChannel: Boolean = (channelFlags & 0x01) != 0 - lazy val availableBalanceForSendMsat: Long = { + lazy val availableBalanceForSend: MilliSatoshi = { val reduced = CommitmentSpec.reduce(remoteCommit.spec, remoteChanges.acked, localChanges.proposed) - val feesMsat = if (localParams.isFunder) Transactions.commitTxFee(Satoshi(remoteParams.dustLimitSatoshis), reduced).amount * 1000 else 0 - math.max(reduced.toRemoteMsat - remoteParams.channelReserveSatoshis * 1000 - feesMsat, 0) + val feesMsat = if (localParams.isFunder) commitTxFee(remoteParams.dustLimit, reduced).toMilliSatoshi else MilliSatoshi(0) + maxOf(reduced.toRemote - remoteParams.channelReserve.toMilliSatoshi - feesMsat, MilliSatoshi(0)) } - lazy val availableBalanceForReceiveMsat: Long = { + lazy val availableBalanceForReceive: MilliSatoshi = { val reduced = CommitmentSpec.reduce(localCommit.spec, localChanges.acked, remoteChanges.proposed) - val feesMsat = if (localParams.isFunder) 0 else Transactions.commitTxFee(Satoshi(localParams.dustLimitSatoshis), reduced).amount * 1000 - math.max(reduced.toRemoteMsat - localParams.channelReserveSatoshis * 1000 - feesMsat, 0) + val feesMsat = if (localParams.isFunder) MilliSatoshi(0) else commitTxFee(localParams.dustLimit, reduced).toMilliSatoshi + maxOf(reduced.toRemote - localParams.channelReserve.toMilliSatoshi - feesMsat, MilliSatoshi(0)) } } @@ -132,12 +134,12 @@ object Commitments { return Left(ExpiryTooBig(commitments.channelId, maximum = maxExpiry, actual = cmd.cltvExpiry, blockCount = blockCount)) } - if (cmd.amountMsat < commitments.remoteParams.htlcMinimumMsat) { - return Left(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimumMsat, actual = cmd.amountMsat)) + if (cmd.amount < commitments.remoteParams.htlcMinimum) { + return Left(HtlcValueTooSmall(commitments.channelId, minimum = commitments.remoteParams.htlcMinimum, actual = cmd.amount)) } // let's compute the current commitment *as seen by them* with this change taken into account - val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add = UpdateAddHtlc(commitments.channelId, commitments.localNextHtlcId, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) // we increment the local htlc index and add an entry to the origins map val commitments1 = addLocalProposal(commitments, add).copy(localNextHtlcId = commitments.localNextHtlcId + 1, originChannels = commitments.originChannels + (add.id -> origin)) // we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation @@ -146,7 +148,7 @@ object Commitments { // the HTLC we are about to create is outgoing, but from their point of view it is incoming val outgoingHtlcs = reduced.htlcs.filter(_.direction == IN) - val htlcValueInFlight = UInt64(outgoingHtlcs.map(_.add.amountMsat).sum) + val htlcValueInFlight = UInt64(outgoingHtlcs.map(_.add.amountMsat).sum.toLong) if (htlcValueInFlight > commitments1.remoteParams.maxHtlcValueInFlightMsat) { // TODO: this should be a specific UPDATE error return Left(HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.remoteParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight)) @@ -158,10 +160,10 @@ object Commitments { // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee // we look from remote's point of view, so if local is funder remote doesn't pay the fees - val fees = if (commitments1.localParams.isFunder) Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount else 0 - val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees - if (missing < 0) { - return Left(InsufficientFunds(commitments.channelId, amountMsat = cmd.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.remoteParams.channelReserveSatoshis, feesSatoshis = fees)) + val fees = if (commitments1.localParams.isFunder) commitTxFee(commitments1.remoteParams.dustLimit, reduced) else Satoshi(0) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteParams.channelReserve - fees + if (missing < Satoshi(0)) { + return Left(InsufficientFunds(commitments.channelId, amount = cmd.amount, missing = -missing, reserve = commitments1.remoteParams.channelReserve, fees = fees)) } Right(commitments1, add) @@ -172,8 +174,8 @@ object Commitments { throw UnexpectedHtlcId(commitments.channelId, expected = commitments.remoteNextHtlcId, actual = add.id) } - if (add.amountMsat < commitments.localParams.htlcMinimumMsat) { - throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimumMsat, actual = add.amountMsat) + if (add.amountMsat < commitments.localParams.htlcMinimum) { + throw HtlcValueTooSmall(commitments.channelId, minimum = commitments.localParams.htlcMinimum, actual = add.amountMsat) } // let's compute the current commitment *as seen by us* including this change @@ -181,7 +183,7 @@ object Commitments { val reduced = CommitmentSpec.reduce(commitments1.localCommit.spec, commitments1.localChanges.acked, commitments1.remoteChanges.proposed) val incomingHtlcs = reduced.htlcs.filter(_.direction == IN) - val htlcValueInFlight = UInt64(incomingHtlcs.map(_.add.amountMsat).sum) + val htlcValueInFlight = UInt64(incomingHtlcs.map(_.add.amountMsat).sum.toLong) if (htlcValueInFlight > commitments1.localParams.maxHtlcValueInFlightMsat) { throw HtlcValueTooHighInFlight(commitments.channelId, maximum = commitments1.localParams.maxHtlcValueInFlightMsat, actual = htlcValueInFlight) } @@ -191,10 +193,10 @@ object Commitments { } // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee - val fees = if (commitments1.localParams.isFunder) 0 else Transactions.commitTxFee(Satoshi(commitments1.localParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw InsufficientFunds(commitments.channelId, amountMsat = add.amountMsat, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = if (commitments1.localParams.isFunder) Satoshi(0) else Transactions.commitTxFee(commitments1.localParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw InsufficientFunds(commitments.channelId, amount = add.amountMsat, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } commitments1 @@ -313,10 +315,10 @@ object Commitments { // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee // we look from remote's point of view, so if local is funder remote doesn't pay the fees - val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.remoteParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } (commitments1, fee) @@ -347,10 +349,10 @@ object Commitments { val reduced = CommitmentSpec.reduce(commitments1.localCommit.spec, commitments1.localChanges.acked, commitments1.remoteChanges.proposed) // a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee - val fees = Transactions.commitTxFee(Satoshi(commitments1.remoteParams.dustLimitSatoshis), reduced).amount - val missing = reduced.toRemoteMsat / 1000 - commitments1.localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(commitments.channelId, missingSatoshis = -1 * missing, reserveSatoshis = commitments1.localParams.channelReserveSatoshis, feesSatoshis = fees) + val fees = commitTxFee(commitments1.remoteParams.dustLimit, reduced) + val missing = reduced.toRemote.truncateToSatoshi - commitments1.localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees) } commitments1 @@ -527,8 +529,8 @@ object Commitments { val remotePaymentPubkey = Generators.derivePubKey(remoteParams.paymentBasepoint, localPerCommitmentPoint) val remoteHtlcPubkey = Generators.derivePubKey(remoteParams.htlcBasepoint, localPerCommitmentPoint) val localRevocationPubkey = Generators.revocationPubKey(remoteParams.revocationBasepoint, localPerCommitmentPoint) - val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, remoteParams.paymentBasepoint, localParams.isFunder, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, remotePaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) - val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) + val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, remoteParams.paymentBasepoint, localParams.isFunder, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, remotePaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) + val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPaymentPubkey, localHtlcPubkey, remoteHtlcPubkey, spec) (commitTx, htlcTimeoutTxs, htlcSuccessTxs) } @@ -538,8 +540,8 @@ object Commitments { val remoteDelayedPaymentPubkey = Generators.derivePubKey(remoteParams.delayedPaymentBasepoint, remotePerCommitmentPoint) val remoteHtlcPubkey = Generators.derivePubKey(remoteParams.htlcBasepoint, remotePerCommitmentPoint) val remoteRevocationPubkey = Generators.revocationPubKey(keyManager.revocationPoint(localParams.channelKeyPath).publicKey, remotePerCommitmentPoint) - val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, remoteParams.paymentBasepoint, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, !localParams.isFunder, Satoshi(remoteParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) - val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, Satoshi(remoteParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) + val commitTx = Transactions.makeCommitTx(commitmentInput, commitTxNumber, remoteParams.paymentBasepoint, keyManager.paymentPoint(localParams.channelKeyPath).publicKey, !localParams.isFunder, remoteParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) + val (htlcTimeoutTxs, htlcSuccessTxs) = Transactions.makeHtlcTxs(commitTx.tx, remoteParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, remoteHtlcPubkey, localHtlcPubkey, spec) (commitTx, htlcTimeoutTxs, htlcSuccessTxs) } @@ -574,18 +576,18 @@ object Commitments { def specs2String(commitments: Commitments): String = { s"""specs: |localcommit: - | toLocal: ${commitments.localCommit.spec.toLocalMsat} - | toRemote: ${commitments.localCommit.spec.toRemoteMsat} + | toLocal: ${commitments.localCommit.spec.toLocal} + | toRemote: ${commitments.localCommit.spec.toRemote} | htlcs: |${commitments.localCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")} |remotecommit: - | toLocal: ${commitments.remoteCommit.spec.toLocalMsat} - | toRemote: ${commitments.remoteCommit.spec.toRemoteMsat} + | toLocal: ${commitments.remoteCommit.spec.toLocal} + | toRemote: ${commitments.remoteCommit.spec.toRemote} | htlcs: |${commitments.remoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")} |next remotecommit: - | toLocal: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toLocalMsat).getOrElse("N/A")} - | toRemote: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toRemoteMsat).getOrElse("N/A")} + | toLocal: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toLocal).getOrElse("N/A")} + | toRemote: ${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.toRemote).getOrElse("N/A")} | htlcs: |${commitments.remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit.spec.htlcs.map(h => s" ${h.direction} ${h.add.id} ${h.add.cltvExpiry}").mkString("\n")).getOrElse("N/A")}""".stripMargin } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala index 5cbe075fcb..1f49e73429 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Helpers.scala @@ -20,6 +20,8 @@ import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, ripemd160, sha256} import fr.acinq.bitcoin.Script._ import fr.acinq.bitcoin.{OutPoint, _} +import fr.acinq.eclair +import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.EclairWallet import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets} import fr.acinq.eclair.channel.Channel.REFRESH_CHANNEL_UPDATE_INTERVAL @@ -88,10 +90,10 @@ object Helpers { // BOLT #2: if the chain_hash value, within the open_channel, message is set to a hash of a chain that is unknown to the receiver: // MUST reject the channel. if (nodeParams.chainHash != open.chainHash) throw InvalidChainHash(open.temporaryChannelId, local = nodeParams.chainHash, remote = open.chainHash) - if (open.fundingSatoshis < nodeParams.minFundingSatoshis || open.fundingSatoshis >= Channel.MAX_FUNDING_SATOSHIS) throw InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS) + if (open.fundingSatoshis < nodeParams.minFundingSatoshis || open.fundingSatoshis >= Channel.MAX_FUNDING) throw InvalidFundingAmount(open.temporaryChannelId, open.fundingSatoshis, nodeParams.minFundingSatoshis, Channel.MAX_FUNDING) // BOLT #2: The receiving node MUST fail the channel if: push_msat is greater than funding_satoshis * 1000. - if (open.pushMsat > 1000 * open.fundingSatoshis) throw InvalidPushAmount(open.temporaryChannelId, open.pushMsat, 1000 * open.fundingSatoshis) + if (open.pushMsat > open.fundingSatoshis.toMilliSatoshi) throw InvalidPushAmount(open.temporaryChannelId, open.pushMsat, open.fundingSatoshis.toMilliSatoshi) // BOLT #2: The receiving node MUST fail the channel if: to_self_delay is unreasonably large. if (open.toSelfDelay > Channel.MAX_TO_SELF_DELAY || open.toSelfDelay > nodeParams.maxToLocalDelayBlocks) throw ToSelfDelayTooHigh(open.temporaryChannelId, open.toSelfDelay, nodeParams.maxToLocalDelayBlocks) @@ -107,8 +109,8 @@ object Helpers { // BOLT #2: The receiving node MUST fail the channel if both to_local and to_remote amounts for the initial commitment // transaction are less than or equal to channel_reserve_satoshis (see BOLT 3). - val (toLocalMsat, toRemoteMsat) = (open.pushMsat, open.fundingSatoshis * 1000 - open.pushMsat) - if (toLocalMsat < open.channelReserveSatoshis * 1000 && toRemoteMsat < open.channelReserveSatoshis * 1000) { + val (toLocalMsat, toRemoteMsat) = (open.pushMsat, open.fundingSatoshis.toMilliSatoshi - open.pushMsat) + if (toLocalMsat < open.channelReserveSatoshis.toMilliSatoshi && toRemoteMsat < open.channelReserveSatoshis.toMilliSatoshi) { throw ChannelReserveNotMet(open.temporaryChannelId, toLocalMsat, toRemoteMsat, open.channelReserveSatoshis) } @@ -122,7 +124,7 @@ object Helpers { // we don't check that the funder's amount for the initial commitment transaction is sufficient for full fee payment // now, but it will be done later when we receive `funding_created` - val reserveToFundingRatio = open.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1) + val reserveToFundingRatio = open.channelReserveSatoshis.toLong.toDouble / Math.max(open.fundingSatoshis.toLong, 1) if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, open.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio) } @@ -151,7 +153,7 @@ object Helpers { // MUST reject the channel. Other fields have the same requirements as their counterparts in open_channel. if (open.channelReserveSatoshis < accept.dustLimitSatoshis) throw DustLimitAboveOurChannelReserve(accept.temporaryChannelId, accept.dustLimitSatoshis, open.channelReserveSatoshis) - val reserveToFundingRatio = accept.channelReserveSatoshis.toDouble / Math.max(open.fundingSatoshis, 1) + val reserveToFundingRatio = accept.channelReserveSatoshis.toLong.toDouble / Math.max(open.fundingSatoshis.toLong, 1) if (reserveToFundingRatio > nodeParams.maxReserveToFundingRatio) throw ChannelReserveTooHigh(open.temporaryChannelId, accept.channelReserveSatoshis, reserveToFundingRatio, nodeParams.maxReserveToFundingRatio) } @@ -219,10 +221,10 @@ object Helpers { case Left(waitingForRevocation) => waitingForRevocation.nextRemoteCommit case _ => commitments.remoteCommit } - val toRemoteSatoshis = remoteCommit.spec.toRemoteMsat / 1000 + val toRemoteSatoshis = remoteCommit.spec.toRemote.truncateToSatoshi // NB: this is an approximation (we don't take network fees into account) - val result = toRemoteSatoshis > commitments.remoteParams.channelReserveSatoshis - log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.remoteParams.channelReserveSatoshis} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}") + val result = toRemoteSatoshis > commitments.remoteParams.channelReserve + log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.remoteParams.channelReserve} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}") result } @@ -252,24 +254,24 @@ object Helpers { * @param remoteFirstPerCommitmentPoint * @return (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput) */ - def makeFirstCommitTxs(keyManager: KeyManager, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingSatoshis: Long, pushMsat: Long, initialFeeratePerKw: Long, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey, maxFeerateMismatch: Double): (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx) = { - val toLocalMsat = if (localParams.isFunder) fundingSatoshis * 1000 - pushMsat else pushMsat - val toRemoteMsat = if (localParams.isFunder) pushMsat else fundingSatoshis * 1000 - pushMsat + def makeFirstCommitTxs(keyManager: KeyManager, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushMsat: MilliSatoshi, initialFeeratePerKw: Long, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey, maxFeerateMismatch: Double): (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx) = { + val toLocalMsat = if (localParams.isFunder) fundingAmount.toMilliSatoshi - pushMsat else pushMsat + val toRemoteMsat = if (localParams.isFunder) pushMsat else fundingAmount.toMilliSatoshi - pushMsat - val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocalMsat = toLocalMsat, toRemoteMsat = toRemoteMsat) - val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocalMsat = toRemoteMsat, toRemoteMsat = toLocalMsat) + val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocal = toLocalMsat, toRemote = toRemoteMsat) + val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], feeratePerKw = initialFeeratePerKw, toLocal = toRemoteMsat, toRemote = toLocalMsat) if (!localParams.isFunder) { // they are funder, therefore they pay the fee: we need to make sure they can afford it! - val toRemoteMsat = remoteSpec.toLocalMsat - val fees = Transactions.commitTxFee(Satoshi(remoteParams.dustLimitSatoshis), remoteSpec).amount - val missing = toRemoteMsat / 1000 - localParams.channelReserveSatoshis - fees - if (missing < 0) { - throw CannotAffordFees(temporaryChannelId, missingSatoshis = -1 * missing, reserveSatoshis = localParams.channelReserveSatoshis, feesSatoshis = fees) + val toRemoteMsat = remoteSpec.toLocal + val fees = commitTxFee(remoteParams.dustLimit, remoteSpec) + val missing = toRemoteMsat.truncateToSatoshi - localParams.channelReserve - fees + if (missing < Satoshi(0)) { + throw CannotAffordFees(temporaryChannelId, missing = -missing, reserve = localParams.channelReserve, fees = fees) } } - val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, Satoshi(fundingSatoshis), keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) + val commitmentInput = makeFundingInputInfo(fundingTxHash, fundingTxOutputIndex, fundingAmount, keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) val localPerCommitmentPoint = keyManager.commitmentPoint(localParams.channelKeyPath, 0) val (localCommitTx, _, _) = Commitments.makeLocalTxs(keyManager, 0, localParams, remoteParams, commitmentInput, localPerCommitmentPoint, localSpec) val (remoteCommitTx, _, _) = Commitments.makeRemoteTxs(keyManager, 0, localParams, remoteParams, commitmentInput, remoteFirstPerCommitmentPoint, remoteSpec) @@ -358,9 +360,9 @@ object Helpers { */ def nothingAtStake(data: HasCommitments): Boolean = data.commitments.localCommit.index == 0 && - data.commitments.localCommit.spec.toLocalMsat == 0 && + data.commitments.localCommit.spec.toLocal == MilliSatoshi(0) && data.commitments.remoteCommit.index == 0 && - data.commitments.remoteCommit.spec.toRemoteMsat == 0 && + data.commitments.remoteCommit.spec.toRemote == MilliSatoshi(0) && data.commitments.remoteNextCommitInfo.isRight /** @@ -453,10 +455,10 @@ object Helpers { require(isValidFinalScriptPubkey(remoteScriptPubkey), "invalid remoteScriptPubkey") log.debug(s"making closing tx with closingFee={} and commitments:\n{}", closingFee, Commitments.specs2String(commitments)) // TODO: check that - val dustLimitSatoshis = Satoshi(Math.max(localParams.dustLimitSatoshis, remoteParams.dustLimitSatoshis)) + val dustLimitSatoshis = maxOf(localParams.dustLimit, remoteParams.dustLimit) val closingTx = Transactions.makeClosingTx(commitInput, localScriptPubkey, remoteScriptPubkey, localParams.isFunder, dustLimitSatoshis, closingFee, localCommit.spec) val localClosingSig = keyManager.sign(closingTx, keyManager.fundingPublicKey(commitments.localParams.channelKeyPath)) - val closingSigned = ClosingSigned(channelId, closingFee.amount, localClosingSig) + val closingSigned = ClosingSigned(channelId, closingFee, localClosingSig) log.info(s"signed closing txid=${closingTx.tx.txid} with closingFeeSatoshis=${closingSigned.feeSatoshis}") log.debug(s"closingTxid=${closingTx.tx.txid} closingTx=${closingTx.tx}}") (closingTx, closingSigned) @@ -467,7 +469,7 @@ object Helpers { val lastCommitFeeSatoshi = commitments.commitInput.txOut.amount.amount - commitments.localCommit.publishableTxs.commitTx.tx.txOut.map(_.amount.amount).sum if (remoteClosingFee.amount > lastCommitFeeSatoshi) { log.error(s"remote proposed a commit fee higher than the last commitment fee: remoteClosingFeeSatoshi=${remoteClosingFee.amount} lastCommitFeeSatoshi=$lastCommitFeeSatoshi") - throw InvalidCloseFee(commitments.channelId, remoteClosingFee.amount) + throw InvalidCloseFee(commitments.channelId, remoteClosingFee) } val (closingTx, closingSigned) = makeClosingTx(keyManager, commitments, localScriptPubkey, remoteScriptPubkey, remoteClosingFee) val signedClosingTx = Transactions.addSigs(closingTx, keyManager.fundingPublicKey(commitments.localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey, closingSigned.signature, remoteClosingSig) @@ -508,7 +510,7 @@ object Helpers { // first we will claim our main output as soon as the delay is over val mainDelayedTx = generateTx("main-delayed-output")(Try { - val claimDelayed = Transactions.makeClaimDelayedOutputTx(tx, Satoshi(localParams.dustLimitSatoshis), localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwDelayed) + val claimDelayed = Transactions.makeClaimDelayedOutputTx(tx, localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwDelayed) val sig = keyManager.sign(claimDelayed, keyManager.delayedPaymentPoint(localParams.channelKeyPath), localPerCommitmentPoint) Transactions.addSigs(claimDelayed, sig) }) @@ -539,7 +541,7 @@ object Helpers { generateTx("claim-htlc-delayed")(Try { val claimDelayed = Transactions.makeClaimDelayedOutputTx( txinfo.tx, - Satoshi(localParams.dustLimitSatoshis), + localParams.dustLimit, localRevocationPubkey, remoteParams.toSelfDelay, localDelayedPubkey, @@ -590,7 +592,7 @@ object Helpers { // incoming htlc for which we have the preimage: we spend it directly case DirectedHtlc(OUT, add: UpdateAddHtlc) if preimages.exists(r => sha256(r) == add.paymentHash) => generateTx("claim-htlc-success")(Try { val preimage = preimages.find(r => sha256(r) == add.paymentHash).get - val txinfo = Transactions.makeClaimHtlcSuccessTx(remoteCommitTx.tx, outputsAlreadyUsed, Satoshi(localParams.dustLimitSatoshis), localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) + val txinfo = Transactions.makeClaimHtlcSuccessTx(remoteCommitTx.tx, outputsAlreadyUsed, localParams.dustLimit, localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) outputsAlreadyUsed = outputsAlreadyUsed + txinfo.input.outPoint.index.toInt val sig = keyManager.sign(txinfo, keyManager.htlcPoint(localParams.channelKeyPath), remoteCommit.remotePerCommitmentPoint) Transactions.addSigs(txinfo, sig, preimage) @@ -600,7 +602,7 @@ object Helpers { // outgoing htlc: they may or may not have the preimage, the only thing to do is try to get back our funds after timeout case DirectedHtlc(IN, add: UpdateAddHtlc) => generateTx("claim-htlc-timeout")(Try { - val txinfo = Transactions.makeClaimHtlcTimeoutTx(remoteCommitTx.tx, outputsAlreadyUsed, Satoshi(localParams.dustLimitSatoshis), localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) + val txinfo = Transactions.makeClaimHtlcTimeoutTx(remoteCommitTx.tx, outputsAlreadyUsed, localParams.dustLimit, localHtlcPubkey, remoteHtlcPubkey, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, add, feeratePerKwHtlc) outputsAlreadyUsed = outputsAlreadyUsed + txinfo.input.outPoint.index.toInt val sig = keyManager.sign(txinfo, keyManager.htlcPoint(localParams.channelKeyPath), remoteCommit.remotePerCommitmentPoint) Transactions.addSigs(txinfo, sig) @@ -630,7 +632,7 @@ object Helpers { val feeratePerKwMain = feeEstimator.getFeeratePerKw(feeTargets.claimMainBlockTarget) val mainTx = generateTx("claim-p2wpkh-output")(Try { - val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, Satoshi(commitments.localParams.dustLimitSatoshis), + val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, commitments.localParams.dustLimit, localPubkey, commitments.localParams.defaultFinalScriptPubKey, feeratePerKwMain) val sig = keyManager.sign(claimMain, keyManager.paymentPoint(commitments.localParams.channelKeyPath), remotePerCommitmentPoint) Transactions.addSigs(claimMain, localPubkey, sig) @@ -679,14 +681,14 @@ object Helpers { // first we will claim our main output right away val mainTx = generateTx("claim-p2wpkh-output")(Try { - val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, Satoshi(localParams.dustLimitSatoshis), localPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwMain) + val claimMain = Transactions.makeClaimP2WPKHOutputTx(tx, localParams.dustLimit, localPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwMain) val sig = keyManager.sign(claimMain, keyManager.paymentPoint(localParams.channelKeyPath), remotePerCommitmentPoint) Transactions.addSigs(claimMain, localPubkey, sig) }) // then we punish them by stealing their main output val mainPenaltyTx = generateTx("main-penalty")(Try { - val txinfo = Transactions.makeMainPenaltyTx(tx, Satoshi(localParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePerKwPenalty) + val txinfo = Transactions.makeMainPenaltyTx(tx, localParams.dustLimit, remoteRevocationPubkey, localParams.defaultFinalScriptPubKey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, feeratePerKwPenalty) val sig = keyManager.sign(txinfo, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) Transactions.addSigs(txinfo, sig) }) @@ -706,7 +708,7 @@ object Helpers { val htlcPenaltyTxs = tx.txOut.collect { case txOut if htlcsRedeemScripts.contains(txOut.publicKeyScript) => val htlcRedeemScript = htlcsRedeemScripts(txOut.publicKeyScript) generateTx("htlc-penalty")(Try { - val htlcPenalty = Transactions.makeHtlcPenaltyTx(tx, outputsAlreadyUsed, htlcRedeemScript, Satoshi(localParams.dustLimitSatoshis), localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) + val htlcPenalty = Transactions.makeHtlcPenaltyTx(tx, outputsAlreadyUsed, htlcRedeemScript, localParams.dustLimit, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) outputsAlreadyUsed = outputsAlreadyUsed + htlcPenalty.input.outPoint.index.toInt val sig = keyManager.sign(htlcPenalty, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) Transactions.addSigs(htlcPenalty, sig, remoteRevocationPubkey) @@ -762,7 +764,7 @@ object Helpers { val feeratePerKwPenalty = feeEstimator.getFeeratePerKw(target = 1) generateTx("claim-htlc-delayed-penalty")(Try { - val htlcDelayedPenalty = Transactions.makeClaimDelayedOutputPenaltyTx(htlcTx, Satoshi(localParams.dustLimitSatoshis), remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) + val htlcDelayedPenalty = Transactions.makeClaimDelayedOutputPenaltyTx(htlcTx, localParams.dustLimit, remoteRevocationPubkey, localParams.toSelfDelay, remoteDelayedPaymentPubkey, localParams.defaultFinalScriptPubKey, feeratePerKwPenalty) val sig = keyManager.sign(htlcDelayedPenalty, keyManager.revocationPoint(localParams.channelKeyPath), remotePerCommitmentSecret) val signedTx = Transactions.addSigs(htlcDelayedPenalty, sig) // we need to make sure that the tx is indeed valid diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala index 63dbe4d74f..5fccd441b1 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/AuditDb.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair.db -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.channel._ import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} @@ -51,8 +51,8 @@ trait AuditDb { } -case class ChannelLifecycleEvent(channelId: ByteVector32, remoteNodeId: PublicKey, capacitySat: Long, isFunder: Boolean, isPrivate: Boolean, event: String) +case class ChannelLifecycleEvent(channelId: ByteVector32, remoteNodeId: PublicKey, capacity: Satoshi, isFunder: Boolean, isPrivate: Boolean, event: String) -case class NetworkFee(remoteNodeId: PublicKey, channelId: ByteVector32, txId: ByteVector32, feeSat: Long, txType: String, timestamp: Long) +case class NetworkFee(remoteNodeId: PublicKey, channelId: ByteVector32, txId: ByteVector32, fee: Satoshi, txType: String, timestamp: Long) -case class Stats(channelId: ByteVector32, avgPaymentAmountSatoshi: Long, paymentCount: Int, relayFeeSatoshi: Long, networkFeeSatoshi: Long) +case class Stats(channelId: ByteVector32, avgPaymentAmount: Satoshi, paymentCount: Int, relayFee: Satoshi, networkFee: Satoshi) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala index b13e190446..b9f4aa838f 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/PaymentsDb.scala @@ -17,7 +17,9 @@ package fr.acinq.eclair.db import java.util.UUID + import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.payment.PaymentRequest trait PaymentsDb { @@ -60,10 +62,10 @@ trait PaymentsDb { * Incoming payment object stored in DB. * * @param paymentHash identifier of the payment - * @param amountMsat amount of the payment, in milli-satoshis + * @param amount amount of the payment, in milli-satoshis * @param receivedAt absolute time in seconds since UNIX epoch when the payment was received. */ -case class IncomingPayment(paymentHash: ByteVector32, amountMsat: Long, receivedAt: Long) +case class IncomingPayment(paymentHash: ByteVector32, amount: MilliSatoshi, receivedAt: Long) /** * Sent payment is every payment that is sent by this node, they may not be finalized and @@ -72,12 +74,12 @@ case class IncomingPayment(paymentHash: ByteVector32, amountMsat: Long, received * @param id internal payment identifier * @param paymentHash payment_hash * @param preimage the preimage of the payment_hash, known if the outgoing payment was successful - * @param amountMsat amount of the payment, in milli-satoshis + * @param amount amount of the payment, in milli-satoshis * @param createdAt absolute time in seconds since UNIX epoch when the payment was created. * @param completedAt absolute time in seconds since UNIX epoch when the payment succeeded. * @param status current status of the payment. */ -case class OutgoingPayment(id: UUID, paymentHash: ByteVector32, preimage:Option[ByteVector32], amountMsat: Long, createdAt: Long, completedAt: Option[Long], status: OutgoingPaymentStatus.Value) +case class OutgoingPayment(id: UUID, paymentHash: ByteVector32, preimage:Option[ByteVector32], amount: MilliSatoshi, createdAt: Long, completedAt: Option[Long], status: OutgoingPaymentStatus.Value) object OutgoingPaymentStatus extends Enumeration { val PENDING = Value(1, "PENDING") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala index 519ed96b8d..c03bec2a84 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqliteAuditDb.scala @@ -19,7 +19,8 @@ package fr.acinq.eclair.db.sqlite import java.sql.{Connection, Statement} import java.util.UUID import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.bitcoin.Satoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.channel.{AvailableBalanceChanged, Channel, ChannelErrorOccured, NetworkFeePaid} import fr.acinq.eclair.db.{AuditDb, ChannelLifecycleEvent, NetworkFee, Stats} import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} @@ -83,9 +84,9 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { using(sqlite.prepareStatement("INSERT INTO balance_updated VALUES (?, ?, ?, ?, ?, ?)")) { statement => statement.setBytes(1, e.channelId.toArray) statement.setBytes(2, e.commitments.remoteParams.nodeId.value.toArray) - statement.setLong(3, e.localBalanceMsat) + statement.setLong(3, e.localBalance.toLong) statement.setLong(4, e.commitments.commitInput.txOut.amount.toLong) - statement.setLong(5, e.commitments.remoteParams.channelReserveSatoshis) // remote decides what our reserve should be + statement.setLong(5, e.commitments.remoteParams.channelReserve.toLong) // remote decides what our reserve should be statement.setLong(6, Platform.currentTime) statement.executeUpdate() } @@ -94,7 +95,7 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { using(sqlite.prepareStatement("INSERT INTO channel_events VALUES (?, ?, ?, ?, ?, ?, ?)")) { statement => statement.setBytes(1, e.channelId.toArray) statement.setBytes(2, e.remoteNodeId.value.toArray) - statement.setLong(3, e.capacitySat) + statement.setLong(3, e.capacity.toLong) statement.setBoolean(4, e.isFunder) statement.setBoolean(5, e.isPrivate) statement.setString(6, e.event) @@ -225,7 +226,7 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { remoteNodeId = PublicKey(rs.getByteVector("node_id")), channelId = rs.getByteVector32("channel_id"), txId = rs.getByteVector32("tx_id"), - feeSat = rs.getLong("fee_sat"), + fee = Satoshi(rs.getLong("fee_sat")), txType = rs.getString("tx_type"), timestamp = rs.getLong("timestamp")) } @@ -267,10 +268,10 @@ class SqliteAuditDb(sqlite: Connection) extends AuditDb with Logging { while (rs.next()) { q = q :+ Stats( channelId = rs.getByteVector32("channel_id"), - avgPaymentAmountSatoshi = rs.getLong("avg_payment_amount_sat"), + avgPaymentAmount = Satoshi(rs.getLong("avg_payment_amount_sat")), paymentCount = rs.getInt("payment_count"), - relayFeeSatoshi = rs.getLong("relay_fee_sat"), - networkFeeSatoshi = rs.getLong("network_fee_sat")) + relayFee = Satoshi(rs.getLong("relay_fee_sat")), + networkFee = Satoshi(rs.getLong("network_fee_sat"))) } q } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala index 8fd6ded566..127ec6be2e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/db/sqlite/SqlitePaymentsDb.scala @@ -25,6 +25,7 @@ import fr.acinq.eclair.payment.PaymentRequest import grizzled.slf4j.Logging import scala.collection.immutable.Queue import OutgoingPaymentStatus._ +import fr.acinq.eclair.MilliSatoshi import concurrent.duration._ import scala.compat.Platform @@ -47,7 +48,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { using(sqlite.prepareStatement("INSERT INTO sent_payments (id, payment_hash, amount_msat, created_at, status) VALUES (?, ?, ?, ?, ?)")) { statement => statement.setString(1, sent.id.toString) statement.setBytes(2, sent.paymentHash.toArray) - statement.setLong(3, sent.amountMsat) + statement.setLong(3, sent.amount.toLong) statement.setLong(4, sent.createdAt) statement.setString(5, sent.status.toString) val res = statement.executeUpdate() @@ -76,7 +77,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -97,7 +98,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -116,7 +117,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { UUID.fromString(rs.getString("id")), rs.getByteVector32("payment_hash"), rs.getByteVector32Nullable("preimage"), - rs.getLong("amount_msat"), + MilliSatoshi(rs.getLong("amount_msat")), rs.getLong("created_at"), getNullableLong(rs, "completed_at"), OutgoingPaymentStatus.withName(rs.getString("status")) @@ -194,7 +195,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { override def addIncomingPayment(payment: IncomingPayment): Unit = { using(sqlite.prepareStatement("UPDATE received_payments SET (received_msat, received_at) = (?, ?) WHERE payment_hash = ?")) { statement => - statement.setLong(1, payment.amountMsat) + statement.setLong(1, payment.amount.toLong) statement.setLong(2, payment.receivedAt) statement.setBytes(3, payment.paymentHash.toArray) val res = statement.executeUpdate() @@ -207,7 +208,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { statement.setBytes(1, paymentHash.toArray) val rs = statement.executeQuery() if (rs.next()) { - Some(IncomingPayment(rs.getByteVector32("payment_hash"), rs.getLong("received_msat"), rs.getLong("received_at"))) + Some(IncomingPayment(rs.getByteVector32("payment_hash"), MilliSatoshi(rs.getLong("received_msat")), rs.getLong("received_at"))) } else { None } @@ -219,7 +220,7 @@ class SqlitePaymentsDb(sqlite: Connection) extends PaymentsDb with Logging { val rs = statement.executeQuery("SELECT payment_hash, received_msat, received_at FROM received_payments WHERE received_msat > 0") var q: Queue[IncomingPayment] = Queue() while (rs.next()) { - q = q :+ IncomingPayment(rs.getByteVector32("payment_hash"), rs.getLong("received_msat"), rs.getLong("received_at")) + q = q :+ IncomingPayment(rs.getByteVector32("payment_hash"), MilliSatoshi(rs.getLong("received_msat")), rs.getLong("received_at")) } q } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala index dbc91f208e..3f672d2e71 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala @@ -25,7 +25,8 @@ import akka.event.Logging.MDC import akka.util.Timeout import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, MilliSatoshi, Protocol, Satoshi} +import fr.acinq.bitcoin.{ByteVector32, DeterministicWallet, Protocol, Satoshi} +import fr.acinq.eclair import fr.acinq.eclair.blockchain.EclairWallet import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.TransportHandler @@ -280,20 +281,20 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A stay case Event(c: Peer.OpenChannel, d: ConnectedData) => - val (channel, localParams) = createNewChannel(nodeParams, funder = true, c.fundingSatoshis.toLong, origin_opt = Some(sender)) + val (channel, localParams) = createNewChannel(nodeParams, funder = true, c.fundingSatoshis, origin_opt = Some(sender)) c.timeout_opt.map(openTimeout => context.system.scheduler.scheduleOnce(openTimeout.duration, channel, Channel.TickChannelOpenTimeout)(context.dispatcher)) val temporaryChannelId = randomBytes32 val channelFeeratePerKw = nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKw(target = nodeParams.onChainFeeConf.feeTargets.commitmentBlockTarget) val fundingTxFeeratePerKw = c.fundingTxFeeratePerKw_opt.getOrElse(nodeParams.onChainFeeConf.feeEstimator.getFeeratePerKw(target = nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget)) log.info(s"requesting a new channel with fundingSatoshis=${c.fundingSatoshis}, pushMsat=${c.pushMsat} and fundingFeeratePerByte=${c.fundingTxFeeratePerKw_opt} temporaryChannelId=$temporaryChannelId localParams=$localParams") - channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis.amount, c.pushMsat.amount, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, d.transport, d.remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags)) + channel ! INPUT_INIT_FUNDER(temporaryChannelId, c.fundingSatoshis, c.pushMsat, channelFeeratePerKw, fundingTxFeeratePerKw, localParams, d.transport, d.remoteInit, c.channelFlags.getOrElse(nodeParams.channelFlags)) stay using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel)) case Event(msg: wire.OpenChannel, d: ConnectedData) => d.transport ! TransportHandler.ReadAck(msg) d.channels.get(TemporaryChannelId(msg.temporaryChannelId)) match { case None => - val (channel, localParams) = createNewChannel(nodeParams, funder = false, fundingSatoshis = msg.fundingSatoshis, origin_opt = None) + val (channel, localParams) = createNewChannel(nodeParams, funder = false, fundingAmount = msg.fundingSatoshis, origin_opt = None) val temporaryChannelId = msg.temporaryChannelId log.info(s"accepting a new channel to $remoteNodeId temporaryChannelId=$temporaryChannelId localParams=$localParams") channel ! INPUT_INIT_FUNDEE(temporaryChannelId, localParams, d.transport, d.remoteInit) @@ -523,9 +524,9 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A case DISCONNECTED -> _ if nodeParams.autoReconnect => cancelTimer(RECONNECT_TIMER) } - def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingSatoshis: Long, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = { + def createNewChannel(nodeParams: NodeParams, funder: Boolean, fundingAmount: Satoshi, origin_opt: Option[ActorRef]): (ActorRef, LocalParams) = { val defaultFinalScriptPubKey = Helpers.getFinalScriptPubKey(wallet, nodeParams.chainHash) - val localParams = makeChannelParams(nodeParams, defaultFinalScriptPubKey, funder, fundingSatoshis) + val localParams = makeChannelParams(nodeParams, defaultFinalScriptPubKey, funder, fundingAmount) val channel = spawnChannel(nodeParams, origin_opt) (channel, localParams) } @@ -605,7 +606,7 @@ object Peer { case class Disconnect(nodeId: PublicKey) case object ResumeAnnouncements case class OpenChannel(remoteNodeId: PublicKey, fundingSatoshis: Satoshi, pushMsat: MilliSatoshi, fundingTxFeeratePerKw_opt: Option[Long], channelFlags: Option[Byte], timeout_opt: Option[Timeout]) { - require(fundingSatoshis.amount < Channel.MAX_FUNDING_SATOSHIS, s"fundingSatoshis must be less than ${Channel.MAX_FUNDING_SATOSHIS}") + require(fundingSatoshis < Channel.MAX_FUNDING, s"fundingSatoshis must be less than ${Channel.MAX_FUNDING}") require(pushMsat.amount <= 1000 * fundingSatoshis.amount, s"pushMsat must be less or equal to fundingSatoshis") require(fundingSatoshis.amount >= 0, s"fundingSatoshis must be positive") require(pushMsat.amount >= 0, s"pushMsat must be positive") @@ -628,22 +629,22 @@ object Peer { // @formatter:on - def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingSatoshis: Long): LocalParams = { + def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingAmount: Satoshi): LocalParams = { val entropy = new Array[Byte](16) secureRandom.nextBytes(entropy) val bis = new ByteArrayInputStream(entropy) val channelKeyPath = DeterministicWallet.KeyPath(Seq(Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN), Protocol.uint32(bis, ByteOrder.BIG_ENDIAN))) - makeChannelParams(nodeParams, defaultFinalScriptPubKey, isFunder, fundingSatoshis, channelKeyPath) + makeChannelParams(nodeParams, defaultFinalScriptPubKey, isFunder, fundingAmount, channelKeyPath) } - def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingSatoshis: Long, channelKeyPath: DeterministicWallet.KeyPath): LocalParams = { + def makeChannelParams(nodeParams: NodeParams, defaultFinalScriptPubKey: ByteVector, isFunder: Boolean, fundingAmount: Satoshi, channelKeyPath: DeterministicWallet.KeyPath): LocalParams = { LocalParams( nodeParams.nodeId, channelKeyPath, - dustLimitSatoshis = nodeParams.dustLimitSatoshis, + dustLimit = nodeParams.dustLimit, maxHtlcValueInFlightMsat = nodeParams.maxHtlcValueInFlightMsat, - channelReserveSatoshis = Math.max((nodeParams.reserveToFundingRatio * fundingSatoshis).toLong, nodeParams.dustLimitSatoshis), // BOLT #2: make sure that our reserve is above our dust limit - htlcMinimumMsat = nodeParams.htlcMinimumMsat, + channelReserve = maxOf(Satoshi((nodeParams.reserveToFundingRatio * fundingAmount.toLong).toLong), nodeParams.dustLimit), // BOLT #2: make sure that our reserve is above our dust limit + htlcMinimum = nodeParams.htlcMinimum, toSelfDelay = nodeParams.toRemoteDelayBlocks, // we choose their delay maxAcceptedHtlcs = nodeParams.maxAcceptedHtlcs, defaultFinalScriptPubKey = defaultFinalScriptPubKey, diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala index 44adabf9eb..3ae5f2ffed 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala @@ -126,7 +126,7 @@ package object eclair { * @param msat amount in millisatoshi * @return the fee (in msat) that a node should be paid to forward an HTLC of 'amount' millisatoshis */ - def nodeFee(baseMsat: Long, proportional: Long, msat: Long): Long = baseMsat + (proportional * msat) / 1000000 + def nodeFee(baseMsat: MilliSatoshi, proportional: Long, msat: MilliSatoshi): MilliSatoshi = baseMsat + (msat * proportional) / 1000000 /** * @@ -159,4 +159,54 @@ package object eclair { * We use this in the context of timestamp filtering, when we don't need an upper bound. */ val MaxEpochSeconds = Duration.fromNanos(Long.MaxValue).toSeconds + + /** + * One MilliSatoshi is a thousand of a Satoshi, the smallest unit usable in bitcoin + * @param amount + */ + case class MilliSatoshi(amount: Long) { + // @formatter:off + def toLong = amount + def +(other: MilliSatoshi) = MilliSatoshi(amount + other.amount) + def -(other: MilliSatoshi) = MilliSatoshi(amount - other.amount) + def *(m: Long) = MilliSatoshi(amount * m) + def /(d: Long) = MilliSatoshi(amount / d) + def compare(other: MilliSatoshi): Int = if (amount == other.amount) 0 else if (amount < other.amount) -1 else 1 + def <= (that: MilliSatoshi): Boolean = compare(that) <= 0 + def >= (that: MilliSatoshi): Boolean = compare(that) >= 0 + def < (that: MilliSatoshi): Boolean = compare(that) < 0 + def > (that: MilliSatoshi): Boolean = compare(that) > 0 + def unary_-() = MilliSatoshi(-amount) + def truncateToSatoshi: Satoshi = Satoshi(amount / 1000) + // @formatter:on + } + + implicit class ToMilliSatoshiConversion(amount: BtcAmount) { + def toMilliSatoshi: MilliSatoshi = amount match { + case sat: Satoshi => satoshi2millisatoshi(sat) + case millis: MilliBtc => satoshi2millisatoshi(millibtc2satoshi(millis)) + case bitcoin: Btc => satoshi2millisatoshi(btc2satoshi(bitcoin)) + } + } + + implicit object NumericMilliSatoshi extends Numeric[MilliSatoshi] { + override def plus(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount + y.amount) + override def minus(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount - y.amount) + override def times(x: MilliSatoshi, y: MilliSatoshi): MilliSatoshi = MilliSatoshi(x.amount * y.amount) + override def negate(x: MilliSatoshi): MilliSatoshi = MilliSatoshi(-x.amount) + override def fromInt(x: Int): MilliSatoshi = MilliSatoshi(x) + override def toInt(x: MilliSatoshi): Int = x.toLong.toInt + override def toLong(x: MilliSatoshi): Long = x.toLong + override def toFloat(x: MilliSatoshi): Float = x.toLong + override def toDouble(x: MilliSatoshi): Double = x.toLong + override def compare(x: MilliSatoshi, y: MilliSatoshi): Int = x.compare(y) + } + + private def satoshi2millisatoshi(input: Satoshi): MilliSatoshi = MilliSatoshi(input.amount * 1000L) + + def maxOf(x: MilliSatoshi, y: MilliSatoshi) = MilliSatoshi(Math.max(x.amount, y.amount)) + def minOf(x: MilliSatoshi, y: MilliSatoshi) = MilliSatoshi(Math.min(x.amount, y.amount)) + def maxOf(x: Satoshi, y: Satoshi) = Satoshi(Math.max(x.amount, y.amount)) + def minOf(x: Satoshi, y: Satoshi) = Satoshi(Math.min(x.amount, y.amount)) + } \ No newline at end of file diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala index 6136b928e4..0c79bd663c 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Auditor.scala @@ -18,12 +18,10 @@ package fr.acinq.eclair.payment import akka.actor.{Actor, ActorLogging, Props} import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.NodeParams -import fr.acinq.eclair.channel.Channel.{LocalError, RemoteError} +import fr.acinq.eclair.{MilliSatoshi, NodeParams} import fr.acinq.eclair.channel.Helpers.Closing.{LocalClose, MutualClose, RecoveryClose, RemoteClose, RevokedClose} import fr.acinq.eclair.channel._ import fr.acinq.eclair.db.{AuditDb, ChannelLifecycleEvent} - import scala.concurrent.ExecutionContext import scala.concurrent.duration._ @@ -57,7 +55,7 @@ class Auditor(nodeParams: NodeParams) extends Actor with ActorLogging { case e: ChannelStateChanged => e match { case ChannelStateChanged(_, _, remoteNodeId, WAIT_FOR_FUNDING_LOCKED, NORMAL, d: DATA_NORMAL) => - db.add(ChannelLifecycleEvent(d.channelId, remoteNodeId, d.commitments.commitInput.txOut.amount.toLong, d.commitments.localParams.isFunder, !d.commitments.announceChannel, "created")) + db.add(ChannelLifecycleEvent(d.channelId, remoteNodeId, d.commitments.commitInput.txOut.amount, d.commitments.localParams.isFunder, !d.commitments.announceChannel, "created")) case _ => () } @@ -69,7 +67,7 @@ class Auditor(nodeParams: NodeParams) extends Actor with ActorLogging { case RecoveryClose => "recovery" case RevokedClose => "revoked" } - db.add(ChannelLifecycleEvent(e.channelId, e.commitments.remoteParams.nodeId, e.commitments.commitInput.txOut.amount.toLong, e.commitments.localParams.isFunder, !e.commitments.announceChannel, event)) + db.add(ChannelLifecycleEvent(e.channelId, e.commitments.remoteParams.nodeId, e.commitments.commitInput.txOut.amount, e.commitments.localParams.isFunder, !e.commitments.announceChannel, event)) } @@ -109,11 +107,11 @@ class BalanceEventThrottler(db: AuditDb) extends Actor with ActorLogging { case ProcessEvent(channelId) => pending.get(channelId) match { case Some(BalanceUpdate(first, last)) => - if (first.commitments.remoteCommit.spec.toRemoteMsat == last.localBalanceMsat) { + if (first.commitments.remoteCommit.spec.toRemote == last.localBalance) { // we don't log anything if the balance didn't change (e.g. it was a probe payment) log.info(s"ignoring balance event for channelId=$channelId (changed was discarded)") } else { - log.info(s"processing balance event for channelId=$channelId balance=${first.localBalanceMsat}->${last.localBalanceMsat}") + log.info(s"processing balance event for channelId=$channelId balance=${first.localBalance}->${last.localBalance}") // we log the last event, which contains the most up to date balance db.add(last) context.become(run(pending - channelId)) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala index 94e33deb94..71396e096e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Autoprobe.scala @@ -21,8 +21,8 @@ import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentResult, RemoteFailure, SendPayment} import fr.acinq.eclair.router.{Announcements, Data} -import fr.acinq.eclair.wire.{IncorrectOrUnknownPaymentDetails} -import fr.acinq.eclair.{NodeParams, randomBytes32, secureRandom} +import fr.acinq.eclair.wire.IncorrectOrUnknownPaymentDetails +import fr.acinq.eclair.{MilliSatoshi, NodeParams, randomBytes32, secureRandom} import scala.concurrent.duration._ @@ -83,7 +83,7 @@ object Autoprobe { val PROBING_INTERVAL = 20 seconds - val PAYMENT_AMOUNT_MSAT = 100 * 1000 // this is below dust_limit so there won't be an output in the commitment tx + val PAYMENT_AMOUNT_MSAT = MilliSatoshi(100 * 1000) // this is below dust_limit so there won't be an output in the commitment tx object TickProbe diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala index 1070f3ef37..d936a1474a 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/LocalPaymentHandler.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.payment import akka.actor.{Actor, ActorLogging, Props, Status} -import fr.acinq.bitcoin.{Crypto, MilliSatoshi} +import fr.acinq.bitcoin.{Crypto} import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC, Channel} import fr.acinq.eclair.db.IncomingPayment import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment @@ -69,18 +69,18 @@ class LocalPaymentHandler(nodeParams: NodeParams) extends Actor with ActorLoggin sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) case _ if htlc.cltvExpiry < minFinalExpiry => sender ! CMD_FAIL_HTLC(htlc.id, Right(FinalExpiryTooSoon), commit = true) - case Some(amount) if MilliSatoshi(htlc.amountMsat) < amount => - log.warning(s"received payment with amount too small for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + case Some(amount) if htlc.amountMsat < amount => + log.warning(s"received payment with amount too small for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) - case Some(amount) if MilliSatoshi(htlc.amountMsat) > amount * 2 => - log.warning(s"received payment with amount too large for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + case Some(amount) if htlc.amountMsat > amount * 2 => + log.warning(s"received payment with amount too large for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) case _ => - log.info(s"received payment for paymentHash=${htlc.paymentHash} amountMsat=${htlc.amountMsat}") + log.info(s"received payment for paymentHash=${htlc.paymentHash} amount=${htlc.amountMsat}") // amount is correct or was not specified in the payment request nodeParams.db.payments.addIncomingPayment(IncomingPayment(htlc.paymentHash, htlc.amountMsat, Platform.currentTime)) sender ! CMD_FULFILL_HTLC(htlc.id, paymentPreimage, commit = true) - context.system.eventStream.publish(PaymentReceived(MilliSatoshi(htlc.amountMsat), htlc.paymentHash, htlc.channelId)) + context.system.eventStream.publish(PaymentReceived(htlc.amountMsat, htlc.paymentHash, htlc.channelId)) } case None => sender ! CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(htlc.amountMsat)), commit = true) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala index 5901dce810..7b0f1b55c2 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentEvents.scala @@ -17,7 +17,10 @@ package fr.acinq.eclair.payment import java.util.UUID -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} + +import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi + import scala.compat.Platform /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala index 907a68a25c..74374ec9d5 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentLifecycle.scala @@ -20,7 +20,7 @@ import java.util.UUID import akka.actor.{ActorRef, FSM, Props, Status} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair._ import fr.acinq.eclair.channel.{AddHtlcFailed, CMD_ADD_HTLC, Channel, Register} import fr.acinq.eclair.crypto.{Sphinx, TransportHandler} @@ -47,14 +47,14 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis when(WAITING_FOR_REQUEST) { case Event(c: SendPaymentToRoute, WaitingForRequest) => - val send = SendPayment(c.amountMsat, c.paymentHash, c.hops.last, finalCltvExpiry = c.finalCltvExpiry, maxAttempts = 1) - paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amountMsat, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) + val send = SendPayment(c.amount, c.paymentHash, c.hops.last, finalCltvExpiry = c.finalCltvExpiry, maxAttempts = 1) + paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amount, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) router ! FinalizeRoute(c.hops) goto(WAITING_FOR_ROUTE) using WaitingForRoute(sender, send, failures = Nil) case Event(c: SendPayment, WaitingForRequest) => - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, routeParams = c.routeParams) - paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amountMsat, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, routeParams = c.routeParams) + paymentsDb.addOutgoingPayment(OutgoingPayment(id, c.paymentHash, None, c.amount, Platform.currentTime, None, OutgoingPaymentStatus.PENDING)) goto(WAITING_FOR_ROUTE) using WaitingForRoute(sender, c, failures = Nil) } @@ -65,7 +65,7 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // we add one block in order to not have our htlc fail when a new block has just been found val finalExpiry = Globals.blockCount.get().toInt + c.finalCltvExpiry.toInt + 1 - val (cmd, sharedSecrets) = buildCommand(id, c.amountMsat, finalExpiry, c.paymentHash, hops) + val (cmd, sharedSecrets) = buildCommand(id,c.amount, finalExpiry, c.paymentHash, hops) register ! Register.ForwardShortId(firstHop.lastUpdate.shortChannelId, cmd) goto(WAITING_FOR_PAYMENT_COMPLETE) using WaitingForComplete(s, c, cmd, failures, sharedSecrets, ignoreNodes, ignoreChannels, hops) @@ -80,8 +80,8 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis case Event(fulfill: UpdateFulfillHtlc, WaitingForComplete(s, c, cmd, _, _, _, _, hops)) => paymentsDb.updateOutgoingPayment(id, OutgoingPaymentStatus.SUCCEEDED, preimage = Some(fulfill.paymentPreimage)) - reply(s, PaymentSucceeded(id, cmd.amountMsat, c.paymentHash, fulfill.paymentPreimage, hops)) - context.system.eventStream.publish(PaymentSent(id, MilliSatoshi(c.amountMsat), MilliSatoshi(cmd.amountMsat - c.amountMsat), cmd.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) + reply(s, PaymentSucceeded(id, cmd.amount, c.paymentHash, fulfill.paymentPreimage, hops)) + context.system.eventStream.publish(PaymentSent(id, c.amount, cmd.amount - c.amount, cmd.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) stop(FSM.Normal) case Event(fail: UpdateFailHtlc, WaitingForComplete(s, c, _, failures, sharedSecrets, ignoreNodes, ignoreChannels, hops)) => @@ -111,12 +111,12 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // in that case we don't know which node is sending garbage, let's try to blacklist all nodes except the one we are directly connected to and the destination node val blacklist = hops.map(_.nextNodeId).drop(1).dropRight(1) log.warning(s"blacklisting intermediate nodes=${blacklist.mkString(",")}") - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes ++ blacklist, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes ++ blacklist, ignoreChannels, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ UnreadableRemoteFailure(hops)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage: Node)) => log.info(s"received 'Node' type error message from nodeId=$nodeId, trying to route around it (failure=$failureMessage)") // let's try to route around this node - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage: Update)) => log.info(s"received 'Update' type error message from nodeId=$nodeId, retrying payment (failure=$failureMessage)") @@ -144,18 +144,18 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis // in any case, we forward the update to the router router ! failureMessage.update // let's try again, router will have updated its state - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels, c.routeParams) } else { // this node is fishy, it gave us a bad sig!! let's filter it out log.warning(s"got bad signature from node=$nodeId update=${failureMessage.update}") - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes + nodeId, ignoreChannels, c.routeParams) } goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) case Success(e@Sphinx.DecryptedFailurePacket(nodeId, failureMessage)) => log.info(s"received an error message from nodeId=$nodeId, trying to use a different channel (failure=$failureMessage)") // let's try again without the channel outgoing from nodeId val faultyChannel = hops.find(_.nodeId == nodeId).map(hop => ChannelDesc(hop.lastUpdate.shortChannelId, hop.nodeId, hop.nextNodeId)) - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels ++ faultyChannel.toSet, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels ++ faultyChannel.toSet, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ RemoteFailure(hops, e)) } @@ -175,7 +175,7 @@ class PaymentLifecycle(nodeParams: NodeParams, id: UUID, router: ActorRef, regis } else { log.info(s"received an error message from local, trying to use a different channel (failure=${t.getMessage})") val faultyChannel = ChannelDesc(hops.head.lastUpdate.shortChannelId, hops.head.nodeId, hops.head.nextNodeId) - router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amountMsat, c.assistedRoutes, ignoreNodes, ignoreChannels + faultyChannel, c.routeParams) + router ! RouteRequest(nodeParams.nodeId, c.targetNodeId, c.amount, c.assistedRoutes, ignoreNodes, ignoreChannels + faultyChannel, c.routeParams) goto(WAITING_FOR_ROUTE) using WaitingForRoute(s, c, failures :+ LocalFailure(t)) } @@ -198,21 +198,21 @@ object PaymentLifecycle { def props(nodeParams: NodeParams, id: UUID, router: ActorRef, register: ActorRef) = Props(classOf[PaymentLifecycle], nodeParams, id, router, register) // @formatter:off - case class ReceivePayment(amountMsat_opt: Option[MilliSatoshi], description: String, expirySeconds_opt: Option[Long] = None, extraHops: List[List[ExtraHop]] = Nil, fallbackAddress: Option[String] = None, paymentPreimage: Option[ByteVector32] = None) + case class ReceivePayment(amount_opt: Option[MilliSatoshi], description: String, expirySeconds_opt: Option[Long] = None, extraHops: List[List[ExtraHop]] = Nil, fallbackAddress: Option[String] = None, paymentPreimage: Option[ByteVector32] = None) sealed trait GenericSendPayment - case class SendPaymentToRoute(amountMsat: Long, paymentHash: ByteVector32, hops: Seq[PublicKey], finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY) extends GenericSendPayment - case class SendPayment(amountMsat: Long, + case class SendPaymentToRoute(amount: MilliSatoshi, paymentHash: ByteVector32, hops: Seq[PublicKey], finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY) extends GenericSendPayment + case class SendPayment(amount: MilliSatoshi, paymentHash: ByteVector32, targetNodeId: PublicKey, assistedRoutes: Seq[Seq[ExtraHop]] = Nil, finalCltvExpiry: Long = Channel.MIN_CLTV_EXPIRY, maxAttempts: Int, routeParams: Option[RouteParams] = None) extends GenericSendPayment { - require(amountMsat > 0, s"amountMsat must be > 0") + require(amount > MilliSatoshi(0), s"amountMsat must be > 0") } sealed trait PaymentResult - case class PaymentSucceeded(id: UUID, amountMsat: Long, paymentHash: ByteVector32, paymentPreimage: ByteVector32, route: Seq[Hop]) extends PaymentResult // note: the amount includes fees + case class PaymentSucceeded(id: UUID, amount: MilliSatoshi, paymentHash: ByteVector32, paymentPreimage: ByteVector32, route: Seq[Hop]) extends PaymentResult // note: the amount includes fees sealed trait PaymentFailure case class LocalFailure(t: Throwable) extends PaymentFailure case class RemoteFailure(route: Seq[Hop], e: Sphinx.DecryptedFailurePacket) extends PaymentFailure @@ -246,7 +246,7 @@ object PaymentLifecycle { /** * - * @param finalAmountMsat the final htlc amount in millisatoshis + * @param finalAmount the final htlc amount in millisatoshis * @param finalExpiry the final htlc expiry in number of blocks * @param hops the hops as computed by the router + extra routes from payment request * @return a (firstAmountMsat, firstExpiry, payloads) tuple where: @@ -254,19 +254,19 @@ object PaymentLifecycle { * - firstExpiry is the cltv expiry for the first htlc in the route * - a sequence of payloads that will be used to build the onion */ - def buildPayloads(finalAmountMsat: Long, finalExpiry: Long, hops: Seq[Hop]): (Long, Long, Seq[PerHopPayload]) = - hops.reverse.foldLeft((finalAmountMsat, finalExpiry, PerHopPayload(ShortChannelId(0L), finalAmountMsat, finalExpiry) :: Nil)) { + def buildPayloads(finalAmount: MilliSatoshi, finalExpiry: Long, hops: Seq[Hop]): (MilliSatoshi, Long, Seq[PerHopPayload]) = + hops.reverse.foldLeft((finalAmount, finalExpiry, PerHopPayload(ShortChannelId(0L), finalAmount, finalExpiry) :: Nil)) { case ((msat, expiry, payloads), hop) => val nextFee = nodeFee(hop.lastUpdate.feeBaseMsat, hop.lastUpdate.feeProportionalMillionths, msat) (msat + nextFee, expiry + hop.lastUpdate.cltvExpiryDelta, PerHopPayload(hop.lastUpdate.shortChannelId, msat, expiry) +: payloads) } - def buildCommand(id: UUID, finalAmountMsat: Long, finalExpiry: Long, paymentHash: ByteVector32, hops: Seq[Hop]): (CMD_ADD_HTLC, Seq[(ByteVector32, PublicKey)]) = { - val (firstAmountMsat, firstExpiry, payloads) = buildPayloads(finalAmountMsat, finalExpiry, hops.drop(1)) + def buildCommand(id: UUID, finalAmount: MilliSatoshi, finalExpiry: Long, paymentHash: ByteVector32, hops: Seq[Hop]): (CMD_ADD_HTLC, Seq[(ByteVector32, PublicKey)]) = { + val (firstAmount, firstExpiry, payloads) = buildPayloads(finalAmount, finalExpiry, hops.drop(1)) val nodes = hops.map(_.nextNodeId) // BOLT 2 requires that associatedData == paymentHash val onion = buildOnion(nodes, payloads, paymentHash) - CMD_ADD_HTLC(firstAmountMsat, paymentHash, firstExpiry, onion.packet, upstream = Left(id), commit = true) -> onion.sharedSecrets + CMD_ADD_HTLC(firstAmount, paymentHash, firstExpiry, onion.packet, upstream = Left(id), commit = true) -> onion.sharedSecrets } /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala index b5c6da04f9..fc6ea0ca3e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/PaymentRequest.scala @@ -16,13 +16,14 @@ package fr.acinq.eclair.payment +import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, ByteVector64, Crypto} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{MilliSatoshi, _} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.payment.PaymentRequest._ import scodec.Codec import scodec.bits.{BitVector, ByteOrdering, ByteVector} import scodec.codecs.{list, ubyte} + import scala.concurrent.duration._ import scala.compat.Platform import scala.util.Try diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala index d5d6b9f17e..7112bdd0ba 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/Relayer.scala @@ -21,14 +21,14 @@ import java.util.UUID import akka.actor.{Actor, ActorLogging, ActorRef, Props, Status} import akka.event.LoggingAdapter import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.db.OutgoingPaymentStatus import fr.acinq.eclair.payment.PaymentLifecycle.{PaymentFailed, PaymentSucceeded} import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{NodeParams, ShortChannelId, nodeFee} +import fr.acinq.eclair.{MilliSatoshi, NodeParams, ShortChannelId, nodeFee} import grizzled.slf4j.Logging import scodec.{Attempt, DecodeResult} @@ -38,7 +38,7 @@ import scala.collection.mutable sealed trait Origin case class Local(id: UUID, sender: Option[ActorRef]) extends Origin // we don't persist reference to local actors -case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountMsatIn: Long, amountMsatOut: Long) extends Origin +case class Relayed(originChannelId: ByteVector32, originHtlcId: Long, amountIn: MilliSatoshi, amountOut: MilliSatoshi) extends Origin sealed trait ForwardMessage case class ForwardAdd(add: UpdateAddHtlc, previousFailures: Seq[AddHtlcFailed] = Seq.empty) extends ForwardMessage @@ -47,7 +47,7 @@ case class ForwardFail(fail: UpdateFailHtlc, to: Origin, htlc: UpdateAddHtlc) ex case class ForwardFailMalformed(fail: UpdateFailMalformedHtlc, to: Origin, htlc: UpdateAddHtlc) extends ForwardMessage case object GetUsableBalances -case class UsableBalances(remoteNodeId: PublicKey, shortChannelId: ShortChannelId, canSendMsat: Long, canReceiveMsat: Long, isPublic: Boolean) +case class UsableBalances(remoteNodeId: PublicKey, shortChannelId: ShortChannelId, canSend: MilliSatoshi, canReceive: MilliSatoshi, isPublic: Boolean) // @formatter:on @@ -77,8 +77,8 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR .map(o => UsableBalances( remoteNodeId = o.nextNodeId, shortChannelId = o.channelUpdate.shortChannelId, - canSendMsat = o.commitments.availableBalanceForSendMsat, - canReceiveMsat = o.commitments.availableBalanceForReceiveMsat, + canSend = o.commitments.availableBalanceForSend, + canReceive = o.commitments.availableBalanceForReceive, isPublic = o.commitments.announceChannel)) case LocalChannelUpdate(_, channelId, shortChannelId, remoteNodeId, _, channelUpdate, commitments) => @@ -158,17 +158,17 @@ class Relayer(nodeParams: NodeParams, register: ActorRef, paymentHandler: ActorR to match { case Local(id, None) => val feesPaid = MilliSatoshi(0) - context.system.eventStream.publish(PaymentSent(id, MilliSatoshi(add.amountMsat), feesPaid, add.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) + context.system.eventStream.publish(PaymentSent(id, add.amountMsat, feesPaid, add.paymentHash, fulfill.paymentPreimage, fulfill.channelId)) // we sent the payment, but we probably restarted and the reference to the original sender was lost, // we publish the failure on the event stream and update the status in paymentDb nodeParams.db.payments.updateOutgoingPayment(id, OutgoingPaymentStatus.SUCCEEDED, Some(fulfill.paymentPreimage)) context.system.eventStream.publish(PaymentSucceeded(id, add.amountMsat, add.paymentHash, fulfill.paymentPreimage, Nil)) // case Local(_, Some(sender)) => sender ! fulfill - case Relayed(originChannelId, originHtlcId, amountMsatIn, amountMsatOut) => + case Relayed(originChannelId, originHtlcId, amountIn, amountOut) => val cmd = CMD_FULFILL_HTLC(originHtlcId, fulfill.paymentPreimage, commit = true) commandBuffer ! CommandBuffer.CommandSend(originChannelId, originHtlcId, cmd) - context.system.eventStream.publish(PaymentRelayed(MilliSatoshi(amountMsatIn), MilliSatoshi(amountMsatOut), add.paymentHash, fromChannelId = originChannelId, toChannelId = fulfill.channelId)) + context.system.eventStream.publish(PaymentRelayed(amountIn, amountOut, add.paymentHash, fromChannelId = originChannelId, toChannelId = fulfill.channelId)) } case ForwardFail(fail, to, add) => @@ -215,7 +215,7 @@ object Relayer extends Logging { sealed trait NextPayload case class FinalPayload(add: UpdateAddHtlc, payload: PerHopPayload) extends NextPayload case class RelayPayload(add: UpdateAddHtlc, payload: PerHopPayload, nextPacket: OnionRoutingPacket) extends NextPayload { - val relayFeeMsat: Long = add.amountMsat - payload.amtToForward + val relayFeeMsat: MilliSatoshi = add.amountMsat - payload.amtToForward val expiryDelta: Long = add.cltvExpiry - payload.outgoingCltvValue } // @formatter:on @@ -330,10 +330,10 @@ object Relayer extends Logging { val channelInfo_opt = channelUpdates.get(shortChannelId) val channelUpdate_opt = channelInfo_opt.map(_.channelUpdate) val relayResult = relayOrFail(relayPayload, channelUpdate_opt) - log.debug(s"candidate channel for htlc #${add.id} paymentHash=${add.paymentHash}: shortChannelId={} balanceMsat={} channelUpdate={} relayResult={}", shortChannelId, channelInfo_opt.map(_.commitments.availableBalanceForSendMsat).getOrElse(""), channelUpdate_opt.getOrElse(""), relayResult) + log.debug(s"candidate channel for htlc #${add.id} paymentHash=${add.paymentHash}: shortChannelId={} balanceMsat={} channelUpdate={} relayResult={}", shortChannelId, channelInfo_opt.map(_.commitments.availableBalanceForSend).getOrElse(""), channelUpdate_opt.getOrElse(""), relayResult) (shortChannelId, channelInfo_opt, relayResult) } - .collect { case (shortChannelId, Some(channelInfo), _: RelaySuccess) => (shortChannelId, channelInfo.commitments.availableBalanceForSendMsat) } + .collect { case (shortChannelId, Some(channelInfo), _: RelaySuccess) => (shortChannelId, channelInfo.commitments.availableBalanceForSend) } .filter(_._2 > relayPayload.payload.amtToForward) // we only keep channels that have enough balance to handle this payment .toList // needed for ordering .sortBy(_._2) // we want to use the channel with the lowest available balance that can process the payment diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala index 1e76f48b7b..abbfd61d4e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Announcements.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.router import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, sha256, verifySignature} import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Features, ShortChannelId, serializationResult} +import fr.acinq.eclair.{Features, MilliSatoshi, ShortChannelId, serializationResult} import scodec.bits.{BitVector, ByteVector} import shapeless.HNil @@ -37,7 +37,7 @@ object Announcements { def nodeAnnouncementWitnessEncode(timestamp: Long, nodeId: PublicKey, rgbColor: Color, alias: String, features: ByteVector, addresses: List[NodeAddress], unknownFields: ByteVector): ByteVector = sha256(sha256(serializationResult(LightningMessageCodecs.nodeAnnouncementWitnessCodec.encode(features :: timestamp :: nodeId :: rgbColor :: alias :: addresses :: unknownFields :: HNil)))) - def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: Long, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Option[Long], unknownFields: ByteVector): ByteVector = + def channelUpdateWitnessEncode(chainHash: ByteVector32, shortChannelId: ShortChannelId, timestamp: Long, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: Option[MilliSatoshi], unknownFields: ByteVector): ByteVector = sha256(sha256(serializationResult(LightningMessageCodecs.channelUpdateWitnessCodec.encode(chainHash :: shortChannelId :: timestamp :: messageFlags :: channelFlags :: cltvExpiryDelta :: htlcMinimumMsat :: feeBaseMsat :: feeProportionalMillionths :: htlcMaximumMsat :: unknownFields :: HNil)))) def signChannelAnnouncement(chainHash: ByteVector32, shortChannelId: ShortChannelId, localNodeSecret: PrivateKey, remoteNodeId: PublicKey, localFundingPrivKey: PrivateKey, remoteFundingKey: PublicKey, features: ByteVector): (ByteVector64, ByteVector64) = { @@ -128,7 +128,7 @@ object Announcements { def makeChannelFlags(isNode1: Boolean, enable: Boolean): Byte = BitVector.bits(!enable :: !isNode1 :: Nil).padLeft(8).toByte() - def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true, timestamp: Long = Platform.currentTime.milliseconds.toSeconds): ChannelUpdate = { + def makeChannelUpdate(chainHash: ByteVector32, nodeSecret: PrivateKey, remoteNodeId: PublicKey, shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, htlcMaximumMsat: MilliSatoshi, enable: Boolean = true, timestamp: Long = Platform.currentTime.milliseconds.toSeconds): ChannelUpdate = { val messageFlags = makeMessageFlags(hasOptionChannelHtlcMax = true) // NB: we always support option_channel_htlc_max val channelFlags = makeChannelFlags(isNode1 = isNode1(nodeSecret.publicKey, remoteNodeId), enable = enable) val htlcMaximumMsatOpt = Some(htlcMaximumMsat) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala index 178036a3ae..27e92a3238 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Graph.scala @@ -29,7 +29,7 @@ object Graph { // @formatter:off // A compound weight for an edge, weight is obtained with (cost X factor),'cost' contains the actual amount+fees in millisatoshi, 'cltvCumulative' the total CLTV necessary to reach this edge - case class RichWeight(cost: Long, length: Int, cltv: Int, weight: Double) extends Ordered[RichWeight] { + case class RichWeight(cost: MilliSatoshi, length: Int, cltv: Int, weight: Double) extends Ordered[RichWeight] { override def compare(that: RichWeight): Int = this.weight.compareTo(that.weight) } case class WeightRatios(cltvDeltaFactor: Double, ageFactor: Double, capacityFactor: Double) { @@ -62,7 +62,7 @@ object Graph { * @param graph * @param sourceNode * @param targetNode - * @param amountMsat + * @param amount * @param pathsToFind * @param wr an object containing the ratios used to 'weight' edges when searching for the shortest path * @param currentBlockHeight the height of the chain tip (latest block) @@ -72,7 +72,7 @@ object Graph { def yenKshortestPaths(graph: DirectedGraph, sourceNode: PublicKey, targetNode: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, ignoredEdges: Set[ChannelDesc], extraEdges: Set[GraphEdge], pathsToFind: Int, @@ -88,9 +88,9 @@ object Graph { val candidates = new mutable.PriorityQueue[WeightedPath] // find the shortest path, k = 0 - val initialWeight = RichWeight(cost = amountMsat, 0, 0, 0) + val initialWeight = RichWeight(cost = amount, 0, 0, 0) val shortestPath = dijkstraShortestPath(graph, sourceNode, targetNode, ignoredEdges, extraEdges, initialWeight, boundaries, currentBlockHeight, wr) - shortestPaths += WeightedPath(shortestPath, pathWeight(shortestPath, amountMsat, isPartial = false, currentBlockHeight, wr)) + shortestPaths += WeightedPath(shortestPath, pathWeight(shortestPath, amount, isPartial = false, currentBlockHeight, wr)) // avoid returning a list with an empty path if (shortestPath.isEmpty) return Seq.empty @@ -110,7 +110,7 @@ object Graph { // select the sub-path from the source to the spur node of the k-th previous shortest path val rootPathEdges = if (i == 0) prevShortestPath.head :: Nil else prevShortestPath.take(i) - val rootPathWeight = pathWeight(rootPathEdges, amountMsat, isPartial = true, currentBlockHeight, wr) + val rootPathWeight = pathWeight(rootPathEdges, amount, isPartial = true, currentBlockHeight, wr) // links to be removed that are part of the previous shortest path and which share the same root path val edgesToIgnore = shortestPaths.flatMap { weightedPath => @@ -136,7 +136,7 @@ object Graph { case false => rootPathEdges ++ spurPath } - val candidatePath = WeightedPath(totalPath, pathWeight(totalPath, amountMsat, isPartial = false, currentBlockHeight, wr)) + val candidatePath = WeightedPath(totalPath, pathWeight(totalPath, amount, isPartial = false, currentBlockHeight, wr)) if (boundaries(candidatePath.weight) && !shortestPaths.contains(candidatePath) && !candidates.exists(_ == candidatePath)) { candidates.enqueue(candidatePath) @@ -237,7 +237,7 @@ object Graph { // we call containsKey first because "getOrDefault" is not available in JDK7 val neighborCost = weight.containsKey(neighbor) match { - case false => RichWeight(Long.MaxValue, Int.MaxValue, Int.MaxValue, Double.MaxValue) + case false => RichWeight(MilliSatoshi(Long.MaxValue), Int.MaxValue, Int.MaxValue, Double.MaxValue) case true => weight.get(neighbor) } @@ -281,7 +281,7 @@ object Graph { private def edgeWeight(edge: GraphEdge, prev: RichWeight, isNeighborTarget: Boolean, currentBlockHeight: Long, weightRatios: Option[WeightRatios]): RichWeight = weightRatios match { case None => val edgeCost = if (isNeighborTarget) prev.cost else edgeFeeCost(edge, prev.cost) - RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + edge.update.cltvExpiryDelta, weight = edgeCost) + RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + edge.update.cltvExpiryDelta, weight = edgeCost.toLong) case Some(wr) => import RoutingHeuristics._ @@ -291,8 +291,8 @@ object Graph { val ageFactor = normalize(channelBlockHeight, min = currentBlockHeight - BLOCK_TIME_TWO_MONTHS, max = currentBlockHeight) // Every edge is weighted by channel capacity, larger channels add less weight - val edgeMaxCapacity = edge.update.htlcMaximumMsat.getOrElse(CAPACITY_CHANNEL_LOW_MSAT) - val capFactor = 1 - normalize(edgeMaxCapacity, CAPACITY_CHANNEL_LOW_MSAT, CAPACITY_CHANNEL_HIGH_MSAT) + val edgeMaxCapacity = edge.update.htlcMaximumMsat.getOrElse(CAPACITY_CHANNEL_LOW) + val capFactor = 1 - normalize(edgeMaxCapacity.toLong, CAPACITY_CHANNEL_LOW.toLong, CAPACITY_CHANNEL_HIGH.toLong) // Every edge is weighted by its clvt-delta value, normalized val channelCltvDelta = edge.update.cltvExpiryDelta @@ -303,7 +303,7 @@ object Graph { // NB we're guaranteed to have weightRatios and factors > 0 val factor = (cltvFactor * wr.cltvDeltaFactor) + (ageFactor * wr.ageFactor) + (capFactor * wr.capacityFactor) - val edgeWeight = if (isNeighborTarget) prev.weight else prev.weight + edgeCost * factor + val edgeWeight = if (isNeighborTarget) prev.weight else prev.weight + edgeCost.toLong * factor RichWeight(cost = edgeCost, length = prev.length + 1, cltv = prev.cltv + channelCltvDelta, weight = edgeWeight) } @@ -317,17 +317,17 @@ object Graph { * @param amountWithFees the value that this edge will have to carry along * @return the new amount updated with the necessary fees for this edge */ - private def edgeFeeCost(edge: GraphEdge, amountWithFees: Long): Long = { - if(edgeHasZeroFee(edge)) amountWithFees + nodeFee(baseMsat = 1, proportional = 0, amountWithFees) + private def edgeFeeCost(edge: GraphEdge, amountWithFees: MilliSatoshi):MilliSatoshi = { + if(edgeHasZeroFee(edge)) amountWithFees + nodeFee(baseMsat = MilliSatoshi(1), proportional = 0, amountWithFees) else amountWithFees + nodeFee(edge.update.feeBaseMsat, edge.update.feeProportionalMillionths, amountWithFees) } private def edgeHasZeroFee(edge: GraphEdge): Boolean = { - edge.update.feeBaseMsat == 0 && edge.update.feeProportionalMillionths == 0 + edge.update.feeBaseMsat.toLong == 0 && edge.update.feeProportionalMillionths == 0 } // Calculates the total cost of a path (amount + fees), direct channels with the source will have a cost of 0 (pay no fees) - def pathWeight(path: Seq[GraphEdge], amountMsat: Long, isPartial: Boolean, currentBlockHeight: Long, wr: Option[WeightRatios]): RichWeight = { + def pathWeight(path: Seq[GraphEdge], amountMsat: MilliSatoshi, isPartial: Boolean, currentBlockHeight: Long, wr: Option[WeightRatios]): RichWeight = { path.drop(if (isPartial) 0 else 1).foldRight(RichWeight(amountMsat, 0, 0, 0)) { (edge, prev) => edgeWeight(edge, prev, false, currentBlockHeight, wr) } @@ -340,8 +340,8 @@ object Graph { val BLOCK_TIME_TWO_MONTHS = 8640 // Low/High bound for channel capacity - val CAPACITY_CHANNEL_LOW_MSAT = 1000 * 1000L // 1000 sat - val CAPACITY_CHANNEL_HIGH_MSAT = Channel.MAX_FUNDING_SATOSHIS * 1000L + val CAPACITY_CHANNEL_LOW = MilliSatoshi(1000 * 1000L) // 1000 sat + val CAPACITY_CHANNEL_HIGH = Channel.MAX_FUNDING.toMilliSatoshi // Low/High bound for CLTV channel value val CLTV_LOW = 9 diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala index 77b70124eb..6e969dbb1e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.router import akka.Done import akka.actor.{ActorRef, Props, Status} import akka.event.Logging.MDC -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin.Script.{pay2wsh, write} import fr.acinq.eclair._ @@ -47,7 +47,7 @@ import scala.util.{Random, Try} case class RouterConf(randomizeRouteSelection: Boolean, channelExcludeDuration: FiniteDuration, routerBroadcastInterval: FiniteDuration, - searchMaxFeeBaseSat: Long, + searchMaxFeeBase: Satoshi, searchMaxFeePct: Double, searchMaxRouteLength: Int, searchMaxCltv: Int, @@ -58,10 +58,10 @@ case class RouterConf(randomizeRouteSelection: Boolean, case class ChannelDesc(shortChannelId: ShortChannelId, a: PublicKey, b: PublicKey) case class Hop(nodeId: PublicKey, nextNodeId: PublicKey, lastUpdate: ChannelUpdate) -case class RouteParams(randomize: Boolean, maxFeeBaseMsat: Long, maxFeePct: Double, routeMaxLength: Int, routeMaxCltv: Int, ratios: Option[WeightRatios]) +case class RouteParams(randomize: Boolean, maxFeeBase: MilliSatoshi, maxFeePct: Double, routeMaxLength: Int, routeMaxCltv: Int, ratios: Option[WeightRatios]) case class RouteRequest(source: PublicKey, target: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, assistedRoutes: Seq[Seq[ExtraHop]] = Nil, ignoreNodes: Set[PublicKey] = Set.empty, ignoreChannels: Set[ChannelDesc] = Set.empty, @@ -732,7 +732,7 @@ object Router { def toFakeUpdate(extraHop: ExtraHop): ChannelUpdate = // the `direction` bit in flags will not be accurate but it doesn't matter because it is not used // what matters is that the `disable` bit is 0 so that this update doesn't get filtered out - ChannelUpdate(signature = ByteVector64.Zeroes, chainHash = ByteVector32.Zeroes, extraHop.shortChannelId, Platform.currentTime.milliseconds.toSeconds, messageFlags = 0, channelFlags = 0, extraHop.cltvExpiryDelta, htlcMinimumMsat = 0L, extraHop.feeBaseMsat, extraHop.feeProportionalMillionths, None) + ChannelUpdate(signature = ByteVector64.Zeroes, chainHash = ByteVector32.Zeroes, extraHop.shortChannelId, Platform.currentTime.milliseconds.toSeconds, messageFlags = 0, channelFlags = 0, extraHop.cltvExpiryDelta, htlcMinimumMsat = MilliSatoshi(0), MilliSatoshi(extraHop.feeBaseMsat), extraHop.feeProportionalMillionths, None) def toFakeUpdates(extraRoute: Seq[ExtraHop], targetNodeId: PublicKey): Map[ChannelDesc, ChannelUpdate] = { // BOLT 11: "For each entry, the pubkey is the node ID of the start of the channel", and the last node is the destination @@ -827,7 +827,7 @@ object Router { def getDefaultRouteParams(routerConf: RouterConf) = RouteParams( randomize = routerConf.randomizeRouteSelection, - maxFeeBaseMsat = routerConf.searchMaxFeeBaseSat * 1000, // converting sat -> msat + maxFeeBase = routerConf.searchMaxFeeBase.toMilliSatoshi, maxFeePct = routerConf.searchMaxFeePct, routeMaxLength = routerConf.searchMaxRouteLength, routeMaxCltv = routerConf.searchMaxCltv, @@ -848,7 +848,7 @@ object Router { * @param g * @param localNodeId * @param targetNodeId - * @param amountMsat the amount that will be sent along this route + * @param amount the amount that will be sent along this route * @param numRoutes the number of shortest-paths to find * @param extraEdges a set of extra edges we want to CONSIDER during the search * @param ignoredEdges a set of extra edges we want to IGNORE during the search @@ -858,7 +858,7 @@ object Router { def findRoute(g: DirectedGraph, localNodeId: PublicKey, targetNodeId: PublicKey, - amountMsat: Long, + amount: MilliSatoshi, numRoutes: Int, extraEdges: Set[GraphEdge] = Set.empty, ignoredEdges: Set[ChannelDesc] = Set.empty, @@ -869,14 +869,14 @@ object Router { val currentBlockHeight = Globals.blockCount.get() val boundaries: RichWeight => Boolean = { weight => - ((weight.cost - amountMsat) < routeParams.maxFeeBaseMsat || (weight.cost - amountMsat) < (routeParams.maxFeePct * amountMsat)) && + ((weight.cost - amount) < routeParams.maxFeeBase || (weight.cost - amount) < amount * routeParams.maxFeePct.toLong) && weight.length <= routeParams.routeMaxLength && weight.length <= ROUTE_MAX_LENGTH && weight.cltv <= routeParams.routeMaxCltv } - val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amountMsat, ignoredEdges, extraEdges, numRoutes, routeParams.ratios, currentBlockHeight, boundaries).toList match { + val foundRoutes = Graph.yenKshortestPaths(g, localNodeId, targetNodeId, amount, ignoredEdges, extraEdges, numRoutes, routeParams.ratios, currentBlockHeight, boundaries).toList match { case Nil if routeParams.routeMaxLength < ROUTE_MAX_LENGTH => // if not found within the constraints we relax and repeat the search - return findRoute(g, localNodeId, targetNodeId, amountMsat, numRoutes, extraEdges, ignoredEdges, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV)) + return findRoute(g, localNodeId, targetNodeId, amount, numRoutes, extraEdges, ignoredEdges, routeParams.copy(routeMaxLength = ROUTE_MAX_LENGTH, routeMaxCltv = DEFAULT_ROUTE_MAX_CLTV)) case Nil => throw RouteNotFound case routes => routes.find(_.path.size == 1) match { case Some(directRoute) => directRoute :: Nil diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala index 3448ecfe99..d4b201202d 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/CommitmentSpec.scala @@ -16,6 +16,7 @@ package fr.acinq.eclair.transactions +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.wire._ /** @@ -30,8 +31,8 @@ case object OUT extends Direction { def opposite = IN } case class DirectedHtlc(direction: Direction, add: UpdateAddHtlc) -final case class CommitmentSpec(htlcs: Set[DirectedHtlc], feeratePerKw: Long, toLocalMsat: Long, toRemoteMsat: Long) { - val totalFunds = toLocalMsat + toRemoteMsat + htlcs.toSeq.map(_.add.amountMsat).sum +final case class CommitmentSpec(htlcs: Set[DirectedHtlc], feeratePerKw: Long, toLocal: MilliSatoshi, toRemote: MilliSatoshi) { + val totalFunds = toLocal + toRemote + htlcs.toSeq.map(_.add.amountMsat).sum } object CommitmentSpec { @@ -43,16 +44,16 @@ object CommitmentSpec { def addHtlc(spec: CommitmentSpec, direction: Direction, update: UpdateAddHtlc): CommitmentSpec = { val htlc = DirectedHtlc(direction, update) direction match { - case OUT => spec.copy(toLocalMsat = spec.toLocalMsat - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) - case IN => spec.copy(toRemoteMsat = spec.toRemoteMsat - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) + case OUT => spec.copy(toLocal = spec.toLocal - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) + case IN => spec.copy(toRemote = spec.toRemote - htlc.add.amountMsat, htlcs = spec.htlcs + htlc) } } // OUT means we are sending an UpdateFulfillHtlc message which means that we are fulfilling an HTLC that they sent def fulfillHtlc(spec: CommitmentSpec, direction: Direction, htlcId: Long): CommitmentSpec = { spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match { - case Some(htlc) if direction == OUT => spec.copy(toLocalMsat = spec.toLocalMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) - case Some(htlc) if direction == IN => spec.copy(toRemoteMsat = spec.toRemoteMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == OUT => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == IN => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}") } } @@ -60,8 +61,8 @@ object CommitmentSpec { // OUT means we are sending an UpdateFailHtlc message which means that we are failing an HTLC that they sent def failHtlc(spec: CommitmentSpec, direction: Direction, htlcId: Long): CommitmentSpec = { spec.htlcs.find(htlc => htlc.direction != direction && htlc.add.id == htlcId) match { - case Some(htlc) if direction == OUT => spec.copy(toRemoteMsat = spec.toRemoteMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) - case Some(htlc) if direction == IN => spec.copy(toLocalMsat = spec.toLocalMsat + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == OUT => spec.copy(toRemote = spec.toRemote + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) + case Some(htlc) if direction == IN => spec.copy(toLocal = spec.toLocal + htlc.add.amountMsat, htlcs = spec.htlcs - htlc) case None => throw new RuntimeException(s"cannot find htlc id=${htlcId}") } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala index 0febeaab9e..9ea53c8a1b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala @@ -21,7 +21,8 @@ import java.nio.ByteOrder import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey, ripemd160} import fr.acinq.bitcoin.Script._ import fr.acinq.bitcoin.SigVersion._ -import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, LexicographicalOrdering, MilliSatoshi, OutPoint, Protocol, SIGHASH_ALL, Satoshi, Script, ScriptElt, ScriptFlags, ScriptWitness, Transaction, TxIn, TxOut, millisatoshi2satoshi} +import fr.acinq.bitcoin._ +import fr.acinq.eclair._ import fr.acinq.eclair.transactions.Scripts._ import fr.acinq.eclair.wire.UpdateAddHtlc import scodec.bits.ByteVector @@ -121,7 +122,7 @@ object Transactions { val htlcTimeoutFee = weight2fee(spec.feeratePerKw, htlcTimeoutWeight) spec.htlcs .filter(_.direction == OUT) - .filter(htlc => MilliSatoshi(htlc.add.amountMsat) >= (dustLimit + htlcTimeoutFee)) + .filter(htlc => htlc.add.amountMsat >= (dustLimit + htlcTimeoutFee).toMilliSatoshi) .toSeq } @@ -129,7 +130,7 @@ object Transactions { val htlcSuccessFee = weight2fee(spec.feeratePerKw, htlcSuccessWeight) spec.htlcs .filter(_.direction == IN) - .filter(htlc => MilliSatoshi(htlc.add.amountMsat) >= (dustLimit + htlcSuccessFee)) + .filter(htlc => htlc.add.amountMsat >= (dustLimit + htlcSuccessFee).toMilliSatoshi) .toSeq } @@ -190,18 +191,18 @@ object Transactions { val commitFee = commitTxFee(localDustLimit, spec) val (toLocalAmount: Satoshi, toRemoteAmount: Satoshi) = if (localIsFunder) { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)) - commitFee, millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat))) + (spec.toLocal.truncateToSatoshi - commitFee, spec.toRemote.truncateToSatoshi) } else { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)), millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat)) - commitFee) + (spec.toLocal.truncateToSatoshi, spec.toRemote.truncateToSatoshi - commitFee) } // NB: we don't care if values are < 0, they will be trimmed if they are < dust limit anyway val toLocalDelayedOutput_opt = if (toLocalAmount >= localDustLimit) Some(TxOut(toLocalAmount, pay2wsh(toLocalDelayed(localRevocationPubkey, toLocalDelay, localDelayedPaymentPubkey)))) else None val toRemoteOutput_opt = if (toRemoteAmount >= localDustLimit) Some(TxOut(toRemoteAmount, pay2wpkh(remotePaymentPubkey))) else None val htlcOfferedOutputs = trimOfferedHtlcs(localDustLimit, spec) - .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes))))) + .map(htlc => TxOut(htlc.add.amountMsat.truncateToSatoshi, pay2wsh(htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes))))) val htlcReceivedOutputs = trimReceivedHtlcs(localDustLimit, spec) - .map(htlc => TxOut(MilliSatoshi(htlc.add.amountMsat), pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes), htlc.add.cltvExpiry)))) + .map(htlc => TxOut(htlc.add.amountMsat.truncateToSatoshi, pay2wsh(htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.add.paymentHash.bytes), htlc.add.cltvExpiry)))) val txnumber = obscuredCommitTxNumber(commitTxNumber, localIsFunder, localPaymentBasePoint, remotePaymentBasePoint) val (sequence, locktime) = encodeTxNumber(txnumber) @@ -218,8 +219,8 @@ object Transactions { val fee = weight2fee(feeratePerKw, htlcTimeoutWeight) val redeemScript = htlcOffered(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash.bytes)) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) - val amount = MilliSatoshi(htlc.amountMsat) - fee + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) + val amount = htlc.amountMsat.truncateToSatoshi - fee if (amount < localDustLimit) { throw AmountBelowDustLimit } @@ -235,8 +236,8 @@ object Transactions { val fee = weight2fee(feeratePerKw, htlcSuccessWeight) val redeemScript = htlcReceived(localHtlcPubkey, remoteHtlcPubkey, localRevocationPubkey, ripemd160(htlc.paymentHash.bytes), htlc.cltvExpiry) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) - val amount = MilliSatoshi(htlc.amountMsat) - fee + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) + val amount = htlc.amountMsat.truncateToSatoshi - fee if (amount < localDustLimit) { throw AmountBelowDustLimit } @@ -266,7 +267,7 @@ object Transactions { def makeClaimHtlcSuccessTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: ByteVector, htlc: UpdateAddHtlc, feeratePerKw: Long): ClaimHtlcSuccessTx = { val redeemScript = htlcOffered(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash.bytes)) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript)) val tx = Transaction( @@ -289,7 +290,7 @@ object Transactions { def makeClaimHtlcTimeoutTx(commitTx: Transaction, outputsAlreadyUsed: Set[Int], localDustLimit: Satoshi, localHtlcPubkey: PublicKey, remoteHtlcPubkey: PublicKey, remoteRevocationPubkey: PublicKey, localFinalScriptPubKey: ByteVector, htlc: UpdateAddHtlc, feeratePerKw: Long): ClaimHtlcTimeoutTx = { val redeemScript = htlcReceived(remoteHtlcPubkey, localHtlcPubkey, remoteRevocationPubkey, ripemd160(htlc.paymentHash.bytes), htlc.cltvExpiry) val pubkeyScript = write(pay2wsh(redeemScript)) - val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(Satoshi(htlc.amountMsat / 1000))) + val outputIndex = findPubKeyScriptIndex(commitTx, pubkeyScript, outputsAlreadyUsed, amount_opt = Some(htlc.amountMsat.truncateToSatoshi)) val input = InputInfo(OutPoint(commitTx, outputIndex), commitTx.txOut(outputIndex), write(redeemScript)) // unsigned tx @@ -447,9 +448,9 @@ object Transactions { require(spec.htlcs.isEmpty, "there shouldn't be any pending htlcs") val (toLocalAmount: Satoshi, toRemoteAmount: Satoshi) = if (localIsFunder) { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)) - closingFee, millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat))) + (spec.toLocal.truncateToSatoshi - closingFee, spec.toRemote.truncateToSatoshi) } else { - (millisatoshi2satoshi(MilliSatoshi(spec.toLocalMsat)), millisatoshi2satoshi(MilliSatoshi(spec.toRemoteMsat)) - closingFee) + (spec.toLocal.truncateToSatoshi, spec.toRemote.truncateToSatoshi - closingFee) } // NB: we don't care if values are < 0, they will be trimmed if they are < dust limit anyway val toLocalOutput_opt = if (toLocalAmount >= dustLimit) Some(TxOut(toLocalAmount, localScriptPubKey)) else None diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala index 97937c05de..df95cd7972 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/ChannelCodecs.scala @@ -61,10 +61,10 @@ object ChannelCodecs extends Logging { val localParamsCodec: Codec[LocalParams] = ( ("nodeId" | publicKey) :: ("channelPath" | keyPathCodec) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimit" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserve" | satoshi) :: + ("htlcMinimum" | millisatoshi) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: ("isFunder" | bool) :: @@ -74,10 +74,10 @@ object ChannelCodecs extends Logging { val remoteParamsCodec: Codec[RemoteParams] = ( ("nodeId" | publicKey) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimit" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserve" | satoshi) :: + ("htlcMinimum" | millisatoshi) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: ("fundingPubKey" | publicKey) :: @@ -105,8 +105,8 @@ object ChannelCodecs extends Logging { val commitmentSpecCodec: Codec[CommitmentSpec] = ( ("htlcs" | setCodec(htlcCodec)) :: ("feeratePerKw" | uint32) :: - ("toLocalMsat" | uint64overflow) :: - ("toRemoteMsat" | uint64overflow)).as[CommitmentSpec] + ("toLocal" | millisatoshi) :: + ("toRemote" | millisatoshi)).as[CommitmentSpec] val outPointCodec: Codec[OutPoint] = variableSizeBytes(uint16, bytes.xmap(d => OutPoint.read(d.toArray), d => OutPoint.write(d))) @@ -186,8 +186,8 @@ object ChannelCodecs extends Logging { val relayedCodec: Codec[Relayed] = ( ("originChannelId" | bytes32) :: ("originHtlcId" | int64) :: - ("amountMsatIn" | uint64overflow) :: - ("amountMsatOut" | uint64overflow)).as[Relayed] + ("amountIn" | millisatoshi) :: + ("amountOut" | millisatoshi)).as[Relayed] // this is for backward compatibility to handle legacy payments that didn't have identifiers val UNKNOWN_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000") diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala index e56a3645fd..fc749c8fe8 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/CommonCodecs.scala @@ -19,9 +19,9 @@ package fr.acinq.eclair.wire import java.net.{Inet4Address, Inet6Address, InetAddress} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} import fr.acinq.eclair.crypto.Mac32 -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import org.apache.commons.codec.binary.Base32 import scodec.bits.{BitVector, ByteVector} import scodec.codecs._ @@ -52,9 +52,11 @@ object CommonCodecs { // this codec can be safely used for values < 2^63 and will fail otherwise // (for something smarter see https://github.com/yzernik/bitcoin-scodec/blob/master/src/main/scala/io/github/yzernik/bitcoinscodec/structures/UInt64.scala) val uint64overflow: Codec[Long] = int64.narrow(l => if (l >= 0) Attempt.Successful(l) else Attempt.failure(Err(s"overflow for value $l")), l => l) - val uint64: Codec[UInt64] = bytes(8).xmap(b => UInt64(b), a => a.toByteVector.padLeft(8)) + val satoshi: Codec[Satoshi] = uint64overflow.xmapc(l => Satoshi(l))(_.toLong) + val millisatoshi: Codec[MilliSatoshi] = uint64overflow.xmapc(l => MilliSatoshi(l))(_.amount) + /** * We impose a minimal encoding on some values (such as varint and truncated int) to ensure that signed hashes can be * re-computed correctly. diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala index a7cee716dd..db816407be 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/FailureMessage.scala @@ -17,8 +17,9 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.crypto.Mac32 -import fr.acinq.eclair.wire.CommonCodecs.{sha256, uint64overflow} +import fr.acinq.eclair.wire.CommonCodecs.{sha256, millisatoshi} import fr.acinq.eclair.wire.LightningMessageCodecs.{channelUpdateCodec, lightningMessageCodec} import scodec.codecs._ import scodec.{Attempt, Codec} @@ -47,16 +48,16 @@ case class TemporaryChannelFailure(update: ChannelUpdate) extends Update { def m case object PermanentChannelFailure extends Perm { def message = "channel is permanently unavailable" } case object RequiredChannelFeatureMissing extends Perm { def message = "channel requires features not present in the onion" } case object UnknownNextPeer extends Perm { def message = "processing node does not know the next peer in the route" } -case class AmountBelowMinimum(amountMsat: Long, update: ChannelUpdate) extends Update { def message = s"payment amount was below the minimum required by the channel" } -case class FeeInsufficient(amountMsat: Long, update: ChannelUpdate) extends Update { def message = s"payment fee was below the minimum required by the channel" } +case class AmountBelowMinimum(amount: MilliSatoshi, update: ChannelUpdate) extends Update { def message = s"payment amount was below the minimum required by the channel" } +case class FeeInsufficient(amount: MilliSatoshi, update: ChannelUpdate) extends Update { def message = s"payment fee was below the minimum required by the channel" } case class ChannelDisabled(messageFlags: Byte, channelFlags: Byte, update: ChannelUpdate) extends Update { def message = "channel is currently disabled" } case class IncorrectCltvExpiry(expiry: Long, update: ChannelUpdate) extends Update { def message = "payment expiry doesn't match the value in the onion" } -case class IncorrectOrUnknownPaymentDetails(amountMsat: Long) extends Perm { def message = "incorrect payment amount or unknown payment hash" } +case class IncorrectOrUnknownPaymentDetails(amount: MilliSatoshi) extends Perm { def message = "incorrect payment amount or unknown payment hash" } case object IncorrectPaymentAmount extends Perm { def message = "payment amount is incorrect" } case class ExpiryTooSoon(update: ChannelUpdate) extends Update { def message = "payment expiry is too close to the current block height for safe handling by the relaying node" } case object FinalExpiryTooSoon extends FailureMessage { def message = "payment expiry is too close to the current block height for safe handling by the final node" } case class FinalIncorrectCltvExpiry(expiry: Long) extends FailureMessage { def message = "payment expiry doesn't match the value in the onion" } -case class FinalIncorrectHtlcAmount(amountMsat: Long) extends FailureMessage { def message = "payment amount is incorrect in the final htlc" } +case class FinalIncorrectHtlcAmount(amount: MilliSatoshi) extends FailureMessage { def message = "payment amount is incorrect in the final htlc" } case object ExpiryTooFar extends FailureMessage { def message = "payment expiry is too far in the future" } // @formatter:on @@ -85,16 +86,16 @@ object FailureMessageCodecs { .typecase(PERM | 8, provide(PermanentChannelFailure)) .typecase(PERM | 9, provide(RequiredChannelFeatureMissing)) .typecase(PERM | 10, provide(UnknownNextPeer)) - .typecase(UPDATE | 11, (("amountMsat" | uint64overflow) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[AmountBelowMinimum]) - .typecase(UPDATE | 12, (("amountMsat" | uint64overflow) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[FeeInsufficient]) + .typecase(UPDATE | 11, (("amountMsat" | millisatoshi) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[AmountBelowMinimum]) + .typecase(UPDATE | 12, (("amountMsat" | millisatoshi) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[FeeInsufficient]) .typecase(UPDATE | 13, (("expiry" | uint32) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[IncorrectCltvExpiry]) .typecase(UPDATE | 14, ("channelUpdate" | channelUpdateWithLengthCodec).as[ExpiryTooSoon]) .typecase(UPDATE | 20, (("messageFlags" | byte) :: ("channelFlags" | byte) :: ("channelUpdate" | channelUpdateWithLengthCodec)).as[ChannelDisabled]) - .typecase(PERM | 15, ("amountMsat" | withDefaultValue(optional(bitsRemaining, uint64overflow), 0L)).as[IncorrectOrUnknownPaymentDetails]) + .typecase(PERM | 15, ("amountMsat" | withDefaultValue(optional(bitsRemaining, millisatoshi), MilliSatoshi(0))).as[IncorrectOrUnknownPaymentDetails]) .typecase(PERM | 16, provide(IncorrectPaymentAmount)) .typecase(17, provide(FinalExpiryTooSoon)) .typecase(18, ("expiry" | uint32).as[FinalIncorrectCltvExpiry]) - .typecase(19, ("amountMsat" | uint64overflow).as[FinalIncorrectHtlcAmount]) + .typecase(19, ("amountMsat" | millisatoshi).as[FinalIncorrectHtlcAmount]) .typecase(21, provide(ExpiryTooFar)) /** diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala index b7e3891c43..055d9ffa16 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageCodecs.scala @@ -16,9 +16,11 @@ package fr.acinq.eclair.wire -import fr.acinq.eclair.wire +import fr.acinq.eclair.crypto.Sphinx +import fr.acinq.eclair.{MilliSatoshi, wire} import fr.acinq.eclair.wire.CommonCodecs._ import scodec.Codec +import scodec.bits.ByteVector import scodec.codecs._ /** @@ -51,12 +53,12 @@ object LightningMessageCodecs { val openChannelCodec: Codec[OpenChannel] = ( ("chainHash" | bytes32) :: ("temporaryChannelId" | bytes32) :: - ("fundingSatoshis" | uint64overflow) :: - ("pushMsat" | uint64overflow) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("fundingSatoshis" | satoshi) :: + ("pushMsat" | millisatoshi) :: + ("dustLimitSatoshis" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserveSatoshis" | satoshi) :: + ("htlcMinimumMsat" | millisatoshi) :: ("feeratePerKw" | uint32) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: @@ -70,10 +72,10 @@ object LightningMessageCodecs { val acceptChannelCodec: Codec[AcceptChannel] = ( ("temporaryChannelId" | bytes32) :: - ("dustLimitSatoshis" | uint64overflow) :: + ("dustLimitSatoshis" | satoshi) :: ("maxHtlcValueInFlightMsat" | uint64) :: - ("channelReserveSatoshis" | uint64overflow) :: - ("htlcMinimumMsat" | uint64overflow) :: + ("channelReserveSatoshis" | satoshi) :: + ("htlcMinimumMsat" | millisatoshi) :: ("minimumDepth" | uint32) :: ("toSelfDelay" | uint16) :: ("maxAcceptedHtlcs" | uint16) :: @@ -104,13 +106,13 @@ object LightningMessageCodecs { val closingSignedCodec: Codec[ClosingSigned] = ( ("channelId" | bytes32) :: - ("feeSatoshis" | uint64overflow) :: + ("feeSatoshis" | satoshi) :: ("signature" | bytes64)).as[ClosingSigned] val updateAddHtlcCodec: Codec[UpdateAddHtlc] = ( ("channelId" | bytes32) :: ("id" | uint64overflow) :: - ("amountMsat" | uint64overflow) :: + ("amountMsat" | millisatoshi) :: ("paymentHash" | bytes32) :: ("expiry" | uint32) :: ("onionRoutingPacket" | OnionCodecs.paymentOnionPacketCodec)).as[UpdateAddHtlc] @@ -189,10 +191,10 @@ object LightningMessageCodecs { (("messageFlags" | byte) >>:~ { messageFlags => ("channelFlags" | byte) :: ("cltvExpiryDelta" | uint16) :: - ("htlcMinimumMsat" | uint64overflow) :: - ("feeBaseMsat" | uint32) :: + ("htlcMinimumMsat" | millisatoshi) :: + ("feeBaseMsat" | uint32.xmapc(l => MilliSatoshi(l))(_.amount)) :: ("feeProportionalMillionths" | uint32) :: - ("htlcMaximumMsat" | conditional((messageFlags & 1) != 0, uint64overflow)) :: + ("htlcMaximumMsat" | conditional((messageFlags & 1) != 0, millisatoshi)) :: ("unknownFields" | bytes) }) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala index 89be06884a..57d3738918 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/LightningMessageTypes.scala @@ -20,9 +20,9 @@ import java.net.{Inet4Address, Inet6Address, InetAddress, InetSocketAddress} import java.nio.charset.StandardCharsets import com.google.common.base.Charsets +import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Satoshi} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{ByteVector32, ByteVector64} -import fr.acinq.eclair.{ShortChannelId, UInt64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, UInt64} import scodec.bits.ByteVector import scala.util.Try @@ -68,12 +68,12 @@ case class ChannelReestablish(channelId: ByteVector32, case class OpenChannel(chainHash: ByteVector32, temporaryChannelId: ByteVector32, - fundingSatoshis: Long, - pushMsat: Long, - dustLimitSatoshis: Long, + fundingSatoshis: Satoshi, + pushMsat: MilliSatoshi, + dustLimitSatoshis: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserveSatoshis: Satoshi, + htlcMinimumMsat: MilliSatoshi, feeratePerKw: Long, toSelfDelay: Int, maxAcceptedHtlcs: Int, @@ -86,10 +86,10 @@ case class OpenChannel(chainHash: ByteVector32, channelFlags: Byte) extends ChannelMessage with HasTemporaryChannelId with HasChainHash case class AcceptChannel(temporaryChannelId: ByteVector32, - dustLimitSatoshis: Long, + dustLimitSatoshis: Satoshi, maxHtlcValueInFlightMsat: UInt64, - channelReserveSatoshis: Long, - htlcMinimumMsat: Long, + channelReserveSatoshis: Satoshi, + htlcMinimumMsat: MilliSatoshi, minimumDepth: Long, toSelfDelay: Int, maxAcceptedHtlcs: Int, @@ -115,12 +115,12 @@ case class Shutdown(channelId: ByteVector32, scriptPubKey: ByteVector) extends ChannelMessage with HasChannelId case class ClosingSigned(channelId: ByteVector32, - feeSatoshis: Long, + feeSatoshis: Satoshi, signature: ByteVector64) extends ChannelMessage with HasChannelId case class UpdateAddHtlc(channelId: ByteVector32, id: Long, - amountMsat: Long, + amountMsat: MilliSatoshi, paymentHash: ByteVector32, cltvExpiry: Long, onionRoutingPacket: OnionRoutingPacket) extends HtlcMessage with UpdateMessage with HasChannelId @@ -217,10 +217,10 @@ case class ChannelUpdate(signature: ByteVector64, messageFlags: Byte, channelFlags: Byte, cltvExpiryDelta: Int, - htlcMinimumMsat: Long, - feeBaseMsat: Long, + htlcMinimumMsat: MilliSatoshi, + feeBaseMsat: MilliSatoshi, feeProportionalMillionths: Long, - htlcMaximumMsat: Option[Long], + htlcMaximumMsat: Option[MilliSatoshi], unknownFields: ByteVector = ByteVector.empty) extends RoutingMessage with HasTimestamp with HasChainHash { require(((messageFlags & 1) != 0) == htlcMaximumMsat.isDefined, "htlcMaximumMsat is not consistent with messageFlags") } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala index 32d942f547..716f11b16e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/Onion.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.crypto.Sphinx import scodec.bits.{BitVector, ByteVector} import scodec.codecs._ @@ -33,7 +33,7 @@ case class OnionRoutingPacket(version: Int, hmac: ByteVector32) case class PerHopPayload(shortChannelId: ShortChannelId, - amtToForward: Long, + amtToForward: MilliSatoshi, outgoingCltvValue: Long) object OnionCodecs { @@ -49,7 +49,7 @@ object OnionCodecs { val perHopPayloadCodec: Codec[PerHopPayload] = ( ("realm" | constant(ByteVector.fromByte(0))) :: ("short_channel_id" | CommonCodecs.shortchannelid) :: - ("amt_to_forward" | CommonCodecs.uint64overflow) :: + ("amt_to_forward" | CommonCodecs.millisatoshi) :: ("outgoing_cltv_value" | uint32) :: ("unused_with_v0_version_on_header" | ignore(8 * 12))).as[PerHopPayload] diff --git a/eclair-core/src/test/resources/api/usablebalances b/eclair-core/src/test/resources/api/usablebalances index edbd4e5a97..c1ef6b4920 100644 --- a/eclair-core/src/test/resources/api/usablebalances +++ b/eclair-core/src/test/resources/api/usablebalances @@ -1 +1 @@ -[{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x1","canSendMsat":100000000,"canReceiveMsat":20000000,"isPublic":true},{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x2","canSendMsat":400000000,"canReceiveMsat":30000000,"isPublic":false}] \ No newline at end of file +[{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x1","canSend":100000000,"canReceive":20000000,"isPublic":true},{"remoteNodeId":"03af0ed6052cf28d670665549bc86f4b721c9fdb309d40c58f5811f63966e005d0","shortChannelId":"0x0x2","canSend":400000000,"canReceive":30000000,"isPublic":false}] \ No newline at end of file diff --git a/eclair-core/src/test/resources/scenarii/01-offer1.script.expected b/eclair-core/src/test/resources/scenarii/01-offer1.script.expected index c3444f106f..5680bafccc 100644 --- a/eclair-core/src/test/resources/scenarii/01-offer1.script.expected +++ b/eclair-core/src/test/resources/scenarii/01-offer1.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) + Offered htlcs: (0,MilliSatoshi(1000000)) Received htlcs: - Balance us: 999000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) - Balance us: 1000000000 - Balance them: 999000000 + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) - Balance us: 1000000000 - Balance them: 999000000 + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) + Offered htlcs: (0,MilliSatoshi(1000000)) Received htlcs: - Balance us: 999000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/02-offer2.script.expected b/eclair-core/src/test/resources/scenarii/02-offer2.script.expected index b12ec47413..2d71ea0dea 100644 --- a/eclair-core/src/test/resources/scenarii/02-offer2.script.expected +++ b/eclair-core/src/test/resources/scenarii/02-offer2.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) (1,2000000) + Offered htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Received htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 1: Offered htlcs: - Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Received htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) (1,2000000) + Offered htlcs: (0,MilliSatoshi(1000000)) (1,MilliSatoshi(2000000)) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected b/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected index 1ffcada1d7..3c3fff0fe3 100644 --- a/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected +++ b/eclair-core/src/test/resources/scenarii/03-fulfill1.script.expected @@ -3,28 +3,28 @@ LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 999000000 - Balance them: 1001000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1001000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 1001000000 - Balance them: 999000000 + Balance us: MilliSatoshi(1001000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 1001000000 - Balance them: 999000000 + Balance us: MilliSatoshi(1001000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: - Balance us: 999000000 - Balance them: 1001000000 + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(1001000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected b/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected index f9eacd72d4..965efa4bc8 100644 --- a/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected +++ b/eclair-core/src/test/resources/scenarii/04-two-commits-onedir.script.expected @@ -3,28 +3,28 @@ LOCAL COMMITS: Commit 1: Offered htlcs: (0,1000000) (1,2000000) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: Offered htlcs: Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: Offered htlcs: Received htlcs: (0,1000000) (1,2000000) - Balance us: 1000000000 - Balance them: 997000000 + Balance us: MilliSatoshi(1000000000) + Balance them: MilliSatoshi(997000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: Offered htlcs: (0,1000000) (1,2000000) Received htlcs: - Balance us: 997000000 - Balance them: 1000000000 + Balance us: MilliSatoshi(997000000) + Balance them: MilliSatoshi(1000000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected b/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected index dfbeddddb2..51469b487b 100644 --- a/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected +++ b/eclair-core/src/test/resources/scenarii/10-offers-crossover.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 1: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 1: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 diff --git a/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected b/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected index bf1061fa9a..2c52101d82 100644 --- a/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected +++ b/eclair-core/src/test/resources/scenarii/11-commits-crossover.script.expected @@ -1,30 +1,30 @@ ***A*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 ***B*** LOCAL COMMITS: Commit 2: - Offered htlcs: (0,2000000) - Received htlcs: (0,1000000) - Balance us: 998000000 - Balance them: 999000000 + Offered htlcs: (0,MilliSatoshi(2000000)) + Received htlcs: (0,MilliSatoshi(1000000)) + Balance us: MilliSatoshi(998000000) + Balance them: MilliSatoshi(999000000) Fee rate: 10000 REMOTE COMMITS: Commit 2: - Offered htlcs: (0,1000000) - Received htlcs: (0,2000000) - Balance us: 999000000 - Balance them: 998000000 + Offered htlcs: (0,MilliSatoshi(1000000)) + Received htlcs: (0,MilliSatoshi(2000000)) + Balance us: MilliSatoshi(999000000) + Balance them: MilliSatoshi(998000000) Fee rate: 10000 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala index ea294912a5..8e051c2c93 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/CoinUtilsSpec.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair -import fr.acinq.bitcoin.{Btc, MilliBtc, MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Btc, MilliBtc, Satoshi} import org.scalatest.FunSuite diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala index 2ab1b0b329..728815f5a3 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/EclairImplSpec.scala @@ -16,18 +16,14 @@ package fr.acinq.eclair -import java.io.File - import akka.actor.ActorSystem import akka.testkit.{TestKit, TestProbe} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} -import fr.acinq.bitcoin.{ByteVector32, Crypto} +import fr.acinq.bitcoin.{ByteVector32, Crypto, Satoshi} import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.eclair.blockchain.TestWallet import fr.acinq.eclair.io.Peer.OpenChannel import fr.acinq.eclair.payment.PaymentLifecycle.{ReceivePayment, SendPayment, SendPaymentToRoute} -import org.scalatest.{Outcome, fixture} import fr.acinq.eclair.payment.PaymentLifecycle.SendPayment import fr.acinq.eclair.payment.PaymentRequest.ExtraHop import org.scalatest.{Matchers, Outcome, fixture} @@ -37,10 +33,8 @@ import fr.acinq.eclair.channel.{CMD_FORCECLOSE, Register} import fr.acinq.eclair.payment.LocalPaymentHandler import fr.acinq.eclair.channel._ import fr.acinq.eclair.db._ -import fr.acinq.eclair.payment.PaymentRequest import fr.acinq.eclair.router.RouteCalculationSpec.makeUpdate import org.mockito.scalatest.IdiomaticMockito - import scala.concurrent.Await import scala.util.{Failure, Success} import scala.concurrent.duration._ @@ -84,12 +78,12 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val nodeId = PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87") // standard conversion - eclair.open(nodeId, fundingSatoshis = 10000000L, pushMsat_opt = None, fundingFeerateSatByte_opt = Some(5), flags_opt = None, openTimeout_opt = None) + eclair.open(nodeId, fundingAmount = Satoshi(10000000L), pushAmount_opt = None, fundingFeerateSatByte_opt = Some(5), flags_opt = None, openTimeout_opt = None) val open = switchboard.expectMsgType[OpenChannel] assert(open.fundingTxFeeratePerKw_opt == Some(1250)) // check that minimum fee rate of 253 sat/bw is used - eclair.open(nodeId, fundingSatoshis = 10000000L, pushMsat_opt = None, fundingFeerateSatByte_opt = Some(1), flags_opt = None, openTimeout_opt = None) + eclair.open(nodeId, fundingAmount = Satoshi(10000000L), pushAmount_opt = None, fundingFeerateSatByte_opt = Some(1), flags_opt = None, openTimeout_opt = None) val open1 = switchboard.expectMsgType[OpenChannel] assert(open1.fundingTxFeeratePerKw_opt == Some(MinimumFeeratePerKw)) } @@ -100,37 +94,37 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val eclair = new EclairImpl(kit) val nodeId = PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87") - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None) val send = paymentInitiator.expectMsgType[SendPayment] assert(send.targetNodeId == nodeId) - assert(send.amountMsat == 123) + assert(send.amount == MilliSatoshi(123)) assert(send.paymentHash == ByteVector32.Zeroes) assert(send.assistedRoutes == Seq.empty) // with assisted routes val hints = Seq(Seq(ExtraHop(Bob.nodeParams.nodeId, ShortChannelId("569178x2331x1"), feeBaseMsat = 10, feeProportionalMillionths = 1, cltvExpiryDelta = 12))) - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = hints, minFinalCltvExpiry_opt = None) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = hints, minFinalCltvExpiry_opt = None) val send1 = paymentInitiator.expectMsgType[SendPayment] assert(send1.targetNodeId == nodeId) - assert(send1.amountMsat == 123) + assert(send1.amount == MilliSatoshi(123)) assert(send1.paymentHash == ByteVector32.Zeroes) assert(send1.assistedRoutes == hints) // with finalCltvExpiry - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = Some(96)) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = Some(96)) val send2 = paymentInitiator.expectMsgType[SendPayment] assert(send2.targetNodeId == nodeId) - assert(send2.amountMsat == 123) + assert(send2.amount == MilliSatoshi(123)) assert(send2.paymentHash == ByteVector32.Zeroes) assert(send2.finalCltvExpiry == 96) // with custom route fees parameters - eclair.send(recipientNodeId = nodeId, amountMsat = 123, paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None, feeThresholdSat_opt = Some(123), maxFeePct_opt = Some(4.20)) + eclair.send(recipientNodeId = nodeId, amount = MilliSatoshi(123), paymentHash = ByteVector32.Zeroes, assistedRoutes = Seq.empty, minFinalCltvExpiry_opt = None, feeThreshold_opt = Some(Satoshi(123)), maxFeePct_opt = Some(4.20)) val send3 = paymentInitiator.expectMsgType[SendPayment] assert(send3.targetNodeId == nodeId) - assert(send3.amountMsat == 123) + assert(send3.amount == MilliSatoshi(123)) assert(send3.paymentHash == ByteVector32.Zeroes) - assert(send3.routeParams.get.maxFeeBaseMsat == 123 * 1000) // conversion sat -> msat + assert(send3.routeParams.get.maxFeeBase == Satoshi(123).toMilliSatoshi) // conversion sat -> msat assert(send3.routeParams.get.maxFeePct == 4.20) } @@ -140,11 +134,11 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val (a, b, c, d, e) = (randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey) val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 13), - makeUpdate(4L, a, e, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdate(2L, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(3L, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(7L, e, c, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 13), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12) ).toMap val eclair = new EclairImpl(kit) @@ -188,15 +182,15 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val fallBackAddressRaw = "muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt" val eclair = new EclairImpl(kit) - eclair.receive("some desc", Some(123L), Some(456), Some(fallBackAddressRaw), None) + eclair.receive("some desc", Some(MilliSatoshi(123L)), Some(456), Some(fallBackAddressRaw), None) val receive = paymentHandler.expectMsgType[ReceivePayment] - assert(receive.amountMsat_opt == Some(MilliSatoshi(123L))) + assert(receive.amount_opt == Some(MilliSatoshi(123L))) assert(receive.expirySeconds_opt == Some(456)) assert(receive.fallbackAddress == Some(fallBackAddressRaw)) // try with wrong address format - assertThrows[IllegalArgumentException](eclair.receive("some desc", Some(123L), Some(456), Some("wassa wassa"), None)) + assertThrows[IllegalArgumentException](eclair.receive("some desc", Some(MilliSatoshi(123L)), Some(456), Some("wassa wassa"), None)) } test("passing a payment_preimage to /createinvoice should result in an invoice with payment_hash=H(payment_preimage)") { fixture => @@ -206,7 +200,7 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val eclair = new EclairImpl(kitWithPaymentHandler) val paymentPreimage = randomBytes32 - val fResp = eclair.receive(description = "some desc", amountMsat_opt = None, expire_opt = None, fallbackAddress_opt = None, paymentPreimage_opt = Some(paymentPreimage)) + val fResp = eclair.receive(description = "some desc", amount_opt = None, expire_opt = None, fallbackAddress_opt = None, paymentPreimage_opt = Some(paymentPreimage)) awaitCond({ fResp.value match { case Some(Success(pr)) => pr.paymentHash == Crypto.sha256(paymentPreimage) @@ -252,12 +246,12 @@ class EclairImplSpec extends TestKit(ActorSystem("mySystem")) with fixture.FunSu val route = Seq(PublicKey(hex"030bb6a5e0c6b203c7e2180fb78c7ba4bdce46126761d8201b91ddac089cdecc87")) val eclair = new EclairImpl(kit) - eclair.sendToRoute(route, 1234, ByteVector32.One, 123) + eclair.sendToRoute(route, MilliSatoshi(1234), ByteVector32.One, 123) val send = paymentInitiator.expectMsgType[SendPaymentToRoute] assert(send.hops == route) - assert(send.amountMsat == 1234) + assert(send.amount == MilliSatoshi(1234)) assert(send.finalCltvExpiry == 123) assert(send.paymentHash == ByteVector32.One) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala index 3f5e0b3744..9c340e5a8e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair import java.sql.{Connection, DriverManager} import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{Block, ByteVector32, Script} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, Script} import fr.acinq.eclair.NodeParams.BITCOIND import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, FeeratesPerKw, OnChainFeeConf} import fr.acinq.eclair.crypto.LocalKeyManager @@ -36,8 +36,8 @@ import scala.concurrent.duration._ */ object TestConstants { - val fundingSatoshis = 1000000L - val pushMsat = 200000000L + val fundingSatoshis = Satoshi(1000000L) + val pushMsat = MilliSatoshi(200000000L) val feeratePerKw = 10000L val emptyOnionPacket = wire.OnionRoutingPacket(0, ByteVector.fill(33)(0), ByteVector.fill(1300)(0), ByteVector32.Zeroes) @@ -70,7 +70,7 @@ object TestConstants { globalFeatures = ByteVector.empty, localFeatures = ByteVector(0), overrideFeatures = Map.empty, - dustLimitSatoshis = 1100, + dustLimit = Satoshi(1100), onChainFeeConf = OnChainFeeConf( feeTargets = FeeTargets(6, 2, 2, 6), feeEstimator = new TestFeeEstimator, @@ -81,11 +81,11 @@ object TestConstants { maxAcceptedHtlcs = 100, expiryDeltaBlocks = 144, fulfillSafetyBeforeTimeoutBlocks = 6, - htlcMinimumMsat = 0, + htlcMinimum = MilliSatoshi(0), minDepthBlocks = 3, toRemoteDelayBlocks = 144, maxToLocalDelayBlocks = 1000, - feeBaseMsat = 546000, + feeBase = MilliSatoshi(546000), feeProportionalMillionth = 10, reserveToFundingRatio = 0.01, // note: not used (overridden below) maxReserveToFundingRatio = 0.05, @@ -101,12 +101,12 @@ object TestConstants { channelFlags = 1, watcherType = BITCOIND, paymentRequestExpiry = 1 hour, - minFundingSatoshis = 1000L, + minFundingSatoshis = Satoshi(1000L), routerConf = RouterConf( randomizeRouteSelection = false, channelExcludeDuration = 60 seconds, routerBroadcastInterval = 5 seconds, - searchMaxFeeBaseSat = 21, + searchMaxFeeBase = Satoshi(21), searchMaxFeePct = 0.03, searchMaxCltv = 2016, searchMaxRouteLength = 20, @@ -124,7 +124,7 @@ object TestConstants { defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(randomBytes32).publicKey)), isFunder = true, fundingSatoshis).copy( - channelReserveSatoshis = 10000 // Bob will need to keep that much satoshis as direct payment + channelReserve = Satoshi(10000) // Bob will need to keep that much satoshis as direct payment ) } @@ -140,7 +140,7 @@ object TestConstants { globalFeatures = ByteVector.empty, localFeatures = ByteVector.empty, // no announcement overrideFeatures = Map.empty, - dustLimitSatoshis = 1000, + dustLimit = Satoshi(1000), onChainFeeConf = OnChainFeeConf( feeTargets = FeeTargets(6, 2, 2, 6), feeEstimator = new TestFeeEstimator, @@ -151,11 +151,11 @@ object TestConstants { maxAcceptedHtlcs = 30, expiryDeltaBlocks = 144, fulfillSafetyBeforeTimeoutBlocks = 6, - htlcMinimumMsat = 1000, + htlcMinimum = MilliSatoshi(1000), minDepthBlocks = 3, toRemoteDelayBlocks = 144, maxToLocalDelayBlocks = 1000, - feeBaseMsat = 546000, + feeBase = MilliSatoshi(546000), feeProportionalMillionth = 10, reserveToFundingRatio = 0.01, // note: not used (overridden below) maxReserveToFundingRatio = 0.05, @@ -171,12 +171,12 @@ object TestConstants { channelFlags = 1, watcherType = BITCOIND, paymentRequestExpiry = 1 hour, - minFundingSatoshis = 1000L, + minFundingSatoshis = Satoshi(1000L), routerConf = RouterConf( randomizeRouteSelection = false, channelExcludeDuration = 60 seconds, routerBroadcastInterval = 5 seconds, - searchMaxFeeBaseSat = 21, + searchMaxFeeBase = Satoshi(21), searchMaxFeePct = 0.03, searchMaxCltv = 2016, searchMaxRouteLength = 20, @@ -194,7 +194,7 @@ object TestConstants { defaultFinalScriptPubKey = Script.write(Script.pay2wpkh(PrivateKey(randomBytes32).publicKey)), isFunder = false, fundingSatoshis).copy( - channelReserveSatoshis = 20000 // Alice will need to keep that much satoshis as direct payment + channelReserve = Satoshi(20000) // Alice will need to keep that much satoshis as direct payment ) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala index 4c3f5d9e9d..87fbffeefe 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/api/ApiServiceSpec.scala @@ -27,7 +27,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest, WSProbe import akka.stream.ActorMaterializer import akka.util.Timeout import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants._ import fr.acinq.eclair._ import fr.acinq.eclair.io.NodeURI @@ -39,6 +39,7 @@ import org.json4s.jackson.Serialization import org.mockito.scalatest.IdiomaticMockito import org.scalatest.{FunSuite, Matchers} import scodec.bits._ + import scala.concurrent.Future import scala.concurrent.duration._ import scala.io.Source @@ -147,8 +148,8 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock val eclair = mock[Eclair] val mockService = new MockService(eclair) eclair.usableBalances()(any[Timeout]) returns Future.successful(List( - UsableBalances(canSendMsat = 100000000, canReceiveMsat = 20000000, shortChannelId = ShortChannelId(1), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = true), - UsableBalances(canSendMsat = 400000000, canReceiveMsat = 30000000, shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false) + UsableBalances(canSend = MilliSatoshi(100000000), canReceive = MilliSatoshi(20000000), shortChannelId = ShortChannelId(1), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = true), + UsableBalances(canSend = MilliSatoshi(400000000), canReceive = MilliSatoshi(30000000), shortChannelId = ShortChannelId(2), remoteNodeId = TestConstants.Alice.keyManager.nodeKey.publicKey, isPublic = false) )) Post("/usablebalances") ~> @@ -269,7 +270,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock check { assert(handled) assert(status == OK) - eclair.send(any, 1258000, any, any, any, any, any, any)(any[Timeout]).wasCalled(once) + eclair.send(any, MilliSatoshi(1258000), any, any, any, any, any, any)(any[Timeout]).wasCalled(once) } @@ -279,7 +280,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock check { assert(handled) assert(status == OK) - eclair.send(any, 123, any, any, any, any, Some(112233), Some(2.34))(any[Timeout]).wasCalled(once) + eclair.send(any, MilliSatoshi(123), any, any, any, any, Some(Satoshi(112233)), Some(2.34))(any[Timeout]).wasCalled(once) } } @@ -311,7 +312,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock val jsonNodes = serialization.write(expectedRoute) val eclair = mock[Eclair] - eclair.sendToRoute(any[List[PublicKey]], anyLong, any[ByteVector32], anyLong)(any[Timeout]) returns Future.successful(paymentUUID) + eclair.sendToRoute(any[List[PublicKey]], any[MilliSatoshi], any[ByteVector32], anyLong)(any[Timeout]) returns Future.successful(paymentUUID) val mockService = new MockService(eclair) Post("/sendtoroute", FormData("route" -> jsonNodes, "amountMsat" -> "1234", "paymentHash" -> ByteVector32.Zeroes.toHex, "finalCltvExpiry" -> "190").toEntity) ~> @@ -322,7 +323,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock assert(handled) assert(status == OK) assert(entityAs[String] == "\""+rawUUID+"\"") - eclair.sendToRoute(expectedRoute, 1234, ByteVector32.Zeroes, 190)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(expectedRoute, MilliSatoshi(1234), ByteVector32.Zeroes, 190)(any[Timeout]).wasCalled(once) } // this test uses CSV encoded route @@ -334,7 +335,7 @@ class ApiServiceSpec extends FunSuite with ScalatestRouteTest with IdiomaticMock assert(handled) assert(status == OK) assert(entityAs[String] == "\""+rawUUID+"\"") - eclair.sendToRoute(expectedRoute, 1234, ByteVector32.One, 190)(any[Timeout]).wasCalled(once) + eclair.sendToRoute(expectedRoute, MilliSatoshi(1234), ByteVector32.One, 190)(any[Timeout]).wasCalled(once) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala index 4313c853e6..38a6b6d7cc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/api/JsonSerializersSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.api import java.net.InetAddress import java.util.UUID -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi, OutPoint, Transaction} +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.payment.{PaymentRequest, PaymentSettlingOnChain} import fr.acinq.eclair.api.JsonSupport.CustomTypeHints diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala index 3af10b6426..50fb3666ef 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/BitcoinCoreWalletSpec.scala @@ -257,7 +257,7 @@ class BitcoinCoreWalletSpec extends TestKit(ActorSystem("test")) with BitcoindSe val tx1 = Transaction.read(signedTx1) // let's then generate another tx that double spends the first one val inputs = tx1.txIn.map(txIn => Map("txid" -> txIn.outPoint.txid.toString, "vout" -> txIn.outPoint.index)).toArray - bitcoinClient.invoke("createrawtransaction", inputs, Map(address -> tx1.txOut.map(_.amount.toLong).sum * 1.0 / 1e8)).pipeTo(sender.ref) + bitcoinClient.invoke("createrawtransaction", inputs, Map(address -> tx1.txOut.map(_.amount).sum.toLong * 1.0 / 1e8)).pipeTo(sender.ref) val JString(unsignedtx2) = sender.expectMsgType[JValue] bitcoinClient.invoke("signrawtransactionwithwallet", unsignedtx2).pipeTo(sender.ref) val JString(signedTx2) = sender.expectMsgType[JValue] \ "hex" diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala index d44d98dc94..188ceada39 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/FuzzySpec.scala @@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch import akka.actor.{Actor, ActorLogging, ActorRef, Props, Status} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ import fr.acinq.eclair.blockchain._ @@ -93,7 +93,7 @@ class FuzzySpec extends TestkitBaseClass with StateTestsHelperMethods with Loggi def buildCmdAdd(paymentHash: ByteVector32, dest: PublicKey) = { // allow overpaying (no more than 2 times the required amount) - val amount = requiredAmount + Random.nextInt(requiredAmount) + val amount = MilliSatoshi(requiredAmount + Random.nextInt(requiredAmount)) val expiry = Globals.blockCount.get().toInt + Channel.MIN_CLTV_EXPIRY + 1 PaymentLifecycle.buildCommand(UUID.randomUUID(), amount, expiry, paymentHash, Hop(null, dest, null) :: Nil)._1 } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala index 9ff1329b8e..08aa1f88d6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/ThroughputSpec.scala @@ -51,7 +51,7 @@ class ThroughputSpec extends FunSuite { case ('add, tgt: ActorRef) => val r = randomBytes32 val h = Crypto.sha256(r) - tgt ! CMD_ADD_HTLC(1, h, 1, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + tgt ! CMD_ADD_HTLC(MilliSatoshi(1), h, 1, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) context.become(run(h2r + (h -> r))) case ('sig, tgt: ActorRef) => tgt ! CMD_SIGN diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala index ac21eeba31..e614c054d7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/StateTestsHelperMethods.scala @@ -20,6 +20,7 @@ import java.util.UUID import akka.testkit.{TestFSMRef, TestKitBase, TestProbe} import fr.acinq.bitcoin.{ByteVector32, Crypto} +import fr.acinq.eclair import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeTargets @@ -29,6 +30,7 @@ import fr.acinq.eclair.payment.PaymentLifecycle import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire._ import fr.acinq.eclair.{Globals, NodeParams, TestConstants, randomBytes32} +import fr.acinq.eclair._ /** * Created by PM on 23/08/2016. @@ -66,7 +68,7 @@ trait StateTestsHelperMethods extends TestKitBase { tags: Set[String] = Set.empty): Unit = { import setup._ val channelFlags = if (tags.contains("channels_public")) ChannelFlags.AnnounceChannel else ChannelFlags.Empty - val pushMsat = if (tags.contains("no_push_msat")) 0 else TestConstants.pushMsat + val pushMsat = if (tags.contains("no_push_msat")) MilliSatoshi(0) else TestConstants.pushMsat val (aliceParams, bobParams) = (Alice.channelParams, Bob.channelParams) val aliceInit = Init(aliceParams.globalFeatures, aliceParams.localFeatures) val bobInit = Init(bobParams.globalFeatures, bobParams.localFeatures) @@ -98,19 +100,19 @@ trait StateTestsHelperMethods extends TestKitBase { bob2blockchain.expectMsgType[WatchConfirmed] // deeply buried awaitCond(alice.stateName == NORMAL) awaitCond(bob.stateName == NORMAL) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSendMsat == math.max(pushMsat - TestConstants.Alice.channelParams.channelReserveSatoshis * 1000, 0)) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == maxOf(pushMsat - TestConstants.Alice.channelParams.channelReserve.toMilliSatoshi, MilliSatoshi(0))) // x2 because alice and bob share the same relayer channelUpdateListener.expectMsgType[LocalChannelUpdate] channelUpdateListener.expectMsgType[LocalChannelUpdate] } - def addHtlc(amountMsat: Int, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = { + def addHtlc(amount: MilliSatoshi, s: TestFSMRef[State, Data, Channel], r: TestFSMRef[State, Data, Channel], s2r: TestProbe, r2s: TestProbe): (ByteVector32, UpdateAddHtlc) = { val R: ByteVector32 = randomBytes32 val H: ByteVector32 = Crypto.sha256(R) val sender = TestProbe() val receiverPubkey = r.underlyingActor.nodeParams.nodeId val expiry = 400144 - val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amountMsat, expiry, H, Hop(null, receiverPubkey, null) :: Nil)._1.copy(commit = false) + val cmd = PaymentLifecycle.buildCommand(UUID.randomUUID, amount, expiry, H, Hop(null, receiverPubkey, null) :: Nil)._1.copy(commit = false) sender.send(s, cmd) sender.expectMsg("ok") val htlc = s2r.expectMsgType[UpdateAddHtlc] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala index b71fd9de45..980384bc58 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForAcceptChannelStateSpec.scala @@ -83,7 +83,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] // we don't want their dust limit to be below 546 - val lowDustLimitSatoshis = 545 + val lowDustLimitSatoshis = Satoshi(545) alice ! accept.copy(dustLimitSatoshis = lowDustLimitSatoshis) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitTooSmall(accept.temporaryChannelId, lowDustLimitSatoshis, Channel.MIN_DUSTLIMIT).getMessage)) @@ -104,7 +104,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] // 30% is huge, recommended ratio is 1% - val reserveTooHigh = (0.3 * TestConstants.fundingSatoshis).toLong + val reserveTooHigh = Satoshi((0.3 * TestConstants.fundingSatoshis.toLong).toLong) alice ! accept.copy(channelReserveSatoshis = reserveTooHigh) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, ChannelReserveTooHigh(accept.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage)) @@ -114,7 +114,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp test("recv AcceptChannel (reserve below dust limit)") { f => import f._ val accept = bob2alice.expectMsgType[AcceptChannel] - val reserveTooSmall = accept.dustLimitSatoshis - 1 + val reserveTooSmall = accept.dustLimitSatoshis - Satoshi(1) alice ! accept.copy(channelReserveSatoshis = reserveTooSmall) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitTooLarge(accept.temporaryChannelId, accept.dustLimitSatoshis, reserveTooSmall).getMessage)) @@ -125,7 +125,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] val open = alice.stateData.asInstanceOf[DATA_WAIT_FOR_ACCEPT_CHANNEL].lastSent - val reserveTooSmall = open.dustLimitSatoshis - 1 + val reserveTooSmall = open.dustLimitSatoshis - Satoshi(1) alice ! accept.copy(channelReserveSatoshis = reserveTooSmall) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, ChannelReserveBelowOurDustLimit(accept.temporaryChannelId, reserveTooSmall, open.dustLimitSatoshis).getMessage)) @@ -136,7 +136,7 @@ class WaitForAcceptChannelStateSpec extends TestkitBaseClass with StateTestsHelp import f._ val accept = bob2alice.expectMsgType[AcceptChannel] val open = alice.stateData.asInstanceOf[DATA_WAIT_FOR_ACCEPT_CHANNEL].lastSent - val dustTooBig = open.channelReserveSatoshis + 1 + val dustTooBig = open.channelReserveSatoshis + Satoshi(1) alice ! accept.copy(dustLimitSatoshis = dustTooBig) val error = alice2bob.expectMsgType[Error] assert(error === Error(accept.temporaryChannelId, DustLimitAboveOurChannelReserve(accept.temporaryChannelId, dustTooBig, open.channelReserveSatoshis).getMessage)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala index 6b74dfd654..549ee6f692 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/a/WaitForOpenChannelStateSpec.scala @@ -17,12 +17,12 @@ package fr.acinq.eclair.channel.states.a import akka.testkit.{TestFSMRef, TestProbe} -import fr.acinq.bitcoin.{Block, ByteVector32} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.wire.{Error, Init, OpenChannel} -import fr.acinq.eclair.{TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass, ToMilliSatoshiConversion} import org.scalatest.Outcome import scala.concurrent.duration._ @@ -69,20 +69,20 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (funding too low)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val lowFundingMsat = 100 - bob ! open.copy(fundingSatoshis = lowFundingMsat) + val lowFunding = Satoshi(100) + bob ! open.copy(fundingSatoshis = lowFunding) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, lowFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS).getMessage)) + assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, lowFunding, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING).getMessage)) awaitCond(bob.stateName == CLOSED) } test("recv OpenChannel (funding too high)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val highFundingMsat = 100000000 + val highFundingMsat = Satoshi(100000000) bob ! open.copy(fundingSatoshis = highFundingMsat) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, highFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING_SATOSHIS).getMessage)) + assert(error.toAscii === Error(open.temporaryChannelId, InvalidFundingAmount(open.temporaryChannelId, highFundingMsat, Bob.nodeParams.minFundingSatoshis, Channel.MAX_FUNDING).getMessage).toAscii) awaitCond(bob.stateName == CLOSED) } @@ -99,10 +99,10 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (invalid push_msat)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val invalidPushMsat = 100000000000L + val invalidPushMsat = MilliSatoshi(100000000000L) bob ! open.copy(pushMsat = invalidPushMsat) val error = bob2alice.expectMsgType[Error] - assert(error === Error(open.temporaryChannelId, InvalidPushAmount(open.temporaryChannelId, invalidPushMsat, 1000 * open.fundingSatoshis).getMessage)) + assert(error === Error(open.temporaryChannelId, InvalidPushAmount(open.temporaryChannelId, invalidPushMsat, open.fundingSatoshis.toMilliSatoshi).getMessage)) awaitCond(bob.stateName == CLOSED) } @@ -120,7 +120,7 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper import f._ val open = alice2bob.expectMsgType[OpenChannel] // 30% is huge, recommended ratio is 1% - val reserveTooHigh = (0.3 * TestConstants.fundingSatoshis).toLong + val reserveTooHigh = Satoshi((0.3 * TestConstants.fundingSatoshis.toLong).toLong) bob ! open.copy(channelReserveSatoshis = reserveTooHigh) val error = bob2alice.expectMsgType[Error] assert(error === Error(open.temporaryChannelId, ChannelReserveTooHigh(open.temporaryChannelId, reserveTooHigh, 0.3, 0.05).getMessage)) @@ -155,7 +155,7 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (reserve below dust)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val reserveTooSmall = open.dustLimitSatoshis - 1 + val reserveTooSmall = open.dustLimitSatoshis - Satoshi(1) bob ! open.copy(channelReserveSatoshis = reserveTooSmall) val error = bob2alice.expectMsgType[Error] // we check that the error uses the temporary channel id @@ -166,12 +166,12 @@ class WaitForOpenChannelStateSpec extends TestkitBaseClass with StateTestsHelper test("recv OpenChannel (toLocal + toRemote below reserve)") { f => import f._ val open = alice2bob.expectMsgType[OpenChannel] - val fundingSatoshis = open.channelReserveSatoshis + 499 + val fundingSatoshis = open.channelReserveSatoshis.toLong + 499 val pushMsat = 500 * 1000 - bob ! open.copy(fundingSatoshis = fundingSatoshis, pushMsat = pushMsat) + bob ! open.copy(fundingSatoshis = Satoshi(fundingSatoshis), pushMsat = MilliSatoshi(pushMsat)) val error = bob2alice.expectMsgType[Error] // we check that the error uses the temporary channel id - assert(error === Error(open.temporaryChannelId, ChannelReserveNotMet(open.temporaryChannelId, 500 * 1000, (open.channelReserveSatoshis - 1) * 1000, open.channelReserveSatoshis).getMessage)) + assert(error === Error(open.temporaryChannelId, ChannelReserveNotMet(open.temporaryChannelId, Satoshi(500).toMilliSatoshi, (open.channelReserveSatoshis - Satoshi(1)).toMilliSatoshi, open.channelReserveSatoshis).getMessage)) awaitCond(bob.stateName == CLOSED) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala index 3bc38c87cf..9a88921c3d 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForFundingCreatedStateSpec.scala @@ -17,14 +17,14 @@ package fr.acinq.eclair.channel.states.b import akka.testkit.{TestFSMRef, TestProbe} -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass} import org.scalatest.{Outcome, Tag} import scala.concurrent.duration._ @@ -41,7 +41,7 @@ class WaitForFundingCreatedStateSpec extends TestkitBaseClass with StateTestsHel val setup = init() import setup._ val (fundingSatoshis, pushMsat) = if (test.tags.contains("funder_below_reserve")) { - (1000100L, 1000000000L) // toRemote = 100 satoshis + (Satoshi(1000100L), MilliSatoshi(1000000000L)) // toRemote = 100 satoshis } else { (TestConstants.fundingSatoshis, TestConstants.pushMsat) } @@ -71,13 +71,13 @@ class WaitForFundingCreatedStateSpec extends TestkitBaseClass with StateTestsHel test("recv FundingCreated (funder can't pay fees)", Tag("funder_below_reserve")) { f => import f._ - val fees = Transactions.commitWeight * TestConstants.feeratePerKw / 1000 - val reserve = Bob.channelParams.channelReserveSatoshis - val missing = 100 - fees - reserve + val fees = Satoshi(Transactions.commitWeight * TestConstants.feeratePerKw / 1000) + val reserve = Bob.channelParams.channelReserve + val missing = Satoshi(100) - fees - reserve val fundingCreated = alice2bob.expectMsgType[FundingCreated] alice2bob.forward(bob) val error = bob2alice.expectMsgType[Error] - assert(error === Error(fundingCreated.temporaryChannelId, s"can't pay the fee: missingSatoshis=${-1 * missing} reserveSatoshis=$reserve feesSatoshis=$fees")) + assert(error === Error(fundingCreated.temporaryChannelId, s"can't pay the fee: missing=${-missing} reserve=$reserve fees=$fees")) awaitCond(bob.stateName == CLOSED) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala index e327d9cad6..2300d25e05 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalStateSpec.scala @@ -23,6 +23,7 @@ import akka.actor.Status.Failure import akka.testkit.TestProbe import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.{ByteVector32, ByteVector64, Crypto, Satoshi, ScriptFlags, Transaction} +import fr.acinq.eclair._ import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.UInt64.Conversions._ import fr.acinq.eclair.blockchain._ @@ -69,7 +70,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val sender = TestProbe() val h = randomBytes32 - val add = CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] @@ -87,7 +88,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val h = randomBytes32 for (i <- 0 until 10) { - sender.send(alice, CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] assert(htlc.id == i && htlc.paymentHash == h) @@ -99,8 +100,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val sender = TestProbe() val h = randomBytes32 - val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = 50000000, cltvExpiry = 400144, paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) - val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - 10000, h, originHtlc.cltvExpiry - 7, TestConstants.emptyOnionPacket, upstream = Right(originHtlc)) + val originHtlc = UpdateAddHtlc(channelId = randomBytes32, id = 5656, amountMsat = MilliSatoshi(50000000), cltvExpiry = 400144, paymentHash = h, onionRoutingPacket = TestConstants.emptyOnionPacket) + val cmd = CMD_ADD_HTLC(originHtlc.amountMsat - MilliSatoshi(10000), h, originHtlc.cltvExpiry - 7, TestConstants.emptyOnionPacket, upstream = Right(originHtlc)) sender.send(alice, cmd) sender.expectMsg("ok") val htlc = alice2bob.expectMsgType[UpdateAddHtlc] @@ -119,7 +120,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val currentBlockCount = Globals.blockCount.get val expiryTooSmall = currentBlockCount + 3 - val add = CMD_ADD_HTLC(500000000, randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, expiryTooSmall, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ExpiryTooSmall(channelId(alice), currentBlockCount + Channel.MIN_CLTV_EXPIRY, expiryTooSmall, currentBlockCount) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -132,7 +133,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] val currentBlockCount = Globals.blockCount.get val expiryTooBig = currentBlockCount + Channel.MAX_CLTV_EXPIRY + 1 - val add = CMD_ADD_HTLC(500000000, randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, expiryTooBig, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ExpiryTooBig(channelId(alice), maximum = currentBlockCount + Channel.MAX_CLTV_EXPIRY, actual = expiryTooBig, blockCount = currentBlockCount) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -143,9 +144,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(50, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(50), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = HtlcValueTooSmall(channelId(alice), 1000, 50) + val error = HtlcValueTooSmall(channelId(alice), MilliSatoshi(1000), MilliSatoshi(50)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -154,9 +155,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(Int.MaxValue, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(Int.MaxValue), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = Int.MaxValue, missingSatoshis = 1376443, reserveSatoshis = 20000, feesSatoshis = 8960) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(Int.MaxValue), missing = Satoshi(1376443), reserve = Satoshi(20000), fees = Satoshi(8960)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -165,18 +166,18 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - sender.send(alice, CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(200000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(200000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(67600000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(67600000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = 1000000, missingSatoshis = 1000, reserveSatoshis = 20000, feesSatoshis = 12400) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(1000000), missing = Satoshi(1000), reserve = Satoshi(20000), fees = Satoshi(12400)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -185,15 +186,15 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - sender.send(alice, CMD_ADD_HTLC(300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - sender.send(alice, CMD_ADD_HTLC(300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] - val add = CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) - val error = InsufficientFunds(channelId(alice), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400) + val error = InsufficientFunds(channelId(alice), amount = MilliSatoshi(500000000), missing = Satoshi(332400), reserve = Satoshi(20000), fees = Satoshi(12400)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) alice2bob.expectNoMsg(200 millis) } @@ -202,7 +203,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] - val add = CMD_ADD_HTLC(151000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(151000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(bob, add) val error = HtlcValueTooHighInFlight(channelId(bob), maximum = 150000000, actual = 151000000) sender.expectMsg(Failure(AddHtlcFailed(channelId(bob), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -215,11 +216,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] // Bob accepts a maximum of 30 htlcs for (i <- 0 until 30) { - sender.send(alice, CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] } - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = TooManyAcceptedHtlcs(channelId(alice), maximum = 30) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -230,7 +231,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis * 2 / 3 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -238,9 +239,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] // this is over channel-capacity - val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis * 2 / 3 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add2 = CMD_ADD_HTLC(TestConstants.fundingSatoshis.toMilliSatoshi * 2 / 3, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add2) - val error = InsufficientFunds(channelId(alice), add2.amountMsat, 564012, 20000, 10680) + val error = InsufficientFunds(channelId(alice), add2.amount, Satoshi(564013), Satoshi(20000), Satoshi(10680)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add2.paymentHash, error, Local(add2.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add2)))) alice2bob.expectNoMsg(200 millis) } @@ -255,7 +256,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].localShutdown.isDefined && alice.stateData.asInstanceOf[DATA_NORMAL].remoteShutdown.isEmpty) // actual test starts here - val add = CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = NoMoreHtlcsClosingInProgress(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), Some(initialState.channelUpdate), Some(add)))) @@ -267,14 +268,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] // let's make alice send an htlc - val add1 = CMD_ADD_HTLC(500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add1 = CMD_ADD_HTLC(MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add1) sender.expectMsg("ok") // at the same time bob initiates a closing sender.send(bob, CMD_CLOSE(None)) sender.expectMsg("ok") // this command will be received by alice right after having received the shutdown - val add2 = CMD_ADD_HTLC(100000000, randomBytes32, 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add2 = CMD_ADD_HTLC(MilliSatoshi(100000000), randomBytes32, 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) // messages cross alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -288,7 +289,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc") { f => import f._ val initialData = bob.stateData.asInstanceOf[DATA_NORMAL] - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150000, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(150000), randomBytes32, 400144, TestConstants.emptyOnionPacket) bob ! htlc awaitCond(bob.stateData == initialData.copy(commitments = initialData.commitments.copy(remoteChanges = initialData.commitments.remoteChanges.copy(proposed = initialData.commitments.remoteChanges.proposed :+ htlc), remoteNextHtlcId = 1))) // bob won't forward the add before it is cross-signed @@ -298,7 +299,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (unexpected id)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, 150000, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 42, MilliSatoshi(150000), randomBytes32, 400144, TestConstants.emptyOnionPacket) bob ! htlc.copy(id = 0) bob ! htlc.copy(id = 1) bob ! htlc.copy(id = 2) @@ -315,10 +316,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (value too small)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, 150, randomBytes32, cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(150), randomBytes32, cltvExpiry = 400144, TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === HtlcValueTooSmall(channelId(bob), minimum = 1000, actual = 150).getMessage) + assert(new String(error.data.toArray) === HtlcValueTooSmall(channelId(bob), minimum = MilliSatoshi(1000), actual = MilliSatoshi(150)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -330,10 +331,10 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, Long.MaxValue, randomBytes32, 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(Long.MaxValue), randomBytes32, 400144, TestConstants.emptyOnionPacket) alice2bob.forward(bob, htlc) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = Long.MaxValue, missingSatoshis = 9223372036083735L, reserveSatoshis = 20000, feesSatoshis = 8960).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(Long.MaxValue), missing = Satoshi(9223372036083735L), reserve = Satoshi(20000), fees = Satoshi(8960)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -345,12 +346,12 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 1/2)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 400000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 200000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 167600000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, 10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(400000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(200000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(167600000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 3, MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = 10000000, missingSatoshis = 11720, reserveSatoshis = 20000, feesSatoshis = 14120).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(10000000), missing = Satoshi(11720), reserve = Satoshi(20000), fees = Satoshi(14120)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -362,11 +363,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (insufficient funds w/ pending htlcs 2/2)") { f => import f._ val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, 300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, 300000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, 500000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(300000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(500000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amountMsat = 500000000, missingSatoshis = 332400, reserveSatoshis = 20000, feesSatoshis = 12400).getMessage) + assert(new String(error.data.toArray) === InsufficientFunds(channelId(bob), amount = MilliSatoshi(500000000), missing = Satoshi(332400), reserve = Satoshi(20000), fees = Satoshi(12400)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -378,7 +379,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateAddHtlc (over max inflight htlc value)") { f => import f._ val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx - alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, 151000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(alice, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(151000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = alice2bob.expectMsgType[Error] assert(new String(error.data.toArray) === HtlcValueTooHighInFlight(channelId(alice), maximum = 150000000, actual = 151000000).getMessage) awaitCond(alice.stateName == CLOSING) @@ -394,9 +395,9 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // Bob accepts a maximum of 30 htlcs for (i <- 0 until 30) { - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, 1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, i, MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) } - alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, 1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket)) + alice2bob.forward(bob, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket)) val error = bob2alice.expectMsgType[Error] assert(new String(error.data.toArray) === TooManyAcceptedHtlcs(channelId(bob), maximum = 30).getMessage) awaitCond(bob.stateName == CLOSING) @@ -410,7 +411,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") val commitSig = alice2bob.expectMsgType[CommitSig] @@ -421,7 +422,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (two identical htlcs in each direction)") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -453,34 +454,34 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() // for the test to be really useful we have constraint on parameters - assert(Alice.nodeParams.dustLimitSatoshis > Bob.nodeParams.dustLimitSatoshis) + assert(Alice.nodeParams.dustLimit > Bob.nodeParams.dustLimit) // we're gonna exchange two htlcs in each direction, the goal is to have bob's commitment have 4 htlcs, and alice's // commitment only have 3. We will then check that alice indeed persisted 4 htlcs, and bob only 3. - val aliceMinReceive = Alice.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight).toLong - val aliceMinOffer = Alice.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight).toLong - val bobMinReceive = Bob.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight).toLong - val bobMinOffer = Bob.nodeParams.dustLimitSatoshis + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight).toLong - val a2b_1 = bobMinReceive + 10 // will be in alice and bob tx - val a2b_2 = bobMinReceive + 20 // will be in alice and bob tx - val b2a_1 = aliceMinReceive + 10 // will be in alice and bob tx - val b2a_2 = bobMinOffer + 10 // will be only be in bob tx + val aliceMinReceive = Alice.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight) + val aliceMinOffer = Alice.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight) + val bobMinReceive = Bob.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcSuccessWeight) + val bobMinOffer = Bob.nodeParams.dustLimit + weight2fee(TestConstants.feeratePerKw, htlcTimeoutWeight) + val a2b_1 = bobMinReceive + Satoshi(10) // will be in alice and bob tx + val a2b_2 = bobMinReceive + Satoshi(20) // will be in alice and bob tx + val b2a_1 = aliceMinReceive + Satoshi(10) // will be in alice and bob tx + val b2a_2 = bobMinOffer + Satoshi(10) // will be only be in bob tx assert(a2b_1 > aliceMinOffer && a2b_1 > bobMinReceive) assert(a2b_2 > aliceMinOffer && a2b_2 > bobMinReceive) assert(b2a_1 > aliceMinReceive && b2a_1 > bobMinOffer) assert(b2a_2 < aliceMinReceive && b2a_2 > bobMinOffer) - sender.send(alice, CMD_ADD_HTLC(a2b_1 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_1.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(a2b_2 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(a2b_2.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(bob, CMD_ADD_HTLC(b2a_1 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_1.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) - sender.send(bob, CMD_ADD_HTLC(b2a_2 * 1000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(bob, CMD_ADD_HTLC(b2a_2.toMilliSatoshi, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") bob2alice.expectMsgType[UpdateAddHtlc] bob2alice.forward(alice) @@ -495,16 +496,16 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(alice.underlyingActor.nodeParams.db.channels.listHtlcInfos(alice.stateData.asInstanceOf[DATA_NORMAL].channelId, 2).size == 4) assert(bob.underlyingActor.nodeParams.db.channels.listHtlcInfos(bob.stateData.asInstanceOf[DATA_NORMAL].channelId, 0).size == 0) assert(bob.underlyingActor.nodeParams.db.channels.listHtlcInfos(bob.stateData.asInstanceOf[DATA_NORMAL].channelId, 1).size == 3) - } + } test("recv CMD_SIGN (htlcs with same pubkeyScript but different amounts)") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) val epsilons = List(3, 1, 5, 7, 6) // unordered on purpose val htlcCount = epsilons.size for (i <- epsilons) { - sender.send(alice, add.copy(amountMsat = add.amountMsat + i * 1000)) + sender.send(alice, add.copy(amount = MilliSatoshi(add.amount.toLong + i * 1000))) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -532,7 +533,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (while waiting for RevokeAndAck (no pending changes)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -550,7 +551,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_SIGN (while waiting for RevokeAndAck (with pending changes)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -560,7 +561,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { assert(waitForRevocation.reSignAsap === false) // actual test starts here - val (r2, htlc2) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r2, htlc2) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectNoMsg(300 millis) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo === Left(waitForRevocation.copy(reSignAsap = true))) @@ -572,7 +573,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // channel starts with all funds on alice's side, so channel will be initially disabled on bob's side assert(Announcements.isEnabled(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate.channelFlags) === false) // alice will send enough funds to bob to make it go above reserve - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r)) sender.expectMsg("ok") @@ -596,7 +597,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] sender.send(alice, CMD_SIGN) @@ -612,7 +613,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc.id && h.direction == IN)) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 1) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.acked.size == 0) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.signed.size == 1) } @@ -621,7 +622,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val initialState = bob.stateData.asInstanceOf[DATA_NORMAL] sender.send(alice, CMD_SIGN) @@ -637,26 +638,26 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc.id && h.direction == OUT)) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 1) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) } test("recv CommitSig (multiple htlcs in both directions)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r2, htlc2) = addHtlc(MilliSatoshi(8000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust) + val (r3, htlc3) = addHtlc(MilliSatoshi(300000), bob, alice, bob2alice, alice2bob) // b->a (dust) - val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r4, htlc4) = addHtlc(MilliSatoshi(1000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r5, htlc5) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) // b->a (regular) - val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust) + val (r6, htlc6) = addHtlc(MilliSatoshi(500000), alice, bob, alice2bob, bob2alice) // a->b (dust) - val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r7, htlc7) = addHtlc(MilliSatoshi(4000000), bob, alice, bob2alice, alice2bob) // b->a (regular) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -698,12 +699,12 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val r = randomBytes32 val h = Crypto.sha256(r) - sender.send(alice, CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc1 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) - sender.send(alice, CMD_ADD_HTLC(50000000, h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(50000000), h, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) sender.expectMsg("ok") val htlc2 = alice2bob.expectMsgType[UpdateAddHtlc] alice2bob.forward(bob) @@ -714,7 +715,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { crossSign(alice, bob, alice2bob, bob2alice) awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.htlcs.exists(h => h.add.id == htlc1.id && h.direction == IN)) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.htlcTxsAndSigs.size == 2) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocalMsat == initialState.commitments.localCommit.spec.toLocalMsat) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.spec.toLocal == initialState.commitments.localCommit.spec.toLocal) assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx.txOut.count(_.amount == Satoshi(50000)) == 2) } @@ -737,7 +738,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CommitSig (invalid signature)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // actual test begins @@ -754,7 +755,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx sender.send(alice, CMD_SIGN) @@ -775,7 +776,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) val tx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx sender.send(alice, CMD_SIGN) @@ -796,7 +797,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (one htlc sent)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -814,7 +815,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (one htlc received)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -843,19 +844,19 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (multiple htlcs in both directions)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r2, htlc2) = addHtlc(8000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r2, htlc2) = addHtlc(MilliSatoshi(8000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r3, htlc3) = addHtlc(300000, bob, alice, bob2alice, alice2bob) // b->a (dust) + val (r3, htlc3) = addHtlc(MilliSatoshi(300000), bob, alice, bob2alice, alice2bob) // b->a (dust) - val (r4, htlc4) = addHtlc(1000000, alice, bob, alice2bob, bob2alice) // a->b (regular) + val (r4, htlc4) = addHtlc(MilliSatoshi(1000000), alice, bob, alice2bob, bob2alice) // a->b (regular) - val (r5, htlc5) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r5, htlc5) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) // b->a (regular) - val (r6, htlc6) = addHtlc(500000, alice, bob, alice2bob, bob2alice) // a->b (dust) + val (r6, htlc6) = addHtlc(MilliSatoshi(500000), alice, bob, alice2bob, bob2alice) // a->b (dust) - val (r7, htlc7) = addHtlc(4000000, bob, alice, bob2alice, alice2bob) // b->a (regular) + val (r7, htlc7) = addHtlc(MilliSatoshi(4000000), bob, alice, bob2alice, alice2bob) // b->a (regular) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -880,13 +881,13 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (with reSignAsap=true)") { f => import f._ val sender = TestProbe() - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.isRight) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] alice2bob.forward(bob) - val (r2, htlc2) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r2, htlc2) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectNoMsg(300 millis) assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteNextCommitInfo.left.toOption.get.reSignAsap === true) @@ -901,7 +902,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -938,7 +939,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (forward UpdateFailHtlc)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure))) sender.expectMsg("ok") @@ -967,7 +968,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevokeAndAck (forward UpdateFailMalformedHtlc)") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_MALFORMED_HTLC(htlc.id, Sphinx.PaymentPacket.hash(htlc.onionRoutingPacket), FailureMessageCodecs.BADONION)) sender.expectMsg("ok") @@ -996,7 +997,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv RevocationTimeout") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -1013,7 +1014,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FULFILL_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1040,7 +1041,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FULFILL_HTLC (invalid preimage)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1063,7 +1064,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FULFILL_HTLC(htlc.id, r)) sender.expectMsg("ok") @@ -1083,7 +1084,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc (sender has not signed htlc)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1117,7 +1118,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFulfillHtlc (invalid preimage)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] val tx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -1138,7 +1139,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FAIL_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1176,7 +1177,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_FAIL_MALFORMED_HTLC") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1221,7 +1222,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailHtlc") { f => import f._ val sender = TestProbe() - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_FAIL_HTLC(htlc.id, Right(PermanentChannelFailure))) sender.expectMsg("ok") @@ -1241,7 +1242,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() // Alice sends an HTLC to Bob, which they both sign - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // Bob fails the HTLC because he cannot parse it val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] @@ -1268,7 +1269,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailMalformedHtlc (invalid failure_code)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1288,7 +1289,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv UpdateFailHtlc (sender has not signed htlc)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1396,7 +1397,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob.feeEstimator.setFeerate(FeeratesPerKw.single(fee.feeratePerKw)) sender.send(bob, fee) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missingSatoshis = 71620000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage) + assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missing = Satoshi(71620000L), reserve = Satoshi(20000L), fees = Satoshi(72400000L)).getMessage) awaitCond(bob.stateName == CLOSING) // channel should be advertised as down assert(channelUpdateListener.expectMsgType[LocalChannelDown].channelId === bob.stateData.asInstanceOf[DATA_CLOSING].channelId) @@ -1446,7 +1447,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_UPDATE_RELAY_FEE ") { f => import f._ val sender = TestProbe() - val newFeeBaseMsat = TestConstants.Alice.nodeParams.feeBaseMsat * 2 + val newFeeBaseMsat = TestConstants.Alice.nodeParams.feeBase * 2 val newFeeProportionalMillionth = TestConstants.Alice.nodeParams.feeProportionalMillionth * 2 sender.send(alice, CMD_UPDATE_RELAY_FEE(newFeeBaseMsat, newFeeProportionalMillionth)) sender.expectMsg("ok") @@ -1471,7 +1472,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (with unacked sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_CLOSE(None)) sender.expectMsg(Failure(CannotCloseWithUnsignedOutgoingHtlcs(channelId(bob)))) } @@ -1486,7 +1487,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (with signed sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_CLOSE(None)) sender.expectMsg("ok") @@ -1511,7 +1512,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_CLOSE (while waiting for a RevokeAndAck)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1536,7 +1537,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with unacked sent htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] // actual test begins @@ -1557,7 +1558,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with unacked received htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // actual test begins sender.send(bob, Shutdown(ByteVector32.Zeroes, TestConstants.Alice.channelParams.defaultFinalScriptPubKey)) bob2alice.expectMsgType[Error] @@ -1581,7 +1582,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with invalid final script and signed htlcs, in response to a Shutdown)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] @@ -1597,7 +1598,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (with signed htlcs)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1609,7 +1610,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Shutdown (while waiting for a RevokeAndAck)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -1628,14 +1629,14 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.send(bob, CMD_CLOSE(None)) bob2alice.expectMsgType[Shutdown] // this is just so we have something to sign - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // now we can sign sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] alice2bob.forward(bob) // adding an outgoing pending htlc - val (r1, htlc1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r1, htlc1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) // actual test begins // alice eventually gets bob's shutdown bob2alice.forward(alice) @@ -1665,7 +1666,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (no htlc timed out)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1677,7 +1678,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (an htlc timed out)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // actual test begins @@ -1696,7 +1697,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled signed htlc ignored by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1731,7 +1732,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled proposed htlc ignored by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1766,7 +1767,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CurrentBlockCount (fulfilled proposed htlc acked but not committed by upstream peer)") { f => import f._ val sender = TestProbe() - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -1844,11 +1845,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -1896,8 +1897,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // assert the feerate of the claim main is what we expect val expectedFeeRate = alice.feeEstimator.getFeeratePerKw(alice.feeTargets.claimMainBlockTarget) - val expectedFee = Transactions.weight2fee(expectedFeeRate, Transactions.claimP2WPKHOutputWeight).toLong - val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount.toLong).sum - claimMain.txOut.map(_.amount.toLong).sum + val expectedFee = Transactions.weight2fee(expectedFeeRate, Transactions.claimP2WPKHOutputWeight) + val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount).sum - claimMain.txOut.map(_.amount).sum assert(claimFee == expectedFee) } @@ -1905,11 +1906,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -1971,7 +1972,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // bob = 200 000 def send(): Transaction = { // alice sends 8 000 sat - val (r, htlc) = addHtlc(10000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(10000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -2030,7 +2031,7 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // alice = 800 000 // bob = 200 000 - val add = CMD_ADD_HTLC(10000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(10000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) sender.expectMsg("ok") alice2bob.expectMsgType[UpdateAddHtlc] @@ -2078,11 +2079,11 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv Error") { f => import f._ - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (rb1, htlcb1) = addHtlc(50000000, bob, alice, bob2alice, alice2bob) - val (rb2, htlcb2) = addHtlc(55000000, bob, alice, bob2alice, alice2bob) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (rb1, htlcb1) = addHtlc(MilliSatoshi(50000000), bob, alice, bob2alice, alice2bob) + val (rb2, htlcb2) = addHtlc(MilliSatoshi(55000000), bob, alice, bob2alice, alice2bob) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(1, ra2, bob, alice, bob2alice, alice2bob) fulfillHtlc(0, rb1, alice, bob, alice2bob, bob2alice) @@ -2299,8 +2300,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { sender.send(alice, WatchEventConfirmed(BITCOIN_FUNDING_DEEPLYBURIED, 400000, 42, null)) val update1a = alice2bob.expectMsgType[ChannelUpdate] assert(Announcements.isEnabled(update1a.channelFlags) == true) - val (_, htlc1) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (_, htlc2) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (_, htlc1) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (_, htlc2) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) val aliceData = alice.stateData.asInstanceOf[DATA_NORMAL] assert(aliceData.commitments.localChanges.proposed.size == 2) @@ -2343,8 +2344,8 @@ class NormalStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val update1a = channelUpdateListener.expectMsgType[LocalChannelUpdate] val update1b = channelUpdateListener.expectMsgType[LocalChannelUpdate] assert(Announcements.isEnabled(update1a.channelUpdate.channelFlags) == true) - val (_, htlc1) = addHtlc(10000, alice, bob, alice2bob, bob2alice) - val (_, htlc2) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (_, htlc1) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) + val (_, htlc2) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) val aliceData = alice.stateData.asInstanceOf[DATA_NORMAL] assert(aliceData.commitments.localChanges.proposed.size == 2) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala index 866a3346f6..916b54c184 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/OfflineStateSpec.scala @@ -31,7 +31,7 @@ import fr.acinq.eclair.payment.CommandBuffer.CommandSend import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.transactions.Transactions.HtlcSuccessTx import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.Outcome import scala.concurrent.duration._ @@ -66,7 +66,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - sender.send(alice, CMD_ADD_HTLC(1000000, ByteVector32.Zeroes, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(1000000), ByteVector32.Zeroes, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc] // add ->b alice2bob.forward(bob) @@ -143,7 +143,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - sender.send(alice, CMD_ADD_HTLC(1000000, randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(1000000), randomBytes32, 400144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val ab_add_0 = alice2bob.expectMsgType[UpdateAddHtlc] // add ->b alice2bob.forward(bob, ab_add_0) @@ -204,11 +204,11 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() - val (ra1, htlca1) = addHtlc(250000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) - val (ra2, htlca2) = addHtlc(100000000, alice, bob, alice2bob, bob2alice) + val (ra2, htlca2) = addHtlc(MilliSatoshi(100000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) - val (ra3, htlca3) = addHtlc(10000, alice, bob, alice2bob, bob2alice) + val (ra3, htlca3) = addHtlc(MilliSatoshi(10000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val oldStateData = alice.stateData fulfillHtlc(htlca1.id, ra1, bob, alice, bob2alice, alice2bob) @@ -261,7 +261,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // we start by storing the current state val oldStateData = alice.stateData // then we add an htlc and sign it - addHtlc(250000000, alice, bob, alice2bob, bob2alice) + addHtlc(MilliSatoshi(250000000), alice, bob, alice2bob, bob2alice) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") alice2bob.expectMsgType[CommitSig] @@ -347,7 +347,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { channelUpdateListener.expectNoMsg(300 millis) // we make alice update here relay fee - sender.send(alice, CMD_UPDATE_RELAY_FEE(4200, 123456)) + sender.send(alice, CMD_UPDATE_RELAY_FEE(MilliSatoshi(4200), 123456)) sender.expectMsg("ok") // alice doesn't broadcast the new channel_update yet @@ -365,7 +365,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // then alice reaches NORMAL state, and after a delay she broadcasts the channel_update val channelUpdate = channelUpdateListener.expectMsgType[LocalChannelUpdate](20 seconds).channelUpdate - assert(channelUpdate.feeBaseMsat === 4200) + assert(channelUpdate.feeBaseMsat === MilliSatoshi(4200)) assert(channelUpdate.feeProportionalMillionths === 123456) assert(Announcements.isEnabled(channelUpdate.channelFlags)) @@ -387,7 +387,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { channelUpdateListener.expectNoMsg(300 millis) // we attempt to send a payment - sender.send(alice, CMD_ADD_HTLC(4200, randomBytes32, 123456, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) + sender.send(alice, CMD_ADD_HTLC(MilliSatoshi(4200), randomBytes32, 123456, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID()))) val failure = sender.expectMsgType[Status.Failure] val AddHtlcFailed(_, _, ChannelUnavailable(_), _, _, _) = failure.cause @@ -401,7 +401,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val register = TestProbe() val commandBuffer = TestActorRef(new CommandBuffer(bob.underlyingActor.nodeParams, register.ref)) - val (r, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) val listener = TestProbe() @@ -442,7 +442,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() val register = TestProbe() val commandBuffer = TestActorRef(new CommandBuffer(bob.underlyingActor.nodeParams, register.ref)) - val (_, htlc) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (_, htlc) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) sender.send(alice, INPUT_DISCONNECTED) @@ -452,7 +452,7 @@ class OfflineStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // We simulate a pending failure on that HTLC. // Even if we get close to expiring upstream we shouldn't close the channel, because we have nothing to lose. - sender.send(commandBuffer, CommandSend(htlc.channelId, htlc.id, CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(0))))) + sender.send(commandBuffer, CommandSend(htlc.channelId, htlc.id, CMD_FAIL_HTLC(htlc.id, Right(IncorrectOrUnknownPaymentDetails(MilliSatoshi(0)))))) sender.send(bob, CurrentBlockCount(htlc.cltvExpiry - bob.underlyingActor.nodeParams.fulfillSafetyBeforeTimeoutBlocks)) bob2blockchain.expectNoMsg(250 millis) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala index 760efbe3f6..0ca1582caa 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/f/ShutdownStateSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.payment._ import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire.{CommitSig, Error, FailureMessageCodecs, PermanentChannelFailure, RevokeAndAck, Shutdown, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFee, UpdateFulfillHtlc} -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -55,7 +55,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val sender = TestProbe() // alice sends an HTLC to bob val h1 = Crypto.sha256(r1) - val amount1 = 300000000 + val amount1 = MilliSatoshi(300000000) val expiry1 = 400144 val cmd1 = PaymentLifecycle.buildCommand(UUID.randomUUID, amount1, expiry1, h1, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil)._1.copy(commit = false) sender.send(alice, cmd1) @@ -65,7 +65,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.remoteChanges.proposed == htlc1 :: Nil) // alice sends another HTLC to bob val h2 = Crypto.sha256(r2) - val amount2 = 200000000 + val amount2 = MilliSatoshi(200000000) val expiry2 = 400144 val cmd2 = PaymentLifecycle.buildCommand(UUID.randomUUID, amount2, expiry2, h2, Hop(null, TestConstants.Bob.nodeParams.nodeId, null) :: Nil)._1.copy(commit = false) sender.send(alice, cmd2) @@ -104,7 +104,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv CMD_ADD_HTLC") { f => import f._ val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, r1, cltvExpiry = 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), r1, cltvExpiry = 300000, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -585,7 +585,7 @@ class ShutdownStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob.feeEstimator.setFeerate(FeeratesPerKw.single(fee.feeratePerKw)) sender.send(bob, fee) val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missingSatoshis = 72120000L, reserveSatoshis = 20000L, feesSatoshis = 72400000L).getMessage) + assert(new String(error.data.toArray) === CannotAffordFees(channelId(bob), missing = Satoshi(72120000L), reserve = Satoshi(20000L), fees = Satoshi(72400000L)).getMessage) awaitCond(bob.stateName == CLOSING) bob2blockchain.expectMsg(PublishAsap(tx)) // commit tx //bob2blockchain.expectMsgType[PublishAsap] // main delayed (removed because of the high fees) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala index 3b798fe887..f5c5a59cd4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/g/NegotiatingStateSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.channel._ import fr.acinq.eclair.channel.states.StateTestsHelperMethods import fr.acinq.eclair.payment.Local import fr.acinq.eclair.wire.{ClosingSigned, Error, Shutdown} -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -85,7 +85,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods import f._ alice2bob.expectMsgType[ClosingSigned] val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(5000000000L), ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -111,7 +111,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods private def testFeeConverge(f: FixtureParam) = { import f._ - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -135,9 +135,9 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods val aliceCloseSig = alice2bob.expectMsgType[ClosingSigned] val sender = TestProbe() val tx = bob.stateData.asInstanceOf[DATA_NEGOTIATING].commitments.localCommit.publishableTxs.commitTx.tx - sender.send(bob, aliceCloseSig.copy(feeSatoshis = 99000)) // sig doesn't matter, it is checked later + sender.send(bob, aliceCloseSig.copy(feeSatoshis = Satoshi(99000))) // sig doesn't matter, it is checked later val error = bob2alice.expectMsgType[Error] - assert(new String(error.data.toArray).startsWith("invalid close fee: fee_satoshis=99000")) + assert(new String(error.data.toArray).startsWith("invalid close fee: fee_satoshis=Satoshi(99000)")) bob2blockchain.expectMsg(PublishAsap(tx)) bob2blockchain.expectMsgType[PublishAsap] bob2blockchain.expectMsgType[WatchConfirmed] @@ -158,7 +158,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods test("recv BITCOIN_FUNDING_SPENT (counterparty's mutual close)") { f => import f._ - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -193,7 +193,7 @@ class NegotiatingStateSpec extends TestkitBaseClass with StateTestsHelperMethods // at this point alice and bob have not yet converged on closing fees, but bob decides to publish a mutual close with one of the previous sigs val d = bob.stateData.asInstanceOf[DATA_NEGOTIATING] implicit val log: LoggingAdapter = bob.underlyingActor.implicitLog - val Success(bobClosingTx) = Closing.checkClosingSignature(Bob.keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, Satoshi(aliceClose1.feeSatoshis), aliceClose1.signature) + val Success(bobClosingTx) = Closing.checkClosingSignature(Bob.keyManager, d.commitments, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, aliceClose1.feeSatoshis, aliceClose1.signature) alice ! WatchEventSpent(BITCOIN_FUNDING_SPENT, bobClosingTx) alice2blockchain.expectMsgType[PublishAsap] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala index b4715a7110..8cff5d868f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/h/ClosingStateSpec.scala @@ -22,7 +22,7 @@ import akka.actor.Status import akka.actor.Status.Failure import akka.testkit.{TestFSMRef, TestProbe} import com.typesafe.sslconfig.util.NoopLogger -import fr.acinq.bitcoin.{ByteVector32, OutPoint, ScriptFlags, Transaction, TxIn} +import fr.acinq.bitcoin.{ByteVector32, OutPoint, Satoshi, ScriptFlags, Transaction, TxIn} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw @@ -33,7 +33,7 @@ import fr.acinq.eclair.channel.{Data, State, _} import fr.acinq.eclair.payment._ import fr.acinq.eclair.transactions.{Scripts, Transactions} import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, TestConstants, TestkitBaseClass, randomBytes32} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestConstants, TestkitBaseClass, randomBytes32} import org.scalatest.{Outcome, Tag} import scodec.bits.ByteVector @@ -89,7 +89,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { within(30 seconds) { reachNormal(setup) val bobCommitTxes: List[PublishableTxs] = (for (amt <- List(100000000, 200000000, 300000000)) yield { - val (r, htlc) = addHtlc(amt, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(amt), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] val bobCommitTx1 = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs @@ -124,7 +124,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { bob2alice.expectMsgType[Shutdown] bob2alice.forward(alice) // agreeing on a closing fee - var aliceCloseFee, bobCloseFee = 0L + var aliceCloseFee, bobCloseFee = Satoshi(0) do { aliceCloseFee = alice2bob.expectMsgType[ClosingSigned].feeSatoshis alice2bob.forward(bob) @@ -157,7 +157,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { val mutualClosingFeeRate = alice.feeEstimator.getFeeratePerKw(alice.feeTargets.mutualCloseBlockTarget) val expectedFirstProposedFee = Closing.firstClosingFee(aliceData.commitments, aliceData.localShutdown.scriptPubKey, aliceData.remoteShutdown.scriptPubKey, mutualClosingFeeRate)(akka.event.NoLogging) assert(alice.feeTargets.mutualCloseBlockTarget == 2 && mutualClosingFeeRate == 250) - assert(closing.feeSatoshis == expectedFirstProposedFee.amount) + assert(closing.feeSatoshis == expectedFirstProposedFee) } test("recv BITCOIN_FUNDING_PUBLISH_FAILED", Tag("funding_unconfirmed")) { f => @@ -301,7 +301,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { // actual test starts here val sender = TestProbe() - val add = CMD_ADD_HTLC(500000000, ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + val add = CMD_ADD_HTLC(MilliSatoshi(500000000), ByteVector32(ByteVector.fill(32)(1)), cltvExpiry = 300000, onion = TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) sender.send(alice, add) val error = ChannelUnavailable(channelId(alice)) sender.expectMsg(Failure(AddHtlcFailed(channelId(alice), add.paymentHash, error, Local(add.upstream.left.get, Some(sender.ref)), None, Some(add)))) @@ -380,7 +380,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { test("recv BITCOIN_OUTPUT_SPENT") { f => import f._ // alice sends an htlc to bob - val (ra1, htlca1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) relayerB.expectMsgType[ForwardAdd] // an error occurs and alice publishes her commit tx @@ -420,7 +420,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[LocalCommitConfirmed]) system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) // alice sends an htlc to bob - val (ra1, htlca1) = addHtlc(50000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(50000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) // an error occurs and alice publishes her commit tx val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx @@ -454,7 +454,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) val aliceCommitTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // alice sends an htlc - val (r, htlc) = addHtlc(4200000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(4200000), alice, bob, alice2bob, bob2alice) // and signs it (but bob doesn't sign it) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -485,7 +485,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { system.eventStream.subscribe(listener.ref, classOf[PaymentSettlingOnChain]) val bobCommitTx = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommit.publishableTxs.commitTx.tx // alice sends an htlc - val (r, htlc) = addHtlc(4200000, alice, bob, alice2bob, bob2alice) + val (r, htlc) = addHtlc(MilliSatoshi(4200000), alice, bob, alice2bob, bob2alice) // and signs it (but bob doesn't sign it) sender.send(alice, CMD_SIGN) sender.expectMsg("ok") @@ -546,7 +546,7 @@ class ClosingStateSpec extends TestkitBaseClass with StateTestsHelperMethods { import f._ val sender = TestProbe() val oldStateData = alice.stateData - val (ra1, htlca1) = addHtlc(25000000, alice, bob, alice2bob, bob2alice) + val (ra1, htlca1) = addHtlc(MilliSatoshi(25000000), alice, bob, alice2bob, bob2alice) crossSign(alice, bob, alice2bob, bob2alice) fulfillHtlc(htlca1.id, ra1, bob, alice, bob2alice, alice2bob) crossSign(bob, alice, bob2alice, alice2bob) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala index 282841c3cc..8892bf3adc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteAuditDbSpec.scala @@ -18,14 +18,14 @@ package fr.acinq.eclair.db import java.util.UUID -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi, Transaction} +import fr.acinq.bitcoin.{Satoshi, Transaction} import fr.acinq.eclair.channel.Channel.{LocalError, RemoteError} import fr.acinq.eclair.channel.{AvailableBalanceChanged, ChannelErrorOccured, NetworkFeePaid} import fr.acinq.eclair.db.sqlite.SqliteAuditDb import fr.acinq.eclair.db.sqlite.SqliteUtils.{getVersion, using} -import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} import fr.acinq.eclair.wire.{ChannelCodecs, ChannelCodecsSpec} import fr.acinq.eclair._ +import fr.acinq.eclair.payment.{PaymentReceived, PaymentRelayed, PaymentSent} import org.scalatest.FunSuite import concurrent.duration._ @@ -50,8 +50,8 @@ class SqliteAuditDbSpec extends FunSuite { val e4 = NetworkFeePaid(null, randomKey.publicKey, randomBytes32, Transaction(0, Seq.empty, Seq.empty, 0), Satoshi(42), "mutual") val e5 = PaymentSent(ChannelCodecs.UNKNOWN_UUID, MilliSatoshi(42000), MilliSatoshi(1000), randomBytes32, randomBytes32, randomBytes32, timestamp = 0) val e6 = PaymentSent(ChannelCodecs.UNKNOWN_UUID, MilliSatoshi(42000), MilliSatoshi(1000), randomBytes32, randomBytes32, randomBytes32, timestamp = (Platform.currentTime.milliseconds + 10.minutes).toMillis) - val e7 = AvailableBalanceChanged(null, randomBytes32, ShortChannelId(500000, 42, 1), 456123000, ChannelCodecsSpec.commitments) - val e8 = ChannelLifecycleEvent(randomBytes32, randomKey.publicKey, 456123000, true, false, "mutual") + val e7 = AvailableBalanceChanged(null, randomBytes32, ShortChannelId(500000, 42, 1), MilliSatoshi(456123000), ChannelCodecsSpec.commitments) + val e8 = ChannelLifecycleEvent(randomBytes32, randomKey.publicKey, Satoshi(456123000), true, false, "mutual") val e9 = ChannelErrorOccured(null, randomBytes32, randomKey.publicKey, null, LocalError(new RuntimeException("oops")), true) val e10 = ChannelErrorOccured(null, randomBytes32, randomKey.publicKey, null, RemoteError(wire.Error(randomBytes32, "remote oops")), true) @@ -97,9 +97,9 @@ class SqliteAuditDbSpec extends FunSuite { db.add(NetworkFeePaid(null, n3, c3, Transaction(0, Seq.empty, Seq.empty, 0), Satoshi(400), "funding")) assert(db.stats.toSet === Set( - Stats(channelId = c1, avgPaymentAmountSatoshi = 42, paymentCount = 3, relayFeeSatoshi = 4, networkFeeSatoshi = 100), - Stats(channelId = c2, avgPaymentAmountSatoshi = 40, paymentCount = 1, relayFeeSatoshi = 2, networkFeeSatoshi = 500), - Stats(channelId = c3, avgPaymentAmountSatoshi = 0, paymentCount = 0, relayFeeSatoshi = 0, networkFeeSatoshi = 400) + Stats(channelId = c1, avgPaymentAmount = Satoshi(42), paymentCount = 3, relayFee = Satoshi(4), networkFee = Satoshi(100)), + Stats(channelId = c2, avgPaymentAmount = Satoshi(40), paymentCount = 1, relayFee = Satoshi(2), networkFee = Satoshi(500)), + Stats(channelId = c3, avgPaymentAmount = Satoshi(0), paymentCount = 0, relayFee = Satoshi(0), networkFee = Satoshi(400)) )) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala index 201cff789a..a721c4d557 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqliteNetworkDbSpec.scala @@ -20,7 +20,7 @@ import fr.acinq.bitcoin.{Block, Crypto, Satoshi} import fr.acinq.eclair.db.sqlite.SqliteNetworkDb import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire.{Color, NodeAddress, Tor2} -import fr.acinq.eclair.{ShortChannelId, TestConstants, randomBytes32, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, randomBytes32, randomKey} import org.scalatest.FunSuite import org.sqlite.SQLiteException @@ -85,9 +85,9 @@ class SqliteNetworkDbSpec extends FunSuite { db.removeChannel(channel_2.shortChannelId) assert(db.listChannels().toSet === Set((channel_1, (txid_1, capacity)), (channel_3, (txid_3, capacity)))) - val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(42), 5, 7000000, 50000, 100, 500000000L, true) - val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(43), 5, 7000000, 50000, 100, 500000000L, true) - val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(44), 5, 7000000, 50000, 100, 500000000L, true) + val channel_update_1 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(42), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) + val channel_update_2 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(43), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) + val channel_update_3 = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, ShortChannelId(44), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) assert(db.listChannelUpdates().toSet === Set.empty) db.addChannelUpdate(channel_update_1) @@ -110,7 +110,7 @@ class SqliteNetworkDbSpec extends FunSuite { val capacity = Satoshi(10000) val channels = shortChannelIds.map(id => Announcements.makeChannelAnnouncement(Block.RegtestGenesisBlock.hash, id, pub, pub, pub, pub, sig, sig, sig, sig)) - val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), 5, 7000000, 50000, 100, 500000000L, true) + val template = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv, pub, ShortChannelId(42), 5, MilliSatoshi(7000000), MilliSatoshi(50000), 100, MilliSatoshi(500000000L), true) val updates = shortChannelIds.map(id => template.copy(shortChannelId = id)) val txid = randomBytes32 channels.foreach(ca => db.addChannel(ca, txid, capacity)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala index b8eec4b784..86abf94cf0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/db/SqlitePaymentsDbSpec.scala @@ -17,17 +17,19 @@ package fr.acinq.eclair.db import java.util.UUID + import fr.acinq.eclair.db.sqlite.SqliteUtils._ -import fr.acinq.bitcoin.{Block, ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{Block, ByteVector32} import fr.acinq.eclair.TestConstants.Bob -import fr.acinq.eclair.{TestConstants, payment} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, payment, randomBytes32} import fr.acinq.eclair.db.sqlite.SqlitePaymentsDb import fr.acinq.eclair.payment.PaymentRequest import org.scalatest.FunSuite import scodec.bits._ -import fr.acinq.eclair.randomBytes32 + import scala.compat.Platform import OutgoingPaymentStatus._ + import concurrent.duration._ class SqlitePaymentsDbSpec extends FunSuite { @@ -51,12 +53,12 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(getVersion(statement, "payments", 1) == 1) // version 1 is deployed now } - val oldReceivedPayment = IncomingPayment(ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), 123, 1233322) + val oldReceivedPayment = IncomingPayment(ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), MilliSatoshi(123), 1233322) // insert old type record using(connection.prepareStatement("INSERT INTO payments VALUES (?, ?, ?)")) { statement => statement.setBytes(1, oldReceivedPayment.paymentHash.toArray) - statement.setLong(2, oldReceivedPayment.amountMsat) + statement.setLong(2, oldReceivedPayment.amount.toLong) statement.setLong(3, oldReceivedPayment.receivedAt) statement.executeUpdate() } @@ -71,9 +73,9 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(preMigrationDb.getIncomingPayment(oldReceivedPayment.paymentHash).isEmpty) // add a few rows - val ps1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) + val ps1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) val i1 = PaymentRequest.read("lnbc10u1pw2t4phpp5ezwm2gdccydhnphfyepklc0wjkxhz0r4tctg9paunh2lxgeqhcmsdqlxycrqvpqwdshgueqvfjhggr0dcsry7qcqzpgfa4ecv7447p9t5hkujy9qgrxvkkf396p9zar9p87rv2htmeuunkhydl40r64n5s2k0u7uelzc8twxmp37nkcch6m0wg5tvvx69yjz8qpk94qf3") - val pr1 = IncomingPayment(i1.paymentHash, 12345678, 1513871928275L) + val pr1 = IncomingPayment(i1.paymentHash, MilliSatoshi(12345678), 1513871928275L) preMigrationDb.addPaymentRequest(i1, ByteVector32.Zeroes) preMigrationDb.addIncomingPayment(pr1) @@ -99,7 +101,7 @@ class SqlitePaymentsDbSpec extends FunSuite { val db = new SqlitePaymentsDb(sqlite) // can't receive a payment without an invoice associated with it - assertThrows[IllegalArgumentException](db.addIncomingPayment(IncomingPayment(ByteVector32(hex"6e7e8018f05e169cf1d99e77dc22cb372d09f10b6a81f1eae410718c56cad188"), 12345678, 1513871928275L))) + assertThrows[IllegalArgumentException](db.addIncomingPayment(IncomingPayment(ByteVector32(hex"6e7e8018f05e169cf1d99e77dc22cb372d09f10b6a81f1eae410718c56cad188"), MilliSatoshi(12345678), 1513871928275L))) val i1 = PaymentRequest.read("lnbc5450n1pw2t4qdpp5vcrf6ylgpettyng4ac3vujsk0zpc25cj0q3zp7l7w44zvxmpzh8qdzz2pshjmt9de6zqen0wgsr2dp4ypcxj7r9d3ejqct5ypekzar0wd5xjuewwpkxzcm99cxqzjccqp2rzjqtspxelp67qc5l56p6999wkatsexzhs826xmupyhk6j8lxl038t27z9tsqqqgpgqqqqqqqlgqqqqqzsqpcz8z8hmy8g3ecunle4n3edn3zg2rly8g4klsk5md736vaqqy3ktxs30ht34rkfkqaffzxmjphvd0637dk2lp6skah2hq09z6lrjna3xqp3d4vyd") val i2 = PaymentRequest.read("lnbc10u1pw2t4phpp5ezwm2gdccydhnphfyepklc0wjkxhz0r4tctg9paunh2lxgeqhcmsdqlxycrqvpqwdshgueqvfjhggr0dcsry7qcqzpgfa4ecv7447p9t5hkujy9qgrxvkkf396p9zar9p87rv2htmeuunkhydl40r64n5s2k0u7uelzc8twxmp37nkcch6m0wg5tvvx69yjz8qpk94qf3") @@ -107,8 +109,8 @@ class SqlitePaymentsDbSpec extends FunSuite { db.addPaymentRequest(i1, ByteVector32.Zeroes) db.addPaymentRequest(i2, ByteVector32.Zeroes) - val p1 = IncomingPayment(i1.paymentHash, 12345678, 1513871928275L) - val p2 = IncomingPayment(i2.paymentHash, 12345678, 1513871928275L) + val p1 = IncomingPayment(i1.paymentHash, MilliSatoshi(12345678), 1513871928275L) + val p2 = IncomingPayment(i2.paymentHash, MilliSatoshi(12345678), 1513871928275L) assert(db.listIncomingPayments() === Nil) db.addIncomingPayment(p1) db.addIncomingPayment(p2) @@ -121,8 +123,8 @@ class SqlitePaymentsDbSpec extends FunSuite { val db = new SqlitePaymentsDb(TestConstants.sqliteInMemory()) - val s1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) - val s2 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"08d47d5f7164d4b696e8f6b62a03094d4f1c65f16e9d7b11c4a98854707e55cf"), None, amountMsat = 12345, createdAt = 12345, None, PENDING) + val s1 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"0f059ef9b55bb70cc09069ee4df854bf0fab650eee6f2b87ba26d1ad08ab114f"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) + val s2 = OutgoingPayment(id = UUID.randomUUID(), paymentHash = ByteVector32(hex"08d47d5f7164d4b696e8f6b62a03094d4f1c65f16e9d7b11c4a98854707e55cf"), None, amount = MilliSatoshi(12345), createdAt = 12345, None, PENDING) assert(db.listOutgoingPayments().isEmpty) db.addOutgoingPayment(s1) @@ -135,7 +137,7 @@ class SqlitePaymentsDbSpec extends FunSuite { assert(db.getOutgoingPayments(s2.paymentHash) === Seq(s2)) assert(db.getOutgoingPayments(ByteVector32.Zeroes) === Seq.empty) - val s3 = s2.copy(id = UUID.randomUUID(), amountMsat = 88776655) + val s3 = s2.copy(id = UUID.randomUUID(), amount = MilliSatoshi(88776655)) db.addOutgoingPayment(s3) db.updateOutgoingPayment(s3.id, FAILED) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala index 8b2c2e79da..0f9857d006 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/IntegrationSpec.scala @@ -24,13 +24,14 @@ import akka.testkit.{TestKit, TestProbe} import com.google.common.net.HostAndPort import com.typesafe.config.{Config, ConfigFactory} import fr.acinq.bitcoin.Crypto.PrivateKey -import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, Crypto, MilliSatoshi, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction} +import fr.acinq.bitcoin.{Base58, Base58Check, Bech32, Block, ByteVector32, Crypto, OP_0, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_PUSHDATA, Satoshi, Script, ScriptFlags, Transaction} import fr.acinq.eclair.blockchain.bitcoind.BitcoindService import fr.acinq.eclair.blockchain.bitcoind.rpc.ExtendedBitcoinClient import fr.acinq.eclair.blockchain.{Watch, WatchConfirmed} import fr.acinq.eclair.channel.Channel.{BroadcastChannelUpdate, PeriodicRefresh} import fr.acinq.eclair.channel.Register.{Forward, ForwardShortId} import fr.acinq.eclair.channel._ +import fr.acinq.eclair._ import fr.acinq.eclair.crypto.Sphinx.DecryptedFailurePacket import fr.acinq.eclair.io.Peer.{Disconnect, PeerRoutingMessage} import fr.acinq.eclair.io.{NodeURI, Peer} @@ -65,7 +66,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService // we override the default because these test were designed to use cost-optimized routes val integrationTestRouteParams = Some(RouteParams( randomize = false, - maxFeeBaseMsat = Long.MaxValue, + maxFeeBase = MilliSatoshi(Long.MaxValue), maxFeePct = Double.MaxValue, routeMaxCltv = Int.MaxValue, routeMaxLength = ROUTE_MAX_LENGTH, @@ -263,7 +264,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // then we make the actual payment sender.send(nodes("A").paymentInitiator, - SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1)) + SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1)) val paymentId = sender.expectMsgType[UUID](5 seconds) val ps = sender.expectMsgType[PaymentSucceeded](5 seconds) assert(ps.id == paymentId) @@ -279,7 +280,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("B").register, ForwardShortId(shortIdBC, CMD_GETINFO)) val commitmentBC = sender.expectMsgType[RES_GETINFO].data.asInstanceOf[DATA_NORMAL].commitments // we then forge a new channel_update for B-C... - val channelUpdateBC = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, nodes("B").nodeParams.privateKey, nodes("C").nodeParams.nodeId, shortIdBC, nodes("B").nodeParams.expiryDeltaBlocks + 1, nodes("C").nodeParams.htlcMinimumMsat, nodes("B").nodeParams.feeBaseMsat, nodes("B").nodeParams.feeProportionalMillionth, 500000000L) + val channelUpdateBC = Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, nodes("B").nodeParams.privateKey, nodes("C").nodeParams.nodeId, shortIdBC, nodes("B").nodeParams.expiryDeltaBlocks + 1, nodes("C").nodeParams.htlcMinimum, nodes("B").nodeParams.feeBase, nodes("B").nodeParams.feeProportionalMillionth, MilliSatoshi(500000000L)) // ...and notify B's relayer sender.send(nodes("B").relayer, LocalChannelUpdate(system.deadLetters, commitmentBC.channelId, shortIdBC, commitmentBC.remoteParams.nodeId, None, channelUpdateBC, commitmentBC)) // we retrieve a payment hash from D @@ -287,7 +288,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] // then we make the actual payment, do not randomize the route to make sure we route through node B - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will receive an error from B that include the updated channel update, then will retry the payment val paymentId = sender.expectMsgType[UUID](5 seconds) @@ -325,7 +326,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] // then we make the payment (B-C has a smaller capacity than A-B and C-D) - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an error from C, then retry and route around C: A->B->E->C->D sender.expectMsgType[UUID](5 seconds) @@ -334,7 +335,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService test("send an HTLC A->D with an unknown payment hash") { val sender = TestProbe() - val pr = SendPayment(100000000L, randomBytes32, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val pr = SendPayment(MilliSatoshi(100000000L), randomBytes32, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, pr) // A will receive an error from D and won't retry @@ -343,7 +344,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(failed.id == paymentId) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(100000000L)))) } test("send an HTLC A->D with a lower amount than requested") { @@ -354,7 +355,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of only 1 mBTC - val sendReq = SendPayment(100000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(100000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -363,7 +364,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(failed.id == paymentId) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(100000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(100000000L)))) } test("send an HTLC A->D with too much overpayment") { @@ -374,7 +375,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of 6 mBTC - val sendReq = SendPayment(600000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(600000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) // A will first receive an IncorrectPaymentAmount error from D @@ -383,7 +384,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService assert(paymentId == failed.id) assert(failed.paymentHash === pr.paymentHash) assert(failed.failures.size === 1) - assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(600000000L))) + assert(failed.failures.head.asInstanceOf[RemoteFailure].e === DecryptedFailurePacket(nodes("D").nodeParams.nodeId, IncorrectOrUnknownPaymentDetails(MilliSatoshi(600000000L)))) } test("send an HTLC A->D with a reasonable overpayment") { @@ -394,7 +395,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val pr = sender.expectMsgType[PaymentRequest] // A send payment of 3 mBTC, more than asked but it should still be accepted - val sendReq = SendPayment(300000000L, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(MilliSatoshi(300000000L), pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] } @@ -407,7 +408,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.send(nodes("D").paymentHandler, ReceivePayment(Some(amountMsat), "1 payment")) val pr = sender.expectMsgType[PaymentRequest] - val sendReq = SendPayment(amountMsat.amount, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) + val sendReq = SendPayment(amountMsat, pr.paymentHash, nodes("D").nodeParams.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 5) sender.send(nodes("A").paymentInitiator, sendReq) sender.expectMsgType[UUID] sender.expectMsgType[PaymentSucceeded] // the payment FSM will also reply to the sender after the payment is completed @@ -423,7 +424,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService // the payment is requesting to use a capacity-optimized route which will select node G even though it's a bit more expensive sender.send(nodes("A").paymentInitiator, - SendPayment(amountMsat.amount, pr.paymentHash, nodes("C").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams.map(_.copy(ratios = Some(WeightRatios(0, 0, 1)))))) + SendPayment(amountMsat, pr.paymentHash, nodes("C").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams.map(_.copy(ratios = Some(WeightRatios(0, 0, 1)))))) sender.expectMsgType[UUID](max = 60 seconds) awaitCond({ @@ -467,7 +468,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F1").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F1").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) paymentSender.expectMsgType[UUID](30 seconds) @@ -547,7 +548,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F2").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F2").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) paymentSender.expectMsgType[UUID](30 seconds) @@ -624,7 +625,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage: ByteVector = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F3").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F3").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) val paymentId = paymentSender.expectMsgType[UUID] @@ -686,7 +687,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val preimage: ByteVector = randomBytes32 val paymentHash = Crypto.sha256(preimage) // A sends a payment to F - val paymentReq = SendPayment(100000000L, paymentHash, nodes("F4").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) + val paymentReq = SendPayment(MilliSatoshi(100000000L), paymentHash, nodes("F4").nodeParams.nodeId, maxAttempts = 1, routeParams = integrationTestRouteParams) val paymentSender = TestProbe() paymentSender.send(nodes("A").paymentInitiator, paymentReq) val paymentId = paymentSender.expectMsgType[UUID](30 seconds) @@ -760,7 +761,7 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService val amountMsat = MilliSatoshi(300000000L) sender.send(paymentHandlerF, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] - val sendReq = SendPayment(300000000L, pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) + val sendReq = SendPayment(MilliSatoshi(300000000L), pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) sender.send(nodes("A").paymentInitiator, sendReq) val paymentId = sender.expectMsgType[UUID] // we forward the htlc to the payment handler @@ -771,8 +772,8 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService sender.expectMsgType[PaymentSucceeded].id === paymentId // we now send a few htlcs C->F and F->C in order to obtain a commitments with multiple htlcs - def send(amountMsat: Long, paymentHandler: ActorRef, paymentInitiator: ActorRef) = { - sender.send(paymentHandler, ReceivePayment(Some(MilliSatoshi(amountMsat)), "1 coffee")) + def send(amountMsat: MilliSatoshi, paymentHandler: ActorRef, paymentInitiator: ActorRef) = { + sender.send(paymentHandler, ReceivePayment(Some(amountMsat), "1 coffee")) val pr = sender.expectMsgType[PaymentRequest] val sendReq = SendPayment(amountMsat, pr.paymentHash, pr.nodeId, routeParams = integrationTestRouteParams, maxAttempts = 1) sender.send(paymentInitiator, sendReq) @@ -780,19 +781,19 @@ class IntegrationSpec extends TestKit(ActorSystem("test")) with BitcoindService } val buffer = TestProbe() - send(100000000, paymentHandlerF, nodes("C").paymentInitiator) // will be left pending + send(MilliSatoshi(100000000), paymentHandlerF, nodes("C").paymentInitiator) // will be left pending forwardHandlerF.expectMsgType[UpdateAddHtlc] forwardHandlerF.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(110000000, paymentHandlerF, nodes("C").paymentInitiator) // will be left pending + send(MilliSatoshi(110000000), paymentHandlerF, nodes("C").paymentInitiator) // will be left pending forwardHandlerF.expectMsgType[UpdateAddHtlc] forwardHandlerF.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(120000000, paymentHandlerC, nodes("F5").paymentInitiator) + send(MilliSatoshi(120000000), paymentHandlerC, nodes("F5").paymentInitiator) forwardHandlerC.expectMsgType[UpdateAddHtlc] forwardHandlerC.forward(buffer.ref) sigListener.expectMsgType[ChannelSignatureReceived] - send(130000000, paymentHandlerC, nodes("F5").paymentInitiator) + send(MilliSatoshi(130000000), paymentHandlerC, nodes("F5").paymentInitiator) forwardHandlerC.expectMsgType[UpdateAddHtlc] forwardHandlerC.forward(buffer.ref) val commitmentsF = sigListener.expectMsgType[ChannelSignatureReceived].commitments diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala index 6697116750..9a0fbfde08 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/RustyTestsSpec.scala @@ -21,14 +21,14 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import akka.actor.{ActorRef, ActorSystem, Props} import akka.testkit.{TestFSMRef, TestKit, TestProbe} -import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.TestConstants.{Alice, Bob, TestFeeEstimator} import fr.acinq.eclair.blockchain._ import fr.acinq.eclair.blockchain.fee.FeeratesPerKw import fr.acinq.eclair.channel._ import fr.acinq.eclair.payment.NoopPaymentHandler import fr.acinq.eclair.wire.Init -import fr.acinq.eclair.{Globals, TestUtils} +import fr.acinq.eclair.{Globals, MilliSatoshi, TestUtils} import org.scalatest.{BeforeAndAfterAll, Matchers, Outcome, fixture} import scala.concurrent.duration._ @@ -60,7 +60,7 @@ class RustyTestsSpec extends TestKit(ActorSystem("test")) with Matchers with fix val bobInit = Init(Bob.channelParams.globalFeatures, Bob.channelParams.localFeatures) // alice and bob will both have 1 000 000 sat feeEstimator.setFeerate(FeeratesPerKw.single(10000)) - alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, 2000000, 1000000000, feeEstimator.getFeeratePerKw(target = 2), feeEstimator.getFeeratePerKw(target = 6), Alice.channelParams, pipe, bobInit, ChannelFlags.Empty) + alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, Satoshi(2000000), MilliSatoshi(1000000000), feeEstimator.getFeeratePerKw(target = 2), feeEstimator.getFeeratePerKw(target = 6), Alice.channelParams, pipe, bobInit, ChannelFlags.Empty) bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, Bob.channelParams, pipe, aliceInit) pipe ! (alice, bob) within(30 seconds) { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala index 2c72623576..1a2f5eabe6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/interop/rustytests/SynchronizationPipe.scala @@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch import akka.actor.{Actor, ActorLogging, ActorRef, Stash} import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.{TestConstants, TestUtils} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, TestUtils} import fr.acinq.eclair.channel._ import fr.acinq.eclair.transactions.{IN, OUT} @@ -57,7 +57,7 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging script match { case offer(x, amount, rhash) :: rest => - resolve(x) ! CMD_ADD_HTLC(amount.toInt, ByteVector32.fromValidHex(rhash), 144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) + resolve(x) ! CMD_ADD_HTLC(MilliSatoshi(amount.toInt), ByteVector32.fromValidHex(rhash), 144, TestConstants.emptyOnionPacket, upstream = Left(UUID.randomUUID())) exec(rest, a, b) case fulfill(x, id, r) :: rest => resolve(x) ! CMD_FULFILL_HTLC(id.toInt, ByteVector32.fromValidHex(r)) @@ -134,15 +134,15 @@ class SynchronizationPipe(latch: CountDownLatch) extends Actor with ActorLogging s" Commit ${d.commitments.localCommit.index}:", s" Offered htlcs: ${localCommit.spec.htlcs.filter(_.direction == OUT).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", s" Received htlcs: ${localCommit.spec.htlcs.filter(_.direction == IN).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", - s" Balance us: ${localCommit.spec.toLocalMsat}", - s" Balance them: ${localCommit.spec.toRemoteMsat}", + s" Balance us: ${localCommit.spec.toLocal}", + s" Balance them: ${localCommit.spec.toRemote}", s" Fee rate: ${localCommit.spec.feeratePerKw}", "REMOTE COMMITS:", s" Commit ${remoteCommit.index}:", s" Offered htlcs: ${remoteCommit.spec.htlcs.filter(_.direction == OUT).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", s" Received htlcs: ${remoteCommit.spec.htlcs.filter(_.direction == IN).map(h => (h.add.id, h.add.amountMsat)).mkString(" ")}", - s" Balance us: ${remoteCommit.spec.toLocalMsat}", - s" Balance them: ${remoteCommit.spec.toRemoteMsat}", + s" Balance us: ${remoteCommit.spec.toLocal}", + s" Balance them: ${remoteCommit.spec.toRemote}", s" Fee rate: ${remoteCommit.spec.feeratePerKw}") .foreach(s => { fout.write(rtrim(s)) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala index 4378f5cf1d..57debf6a86 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/HtlcReaperSpec.scala @@ -18,6 +18,7 @@ package fr.acinq.eclair.io import akka.actor.{ActorSystem, Props} import akka.testkit.{TestKit, TestProbe} +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.channel._ import fr.acinq.eclair.{TestConstants, randomBytes32} import fr.acinq.eclair.wire.{ChannelCodecsSpec, TemporaryNodeFailure, UpdateAddHtlc} @@ -36,11 +37,11 @@ class HtlcReaperSpec extends TestKit(ActorSystem("test")) with FunSuiteLike { val data = ChannelCodecsSpec.normal // assuming that data has incoming htlcs 0 and 1, we don't care about the amount/payment_hash/onion fields - val add0 = UpdateAddHtlc(data.channelId, 0, 20000, randomBytes32, 100, TestConstants.emptyOnionPacket) - val add1 = UpdateAddHtlc(data.channelId, 1, 30000, randomBytes32, 100, TestConstants.emptyOnionPacket) + val add0 = UpdateAddHtlc(data.channelId, 0, MilliSatoshi(20000), randomBytes32, 100, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(data.channelId, 1, MilliSatoshi(30000), randomBytes32, 100, TestConstants.emptyOnionPacket) // unrelated htlc - val add99 = UpdateAddHtlc(randomBytes32, 0, 12345678, randomBytes32, 100, TestConstants.emptyOnionPacket) + val add99 = UpdateAddHtlc(randomBytes32, 0, MilliSatoshi(12345678), randomBytes32, 100, TestConstants.emptyOnionPacket) val brokenHtlcs = Seq(add0, add1, add99) val brokenHtlcKiller = system.actorOf(Props[HtlcReaper], name = "htlc-reaper") diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala index 76fd945de6..d53d86ddba 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala @@ -22,7 +22,7 @@ import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition} import akka.actor.{ActorRef, PoisonPill} import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.{Satoshi} import fr.acinq.eclair.TestConstants._ import fr.acinq.eclair._ import fr.acinq.eclair.blockchain.{EclairWallet, TestWallet} diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala index ccc65efe0b..71027c5e10 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/ChannelSelectionSpec.scala @@ -22,7 +22,7 @@ import fr.acinq.eclair.channel.{CMD_ADD_HTLC, CMD_FAIL_HTLC} import fr.acinq.eclair.payment.Relayer.{OutgoingChannel, RelayFailure, RelayPayload, RelaySuccess} import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, TestConstants, randomBytes32, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, randomBytes32, randomKey} import fr.acinq.eclair.payment.HtlcGenerationSpec.makeCommitments import org.scalatest.FunSuite @@ -33,17 +33,17 @@ class ChannelSelectionSpec extends FunSuite { /** * This is just a simplified helper function with random values for fields we are not using here */ - def dummyUpdate(shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: Long, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true) = - Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, shortChannelId, cltvExpiryDelta, htlcMinimumMsat, feeBaseMsat, feeProportionalMillionths, htlcMaximumMsat, enable) + def dummyUpdate(shortChannelId: ShortChannelId, cltvExpiryDelta: Int, htlcMinimumMsat: MilliSatoshi, feeBaseMsat: Long, feeProportionalMillionths: Long, htlcMaximumMsat: Long, enable: Boolean = true) = + Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, randomKey, randomKey.publicKey, shortChannelId, cltvExpiryDelta, htlcMinimumMsat, MilliSatoshi(feeBaseMsat), feeProportionalMillionths, MilliSatoshi(htlcMaximumMsat), enable) test("convert to CMD_FAIL_HTLC/CMD_ADD_HTLC") { val relayPayload = RelayPayload( - add = UpdateAddHtlc(randomBytes32, 42, 1000000, randomBytes32, 70, TestConstants.emptyOnionPacket), - payload = PerHopPayload(ShortChannelId(12345), amtToForward = 998900, outgoingCltvValue = 60), + add = UpdateAddHtlc(randomBytes32, 42, MilliSatoshi(1000000), randomBytes32, 70, TestConstants.emptyOnionPacket), + payload = PerHopPayload(ShortChannelId(12345), amtToForward = MilliSatoshi(998900), outgoingCltvValue = 60), nextPacket = TestConstants.emptyOnionPacket // just a placeholder ) - val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, 100, 1000, 100, 10000000, true) + val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, MilliSatoshi(100), 1000, 100, 10000000, true) implicit val log = akka.event.NoLogging @@ -55,36 +55,36 @@ class ChannelSelectionSpec extends FunSuite { val channelUpdate_disabled = channelUpdate.copy(channelFlags = Announcements.makeChannelFlags(true, enable = false)) assert(Relayer.relayOrFail(relayPayload, Some(channelUpdate_disabled)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(ChannelDisabled(channelUpdate_disabled.messageFlags, channelUpdate_disabled.channelFlags, channelUpdate_disabled)), commit = true))) // amount too low - val relayPayload_toolow = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 99)) + val relayPayload_toolow = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(99))) assert(Relayer.relayOrFail(relayPayload_toolow, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(AmountBelowMinimum(relayPayload_toolow.payload.amtToForward, channelUpdate)), commit = true))) // incorrect cltv expiry val relayPayload_incorrectcltv = relayPayload.copy(payload = relayPayload.payload.copy(outgoingCltvValue = 42)) assert(Relayer.relayOrFail(relayPayload_incorrectcltv, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(IncorrectCltvExpiry(relayPayload_incorrectcltv.payload.outgoingCltvValue, channelUpdate)), commit = true))) // insufficient fee - val relayPayload_insufficientfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 998910)) + val relayPayload_insufficientfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(998910))) assert(Relayer.relayOrFail(relayPayload_insufficientfee, Some(channelUpdate)) === RelayFailure(CMD_FAIL_HTLC(relayPayload.add.id, Right(FeeInsufficient(relayPayload_insufficientfee.add.amountMsat, channelUpdate)), commit = true))) // note that a generous fee is ok! - val relayPayload_highfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = 900000)) + val relayPayload_highfee = relayPayload.copy(payload = relayPayload.payload.copy(amtToForward = MilliSatoshi(900000))) assert(Relayer.relayOrFail(relayPayload_highfee, Some(channelUpdate)) === RelaySuccess(ShortChannelId(12345), CMD_ADD_HTLC(relayPayload_highfee.payload.amtToForward, relayPayload_highfee.add.paymentHash, relayPayload_highfee.payload.outgoingCltvValue, relayPayload_highfee.nextPacket, upstream = Right(relayPayload.add), commit = true))) } test("channel selection") { val relayPayload = RelayPayload( - add = UpdateAddHtlc(randomBytes32, 42, 1000000, randomBytes32, 70, TestConstants.emptyOnionPacket), - payload = PerHopPayload(ShortChannelId(12345), amtToForward = 998900, outgoingCltvValue = 60), + add = UpdateAddHtlc(randomBytes32, 42, MilliSatoshi(1000000), randomBytes32, 70, TestConstants.emptyOnionPacket), + payload = PerHopPayload(ShortChannelId(12345), amtToForward = MilliSatoshi(998900), outgoingCltvValue = 60), nextPacket = TestConstants.emptyOnionPacket // just a placeholder ) val (a, b) = (randomKey.publicKey, randomKey.publicKey) - val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, 100, 1000, 100, 10000000, true) + val channelUpdate = dummyUpdate(ShortChannelId(12345), 10, MilliSatoshi(100), 1000, 100, 10000000, true) val channelUpdates = Map( - ShortChannelId(11111) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 100000000)), - ShortChannelId(12345) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 20000000)), - ShortChannelId(22222) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 10000000)), - ShortChannelId(33333) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, 100000)), - ShortChannelId(44444) -> OutgoingChannel(b, channelUpdate, makeCommitments(ByteVector32.Zeroes, 1000000)) + ShortChannelId(11111) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(100000000))), + ShortChannelId(12345) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(20000000))), + ShortChannelId(22222) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(10000000))), + ShortChannelId(33333) -> OutgoingChannel(a, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(100000))), + ShortChannelId(44444) -> OutgoingChannel(b, channelUpdate, makeCommitments(ByteVector32.Zeroes, MilliSatoshi(1000000))) ) val node2channels = new mutable.HashMap[PublicKey, mutable.Set[ShortChannelId]] with mutable.MultiMap[PublicKey, ShortChannelId] @@ -104,11 +104,11 @@ class ChannelSelectionSpec extends FunSuite { // all the suitable channels have been tried assert(Relayer.selectPreferredChannel(relayPayload, channelUpdates, node2channels, Seq(ShortChannelId(22222), ShortChannelId(12345), ShortChannelId(11111))) === None) // higher amount payment (have to increased incoming htlc amount for fees to be sufficient) - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.add.amountMsat).setTo(60000000).modify(_.payload.amtToForward).setTo(50000000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(11111))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.add.amountMsat).setTo(MilliSatoshi(60000000)).modify(_.payload.amtToForward).setTo(MilliSatoshi(50000000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(11111))) // lower amount payment - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(1000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(33333))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(MilliSatoshi(1000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(33333))) // payment too high, no suitable channel found - assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(1000000000), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) + assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.amtToForward).setTo(MilliSatoshi(1000000000)), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) // invalid cltv expiry, no suitable channel, we keep the requested one assert(Relayer.selectPreferredChannel(relayPayload.modify(_.payload.outgoingCltvValue).setTo(40), channelUpdates, node2channels, Seq.empty) === Some(ShortChannelId(12345))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala index 9114a8e418..02252bc923 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/HtlcGenerationSpec.scala @@ -20,13 +20,14 @@ import java.util.UUID import fr.acinq.bitcoin.DeterministicWallet.ExtendedPrivateKey import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet} +import fr.acinq.eclair.maxOf import fr.acinq.eclair.channel.{Channel, ChannelVersion, Commitments} import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.crypto.Sphinx.{DecryptedPacket, PacketAndSecrets} import fr.acinq.eclair.payment.PaymentLifecycle._ import fr.acinq.eclair.router.Hop import fr.acinq.eclair.wire.{ChannelUpdate, OnionCodecs, PerHopPayload} -import fr.acinq.eclair.{ShortChannelId, TestConstants, nodeFee, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, nodeFee, randomBytes32} import org.scalatest.FunSuite import scodec.bits.ByteVector @@ -37,9 +38,9 @@ import scodec.bits.ByteVector class HtlcGenerationSpec extends FunSuite { test("compute fees") { - val feeBaseMsat = 150000L + val feeBaseMsat = MilliSatoshi(150000L) val feeProportionalMillionth = 4L - val htlcAmountMsat = 42000000 + val htlcAmountMsat = MilliSatoshi(42000000) // spec: fee-base-msat + htlc-amount-msat * fee-proportional-millionths / 1000000 val ref = feeBaseMsat + htlcAmountMsat * feeProportionalMillionth / 1000000 val fee = nodeFee(feeBaseMsat, feeProportionalMillionth, htlcAmountMsat) @@ -98,7 +99,7 @@ class HtlcGenerationSpec extends FunSuite { val (add, _) = buildCommand(UUID.randomUUID, finalAmountMsat, finalExpiry, paymentHash, hops) - assert(add.amountMsat > finalAmountMsat) + assert(add.amount > finalAmountMsat) assert(add.cltvExpiry === finalExpiry + channelUpdate_de.cltvExpiryDelta + channelUpdate_cd.cltvExpiryDelta + channelUpdate_bc.cltvExpiryDelta) assert(add.paymentHash === paymentHash) assert(add.onion.payload.length === Sphinx.PaymentPacket.PayloadLength) @@ -132,7 +133,7 @@ class HtlcGenerationSpec extends FunSuite { test("build a command with no hops") { val (add, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops.take(1)) - assert(add.amountMsat === finalAmountMsat) + assert(add.amount === finalAmountMsat) assert(add.cltvExpiry === finalExpiry) assert(add.paymentHash === paymentHash) assert(add.onion.payload.length === Sphinx.PaymentPacket.PayloadLength) @@ -149,10 +150,10 @@ class HtlcGenerationSpec extends FunSuite { object HtlcGenerationSpec { - def makeCommitments(channelId: ByteVector32, availableBalanceForSend: Long = 50000000L, availableBalanceForReceive: Long = 50000000L) = + def makeCommitments(channelId: ByteVector32, testAvailableBalanceForSend: MilliSatoshi = MilliSatoshi(50000000L), testAvailableBalanceForReceive: MilliSatoshi = MilliSatoshi(50000000L)) = new Commitments(ChannelVersion.STANDARD, null, null, 0.toByte, null, null, null, null, 0, 0, Map.empty, null, null, null, channelId) { - override lazy val availableBalanceForSendMsat: Long = availableBalanceForSend.max(0) - override lazy val availableBalanceForReceiveMsat: Long = availableBalanceForReceive.max(0) + override lazy val availableBalanceForSend: MilliSatoshi = maxOf(testAvailableBalanceForSend, MilliSatoshi(0)) + override lazy val availableBalanceForReceive: MilliSatoshi = maxOf(testAvailableBalanceForReceive, MilliSatoshi(0)) } def randomExtendedPrivateKey: ExtendedPrivateKey = DeterministicWallet.generate(randomBytes32) @@ -160,11 +161,11 @@ object HtlcGenerationSpec { val (priv_a, priv_b, priv_c, priv_d, priv_e) = (TestConstants.Alice.keyManager.nodeKey, TestConstants.Bob.keyManager.nodeKey, randomExtendedPrivateKey, randomExtendedPrivateKey, randomExtendedPrivateKey) val (a, b, c, d, e) = (priv_a.publicKey, priv_b.publicKey, priv_c.publicKey, priv_d.publicKey, priv_e.publicKey) val sig = Crypto.sign(Crypto.sha256(ByteVector.empty), priv_a.privateKey) - val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0, 1, 0, 0, 42000, 0, 0, Some(500000000L)) - val channelUpdate_ab = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(1), cltvExpiryDelta = 4, feeBaseMsat = 642000, feeProportionalMillionths = 7) - val channelUpdate_bc = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(2), cltvExpiryDelta = 5, feeBaseMsat = 153000, feeProportionalMillionths = 4) - val channelUpdate_cd = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(3), cltvExpiryDelta = 10, feeBaseMsat = 60000, feeProportionalMillionths = 1) - val channelUpdate_de = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(4), cltvExpiryDelta = 7, feeBaseMsat = 766000, feeProportionalMillionths = 10) + val defaultChannelUpdate = ChannelUpdate(sig, Block.RegtestGenesisBlock.hash, ShortChannelId(0), 0, 1, 0, 0, MilliSatoshi(42000), MilliSatoshi(0), 0, Some(MilliSatoshi(500000000L))) + val channelUpdate_ab = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(1), cltvExpiryDelta = 4, feeBaseMsat = MilliSatoshi(642000), feeProportionalMillionths = 7) + val channelUpdate_bc = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(2), cltvExpiryDelta = 5, feeBaseMsat = MilliSatoshi(153000), feeProportionalMillionths = 4) + val channelUpdate_cd = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(3), cltvExpiryDelta = 10, feeBaseMsat = MilliSatoshi(60000), feeProportionalMillionths = 1) + val channelUpdate_de = defaultChannelUpdate.copy(shortChannelId = ShortChannelId(4), cltvExpiryDelta = 7, feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10) // simple route a -> b -> c -> d -> e @@ -174,7 +175,7 @@ object HtlcGenerationSpec { Hop(c, d, channelUpdate_cd) :: Hop(d, e, channelUpdate_de) :: Nil - val finalAmountMsat = 42000000L + val finalAmountMsat = MilliSatoshi(42000000L) val currentBlockCount = 420000 val finalExpiry = currentBlockCount + Channel.MIN_CLTV_EXPIRY val paymentPreimage = randomBytes32 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala index 3915d24895..4a1b21f452 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentHandlerSpec.scala @@ -19,13 +19,13 @@ package fr.acinq.eclair.payment import akka.actor.Status.Failure import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestKit, TestProbe} -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.ByteVector32 import fr.acinq.eclair.TestConstants.Alice import fr.acinq.eclair.channel.{CMD_FAIL_HTLC, CMD_FULFILL_HTLC} import fr.acinq.eclair.payment.PaymentLifecycle.ReceivePayment import fr.acinq.eclair.payment.PaymentRequest.ExtraHop import fr.acinq.eclair.wire.{FinalExpiryTooSoon, UpdateAddHtlc} -import fr.acinq.eclair.{Globals, ShortChannelId, TestConstants, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, TestConstants, randomKey} import org.scalatest.FunSuiteLike import scodec.bits.ByteVector @@ -54,7 +54,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike assert(nodeParams.db.payments.getPendingPaymentRequestAndPreimage(pr.paymentHash).isDefined) assert(!nodeParams.db.payments.getPendingPaymentRequestAndPreimage(pr.paymentHash).get._2.isExpired) - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FULFILL_HTLC] @@ -68,7 +68,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike val pr = sender.expectMsgType[PaymentRequest] assert(nodeParams.db.payments.getIncomingPayment(pr.paymentHash).isEmpty) - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FULFILL_HTLC] val paymentRelayed = eventListener.expectMsgType[PaymentReceived] @@ -81,7 +81,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike val pr = sender.expectMsgType[PaymentRequest] assert(nodeParams.db.payments.getIncomingPayment(pr.paymentHash).isEmpty) - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, cltvExpiry = Globals.blockCount.get() + 3, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, cltvExpiry = Globals.blockCount.get() + 3, TestConstants.emptyOnionPacket) sender.send(handler, add) assert(sender.expectMsgType[CMD_FAIL_HTLC].reason == Right(FinalExpiryTooSoon)) eventListener.expectNoMsg(300 milliseconds) @@ -164,7 +164,7 @@ class PaymentHandlerSpec extends TestKit(ActorSystem("test")) with FunSuiteLike sender.send(handler, ReceivePayment(Some(amountMsat), "some desc", expirySeconds_opt = Some(0))) val pr = sender.expectMsgType[PaymentRequest] - val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat.amount, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) + val add = UpdateAddHtlc(ByteVector32(ByteVector.fill(32)(1)), 0, amountMsat, pr.paymentHash, expiry, TestConstants.emptyOnionPacket) sender.send(handler, add) sender.expectMsgType[CMD_FAIL_HTLC] diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala index 79192309bf..9d178c0819 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentLifecycleSpec.scala @@ -22,7 +22,7 @@ import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition} import akka.actor.Status import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.Script.{pay2wsh, write} -import fr.acinq.bitcoin.{Block, ByteVector32, MilliSatoshi, Satoshi, Transaction, TxOut} +import fr.acinq.bitcoin.{Block, ByteVector32, Satoshi, Transaction, TxOut} import fr.acinq.eclair.blockchain.{UtxoStatus, ValidateRequest, ValidateResult, WatchSpentBasic} import fr.acinq.eclair.channel.Register.ForwardShortId import fr.acinq.eclair.channel.{AddHtlcFailed, ChannelUnavailable} @@ -42,7 +42,7 @@ import fr.acinq.eclair._ class PaymentLifecycleSpec extends BaseRouterSpec { - val defaultAmountMsat = 142000000L + val defaultAmountMsat = MilliSatoshi(142000000L) test("send to route") { fixture => import fixture._ @@ -109,7 +109,7 @@ class PaymentLifecycleSpec extends BaseRouterSpec { paymentFSM ! SubscribeTransitionCallBack(monitor.ref) val CurrentState(_, WAITING_FOR_REQUEST) = monitor.expectMsgClass(classOf[CurrentState[_]]) - val request = SendPayment(defaultAmountMsat, randomBytes32, d, routeParams = Some(RouteParams(randomize = false, maxFeeBaseMsat = 100, maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = 2016, ratios = None)), maxAttempts = 5) + val request = SendPayment(defaultAmountMsat, randomBytes32, d, routeParams = Some(RouteParams(randomize = false, maxFeeBase = MilliSatoshi(100), maxFeePct = 0.0, routeMaxLength = 20, routeMaxCltv = 2016, ratios = None)), maxAttempts = 5) sender.send(paymentFSM, request) val Transition(_, WAITING_FOR_REQUEST, WAITING_FOR_ROUTE) = monitor.expectMsgClass(classOf[Transition[_]]) @@ -389,9 +389,9 @@ class PaymentLifecycleSpec extends BaseRouterSpec { sender.send(paymentFSM, UpdateFulfillHtlc(ByteVector32.Zeroes, 0, defaultPaymentHash)) val paymentOK = sender.expectMsgType[PaymentSucceeded] - val PaymentSent(_, MilliSatoshi(request.amountMsat), fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] + val PaymentSent(_, request.amount, fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] assert(fee > MilliSatoshi(0)) - assert(fee === MilliSatoshi(paymentOK.amountMsat - request.amountMsat)) + assert(fee === paymentOK.amount - request.amount) awaitCond(paymentDb.getOutgoingPayment(id).exists(_.status == OutgoingPaymentStatus.SUCCEEDED)) } @@ -409,8 +409,8 @@ class PaymentLifecycleSpec extends BaseRouterSpec { val ann_g = makeNodeAnnouncement(priv_g, "node-G", Color(-30, 10, -50), Nil) val channelId_bg = ShortChannelId(420000, 5, 0) val chan_bg = channelAnnouncement(channelId_bg, priv_b, priv_g, priv_funding_b, priv_funding_g) - val channelUpdate_bg = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, g, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 0, feeProportionalMillionths = 0, htlcMaximumMsat = 500000000L) - val channelUpdate_gb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_g, b, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) + val channelUpdate_bg = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, g, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(0), feeProportionalMillionths = 0, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_gb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_g, b, channelId_bg, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) assert(Router.getDesc(channelUpdate_bg, chan_bg) === ChannelDesc(chan_bg.shortChannelId, priv_b.publicKey, priv_g.publicKey)) router ! PeerRoutingMessage(null, remoteNodeId, chan_bg) router ! PeerRoutingMessage(null, remoteNodeId, ann_g) @@ -441,13 +441,13 @@ class PaymentLifecycleSpec extends BaseRouterSpec { sender.send(paymentFSM, UpdateFulfillHtlc(ByteVector32.Zeroes, 0, defaultPaymentHash)) val paymentOK = sender.expectMsgType[PaymentSucceeded] - val PaymentSent(_, MilliSatoshi(request.amountMsat), fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] + val PaymentSent(_, request.amount, fee, request.paymentHash, paymentOK.paymentPreimage, _, _) = eventListener.expectMsgType[PaymentSent] // during the route computation the fees were treated as if they were 1msat but when sending the onion we actually put zero // NB: A -> B doesn't pay fees because it's our direct neighbor // NB: B -> G doesn't asks for fees at all assert(fee === MilliSatoshi(0)) - assert(fee === MilliSatoshi(paymentOK.amountMsat - request.amountMsat)) + assert(fee === paymentOK.amount - request.amount) } test("filter errors properly") { _ => diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala index f67bd0e074..19706bcac9 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PaymentRequestSpec.scala @@ -17,14 +17,15 @@ package fr.acinq.eclair.payment import java.nio.ByteOrder - import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{Block, Btc, ByteVector32, Crypto, MilliBtc, MilliSatoshi, Protocol, Satoshi} -import fr.acinq.eclair.ShortChannelId +import fr.acinq.bitcoin.{Block, Btc, ByteVector32, Crypto, MilliBtc, Protocol, Satoshi} +import fr.acinq.bitcoin._ +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.payment.PaymentRequest._ import org.scalatest.FunSuite import scodec.DecodeResult import scodec.bits._ +import fr.acinq.eclair._ /** * Created by fabrice on 15/05/17. @@ -42,13 +43,13 @@ class PaymentRequestSpec extends FunSuite { assert('p' === Amount.unit(MilliSatoshi(99))) assert('n' === Amount.unit(MilliSatoshi(100))) assert('p' === Amount.unit(MilliSatoshi(101))) - assert('n' === Amount.unit(Satoshi(1))) - assert('u' === Amount.unit(Satoshi(100))) - assert('n' === Amount.unit(Satoshi(101))) - assert('u' === Amount.unit(Satoshi(1155400))) - assert('m' === Amount.unit(MilliBtc(1))) - assert('m' === Amount.unit(MilliBtc(10))) - assert('m' === Amount.unit(Btc(1))) + assert('n' === Amount.unit(Satoshi(1).toMilliSatoshi)) + assert('u' === Amount.unit(Satoshi(100).toMilliSatoshi)) + assert('n' === Amount.unit(Satoshi(101).toMilliSatoshi)) + assert('u' === Amount.unit(Satoshi(1155400).toMilliSatoshi)) + assert('m' === Amount.unit(millibtc2satoshi(MilliBtc(1)).toMilliSatoshi)) + assert('m' === Amount.unit(millibtc2satoshi(MilliBtc(10)).toMilliSatoshi)) + assert('m' === Amount.unit(btc2satoshi(Btc(1)).toMilliSatoshi)) } test("check that we can still decode non-minimal amount encoding") { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala index c6f6a27d41..97db737ee4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/RelayerSpec.scala @@ -20,13 +20,13 @@ import java.util.UUID import akka.actor.{ActorRef, Status} import akka.testkit.TestProbe -import fr.acinq.bitcoin.{ByteVector32, MilliSatoshi} +import fr.acinq.bitcoin.{ByteVector32, Satoshi} import fr.acinq.eclair.channel._ import fr.acinq.eclair.crypto.Sphinx import fr.acinq.eclair.payment.PaymentLifecycle.buildCommand import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{ShortChannelId, TestConstants, TestkitBaseClass, UInt64, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, TestConstants, TestkitBaseClass, UInt64, randomBytes32} import org.scalatest.Outcome import scodec.bits.ByteVector @@ -63,7 +63,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -83,14 +83,14 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) // we tell the relayer about channel B-C relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) // this is another channel B-C, with less balance (it will be preferred) val (channelId_bc_1, channelUpdate_bc_1) = (randomBytes32, channelUpdate_bc.copy(shortChannelId = ShortChannelId("500000x1x1"))) - relayer ! LocalChannelUpdate(null, channelId_bc_1, channelUpdate_bc_1.shortChannelId, c, None, channelUpdate_bc_1, makeCommitments(channelId_bc_1, 49000000L)) + relayer ! LocalChannelUpdate(null, channelId_bc_1, channelUpdate_bc_1.shortChannelId, c, None, channelUpdate_bc_1, makeCommitments(channelId_bc_1, MilliSatoshi(49000000L))) sender.send(relayer, ForwardAdd(add_ab)) @@ -100,7 +100,7 @@ class RelayerSpec extends TestkitBaseClass { assert(fwd1.message.upstream === Right(add_ab)) // channel returns an error - val origin = Relayed(channelId_ab, originHtlcId = 42, amountMsatIn = 1100000, amountMsatOut = 1000000) + val origin = Relayed(channelId_ab, originHtlcId = 42, amountIn = MilliSatoshi(1100000), amountOut = MilliSatoshi(1000000)) sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc_1, paymentHash, HtlcValueTooHighInFlight(channelId_bc_1, UInt64(1000000000L), UInt64(1516977616L)), origin, Some(channelUpdate_bc_1), originalCommand = Some(fwd1.message)))) // second try @@ -128,7 +128,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) sender.send(relayer, ForwardAdd(add_ab)) @@ -148,7 +148,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -174,7 +174,7 @@ class RelayerSpec extends TestkitBaseClass { // check that payments are sent properly val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -190,7 +190,7 @@ class RelayerSpec extends TestkitBaseClass { relayer ! LocalChannelDown(sender.ref, channelId = channelId_bc, shortChannelId = channelUpdate_bc.shortChannelId, remoteNodeId = TestConstants.Bob.nodeParams.nodeId) val (cmd1, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, randomBytes32, hops) - val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amountMsat, cmd1.paymentHash, cmd1.cltvExpiry, cmd1.onion) + val add_ab1 = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd1.amount, cmd1.paymentHash, cmd1.cltvExpiry, cmd1.onion) sender.send(relayer, ForwardAdd(add_ab1)) val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message @@ -208,7 +208,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) val channelUpdate_bc_disabled = channelUpdate_bc.copy(channelFlags = Announcements.makeChannelFlags(Announcements.isNode1(channelUpdate_bc.channelFlags), enable = false)) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc_disabled, makeCommitments(channelId_bc)) @@ -229,7 +229,7 @@ class RelayerSpec extends TestkitBaseClass { // we use this to build a valid onion val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops) // and then manually build an htlc with an invalid onion (hmac) - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion.copy(hmac = cmd.onion.hmac.reverse)) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion.copy(hmac = cmd.onion.hmac.reverse)) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -248,16 +248,16 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() // we use this to build a valid onion - val (cmd, _) = buildCommand(UUID.randomUUID(), channelUpdate_bc.htlcMinimumMsat - 1, finalExpiry, paymentHash, hops.map(hop => hop.copy(lastUpdate = hop.lastUpdate.copy(feeBaseMsat = 0, feeProportionalMillionths = 0)))) + val (cmd, _) = buildCommand(UUID.randomUUID(), channelUpdate_bc.htlcMinimumMsat - MilliSatoshi(1), finalExpiry, paymentHash, hops.map(hop => hop.copy(lastUpdate = hop.lastUpdate.copy(feeBaseMsat = MilliSatoshi(0), feeProportionalMillionths = 0)))) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) val fail = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message assert(fail.id === add_ab.id) - assert(fail.reason == Right(AmountBelowMinimum(cmd.amountMsat, channelUpdate_bc))) + assert(fail.reason == Right(AmountBelowMinimum(cmd.amount, channelUpdate_bc))) register.expectNoMsg(100 millis) paymentHandler.expectNoMsg(100 millis) @@ -270,7 +270,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(cltvExpiryDelta = 0))) val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -290,7 +290,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.updated(1, hops(1).copy(lastUpdate = hops(1).lastUpdate.copy(feeBaseMsat = hops(1).lastUpdate.feeBaseMsat / 2))) val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -311,7 +311,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.head :: Nil val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc with a wrong expiry - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat - 1, cmd.paymentHash, cmd.cltvExpiry, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount - MilliSatoshi(1), cmd.paymentHash, cmd.cltvExpiry, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -332,7 +332,7 @@ class RelayerSpec extends TestkitBaseClass { val hops1 = hops.head :: Nil val (cmd, _) = buildCommand(UUID.randomUUID(), finalAmountMsat, finalExpiry, paymentHash, hops1) // and then manually build an htlc with a wrong expiry - val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amountMsat, cmd.paymentHash, cmd.cltvExpiry - 1, cmd.onion) + val add_ab = UpdateAddHtlc(channelId = channelId_ab, id = 123456, cmd.amount, cmd.paymentHash, cmd.cltvExpiry - 1, cmd.onion) relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc)) sender.send(relayer, ForwardAdd(add_ab)) @@ -350,7 +350,7 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() val paymentHash = randomBytes32 - val origin = Relayed(channelId_ab, originHtlcId = 42, amountMsatIn = 1100000, amountMsatOut = 1000000) + val origin = Relayed(channelId_ab, originHtlcId = 42, amountIn = MilliSatoshi(1100000), amountOut = MilliSatoshi(1000000)) sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, ExpiryTooSmall(channelId_bc, 100, 0, 0), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(ExpiryTooSoon(channelUpdate_bc))) @@ -358,7 +358,7 @@ class RelayerSpec extends TestkitBaseClass { sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, ExpiryTooBig(channelId_bc, 100, 200, 0), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(ExpiryTooFar)) - sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, InsufficientFunds(channelId_bc, origin.amountMsatOut, 100, 0, 0), origin, Some(channelUpdate_bc), None))) + sender.send(relayer, Status.Failure(AddHtlcFailed(channelId_bc, paymentHash, InsufficientFunds(channelId_bc, origin.amountOut, Satoshi(100), Satoshi(0), Satoshi(0)), origin, Some(channelUpdate_bc), None))) assert(register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]].message.reason === Right(TemporaryChannelFailure(channelUpdate_bc))) val channelUpdate_bc_disabled = channelUpdate_bc.copy(channelFlags = 2) @@ -383,9 +383,9 @@ class RelayerSpec extends TestkitBaseClass { system.eventStream.subscribe(eventListener.ref, classOf[PaymentEvent]) // we build a fake htlc for the downstream channel - val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) + val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = MilliSatoshi(10000000L), paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) val fulfill_ba = UpdateFulfillHtlc(channelId = channelId_bc, id = 42, paymentPreimage = ByteVector32.Zeroes) - val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L) + val origin = Relayed(channelId_ab, 150, MilliSatoshi(11000000L), MilliSatoshi(10000000L)) sender.send(relayer, ForwardFulfill(fulfill_ba, origin, add_bc)) val fwd = register.expectMsgType[Register.Forward[CMD_FULFILL_HTLC]] @@ -393,7 +393,7 @@ class RelayerSpec extends TestkitBaseClass { assert(fwd.message.id === origin.originHtlcId) val paymentRelayed = eventListener.expectMsgType[PaymentRelayed] - assert(paymentRelayed.copy(timestamp = 0) === PaymentRelayed(MilliSatoshi(origin.amountMsatIn), MilliSatoshi(origin.amountMsatOut), add_bc.paymentHash, channelId_ab, channelId_bc, timestamp = 0)) + assert(paymentRelayed.copy(timestamp = 0) === PaymentRelayed(origin.amountIn, origin.amountOut, add_bc.paymentHash, channelId_ab, channelId_bc, timestamp = 0)) } test("relay an htlc-fail") { f => @@ -401,9 +401,9 @@ class RelayerSpec extends TestkitBaseClass { val sender = TestProbe() // we build a fake htlc for the downstream channel - val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = 10000000L, paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) + val add_bc = UpdateAddHtlc(channelId = channelId_bc, id = 72, amountMsat = MilliSatoshi(10000000L), paymentHash = ByteVector32.Zeroes, cltvExpiry = 4200, onionRoutingPacket = TestConstants.emptyOnionPacket) val fail_ba = UpdateFailHtlc(channelId = channelId_bc, id = 42, reason = Sphinx.FailurePacket.create(ByteVector32(ByteVector.fill(32)(1)), TemporaryChannelFailure(channelUpdate_cd))) - val origin = Relayed(channelId_ab, 150, 11000000L, 10000000L) + val origin = Relayed(channelId_ab, 150, MilliSatoshi(11000000L), MilliSatoshi(10000000L)) sender.send(relayer, ForwardFail(fail_ba, origin, add_bc)) val fwd = register.expectMsgType[Register.Forward[CMD_FAIL_HTLC]] @@ -414,31 +414,31 @@ class RelayerSpec extends TestkitBaseClass { test("get usable balances") { f => import f._ val sender = TestProbe() - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, -2000, 300000)) - relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc, 400000, -5000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, MilliSatoshi(-2000), MilliSatoshi(300000))) + relayer ! LocalChannelUpdate(null, channelId_bc, channelUpdate_bc.shortChannelId, c, None, channelUpdate_bc, makeCommitments(channelId_bc, MilliSatoshi(400000), MilliSatoshi(-5000))) sender.send(relayer, GetUsableBalances) val usableBalances1 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances1.size === 2) - assert(usableBalances1.head.canSendMsat === 0 && usableBalances1.head.canReceiveMsat === 300000 && usableBalances1.head.shortChannelId == channelUpdate_ab.shortChannelId) - assert(usableBalances1.last.canReceiveMsat === 0 && usableBalances1.last.canSendMsat === 400000 && usableBalances1.last.shortChannelId == channelUpdate_bc.shortChannelId) + assert(usableBalances1.head.canSend === MilliSatoshi(0) && usableBalances1.head.canReceive === MilliSatoshi(300000) && usableBalances1.head.shortChannelId == channelUpdate_ab.shortChannelId) + assert(usableBalances1.last.canReceive === MilliSatoshi(0) && usableBalances1.last.canSend === MilliSatoshi(400000) && usableBalances1.last.shortChannelId == channelUpdate_bc.shortChannelId) - relayer ! AvailableBalanceChanged(null, channelId_bc, channelUpdate_bc.shortChannelId, 0, makeCommitments(channelId_bc, 200000, 500000)) + relayer ! AvailableBalanceChanged(null, channelId_bc, channelUpdate_bc.shortChannelId, MilliSatoshi(0), makeCommitments(channelId_bc, MilliSatoshi(200000), MilliSatoshi(500000))) sender.send(relayer, GetUsableBalances) val usableBalances2 = sender.expectMsgType[Iterable[UsableBalances]] - assert(usableBalances2.last.canReceiveMsat === 500000 && usableBalances2.last.canSendMsat === 200000) + assert(usableBalances2.last.canReceive === MilliSatoshi(500000) && usableBalances2.last.canSend === MilliSatoshi(200000)) - relayer ! AvailableBalanceChanged(null, channelId_ab, channelUpdate_ab.shortChannelId, 0, makeCommitments(channelId_ab, 100000, 200000)) + relayer ! AvailableBalanceChanged(null, channelId_ab, channelUpdate_ab.shortChannelId, MilliSatoshi(0), makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) relayer ! LocalChannelDown(null, channelId_bc, channelUpdate_bc.shortChannelId, c) sender.send(relayer, GetUsableBalances) val usableBalances3 = sender.expectMsgType[Iterable[UsableBalances]] - assert(usableBalances3.size === 1 && usableBalances3.head.canSendMsat === 100000) + assert(usableBalances3.size === 1 && usableBalances3.head.canSend === MilliSatoshi(100000)) - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab.copy(channelFlags = 2), makeCommitments(channelId_ab, 100000, 200000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab.copy(channelFlags = 2), makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) sender.send(relayer, GetUsableBalances) val usableBalances4 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances4.isEmpty) - relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, 100000, 200000)) + relayer ! LocalChannelUpdate(null, channelId_ab, channelUpdate_ab.shortChannelId, a, None, channelUpdate_ab, makeCommitments(channelId_ab, MilliSatoshi(100000), MilliSatoshi(200000))) sender.send(relayer, GetUsableBalances) val usableBalances5 = sender.expectMsgType[Iterable[UsableBalances]] assert(usableBalances5.size === 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala index 0667d2a7bd..a3c29fda8e 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsBatchValidationSpec.scala @@ -27,10 +27,9 @@ import fr.acinq.eclair.blockchain.bitcoind.BitcoinCoreWallet import fr.acinq.eclair.blockchain.bitcoind.rpc.{BasicBitcoinJsonRPCClient, ExtendedBitcoinClient} import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.wire.{ChannelAnnouncement, ChannelUpdate} -import fr.acinq.eclair.{ShortChannelId, randomKey} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, randomKey} import org.scalatest.FunSuite import scodec.bits.ByteVector - import scala.concurrent.duration._ import scala.concurrent.{Await, ExecutionContext} @@ -104,6 +103,6 @@ object AnnouncementsBatchValidationSpec { } def makeChannelUpdate(c: SimulatedChannel, shortChannelId: ShortChannelId): ChannelUpdate = - Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, c.node1Key, c.node2Key.publicKey, shortChannelId, 10, 1000, 10, 100, 500000000L) + Announcements.makeChannelUpdate(Block.RegtestGenesisBlock.hash, c.node1Key, c.node2Key.publicKey, shortChannelId, 10, MilliSatoshi(1000), MilliSatoshi(10), 100, MilliSatoshi(500000000L)) } \ No newline at end of file diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala index d64bfab29f..41b5c722f1 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/AnnouncementsSpec.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair.router -import fr.acinq.bitcoin.Block +import fr.acinq.bitcoin.{Block} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} import fr.acinq.eclair.TestConstants.Alice import fr.acinq.eclair._ @@ -55,7 +55,7 @@ class AnnouncementsSpec extends FunSuite { } test("create valid signed channel update announcement") { - val ann = makeChannelUpdate(Block.RegtestGenesisBlock.hash, Alice.nodeParams.privateKey, randomKey.publicKey, ShortChannelId(45561L), Alice.nodeParams.expiryDeltaBlocks, Alice.nodeParams.htlcMinimumMsat, Alice.nodeParams.feeBaseMsat, Alice.nodeParams.feeProportionalMillionth, 500000000L) + val ann = makeChannelUpdate(Block.RegtestGenesisBlock.hash, Alice.nodeParams.privateKey, randomKey.publicKey, ShortChannelId(45561L), Alice.nodeParams.expiryDeltaBlocks, Alice.nodeParams.htlcMinimum, Alice.nodeParams.feeBase, Alice.nodeParams.feeProportionalMillionth, MilliSatoshi(500000000L)) assert(checkSig(ann, Alice.nodeParams.nodeId)) assert(checkSig(ann, randomKey.publicKey) === false) } @@ -66,10 +66,10 @@ class AnnouncementsSpec extends FunSuite { // NB: node1 < node2 (public keys) assert(isNode1(node1_priv.publicKey, node2_priv.publicKey)) assert(!isNode1(node2_priv.publicKey, node1_priv.publicKey)) - val channelUpdate1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = true) - val channelUpdate1_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = false) - val channelUpdate2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = true) - val channelUpdate2_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, 0, 0, 0, 500000000L, enable = false) + val channelUpdate1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = true) + val channelUpdate1_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node1_priv, node2_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = false) + val channelUpdate2 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = true) + val channelUpdate2_disabled = makeChannelUpdate(Block.RegtestGenesisBlock.hash, node2_priv, node1_priv.publicKey, ShortChannelId(0), 0, MilliSatoshi(0), MilliSatoshi(0), 0, MilliSatoshi(500000000L), enable = false) assert(channelUpdate1.channelFlags == 0) // ....00 assert(channelUpdate1_disabled.channelFlags == 2) // ....10 assert(channelUpdate2.channelFlags == 1) // ....01 diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala index fc63698bb1..4acdc1db69 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/BaseRouterSpec.scala @@ -78,14 +78,14 @@ abstract class BaseRouterSpec extends TestkitBaseClass { val chan_cd = channelAnnouncement(channelId_cd, priv_c, priv_d, priv_funding_c, priv_funding_d) val chan_ef = channelAnnouncement(channelId_ef, priv_e, priv_f, priv_funding_e, priv_funding_f) - val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, b, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L) - val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, a, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L) - val channelUpdate_bc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 1, htlcMaximumMsat = 500000000L) - val channelUpdate_cb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, b, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 1, htlcMaximumMsat = 500000000L) - val channelUpdate_cd = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L) - val channelUpdate_dc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_d, c, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L) - val channelUpdate_ef = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_e, f, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) - val channelUpdate_fe = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_f, e, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = 0, feeBaseMsat = 10, feeProportionalMillionths = 8, htlcMaximumMsat = 500000000L) + val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, b, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, a, channelId_ab, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_bc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, c, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 1, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_cb = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, b, channelId_bc, cltvExpiryDelta = 5, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 1, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_cd = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_dc = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_d, c, channelId_cd, cltvExpiryDelta = 3, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_ef = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_e, f, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) + val channelUpdate_fe = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_f, e, channelId_ef, cltvExpiryDelta = 9, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(10), feeProportionalMillionths = 8, htlcMaximumMsat = MilliSatoshi(500000000L)) override def withFixture(test: OneArgTest): Outcome = { // the network will be a --(1)--> b ---(2)--> c --(3)--> d and e --(4)--> f (we are a) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala index 85c0e2ce82..9730365850 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/GraphSpec.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.router import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge} import fr.acinq.eclair.router.RouteCalculationSpec._ import fr.acinq.eclair.wire.ChannelUpdate @@ -46,12 +46,12 @@ class GraphSpec extends FunSuite { def makeTestGraph() = { val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, a, d, 0, 0), - makeUpdate(4L, d, c, 0, 0), - makeUpdate(5L, c, e, 0, 0), - makeUpdate(6L, b, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, a, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, c, MilliSatoshi(0), 0), + makeUpdate(5L, c, e, MilliSatoshi(0), 0), + makeUpdate(6L, b, e, MilliSatoshi(0), 0) ) DirectedGraph.makeGraph(updates.toMap) @@ -72,11 +72,11 @@ class GraphSpec extends FunSuite { assert(otherGraph.vertexSet().size === 5) // add some edges to the graph - val (descAB, updateAB) = makeUpdate(1L, a, b, 0, 0) - val (descBC, updateBC) = makeUpdate(2L, b, c, 0, 0) - val (descAD, updateAD) = makeUpdate(3L, a, d, 0, 0) - val (descDC, updateDC) = makeUpdate(4L, d, c, 0, 0) - val (descCE, updateCE) = makeUpdate(5L, c, e, 0, 0) + val (descAB, updateAB) = makeUpdate(1L, a, b, MilliSatoshi(0), 0) + val (descBC, updateBC) = makeUpdate(2L, b, c, MilliSatoshi(0), 0) + val (descAD, updateAD) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, updateDC) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) + val (descCE, updateCE) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) val graphWithEdges = graph .addEdge(descAB, updateAB) @@ -98,12 +98,12 @@ class GraphSpec extends FunSuite { test("instantiate a graph adding edges only") { - val edgeAB = edgeFromDesc(makeUpdate(1L, a, b, 0, 0)) - val (descBC, updateBC) = makeUpdate(2L, b, c, 0, 0) - val (descAD, updateAD) = makeUpdate(3L, a, d, 0, 0) - val (descDC, updateDC) = makeUpdate(4L, d, c, 0, 0) - val (descCE, updateCE) = makeUpdate(5L, c, e, 0, 0) - val (descBE, updateBE) = makeUpdate(6L, b, e, 0, 0) + val edgeAB = edgeFromDesc(makeUpdate(1L, a, b, MilliSatoshi(0), 0)) + val (descBC, updateBC) = makeUpdate(2L, b, c, MilliSatoshi(0), 0) + val (descAD, updateAD) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, updateDC) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) + val (descCE, updateCE) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) + val (descBE, updateBE) = makeUpdate(6L, b, e, MilliSatoshi(0), 0) val graph = DirectedGraph(edgeAB) .addEdge(descAD, updateAD) @@ -121,10 +121,10 @@ class GraphSpec extends FunSuite { test("containsEdge should return true if the graph contains that edge, false otherwise") { val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ) val graph = DirectedGraph().addEdges(updates) @@ -144,10 +144,10 @@ class GraphSpec extends FunSuite { val graph = makeTestGraph() - val (descBE, _) = makeUpdate(6L, b, e, 0, 0) - val (descCE, _) = makeUpdate(5L, c, e, 0, 0) - val (descAD, _) = makeUpdate(3L, a, d, 0, 0) - val (descDC, _) = makeUpdate(4L, d, c, 0, 0) + val (descBE, _) = makeUpdate(6L, b, e, MilliSatoshi(0), 0) + val (descCE, _) = makeUpdate(5L, c, e, MilliSatoshi(0), 0) + val (descAD, _) = makeUpdate(3L, a, d, MilliSatoshi(0), 0) + val (descDC, _) = makeUpdate(4L, d, c, MilliSatoshi(0), 0) assert(graph.edgeSet().size === 6) @@ -168,8 +168,8 @@ class GraphSpec extends FunSuite { // contains an edge A --> B val updates = Seq( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0) ) val graph = DirectedGraph().addEdges(updates) @@ -199,19 +199,19 @@ class GraphSpec extends FunSuite { assert(graph.edgesOf(a).size == 2) //now add a new edge a -> b but with a different channel update and a different ShortChannelId - val newEdgeForNewChannel = edgeFromDesc(makeUpdate(15L, a, b, 20, 0)) + val newEdgeForNewChannel = edgeFromDesc(makeUpdate(15L, a, b, MilliSatoshi(20), 0)) val mutatedGraph = graph.addEdge(newEdgeForNewChannel) assert(mutatedGraph.edgesOf(a).size == 3) //if the ShortChannelId is the same we replace the edge and the update, this edge have an update with a different 'feeBaseMsat' - val edgeForTheSameChannel = edgeFromDesc(makeUpdate(15L, a, b, 30, 0)) + val edgeForTheSameChannel = edgeFromDesc(makeUpdate(15L, a, b, MilliSatoshi(30), 0)) val mutatedGraph2 = mutatedGraph.addEdge(edgeForTheSameChannel) assert(mutatedGraph2.edgesOf(a).size == 3) // A --> B , A --> B , A --> D assert(mutatedGraph2.getEdgesBetween(a, b).size === 2) - assert(mutatedGraph2.getEdge(edgeForTheSameChannel).get.update.feeBaseMsat === 30) + assert(mutatedGraph2.getEdge(edgeForTheSameChannel).get.update.feeBaseMsat === MilliSatoshi(30)) } test("remove a vertex with incoming edges and check those edges are removed too") { @@ -234,11 +234,11 @@ class GraphSpec extends FunSuite { def edgeFromDesc(tuple: (ChannelDesc, ChannelUpdate)): GraphEdge = GraphEdge(tuple._1, tuple._2) def descFromNodes(shortChannelId: Long, a: PublicKey, b: PublicKey): ChannelDesc = { - makeUpdate(shortChannelId, a, b, 0, 0)._1 + makeUpdate(shortChannelId, a, b, MilliSatoshi(0), 0)._1 } def edgeFromNodes(shortChannelId: Long, a: PublicKey, b: PublicKey): GraphEdge = { - edgeFromDesc(makeUpdate(shortChannelId, a, b, 0, 0)) + edgeFromDesc(makeUpdate(shortChannelId, a, b, MilliSatoshi(0), 0)) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala index c09fd82df1..c4930b0bdd 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouteCalculationSpec.scala @@ -24,7 +24,7 @@ import fr.acinq.eclair.router.Graph.GraphStructure.{DirectedGraph, GraphEdge} import fr.acinq.eclair.router.Graph.{RichWeight, WeightRatios} import fr.acinq.eclair.transactions.Transactions import fr.acinq.eclair.wire._ -import fr.acinq.eclair.{Globals, ShortChannelId, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, randomKey} import org.scalatest.FunSuite import scodec.bits._ @@ -43,10 +43,10 @@ class RouteCalculationSpec extends FunSuite { test("calculate simple route") { val updates = List( - makeUpdate(1L, a, b, 1, 10, cltvDelta = 1), - makeUpdate(2L, b, c, 1, 10, cltvDelta = 1), - makeUpdate(3L, c, d, 1, 10, cltvDelta = 1), - makeUpdate(4L, d, e, 1, 10, cltvDelta = 1) + makeUpdate(1L, a, b, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(2L, b, c, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(3L, c, d, MilliSatoshi(1), 10, cltvDelta = 1), + makeUpdate(4L, d, e, MilliSatoshi(1), 10, cltvDelta = 1) ).toMap val g = makeGraph(updates) @@ -75,43 +75,43 @@ class RouteCalculationSpec extends FunSuite { // cost(AE) = 10007 -> A is source, shortest path found // cost(AB) = 10009 - val amountMsat = 10000 - val expectedCost = 10007 + val amount = MilliSatoshi(10000) + val expectedCost = MilliSatoshi(10007) val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 1, feeProportionalMillionth = 200, minHtlcMsat = 0), - makeUpdate(4L, a, e, feeBaseMsat = 1, feeProportionalMillionth = 200, minHtlcMsat = 0), - makeUpdate(2L, b, c, feeBaseMsat = 1, feeProportionalMillionth = 300, minHtlcMsat = 0), - makeUpdate(3L, c, d, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0), - makeUpdate(5L, e, f, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0), - makeUpdate(6L, f, d, feeBaseMsat = 1, feeProportionalMillionth = 100, minHtlcMsat = 0) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(1), feeProportionalMillionth = 200, minHtlc = MilliSatoshi(0)), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(1), feeProportionalMillionth = 200, minHtlc = MilliSatoshi(0)), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), feeProportionalMillionth = 300, minHtlc = MilliSatoshi(0)), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0)), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0)), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(1), feeProportionalMillionth = 100, minHtlc = MilliSatoshi(0)) ).toMap val graph = makeGraph(updates) - val Success(route) = Router.findRoute(graph, a, d, amountMsat, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route) = Router.findRoute(graph, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) - val totalCost = Graph.pathWeight(hops2Edges(route), amountMsat, false, 0, None).cost + val totalCost = Graph.pathWeight(hops2Edges(route), amount, false, 0, None).cost assert(hops2Ids(route) === 4 :: 5 :: 6 :: Nil) assert(totalCost === expectedCost) // now channel 5 could route the amount (10000) but not the amount + fees (10007) - val (desc, update) = makeUpdate(5L, e, f, feeBaseMsat = 1, feeProportionalMillionth = 400, minHtlcMsat = 0, maxHtlcMsat = Some(10005L)) + val (desc, update) = makeUpdate(5L, e, f, feeBase = MilliSatoshi(1), feeProportionalMillionth = 400, minHtlc = MilliSatoshi(0), maxHtlc = Some(MilliSatoshi(10005L))) val graph1 = graph.addEdge(desc, update) - val Success(route1) = Router.findRoute(graph1, a, d, amountMsat, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) + val Success(route1) = Router.findRoute(graph1, a, d, amount, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) assert(hops2Ids(route1) === 1 :: 2 :: 3 :: Nil) } test("calculate route considering the direct channel pays no fees") { val updates = List( - makeUpdate(1L, a, b, 5, 0), // a -> b - makeUpdate(2L, a, d, 15, 0), // a -> d this goes a bit closer to the target and asks for higher fees but is a direct channel - makeUpdate(3L, b, c, 5, 0), // b -> c - makeUpdate(4L, c, d, 5, 0), // c -> d - makeUpdate(5L, d, e, 5, 0) // d -> e + makeUpdate(1L, a, b, MilliSatoshi(5), 0), // a -> b + makeUpdate(2L, a, d, MilliSatoshi(15), 0), // a -> d this goes a bit closer to the target and asks for higher fees but is a direct channel + makeUpdate(3L, b, c, MilliSatoshi(5), 0), // b -> c + makeUpdate(4L, c, d, MilliSatoshi(5), 0), // c -> d + makeUpdate(5L, d, e, MilliSatoshi(5), 0) // d -> e ).toMap val g = makeGraph(updates) @@ -123,10 +123,10 @@ class RouteCalculationSpec extends FunSuite { test("calculate simple route (add and remove edges") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -149,10 +149,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(2L, g, h, 0, 0), - makeUpdate(3L, h, i, 0, 0), - makeUpdate(4L, f, h, 50, 0) // more expensive + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(2L, g, h, MilliSatoshi(0), 0), + makeUpdate(3L, h, i, MilliSatoshi(0), 0), + makeUpdate(4L, f, h, MilliSatoshi(50), 0) // more expensive ).toMap val graph = makeGraph(updates) @@ -172,10 +172,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(4L, f, i, 50, 0), // our starting node F has a direct channel with I - makeUpdate(2L, g, h, 0, 0), - makeUpdate(3L, h, i, 0, 0) + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(4L, f, i, MilliSatoshi(50), 0), // our starting node F has a direct channel with I + makeUpdate(2L, g, h, MilliSatoshi(0), 0), + makeUpdate(3L, h, i, MilliSatoshi(0), 0) ).toMap val graph = makeGraph(updates) @@ -193,10 +193,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 1, 0), + makeUpdate(1L, f, g, MilliSatoshi(1), 0), // the maximum htlc allowed by this channel is only 50msat greater than what we're sending - makeUpdate(2L, g, h, 1, 0, maxHtlcMsat = Some(DEFAULT_AMOUNT_MSAT + 50)), - makeUpdate(3L, h, i, 1, 0) + makeUpdate(2L, g, h, MilliSatoshi(1), 0, maxHtlc = Some(DEFAULT_AMOUNT_MSAT + MilliSatoshi(50))), + makeUpdate(3L, h, i, MilliSatoshi(1), 0) ).toMap val graph = makeGraph(updates) @@ -214,10 +214,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 1, 0), + makeUpdate(1L, f, g, MilliSatoshi(1), 0), // this channel requires a minimum amount that is larger than what we are sending - makeUpdate(2L, g, h, 1, 0, minHtlcMsat = DEFAULT_AMOUNT_MSAT + 50), - makeUpdate(3L, h, i, 1, 0) + makeUpdate(2L, g, h, MilliSatoshi(1), 0, minHtlc = DEFAULT_AMOUNT_MSAT + MilliSatoshi(50)), + makeUpdate(3L, h, i, MilliSatoshi(1), 0) ).toMap val graph = makeGraph(updates) @@ -236,10 +236,10 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, f, g, 0, 0), - makeUpdate(2L, g, h, 5, 5), // expensive g -> h channel - makeUpdate(6L, g, h, 0, 0), // cheap g -> h channel - makeUpdate(3L, h, i, 0, 0) + makeUpdate(1L, f, g, MilliSatoshi(0), 0), + makeUpdate(2L, g, h, MilliSatoshi(5), 5), // expensive g -> h channel + makeUpdate(6L, g, h, MilliSatoshi(0), 0), // cheap g -> h channel + makeUpdate(3L, h, i, MilliSatoshi(0), 0) ).toMap val graph = makeGraph(updates) @@ -251,11 +251,11 @@ class RouteCalculationSpec extends FunSuite { test("calculate longer but cheaper route") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0), - makeUpdate(5L, b, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0), + makeUpdate(5L, b, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -267,8 +267,8 @@ class RouteCalculationSpec extends FunSuite { test("no local channels") { val updates = List( - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -280,9 +280,9 @@ class RouteCalculationSpec extends FunSuite { test("route not found") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -294,8 +294,8 @@ class RouteCalculationSpec extends FunSuite { test("route not found (source OR target node not connected)") { val updates = List( - makeUpdate(2L, b, c, 0, 0), - makeUpdate(4L, c, d, 0, 0) + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(4L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates).addVertex(a).addVertex(e) @@ -310,15 +310,15 @@ class RouteCalculationSpec extends FunSuite { val lowAmount = DEFAULT_AMOUNT_MSAT / 10 val updatesHi = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0, maxHtlcMsat = Some(DEFAULT_AMOUNT_MSAT)), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0, maxHtlc = Some(DEFAULT_AMOUNT_MSAT)), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val updatesLo = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0, minHtlcMsat = DEFAULT_AMOUNT_MSAT), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0, minHtlc = DEFAULT_AMOUNT_MSAT), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updatesHi) @@ -331,9 +331,9 @@ class RouteCalculationSpec extends FunSuite { test("route to self") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -345,10 +345,10 @@ class RouteCalculationSpec extends FunSuite { test("route to immediate neighbor") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -359,10 +359,10 @@ class RouteCalculationSpec extends FunSuite { test("directed graph") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap // a->e works, e->a fails @@ -380,14 +380,14 @@ class RouteCalculationSpec extends FunSuite { val DUMMY_SIG = Transactions.PlaceHolderSig - val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0L, 0, 0, 1, 42, 2500, 140, None) - val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1L, 0, 1, 1, 43, 2501, 141, None) - val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 0, 1, 44, 2502, 142, None) - val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 1, 1, 45, 2503, 143, None) - val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 1, 0, 1, 46, 2504, 144, Some(500000000L)) - val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 0, 1, 1, 47, 2505, 145, None) - val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 0, 1, 48, 2506, 146, None) - val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 1, 1, 49, 2507, 147, None) + val uab = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 0L, 0, 0, 1, MilliSatoshi(42), MilliSatoshi(2500), 140, None) + val uba = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(1L), 1L, 0, 1, 1, MilliSatoshi(43), MilliSatoshi(2501), 141, None) + val ubc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 0, 1, MilliSatoshi(44), MilliSatoshi(2502), 142, None) + val ucb = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(2L), 1L, 0, 1, 1, MilliSatoshi(45), MilliSatoshi(2503), 143, None) + val ucd = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 1, 0, 1, MilliSatoshi(46), MilliSatoshi(2504), 144, Some(MilliSatoshi(500000000L))) + val udc = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(3L), 1L, 0, 1, 1, MilliSatoshi(47), MilliSatoshi(2505), 145, None) + val ude = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 0, 1, MilliSatoshi(48), MilliSatoshi(2506), 146, None) + val ued = ChannelUpdate(DUMMY_SIG, Block.RegtestGenesisBlock.hash, ShortChannelId(4L), 1L, 0, 1, 1, MilliSatoshi(49), MilliSatoshi(2507), 147, None) val updates = Map( ChannelDesc(ShortChannelId(1L), a, b) -> uab, @@ -434,10 +434,10 @@ class RouteCalculationSpec extends FunSuite { test("blacklist routes") { val updates = List( - makeUpdate(1L, a, b, 0, 0), - makeUpdate(2L, b, c, 0, 0), - makeUpdate(3L, c, d, 0, 0), - makeUpdate(4L, d, e, 0, 0) + makeUpdate(1L, a, b, MilliSatoshi(0), 0), + makeUpdate(2L, b, c, MilliSatoshi(0), 0), + makeUpdate(3L, c, d, MilliSatoshi(0), 0), + makeUpdate(4L, d, e, MilliSatoshi(0), 0) ).toMap val g = makeGraph(updates) @@ -446,7 +446,7 @@ class RouteCalculationSpec extends FunSuite { assert(route1.map(hops2Ids) === Failure(RouteNotFound)) // verify that we left the graph untouched - assert(g.containsEdge(makeUpdate(3L, c, d, 0, 0)._1)) // c -> d + assert(g.containsEdge(makeUpdate(3L, c, d, MilliSatoshi(0), 0)._1)) // c -> d assert(g.containsVertex(c)) assert(g.containsVertex(d)) @@ -457,9 +457,9 @@ class RouteCalculationSpec extends FunSuite { test("route to a destination that is not in the graph (with assisted routes)") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, d, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -468,7 +468,7 @@ class RouteCalculationSpec extends FunSuite { assert(route.map(hops2Ids) === Failure(RouteNotFound)) // now we add the missing edge to reach the destination - val (extraDesc, extraUpdate) = makeUpdate(4L, d, e, 5, 5) + val (extraDesc, extraUpdate) = makeUpdate(4L, d, e, MilliSatoshi(5), 5) val extraGraphEdges = Set(GraphEdge(extraDesc, extraUpdate)) val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS) @@ -478,25 +478,25 @@ class RouteCalculationSpec extends FunSuite { test("verify that extra hops takes precedence over known channels") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, d, 10, 10), - makeUpdate(4L, d, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10), + makeUpdate(4L, d, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) val route1 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS) assert(route1.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) - assert(route1.get(1).lastUpdate.feeBaseMsat == 10) + assert(route1.get(1).lastUpdate.feeBaseMsat == MilliSatoshi(10)) - val (extraDesc, extraUpdate) = makeUpdate(2L, b, c, 5, 5) + val (extraDesc, extraUpdate) = makeUpdate(2L, b, c, MilliSatoshi(5), 5) val extraGraphEdges = Set(GraphEdge(extraDesc, extraUpdate)) val route2 = Router.findRoute(g, a, e, DEFAULT_AMOUNT_MSAT, numRoutes = 1, extraEdges = extraGraphEdges, routeParams = DEFAULT_ROUTE_PARAMS) assert(route2.map(hops2Ids) === Success(1 :: 2 :: 3 :: 4 :: Nil)) - assert(route2.get(1).lastUpdate.feeBaseMsat == 5) + assert(route2.get(1).lastUpdate.feeBaseMsat == MilliSatoshi(5)) } test("compute ignored channels") { @@ -519,15 +519,15 @@ class RouteCalculationSpec extends FunSuite { ) val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(2L, c, b, 10, 10), - makeUpdate(3L, c, d, 10, 10), - makeUpdate(4L, d, e, 10, 10), - makeUpdate(5L, f, g, 10, 10), - makeUpdate(6L, f, h, 10, 10), - makeUpdate(7L, h, i, 10, 10), - makeUpdate(8L, i, j, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(2L, c, b, MilliSatoshi(10), 10), + makeUpdate(3L, c, d, MilliSatoshi(10), 10), + makeUpdate(4L, d, e, MilliSatoshi(10), 10), + makeUpdate(5L, f, g, MilliSatoshi(10), 10), + makeUpdate(6L, f, h, MilliSatoshi(10), 10), + makeUpdate(7L, h, i, MilliSatoshi(10), 10), + makeUpdate(8L, i, j, MilliSatoshi(10), 10) ).toMap val ignored = Router.getIgnoredChannelDesc(updates, ignoreNodes = Set(c, j, randomKey.publicKey)) @@ -547,7 +547,7 @@ class RouteCalculationSpec extends FunSuite { val updates = nodes .zip(nodes.drop(1)) // (0, 1) :: (1, 2) :: ... .zipWithIndex // ((0, 1), 0) :: ((1, 2), 1) :: ... - .map { case ((na, nb), index) => makeUpdate(index, na, nb, 5, 0) } + .map { case ((na, nb), index) => makeUpdate(index, na, nb, MilliSatoshi(5), 0) } .toMap val g = makeGraph(updates) @@ -565,10 +565,10 @@ class RouteCalculationSpec extends FunSuite { val updates = nodes .zip(nodes.drop(1)) // (0, 1) :: (1, 2) :: ... .zipWithIndex // ((0, 1), 0) :: ((1, 2), 1) :: ... - .map { case ((na, nb), index) => makeUpdate(index, na, nb, 1, 0) } + .map { case ((na, nb), index) => makeUpdate(index, na, nb, MilliSatoshi(1), 0) } .toMap - val updates2 = updates + makeUpdate(99, nodes(2), nodes(48), 1000, 0) // expensive shorter route + val updates2 = updates + makeUpdate(99, nodes(2), nodes(48), MilliSatoshi(1000), 0) // expensive shorter route val g = makeGraph(updates2) @@ -581,12 +581,12 @@ class RouteCalculationSpec extends FunSuite { val f = randomKey.publicKey val g = makeGraph(List( - makeUpdate(1, a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(2, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(3, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 50), - makeUpdate(4, a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(5, e, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6, f, d, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9) + makeUpdate(1, a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(2, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(3, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 50), + makeUpdate(4, a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(5, e, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6, f, d, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9) ).toMap) val route = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxCltv = 28)) @@ -598,12 +598,12 @@ class RouteCalculationSpec extends FunSuite { val f = randomKey.publicKey val g = makeGraph(List( - makeUpdate(1, a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(2, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(3, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(4, d, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(5, e, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6, b, f, feeBaseMsat = 5, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9) + makeUpdate(1, a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(2, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(3, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(4, d, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(5, e, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6, b, f, feeBase = MilliSatoshi(5), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9) ).toMap) val route = Router.findRoute(g, a, f, DEFAULT_AMOUNT_MSAT, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(routeMaxLength = 3)) @@ -613,11 +613,11 @@ class RouteCalculationSpec extends FunSuite { test("ignore loops") { val updates = List( - makeUpdate(1L, a, b, 10, 10), - makeUpdate(2L, b, c, 10, 10), - makeUpdate(3L, c, a, 10, 10), - makeUpdate(4L, c, d, 10, 10), - makeUpdate(5L, d, e, 10, 10) + makeUpdate(1L, a, b, MilliSatoshi(10), 10), + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(3L, c, a, MilliSatoshi(10), 10), + makeUpdate(4L, c, d, MilliSatoshi(10), 10), + makeUpdate(5L, d, e, MilliSatoshi(10), 10) ).toMap val g = makeGraph(updates) @@ -630,13 +630,13 @@ class RouteCalculationSpec extends FunSuite { // the graph contains a possible 0-cost path that goes back on its steps ( e -> f, f -> e ) val updates = List( - makeUpdate(1L, a, b, 10, 10), // a -> b - makeUpdate(2L, b, c, 10, 10), - makeUpdate(4L, c, d, 10, 10), - makeUpdate(3L, b, e, 0, 0), // b -> e - makeUpdate(6L, e, f, 0, 0), // e -> f - makeUpdate(6L, f, e, 0, 0), // e <- f - makeUpdate(5L, e, d, 0, 0) // e -> d + makeUpdate(1L, a, b, MilliSatoshi(10), 10), // a -> b + makeUpdate(2L, b, c, MilliSatoshi(10), 10), + makeUpdate(4L, c, d, MilliSatoshi(10), 10), + makeUpdate(3L, b, e, MilliSatoshi(0), 0), // b -> e + makeUpdate(6L, e, f, MilliSatoshi(0), 0), // e -> f + makeUpdate(6L, f, e, MilliSatoshi(0), 0), // e <- f + makeUpdate(5L, e, d, MilliSatoshi(0), 0) // e -> d ).toMap val g = makeGraph(updates) @@ -671,13 +671,13 @@ class RouteCalculationSpec extends FunSuite { val edges = Seq( - makeUpdate(1L, d, a, 1, 0), - makeUpdate(2L, d, e, 1, 0), - makeUpdate(3L, a, e, 1, 0), - makeUpdate(4L, e, b, 1, 0), - makeUpdate(5L, e, f, 1, 0), - makeUpdate(6L, b, c, 1, 0), - makeUpdate(7L, c, f, 1, 0) + makeUpdate(1L, d, a, MilliSatoshi(1), 0), + makeUpdate(2L, d, e, MilliSatoshi(1), 0), + makeUpdate(3L, a, e, MilliSatoshi(1), 0), + makeUpdate(4L, e, b, MilliSatoshi(1), 0), + makeUpdate(5L, e, f, MilliSatoshi(1), 0), + makeUpdate(6L, b, c, MilliSatoshi(1), 0), + makeUpdate(7L, c, f, MilliSatoshi(1), 0) ).toMap val graph = DirectedGraph.makeGraph(edges) @@ -703,15 +703,15 @@ class RouteCalculationSpec extends FunSuite { val edges = Seq( - makeUpdate(10L, c, e, 2, 0), - makeUpdate(20L, c, d, 3, 0), - makeUpdate(30L, d, f, 4, 5), // D- > F has a higher cost to distinguish it from the 2nd cheapest route - makeUpdate(40L, e, d, 1, 0), - makeUpdate(50L, e, f, 2, 0), - makeUpdate(60L, e, g, 3, 0), - makeUpdate(70L, f, g, 2, 0), - makeUpdate(80L, f, h, 1, 0), - makeUpdate(90L, g, h, 2, 0) + makeUpdate(10L, c, e, MilliSatoshi(2), 0), + makeUpdate(20L, c, d, MilliSatoshi(3), 0), + makeUpdate(30L, d, f, MilliSatoshi(4), 5), // D- > F has a higher cost to distinguish it from the 2nd cheapest route + makeUpdate(40L, e, d, MilliSatoshi(1), 0), + makeUpdate(50L, e, f, MilliSatoshi(2), 0), + makeUpdate(60L, e, g, MilliSatoshi(3), 0), + makeUpdate(70L, f, g, MilliSatoshi(2), 0), + makeUpdate(80L, f, h, MilliSatoshi(1), 0), + makeUpdate(90L, g, h, MilliSatoshi(2), 0) ) val graph = DirectedGraph().addEdges(edges) @@ -732,19 +732,19 @@ class RouteCalculationSpec extends FunSuite { // simple graph with only 2 possible paths from A to F val edges = Seq( - makeUpdate(1L, a, b, 1, 0), - makeUpdate(1L, b, a, 1, 0), - makeUpdate(2L, b, c, 1, 0), - makeUpdate(2L, c, b, 1, 0), - makeUpdate(3L, c, f, 1, 0), - makeUpdate(3L, f, c, 1, 0), - makeUpdate(4L, c, d, 1, 0), - makeUpdate(4L, d, c, 1, 0), - makeUpdate(41L, d, c, 1, 0), // there is more than one D -> C channel - makeUpdate(5L, d, e, 1, 0), - makeUpdate(5L, e, d, 1, 0), - makeUpdate(6L, e, f, 1, 0), - makeUpdate(6L, f, e, 1, 0) + makeUpdate(1L, a, b, MilliSatoshi(1), 0), + makeUpdate(1L, b, a, MilliSatoshi(1), 0), + makeUpdate(2L, b, c, MilliSatoshi(1), 0), + makeUpdate(2L, c, b, MilliSatoshi(1), 0), + makeUpdate(3L, c, f, MilliSatoshi(1), 0), + makeUpdate(3L, f, c, MilliSatoshi(1), 0), + makeUpdate(4L, c, d, MilliSatoshi(1), 0), + makeUpdate(4L, d, c, MilliSatoshi(1), 0), + makeUpdate(41L, d, c, MilliSatoshi(1), 0), // there is more than one D -> C channel + makeUpdate(5L, d, e, MilliSatoshi(1), 0), + makeUpdate(5L, e, d, MilliSatoshi(1), 0), + makeUpdate(6L, e, f, MilliSatoshi(1), 0), + makeUpdate(6L, f, e, MilliSatoshi(1), 0) ) val graph = DirectedGraph().addEdges(edges) @@ -759,19 +759,19 @@ class RouteCalculationSpec extends FunSuite { test("select a random route below the requested fee") { - val strictFeeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBaseMsat = 7, maxFeePct = 0) + val strictFeeParams = DEFAULT_ROUTE_PARAMS.copy(maxFeeBase = MilliSatoshi(7), maxFeePct = 0) // A -> B -> C -> D has total cost of 10000005 // A -> E -> C -> D has total cost of 11080003 !! // A -> E -> F -> D has total cost of 10000006 val g = makeGraph(List( - makeUpdate(1L, a, b, feeBaseMsat = 1, 0), - makeUpdate(4L, a, e, feeBaseMsat = 1, 0), - makeUpdate(2L, b, c, feeBaseMsat = 2, 0), - makeUpdate(3L, c, d, feeBaseMsat = 3, 0), - makeUpdate(5L, e, f, feeBaseMsat = 3, 0), - makeUpdate(6L, f, d, feeBaseMsat = 3, 0), - makeUpdate(7L, e, c, feeBaseMsat = 9, 0) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(1), 0), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(1), 0), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(2), 0), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(3), 0), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(3), 0), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(3), 0), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(9), 0) ).toMap) (for {_ <- 0 to 10} yield Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT, numRoutes = 3, routeParams = strictFeeParams)).map { @@ -781,25 +781,25 @@ class RouteCalculationSpec extends FunSuite { val routeCost = Graph.pathWeight(hops2Edges(someRoute), DEFAULT_AMOUNT_MSAT, isPartial = false, 0, None).cost - DEFAULT_AMOUNT_MSAT // over the three routes we could only get the 2 cheapest because the third is too expensive (over 7msat of fees) - assert(routeCost == 5 || routeCost == 6) + assert(routeCost == MilliSatoshi(5) || routeCost == MilliSatoshi(6)) } } test("Use weight ratios to when computing the edge weight") { - val largeCapacity = 8000000000L + val largeCapacity = MilliSatoshi(8000000000L) // A -> B -> C -> D is 'fee optimized', lower fees route (totFees = 2, totCltv = 4000) // A -> E -> F -> D is 'timeout optimized', lower CLTV route (totFees = 3, totCltv = 18) // A -> E -> C -> D is 'capacity optimized', more recent channel/larger capacity route val updates = List( - makeUpdate(1L, a, b, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 13), - makeUpdate(4L, a, e, feeBaseMsat = 0, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdate(2L, b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(3L, c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 500), - makeUpdate(5L, e, f, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(6L, f, d, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 9), - makeUpdate(7L, e, c, feeBaseMsat = 2, 0, minHtlcMsat = 0, maxHtlcMsat = Some(largeCapacity), cltvDelta = 12) + makeUpdate(1L, a, b, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 13), + makeUpdate(4L, a, e, feeBase = MilliSatoshi(0), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdate(2L, b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(3L, c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 500), + makeUpdate(5L, e, f, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(6L, f, d, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 9), + makeUpdate(7L, e, c, feeBase = MilliSatoshi(2), 0, minHtlc = MilliSatoshi(0), maxHtlc = Some(largeCapacity), cltvDelta = 12) ).toMap val g = makeGraph(updates) @@ -829,12 +829,12 @@ class RouteCalculationSpec extends FunSuite { val currentBlockHeight = 554000 val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), // younger channel - makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x5"), e, f, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x6"), f, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144) + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), // younger channel + makeUpdateShort(ShortChannelId(s"${currentBlockHeight - 3000}x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"${currentBlockHeight}x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144) ).toMap) Globals.blockCount.set(currentBlockHeight) @@ -851,12 +851,12 @@ class RouteCalculationSpec extends FunSuite { test("prefer a route with a smaller total CLTV if fees and score are the same") { val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 10), // smaller CLTV - makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12), - makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 12) + makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 10), // smaller CLTV + makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12), + makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 12) ).toMap) @@ -875,12 +875,12 @@ class RouteCalculationSpec extends FunSuite { // A -> B -> C -> D is cheaper but has a total CLTV > 2016! // A -> E -> F -> D is more expensive but has a total CLTV < 2016 val g = makeGraph(List( - makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 1000), - makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBaseMsat = 1, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 900), - makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBaseMsat = 10, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144), - makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBaseMsat = 10, 0, minHtlcMsat = 0, maxHtlcMsat = None, cltvDelta = 144) + makeUpdateShort(ShortChannelId(s"0x0x1"), a, b, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x4"), a, e, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x2"), b, c, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 1000), + makeUpdateShort(ShortChannelId(s"0x0x3"), c, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 900), + makeUpdateShort(ShortChannelId(s"0x0x5"), e, f, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144), + makeUpdateShort(ShortChannelId(s"0x0x6"), f, d, feeBase = MilliSatoshi(1), 0, minHtlc = MilliSatoshi(0), maxHtlc = None, cltvDelta = 144) ).toMap) val Success(routeScoreOptimized) = Router.findRoute(g, a, d, DEFAULT_AMOUNT_MSAT / 2, numRoutes = 1, routeParams = DEFAULT_ROUTE_PARAMS.copy(ratios = Some(WeightRatios( @@ -897,22 +897,22 @@ class RouteCalculationSpec extends FunSuite { // This test have a channel (542280x2156x0) that according to heuristics is very convenient but actually useless to reach the target, // then if the cost function is not monotonic the path-finding breaks because the result path contains a loop. val updates = List( - ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = 0, feeBaseMsat = 1000, 100, Some(15000000000L)), - ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 0.toByte, 14, htlcMinimumMsat = 1, 1000, 10, Some(4294967295L)), - ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = 1000, feeBaseMsat = 1000, 100, Some(16777000000L)), - ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = 1, 667, 1, Some(16777000000L)), - ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 3.toByte, 144, htlcMinimumMsat = 1, 1000, 100, Some(230000000L)), - ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = 1, 1000, 100, Some(230000000L)) + ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(1000), 100, Some(MilliSatoshi(15000000000L))), + ChannelDesc(ShortChannelId("565643x1216x0"), PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565643x1216x0"), 0, 1.toByte, 0.toByte, 14, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 10, Some(MilliSatoshi(4294967295L))), + ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 1.toByte, 144, htlcMinimumMsat = MilliSatoshi(1000), feeBaseMsat = MilliSatoshi(1000), 100, Some(MilliSatoshi(16777000000L))), + ChannelDesc(ShortChannelId("542280x2156x0"), PublicKey(hex"03cb7983dc247f9f81a0fa2dfa3ce1c255365f7279c8dd143e086ca333df10e278"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("542280x2156x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(667), 1, Some(MilliSatoshi(16777000000L))), + ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 3.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 100, Some(MilliSatoshi(230000000L))), + ChannelDesc(ShortChannelId("565779x2711x0"), PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96"), PublicKey(hex"03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f")) -> ChannelUpdate(ByteVector64.Zeroes, ByteVector32.Zeroes, ShortChannelId("565779x2711x0"), 0, 1.toByte, 0.toByte, 144, htlcMinimumMsat = MilliSatoshi(1), MilliSatoshi(1000), 100, Some(MilliSatoshi(230000000L))) ).toMap val g = DirectedGraph.makeGraph(updates) - val params = RouteParams(randomize = false, maxFeeBaseMsat = 21000, maxFeePct = 0.03, routeMaxCltv = 1008, routeMaxLength = 6, ratios = Some( + val params = RouteParams(randomize = false, maxFeeBase = MilliSatoshi(21000), maxFeePct = 0.03, routeMaxCltv = 1008, routeMaxLength = 6, ratios = Some( WeightRatios(cltvDeltaFactor = 0.15, ageFactor = 0.35, capacityFactor = 0.5) )) val thisNode = PublicKey(hex"036d65409c41ab7380a43448f257809e7496b52bf92057c09c4f300cbd61c50d96") val targetNode = PublicKey(hex"024655b768ef40951b20053a5c4b951606d4d86085d51238f2c67c7dec29c792ca") - val amount = 351000 + val amount = MilliSatoshi(351000) Globals.blockCount.set(567634) // simulate mainnet block for heuristic val Success(route) = Router.findRoute(g, thisNode, targetNode, amount, 1, Set.empty, Set.empty, params) @@ -927,9 +927,9 @@ object RouteCalculationSpec { val noopBoundaries = { _: RichWeight => true } - val DEFAULT_AMOUNT_MSAT = 10000000 + val DEFAULT_AMOUNT_MSAT = MilliSatoshi(10000000) - val DEFAULT_ROUTE_PARAMS = RouteParams(randomize = false, maxFeeBaseMsat = 21000, maxFeePct = 0.03, routeMaxCltv = 2016, routeMaxLength = 6, ratios = None) + val DEFAULT_ROUTE_PARAMS = RouteParams(randomize = false, maxFeeBase = MilliSatoshi(21000), maxFeePct = 0.03, routeMaxCltv = 2016, routeMaxLength = 6, ratios = None) val DUMMY_SIG = Transactions.PlaceHolderSig @@ -938,26 +938,26 @@ object RouteCalculationSpec { ChannelAnnouncement(DUMMY_SIG, DUMMY_SIG, DUMMY_SIG, DUMMY_SIG, ByteVector.empty, Block.RegtestGenesisBlock.hash, ShortChannelId(shortChannelId), nodeId1, nodeId2, randomKey.publicKey, randomKey.publicKey) } - def makeUpdate(shortChannelId: Long, nodeId1: PublicKey, nodeId2: PublicKey, feeBaseMsat: Int, feeProportionalMillionth: Int, minHtlcMsat: Long = DEFAULT_AMOUNT_MSAT, maxHtlcMsat: Option[Long] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = { - makeUpdateShort(ShortChannelId(shortChannelId), nodeId1, nodeId2, feeBaseMsat, feeProportionalMillionth, minHtlcMsat, maxHtlcMsat, cltvDelta) + def makeUpdate(shortChannelId: Long, nodeId1: PublicKey, nodeId2: PublicKey, feeBase: MilliSatoshi, feeProportionalMillionth: Int, minHtlc: MilliSatoshi = DEFAULT_AMOUNT_MSAT, maxHtlc: Option[MilliSatoshi] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = { + makeUpdateShort(ShortChannelId(shortChannelId), nodeId1, nodeId2, feeBase, feeProportionalMillionth, minHtlc, maxHtlc, cltvDelta) } - def makeUpdateShort(shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, feeBaseMsat: Int, feeProportionalMillionth: Int, minHtlcMsat: Long = DEFAULT_AMOUNT_MSAT, maxHtlcMsat: Option[Long] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = + def makeUpdateShort(shortChannelId: ShortChannelId, nodeId1: PublicKey, nodeId2: PublicKey, feeBase: MilliSatoshi, feeProportionalMillionth: Int, minHtlc: MilliSatoshi = DEFAULT_AMOUNT_MSAT, maxHtlc: Option[MilliSatoshi] = None, cltvDelta: Int = 0): (ChannelDesc, ChannelUpdate) = ChannelDesc(shortChannelId, nodeId1, nodeId2) -> ChannelUpdate( signature = DUMMY_SIG, chainHash = Block.RegtestGenesisBlock.hash, shortChannelId = shortChannelId, timestamp = 0L, - messageFlags = maxHtlcMsat match { + messageFlags = maxHtlc match { case Some(_) => 1 case None => 0 }, channelFlags = 0, cltvExpiryDelta = cltvDelta, - htlcMinimumMsat = minHtlcMsat, - feeBaseMsat = feeBaseMsat, + htlcMinimumMsat = minHtlc, + feeBaseMsat = feeBase, feeProportionalMillionths = feeProportionalMillionth, - htlcMaximumMsat = maxHtlcMsat + htlcMaximumMsat = maxHtlc ) def makeGraph(updates: Map[ChannelDesc, ChannelUpdate]) = DirectedGraph.makeGraph(updates) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala index 5be5540eee..c5bc27ba6b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala @@ -30,7 +30,7 @@ import fr.acinq.eclair.router.Announcements.makeChannelUpdate import fr.acinq.eclair.router.RouteCalculationSpec.DEFAULT_AMOUNT_MSAT import fr.acinq.eclair.transactions.Scripts import fr.acinq.eclair.wire.QueryShortChannelIds -import fr.acinq.eclair.{Globals, ShortChannelId, randomKey} +import fr.acinq.eclair.{Globals, MilliSatoshi, ShortChannelId, randomKey} import scodec.bits._ import scala.collection.SortedSet @@ -52,21 +52,21 @@ class RouterSpec extends BaseRouterSpec { val channelId_ac = ShortChannelId(420000, 5, 0) val chan_ac = channelAnnouncement(channelId_ac, priv_a, priv_c, priv_funding_a, priv_funding_c) - val update_ac = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId_ac, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ac = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId_ac, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-x will not be found val priv_x = randomKey val chan_ax = channelAnnouncement(ShortChannelId(42001), priv_a, priv_x, priv_funding_a, randomKey) - val update_ax = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_x.publicKey, chan_ax.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ax = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_x.publicKey, chan_ax.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-y will have an invalid script val priv_y = randomKey val priv_funding_y = randomKey val chan_ay = channelAnnouncement(ShortChannelId(42002), priv_a, priv_y, priv_funding_a, priv_funding_y) - val update_ay = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_y.publicKey, chan_ay.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_ay = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_y.publicKey, chan_ay.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) // a-z will be spent val priv_z = randomKey val priv_funding_z = randomKey val chan_az = channelAnnouncement(ShortChannelId(42003), priv_a, priv_z, priv_funding_a, priv_funding_z) - val update_az = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_z.publicKey, chan_az.shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L) + val update_az = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_z.publicKey, chan_az.shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L)) router ! PeerRoutingMessage(null, remoteNodeId, chan_ac) router ! PeerRoutingMessage(null, remoteNodeId, chan_ax) @@ -201,7 +201,7 @@ class RouterSpec extends BaseRouterSpec { assert(res.hops.map(_.nodeId).toList === a :: b :: c :: Nil) assert(res.hops.last.nextNodeId === d) - val channelUpdate_cd1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, 0, feeBaseMsat = 153000, feeProportionalMillionths = 4, htlcMaximumMsat = 500000000L, enable = false) + val channelUpdate_cd1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_c, d, channelId_cd, cltvExpiryDelta = 3, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(153000), feeProportionalMillionths = 4, htlcMaximumMsat = MilliSatoshi(500000000L), enable = false) sender.send(router, PeerRoutingMessage(null, remoteNodeId, channelUpdate_cd1)) sender.expectMsg(TransportHandler.ReadAck(channelUpdate_cd1)) sender.send(router, RouteRequest(a, d, DEFAULT_AMOUNT_MSAT, routeParams = relaxedRouteParams)) @@ -257,7 +257,7 @@ class RouterSpec extends BaseRouterSpec { val channelId = ShortChannelId(blockHeight, 5, 0) val announcement = channelAnnouncement(channelId, priv_a, priv_c, priv_funding_a, priv_funding_c) val timestamp = (Platform.currentTime.milliseconds - 14.days - 1.day).toSeconds - val update = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, htlcMaximumMsat = 5, timestamp = timestamp) + val update = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(5), timestamp = timestamp) val probe = TestProbe() probe.ignoreMsg { case _: TransportHandler.ReadAck => true } probe.send(router, PeerRoutingMessage(null, remoteNodeId, announcement)) @@ -271,7 +271,7 @@ class RouterSpec extends BaseRouterSpec { val state = sender.expectMsgType[RoutingState] - val update1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, htlcMaximumMsat = 500000000L, timestamp = Platform.currentTime.millisecond.toSeconds) + val update1 = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, c, channelId, cltvExpiryDelta = 7, htlcMinimumMsat = MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, htlcMaximumMsat = MilliSatoshi(500000000L), timestamp = Platform.currentTime.millisecond.toSeconds) // we want to make sure that transport receives the query val transport = TestProbe() diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala index 1c4d851b65..34905e88ae 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/router/RoutingSyncSpec.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.router import akka.actor.ActorSystem import akka.testkit.{TestFSMRef, TestKit, TestProbe} -import fr.acinq.bitcoin.Block +import fr.acinq.bitcoin.{Block} import fr.acinq.eclair.TestConstants.{Alice, Bob} import fr.acinq.eclair._ import fr.acinq.eclair.crypto.TransportHandler @@ -132,8 +132,8 @@ object RoutingSyncSpec { val (priv_a, priv_b, priv_funding_a, priv_funding_b) = (randomKey, randomKey, randomKey, randomKey) val channelAnn_ab = channelAnnouncement(shortChannelId, priv_a, priv_b, priv_funding_a, priv_funding_b) val TxCoordinates(blockHeight, _, _) = ShortChannelId.coordinates(shortChannelId) - val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_b.publicKey, shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L, timestamp = blockHeight) - val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, priv_a.publicKey, shortChannelId, cltvExpiryDelta = 7, 0, feeBaseMsat = 766000, feeProportionalMillionths = 10, 500000000L, timestamp = blockHeight) + val channelUpdate_ab = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_a, priv_b.publicKey, shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L), timestamp = blockHeight) + val channelUpdate_ba = makeChannelUpdate(Block.RegtestGenesisBlock.hash, priv_b, priv_a.publicKey, shortChannelId, cltvExpiryDelta = 7, MilliSatoshi(0), feeBaseMsat = MilliSatoshi(766000), feeProportionalMillionths = 10, MilliSatoshi(500000000L), timestamp = blockHeight) val nodeAnnouncement_a = makeNodeAnnouncement(priv_a, "a", Color(0, 0, 0), List()) val nodeAnnouncement_b = makeNodeAnnouncement(priv_b, "b", Color(0, 0, 0), List()) (channelAnn_ab, channelUpdate_ab, channelUpdate_ba, nodeAnnouncement_a, nodeAnnouncement_b) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala index 1b95b50856..3df953ab0b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/CommitmentSpecSpec.scala @@ -17,53 +17,53 @@ package fr.acinq.eclair.transactions import fr.acinq.bitcoin.{ByteVector32, Crypto} -import fr.acinq.eclair.{TestConstants, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, randomBytes32} import fr.acinq.eclair.wire.{UpdateAddHtlc, UpdateFailHtlc, UpdateFulfillHtlc} import org.scalatest.FunSuite class CommitmentSpecSpec extends FunSuite { test("add, fulfill and fail htlcs from the sender side") { - val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocalMsat = 5000 * 1000, toRemoteMsat = 0) + val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocal = MilliSatoshi(5000000), toRemote = MilliSatoshi(0)) val R = randomBytes32 val H = Crypto.sha256(R) - val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec1 = CommitmentSpec.reduce(spec, add1 :: Nil, Nil) - assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(OUT, add1)), toLocalMsat = 3000 * 1000)) + assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(OUT, add1)), toLocal = MilliSatoshi(3000000))) - val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, 1000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(1000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec2 = CommitmentSpec.reduce(spec1, add2 :: Nil, Nil) - assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(OUT, add1), DirectedHtlc(OUT, add2)), toLocalMsat = 2000 * 1000)) + assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(OUT, add1), DirectedHtlc(OUT, add2)), toLocal = MilliSatoshi(2000000))) val ful1 = UpdateFulfillHtlc(ByteVector32.Zeroes, add1.id, R) val spec3 = CommitmentSpec.reduce(spec2, Nil, ful1 :: Nil) - assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(OUT, add2)), toRemoteMsat = 2000 * 1000)) + assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(OUT, add2)), toRemote = MilliSatoshi(2000000))) val fail1 = UpdateFailHtlc(ByteVector32.Zeroes, add2.id, R) val spec4 = CommitmentSpec.reduce(spec3, Nil, fail1 :: Nil) - assert(spec4 === spec3.copy(htlcs = Set(), toLocalMsat = 3000 * 1000)) + assert(spec4 === spec3.copy(htlcs = Set(), toLocal = MilliSatoshi(3000000))) } test("add, fulfill and fail htlcs from the receiver side") { - val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocalMsat = 0, toRemoteMsat = 5000 * 1000) + val spec = CommitmentSpec(htlcs = Set(), feeratePerKw = 1000, toLocal = MilliSatoshi(0), toRemote = MilliSatoshi(5000 * 1000)) val R = randomBytes32 val H = Crypto.sha256(R) - val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, 2000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add1 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec1 = CommitmentSpec.reduce(spec, Nil, add1 :: Nil) - assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(IN, add1)), toRemoteMsat = 3000 * 1000)) + assert(spec1 === spec.copy(htlcs = Set(DirectedHtlc(IN, add1)), toRemote = MilliSatoshi(3000 * 1000))) - val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, 1000 * 1000, H, 400, TestConstants.emptyOnionPacket) + val add2 = UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(1000 * 1000), H, 400, TestConstants.emptyOnionPacket) val spec2 = CommitmentSpec.reduce(spec1, Nil, add2 :: Nil) - assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(IN, add1), DirectedHtlc(IN, add2)), toRemoteMsat = 2000 * 1000)) + assert(spec2 === spec1.copy(htlcs = Set(DirectedHtlc(IN, add1), DirectedHtlc(IN, add2)), toRemote = MilliSatoshi(2000 * 1000))) val ful1 = UpdateFulfillHtlc(ByteVector32.Zeroes, add1.id, R) val spec3 = CommitmentSpec.reduce(spec2, ful1 :: Nil, Nil) - assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(IN, add2)), toLocalMsat = 2000 * 1000)) + assert(spec3 === spec2.copy(htlcs = Set(DirectedHtlc(IN, add2)), toLocal = MilliSatoshi(2000 * 1000))) val fail1 = UpdateFailHtlc(ByteVector32.Zeroes, add2.id, R) val spec4 = CommitmentSpec.reduce(spec3, fail1 :: Nil, Nil) - assert(spec4 === spec3.copy(htlcs = Set(), toRemoteMsat = 3000 * 1000)) + assert(spec4 === spec3.copy(htlcs = Set(), toRemote = MilliSatoshi(3000 * 1000))) } } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala index 013d70b8fb..d52e1f13cc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TestVectorsSpec.scala @@ -17,8 +17,9 @@ package fr.acinq.eclair.transactions import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin._ -import fr.acinq.eclair.TestConstants +import fr.acinq.bitcoin +import fr.acinq.bitcoin.{ByteVector32, Crypto, Satoshi, Script, ScriptFlags, Transaction} +import fr.acinq.eclair.{MilliSatoshi, TestConstants} import fr.acinq.eclair.channel.Helpers.Funding import fr.acinq.eclair.crypto.Generators import fr.acinq.eclair.transactions.Transactions.{HtlcSuccessTx, HtlcTimeoutTx, TransactionWithInputInfo} @@ -154,11 +155,11 @@ class TestVectorsSpec extends FunSuite with Logging { ) val htlcs = Seq( - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(3000000).amount, Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(4000000).amount, Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(3000000), Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(4000000), Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) ) val htlcScripts = htlcs.map(htlc => htlc.direction match { case OUT => Scripts.htlcOffered(Local.payment_privkey.publicKey, Remote.payment_privkey.publicKey, Local.revocation_pubkey, Crypto.ripemd160(htlc.add.paymentHash)) @@ -178,8 +179,8 @@ class TestVectorsSpec extends FunSuite with Logging { } def run(spec: CommitmentSpec) = { - logger.info(s"to_local_msat: ${spec.toLocalMsat}") - logger.info(s"to_remote_msat: ${spec.toRemoteMsat}") + logger.info(s"to_local_msat: ${spec.toLocal}") + logger.info(s"to_remote_msat: ${spec.toRemote}") logger.info(s"local_feerate_per_kw: ${spec.feeratePerKw}") val commitTx = { @@ -286,7 +287,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("simple commitment tx with no HTLCs") { val name = "simple commitment tx with no HTLCs" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = Set.empty, feeratePerKw = 15000, toLocalMsat = 7000000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = Set.empty, feeratePerKw = 15000, toLocal = MilliSatoshi(7000000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -297,7 +298,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with all 5 htlcs untrimmed (minimum feerate)") { val name = "commitment tx with all 5 htlcs untrimmed (minimum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 0, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 0, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 7) @@ -308,7 +309,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 7 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 7) @@ -322,7 +323,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 6 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 6) @@ -336,7 +337,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 6 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 6) @@ -350,7 +351,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 5 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 5) @@ -364,7 +365,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 5 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 5) @@ -378,7 +379,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 4 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 1454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 4) @@ -392,7 +393,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 4 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 2454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 4) @@ -406,7 +407,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 3 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 2454999 / Transactions.htlcTimeoutWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 3) @@ -420,7 +421,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 3 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") val feeratePerKw = 3454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 3) @@ -434,7 +435,7 @@ class TestVectorsSpec extends FunSuite with Logging { val name = "commitment tx with 2 outputs untrimmed (minimum feerate)" logger.info(s"name: $name") val feeratePerKw = 3454999 / Transactions.htlcSuccessWeight - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = feeratePerKw + 1, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -447,7 +448,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with 2 outputs untrimmed (maximum feerate)") { val name = "commitment tx with 2 outputs untrimmed (maximum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651180, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651180, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 2) @@ -460,7 +461,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with 1 output untrimmed (minimum feerate)") { val name = "commitment tx with 1 output untrimmed (minimum feerate)" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651181, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651181, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 1) @@ -473,7 +474,7 @@ class TestVectorsSpec extends FunSuite with Logging { test("commitment tx with fee greater than funder amount") { val name = "commitment tx with fee greater than funder amount" logger.info(s"name: $name") - val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651936, toLocalMsat = 6988000000L, toRemoteMsat = 3000000000L) + val spec = CommitmentSpec(htlcs = htlcs.toSet, feeratePerKw = 9651936, toLocal = MilliSatoshi(6988000000L), toRemote = MilliSatoshi(3000000000L)) val (commitTx, htlcTxs) = run(spec) assert(commitTx.tx.txOut.length == 1) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala index f3819c8bc8..e323788552 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala @@ -20,9 +20,10 @@ import java.nio.ByteOrder import fr.acinq.bitcoin.Crypto.{PrivateKey, ripemd160, sha256} import fr.acinq.bitcoin.Script.{pay2wpkh, pay2wsh, write} -import fr.acinq.bitcoin._ +import fr.acinq.bitcoin.{Btc, ByteVector32, Crypto, MilliBtc, Protocol, Satoshi, Script, Transaction, TxOut, millibtc2satoshi} import fr.acinq.eclair.channel.Helpers.Funding -import fr.acinq.eclair.{TestConstants, randomBytes32} +import fr.acinq.eclair.{MilliSatoshi, TestConstants, randomBytes32} +import fr.acinq.eclair._ import fr.acinq.eclair.transactions.Scripts.{htlcOffered, htlcReceived, toLocalDelayed} import fr.acinq.eclair.transactions.Transactions.{addSigs, _} import fr.acinq.eclair.wire.UpdateAddHtlc @@ -63,12 +64,12 @@ class TransactionsSpec extends FunSuite with Logging { test("compute fees") { // see BOLT #3 specs val htlcs = Set( - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(5000000).amount, ByteVector32.Zeroes, 552, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, ByteVector32.Zeroes, 553, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(7000000).amount, ByteVector32.Zeroes, 550, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(800000).amount, ByteVector32.Zeroes, 551, TestConstants.emptyOnionPacket)) + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(5000000), ByteVector32.Zeroes, 552, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), ByteVector32.Zeroes, 553, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(7000000), ByteVector32.Zeroes, 550, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(800000), ByteVector32.Zeroes, 551, TestConstants.emptyOnionPacket)) ) - val spec = CommitmentSpec(htlcs, feeratePerKw = 5000, toLocalMsat = 0, toRemoteMsat = 0) + val spec = CommitmentSpec(htlcs, feeratePerKw = 5000, toLocal = MilliSatoshi(0), toRemote = MilliSatoshi(0)) val fee = Transactions.commitTxFee(Satoshi(546), spec) assert(fee == Satoshi(5340)) } @@ -125,10 +126,10 @@ class TransactionsSpec extends FunSuite with Logging { // HtlcPenaltyTx // first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx val paymentPreimage = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) val redeemScript = htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry) val pubKeyScript = write(pay2wsh(redeemScript)) - val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val htlcPenaltyTx = makeHtlcPenaltyTx(commitTx, outputsAlreadyUsed = Set.empty, Script.write(redeemScript), localDustLimit, finalPubKeyScript, feeratePerKw) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(htlcPenaltyTx, PlaceHolderSig, localRevocationPriv.publicKey).tx) @@ -140,9 +141,9 @@ class TransactionsSpec extends FunSuite with Logging { // ClaimHtlcSuccessTx // first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx val paymentPreimage = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) val pubKeyScript = write(pay2wsh(htlcOffered(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash)))) - val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val claimHtlcSuccessTx = makeClaimHtlcSuccessTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(claimHtlcSuccessTx, PlaceHolderSig, paymentPreimage).tx) @@ -154,9 +155,9 @@ class TransactionsSpec extends FunSuite with Logging { // ClaimHtlcTimeoutTx // first we create a fake commitTx tx, containing only the output that will be spent by the ClaimHtlcSuccessTx val paymentPreimage = randomBytes32 - val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, Satoshi(20000).amount * 1000, sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) + val htlc = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(20000 * 1000), sha256(paymentPreimage), cltvExpiry = 400144, TestConstants.emptyOnionPacket) val pubKeyScript = write(pay2wsh(htlcReceived(localHtlcPriv.publicKey, remoteHtlcPriv.publicKey, localRevocationPriv.publicKey, ripemd160(htlc.paymentHash), htlc.cltvExpiry))) - val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(Satoshi(htlc.amountMsat / 1000), pubKeyScript) :: Nil, lockTime = 0) + val commitTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(htlc.amountMsat.truncateToSatoshi, pubKeyScript) :: Nil, lockTime = 0) val claimClaimHtlcTimeoutTx = makeClaimHtlcTimeoutTx(commitTx, outputsAlreadyUsed = Set.empty, localDustLimit, remoteHtlcPriv.publicKey, localHtlcPriv.publicKey, localRevocationPriv.publicKey, finalPubKeyScript, htlc, feeratePerKw) // we use dummy signatures to compute the weight val weight = Transaction.weight(addSigs(claimClaimHtlcTimeoutTx, PlaceHolderSig).tx) @@ -183,14 +184,14 @@ class TransactionsSpec extends FunSuite with Logging { // htlc1 and htlc2 are regular IN/OUT htlcs val paymentPreimage1 = randomBytes32 - val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, millibtc2satoshi(MilliBtc(100)).amount * 1000, sha256(paymentPreimage1), 300, TestConstants.emptyOnionPacket) + val htlc1 = UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliBtc(100).toMilliSatoshi, sha256(paymentPreimage1), 300, TestConstants.emptyOnionPacket) val paymentPreimage2 = randomBytes32 - val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, millibtc2satoshi(MilliBtc(200)).amount * 1000, sha256(paymentPreimage2), 300, TestConstants.emptyOnionPacket) + val htlc2 = UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliBtc(200).toMilliSatoshi, sha256(paymentPreimage2), 300, TestConstants.emptyOnionPacket) // htlc3 and htlc4 are dust htlcs IN/OUT htlcs, with an amount large enough to be included in the commit tx, but too small to be claimed at 2nd stage val paymentPreimage3 = randomBytes32 - val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, htlcTimeoutWeight)).amount * 1000, sha256(paymentPreimage3), 300, TestConstants.emptyOnionPacket) + val htlc3 = UpdateAddHtlc(ByteVector32.Zeroes, 2, (localDustLimit + weight2fee(feeratePerKw, htlcTimeoutWeight)).toMilliSatoshi, sha256(paymentPreimage3), 300, TestConstants.emptyOnionPacket) val paymentPreimage4 = randomBytes32 - val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, htlcSuccessWeight)).amount * 1000, sha256(paymentPreimage4), 300, TestConstants.emptyOnionPacket) + val htlc4 = UpdateAddHtlc(ByteVector32.Zeroes, 3, (localDustLimit + weight2fee(feeratePerKw, htlcSuccessWeight)).toMilliSatoshi, sha256(paymentPreimage4), 300, TestConstants.emptyOnionPacket) val spec = CommitmentSpec( htlcs = Set( DirectedHtlc(OUT, htlc1), @@ -199,8 +200,8 @@ class TransactionsSpec extends FunSuite with Logging { DirectedHtlc(IN, htlc4) ), feeratePerKw = feeratePerKw, - toLocalMsat = millibtc2satoshi(MilliBtc(400)).amount * 1000, - toRemoteMsat = millibtc2satoshi(MilliBtc(300)).amount * 1000) + toLocal = millibtc2satoshi(MilliBtc(400)).toMilliSatoshi, + toRemote = millibtc2satoshi(MilliBtc(300)).toMilliSatoshi) val commitTxNumber = 0x404142434445L val commitTx = { @@ -320,7 +321,7 @@ class TransactionsSpec extends FunSuite with Logging { } def htlc(direction: Direction, amount: Satoshi): DirectedHtlc = - DirectedHtlc(direction, UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.amount * 1000, ByteVector32.Zeroes, 144, TestConstants.emptyOnionPacket)) + DirectedHtlc(direction, UpdateAddHtlc(ByteVector32.Zeroes, 0, amount.toMilliSatoshi, ByteVector32.Zeroes, 144, TestConstants.emptyOnionPacket)) test("BOLT 2 fee tests") { @@ -353,7 +354,7 @@ class TransactionsSpec extends FunSuite with Logging { case "received" => htlc(IN, Satoshi(amount.toLong)) } }).toSet - TestSetup(name, dustLimit, CommitmentSpec(htlcs = htlcs, feeratePerKw = feerate_per_kw.toLong, toLocalMsat = to_local_msat.toLong, toRemoteMsat = to_remote_msat.toLong), Satoshi(fee.toLong)) + TestSetup(name, dustLimit, CommitmentSpec(htlcs = htlcs, feeratePerKw = feerate_per_kw.toLong, toLocal = MilliSatoshi(to_local_msat.toLong), toRemote = MilliSatoshi(to_remote_msat.toLong)), Satoshi(fee.toLong)) }) // simple non-reg test making sure we are not missing tests diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala index 81f6e6e0d6..c5a0af0449 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/ChannelCodecsSpec.scala @@ -21,7 +21,7 @@ import java.util.UUID import akka.actor.ActorSystem import fr.acinq.bitcoin.Crypto.PrivateKey import fr.acinq.bitcoin.DeterministicWallet.KeyPath -import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, MilliSatoshi, OutPoint, Satoshi, Transaction} +import fr.acinq.bitcoin.{Block, ByteVector32, Crypto, DeterministicWallet, OutPoint, Satoshi, Transaction} import fr.acinq.eclair._ import fr.acinq.eclair.api.JsonSupport import fr.acinq.eclair.channel.Helpers.Funding @@ -84,10 +84,10 @@ class ChannelCodecsSpec extends FunSuite { val o = LocalParams( nodeId = randomKey.publicKey, channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)), - dustLimitSatoshis = Random.nextInt(Int.MaxValue), + dustLimit = Satoshi(Random.nextInt(Int.MaxValue)), maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)), - channelReserveSatoshis = Random.nextInt(Int.MaxValue), - htlcMinimumMsat = Random.nextInt(Int.MaxValue), + channelReserve = Satoshi(Random.nextInt(Int.MaxValue)), + htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)), toSelfDelay = Random.nextInt(Short.MaxValue), maxAcceptedHtlcs = Random.nextInt(Short.MaxValue), defaultFinalScriptPubKey = randomBytes(10 + Random.nextInt(200)), @@ -102,10 +102,10 @@ class ChannelCodecsSpec extends FunSuite { test("encode/decode remoteparams") { val o = RemoteParams( nodeId = randomKey.publicKey, - dustLimitSatoshis = Random.nextInt(Int.MaxValue), + dustLimit = Satoshi(Random.nextInt(Int.MaxValue)), maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)), - channelReserveSatoshis = Random.nextInt(Int.MaxValue), - htlcMinimumMsat = Random.nextInt(Int.MaxValue), + channelReserve = Satoshi(Random.nextInt(Int.MaxValue)), + htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)), toSelfDelay = Random.nextInt(Short.MaxValue), maxAcceptedHtlcs = Random.nextInt(Short.MaxValue), fundingPubKey = randomKey.publicKey, @@ -129,7 +129,7 @@ class ChannelCodecsSpec extends FunSuite { val add = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) @@ -143,14 +143,14 @@ class ChannelCodecsSpec extends FunSuite { val add1 = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) val add2 = UpdateAddHtlc( channelId = randomBytes32, id = Random.nextInt(Int.MaxValue), - amountMsat = Random.nextInt(Int.MaxValue), + amountMsat = MilliSatoshi(Random.nextInt(Int.MaxValue)), cltvExpiry = Random.nextInt(Int.MaxValue), paymentHash = randomBytes32, onionRoutingPacket = TestConstants.emptyOnionPacket) @@ -161,8 +161,8 @@ class ChannelCodecsSpec extends FunSuite { val o = CommitmentSpec( htlcs = Set(htlc1, htlc2), feeratePerKw = Random.nextInt(Int.MaxValue), - toLocalMsat = Random.nextInt(Int.MaxValue), - toRemoteMsat = Random.nextInt(Int.MaxValue) + toLocal = MilliSatoshi(Random.nextInt(Int.MaxValue)), + toRemote = MilliSatoshi(Random.nextInt(Int.MaxValue)) ) val encoded = commitmentSpecCodec.encode(o).require val decoded = commitmentSpecCodec.decode(encoded).require @@ -173,17 +173,17 @@ class ChannelCodecsSpec extends FunSuite { val id = UUID.randomUUID() assert(originCodec.decodeValue(originCodec.encode(Local(id, Some(ActorSystem("system").deadLetters))).require).require === Local(id, None)) // TODO: add backward compatibility check - val relayed = Relayed(randomBytes32, 4324, 12000000L, 11000000L) + val relayed = Relayed(randomBytes32, 4324, MilliSatoshi(12000000L), MilliSatoshi(11000000L)) assert(originCodec.decodeValue(originCodec.encode(relayed).require).require === relayed) } test("encode/decode map of origins") { val map = Map( 1L -> Local(UUID.randomUUID(), None), - 42L -> Relayed(randomBytes32, 4324, 12000000L, 11000000L), - 130L -> Relayed(randomBytes32, -45, 13000000L, 12000000L), - 1000L -> Relayed(randomBytes32, 10, 14000000L, 13000000L), - -32L -> Relayed(randomBytes32, 54, 15000000L, 14000000L), + 42L -> Relayed(randomBytes32, 4324, MilliSatoshi(12000000L), MilliSatoshi(11000000L)), + 130L -> Relayed(randomBytes32, -45, MilliSatoshi(13000000L), MilliSatoshi(12000000L)), + 1000L -> Relayed(randomBytes32, 10, MilliSatoshi(14000000L), MilliSatoshi(13000000L)), + -32L -> Relayed(randomBytes32, 54, MilliSatoshi(15000000L), MilliSatoshi(14000000L)), -4L -> Local(UUID.randomUUID(), None)) assert(originsMapCodec.decodeValue(originsMapCodec.encode(map).require).require === map) } @@ -313,8 +313,24 @@ class ChannelCodecsSpec extends FunSuite { // and we decode with the new codec val newnormal = stateDataCodec.decode(newbin.bits).require.value // finally we check that the actual data is the same as before (we just remove the new json field) - val oldjson = Serialization.write(oldnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "") - val newjson = Serialization.write(newnormal)(JsonSupport.formats).replace(""","unknownFields":""""", "").replace(""""channelVersion":"00000000000000000000000000000000",""", "") + val oldjson = Serialization.write(oldnormal)(JsonSupport.formats) + .replace(""","unknownFields":""""", "") + .replace(""""channelVersion":"00000000000000000000000000000000",""", "") + .replace(""""dustLimit"""", """"dustLimitSatoshis"""") + .replace(""""channelReserve"""", """"channelReserveSatoshis"""") + .replace(""""htlcMinimum"""", """"htlcMinimumMsat"""") + .replace(""""toLocal"""", """"toLocalMsat"""") + .replace(""""toRemote"""", """"toRemoteMsat"""") + + val newjson = Serialization.write(newnormal)(JsonSupport.formats) + .replace(""","unknownFields":""""", "") + .replace(""""channelVersion":"00000000000000000000000000000000",""", "") + .replace(""""dustLimit"""", """"dustLimitSatoshis"""") + .replace(""""channelReserve"""", """"channelReserveSatoshis"""") + .replace(""""htlcMinimum"""", """"htlcMinimumMsat"""") + .replace(""""toLocal"""", """"toLocalMsat"""") + .replace(""""toRemote"""", """"toRemoteMsat"""") + assert(oldjson === refjson) assert(newjson === refjson) } @@ -328,10 +344,10 @@ object ChannelCodecsSpec { val localParams = LocalParams( keyManager.nodeId, channelKeyPath = DeterministicWallet.KeyPath(Seq(42L)), - dustLimitSatoshis = Satoshi(546).toLong, + dustLimit = Satoshi(546), maxHtlcValueInFlightMsat = UInt64(50000000), - channelReserveSatoshis = 10000, - htlcMinimumMsat = 10000, + channelReserve = Satoshi(10000), + htlcMinimum = MilliSatoshi(10000), toSelfDelay = 144, maxAcceptedHtlcs = 50, defaultFinalScriptPubKey = ByteVector.empty, @@ -341,10 +357,10 @@ object ChannelCodecsSpec { val remoteParams = RemoteParams( nodeId = randomKey.publicKey, - dustLimitSatoshis = Satoshi(546).toLong, + dustLimit = Satoshi(546), maxHtlcValueInFlightMsat = UInt64(5000000), - channelReserveSatoshis = 10000, - htlcMinimumMsat = 5000, + channelReserve = Satoshi(10000), + htlcMinimum = MilliSatoshi(5000), toSelfDelay = 144, maxAcceptedHtlcs = 50, fundingPubKey = PrivateKey(ByteVector32(ByteVector.fill(32)(1)) :+ 1.toByte).publicKey, @@ -364,27 +380,27 @@ object ChannelCodecsSpec { ) val htlcs = Seq( - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000).amount, Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(2000000).amount, Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), - DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 31, MilliSatoshi(3000000).amount, Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), - DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(4000000).amount, Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 0, MilliSatoshi(1000000), Crypto.sha256(paymentPreimages(0)), 500, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 1, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(1)), 501, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 30, MilliSatoshi(2000000), Crypto.sha256(paymentPreimages(2)), 502, TestConstants.emptyOnionPacket)), + DirectedHtlc(OUT, UpdateAddHtlc(ByteVector32.Zeroes, 31, MilliSatoshi(3000000), Crypto.sha256(paymentPreimages(3)), 503, TestConstants.emptyOnionPacket)), + DirectedHtlc(IN, UpdateAddHtlc(ByteVector32.Zeroes, 2, MilliSatoshi(4000000), Crypto.sha256(paymentPreimages(4)), 504, TestConstants.emptyOnionPacket)) ) val fundingTx = Transaction.read("0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000") val fundingAmount = fundingTx.txOut(0).amount val commitmentInput = Funding.makeFundingInputInfo(fundingTx.hash, 0, fundingAmount, keyManager.fundingPublicKey(localParams.channelKeyPath).publicKey, remoteParams.fundingPubKey) - val localCommit = LocalCommit(0, CommitmentSpec(htlcs.toSet, 1500, 50000000, 70000000), PublishableTxs(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), Nil)) - val remoteCommit = RemoteCommit(0, CommitmentSpec(htlcs.map(htlc => htlc.copy(direction = htlc.direction.opposite)).toSet, 1500, 50000, 700000), ByteVector32(hex"0303030303030303030303030303030303030303030303030303030303030303"), PrivateKey(ByteVector.fill(32)(4)).publicKey) + val localCommit = LocalCommit(0, CommitmentSpec(htlcs.toSet, 1500, MilliSatoshi(50000000), MilliSatoshi(70000000)), PublishableTxs(CommitTx(commitmentInput, Transaction(2, Nil, Nil, 0)), Nil)) + val remoteCommit = RemoteCommit(0, CommitmentSpec(htlcs.map(htlc => htlc.copy(direction = htlc.direction.opposite)).toSet, 1500, MilliSatoshi(50000), MilliSatoshi(700000)), ByteVector32(hex"0303030303030303030303030303030303030303030303030303030303030303"), PrivateKey(ByteVector.fill(32)(4)).publicKey) val commitments = Commitments(ChannelVersion.STANDARD, localParams, remoteParams, channelFlags = 0x01.toByte, localCommit, remoteCommit, LocalChanges(Nil, Nil, Nil), RemoteChanges(Nil, Nil, Nil), localNextHtlcId = 32L, remoteNextHtlcId = 4L, - originChannels = Map(42L -> Local(UUID.randomUUID, None), 15000L -> Relayed(ByteVector32(ByteVector.fill(32)(42)), 43, 11000000L, 10000000L)), + originChannels = Map(42L -> Local(UUID.randomUUID, None), 15000L -> Relayed(ByteVector32(ByteVector.fill(32)(42)), 43, MilliSatoshi(11000000L), MilliSatoshi(10000000L))), remoteNextCommitInfo = Right(randomKey.publicKey), commitInput = commitmentInput, remotePerCommitmentSecrets = ShaChain.init, channelId = ByteVector32.Zeroes) - val channelUpdate = Announcements.makeChannelUpdate(ByteVector32(ByteVector.fill(32)(1)), randomKey, randomKey.publicKey, ShortChannelId(142553), 42, 15, 575, 53, Channel.MAX_FUNDING_SATOSHIS * 1000L) + val channelUpdate = Announcements.makeChannelUpdate(ByteVector32(ByteVector.fill(32)(1)), randomKey, randomKey.publicKey, ShortChannelId(142553), 42, MilliSatoshi(15), MilliSatoshi(575), 53, Channel.MAX_FUNDING.toMilliSatoshi) val normal = DATA_NORMAL(commitments, ShortChannelId(42), true, None, channelUpdate, None, None) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala index 3b3ecd7a61..7f1f2ef22f 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/FailureMessageCodecsSpec.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64} import fr.acinq.eclair.crypto.Hmac256 -import fr.acinq.eclair.{ShortChannelId, randomBytes32, randomBytes64} +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId, randomBytes32, randomBytes64} import fr.acinq.eclair.wire.FailureMessageCodecs._ import org.scalatest.FunSuite import scodec.bits._ @@ -36,8 +36,8 @@ class FailureMessageCodecsSpec extends FunSuite { cltvExpiryDelta = 100, messageFlags = 0, channelFlags = 1, - htlcMinimumMsat = 1000, - feeBaseMsat = 12, + htlcMinimumMsat = MilliSatoshi(1000), + feeBaseMsat = MilliSatoshi(12), feeProportionalMillionths = 76, htlcMaximumMsat = None) @@ -46,8 +46,8 @@ class FailureMessageCodecsSpec extends FunSuite { InvalidRealm :: TemporaryNodeFailure :: PermanentNodeFailure :: RequiredNodeFeatureMissing :: InvalidOnionVersion(randomBytes32) :: InvalidOnionHmac(randomBytes32) :: InvalidOnionKey(randomBytes32) :: InvalidOnionPayload(randomBytes32) :: TemporaryChannelFailure(channelUpdate) :: PermanentChannelFailure :: RequiredChannelFeatureMissing :: UnknownNextPeer :: - AmountBelowMinimum(123456, channelUpdate) :: FeeInsufficient(546463, channelUpdate) :: IncorrectCltvExpiry(1211, channelUpdate) :: ExpiryTooSoon(channelUpdate) :: - IncorrectOrUnknownPaymentDetails(123456L) :: IncorrectPaymentAmount :: FinalExpiryTooSoon :: FinalIncorrectCltvExpiry(1234) :: ChannelDisabled(0, 1, channelUpdate) :: ExpiryTooFar :: Nil + AmountBelowMinimum(MilliSatoshi(123456), channelUpdate) :: FeeInsufficient(MilliSatoshi(546463), channelUpdate) :: IncorrectCltvExpiry(1211, channelUpdate) :: ExpiryTooSoon(channelUpdate) :: + IncorrectOrUnknownPaymentDetails(MilliSatoshi(123456L)) :: IncorrectPaymentAmount :: FinalExpiryTooSoon :: FinalIncorrectCltvExpiry(1234) :: ChannelDisabled(0, 1, channelUpdate) :: ExpiryTooFar :: Nil msgs.foreach { msg => { @@ -75,7 +75,7 @@ class FailureMessageCodecsSpec extends FunSuite { val codec = failureOnionCodec(Hmac256(ByteVector32.Zeroes)) val testCases = Map( InvalidOnionKey(ByteVector32(hex"2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a")) -> hex"41a824e2d630111669fa3e52b600a518f369691909b4e89205dc624ee17ed2c1 0022 c006 2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a 00de 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - IncorrectOrUnknownPaymentDetails(42) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + IncorrectOrUnknownPaymentDetails(MilliSatoshi(42)) -> hex"ba6e122b2941619e2106e8437bf525356ffc8439ac3b2245f68546e298a08cc6 000a 400f 000000000000002a 00f6 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ) for ((expected, bin) <- testCases) { @@ -112,7 +112,7 @@ class FailureMessageCodecsSpec extends FunSuite { test("support encoding of channel_update with/without type in failure messages") { val tmp_channel_failure_notype = hex"10070080cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" val tmp_channel_failure_withtype = hex"100700820102cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f45782196fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d619000000000008260500041300005b91b52f0003000e00000000000003e80000000100000001" - val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759, 0, 3, 14, 1000, 1, 1, None)) + val ref = TemporaryChannelFailure(ChannelUpdate(ByteVector64(hex"cc3e80149073ed487c76e48e9622bf980f78267b8a34a3f61921f2d8fce6063b08e74f34a073a13f2097337e4915bb4c001f3b5c4d81e9524ed575e1f4578219"), Block.LivenetGenesisBlock.hash, ShortChannelId(0x826050004130000L), 1536275759, 0, 3, 14, MilliSatoshi(1000), MilliSatoshi(1), 1, None)) val u = failureMessageCodec.decode(tmp_channel_failure_notype.toBitVector).require.value assert(u === ref) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala index ff2c26b5a2..90edaabaee 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/LightningMessageCodecsSpec.scala @@ -19,7 +19,7 @@ package fr.acinq.eclair.wire import java.net.{Inet4Address, InetAddress} import fr.acinq.bitcoin.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64} +import fr.acinq.bitcoin.{Block, ByteVector32, ByteVector64, Satoshi} import fr.acinq.eclair._ import fr.acinq.eclair.router.Announcements import fr.acinq.eclair.wire.LightningMessageCodecs._ @@ -52,15 +52,15 @@ class LightningMessageCodecsSpec extends FunSuite { } test("encode/decode all channel messages") { - val open = OpenChannel(randomBytes32, randomBytes32, 3, 4, 5, UInt64(6), 7, 8, 9, 10, 11, publicKey(1), point(2), point(3), point(4), point(5), point(6), 0.toByte) - val accept = AcceptChannel(randomBytes32, 3, UInt64(4), 5, 6, 7, 8, 9, publicKey(1), point(2), point(3), point(4), point(5), point(6)) + val open = OpenChannel(randomBytes32, randomBytes32, Satoshi(3), MilliSatoshi(4), Satoshi(5), UInt64(6), Satoshi(7), MilliSatoshi(8), 9, 10, 11, publicKey(1), point(2), point(3), point(4), point(5), point(6), 0.toByte) + val accept = AcceptChannel(randomBytes32, Satoshi(3), UInt64(4), Satoshi(5), MilliSatoshi(6), 7, 8, 9, publicKey(1), point(2), point(3), point(4), point(5), point(6)) val funding_created = FundingCreated(randomBytes32, bin32(0), 3, randomBytes64) val funding_signed = FundingSigned(randomBytes32, randomBytes64) val funding_locked = FundingLocked(randomBytes32, point(2)) val update_fee = UpdateFee(randomBytes32, 2) val shutdown = Shutdown(randomBytes32, bin(47, 0)) - val closing_signed = ClosingSigned(randomBytes32, 2, randomBytes64) - val update_add_htlc = UpdateAddHtlc(randomBytes32, 2, 3, bin32(0), 4, TestConstants.emptyOnionPacket) + val closing_signed = ClosingSigned(randomBytes32, Satoshi(2), randomBytes64) + val update_add_htlc = UpdateAddHtlc(randomBytes32, 2, MilliSatoshi(3), bin32(0), 4, TestConstants.emptyOnionPacket) val update_fulfill_htlc = UpdateFulfillHtlc(randomBytes32, 2, bin32(0)) val update_fail_htlc = UpdateFailHtlc(randomBytes32, 2, bin(154, 0)) val update_fail_malformed_htlc = UpdateFailMalformedHtlc(randomBytes32, 2, randomBytes32, 1111) @@ -68,7 +68,7 @@ class LightningMessageCodecsSpec extends FunSuite { val revoke_and_ack = RevokeAndAck(randomBytes32, scalar(0), point(1)) val channel_announcement = ChannelAnnouncement(randomBytes64, randomBytes64, randomBytes64, randomBytes64, bin(7, 9), Block.RegtestGenesisBlock.hash, ShortChannelId(1), randomKey.publicKey, randomKey.publicKey, randomKey.publicKey, randomKey.publicKey) val node_announcement = NodeAnnouncement(randomBytes64, bin(1, 2), 1, randomKey.publicKey, Color(100.toByte, 200.toByte, 300.toByte), "node-alias", IPv4(InetAddress.getByAddress(Array[Byte](192.toByte, 168.toByte, 1.toByte, 42.toByte)).asInstanceOf[Inet4Address], 42000) :: Nil) - val channel_update = ChannelUpdate(randomBytes64, Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2, 42, 0, 3, 4, 5, 6, None) + val channel_update = ChannelUpdate(randomBytes64, Block.RegtestGenesisBlock.hash, ShortChannelId(1), 2, 42, 0, 3, MilliSatoshi(4), MilliSatoshi(5), 6, None) val announcement_signatures = AnnouncementSignatures(randomBytes32, ShortChannelId(42), randomBytes64, randomBytes64) val gossip_timestamp_filter = GossipTimestampFilter(Block.RegtestGenesisBlock.blockId, 100000, 1500) val query_short_channel_id = QueryShortChannelIds(Block.RegtestGenesisBlock.blockId, randomBytes(7515)) @@ -96,7 +96,7 @@ class LightningMessageCodecsSpec extends FunSuite { // this was generated by c-lightning val bin = hex"010258fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf1792306226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0005a100000200005bc75919010100060000000000000001000000010000000a000000003a699d00" val update = lightningMessageCodec.decode(bin.bits).require.value.asInstanceOf[ChannelUpdate] - assert(update === ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129, 1, 1, 6, 1, 1, 10, Some(980000000L))) + assert(update === ChannelUpdate(ByteVector64(hex"58fff7d0e987e2cdd560e3bb5a046b4efe7b26c969c2f51da1dceec7bcb8ae1b634790503d5290c1a6c51d681cf8f4211d27ed33a257dcc1102862571bf17923"), ByteVector32(hex"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f"), ShortChannelId(0x5a10000020000L), 1539791129, 1, 1, 6, MilliSatoshi(1), MilliSatoshi(1), 10, Some(MilliSatoshi(980000000L)))) val nodeId = PublicKey(hex"03370c9bac836e557eb4f017fe8f9cc047f44db39c1c4e410ff0f7be142b817ae4") assert(Announcements.checkSig(update, nodeId)) val bin2 = ByteVector(lightningMessageCodec.encode(update).require.toByteArray) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala index 763a9ce42a..bf236ed7b7 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/OnionCodecsSpec.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.wire import fr.acinq.bitcoin.ByteVector32 -import fr.acinq.eclair.ShortChannelId +import fr.acinq.eclair.{MilliSatoshi, ShortChannelId} import fr.acinq.eclair.wire.OnionCodecs._ import org.scalatest.FunSuite import scodec.bits.HexStringSyntax @@ -40,7 +40,7 @@ class OnionCodecsSpec extends FunSuite { } test("encode/decode per-hop payload") { - val payload = PerHopPayload(shortChannelId = ShortChannelId(42), amtToForward = 142000, outgoingCltvValue = 500000) + val payload = PerHopPayload(shortChannelId = ShortChannelId(42), amtToForward = MilliSatoshi(142000), outgoingCltvValue = 500000) val bin = perHopPayloadCodec.encode(payload).require assert(bin.toByteVector.size === 33) val payload1 = perHopPayloadCodec.decode(bin).require.value diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala index ed96e755f0..1f955e9d4b 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/GUIUpdater.scala @@ -21,7 +21,7 @@ import java.time.LocalDateTime import akka.actor.{Actor, ActorLogging, ActorRef, Terminated} import fr.acinq.bitcoin.Crypto.PublicKey import fr.acinq.bitcoin._ -import fr.acinq.eclair.CoinUtils +import fr.acinq.eclair.{CoinUtils, MilliSatoshi} import fr.acinq.eclair.blockchain.bitcoind.zmq.ZMQActor.{ZMQConnected, ZMQDisconnected} import fr.acinq.eclair.blockchain.electrum.ElectrumClient.{ElectrumDisconnected, ElectrumReady} import fr.acinq.eclair.channel._ @@ -187,11 +187,11 @@ class GUIUpdater(mainController: MainController) extends Actor with ActorLogging val c = mainController.networkChannelsMap.get(channelUpdate.shortChannelId) if (Announcements.isNode1(channelUpdate.channelFlags)) { c.isNode1Enabled = Some(Announcements.isEnabled(channelUpdate.channelFlags)) - c.feeBaseMsatNode1_opt = Some(channelUpdate.feeBaseMsat) + c.feeBaseMsatNode1_opt = Some(channelUpdate.feeBaseMsat.toLong) c.feeProportionalMillionthsNode1_opt = Some(channelUpdate.feeProportionalMillionths) } else { c.isNode2Enabled = Some(Announcements.isEnabled(channelUpdate.channelFlags)) - c.feeBaseMsatNode2_opt = Some(channelUpdate.feeBaseMsat) + c.feeBaseMsatNode2_opt = Some(channelUpdate.feeBaseMsat.toLong) c.feeProportionalMillionthsNode2_opt = Some(channelUpdate.feeProportionalMillionths) } mainController.networkChannelsMap.put(channelUpdate.shortChannelId, c) @@ -200,7 +200,7 @@ class GUIUpdater(mainController: MainController) extends Actor with ActorLogging } case p: PaymentSucceeded => - val message = CoinUtils.formatAmountInUnit(MilliSatoshi(p.amountMsat), FxApp.getUnit, withUnit = true) + val message = CoinUtils.formatAmountInUnit(p.amount, FxApp.getUnit, withUnit = true) mainController.handlers.notification("Payment Sent", message, NOTIFICATION_SUCCESS) case p: PaymentFailed => diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala index 80e286051b..54765f834a 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/Handlers.scala @@ -20,7 +20,7 @@ import java.util.UUID import akka.pattern.{AskTimeoutException, ask} import akka.util.Timeout -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair._ import fr.acinq.eclair.gui.controllers._ import fr.acinq.eclair.io.{NodeURI, Peer} @@ -88,8 +88,8 @@ class Handlers(fKit: Future[Kit])(implicit ec: ExecutionContext = ExecutionConte (for { kit <- fKit sendPayment = req.minFinalCltvExpiry match { - case None => SendPayment(amountMsat, req.paymentHash, req.nodeId, req.routingInfo, maxAttempts = kit.nodeParams.maxPaymentAttempts) - case Some(minFinalCltvExpiry) => SendPayment(amountMsat, req.paymentHash, req.nodeId, req.routingInfo, finalCltvExpiry = minFinalCltvExpiry, maxAttempts = kit.nodeParams.maxPaymentAttempts) + case None => SendPayment(MilliSatoshi(amountMsat), req.paymentHash, req.nodeId, req.routingInfo, maxAttempts = kit.nodeParams.maxPaymentAttempts) + case Some(minFinalCltvExpiry) => SendPayment(MilliSatoshi(amountMsat), req.paymentHash, req.nodeId, req.routingInfo, finalCltvExpiry = minFinalCltvExpiry, maxAttempts = kit.nodeParams.maxPaymentAttempts) } res <- (kit.paymentInitiator ? sendPayment).mapTo[UUID] } yield res).recover { diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala index 13e9be2d83..c4691f0148 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ChannelPaneController.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.gui.controllers import akka.actor.ActorRef import com.google.common.base.Strings -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.CoinUtils import fr.acinq.eclair.channel.{CMD_CLOSE, CMD_FORCECLOSE, Commitments} import fr.acinq.eclair.gui.FxApp @@ -129,12 +129,12 @@ class ChannelPaneController(val channelRef: ActorRef, val peerNodeId: String) ex } def updateBalance(commitments: Commitments) { - balance = MilliSatoshi(commitments.localCommit.spec.toLocalMsat) - capacity = MilliSatoshi(commitments.localCommit.spec.totalFunds) + balance = commitments.localCommit.spec.toLocal + capacity = commitments.localCommit.spec.totalFunds } def refreshBalance(): Unit = { - amountUs.setText(s"${CoinUtils.formatAmountInUnit(balance, FxApp.getUnit)} / ${CoinUtils.formatAmountInUnit(capacity, FxApp.getUnit, withUnit = true)}") + amountUs.setText(s"${CoinUtils.formatAmountInUnit(balance, FxApp.getUnit, false)} / ${CoinUtils.formatAmountInUnit(capacity, FxApp.getUnit, withUnit = true)}") balanceBar.setProgress(balance.amount.toDouble / capacity.amount) } diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala index 1a8707be1c..60626148c8 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/MainController.scala @@ -23,14 +23,14 @@ import java.util.Locale import com.google.common.net.HostAndPort import fr.acinq.bitcoin.Crypto.PublicKey -import fr.acinq.bitcoin.{MilliSatoshi, Satoshi} +import fr.acinq.bitcoin.Satoshi import fr.acinq.eclair.NodeParams.{BITCOIND, ELECTRUM} import fr.acinq.eclair.gui.stages._ import fr.acinq.eclair.gui.utils.{ContextMenuUtils, CopyAction, IndexedObservableList} import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.payment.{PaymentEvent, PaymentReceived, PaymentRelayed, PaymentSent} import fr.acinq.eclair.wire.{ChannelAnnouncement, NodeAnnouncement} -import fr.acinq.eclair.{CoinUtils, Setup, ShortChannelId} +import fr.acinq.eclair.{CoinUtils, MilliSatoshi, Setup, ShortChannelId} import grizzled.slf4j.Logging import javafx.animation.{FadeTransition, ParallelTransition, SequentialTransition, TranslateTransition} import javafx.application.{HostServices, Platform} diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala index 9f6560553e..361ba9337a 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/OpenChannelController.scala @@ -19,12 +19,13 @@ package fr.acinq.eclair.gui.controllers import java.lang.Boolean import com.google.common.base.Strings -import fr.acinq.bitcoin.{Satoshi, _} +import fr.acinq.bitcoin.Satoshi +import fr.acinq.eclair._ import fr.acinq.eclair.channel.{Channel, ChannelFlags} import fr.acinq.eclair.gui.utils.Constants import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.io.{NodeURI, Peer} -import fr.acinq.eclair.{CoinUtils, Globals} +import fr.acinq.eclair.{CoinUtils, Globals, MilliSatoshi} import grizzled.slf4j.Logging import javafx.beans.value.{ChangeListener, ObservableValue} import javafx.event.ActionEvent @@ -79,8 +80,8 @@ class OpenChannelController(val handlers: Handlers, val stage: Stage) extends Lo fundingSatError.setText("Capacity must be greater than 0") case Success(capacitySat) if capacitySat.amount < 50000 => fundingSatError.setText("Capacity is low and the channel may not be able to open") - case Success(capacitySat) if capacitySat.amount >= Channel.MAX_FUNDING_SATOSHIS => - fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Satoshi(Channel.MAX_FUNDING_SATOSHIS), FxApp.getUnit, withUnit = true)}") + case Success(capacitySat) if capacitySat >= Channel.MAX_FUNDING => + fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Channel.MAX_FUNDING, FxApp.getUnit, withUnit = true)}") case Success(_) => fundingSatError.setText("") case _ => fundingSatError.setText("Capacity is not valid") } @@ -100,9 +101,9 @@ class OpenChannelController(val handlers: Handlers, val stage: Stage) extends Lo Try(if (Strings.isNullOrEmpty(feerateField.getText())) None else Some(feerateField.getText().toLong))) match { case (Success(capacitySat), _, _) if capacitySat.amount <= 0 => fundingSatError.setText("Capacity must be greater than 0") - case (Success(capacitySat), _, _) if capacitySat.amount >= Channel.MAX_FUNDING_SATOSHIS => - fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Satoshi(Channel.MAX_FUNDING_SATOSHIS), FxApp.getUnit, withUnit = true)}") - case (Success(capacitySat), Success(pushMsat), _) if pushMsat > satoshi2millisatoshi(capacitySat).amount => + case (Success(capacitySat), _, _) if capacitySat >= Channel.MAX_FUNDING => + fundingSatError.setText(s"Capacity must be less than ${CoinUtils.formatAmountInUnit(Channel.MAX_FUNDING, FxApp.getUnit, withUnit = true)}") + case (Success(capacitySat), Success(pushMsat), _) if pushMsat > capacitySat.toMilliSatoshi.toLong => pushMsatError.setText("Push must be less or equal to capacity") case (Success(_), Success(pushMsat), _) if pushMsat < 0 => pushMsatError.setText("Push must be positive") diff --git a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala index b0ad2fe916..2abe281afb 100644 --- a/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala +++ b/eclair-node-gui/src/main/scala/fr/acinq/eclair/gui/controllers/ReceivePaymentController.scala @@ -24,7 +24,7 @@ import javafx.scene.image.{ImageView, WritableImage} import javafx.scene.layout.GridPane import javafx.stage.Stage -import fr.acinq.bitcoin.MilliSatoshi +import fr.acinq.eclair.MilliSatoshi import fr.acinq.eclair.CoinUtils import fr.acinq.eclair.gui.{FxApp, Handlers} import fr.acinq.eclair.gui.utils._ diff --git a/pom.xml b/pom.xml index ed1e45b218..e5aa8bbc1d 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 2.4.20 10.0.11 1.3.9 - 0.13 + 0.14 24.0-android