mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 10:46:42 +01:00
Add an ability to set custom payout and change addresses (#4101)
* Add an ability to set custom payout and change addresses * config changes * formatting * respond to the comments
This commit is contained in:
parent
5b1b1ee149
commit
5777ec1c31
19 changed files with 596 additions and 297 deletions
|
@ -952,18 +952,26 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
|||
)
|
||||
|
||||
"create a dlc offer" in {
|
||||
(mockWalletApi
|
||||
.createDLCOffer(_: ContractInfoTLV,
|
||||
(
|
||||
mockWalletApi
|
||||
.createDLCOffer(
|
||||
_: ContractInfoTLV,
|
||||
_: Satoshis,
|
||||
_: Option[SatoshisPerVirtualByte],
|
||||
_: UInt32,
|
||||
_: 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))
|
||||
|
||||
|
@ -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(
|
||||
|
|
|
@ -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}
|
||||
|
@ -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] = {
|
||||
|
||||
jsArr.arr.toList match {
|
||||
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil =>
|
||||
Try {
|
||||
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)
|
||||
refundLT,
|
||||
payoutAddressOpt,
|
||||
changeAddressOpt)
|
||||
}
|
||||
|
||||
jsArr.arr.toList match {
|
||||
case contractInfoJs :: collateralJs :: feeRateOptJs :: locktimeJs :: refundLTJs :: Nil =>
|
||||
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(
|
||||
|
@ -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"))
|
||||
|
||||
|
@ -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] = {
|
||||
jsArr.arr.toList match {
|
||||
case pathJs :: Nil =>
|
||||
Try {
|
||||
val path = new File(pathJs.str).toPath
|
||||
DLCDataFromFile(path, None)
|
||||
}
|
||||
case pathJs :: destJs :: Nil =>
|
||||
Try {
|
||||
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)
|
||||
DLCDataFromFile(path, destOpt)
|
||||
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 =>
|
||||
parseParameters(pathJs, Null, Null, Null)
|
||||
case pathJs :: destJs :: Nil =>
|
||||
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 =>
|
||||
|
|
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -316,7 +318,9 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
|
|||
collateral,
|
||||
feeRateOpt,
|
||||
locktime,
|
||||
refundLT)
|
||||
refundLT,
|
||||
payoutAddressOpt,
|
||||
changeAddressOpt)
|
||||
.map { offer =>
|
||||
Server.httpSuccess(offer.toMessage.hex)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -341,7 +346,11 @@ 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)
|
||||
|
@ -349,7 +358,9 @@ case class WalletRoutes(wallet: AnyDLCHDWalletApi)(implicit
|
|||
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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
|
|
@ -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}
|
|
@ -5,6 +5,7 @@ import org.bitcoins.core.api.wallet._
|
|||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.dlc.accounting._
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage._
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
import org.bitcoins.core.protocol.tlv._
|
||||
|
@ -22,9 +23,17 @@ trait DLCWalletApi { self: WalletApi =>
|
|||
collateral: Satoshis,
|
||||
feeRateOpt: Option[SatoshisPerVirtualByte],
|
||||
locktime: UInt32,
|
||||
refundLT: UInt32): Future[DLCOffer] = {
|
||||
refundLT: UInt32,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = {
|
||||
val contractInfo = ContractInfo.fromTLV(contractInfoTLV)
|
||||
createDLCOffer(contractInfo, collateral, feeRateOpt, locktime, refundLT)
|
||||
createDLCOffer(contractInfo,
|
||||
collateral,
|
||||
feeRateOpt,
|
||||
locktime,
|
||||
refundLT,
|
||||
externalPayoutAddressOpt,
|
||||
externalChangeAddressOpt)
|
||||
}
|
||||
|
||||
def createDLCOffer(
|
||||
|
@ -32,7 +41,9 @@ trait DLCWalletApi { self: WalletApi =>
|
|||
collateral: Satoshis,
|
||||
feeRateOpt: Option[SatoshisPerVirtualByte],
|
||||
locktime: UInt32,
|
||||
refundLT: UInt32): Future[DLCOffer]
|
||||
refundLT: UInt32,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer]
|
||||
|
||||
def registerDLCOffer(dlcOffer: DLCOffer): Future[DLCOffer] = {
|
||||
createDLCOffer(
|
||||
|
@ -40,15 +51,25 @@ trait DLCWalletApi { self: WalletApi =>
|
|||
dlcOffer.totalCollateral,
|
||||
Some(dlcOffer.feeRate),
|
||||
dlcOffer.timeouts.contractMaturity.toUInt32,
|
||||
dlcOffer.timeouts.contractTimeout.toUInt32
|
||||
dlcOffer.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
}
|
||||
|
||||
def acceptDLCOffer(dlcOfferTLV: DLCOfferTLV): Future[DLCAccept] = {
|
||||
acceptDLCOffer(DLCOffer.fromTLV(dlcOfferTLV))
|
||||
def acceptDLCOffer(
|
||||
dlcOfferTLV: DLCOfferTLV,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] = {
|
||||
acceptDLCOffer(DLCOffer.fromTLV(dlcOfferTLV),
|
||||
externalPayoutAddressOpt,
|
||||
externalChangeAddressOpt)
|
||||
}
|
||||
|
||||
def acceptDLCOffer(dlcOffer: DLCOffer): Future[DLCAccept]
|
||||
def acceptDLCOffer(
|
||||
dlcOffer: DLCOffer,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept]
|
||||
|
||||
def signDLC(acceptTLV: DLCAcceptTLV): Future[DLCSign]
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.bitcoins.core.crypto.ExtPublicKey
|
|||
import org.bitcoins.core.hd.{BIP32Path, HDChainType}
|
||||
import org.bitcoins.core.number.UInt16
|
||||
import org.bitcoins.core.policy.Policy
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
|
||||
DLCAccept,
|
||||
|
@ -324,7 +325,8 @@ object DLCUtil {
|
|||
xpub: ExtPublicKey,
|
||||
chainType: HDChainType,
|
||||
keyIndex: Int,
|
||||
networkParameters: NetworkParameters): DLCPublicKeys = {
|
||||
networkParameters: NetworkParameters,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress]): DLCPublicKeys = {
|
||||
val chainIndex = chainType.index
|
||||
val fundingKey =
|
||||
xpub
|
||||
|
@ -332,16 +334,20 @@ object DLCUtil {
|
|||
.get
|
||||
.key
|
||||
|
||||
networkParameters match {
|
||||
case bitcoinNetwork: BitcoinNetwork =>
|
||||
externalPayoutAddressOpt match {
|
||||
case None =>
|
||||
val payoutKey =
|
||||
xpub
|
||||
.deriveChildPubKey(
|
||||
BIP32Path.fromString(s"m/$chainIndex/${keyIndex + 1}"))
|
||||
.get
|
||||
.key
|
||||
|
||||
networkParameters match {
|
||||
case bitcoinNetwork: BitcoinNetwork =>
|
||||
DLCPublicKeys.fromPubKeys(fundingKey, payoutKey, bitcoinNetwork)
|
||||
case Some(externalPayoutAddress) =>
|
||||
DLCPublicKeys(fundingKey, externalPayoutAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,6 +134,10 @@ bitcoin-s {
|
|||
# before we timeout
|
||||
addressQueueTimeout = 5 seconds
|
||||
|
||||
# Allow external payout and change addresses in DLCs
|
||||
# By default all DLC addresses are generated by the wallet itself
|
||||
allowExternalDLCAddresses = false
|
||||
|
||||
# this config key is read by Slick
|
||||
db {
|
||||
name = walletdb
|
||||
|
|
|
@ -57,8 +57,10 @@ class DLCNegotiationTest extends BitcoinSDualWalletTest {
|
|||
half,
|
||||
Some(SatoshisPerVirtualByte.one),
|
||||
UInt32.zero,
|
||||
UInt32.one)
|
||||
accept <- walletA.acceptDLCOffer(offer)
|
||||
UInt32.one,
|
||||
None,
|
||||
None)
|
||||
accept <- walletA.acceptDLCOffer(offer, None, None)
|
||||
|
||||
// Send accept message to begin p2p
|
||||
_ = handler ! accept.toMessage
|
||||
|
|
|
@ -35,7 +35,9 @@ class DLCNodeTest extends BitcoinSDLCNodeTest {
|
|||
half,
|
||||
Some(SatoshisPerVirtualByte.one),
|
||||
UInt32.zero,
|
||||
UInt32.one)
|
||||
UInt32.one,
|
||||
None,
|
||||
None)
|
||||
|
||||
_ <- nodeB.acceptDLCOffer(addrA, offer.toMessage)
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class DLCDataHandler(dlcWalletApi: DLCWalletApi, connectionHandler: ActorRef)
|
|||
Future.unit
|
||||
case dlcOffer: DLCOfferTLV =>
|
||||
val f = for {
|
||||
accept <- dlcWalletApi.acceptDLCOffer(dlcOffer)
|
||||
accept <- dlcWalletApi.acceptDLCOffer(dlcOffer, None, None)
|
||||
_ = connectionHandler ! accept.toMessage
|
||||
} yield ()
|
||||
f
|
||||
|
|
|
@ -362,11 +362,15 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
|
|||
|
||||
//helper method to make an offer
|
||||
def makeOffer(): Future[DLCOffer] = {
|
||||
walletA.createDLCOffer(contractInfoTLV = contractInfo,
|
||||
walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one)
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
}
|
||||
|
||||
//simply try to make 2 offers with the same contract info
|
||||
|
@ -414,7 +418,9 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
|
|||
status.localCollateral.satoshis,
|
||||
None,
|
||||
UInt32.zero,
|
||||
UInt32.one)
|
||||
UInt32.one,
|
||||
None,
|
||||
None)
|
||||
|
||||
_ <- walletA.listDLCs()
|
||||
} yield succeed
|
||||
|
|
|
@ -51,7 +51,9 @@ class MultiWalletDLCTest extends BitcoinSWalletTest {
|
|||
half,
|
||||
Some(SatoshisPerVirtualByte.one),
|
||||
UInt32.zero,
|
||||
UInt32.one)
|
||||
UInt32.one,
|
||||
None,
|
||||
None)
|
||||
dlcsA <- walletA.listDLCs()
|
||||
dlcsB <- walletB.listDLCs()
|
||||
|
||||
|
@ -68,12 +70,15 @@ class MultiWalletDLCTest extends BitcoinSWalletTest {
|
|||
fundedWallet: FundedDLCWallet =>
|
||||
//see: https://github.com/bitcoin-s/bitcoin-s/issues/3813#issue-1051117559
|
||||
val wallet = fundedWallet.wallet
|
||||
val offerF = wallet.createDLCOffer(contractInfo = sampleContractInfo,
|
||||
val offerF = wallet.createDLCOffer(
|
||||
contractInfo = sampleContractInfo,
|
||||
collateral = half,
|
||||
feeRateOpt =
|
||||
Some(SatoshisPerVirtualByte.one),
|
||||
feeRateOpt = Some(SatoshisPerVirtualByte.one),
|
||||
locktime = UInt32.zero,
|
||||
refundLocktime = UInt32.one)
|
||||
refundLocktime = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
|
||||
//now unreserve the utxo
|
||||
val reservedUtxoF = for {
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.bitcoins.dlc.wallet
|
|||
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage._
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
import org.bitcoins.core.protocol.script.P2WPKHWitnessV0
|
||||
|
@ -44,7 +45,9 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
dlcA1Opt <- walletA.dlcDAO.read(dlcId)
|
||||
|
@ -61,7 +64,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
assert(offer.changeAddress.value.nonEmpty)
|
||||
}
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
dlcB1Opt <- walletB.dlcDAO.read(dlcId)
|
||||
_ = {
|
||||
assert(dlcB1Opt.isDefined)
|
||||
|
@ -185,11 +188,13 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
|
||||
// reorder dlc inputs in wallets
|
||||
_ <- reorderInputDbs(walletA, dlcId)
|
||||
|
@ -242,11 +247,13 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer.toTLV)
|
||||
accept <- walletB.acceptDLCOffer(offer.toTLV, None, None)
|
||||
|
||||
// reorder dlc inputs in wallets
|
||||
_ <- reorderInputDbs(walletA, dlcId)
|
||||
|
@ -271,7 +278,9 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
dlcA1Opt <- walletA.dlcDAO.read(dlcId)
|
||||
|
@ -287,7 +296,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
assert(offer.changeAddress.value.nonEmpty)
|
||||
}
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer.toTLV)
|
||||
accept <- walletB.acceptDLCOffer(offer.toTLV, None, None)
|
||||
dlcB1Opt <- walletB.dlcDAO.read(dlcId)
|
||||
_ = {
|
||||
assert(dlcB1Opt.isDefined)
|
||||
|
@ -365,10 +374,12 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
} yield accept
|
||||
}
|
||||
|
||||
|
@ -509,7 +520,9 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
|
@ -552,9 +565,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
_ <- walletB.acceptDLCOffer(offer)
|
||||
_ <- walletB.acceptDLCOffer(offer, None, None)
|
||||
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
|
||||
|
@ -593,9 +608,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
sign <- walletA.signDLC(accept)
|
||||
_ <- walletB.addDLCSigs(sign)
|
||||
|
||||
|
@ -635,9 +652,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
sign <- walletA.signDLC(accept)
|
||||
_ <- walletB.addDLCSigs(sign)
|
||||
|
||||
|
@ -668,9 +687,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
UInt32.max
|
||||
UInt32.max,
|
||||
None,
|
||||
None
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
sign <- walletA.signDLC(accept)
|
||||
_ <- walletB.addDLCSigs(sign)
|
||||
|
||||
|
@ -733,7 +754,9 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
offerData.timeouts.contractTimeout.toUInt32
|
||||
offerData.timeouts.contractTimeout.toUInt32,
|
||||
None,
|
||||
None
|
||||
)
|
||||
_ = {
|
||||
assert(offer.oracleInfos == offerData.oracleInfos)
|
||||
|
@ -747,7 +770,7 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
|
||||
dlcId = calcDLCId(offer.fundingInputs.map(_.outPoint))
|
||||
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
_ = {
|
||||
assert(accept.fundingInputs.nonEmpty)
|
||||
assert(
|
||||
|
@ -824,19 +847,23 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
val totalCollateral = Satoshis(5000)
|
||||
|
||||
def makeOffer(contractInfo: ContractInfoV0TLV): Future[DLCOffer] = {
|
||||
walletA.createDLCOffer(contractInfoTLV = contractInfo,
|
||||
walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one)
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
}
|
||||
|
||||
for {
|
||||
offerA <- makeOffer(contractInfoA)
|
||||
offerB <- makeOffer(contractInfoB)
|
||||
|
||||
_ <- walletB.acceptDLCOffer(offerA)
|
||||
_ <- walletB.acceptDLCOffer(offerB)
|
||||
_ <- walletB.acceptDLCOffer(offerA, None, None)
|
||||
_ <- walletB.acceptDLCOffer(offerB, None, None)
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
|
@ -868,17 +895,21 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
val totalCollateral = Satoshis(100000)
|
||||
|
||||
def makeOffer(contractInfo: ContractInfoV0TLV): Future[DLCOffer] = {
|
||||
walletA.createDLCOffer(contractInfoTLV = contractInfo,
|
||||
walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one)
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
}
|
||||
|
||||
for {
|
||||
offer <- makeOffer(contractInfoA)
|
||||
accept1F = walletB.acceptDLCOffer(offer)
|
||||
accept2F = walletB.acceptDLCOffer(offer)
|
||||
accept1F = walletB.acceptDLCOffer(offer, None, None)
|
||||
accept2F = walletB.acceptDLCOffer(offer, None, None)
|
||||
_ <- recoverToSucceededIf[DuplicateOfferException](
|
||||
Future.sequence(Seq(accept1F, accept2F)))
|
||||
} yield {
|
||||
|
@ -895,17 +926,21 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
val totalCollateral = Satoshis(100000)
|
||||
|
||||
def makeOffer(contractInfo: ContractInfoV0TLV): Future[DLCOffer] = {
|
||||
walletA.createDLCOffer(contractInfoTLV = contractInfo,
|
||||
walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one)
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
}
|
||||
|
||||
for {
|
||||
offer <- makeOffer(contractInfoA)
|
||||
accept1 <- walletB.acceptDLCOffer(offer)
|
||||
accept2 <- walletB.acceptDLCOffer(offer)
|
||||
accept1 <- walletB.acceptDLCOffer(offer, None, None)
|
||||
accept2 <- walletB.acceptDLCOffer(offer, None, None)
|
||||
} yield {
|
||||
assert(accept1 == accept2)
|
||||
}
|
||||
|
@ -923,9 +958,11 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
UInt32.max
|
||||
UInt32.max,
|
||||
None,
|
||||
None
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer, None, None)
|
||||
res <- recoverToSucceededIf[IllegalArgumentException](
|
||||
walletB.signDLC(accept))
|
||||
} yield res
|
||||
|
@ -944,7 +981,9 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
offerData.totalCollateral,
|
||||
Some(offerData.feeRate),
|
||||
offerData.timeouts.contractMaturity.toUInt32,
|
||||
UInt32.max
|
||||
UInt32.max,
|
||||
None,
|
||||
None
|
||||
))
|
||||
} yield {
|
||||
res
|
||||
|
@ -963,18 +1002,82 @@ class WalletDLCSetupTest extends BitcoinSDualWalletTest {
|
|||
val totalCollateral = Satoshis(5000)
|
||||
|
||||
for {
|
||||
offer <- walletA.createDLCOffer(contractInfoTLV = contractInfo,
|
||||
offer <- walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one)
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
invalidOffer = offer.copy(contractInfo = invalidContractInfo)
|
||||
res <- recoverToSucceededIf[InvalidAnnouncementSignature](
|
||||
walletB.acceptDLCOffer(invalidOffer))
|
||||
walletB.acceptDLCOffer(invalidOffer, None, None))
|
||||
} yield {
|
||||
res
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it must "use external payout and change addresses when they are provided" in {
|
||||
wallets =>
|
||||
val walletA = wallets._1.wallet
|
||||
val walletB = wallets._2.wallet
|
||||
|
||||
//https://test.oracle.suredbits.com/contract/enum/75b08299654dca23b80cf359db6afb6cfd6e55bc898b5397d3c0fe796dfc13f0/12fb3e5f091086329ed0d2a12c3fcfa80111a36ef3fc1ac9c2567076a57d6a73
|
||||
val contractInfo = ContractInfoV0TLV.fromHex(
|
||||
"fdd82eeb00000000000186a0fda71026030359455300000000000186a0024e4f0000000000000000056f746865720000000000000000fda712b5fdd824b1596ec40d0dae3fdf54d9795ad51ec069970c6863a02d244663d39fd6bedadc0070349e1ba2e17583ee2d1cb3ae6fffaaa1c45039b61c5c4f1d0d864221c461745d1bcfab252c6dd9edd7aea4c5eeeef138f7ff7346061ea40143a9f5ae80baa9fdd8224d0001fa5b84283852400b21a840d5d5ca1cc31867c37326ad521aa50bebf3df4eea1a60b03280fdd8060f000303594553024e4f056f74686572135465746865722d52657365727665732d363342")
|
||||
val contractInfo1 = DLCWalletUtil.sampleDLCOffer.contractInfo.toTLV
|
||||
|
||||
val feeRateOpt = Some(SatoshisPerVirtualByte(Satoshis.one))
|
||||
val totalCollateral = Satoshis(5000)
|
||||
val feeRateOpt1 = Some(SatoshisPerVirtualByte(Satoshis(2)))
|
||||
val totalCollateral1 = Satoshis(10000)
|
||||
|
||||
// random testnet addresses
|
||||
val payoutAddressAOpt = Some(
|
||||
BitcoinAddress.fromString("tb1qw98mrsxpqtz25xe332khnvlapvl09ejnzk7c3f"))
|
||||
val changeAddressAOpt = Some(
|
||||
BitcoinAddress.fromString("tb1qkfaglsvpcwe5pm9ktqs80u9d9jd0qzgqjqd240"))
|
||||
val payoutAddressBOpt =
|
||||
Some(BitcoinAddress.fromString("2MsM67NLa71fHvTUBqNENW15P68nHB2vVXb"))
|
||||
val changeAddressBOpt =
|
||||
Some(BitcoinAddress.fromString("2N4YXTxKEso3yeYXNn5h42Vqu3FzTTQ8Lq5"))
|
||||
|
||||
for {
|
||||
offer <- walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo,
|
||||
collateral = totalCollateral,
|
||||
feeRateOpt = feeRateOpt,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = payoutAddressAOpt,
|
||||
externalChangeAddressOpt = changeAddressAOpt
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer,
|
||||
payoutAddressBOpt,
|
||||
changeAddressBOpt)
|
||||
offer1 <- walletA.createDLCOffer(
|
||||
contractInfoTLV = contractInfo1,
|
||||
collateral = totalCollateral1,
|
||||
feeRateOpt = feeRateOpt1,
|
||||
locktime = UInt32.zero,
|
||||
refundLT = UInt32.one,
|
||||
externalPayoutAddressOpt = None,
|
||||
externalChangeAddressOpt = None
|
||||
)
|
||||
accept1 <- walletB.acceptDLCOffer(offer1, None, None)
|
||||
} yield {
|
||||
assert(offer.pubKeys.payoutAddress == payoutAddressAOpt.get)
|
||||
assert(offer.changeAddress == changeAddressAOpt.get)
|
||||
assert(accept.pubKeys.payoutAddress == payoutAddressBOpt.get)
|
||||
assert(accept.changeAddress == changeAddressBOpt.get)
|
||||
assert(offer1.pubKeys.payoutAddress != payoutAddressAOpt.get)
|
||||
assert(offer1.changeAddress != changeAddressAOpt.get)
|
||||
assert(accept1.pubKeys.payoutAddress != payoutAddressBOpt.get)
|
||||
assert(accept1.changeAddress != changeAddressBOpt.get)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -268,8 +268,16 @@ abstract class DLCWallet
|
|||
collateral: Satoshis,
|
||||
feeRateOpt: Option[SatoshisPerVirtualByte],
|
||||
locktime: UInt32,
|
||||
refundLocktime: UInt32): Future[DLCOffer] = {
|
||||
refundLocktime: UInt32,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCOffer] = {
|
||||
logger.info("Creating DLC Offer")
|
||||
if (
|
||||
!walletConfig.allowExternalDLCAddresses && (externalPayoutAddressOpt.nonEmpty || externalChangeAddressOpt.nonEmpty)
|
||||
) {
|
||||
return Future.failed(
|
||||
new IllegalArgumentException("External DLC addresses are not allowed"))
|
||||
}
|
||||
if (!validateAnnouncementSignatures(contractInfo.oracleInfos)) {
|
||||
return Future.failed(
|
||||
InvalidAnnouncementSignature(
|
||||
|
@ -279,19 +287,15 @@ abstract class DLCWallet
|
|||
val announcements =
|
||||
contractInfo.oracleInfos.head.singleOracleInfos.map(_.announcement)
|
||||
|
||||
//hack for now to get around https://github.com/bitcoin-s/bitcoin-s/issues/3127
|
||||
//filter announcements that we already have in the db
|
||||
val groupedAnnouncementsF: Future[AnnouncementGrouping] = {
|
||||
groupByExistingAnnouncements(announcements)
|
||||
}
|
||||
|
||||
val feeRateF = determineFeeRate(feeRateOpt).map { fee =>
|
||||
SatoshisPerVirtualByte(fee.currencyUnit)
|
||||
}
|
||||
|
||||
for {
|
||||
feeRate <- feeRateF
|
||||
groupedAnnouncements <- groupedAnnouncementsF
|
||||
//hack for now to get around https://github.com/bitcoin-s/bitcoin-s/issues/3127
|
||||
//filter announcements that we already have in the db
|
||||
groupedAnnouncements <- groupByExistingAnnouncements(announcements)
|
||||
announcementDataDbs <- announcementDAO.createAll(
|
||||
groupedAnnouncements.newAnnouncements)
|
||||
allAnnouncementDbs =
|
||||
|
@ -341,14 +345,17 @@ abstract class DLCWallet
|
|||
used = false)
|
||||
}
|
||||
|
||||
changeSPK =
|
||||
txBuilder.finalizer.changeSPK
|
||||
changeAddr = BitcoinAddress.fromScriptPubKey(changeSPK, networkParameters)
|
||||
changeAddr = externalChangeAddressOpt.getOrElse {
|
||||
val changeSPK = txBuilder.finalizer.changeSPK
|
||||
BitcoinAddress.fromScriptPubKey(changeSPK, networkParameters)
|
||||
}
|
||||
|
||||
dlcPubKeys = DLCUtil.calcDLCPubKeys(xpub = account.xpub,
|
||||
chainType = chainType,
|
||||
keyIndex = nextIndex,
|
||||
networkParameters = networkParameters)
|
||||
networkParameters = networkParameters,
|
||||
externalPayoutAddressOpt =
|
||||
externalPayoutAddressOpt)
|
||||
|
||||
_ = logger.debug(
|
||||
s"DLC Offer data collected, creating database entry, ${dlcId.hex}")
|
||||
|
@ -552,8 +559,17 @@ abstract class DLCWallet
|
|||
*
|
||||
* This is the first step of the recipient
|
||||
*/
|
||||
override def acceptDLCOffer(offer: DLCOffer): Future[DLCAccept] = {
|
||||
override def acceptDLCOffer(
|
||||
offer: DLCOffer,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] = {
|
||||
logger.debug("Calculating relevant wallet data for DLC Accept")
|
||||
if (
|
||||
!walletConfig.allowExternalDLCAddresses && (externalPayoutAddressOpt.nonEmpty || externalChangeAddressOpt.nonEmpty)
|
||||
) {
|
||||
return Future.failed(
|
||||
new IllegalArgumentException("External DLC addresses are not allowed"))
|
||||
}
|
||||
if (!validateAnnouncementSignatures(offer.oracleInfos)) {
|
||||
return Future.failed(InvalidAnnouncementSignature(
|
||||
s"Offer ${offer.tempContractId.hex} contains invalid announcement signature(s)"))
|
||||
|
@ -573,7 +589,11 @@ abstract class DLCWallet
|
|||
dlcAccept <- {
|
||||
dlcAcceptOpt match {
|
||||
case Some(accept) => Future.successful(accept)
|
||||
case None => createNewDLCAccept(collateral, offer)
|
||||
case None =>
|
||||
createNewDLCAccept(collateral,
|
||||
offer,
|
||||
externalPayoutAddressOpt,
|
||||
externalChangeAddressOpt)
|
||||
}
|
||||
}
|
||||
status <- findDLC(dlcId)
|
||||
|
@ -612,7 +632,10 @@ abstract class DLCWallet
|
|||
|
||||
private def createNewDLCAccept(
|
||||
collateral: CurrencyUnit,
|
||||
offer: DLCOffer): Future[DLCAccept] = Future {
|
||||
offer: DLCOffer,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): Future[DLCAccept] =
|
||||
Future {
|
||||
DLCWallet.AcceptingOffersLatch.startAccepting(offer.tempContractId)
|
||||
|
||||
logger.info(
|
||||
|
@ -642,7 +665,9 @@ abstract class DLCWallet
|
|||
account = account,
|
||||
fundingPrivKey = fundingPrivKey,
|
||||
collateral = collateral,
|
||||
networkParameters = networkParameters
|
||||
networkParameters = networkParameters,
|
||||
externalPayoutAddressOpt = externalPayoutAddressOpt,
|
||||
externalChangeAddressOpt = externalChangeAddressOpt
|
||||
)
|
||||
builder = DLCTxBuilder(offer, acceptWithoutSigs)
|
||||
|
||||
|
@ -731,7 +756,8 @@ abstract class DLCWallet
|
|||
|
||||
accept =
|
||||
dlcAcceptDb.toDLCAccept(tempContractId = dlc.tempContractId,
|
||||
fundingInputs = acceptWithoutSigs.fundingInputs,
|
||||
fundingInputs =
|
||||
acceptWithoutSigs.fundingInputs,
|
||||
outcomeSigs = cetSigs.outcomeSigs,
|
||||
refundSig = refundSig)
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ object DLCAcceptUtil extends Logging {
|
|||
account: AccountDb,
|
||||
fundingPrivKey: AdaptorSign,
|
||||
collateral: CurrencyUnit,
|
||||
networkParameters: NetworkParameters): (
|
||||
networkParameters: NetworkParameters,
|
||||
externalPayoutAddressOpt: Option[BitcoinAddress],
|
||||
externalChangeAddressOpt: Option[BitcoinAddress]): (
|
||||
DLCAcceptWithoutSigs,
|
||||
DLCPublicKeys) = {
|
||||
val serialIds = DLCMessage.genSerialIds(
|
||||
|
@ -49,15 +51,18 @@ object DLCAcceptUtil extends Logging {
|
|||
.fromInputSigningInfo(utxo, id, TransactionConstants.enableRBFSequence)
|
||||
}
|
||||
|
||||
val changeAddr = externalChangeAddressOpt.getOrElse {
|
||||
val changeSPK = txBuilder.finalizer.changeSPK
|
||||
val changeAddr =
|
||||
BitcoinAddress.fromScriptPubKey(changeSPK, networkParameters)
|
||||
}
|
||||
|
||||
val dlcPubKeys = DLCUtil.calcDLCPubKeys(xpub = account.xpub,
|
||||
val dlcPubKeys = DLCUtil.calcDLCPubKeys(
|
||||
xpub = account.xpub,
|
||||
chainType = dlc.changeIndex,
|
||||
keyIndex = dlc.keyIndex,
|
||||
networkParameters =
|
||||
networkParameters)
|
||||
networkParameters = networkParameters,
|
||||
externalPayoutAddressOpt = externalPayoutAddressOpt
|
||||
)
|
||||
|
||||
require(dlcPubKeys.fundingKey == fundingPrivKey.publicKey,
|
||||
"Did not derive the same funding private and public key")
|
||||
|
|
|
@ -242,6 +242,10 @@ bitcoin-s {
|
|||
# before we timeout
|
||||
addressQueueTimeout = 5 seconds
|
||||
|
||||
# Allow external payout and change addresses in DLCs
|
||||
# By default all DLC addresses are generated by the wallet itself
|
||||
allowExternalDLCAddresses = false
|
||||
|
||||
# How often the wallet will rebroadcast unconfirmed transactions
|
||||
rebroadcastFrequency = 4 hours
|
||||
|
||||
|
|
|
@ -46,7 +46,9 @@ object BitcoinSTestAppConfig {
|
|||
| node {
|
||||
| mode = spv
|
||||
| }
|
||||
|
|
||||
| wallet {
|
||||
| allowExternalDLCAddresses = true
|
||||
| }
|
||||
| proxy.enabled = $torEnabled
|
||||
| tor.enabled = $torEnabled
|
||||
| tor.use-random-ports = false
|
||||
|
|
|
@ -274,7 +274,12 @@ object DLCWalletUtil extends Logging {
|
|||
def initDLC(
|
||||
fundedWalletA: FundedDLCWallet,
|
||||
fundedWalletB: FundedDLCWallet,
|
||||
contractInfo: ContractInfo)(implicit ec: ExecutionContext): Future[
|
||||
contractInfo: ContractInfo,
|
||||
payoutAddressAOpt: Option[BitcoinAddress] = None,
|
||||
changeAddressAOpt: Option[BitcoinAddress] = None,
|
||||
payoutAddressBOpt: Option[BitcoinAddress] = None,
|
||||
changeAddressBOpt: Option[BitcoinAddress] = None)(implicit
|
||||
ec: ExecutionContext): Future[
|
||||
(InitializedDLCWallet, InitializedDLCWallet)] = {
|
||||
val walletA = fundedWalletA.wallet
|
||||
val walletB = fundedWalletB.wallet
|
||||
|
@ -285,9 +290,13 @@ object DLCWalletUtil extends Logging {
|
|||
collateral = half,
|
||||
feeRateOpt = Some(SatoshisPerVirtualByte.fromLong(10)),
|
||||
locktime = dummyTimeouts.contractMaturity.toUInt32,
|
||||
refundLocktime = dummyTimeouts.contractTimeout.toUInt32
|
||||
refundLocktime = dummyTimeouts.contractTimeout.toUInt32,
|
||||
externalPayoutAddressOpt = payoutAddressAOpt,
|
||||
externalChangeAddressOpt = changeAddressAOpt
|
||||
)
|
||||
accept <- walletB.acceptDLCOffer(offer)
|
||||
accept <- walletB.acceptDLCOffer(offer,
|
||||
payoutAddressBOpt,
|
||||
changeAddressBOpt)
|
||||
sigs <- walletA.signDLC(accept)
|
||||
_ <- walletB.addDLCSigs(sigs)
|
||||
tx <- walletB.broadcastDLCFundingTx(sigs.contractId)
|
||||
|
|
|
@ -145,6 +145,9 @@ case class WalletAppConfig(baseDatadir: Path, configOverrides: Vector[Config])(
|
|||
lazy val feeProviderTargetOpt: Option[Int] =
|
||||
config.getIntOpt("bitcoin-s.fee-provider.target")
|
||||
|
||||
lazy val allowExternalDLCAddresses: Boolean =
|
||||
config.getBoolean("bitcoin-s.wallet.allowExternalDLCAddresses")
|
||||
|
||||
lazy val bip39PasswordOpt: Option[String] = kmConf.bip39PasswordOpt
|
||||
|
||||
lazy val aesPasswordOpt: Option[AesPassword] = kmConf.aesPasswordOpt
|
||||
|
|
Loading…
Add table
Reference in a new issue