Skip to content

Commit

Permalink
Add an ability to set custom payout and change addresses (#4101)
Browse files Browse the repository at this point in the history
* Add an ability to set custom payout and change addresses

* config changes

* formatting

* respond to the comments
  • Loading branch information
rorp committed Feb 18, 2022
1 parent 5b1b1ee commit 5777ec1
Show file tree
Hide file tree
Showing 19 changed files with 600 additions and 301 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -952,18 +952,26 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
)

"create a dlc offer" in {
(mockWalletApi
.createDLCOffer(_: ContractInfoTLV,
_: Satoshis,
_: Option[SatoshisPerVirtualByte],
_: UInt32,
_: UInt32))
(
mockWalletApi
.createDLCOffer(
_: ContractInfoTLV,
_: Satoshis,
_: Option[SatoshisPerVirtualByte],
_: UInt32,
_: UInt32,
_: Option[BitcoinAddress],
_: Option[BitcoinAddress]
)
)
.expects(
contractInfoTLV,
Satoshis(2500),
Some(SatoshisPerVirtualByte(Satoshis.one)),
UInt32(contractMaturity),
UInt32(contractTimeout)
UInt32(contractTimeout),
None,
None
)
.returning(Future.successful(offer))

Expand Down Expand Up @@ -1021,8 +1029,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {

"accept a dlc offer" in {
(mockWalletApi
.acceptDLCOffer(_: DLCOfferTLV))
.expects(offer.toTLV)
.acceptDLCOffer(_: DLCOfferTLV,
_: Option[BitcoinAddress],
_: Option[BitcoinAddress]))
.expects(offer.toTLV, None, None)
.returning(Future.successful(accept))

val route = walletRoutes.handleCommand(
Expand Down
137 changes: 107 additions & 30 deletions app/server/src/main/scala/org/bitcoins/server/ServerJsonModels.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package org.bitcoins.server

import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.LockUnspentOutputParameter
import org.bitcoins.commons.jsonmodels.cli.ContractDescriptorParser
import org.bitcoins.commons.serializers.{JsonReaders}
import org.bitcoins.commons.serializers.JsonReaders
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
Expand Down Expand Up @@ -655,26 +655,67 @@ case class CreateDLCOffer(
collateral: Satoshis,
feeRateOpt: Option[SatoshisPerVirtualByte],
locktime: UInt32,
refundLocktime: UInt32)
refundLocktime: UInt32,
externalPayoutAddressOpt: Option[BitcoinAddress],
externalChangeAddressOpt: Option[BitcoinAddress])

object CreateDLCOffer extends ServerJsonModels {

def fromJsArr(jsArr: ujson.Arr): Try[CreateDLCOffer] = {

def parseParameters(
contractInfoJs: Value,
collateralJs: Value,
feeRateOptJs: Value,
locktimeJs: Value,
refundLTJs: Value,
payoutAddressJs: Value,
changeAddressJs: Value) = Try {
val contractInfoTLV = jsToContractInfoTLV(contractInfoJs)
val collateral = jsToSatoshis(collateralJs)
val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs)
val locktime = jsToUInt32(locktimeJs)
val refundLT = jsToUInt32(refundLTJs)
val payoutAddressJsOpt = nullToOpt(payoutAddressJs)
val payoutAddressOpt =
payoutAddressJsOpt.map(js => jsToBitcoinAddress(js))
val changeAddressJsOpt = nullToOpt(changeAddressJs)
val changeAddressOpt =
changeAddressJsOpt.map(js => jsToBitcoinAddress(js))
CreateDLCOffer(contractInfoTLV,
collateral,
feeRate,
locktime,
refundLT,
payoutAddressOpt,
changeAddressOpt)
}

jsArr.arr.toList match {
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil =>
Try {
val contractInfoTLV = jsToContractInfoTLV(contractInfoJs)
val collateral = jsToSatoshis(collateralJs)
val feeRate = jsToSatoshisPerVirtualByteOpt(feeRateOptJs)
val locktime = jsToUInt32(locktimeJs)
val refundLT = jsToUInt32(refundLTJs)
CreateDLCOffer(contractInfoTLV,
collateral,
feeRate,
locktime,
refundLT)
}
parseParameters(contractInfoJs,
collateralJs,
feeRateOptJs,
locktimeJs,
refundLTJs,
Null,
Null)
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: Nil =>
parseParameters(contractInfoJs,
collateralJs,
feeRateOptJs,
locktimeJs,
refundLTJs,
payoutAddressJs,
Null)
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: payoutAddressJs :: changeAddressJs :: Nil =>
parseParameters(contractInfoJs,
collateralJs,
feeRateOptJs,
locktimeJs,
refundLTJs,
payoutAddressJs,
changeAddressJs)
case other =>
Failure(
new IllegalArgumentException(
Expand Down Expand Up @@ -839,17 +880,35 @@ object DecodeAttestations extends ServerJsonModels {
}
}

case class AcceptDLCOffer(offer: LnMessage[DLCOfferTLV])
case class AcceptDLCOffer(
offer: LnMessage[DLCOfferTLV],
externalPayoutAddressOpt: Option[BitcoinAddress],
externalChangeAddressOpt: Option[BitcoinAddress])

object AcceptDLCOffer extends ServerJsonModels {

def fromJsArr(jsArr: ujson.Arr): Try[AcceptDLCOffer] = {
def parseParameters(
offerJs: Value,
payoutAddressJs: Value,
changeAddressJs: Value) = Try {
val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str)
val payoutAddressJsOpt = nullToOpt(payoutAddressJs)
val payoutAddressOpt =
payoutAddressJsOpt.map(js => jsToBitcoinAddress(js))
val changeAddressJsOpt = nullToOpt(changeAddressJs)
val changeAddressOpt =
changeAddressJsOpt.map(js => jsToBitcoinAddress(js))
AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt)
}

jsArr.arr.toList match {
case offerJs :: Nil =>
Try {
val offer = LnMessageFactory(DLCOfferTLV).fromHex(offerJs.str)
AcceptDLCOffer(offer)
}
parseParameters(offerJs, Null, Null)
case offerJs :: payoutAddressJs :: Nil =>
parseParameters(offerJs, payoutAddressJs, Null)
case offerJs :: payoutAddressJs :: changeAddressJs :: Nil =>
parseParameters(offerJs, payoutAddressJs, changeAddressJs)
case Nil =>
Failure(new IllegalArgumentException("Missing offer argument"))

Expand Down Expand Up @@ -930,24 +989,42 @@ object AddDLCSigs extends ServerJsonModels {
}
}

case class DLCDataFromFile(path: Path, destinationOpt: Option[Path])
case class DLCDataFromFile(
path: Path,
destinationOpt: Option[Path],
externalPayoutAddressOpt: Option[BitcoinAddress],
externalChangeAddressOpt: Option[BitcoinAddress])

object DLCDataFromFile extends ServerJsonModels {

def fromJsArr(jsArr: ujson.Arr): Try[DLCDataFromFile] = {
def parseParameters(
pathJs: Value,
destJs: Value,
payoutAddressJs: Value,
changeAddressJs: Value) = Try {
val path = new File(pathJs.str).toPath
val destJsOpt = nullToOpt(destJs)
val destOpt = destJsOpt.map(js => new File(js.str).toPath)
val payoutAddressJsOpt = nullToOpt(payoutAddressJs)
val payoutAddressOpt =
payoutAddressJsOpt.map(js => jsToBitcoinAddress(js))
val changeAddressJsOpt = nullToOpt(changeAddressJs)
val changeAddressOpt =
changeAddressJsOpt.map(js => jsToBitcoinAddress(js))

DLCDataFromFile(path, destOpt, payoutAddressOpt, changeAddressOpt)
}

jsArr.arr.toList match {
case pathJs :: Nil =>
Try {
val path = new File(pathJs.str).toPath
DLCDataFromFile(path, None)
}
parseParameters(pathJs, Null, Null, Null)
case pathJs :: destJs :: Nil =>
Try {
val path = new File(pathJs.str).toPath
val destJsOpt = nullToOpt(destJs)
val destOpt = destJsOpt.map(js => new File(js.str).toPath)
DLCDataFromFile(path, destOpt)
}
parseParameters(pathJs, destJs, Null, Null)
case pathJs :: destJs :: payoutAddressJs :: Nil =>
parseParameters(pathJs, destJs, payoutAddressJs, Null)
case pathJs :: destJs :: payoutAddressJs :: changeAddressJs :: Nil =>
parseParameters(pathJs, destJs, payoutAddressJs, changeAddressJs)
case Nil =>
Failure(new IllegalArgumentException("Missing path argument"))
case other =>
Expand Down
33 changes: 22 additions & 11 deletions app/server/src/main/scala/org/bitcoins/server/WalletRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import akka.http.scaladsl.server._
import akka.stream.Materializer
import grizzled.slf4j.Logging
import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.api.dlc.wallet.AnyDLCHDWalletApi
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
import org.bitcoins.core.currency._
import org.bitcoins.core.protocol.tlv._
import org.bitcoins.core.protocol.transaction.Transaction
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.core.wallet.utxo.{AddressLabelTagType, TxoState}
import org.bitcoins.crypto.NetworkElement
import org.bitcoins.core.api.dlc.wallet.AnyDLCHDWalletApi
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.keymanager._
import org.bitcoins.keymanager.config.KeyManagerAppConfig
import org.bitcoins.server.routes.{Server, ServerCommand, ServerRoute}
Expand Down Expand Up @@ -298,7 +298,9 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
collateral,
feeRateOpt,
locktime,
refundLT)) =>
refundLT,
payoutAddressOpt,
changeAddressOpt)) =>
complete {
val announcements = contractInfo.oracleInfo match {
case OracleInfoV0TLV(announcement) => Vector(announcement)
Expand All @@ -316,7 +318,9 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
collateral,
feeRateOpt,
locktime,
refundLT)
refundLT,
payoutAddressOpt,
changeAddressOpt)
.map { offer =>
Server.httpSuccess(offer.toMessage.hex)
}
Expand All @@ -327,10 +331,11 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
AcceptDLCOffer.fromJsArr(arr) match {
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(AcceptDLCOffer(offer)) =>
case Success(
AcceptDLCOffer(offer, payoutAddressOpt, changeAddressOpt)) =>
complete {
wallet
.acceptDLCOffer(offer.tlv)
.acceptDLCOffer(offer.tlv, payoutAddressOpt, changeAddressOpt)
.map { accept =>
Server.httpSuccess(accept.toMessage.hex)
}
Expand All @@ -341,15 +346,21 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
DLCDataFromFile.fromJsArr(arr) match {
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(DLCDataFromFile(path, destOpt)) =>
case Success(
DLCDataFromFile(path,
destOpt,
payoutAddressOpt,
changeAddressOpt)) =>
complete {

val hex = Files.readAllLines(path).get(0)

val offerMessage = LnMessageFactory(DLCOfferTLV).fromHex(hex)

wallet
.acceptDLCOffer(offerMessage.tlv)
.acceptDLCOffer(offerMessage.tlv,
payoutAddressOpt,
changeAddressOpt)
.map { accept =>
val ret = handleDestinationOpt(accept.toMessage.hex, destOpt)
Server.httpSuccess(ret)
Expand All @@ -375,7 +386,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
DLCDataFromFile.fromJsArr(arr) match {
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(DLCDataFromFile(path, destOpt)) =>
case Success(DLCDataFromFile(path, destOpt, _, _)) =>
complete {

val hex = Files.readAllLines(path).get(0)
Expand Down Expand Up @@ -408,7 +419,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
DLCDataFromFile.fromJsArr(arr) match {
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(DLCDataFromFile(path, _)) =>
case Success(DLCDataFromFile(path, _, _, _)) =>
complete {

val hex = Files.readAllLines(path).get(0)
Expand Down Expand Up @@ -439,7 +450,7 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
DLCDataFromFile.fromJsArr(arr) match {
case Failure(exception) =>
complete(Server.httpBadRequest(exception))
case Success(DLCDataFromFile(path, _)) =>
case Success(DLCDataFromFile(path, _, _, _)) =>
val hex = Files.readAllLines(path).get(0)

val signMessage = LnMessageFactory(DLCSignTLV).fromHex(hex)
Expand Down
3 changes: 3 additions & 0 deletions app/server/src/universal/docker-application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ bitcoin-s.dlcnode.proxy.socks5 = ${?BITCOIN_S_DLCNODE_PROXY_SOCKS5}
bitcoin-s.dlcnode.tor.enabled = ${?BITCOIN_S_DLCNODE_TOR_ENABLED}
bitcoin-s.dlcnode.external-ip = ${?BITCOIN_S_DLCNODE_EXTERNAL_IP}

bitcoin-s.wallet.allowExternalDLCAddresses = false
bitcoin-s.wallet.allowExternalDLCAddresses = ${?BITCOIN_S_ALLOW_EXT_DLC_ADDRESSES}

bitcoin-s.tor.enabled = ${?BITCOIN_S_TOR_ENABLED}
bitcoin-s.tor.provided = ${?BITCOIN_S_TOR_PROVIDED}

0 comments on commit 5777ec1

Please sign in to comment.